Skip to content

Commit acebe3e

Browse files
committed
Add 'snippet' class as a part of the 2.1 spec
Add 'spdx_id', 'name', 'comment', 'copyright', and 'license_comment' to the 'snippet' class. Signed-off-by: Yash Nisar <[email protected]>
1 parent f141f1f commit acebe3e

File tree

14 files changed

+633
-7
lines changed

14 files changed

+633
-7
lines changed

data/SPDXRdfExample.rdf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
xmlns:j.0="http://usefulinc.com/ns/doap#"
44
xmlns="http://spdx.org/rdf/terms#"
55
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
6+
<Snippet rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Snippet">
7+
<name>from linux kernel</name>
8+
<copyrightText>Copyright 2008-2010 John Smith</copyrightText>
9+
<licenseComments>The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.</licenseComments>
10+
<rdfs:comment>This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0-or-later.</rdfs:comment>
11+
</Snippet>
612
<SpdxDocument rdf:about="http://www.spdx.org/tools#SPDXANALYSIS">
713
<creationInfo>
814
<CreationInfo>

data/SPDXTagExample.tag

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ ArtifactOfProjectHomePage: http://www.openjena.org/
6565
ArtifactOfProjectURI: UNKNOWN
6666
FileComment: <text>This file belongs to Jena</text>
6767

68+
## Snippet Information
69+
SnippetSPDXID: SPDXRef-Snippet
70+
SnippetLicenseComments: <text>The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.</text>
71+
SnippetCopyrightText: <text> Copyright 2008-2010 John Smith </text>
72+
SnippetComment: <text>This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0-or-later.</text>
73+
SnippetName: from linux kernel
6874

6975
## License Information
7076
LicenseID: LicenseRef-3

spdx/document.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,11 @@ class Document(object):
196196
SPDX license list. Optional, many. Type: ExtractedLicense.
197197
- reviews: SPDX document review information, Optional zero or more.
198198
Type: Review.
199+
- snippet: Snippet information. Optional zero or more. Type: Snippet.
199200
"""
200201

201-
def __init__(self, version=None, data_license=None, comment=None, package=None):
202+
def __init__(self, version=None, data_license=None, comment=None,
203+
package=None, snippet=None):
202204
# avoid recursive impor
203205
from spdx.creationinfo import CreationInfo
204206
self.version = version
@@ -208,13 +210,17 @@ def __init__(self, version=None, data_license=None, comment=None, package=None):
208210
self.package = package
209211
self.extracted_licenses = []
210212
self.reviews = []
213+
self.snippet = []
211214

212215
def add_review(self, review):
213216
self.reviews.append(review)
214217

215218
def add_extr_lic(self, lic):
216219
self.extracted_licenses.append(lic)
217220

221+
def add_snippet(self, snip):
222+
self.snippet.append(snip)
223+
218224
@property
219225
def files(self):
220226
return self.package.files
@@ -241,6 +247,7 @@ def validate(self, messages=None):
241247
and self.validate_package(messages)
242248
and self.validate_extracted_licenses(messages)
243249
and self.validate_reviews(messages)
250+
and self.validate_snippet(messages)
244251
)
245252

246253
def validate_version(self, messages=None):
@@ -277,6 +284,15 @@ def validate_reviews(self, messages=None):
277284
valid = review.validate(messages) and valid
278285
return valid
279286

287+
def validate_snippet(self, messages=None):
288+
# FIXME: messages should be returned
289+
messages = messages if messages is not None else []
290+
291+
valid = True
292+
for snippet in self.snippet:
293+
valid = snippet.validate(messages) and valid
294+
return valid
295+
280296
def validate_creation_info(self, messages=None):
281297
# FIXME: messages should be returned
282298
messages = messages if messages is not None else []

spdx/parsers/lexers/tagvalue.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ class Lexer(object):
7070
'LicenseName': 'LICS_NAME',
7171
'LicenseCrossReference': 'LICS_CRS_REF',
7272
'LicenseComment': 'LICS_COMMENT',
73+
# Snippet
74+
'SnippetSPDXID': 'SNIPPET_SPDX_ID',
75+
'SnippetName': 'SNIPPET_NAME',
76+
'SnippetComment': 'SNIPPET_COMMENT',
77+
'SnippetCopyrightText': 'SNIPPET_CR_TEXT',
78+
'SnippetLicenseComments': 'SNIPPET_LICS_COMMENT',
7379
# Common
7480
'NOASSERTION': 'NO_ASSERT',
7581
'UNKNOWN': 'UN_KNOWN',
@@ -94,8 +100,9 @@ def t_text_end(self, t):
94100
r'</text>\s*'
95101
t.type = 'TEXT'
96102
t.value = t.lexer.lexdata[
97-
t.lexer.text_start:t.lexer.lexpos].strip()
103+
t.lexer.text_start:t.lexer.lexpos]
98104
t.lexer.lineno += t.value.count('\n')
105+
t.value = t.value.strip()
99106
t.lexer.begin('INITIAL')
100107
return t
101108

spdx/parsers/rdf.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
'FILE_SINGLE_LICS': 'File concluded license must be a license url or spdx:noassertion or spdx:none.',
4848
'REVIEWER_VALUE' : 'Invalid reviewer value \'{0}\' must be Organization, Tool or Person.',
4949
'REVIEW_DATE' : 'Invalid review date value \'{0}\' must be date in ISO 8601 format.',
50+
'SNIPPET_SPDX_ID_VALUE' : 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string '
51+
'containing letters, numbers, ".", "-".',
5052
}
5153

5254

@@ -658,6 +660,49 @@ def p_file_lic_conc(self, f_term, predicate):
658660
self.more_than_one_error('file {0}'.format(predicate))
659661

660662

663+
class SnippetParser(LicenseParser):
664+
"""
665+
Helper class for parsing snippet information.
666+
"""
667+
668+
def __init__(self, builder, logger):
669+
super(SnippetParser, self).__init__(builder, logger)
670+
671+
def parse_snippet(self, snippet_term):
672+
try:
673+
self.builder.create_snippet(self.doc, snippet_term)
674+
except SPDXValueError:
675+
self.value_error('SNIPPET_SPDX_ID_VALUE', snippet_term)
676+
677+
for _s, _p, o in self.graph.triples((snippet_term, self.spdx_namespace['name'], None)):
678+
try:
679+
self.builder.set_snippet_name(self.doc, six.text_type(o))
680+
except CardinalityError:
681+
self.more_than_one_error('snippetName')
682+
break
683+
684+
for _s, _p, o in self.graph.triples((snippet_term, self.spdx_namespace['licenseComments'], None)):
685+
try:
686+
self.builder.set_snippet_lic_comment(self.doc, six.text_type(o))
687+
except CardinalityError:
688+
self.more_than_one_error('licenseComments')
689+
break
690+
691+
for _s, _p, o in self.graph.triples((snippet_term, RDFS.comment, None)):
692+
try:
693+
self.builder.set_snippet_comment(self.doc, six.text_type(o))
694+
except CardinalityError:
695+
self.more_than_one_error('comment')
696+
break
697+
698+
for _s, _p, o in self.graph.triples((snippet_term, self.spdx_namespace['copyrightText'], None)):
699+
try:
700+
self.builder.set_snippet_copyright(self.doc, self.to_special_value(six.text_type(o)))
701+
except CardinalityError:
702+
self.more_than_one_error('copyrightText')
703+
break
704+
705+
661706
class ReviewParser(BaseParser):
662707
"""
663708
Helper class for parsing review information.
@@ -722,7 +767,7 @@ def get_reviewer(self, r_term):
722767
self.value_error('REVIEWER_VALUE', reviewer_list[0][2])
723768

724769

725-
class Parser(PackageParser, FileParser, ReviewParser):
770+
class Parser(PackageParser, FileParser, SnippetParser, ReviewParser):
726771
"""
727772
RDF/XML file parser.
728773
"""
@@ -751,6 +796,9 @@ def parse(self, fil):
751796
for s, _p, o in self.graph.triples((None, self.spdx_namespace['referencesFile'], None)):
752797
self.parse_file(o)
753798

799+
for s, _p, o in self.graph.triples((None, RDF.type, self.spdx_namespace['Snippet'])):
800+
self.parse_snippet(s)
801+
754802
for s, _p, o in self.graph.triples((None, self.spdx_namespace['reviewed'], None)):
755803
self.parse_review(o)
756804

spdx/parsers/rdfbuilders.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,50 @@ def set_file_notice(self, doc, text):
305305
raise OrderError('File::Notice')
306306

307307

308+
class SnippetBuilder(tagvaluebuilders.SnippetBuilder):
309+
310+
def __init__(self):
311+
super(SnippetBuilder, self).__init__()
312+
313+
def set_snippet_lic_comment(self, doc, lic_comment):
314+
"""Sets the snippet's license comment.
315+
Raises OrderError if no snippet previously defined.
316+
Raises CardinalityError if already set.
317+
"""
318+
self.assert_snippet_exists()
319+
if not self.snippet_lic_comment_set:
320+
self.snippet_lic_comment_set = True
321+
doc.snippet[-1].license_comment = lic_comment
322+
else:
323+
CardinalityError('Snippet::licenseComments')
324+
325+
def set_snippet_comment(self, doc, comment):
326+
"""
327+
Sets general comments about the snippet.
328+
Raises OrderError if no snippet previously defined.
329+
Raises CardinalityError if comment already set.
330+
"""
331+
self.assert_snippet_exists()
332+
if not self.snippet_comment_set:
333+
self.snippet_comment_set = True
334+
doc.snippet[-1].comment = comment
335+
return True
336+
else:
337+
raise CardinalityError('Snippet::comment')
338+
339+
def set_snippet_copyright(self, doc, copyright):
340+
"""Sets the snippet's copyright text.
341+
Raises OrderError if no snippet previously defined.
342+
Raises CardinalityError if already set.
343+
"""
344+
self.assert_snippet_exists()
345+
if not self.snippet_copyright_set:
346+
self.snippet_copyright_set = True
347+
doc.snippet[-1].copyright = copyright
348+
else:
349+
raise CardinalityError('Snippet::copyrightText')
350+
351+
308352
class ReviewBuilder(tagvaluebuilders.ReviewBuilder):
309353

310354
def __init__(self):
@@ -325,7 +369,8 @@ def add_review_comment(self, doc, comment):
325369
raise OrderError('ReviewComment')
326370

327371

328-
class Builder(DocBuilder, EntityBuilder, CreationInfoBuilder, PackageBuilder, FileBuilder, ReviewBuilder):
372+
class Builder(DocBuilder, EntityBuilder, CreationInfoBuilder, PackageBuilder, FileBuilder,
373+
SnippetBuilder, ReviewBuilder):
329374

330375
def __init__(self):
331376
super(Builder, self).__init__()

spdx/parsers/tagvalue.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@
8282
'LICS_COMMENT_VALUE' : 'LicenseComment must be free form text, line: {0}',
8383
'LICS_CRS_REF_VALUE' : 'LicenseCrossReference must be uri as single line of text, line: {0}',
8484
'PKG_CPY_TEXT_VALUE' : 'Package copyright text must be free form text, line: {0}',
85+
'SNIP_SPDX_ID_VALUE' : 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string '
86+
'containing letters, numbers, ".", "-".',
87+
'SNIPPET_NAME_VALUE' : 'SnippetName must be a single line of text, line: {0}',
88+
'SNIP_COMMENT_VALUE' : 'SnippetComment must be free form text, line: {0}',
89+
'SNIP_COPYRIGHT_VALUE' : 'SnippetCopyrightText must be one of NOASSERTION, NONE or free form text, line: {0}',
90+
'SNIP_LICS_COMMENT_VALUE' : 'SnippetLicenseComments must be free form text, line: {0}',
8591
}
8692

8793

@@ -143,6 +149,11 @@ def p_attrib(self, p):
143149
| file_contrib
144150
| file_dep
145151
| file_artifact
152+
| snip_spdx_id
153+
| snip_name
154+
| snip_comment
155+
| snip_cr_text
156+
| snip_lic_comment
146157
| extr_lic_id
147158
| extr_lic_text
148159
| extr_lic_name
@@ -990,6 +1001,124 @@ def p_package_name_1(self, p):
9901001
msg = ERROR_MESSAGES['PACKAGE_NAME_VALUE'].format(p.lineno(1))
9911002
self.logger.log(msg)
9921003

1004+
def p_snip_spdx_id(self, p):
1005+
"""snip_spdx_id : SNIPPET_SPDX_ID LINE"""
1006+
try:
1007+
if six.PY2:
1008+
value = p[2].decode(encoding='utf-8')
1009+
else:
1010+
value = p[2]
1011+
self.builder.create_snippet(self.document, value)
1012+
except SPDXValueError:
1013+
self.error = True
1014+
msg = ERROR_MESSAGES['SNIP_SPDX_ID_VALUE'].format(p.lineno(2))
1015+
self.logger.log(msg)
1016+
1017+
def p_snip_spdx_id_1(self, p):
1018+
"""snip_spdx_id : SNIPPET_SPDX_ID error"""
1019+
self.error = True
1020+
msg = ERROR_MESSAGES['SNIP_SPDX_ID_VALUE'].format(p.lineno(1))
1021+
self.logger.log(msg)
1022+
1023+
def p_snippet_name(self, p):
1024+
"""snip_name : SNIPPET_NAME LINE"""
1025+
try:
1026+
if six.PY2:
1027+
value = p[2].decode(encoding='utf-8')
1028+
else:
1029+
value = p[2]
1030+
self.builder.set_snippet_name(self.document, value)
1031+
except OrderError:
1032+
self.order_error('SnippetName', 'SnippetSPDXID', p.lineno(1))
1033+
except CardinalityError:
1034+
self.more_than_one_error('SnippetName', p.lineno(1))
1035+
1036+
def p_snippet_name_1(self, p):
1037+
"""snip_name : SNIPPET_NAME error"""
1038+
self.error = True
1039+
msg = ERROR_MESSAGES['SNIPPET_NAME_VALUE'].format(p.lineno(1))
1040+
self.logger.log(msg)
1041+
1042+
def p_snippet_comment(self, p):
1043+
"""snip_comment : SNIPPET_COMMENT TEXT"""
1044+
try:
1045+
if six.PY2:
1046+
value = p[2].decode(encoding='utf-8')
1047+
else:
1048+
value = p[2]
1049+
self.builder.set_snippet_comment(self.document, value)
1050+
except OrderError:
1051+
self.order_error('SnippetComment', 'SnippetSPDXID', p.lineno(1))
1052+
except SPDXValueError:
1053+
self.error = True
1054+
msg = ERROR_MESSAGES['SNIP_COMMENT_VALUE'].format(p.lineno(2))
1055+
self.logger.log(msg)
1056+
except CardinalityError:
1057+
self.more_than_one_error('SnippetComment', p.lineno(1))
1058+
1059+
def p_snippet_comment_1(self, p):
1060+
"""snip_comment : SNIPPET_COMMENT error"""
1061+
self.error = True
1062+
msg = ERROR_MESSAGES['SNIP_COMMENT_VALUE'].format(p.lineno(1))
1063+
self.logger.log(msg)
1064+
1065+
def p_snippet_cr_text(self, p):
1066+
"""snip_cr_text : SNIPPET_CR_TEXT snip_cr_value"""
1067+
try:
1068+
self.builder.set_snippet_copyright(self.document, p[2])
1069+
except OrderError:
1070+
self.order_error('SnippetCopyrightText', 'SnippetSPDXID', p.lineno(1))
1071+
except SPDXValueError:
1072+
self.error = True
1073+
msg = ERROR_MESSAGES['SNIP_COPYRIGHT_VALUE'].format(p.lineno(2))
1074+
self.logger.log(msg)
1075+
except CardinalityError:
1076+
self.more_than_one_error('SnippetCopyrightText', p.lineno(1))
1077+
1078+
def p_snippet_cr_text_1(self, p):
1079+
"""snip_cr_text : SNIPPET_CR_TEXT error"""
1080+
self.error = True
1081+
msg = ERROR_MESSAGES['SNIP_COPYRIGHT_VALUE'].format(p.lineno(1))
1082+
self.logger.log(msg)
1083+
1084+
def p_snippet_cr_value_1(self, p):
1085+
"""snip_cr_value : TEXT"""
1086+
if six.PY2:
1087+
p[0] = p[1].decode(encoding='utf-8')
1088+
else:
1089+
p[0] = p[1]
1090+
1091+
def p_snippet_cr_value_2(self, p):
1092+
"""snip_cr_value : NONE"""
1093+
p[0] = utils.SPDXNone()
1094+
1095+
def p_snippet_cr_value_3(self, p):
1096+
"""snip_cr_value : NO_ASSERT"""
1097+
p[0] = utils.NoAssert()
1098+
1099+
def p_snippet_lic_comment(self, p):
1100+
"""snip_lic_comment : SNIPPET_LICS_COMMENT TEXT"""
1101+
try:
1102+
if six.PY2:
1103+
value = p[2].decode(encoding='utf-8')
1104+
else:
1105+
value = p[2]
1106+
self.builder.set_snippet_lic_comment(self.document, value)
1107+
except OrderError:
1108+
self.order_error('SnippetLicenseComments', 'SnippetSPDXID', p.lineno(1))
1109+
except SPDXValueError:
1110+
self.error = True
1111+
msg = ERROR_MESSAGES['SNIP_LICS_COMMENT_VALUE'].format(p.lineno(2))
1112+
self.logger.log(msg)
1113+
except CardinalityError:
1114+
self.more_than_one_error('SnippetLicenseComments', p.lineno(1))
1115+
1116+
def p_snippet_lic_comment_1(self, p):
1117+
"""snip_lic_comment : SNIPPET_LICS_COMMENT error"""
1118+
self.error = True
1119+
msg = ERROR_MESSAGES['SNIP_LICS_COMMENT_VALUE'].format(p.lineno(1))
1120+
self.logger.log(msg)
1121+
9931122
def p_reviewer_1(self, p):
9941123
"""reviewer : REVIEWER entity"""
9951124
self.builder.add_reviewer(self.document, p[2])

0 commit comments

Comments
 (0)