Skip to content

Commit 3e11778

Browse files
Add is_direct attribute to dependency model #3780
Adds is_direct attribute to differentiate between direct dependecy relationships and dependencies listed in lockfiles which have both direct and transitive dependencies together, which will have is_direct as False. Reference: #3780 Signed-off-by: Ayan Sinha Mahapatra <[email protected]>
1 parent 69ea6b7 commit 3e11778

File tree

7 files changed

+1275
-1
lines changed

7 files changed

+1275
-1
lines changed

src/packagedcode/models.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,14 @@ class DependentPackage(ModelMixin):
371371
'been resolved and this dependency url points to an '
372372
'exact version.')
373373

374+
is_direct = Boolean(
375+
default=True,
376+
label='is direct flag',
377+
help='True if this dependency version requirement is '
378+
'a direct dependency relation between two packages '
379+
'as opposed to a transitive dependency relation, '
380+
'which are present in lockfiles/dependency list.')
381+
374382
resolved_package = Mapping(
375383
label='resolved package data',
376384
help='A mapping of resolved package data for this dependent package, '
@@ -1072,6 +1080,17 @@ def is_datafile(cls, location, filetypes=tuple(), _bare_filename=False):
10721080
actual_type = T.filetype_file.lower()
10731081
return any(ft in actual_type for ft in filetypes)
10741082

1083+
@classmethod
1084+
def is_lockfile(cls):
1085+
"""
1086+
Return True if this is a lockfile, False otherwise.
1087+
1088+
This has to be implemented by datafile handlers classes
1089+
of lockfiles, to return True, in contrast to the default
1090+
value False.
1091+
"""
1092+
return False
1093+
10751094
@classmethod
10761095
def parse(cls, location, package_only=False):
10771096
"""

src/packagedcode/npm.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ def update_dependencies_by_purl(
204204
is_runtime=False,
205205
is_optional=False,
206206
is_resolved=False,
207+
is_direct=True,
207208
):
208209

209210
metadata_deps = ['peerDependenciesMeta', 'dependenciesMeta']
@@ -221,6 +222,7 @@ def update_dependencies_by_purl(
221222
is_runtime=is_runtime,
222223
is_optional=is_optional,
223224
is_resolved=is_resolved,
225+
is_direct=is_direct,
224226
)
225227
dependecies_by_purl[dep_purl] = dep_package
226228

@@ -244,6 +246,7 @@ def update_dependencies_by_purl(
244246
is_runtime=is_runtime,
245247
is_optional=metadata.get("optional"),
246248
is_resolved=is_resolved,
249+
is_direct=is_direct,
247250
)
248251
dependecies_by_purl[dep_purl] = dep_package
249252
continue
@@ -264,6 +267,7 @@ def update_dependencies_by_purl(
264267
is_runtime=is_runtime,
265268
is_optional=is_optional,
266269
is_resolved=is_resolved,
270+
is_direct=is_direct,
267271
)
268272
dependecies_by_purl[dep_purl] = dep_package
269273

@@ -476,6 +480,10 @@ def parse(cls, location, package_only=False):
476480

477481
class BaseNpmLockHandler(BaseNpmHandler):
478482

483+
@classmethod
484+
def is_lockfile(cls):
485+
return True
486+
479487
@classmethod
480488
def parse(cls, location, package_only=False):
481489

@@ -590,6 +598,7 @@ def parse(cls, location, package_only=False):
590598
is_runtime=is_runtime,
591599
is_optional=is_optional,
592600
is_resolved=True,
601+
is_direct=False,
593602
)
594603

595604
# URLs and checksums
@@ -638,6 +647,7 @@ def parse(cls, location, package_only=False):
638647
is_runtime=is_runtime,
639648
is_optional=is_optional,
640649
is_resolved=False,
650+
is_direct=True,
641651
)
642652

643653
resolved_package.dependencies = [
@@ -723,6 +733,10 @@ class YarnLockV2Handler(BaseNpmHandler):
723733
def is_datafile(cls, location, filetypes=tuple()):
724734
return super().is_datafile(location, filetypes=filetypes) and is_yarn_v2(location)
725735

736+
@classmethod
737+
def is_lockfile(cls):
738+
return True
739+
726740
@classmethod
727741
def parse(cls, location, package_only=False):
728742
"""
@@ -833,6 +847,10 @@ class YarnLockV1Handler(BaseNpmHandler):
833847
description = 'yarn.lock lockfile v1 format'
834848
documentation_url = 'https://classic.yarnpkg.com/lang/en/docs/yarn-lock/'
835849

850+
@classmethod
851+
def is_lockfile(cls):
852+
return True
853+
836854
@classmethod
837855
def is_datafile(cls, location, filetypes=tuple()):
838856
return super().is_datafile(location, filetypes=filetypes) and not is_yarn_v2(location)
@@ -953,6 +971,7 @@ def parse(cls, location, package_only=False):
953971
scope='dependencies',
954972
is_optional=False,
955973
is_runtime=True,
974+
is_direct=True,
956975
)
957976
resolved_package_data.dependencies.append(subdep)
958977

@@ -972,6 +991,7 @@ def parse(cls, location, package_only=False):
972991
scope='dependencies',
973992
is_optional=False,
974993
is_runtime=True,
994+
is_direct=False,
975995
resolved_package=resolved_package_data.to_dict(),
976996
)
977997
dependencies.append(dep.to_dict())
@@ -988,6 +1008,10 @@ def parse(cls, location, package_only=False):
9881008

9891009
class BasePnpmLockHandler(BaseNpmHandler):
9901010

1011+
@classmethod
1012+
def is_lockfile(cls):
1013+
return True
1014+
9911015
@classmethod
9921016
def parse(cls, location, package_only=False):
9931017
"""
@@ -1063,19 +1087,22 @@ def parse(cls, location, package_only=False):
10631087
scope='dependencies',
10641088
dependecies_by_purl=deps_for_resolved_by_purl,
10651089
is_resolved=True,
1090+
is_direct=False,
10661091
)
10671092
cls.update_dependencies_by_purl(
10681093
dependencies=peer_dependencies,
10691094
scope='peerDependencies',
10701095
dependecies_by_purl=deps_for_resolved_by_purl,
10711096
is_optional=True,
1097+
is_direct=False,
10721098
)
10731099
cls.update_dependencies_by_purl(
10741100
dependencies=optional_dependencies,
10751101
scope='optionalDependencies',
10761102
dependecies_by_purl=deps_for_resolved_by_purl,
10771103
is_resolved=True,
10781104
is_optional=True,
1105+
is_direct=False,
10791106
)
10801107
cls.update_dependencies_by_purl(
10811108
dependencies=peer_dependencies_meta,
@@ -1122,6 +1149,7 @@ def parse(cls, location, package_only=False):
11221149
is_optional=is_optional,
11231150
is_runtime=is_runtime,
11241151
is_resolved=True,
1152+
is_direct=True,
11251153
resolved_package=resolved_package.to_dict(),
11261154
extra_data=extra_data_deps,
11271155
)
@@ -1577,7 +1605,7 @@ def bundle_deps_mapper(bundle_deps, package):
15771605
return package
15781606

15791607

1580-
def deps_mapper(deps, package, field_name):
1608+
def deps_mapper(deps, package, field_name, is_direct=True):
15811609
"""
15821610
Handle deps such as dependencies, devDependencies, peerDependencies, optionalDependencies
15831611
return a tuple of (dep type, list of deps)
@@ -1630,6 +1658,7 @@ def deps_mapper(deps, package, field_name):
16301658
purl=purl,
16311659
scope=field_name,
16321660
extracted_requirement=requirement,
1661+
is_direct=is_direct,
16331662
**dependency_attributes
16341663
)
16351664
dependencies.append(dep)

0 commit comments

Comments
 (0)