77# See https://aboutcode.org for more information about nexB OSS projects.
88#
99
10- import logging
11- import sys
1210import warnings
1311
14- import attr
1512import saneyaml
16- from commoncode import filetype
1713from packageurl import PackageURL
1814
1915from packagedcode import models
2016from packagedcode .utils import combine_expressions
21-
22- TRACE = False
23-
24-
25- def logger_debug (* args ):
26- pass
27-
28-
29- logger = logging .getLogger (__name__ )
30-
31- if TRACE :
32- logging .basicConfig (stream = sys .stdout )
33- logger .setLevel (logging .DEBUG )
34-
35- def logger_debug (* args ):
36- return logger .debug ('' .join (isinstance (a , str ) and a or repr (a ) for a in args ))
37-
3817"""
3918Collect data from Dart pub packages.
4019See https://dart.dev/tools/pub/pubspec
41- """
4220
43- """
4421TODO:
4522- license is only in a LICENSE file
4623 https://dart.dev/tools/pub/publishing#preparing-to-publish
@@ -56,34 +33,51 @@ def logger_debug(*args):
5633See https://github.com/dart-lang/pub/blob/master/doc/repository-spec-v2.md
5734"""
5835
36+ # FIXME: warnings reported here DO NOT work. We should have a better way
5937
60- @attr .s ()
61- class PubspecPackageData (models .PackageData ):
62- default_type = 'pubspec'
63- default_primary_language = 'dart'
64- default_web_baseurl = 'https://pub.dev/packages'
65- default_download_baseurl = 'https://pub.dartlang.org/packages'
66- default_api_baseurl = 'https://pub.dev/api/packages'
6738
68- def repository_homepage_url (self , baseurl = default_web_baseurl ):
69- return f'{ baseurl } /{ self .name } /versions/{ self .version } '
39+ class BaseDartPubspecHandler (models .DatafileHandler ):
40+
41+ @classmethod
42+ def assemble (cls , package_data , resource , codebase ):
43+ datafile_name_patterns = \
44+ DartPubspecYamlHandler .path_patterns + DartPubspecLockHandler .path_patterns
45+
46+ if resource .has_parent ():
47+ dir_resource = resource .parent (codebase )
48+ else :
49+ dir_resource = resource
7050
71- def repository_download_url (self , baseurl = default_download_baseurl ):
72- # A URL should be in the form of:
73- # https://pub.dartlang.org/packages/url_launcher/versions/6.0.9.tar.gz
74- # And it may resolve to:
75- # https://storage.googleapis.com/pub-packages/packages/http-0.13.2.tar.gz
76- # as seen in the pub.dev web pages
77- return f'{ baseurl } /{ self .name } /versions/{ self .version } .tar.gz'
51+ yield from cls .assemble_from_many_datafiles (
52+ datafile_name_patterns = datafile_name_patterns ,
53+ directory = dir_resource ,
54+ codebase = codebase ,
55+ )
7856
79- def api_data_url (self , baseurl = default_api_baseurl ):
80- return f'{ baseurl } /{ self .name } /versions/{ self .version } '
57+ @classmethod
58+ def compute_normalized_license (cls , package ):
59+ return compute_normalized_license (package .declared_license )
8160
82- def compute_normalized_license (self ):
83- return compute_normalized_license (self .declared_license )
8461
62+ class DartPubspecYamlHandler (BaseDartPubspecHandler ):
63+ datasource_id = 'pubspec_yaml'
64+ path_patterns = ('*pubspec.yaml' ,)
65+ default_package_type = 'pubspec'
66+ default_primary_language = 'dart'
67+ description = 'Dart pubspec manifest'
68+ documentation_url = 'https://dart.dev/tools/pub/pubspec'
8569
86- def compute_normalized_license (declared_license , location = None ):
70+ @classmethod
71+ def parse (cls , location ):
72+ with open (location ) as inp :
73+ pubspec_data = saneyaml .load (inp .read ())
74+
75+ package_data = build_package (pubspec_data )
76+ if package_data :
77+ yield package_data
78+
79+
80+ def compute_normalized_license (declared_license ):
8781 """
8882 Return a normalized license expression string detected from a list of
8983 declared license items.
@@ -106,79 +100,27 @@ def compute_normalized_license(declared_license, location=None):
106100 return combine_expressions (detected_licenses )
107101
108102
109- @attr .s ()
110- class PubspecYaml (PubspecPackageData , models .PackageDataFile ):
111-
112- file_patterns = ('*pubspec.yaml' ,)
113- extensions = ('.yaml' ,)
114-
115- @classmethod
116- def is_package_data_file (cls , location ):
117- """
118- Return True if the file at ``location`` is likely a manifest of this type.
119- """
120- return file_endswith (location , 'pubspec.yaml' )
121-
122- @classmethod
123- def recognize (cls , location , compute_normalized_license = False ):
124- """
125- Yield one or more Package manifest objects given a file ``location`` pointing to a
126- package archive, manifest or similar.
127- """
128- with open (location ) as inp :
129- package_data = saneyaml .load (inp .read ())
130-
131- package = build_package (cls , package_data )
132- if package and compute_normalized_license :
133- package .compute_normalized_license ()
134- yield package
135-
136-
137- def file_endswith (location , endswith ):
138- """
139- Check if the file at ``location`` ends with ``endswith`` string or tuple.
140- """
141- return filetype .is_file (location ) and location .endswith (endswith )
142-
143-
144- @attr .s ()
145- class PubspecLock (PubspecPackageData , models .PackageDataFile ):
146-
147- file_patterns = ('*pubspec.lock' ,)
148- extensions = ('.lock' ,)
149-
150- @classmethod
151- def is_package_data_file (cls , location ):
152- """
153- Return True if the file at ``location`` is likely a manifest of this type.
154- """
155- return file_endswith (location , 'pubspec.lock' )
103+ class DartPubspecLockHandler (BaseDartPubspecHandler ):
104+ datasource_id = 'pubspec_lock'
105+ path_patterns = ('*pubspec.lock' ,)
106+ default_package_type = 'pubspec'
107+ default_primary_language = 'dart'
108+ description = 'Dart pubspec lockfile'
109+ documentation_url = 'https://web.archive.org/web/20220330081004/https://gpalma.pt/blog/what-is-the-pubspec-lock/'
156110
157111 @classmethod
158- def recognize (cls , location ):
159- """
160- Yield one or more Package manifest objects given a file ``location`` pointing to a
161- package archive, manifest or similar.
162- """
112+ def parse (cls , location ):
163113 with open (location ) as inp :
164114 locks_data = saneyaml .load (inp .read ())
165115
166- yield cls (dependencies = list (collect_locks (locks_data )))
167-
168-
169- @attr .s ()
170- class PubspecPackage (PubspecPackageData , models .Package ):
171- """
172- A Pubspec Package that is created out of one/multiple pubspec package
173- manifests.
174- """
116+ dependencies = list (collect_locks (locks_data ))
175117
176- @ property
177- def manifests ( self ):
178- return [
179- PubspecYaml ,
180- PubspecLock
181- ]
118+ yield models . PackageData (
119+ datasource_id = cls . datasource_id ,
120+ type = cls . default_package_type ,
121+ primary_language = cls . default_primary_language ,
122+ dependencies = dependencies
123+ )
182124
183125
184126def collect_locks (locks_data ):
@@ -303,10 +245,17 @@ def build_dep(name, version, scope, is_runtime=True, is_optional=False):
303245
304246 if version .replace ('.' , '' ).isdigit ():
305247 # version is pinned exactly if it is only made of dots and digits
306- purl = PackageURL (type = 'pubspec' , name = name , version = version )
248+ purl = PackageURL (
249+ type = 'pubspec' ,
250+ name = name ,
251+ version = version ,
252+ )
307253 is_resolved = True
308254 else :
309- purl = PackageURL (type = 'pubspec' , name = name )
255+ purl = PackageURL (
256+ type = 'pubspec' ,
257+ name = name ,
258+ )
310259 is_resolved = False
311260
312261 dep = models .DependentPackage (
@@ -320,7 +269,7 @@ def build_dep(name, version, scope, is_runtime=True, is_optional=False):
320269 return dep
321270
322271
323- def build_package (cls , pubspec_data ):
272+ def build_package (pubspec_data ):
324273 """
325274 Return a package object from a package data mapping or None
326275 """
@@ -332,6 +281,18 @@ def build_package(cls, pubspec_data):
332281 vcs_url = pubspec_data .get ('repository' )
333282 download_url = pubspec_data .get ('archive_url' )
334283
284+ api_data_url = name and version and f'https://pub.dev/api/packages/{ name } /versions/{ version } '
285+ repository_homepage_url = name and version and f'https://pub.dev/packages/{ name } /versions/{ version } '
286+
287+ # A URL should be in the form of:
288+ # https://pub.dartlang.org/packages/url_launcher/versions/6.0.9.tar.gz
289+ # And it may resolve to:
290+ # https://storage.googleapis.com/pub-packages/packages/http-0.13.2.tar.gz
291+ # as seen in the pub.dev web pages
292+ repository_download_url = name and version and f'https://pub.dartlang.org/packages/{ name } /versions/{ version } .tar.gz'
293+
294+ download_url = download_url or repository_download_url
295+
335296 # Author and authors are deprecated
336297 authors = []
337298 author = pubspec_data .get ('author' )
@@ -385,20 +346,26 @@ def add_to_extra_if_present(_key):
385346 add_to_extra_if_present ('executables' )
386347 add_to_extra_if_present ('publish_to' )
387348
388- package = cls (
349+ package = models .PackageData (
350+ datasource_id = DartPubspecYamlHandler .datasource_id ,
351+ type = DartPubspecYamlHandler .default_primary_language ,
352+ primary_language = DartPubspecYamlHandler .default_primary_language ,
389353 name = name ,
390354 version = version ,
355+ download_url = download_url ,
391356 vcs_url = vcs_url ,
392357 description = description ,
393358 declared_license = declared_license ,
394359 parties = parties ,
395360 homepage_url = homepage_url ,
396361 dependencies = package_dependencies ,
397362 extra_data = extra_data ,
363+ repository_homepage_url = repository_homepage_url ,
364+ api_data_url = api_data_url ,
365+ repository_download_url = repository_download_url ,
398366 )
399367
400- if not download_url :
401- package .download_url = package . repository_download_url ( )
368+ if not package . license_expression and package . declared_license :
369+ package .license_expression = models . compute_normalized_license ( package . declared_license )
402370
403371 return package
404-
0 commit comments