Skip to content

Add 'Snippet' class as a part of the SPDX 2.1 specification #107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions data/SPDXRdfExample.rdf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
xmlns:j.0="http://usefulinc.com/ns/doap#"
xmlns="http://spdx.org/rdf/terms#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<Snippet rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Snippet">
<snippetFromFile>
<File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DoapSource"/>
</snippetFromFile>
<name>from linux kernel</name>
<copyrightText>Copyright 2008-2010 John Smith</copyrightText>
<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>
<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>
<licenseConcluded rdf:resource="http://spdx.org/licenses/Apache-2.0"/>
<licenseInfoInSnippet rdf:resource="http://spdx.org/licenses/Apache-2.0"/>
</Snippet>
<SpdxDocument rdf:about="https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DOCUMENT">
<name>Sample_Document-V2.1</name>
<creationInfo>
Expand Down
9 changes: 9 additions & 0 deletions data/SPDXTagExample.tag
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ ArtifactOfProjectHomePage: http://www.openjena.org/
ArtifactOfProjectURI: UNKNOWN
FileComment: <text>This file belongs to Jena</text>

## Snippet Information
SnippetSPDXID: SPDXRef-Snippet
SnippetFromFileSPDXID: SPDXRef-DoapSource
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>
SnippetCopyrightText: <text> Copyright 2008-2010 John Smith </text>
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>
SnippetName: from linux kernel
SnippetLicenseConcluded: Apache-2.0
LicenseInfoInSnippet: Apache-2.0

## License Information
LicenseID: LicenseRef-3
Expand Down
12 changes: 12 additions & 0 deletions spdx/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ class Document(object):
Type: Review.
- annotations: SPDX document annotation information, Optional zero or more.
Type: Annotation.
- snippet: Snippet information. Optional zero or more. Type: Snippet.
"""

def __init__(self, version=None, data_license=None, name=None, spdx_id=None,
Expand All @@ -284,6 +285,7 @@ def __init__(self, version=None, data_license=None, name=None, spdx_id=None,
self.extracted_licenses = []
self.reviews = []
self.annotations = []
self.snippet = []

def add_review(self, review):
self.reviews.append(review)
Expand All @@ -297,6 +299,9 @@ def add_extr_lic(self, lic):
def add_ext_document_reference(self, ext_doc_ref):
self.ext_document_references.append(ext_doc_ref)

def add_snippet(self, snip):
self.snippet.append(snip)

@property
def files(self):
return self.package.files
Expand Down Expand Up @@ -324,6 +329,7 @@ def validate(self, messages):
messages = self.validate_package(messages)
messages = self.validate_extracted_licenses(messages)
messages = self.validate_reviews(messages)
messages = self.validate_snippet(messages)

return messages

Expand Down Expand Up @@ -389,6 +395,12 @@ def validate_annotations(self, messages):

return messages

def validate_snippet(self, messages=None):
for snippet in self.snippet:
messages = snippet.validate(messages)

return messages

def validate_creation_info(self, messages):
if self.creation_info is not None:
messages = self.creation_info.validate(messages)
Expand Down
9 changes: 9 additions & 0 deletions spdx/parsers/lexers/tagvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ class Lexer(object):
'LicenseName': 'LICS_NAME',
'LicenseCrossReference': 'LICS_CRS_REF',
'LicenseComment': 'LICS_COMMENT',
# Snippet
'SnippetSPDXID': 'SNIPPET_SPDX_ID',
'SnippetName': 'SNIPPET_NAME',
'SnippetComment': 'SNIPPET_COMMENT',
'SnippetCopyrightText': 'SNIPPET_CR_TEXT',
'SnippetLicenseComments': 'SNIPPET_LICS_COMMENT',
'SnippetFromFileSPDXID': 'SNIPPET_FILE_SPDXID',
'SnippetLicenseConcluded': 'SNIPPET_LICS_CONC',
'LicenseInfoInSnippet': 'SNIPPET_LICS_INFO',
# Common
'NOASSERTION': 'NO_ASSERT',
'UNKNOWN': 'UN_KNOWN',
Expand Down
93 changes: 91 additions & 2 deletions spdx/parsers/rdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,12 @@
'REVIEWER_VALUE' : 'Invalid reviewer value \'{0}\' must be Organization, Tool or Person.',
'REVIEW_DATE' : 'Invalid review date value \'{0}\' must be date in ISO 8601 format.',
'ANNOTATOR_VALUE': 'Invalid annotator value \'{0}\' must be Organization, Tool or Person.',
'ANNOTATION_DATE': 'Invalid annotation date value \'{0}\' must be date in ISO 8601 format.'
'ANNOTATION_DATE': 'Invalid annotation date value \'{0}\' must be date in ISO 8601 format.',
'SNIPPET_SPDX_ID_VALUE' : 'SPDXID must be "SPDXRef-[idstring]" where [idstring] is a unique string '
'containing letters, numbers, ".", "-".',
'SNIPPET_SINGLE_LICS' : 'Snippet Concluded License must be a license url or spdx:noassertion or spdx:none.',
'SNIPPET_LIC_INFO' : 'License Information in Snippet must be a license url or a reference '
'to the license, denoted by LicenseRef-[idstring] or spdx:noassertion or spdx:none.',
}


Expand Down Expand Up @@ -677,6 +682,87 @@ def p_file_lic_conc(self, f_term, predicate):
self.more_than_one_error('file {0}'.format(predicate))


class SnippetParser(LicenseParser):
"""
Helper class for parsing snippet information.
"""

def __init__(self, builder, logger):
super(SnippetParser, self).__init__(builder, logger)

def parse_snippet(self, snippet_term):
try:
self.builder.create_snippet(self.doc, snippet_term)
except SPDXValueError:
self.value_error('SNIPPET_SPDX_ID_VALUE', snippet_term)

for _s, _p, o in self.graph.triples((snippet_term, self.spdx_namespace['name'], None)):
try:
self.builder.set_snippet_name(self.doc, six.text_type(o))
except CardinalityError:
self.more_than_one_error('snippetName')
break

for _s, _p, o in self.graph.triples((snippet_term, self.spdx_namespace['licenseComments'], None)):
try:
self.builder.set_snippet_lic_comment(self.doc, six.text_type(o))
except CardinalityError:
self.more_than_one_error('licenseComments')
break

for _s, _p, o in self.graph.triples((snippet_term, RDFS.comment, None)):
try:
self.builder.set_snippet_comment(self.doc, six.text_type(o))
except CardinalityError:
self.more_than_one_error('comment')
break

for _s, _p, o in self.graph.triples((snippet_term, self.spdx_namespace['copyrightText'], None)):
try:
self.builder.set_snippet_copyright(self.doc, self.to_special_value(six.text_type(o)))
except CardinalityError:
self.more_than_one_error('copyrightText')
break

try:
for _, _, licenses in self.graph.triples(
(snippet_term, self.spdx_namespace['licenseConcluded'], None)):
if (licenses, RDF.type, self.spdx_namespace['ConjunctiveLicenseSet']) in self.graph:
lics = self.handle_conjunctive_list(licenses)
self.builder.set_snip_concluded_license(self.doc, lics)

elif (licenses, RDF.type, self.spdx_namespace['DisjunctiveLicenseSet']) in self.graph:
lics = self.handle_disjunctive_list(licenses)
self.builder.set_snip_concluded_license(self.doc, lics)

else:
try:
lics = self.handle_lics(licenses)
self.builder.set_snip_concluded_license(self.doc, lics)
except SPDXValueError:
self.value_error('SNIPPET_SINGLE_LICS', licenses)
except CardinalityError:
self.more_than_one_error('package {0}'.format(
self.spdx_namespace['licenseConcluded']))

for _, _, info in self.graph.triples(
(snippet_term, self.spdx_namespace['licenseInfoInSnippet'], None)):
lic = self.handle_lics(info)
if lic is not None:
try:
self.builder.set_snippet_lics_info(self.doc, lic)
except SPDXValueError:
self.value_error('SNIPPET_LIC_INFO', lic)

for _s, _p, o in self.graph.triples(
(snippet_term, self.spdx_namespace['snippetFromFile'], None)):
try:
self.builder.set_snip_from_file_spdxid(self.doc, six.text_type(o))
except CardinalityError:
self.more_than_one_error('snippetFromFile')
break


class ReviewParser(BaseParser):
"""
Helper class for parsing review information.
Expand Down Expand Up @@ -824,7 +910,7 @@ def get_annotator(self, r_term):
self.value_error('ANNOTATOR_VALUE', annotator_list[0][2])


class Parser(PackageParser, FileParser, ReviewParser, AnnotationParser):
class Parser(PackageParser, FileParser, SnippetParser, ReviewParser, AnnotationParser):
"""
RDF/XML file parser.
"""
Expand Down Expand Up @@ -856,6 +942,9 @@ def parse(self, fil):
for s, _p, o in self.graph.triples((None, self.spdx_namespace['referencesFile'], None)):
self.parse_file(o)

for s, _p, o in self.graph.triples((None, RDF.type, self.spdx_namespace['Snippet'])):
self.parse_snippet(s)

for s, _p, o in self.graph.triples((None, self.spdx_namespace['reviewed'], None)):
self.parse_review(o)

Expand Down
46 changes: 45 additions & 1 deletion spdx/parsers/rdfbuilders.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,50 @@ def set_file_notice(self, doc, text):
raise OrderError('File::Notice')


class SnippetBuilder(tagvaluebuilders.SnippetBuilder):

def __init__(self):
super(SnippetBuilder, self).__init__()

def set_snippet_lic_comment(self, doc, lic_comment):
"""Sets the snippet's license comment.
Raises OrderError if no snippet previously defined.
Raises CardinalityError if already set.
"""
self.assert_snippet_exists()
if not self.snippet_lic_comment_set:
self.snippet_lic_comment_set = True
doc.snippet[-1].license_comment = lic_comment
else:
CardinalityError('Snippet::licenseComments')

def set_snippet_comment(self, doc, comment):
"""
Sets general comments about the snippet.
Raises OrderError if no snippet previously defined.
Raises CardinalityError if comment already set.
"""
self.assert_snippet_exists()
if not self.snippet_comment_set:
self.snippet_comment_set = True
doc.snippet[-1].comment = comment
return True
else:
raise CardinalityError('Snippet::comment')

def set_snippet_copyright(self, doc, copyright):
"""Sets the snippet's copyright text.
Raises OrderError if no snippet previously defined.
Raises CardinalityError if already set.
"""
self.assert_snippet_exists()
if not self.snippet_copyright_set:
self.snippet_copyright_set = True
doc.snippet[-1].copyright = copyright
else:
raise CardinalityError('Snippet::copyrightText')


class ReviewBuilder(tagvaluebuilders.ReviewBuilder):

def __init__(self):
Expand Down Expand Up @@ -426,7 +470,7 @@ def add_annotation_type(self, doc, annotation_type):


class Builder(DocBuilder, EntityBuilder, CreationInfoBuilder, PackageBuilder,
FileBuilder, ReviewBuilder, ExternalDocumentRefBuilder,
FileBuilder, SnippetBuilder, ReviewBuilder, ExternalDocumentRefBuilder,
AnnotationBuilder):

def __init__(self):
Expand Down
Loading