Skip to content

Commit a6017f5

Browse files
committed
Refine NuGet nuspec dependency extraction
Factor out common code in function, also include framework and set dependency flags accordingly. Reference: #2308 Reference: #648 Signed-off-by: Philippe Ombredanne <[email protected]>
1 parent 60a14ec commit a6017f5

File tree

7 files changed

+853
-508
lines changed

7 files changed

+853
-508
lines changed

src/packagedcode/nuget.py

Lines changed: 61 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -8,96 +8,75 @@
88
#
99

1010
import xmltodict
11+
from packageurl import PackageURL
1112

1213
from packagedcode import models
1314
from packagedcode.utils import build_description
1415

15-
1616
"""
1717
Handle NuGet packages and their manifests.
1818
"""
1919

20-
# TODO: Add section to handle dependencies of dependencies
21-
2220

2321
def get_dependencies(nuspec):
2422
"""
25-
Return a list of Dependent package objects found in a NuGet `nuspec` object.
23+
Yield DependentPackage found in a NuGet ``nuspec`` object.
24+
"""
25+
# This is either a list of dependency or a list of group/dependency
26+
# or a single dep or a single group mapping
27+
28+
dependencies = nuspec.get('dependencies') or []
29+
if isinstance(dependencies, dict):
30+
# wrap the mapping in a list if we have more than one dependencies
31+
dependencies = [dependencies]
32+
33+
for depends in dependencies:
34+
groups = depends.get('group') or []
35+
if groups:
36+
if isinstance(groups, dict):
37+
# wrap the mapping in a list
38+
groups = [groups]
39+
40+
for group in groups:
41+
extra_data = dict(framework=group['@targetFramework'])
42+
deps = group.get('dependency') or []
43+
yield from _get_dep_packs(deps, extra_data)
44+
else:
45+
# {'dependency': {'@id': 'jQuery', '@version': '1.9.1'}}
46+
deps = depends.get('dependency') or []
47+
yield from _get_dep_packs(deps, extra_data={})
48+
49+
50+
def _get_dep_packs(deps, extra_data):
2651
"""
27-
dependencies = []
28-
try:
29-
if "dependencies" in nuspec:
30-
if "group" in nuspec["dependencies"]:
31-
for group in nuspec["dependencies"]["group"]:
32-
if "dependency" in group:
33-
for dependency in group["dependency"]:
34-
#dependencies.append(dependency)
35-
dpurl = models.PackageURL(
36-
type='nuget',
37-
namespace=None,
38-
name=dependency.get("@id"),
39-
version=dependency.get("@version"),
40-
qualifiers=None
41-
)
42-
dep_pack = models.DependentPackage(
43-
purl=str(dpurl),
44-
extracted_requirement=dependency.get("@version"),
45-
scope="dependency",
46-
is_runtime=False,
47-
is_optional=False,
48-
is_resolved=True,
49-
)
50-
51-
dependencies.append(dep_pack)
52-
53-
if "dependency" in nuspec.get("dependencies"):
54-
if "@id" and "@version" in nuspec.get("dependencies").get("dependency"):
55-
dpurl = models.PackageURL(
56-
type='nuget',
57-
namespace=None,
58-
name=nuspec.get("dependencies").get(
59-
"dependency").get("@id"),
60-
version=nuspec.get("dependencies").get(
61-
"dependency").get("@version"),
62-
qualifiers=None
63-
)
64-
dep_pack = models.DependentPackage(
65-
purl=str(dpurl),
66-
extracted_requirement=nuspec.get("dependencies").get(
67-
"dependency").get("@version"),
68-
scope="dependency",
69-
is_runtime=False,
70-
is_optional=False,
71-
is_resolved=True,
72-
)
73-
74-
dependencies.append(dep_pack)
75-
76-
else:
77-
for dependency in nuspec.get("dependencies").get("dependency"):
78-
dpurl = models.PackageURL(
79-
type='nuget',
80-
namespace=None,
81-
name=dependency.get("@id"),
82-
version=dependency.get("@version"),
83-
qualifiers=None
84-
)
85-
dep_pack = models.DependentPackage(
86-
purl=str(dpurl),
87-
extracted_requirement=dependency.get(
88-
"@version"),
89-
scope="dependency",
90-
is_runtime=False,
91-
is_optional=False,
92-
is_resolved=True,
93-
)
94-
dependencies.append(dep_pack)
95-
96-
return dependencies
97-
98-
except Exception as e:
99-
print(e)
100-
return dependencies
52+
Yield DependentPackage found in a NuGet ``deps`` mapping or list of mappings.
53+
"""
54+
if not deps:
55+
return
56+
57+
if isinstance(deps, dict):
58+
# wrap the mapping in a list
59+
deps = [deps]
60+
61+
for dep in deps:
62+
extra = dict(extra_data) or {}
63+
include = dep.get('@include')
64+
if include:
65+
extra['include'] = include
66+
exclude = dep.get('@exclude')
67+
if exclude:
68+
extra['exclude'] = exclude
69+
70+
yield models.DependentPackage(
71+
purl=str(PackageURL(type='nuget', name=dep.get('@id'))),
72+
# this is a range, not a version
73+
extracted_requirement=dep.get('@version'),
74+
scope='dependency',
75+
is_runtime=True,
76+
is_optional=False,
77+
is_resolved=False,
78+
extra_data=extra,
79+
)
10180

10281

10382
def get_urls(name, version, **kwargs):
@@ -144,15 +123,14 @@ def parse(cls, location):
144123

145124
# Summary: A short description of the package for UI display. If omitted, a
146125
# truncated version of description is used.
147-
description = build_description(
148-
nuspec.get('summary'), nuspec.get('description'))
126+
description = build_description(nuspec.get('summary'), nuspec.get('description'))
149127

150128
# title: A human-friendly title of the package, typically used in UI
151129
# displays as on nuget.org and the Package Manager in Visual Studio. If not
152130
# specified, the package ID is used.
153131
title = nuspec.get('title')
154132
if title and title != name:
155-
description = build_description(nuspec.get('title'), description)
133+
description = build_description(title, description)
156134

157135
parties = []
158136
authors = nuspec.get('authors')
@@ -181,11 +159,10 @@ def parse(cls, location):
181159
# This is a SPDX license expression
182160
if 'license' in nuspec:
183161
extracted_license_statement = nuspec.get('license')
184-
# TODO: try to convert to normal license expression
185162
# Deprecated and not a license expression, just a URL
186163
elif 'licenseUrl' in nuspec:
187164
extracted_license_statement = nuspec.get('licenseUrl')
188-
165+
189166
yield models.PackageData(
190167
datasource_id=cls.datasource_id,
191168
type=cls.default_package_type,
@@ -194,7 +171,7 @@ def parse(cls, location):
194171
description=description or None,
195172
homepage_url=nuspec.get('projectUrl') or None,
196173
parties=parties,
197-
dependencies=get_dependencies(nuspec),
174+
dependencies=list(get_dependencies(nuspec)),
198175
extracted_license_statement=extracted_license_statement,
199176
copyright=nuspec.get('copyright') or None,
200177
vcs_url=vcs_url,

0 commit comments

Comments
 (0)