@@ -563,6 +563,102 @@ def parse(cls, location, package_only=False):
563
563
yield models .PackageData .from_data (package_data , package_only )
564
564
565
565
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
566
662
567
663
568
664
META_DIR_SUFFIXES = '.dist-info' , '.egg-info' , 'EGG-INFO' ,
@@ -1494,7 +1590,7 @@ def get_dist_dependencies(dist):
1494
1590
return get_requires_dependencies (requires = dist .requires )
1495
1591
1496
1592
1497
- def get_requires_dependencies (requires , default_scope = 'install' ):
1593
+ def get_requires_dependencies (requires , default_scope = 'install' , is_direct = True ):
1498
1594
"""
1499
1595
Return a list of DependentPackage found in a ``requires`` list of
1500
1596
requirement strings or an empty list.
@@ -1539,6 +1635,7 @@ def get_requires_dependencies(requires, default_scope='install'):
1539
1635
is_runtime = True ,
1540
1636
is_optional = True if bool (extra ) else False ,
1541
1637
is_resolved = is_resolved ,
1638
+ is_direct = is_direct ,
1542
1639
extracted_requirement = str (req ),
1543
1640
))
1544
1641
0 commit comments