77# See https://aboutcode.org for more information about nexB OSS projects.
88#
99
10- import attr
1110import xmltodict
1211
13- from commoncode import filetype
1412from packagedcode import models
1513from packagedcode .utils import build_description
1614
17-
18- # TODO: add dependencies
19-
2015"""
21- Handle nuget.org Nuget packages.
16+ Handle NuGet packages and their manifests .
2217"""
18+ # TODO: add dependencies
2319
24- # Tracing flags
25- TRACE = False
26-
27-
28- def logger_debug (* args ):
29- pass
30-
31-
32- if TRACE :
33- import logging
34- import sys
35- logger = logging .getLogger (__name__ )
36- # logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
37- logging .basicConfig (stream = sys .stdout )
38- logger .setLevel (logging .DEBUG )
3920
40- def logger_debug (* args ):
41- return logger .debug (' ' .join (isinstance (a , str ) and a or repr (a ) for a in args ))
21+ def get_urls (name , version ):
22+ return dict (
23+ repository_homepage_url = f'https://www.nuget.org/packages/{ name } /{ version } ' ,
24+ repository_download_url = f'https://www.nuget.org/api/v2/package/{ name } /{ version } ' ,
25+ # the name is lowercased as in
26+ # https://api.nuget.org/v3/registration3/newtonsoft.json/10.0.1.json
27+ api_data_url = f'https://api.nuget.org/v3/registration3/{ name .lower ()} /{ version } .json' ,
28+ )
4229
4330
44- @attr .s ()
45- class NugetPackageData (models .PackageData ):
46- # file_patterns = ('[Content_Types].xml', '*.nuspec',)
31+ class NugetNupkgHandler (models .NonAssemblableDatafileHandler ):
32+ datasource_id = 'nuget_nupkg'
33+ path_patterns = ('*.nupkg' ,)
34+ default_package_type = 'nuget'
4735 filetypes = ('zip archive' , 'microsoft ooxml' ,)
48- mimetypes = ('application/zip' , 'application/octet-stream' ,)
49- # extensions = ('.nupkg',)
50-
51- default_type = 'nuget'
52- default_web_baseurl = 'https://www.nuget.org/packages/'
53- default_download_baseurl = 'https://www.nuget.org/api/v2/package/'
54- default_api_baseurl = 'https://api.nuget.org/v3/registration3/'
36+ description = 'NuGet nupkg package archive'
37+ documentation_url = 'https://en.wikipedia.org/wiki/Open_Packaging_Conventions'
5538
56- @classmethod
57- def get_package_root (cls , manifest_resource , codebase ):
58- if manifest_resource .name .endswith ('.nupkg' ):
59- return manifest_resource
60- if manifest_resource .name .endswith (('[Content_Types].xml' , '.nuspec' ,)):
61- return manifest_resource .parent (codebase )
62- return manifest_resource
63-
64- def repository_homepage_url (self , baseurl = default_web_baseurl ):
65- return baseurl + '{name}/{version}' .format (
66- name = self .name , version = self .version )
67-
68- def repository_download_url (self , baseurl = default_download_baseurl ):
69- return baseurl + '{name}/{version}' .format (
70- name = self .name , version = self .version )
71-
72- def api_data_url (self , baseurl = default_api_baseurl ):
73- # the name is lowercased
74- # https://api.nuget.org/v3/registration3/newtonsoft.json/10.0.1.json
75- return baseurl + '{name}/{version}.json' .format (
76- name = self .name .lower (), version = self .version )
77-
78-
79- nuspec_tags = [
80- 'id' ,
81- 'version' ,
82- 'title' ,
83- 'authors' ,
84- 'owners' ,
85- 'licenseUrl' ,
86- 'projectUrl' ,
87- 'requireLicenseAcceptance' ,
88- 'description' ,
89- 'summary' ,
90- 'releaseNotes' ,
91- 'copyright' ,
92- 'repository/@type' ,
93- 'repository/@url' ,
94- ]
95-
96-
97- @attr .s ()
98- class Nuspec (NugetPackageData , models .PackageDataFile ):
99-
100- file_patterns = ('*.nuspec' ,)
101- extensions = ('.nuspec' ,)
10239
103- @ classmethod
104- def is_package_data_file ( cls , location ):
105- """
106- Return True if the file at ``location`` is likely a manifest of this type.
107- """
108- return filetype . is_file ( location ) and location . endswith ( '. nuspec')
40+ class NugetNuspecHandler ( models . DatafileHandler ):
41+ datasource_id = 'nuget_nupsec'
42+ path_patterns = ( '*.nuspec' ,)
43+ default_package_type = 'nuget'
44+ description = 'NuGet nuspec package manifest'
45+ documentation_url = 'https://docs.microsoft.com/en-us/nuget/reference/ nuspec'
10946
11047 @classmethod
111- def recognize (cls , location ):
112- """
113- Yield one or more Package manifest objects given a file ``location`` pointing to a
114- package archive, manifest or similar.
115- """
48+ def parse (cls , location ):
11649 with open (location , 'rb' ) as loc :
11750 parsed = xmltodict .parse (loc )
11851
119- if TRACE :
120- logger_debug ('parsed:' , parsed )
12152 if not parsed :
12253 return
12354
124- pack = parsed .get ('package' , {} ) or {}
55+ pack = parsed .get ('package' ) or {}
12556 nuspec = pack .get ('metadata' )
12657 if not nuspec :
12758 return
12859
129- name = nuspec .get ('id' )
130- version = nuspec .get ('version' )
60+ name = nuspec .get ('id' )
61+ version = nuspec .get ('version' )
13162
13263 # Summary: A short description of the package for UI display. If omitted, a
13364 # truncated version of description is used.
@@ -149,37 +80,35 @@ def recognize(cls, location):
14980 if owners :
15081 parties .append (models .Party (name = owners , role = 'owner' ))
15182
83+ vcs_url = None
84+
15285 repo = nuspec .get ('repository' ) or {}
153- vcs_tool = repo .get ('@type' ) or ''
15486 vcs_repository = repo .get ('@url' ) or ''
155- vcs_url = None
15687 if vcs_repository :
88+ vcs_tool = repo .get ('@type' ) or ''
15789 if vcs_tool :
158- vcs_url = '{ }+{}' . format ( vcs_tool , vcs_repository )
90+ vcs_url = f' { vcs_tool } +{ vcs_repository } '
15991 else :
16092 vcs_url = vcs_repository
16193
162- yield cls (
94+ urls = get_urls (name , version )
95+
96+ package_data = models .PackageData (
97+ datasource_id = cls .datasource_id ,
98+ type = cls .default_package_type ,
16399 name = name ,
164100 version = version ,
165101 description = description or None ,
166102 homepage_url = nuspec .get ('projectUrl' ) or None ,
167103 parties = parties ,
104+ # FIXME: license has evolved and is now SPDX...
168105 declared_license = nuspec .get ('licenseUrl' ) or None ,
169106 copyright = nuspec .get ('copyright' ) or None ,
170107 vcs_url = vcs_url ,
108+ ** urls ,
171109 )
172110
111+ if not package_data .license_expression and package_data .declared_license :
112+ package_data .license_expression = cls .compute_normalized_license (package_data )
173113
174- @attr .s ()
175- class NugetPackage (NugetPackageData , models .Package ):
176- """
177- A Nuget Package that is created out of one/multiple nuget package
178- manifests.
179- """
180-
181- @property
182- def manifests (self ):
183- return [
184- Nuspec
185- ]
114+ yield package_data
0 commit comments