|
42 | 42 | from packagedcode.utils import parse_maintainer_name_email
|
43 | 43 | from packagedcode.utils import yield_dependencies_from_package_data
|
44 | 44 | from packagedcode.utils import yield_dependencies_from_package_resource
|
| 45 | +from packagedcode.utils import get_base_purl |
45 | 46 |
|
46 | 47 | try:
|
47 | 48 | from zipfile import Path as ZipPath
|
@@ -563,6 +564,123 @@ def parse(cls, location, package_only=False):
|
563 | 564 | yield models.PackageData.from_data(package_data, package_only)
|
564 | 565 |
|
565 | 566 |
|
| 567 | +class PipInspectDeplockHandler(models.DatafileHandler): |
| 568 | + datasource_id = 'pypi_inspect_deplock' |
| 569 | + path_patterns = ('*pip-inspect.deplock',) |
| 570 | + default_package_type = 'pypi' |
| 571 | + default_primary_language = 'Python' |
| 572 | + description = 'Python poetry pyproject.toml' |
| 573 | + # These are files generated by deplock, see https://github.com/nexB/dependency-inspector |
| 574 | + documentation_url = 'https://pip.pypa.io/en/stable/cli/pip_inspect/' |
| 575 | + |
| 576 | + @classmethod |
| 577 | + def get_resolved_package_from_metadata(cls, metadata, package_only=False): |
| 578 | + |
| 579 | + requires_dist = metadata.get('requires_dist') |
| 580 | + dependencies_for_resolved = get_requires_dependencies( |
| 581 | + requires=requires_dist, |
| 582 | + ) |
| 583 | + package_data = dict( |
| 584 | + datasource_id=cls.datasource_id, |
| 585 | + type=cls.default_package_type, |
| 586 | + primary_language='Python', |
| 587 | + name=metadata.get('name'), |
| 588 | + version=metadata.get('version'), |
| 589 | + extracted_license_statement=metadata.get('license'), |
| 590 | + description=metadata.get('description'), |
| 591 | + keywords=metadata.get('keywords'), |
| 592 | + is_virtual=True, |
| 593 | + dependencies=[ |
| 594 | + dep.to_dict() |
| 595 | + for dep in dependencies_for_resolved |
| 596 | + ], |
| 597 | + ) |
| 598 | + return models.PackageData.from_data(package_data, package_only) |
| 599 | + |
| 600 | + @classmethod |
| 601 | + def parse(cls, location, package_only=False): |
| 602 | + |
| 603 | + with open(location) as f: |
| 604 | + content = f.read() |
| 605 | + |
| 606 | + data = json.loads(content) |
| 607 | + installed_packages = data.get('installed') |
| 608 | + if not installed_packages: |
| 609 | + return |
| 610 | + |
| 611 | + main_package_metadata = {} |
| 612 | + dependencies = [] |
| 613 | + |
| 614 | + direct_deps_of_main_package = [] |
| 615 | + |
| 616 | + for package_metadata in installed_packages: |
| 617 | + package_metadata_dep = package_metadata.get('metadata') |
| 618 | + |
| 619 | + # `direct_url` is only present for root package |
| 620 | + # `requested` is true for root package and direct dependencies only |
| 621 | + if package_metadata.get('requested') and 'direct_url' in package_metadata: |
| 622 | + main_package_metadata = package_metadata_dep |
| 623 | + main_package_requires = main_package_metadata.get('requires_dist') |
| 624 | + dependencies_for_main = get_requires_dependencies( |
| 625 | + requires=main_package_requires, |
| 626 | + ) |
| 627 | + direct_deps_of_main_package.extend([ |
| 628 | + get_base_purl(dep.purl) |
| 629 | + for dep in dependencies_for_main |
| 630 | + ]) |
| 631 | + continue |
| 632 | + |
| 633 | + package_data_dep = cls.get_resolved_package_from_metadata( |
| 634 | + metadata=package_metadata_dep, |
| 635 | + package_only=package_only, |
| 636 | + ) |
| 637 | + dep_purl = package_data_dep.purl |
| 638 | + dependency = models.DependentPackage( |
| 639 | + purl=dep_purl, |
| 640 | + extracted_requirement=None, |
| 641 | + scope=None, |
| 642 | + is_runtime=True, |
| 643 | + is_optional=False, |
| 644 | + is_direct=False, |
| 645 | + is_resolved=True, |
| 646 | + resolved_package=package_data_dep.to_dict() |
| 647 | + ) |
| 648 | + dependencies.append(dependency) |
| 649 | + |
| 650 | + dependency_mappings = [] |
| 651 | + resolved_main_dependencies = [] |
| 652 | + |
| 653 | + # Update is_direct for direct dependencies |
| 654 | + for dep in dependencies: |
| 655 | + base_purl = get_base_purl(dep.purl) |
| 656 | + if base_purl in direct_deps_of_main_package: |
| 657 | + dep.is_direct = True |
| 658 | + resolved_main_dependencies.append(base_purl) |
| 659 | + |
| 660 | + dependency_mappings.append(dep.to_dict()) |
| 661 | + |
| 662 | + pip_version = data.get('pip_version') |
| 663 | + inspect_version = data.get('version') |
| 664 | + extra_data = { |
| 665 | + "pip_version": pip_version, |
| 666 | + "inspect_version": inspect_version, |
| 667 | + } |
| 668 | + |
| 669 | + package_data_main = cls.get_resolved_package_from_metadata( |
| 670 | + metadata=main_package_metadata, |
| 671 | + package_only=package_only, |
| 672 | + ) |
| 673 | + |
| 674 | + main_dependencies = [] |
| 675 | + for dep in package_data_main.dependencies: |
| 676 | + base_purl = get_base_purl(purl=dep.get('purl')) |
| 677 | + if base_purl not in resolved_main_dependencies: |
| 678 | + main_dependencies.append(dep) |
| 679 | + |
| 680 | + package_data_main.dependencies = dependencies |
| 681 | + package_data_main.dependencies.extend(main_dependencies) |
| 682 | + package_data_main.extra_data = extra_data |
| 683 | + yield package_data_main |
566 | 684 |
|
567 | 685 |
|
568 | 686 | META_DIR_SUFFIXES = '.dist-info', '.egg-info', 'EGG-INFO',
|
@@ -1494,7 +1612,7 @@ def get_dist_dependencies(dist):
|
1494 | 1612 | return get_requires_dependencies(requires=dist.requires)
|
1495 | 1613 |
|
1496 | 1614 |
|
1497 |
| -def get_requires_dependencies(requires, default_scope='install'): |
| 1615 | +def get_requires_dependencies(requires, default_scope='install', is_direct=True): |
1498 | 1616 | """
|
1499 | 1617 | Return a list of DependentPackage found in a ``requires`` list of
|
1500 | 1618 | requirement strings or an empty list.
|
@@ -1539,6 +1657,7 @@ def get_requires_dependencies(requires, default_scope='install'):
|
1539 | 1657 | is_runtime=True,
|
1540 | 1658 | is_optional=True if bool(extra) else False,
|
1541 | 1659 | is_resolved=is_resolved,
|
| 1660 | + is_direct=is_direct, |
1542 | 1661 | extracted_requirement=str(req),
|
1543 | 1662 | ))
|
1544 | 1663 |
|
|
0 commit comments