@@ -563,6 +563,102 @@ def parse(cls, location, package_only=False):
563563 yield models .PackageData .from_data (package_data , package_only )
564564
565565
566+ class PipInspectDeplockHandler (models .DatafileHandler ):
567+ datasource_id = 'pypi_inspect_deplock'
568+ path_patterns = ('*pip-inspect.deplock' ,)
569+ default_package_type = 'pypi'
570+ default_primary_language = 'Python'
571+ description = 'Python poetry pyproject.toml'
572+ # These are files generated by deplock, see https://github.com/nexB/dependency-inspector
573+ documentation_url = 'https://pip.pypa.io/en/stable/cli/pip_inspect/'
574+
575+ @classmethod
576+ def get_resolved_package_from_metadata (cls , metadata , package_only = False ):
577+
578+ requires_dist = metadata .get ('requires_dist' )
579+ dependencies_for_resolved = get_requires_dependencies (
580+ requires = requires_dist ,
581+ )
582+ package_data = dict (
583+ datasource_id = cls .datasource_id ,
584+ type = cls .default_package_type ,
585+ primary_language = 'Python' ,
586+ name = metadata .get ('name' ),
587+ version = metadata .get ('version' ),
588+ extracted_license_statement = metadata .get ('license' ),
589+ description = metadata .get ('description' ),
590+ keywords = metadata .get ('keywords' ),
591+ is_virtual = True ,
592+ dependencies = [
593+ dep .to_dict ()
594+ for dep in dependencies_for_resolved
595+ ],
596+ )
597+ return models .PackageData .from_data (package_data , package_only )
598+
599+ @classmethod
600+ def parse (cls , location , package_only = False ):
601+
602+ with open (location ) as f :
603+ content = f .read ()
604+
605+ data = json .loads (content )
606+ installed_packages = data .get ('installed' )
607+ if not installed_packages :
608+ return
609+
610+ main_package_metadata = {}
611+ dependencies = []
612+
613+ for package_metadata in installed_packages :
614+ package_metadata_dep = package_metadata .get ('metadata' )
615+
616+ # `direct_url` is only present for root package
617+ # `requested` is true for root package and direct dependencies only
618+ if package_metadata .get ('requested' ) and 'direct_url' in package_metadata :
619+ main_package_metadata = package_metadata_dep
620+ main_package_requires = main_package_metadata .get ('requires_dist' )
621+ dependencies_for_main = get_requires_dependencies (
622+ requires = main_package_requires ,
623+ )
624+ dependencies .extend ([
625+ dep .to_dict ()
626+ for dep in dependencies_for_main
627+ ])
628+ continue
629+
630+ package_data_dep = cls .get_resolved_package_from_metadata (
631+ metadata = package_metadata_dep ,
632+ package_only = package_only ,
633+ )
634+ dep_purl = package_data_dep .purl
635+
636+ dependency = models .DependentPackage (
637+ purl = dep_purl ,
638+ extracted_requirement = None ,
639+ scope = None ,
640+ is_runtime = True ,
641+ is_optional = False ,
642+ is_direct = False ,
643+ is_resolved = True ,
644+ resolved_package = package_data_dep .to_dict ()
645+ )
646+ dependencies .append (dependency .to_dict ())
647+
648+ pip_version = data .get ('pip_version' )
649+ inspect_version = data .get ('version' )
650+ extra_data = {
651+ "pip_version" : pip_version ,
652+ "inspect_version" : inspect_version ,
653+ }
654+
655+ package_data_main = cls .get_resolved_package_from_metadata (
656+ metadata = main_package_metadata ,
657+ package_only = package_only ,
658+ )
659+ package_data_main .dependencies = dependencies
660+ package_data_main .extra_data = extra_data
661+ yield package_data_main
566662
567663
568664META_DIR_SUFFIXES = '.dist-info' , '.egg-info' , 'EGG-INFO' ,
@@ -1494,7 +1590,7 @@ def get_dist_dependencies(dist):
14941590 return get_requires_dependencies (requires = dist .requires )
14951591
14961592
1497- def get_requires_dependencies (requires , default_scope = 'install' ):
1593+ def get_requires_dependencies (requires , default_scope = 'install' , is_direct = True ):
14981594 """
14991595 Return a list of DependentPackage found in a ``requires`` list of
15001596 requirement strings or an empty list.
@@ -1539,6 +1635,7 @@ def get_requires_dependencies(requires, default_scope='install'):
15391635 is_runtime = True ,
15401636 is_optional = True if bool (extra ) else False ,
15411637 is_resolved = is_resolved ,
1638+ is_direct = is_direct ,
15421639 extracted_requirement = str (req ),
15431640 ))
15441641
0 commit comments