13
13
import packageurl
14
14
15
15
"""Preview of High-Level, object oriented Python interface to the SW360 REST API.
16
- For now, this does NOT strive to be stable or complete. Feel free to use it as
17
- a more convenient abstraction for some (important) objects, but be prepared for
18
- changes."""
16
+ For now, this only allows read access and does NOT strive to be stable or complete.
17
+ Feel free to use it as a more convenient abstraction for some (important) objects,
18
+ but be prepared for API changes."""
19
19
20
20
21
21
class SW360Resource :
@@ -25,22 +25,22 @@ class SW360Resource:
25
25
26
26
all_resources = {}
27
27
28
- def __new__ (cls , json = None , resource_id = None , ** kwargs ):
28
+ def __new__ (cls , json = None , id_ = None , ** kwargs ):
29
29
"""Check if this resource already exists."""
30
30
key = None
31
- if resource_id :
32
- key = (cls .__name__ , resource_id )
31
+ if id_ :
32
+ key = (cls .__name__ , id_ )
33
33
elif json and "id" in json :
34
34
key = (cls .__name__ , json ["id" ])
35
35
36
36
if key :
37
- if key not in cls .all_releases :
37
+ if key not in cls .all_resources :
38
38
cls .all_resources [key ] = super (SW360Resource , cls ).__new__ (cls )
39
39
return cls .all_resources [key ]
40
40
else :
41
41
return super (SW360Resource , cls ).__new__ (cls )
42
42
43
- def __init__ (self , json = None , resource_id = None , parent = None , users = {}, sw360 = None , ** kwargs ):
43
+ def __init__ (self , json = None , id_ = None , parent = None , users = {}, sw360 = None , ** kwargs ):
44
44
if not hasattr (self , "details" ):
45
45
self .details = {}
46
46
"""All resource details which are not explicitely supported by the
@@ -51,9 +51,9 @@ def __init__(self, json=None, resource_id=None, parent=None, users={}, sw360=Non
51
51
if not hasattr (self , "users" ):
52
52
self .users = {}
53
53
54
- if resource_id and getattr (self , "id" , resource_id ) != resource_id :
55
- raise ValueError ("Resource id mismatch" , self .id , resource_id )
56
- self .id = resource_id
54
+ if id_ and getattr (self , "id" , id_ ) != id_ :
55
+ raise ValueError ("Resource id mismatch" , self .id , id_ )
56
+ self .id = id_
57
57
"""All SW360 resource instances have an `id`. If it is set to `None`,
58
58
the object is yet unknown to SW360 - otherwise, it stores the SW360
59
59
id (release_id, component_id, etc.)."""
@@ -82,14 +82,16 @@ def __setattr__(self, name, value):
82
82
super ().__setattr__ (name , value )
83
83
if name == "id" and value is not None :
84
84
key = (self .__class__ .__name__ , value )
85
+ if key in self .all_resources and self .all_resources [key ] != self :
86
+ raise ValueError ("Duplicate object detected for " , key )
85
87
self .all_resources [key ] = self
86
88
87
89
def _parse_release_list (self , json_list , parent = None , users = {}):
88
90
"""Parse a JSON list of releases, create according objects and add
89
91
them to `container`."""
90
92
releases = {}
91
93
for release_json in json_list :
92
- release = Release (parent = parent , users = users , sw360 = self .sw360 )
94
+ release = Release (id_ = release_json [ "id" ], parent = parent , users = users , sw360 = self .sw360 )
93
95
release .from_json (release_json )
94
96
releases [release .id ] = release
95
97
return releases
@@ -99,7 +101,9 @@ def _parse_attachment_list(self, json_list, parent=None):
99
101
them to `container`."""
100
102
attachments = {}
101
103
for attachment_json in json_list :
102
- attachment = Attachment (parent = parent , sw360 = self .sw360 )
104
+ # attachment id is not available here, so we need to extract it
105
+ attachment_id = attachment_json ["_links" ]["self" ]["href" ].split ("/" )[- 1 ]
106
+ attachment = Attachment (id_ = attachment_id , parent = parent , sw360 = self .sw360 )
103
107
attachment .from_json (attachment_json )
104
108
attachments [attachment .id ] = attachment
105
109
return attachments
@@ -109,15 +113,15 @@ def _parse_project_list(self, json_list, users={}):
109
113
them to `container`."""
110
114
projects = {}
111
115
for project_json in json_list :
112
- project = Project (users = users , sw360 = self .sw360 )
116
+ project = Project (id_ = project_json [ "id" ], users = users , sw360 = self .sw360 )
113
117
project .from_json (project_json )
114
118
projects [project .id ] = project
115
119
return projects
116
120
117
121
def _parse_link (self , key , links_key , links_value ):
118
122
"""Parse a _links or _embedded section in JSON"""
119
123
if links_key == "sw360:component" :
120
- self .parent = Component (component_id = links_value ["href" ].split ("/" )[- 1 ], sw360 = self .sw360 )
124
+ self .parent = Component (id_ = links_value ["href" ].split ("/" )[- 1 ], sw360 = self .sw360 )
121
125
elif links_key == "sw360:downloadLink" :
122
126
self .download_link = links_value ["href" ]
123
127
elif links_key == "sw360:attachments" :
@@ -200,7 +204,7 @@ def __repr__(self):
200
204
or k .endswith ("_id" )):
201
205
repr_ .append (f'{ k } ={ v !r} ' )
202
206
if k == "id" :
203
- repr_ .append (f'{ self . __class__ . __name__ . lower () } _id ={ v !r} ' )
207
+ repr_ .append (f'id_ ={ v !r} ' )
204
208
return (f'{ self .__class__ .__name__ } ('
205
209
+ ", " .join (repr_ )
206
210
+ ")" )
@@ -209,10 +213,10 @@ def __repr__(self):
209
213
class Release (SW360Resource ):
210
214
"""A release is the SW360 abstraction for a single version of a component.
211
215
212
- You can either create it from a SW360 `json` object or by specifying the
213
- details via the constructor parameters, see list below. Only the most
214
- important attributes are supported, rest hast be provided via `kwargs` and
215
- is stored in the `details` attribute of instances.
216
+ You can either create it by using `get`, from a SW360 `json` object or by
217
+ creating a fresh instance. The list below describes supported attributes.
218
+ Only the most important ones are supported, rest hast be provided via
219
+ `kwargs` and is stored in the `details` attribute of instances.
216
220
217
221
For JSON parsing, please read documentation of from_json() method.
218
222
@@ -222,19 +226,19 @@ class Release(SW360Resource):
222
226
(instances of Release() or Project() with id as key)
223
227
:param version: the actual version
224
228
:param downloadurl: URL the release was downloaded from
225
- :param release_id : id of the release (if exists in SW360 already)
229
+ :param id_ : id of the release (if exists in SW360 already)
226
230
:param sw360: your SW360 instance for interacting with the API
227
231
:param kwargs: additional relase details as specified in the SW360 REST API
228
232
:type json: SW360 JSON object
229
233
:type parent: Component() object
230
234
:type users: dictionary
231
235
:type version: string
232
236
:type downloadurl: string
233
- :type release_id : string
237
+ :type id_ : string
234
238
:type sw360: instance from SW360 class
235
239
:type kwargs: dictionary
236
240
"""
237
- def __init__ (self , json = None , release_id = None , parent = None , users = {},
241
+ def __init__ (self , json = None , id_ = None , parent = None , users = {},
238
242
name = None , version = None , downloadurl = None , sw360 = None , ** kwargs ):
239
243
self .attachments = {}
240
244
self .external_ids = {}
@@ -243,7 +247,7 @@ def __init__(self, json=None, release_id=None, parent=None, users={},
243
247
self .name = name
244
248
self .version = version
245
249
self .downloadurl = downloadurl
246
- super ().__init__ (json = json , resource_id = release_id , parent = parent , users = users ,
250
+ super ().__init__ (json = json , id_ = id_ , parent = parent , users = users ,
247
251
sw360 = sw360 , ** kwargs )
248
252
249
253
def from_json (self , json ):
@@ -263,14 +267,14 @@ def from_json(self, json):
263
267
json ,
264
268
copy_attributes = ("name" , "version" , "downloadurl" , "externalIds" ))
265
269
266
- def get (self , sw360 = None , id_ = None ):
267
- """Retrieve/update release from SW360."""
268
- if sw360 :
269
- self .sw360 = sw360
270
- if id_ :
271
- self .id = id_
270
+ @classmethod
271
+ def get (cls , sw360 , id_ ):
272
+ """Retrieve a release from SW360."""
273
+ return Release (id_ = id_ , json = sw360 .get_release (id_ ), sw360 = sw360 )
274
+
275
+ def update (self ):
276
+ """update release from SW360."""
272
277
self .from_json (self .sw360 .get_release (self .id ))
273
- return self
274
278
275
279
def __str__ (self ):
276
280
return f'{ self .name } { self .version } ({ self .id } )'
@@ -282,15 +286,15 @@ class Attachment(SW360Resource):
282
286
("SOURCE_SELF"), clearing reports ("CLEARING_REPORT") or CLI files
283
287
("COMPONENT_LICENSE_INFO_XML").
284
288
285
- You can either create it from a SW360 `json` object or by specifying the
286
- details via the constructor parameters, see list below. Only the most
287
- important attributes are supported, rest hast be provided via `kwargs` and
288
- is stored in the `details` attribute of instances.
289
+ You can either create it by using `get`, from a SW360 `json` object or by
290
+ creating a fresh instance. The list below describes supported attributes.
291
+ Only the most important ones are supported, rest hast be provided via
292
+ `kwargs` and is stored in the `details` attribute of instances.
289
293
290
294
For JSON parsing, please read documentation of from_json() method.
291
295
292
296
:param json: create it from SW360 JSON object by calling from_json()
293
- :param attachment_id : SW360 id of the attachment (if it exists already)
297
+ :param id_ : SW360 id of the attachment (if it exists already)
294
298
:param parent: SW360 resource (release, component or project) the attachment belongs to
295
299
:param filename: the filename of the attachment
296
300
:param sha1: SHA1 sum of the file to check its integrity
@@ -300,21 +304,21 @@ class Attachment(SW360Resource):
300
304
:param sw360: your SW360 instance for interacting with the API
301
305
:param kwargs: additional relase details as specified in the SW360 REST API
302
306
:type json: SW360 JSON object
303
- :type attachment_id : string
307
+ :type id_ : string
304
308
:type release_id: string
305
309
:type filename: string
306
310
:type sha1: string
307
311
:type attachment_type: string
308
312
:type sw360: instance from SW360 class
309
313
:type kwargs: dictionary
310
314
"""
311
- def __init__ (self , json = None , attachment_id = None , parent = None ,
315
+ def __init__ (self , json = None , id_ = None , parent = None ,
312
316
filename = None , sha1 = None , attachment_type = None , sw360 = None , ** kwargs ):
313
317
self .attachment_type = attachment_type
314
318
self .filename = filename
315
319
self .sha1 = sha1
316
320
self .download_link = None
317
- super ().__init__ (json = json , resource_id = attachment_id , parent = parent ,
321
+ super ().__init__ (json = json , id_ = id_ , parent = parent ,
318
322
sw360 = sw360 , ** kwargs )
319
323
320
324
def from_json (self , json ):
@@ -338,14 +342,14 @@ def from_json(self, json):
338
342
"checkedBy" , "checkedTeam" , "checkedComment" , "checkedOn" ,
339
343
"checkStatus" ))
340
344
341
- def get (self , sw360 = None , id_ = None ):
342
- """Retrieve/update attachment from SW360."""
343
- if sw360 :
344
- self .sw360 = sw360
345
- if id_ :
346
- self .id = id_
345
+ @classmethod
346
+ def get (cls , sw360 , id_ ):
347
+ """Retrieve attachment info from SW360."""
348
+ return Attachment (id_ = id_ , json = sw360 .get_attachment (id_ ), sw360 = sw360 )
349
+
350
+ def update (self ):
351
+ """update attachment info from SW360."""
347
352
self .from_json (self .sw360 .get_attachment (self .id ))
348
- return self
349
353
350
354
def download (self , target_path , filename = None ):
351
355
"""download an attachment to local file.
@@ -368,15 +372,15 @@ class Component(SW360Resource):
368
372
"""A component is the SW360 abstraction for a single software
369
373
package/library/program/etc.
370
374
371
- You can either create it from a SW360 `json` object or by specifying the
372
- details via the constructor parameters, see list below. Only the most
373
- important attributes are supported, rest hast be provided via `kwargs` and
374
- is stored in the `details` attribute of instances.
375
+ You can either create it by using `get`, from a SW360 `json` object or by
376
+ creating a fresh instance. The list below describes supported attributes.
377
+ Only the most important ones are supported, rest hast be provided via
378
+ `kwargs` and is stored in the `details` attribute of instances.
375
379
376
380
For JSON parsing, please read documentation of from_json() method.
377
381
378
382
:param json: create component from SW360 JSON object by calling from_json()
379
- :param component_id : id of the component (if exists in SW360 already)
383
+ :param id_ : id of the component (if exists in SW360 already)
380
384
:param name: name of the component
381
385
:param description: short description for component
382
386
:param homepage: homepage of the component
@@ -385,15 +389,15 @@ class Component(SW360Resource):
385
389
:param sw360: your SW360 instance for interacting with the API
386
390
:param kwargs: additional component details as specified in the SW360 REST API
387
391
:type json: SW360 JSON object
388
- :type component_id : string
392
+ :type id_ : string
389
393
:type name: string
390
394
:type description: string
391
395
:type homepage: string
392
396
:type component_type: string
393
397
:type sw360: instance from SW360 class
394
398
:type kwargs: dictionary
395
399
"""
396
- def __init__ (self , json = None , component_id = None , name = None , description = None ,
400
+ def __init__ (self , json = None , id_ = None , name = None , description = None ,
397
401
homepage = None , component_type = None , sw360 = None , ** kwargs ):
398
402
self .releases = {}
399
403
self .attachments = {}
@@ -405,7 +409,7 @@ def __init__(self, json=None, component_id=None, name=None, description=None,
405
409
self .homepage = homepage
406
410
self .component_type = component_type
407
411
408
- super ().__init__ (json = json , resource_id = component_id , sw360 = sw360 , ** kwargs )
412
+ super ().__init__ (json = json , id_ = id_ , sw360 = sw360 , ** kwargs )
409
413
410
414
def from_json (self , json ):
411
415
"""Parse component JSON object from SW360 REST API. Information for
@@ -428,14 +432,14 @@ def from_json(self, json):
428
432
copy_attributes = ("name" , "description" , "homepage" ,
429
433
"componentType" , "externalIds" ))
430
434
431
- def get (self , sw360 = None , id_ = None ):
432
- """Retrieve/update component from SW360."""
433
- if sw360 :
434
- self .sw360 = sw360
435
- if id_ :
436
- self .id = id_
435
+ @classmethod
436
+ def get (cls , sw360 , id_ ):
437
+ """Retrieve component from SW360."""
438
+ return Component (id_ = id_ , json = sw360 .get_component (id_ ), sw360 = sw360 )
439
+
440
+ def update (self ):
441
+ """update component from SW360."""
437
442
self .from_json (self .sw360 .get_component (self .id ))
438
- return self
439
443
440
444
def __str__ (self ):
441
445
return f'{ self .name } ({ self .id } )'
@@ -446,15 +450,15 @@ class Project(SW360Resource):
446
450
used in a project/product. It can contain links to other `Project`s or
447
451
`Release`s.
448
452
449
- You can either create it from a SW360 `json` object or by specifying the
450
- details via the constructor parameters, see list below. Only the most
451
- important attributes are supported, rest hast be provided via `kwargs` and
452
- is stored in the `details` attribute of instances.
453
+ You can either create it by using `get`, from a SW360 `json` object or by
454
+ creating a fresh instance. The list below describes supported attributes.
455
+ Only the most important ones are supported, rest hast be provided via
456
+ `kwargs` and is stored in the `details` attribute of instances.
453
457
454
458
For JSON parsing, please read documentation of from_json() method.
455
459
456
460
:param json: create component from SW360 JSON object by calling from_json()
457
- :param project_id : id of the project (if exists in SW360 already)
461
+ :param id_ : id of the project (if exists in SW360 already)
458
462
:param users: dictionary of SW360 resources which link to the project
459
463
(instances of Project() with id as key)
460
464
:param name: name of the project
@@ -468,7 +472,7 @@ class Project(SW360Resource):
468
472
:param sw360: your SW360 instance for interacting with the API
469
473
:param kwargs: additional project details as specified in the SW360 REST API
470
474
:type json: SW360 JSON object
471
- :type project_id : string
475
+ :type id_ : string
472
476
:type name: string
473
477
:type version: string
474
478
:type description: string
@@ -477,7 +481,7 @@ class Project(SW360Resource):
477
481
:type sw360: instance from SW360 class
478
482
:type kwargs: dictionary
479
483
"""
480
- def __init__ (self , json = None , project_id = None , users = {},
484
+ def __init__ (self , json = None , id_ = None , users = {},
481
485
name = None , version = None , description = None , visibility = None , project_type = None ,
482
486
sw360 = None , ** kwargs ):
483
487
self .releases = {}
@@ -489,7 +493,7 @@ def __init__(self, json=None, project_id=None, users={},
489
493
self .description = description
490
494
self .visibility = visibility
491
495
self .project_type = project_type
492
- super ().__init__ (json = json , resource_id = project_id , users = users ,
496
+ super ().__init__ (json = json , id_ = id_ , users = users ,
493
497
sw360 = sw360 , ** kwargs )
494
498
495
499
def from_json (self , json ):
@@ -512,14 +516,14 @@ def from_json(self, json):
512
516
copy_attributes = ("name" , "description" , "version" , "visibility" ,
513
517
"projectType" , "externalIds" ))
514
518
515
- def get (self , sw360 = None , id_ = None ):
516
- """Retrieve/update project from SW360."""
517
- if sw360 :
518
- self .sw360 = sw360
519
- if id_ :
520
- self .id = id_
519
+ @classmethod
520
+ def get (cls , sw360 , id_ ):
521
+ """Retrieve project from SW360."""
522
+ return Project (id_ = id_ , json = sw360 .get_project (id_ ), sw360 = sw360 )
523
+
524
+ def update (self ):
525
+ """update project from SW360."""
521
526
self .from_json (self .sw360 .get_project (self .id ))
522
- return self
523
527
524
528
def __str__ (self ):
525
529
return f'{ self .name } { self .version } ({ self .id } )'
0 commit comments