From 366e21af1cf4db5d21c3e2f7e16ec83e61f8aaf8 Mon Sep 17 00:00:00 2001 From: yash-nisar Date: Thu, 24 May 2018 10:49:40 +0530 Subject: [PATCH] Add 'name' field to the 'Document' class Signed-off-by: Yash Nisar --- data/SPDXRdfExample.rdf | 1 + data/SPDXSimpleTag.tag | 1 + data/SPDXTagExample.tag | 1 + spdx/document.py | 16 +++++++++++++++- spdx/parsers/lexers/tagvalue.py | 1 + spdx/parsers/rdf.py | 9 ++++++++- spdx/parsers/rdfbuilders.py | 12 ++++++++++++ spdx/parsers/tagvalue.py | 19 +++++++++++++++++++ spdx/parsers/tagvaluebuilders.py | 12 ++++++++++++ spdx/parsers/validations.py | 4 ++++ spdx/writers/rdf.py | 2 ++ spdx/writers/tagvalue.py | 1 + tests/data/doc_write/rdf-simple-plus.json | 5 ++++- tests/data/doc_write/rdf-simple.json | 5 ++++- tests/data/doc_write/tv-simple-plus.tv | 1 + tests/data/doc_write/tv-simple.tv | 1 + tests/test_builder.py | 11 +++++++++++ tests/test_document.py | 9 ++++++--- tests/test_tag_value_parser.py | 12 ++++++++++-- 19 files changed, 114 insertions(+), 9 deletions(-) diff --git a/data/SPDXRdfExample.rdf b/data/SPDXRdfExample.rdf index 4ada369ba..6d9c78af8 100644 --- a/data/SPDXRdfExample.rdf +++ b/data/SPDXRdfExample.rdf @@ -4,6 +4,7 @@ xmlns="http://spdx.org/rdf/terms#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"> + Sample_Document-V2.1 2010-02-03T00:00:00Z diff --git a/data/SPDXSimpleTag.tag b/data/SPDXSimpleTag.tag index 8e7e16ff7..cf47c7768 100644 --- a/data/SPDXSimpleTag.tag +++ b/data/SPDXSimpleTag.tag @@ -1,6 +1,7 @@ # Document info SPDXVersion: SPDX-2.1 DataLicense: CC0-1.0 +DocumentName: Sample_Document-V2.1 DocumentComment: Sample Comment # Creation info diff --git a/data/SPDXTagExample.tag b/data/SPDXTagExample.tag index 25261122b..fc850694a 100644 --- a/data/SPDXTagExample.tag +++ b/data/SPDXTagExample.tag @@ -1,5 +1,6 @@ SPDXVersion: SPDX-2.1 DataLicense: CC0-1.0 +DocumentName: Sample_Document-V2.1 DocumentComment: This is a sample spreadsheet ## Creation Information diff --git a/spdx/document.py b/spdx/document.py index 7d307fc84..3bbcab082 100644 --- a/spdx/document.py +++ b/spdx/document.py @@ -189,6 +189,7 @@ class Document(object): Represent an SPDX document with these fields: - version: Spec version. Mandatory, one - Type: Version. - data_license: SPDX-Metadata license. Mandatory, one. Type: License. + - name: Name of the document. Mandatory, one. Type: str. - comment: Comments on the SPDX file, optional one. Type: str - creation_info: SPDX file creation info. Mandatory, one. Type: CreationInfo - package: Package described by this document. Mandatory, one. Type: Package @@ -198,11 +199,13 @@ class Document(object): Type: Review. """ - def __init__(self, version=None, data_license=None, comment=None, package=None): + def __init__(self, version=None, data_license=None, name=None, comment=None, + package=None): # avoid recursive impor from spdx.creationinfo import CreationInfo self.version = version self.data_license = data_license + self.name = name self.comment = comment self.creation_info = CreationInfo() self.package = package @@ -237,6 +240,7 @@ def validate(self, messages=None): return (self.validate_version(messages) and self.validate_data_lics(messages) + and self.validate_name(messages) and self.validate_creation_info(messages) and self.validate_package(messages) and self.validate_extracted_licenses(messages) @@ -268,6 +272,16 @@ def validate_data_lics(self, messages=None): messages.append('Document data license must be CC0-1.0.') return False + def validate_name(self, messages=None): + # FIXME: messages should be returned + messages = messages if messages is not None else [] + + if self.name is None: + messages.append('Document has no name.') + return False + else: + return True + def validate_reviews(self, messages=None): # FIXME: messages should be returned messages = messages if messages is not None else [] diff --git a/spdx/parsers/lexers/tagvalue.py b/spdx/parsers/lexers/tagvalue.py index 53f1573c3..ff51472d3 100644 --- a/spdx/parsers/lexers/tagvalue.py +++ b/spdx/parsers/lexers/tagvalue.py @@ -21,6 +21,7 @@ class Lexer(object): # Top level fields 'SPDXVersion': 'DOC_VERSION', 'DataLicense': 'DOC_LICENSE', + 'DocumentName': 'DOC_NAME', 'DocumentComment': 'DOC_COMMENT', # Creation info 'Creator': 'CREATOR', diff --git a/spdx/parsers/rdf.py b/spdx/parsers/rdf.py index 7da300990..72fe49b5c 100644 --- a/spdx/parsers/rdf.py +++ b/spdx/parsers/rdf.py @@ -799,7 +799,7 @@ def parse_creation_info(self, ci_term): self.value_error('LL_VALUE', o) def parse_doc_fields(self, doc_term): - """Parses the version, data license and comment.""" + """Parses the version, data license, name and comment.""" for _s, _p, o in self.graph.triples((doc_term, self.spdx_namespace['specVersion'], None)): try: self.builder.set_doc_version(self.doc, six.text_type(o)) @@ -816,6 +816,13 @@ def parse_doc_fields(self, doc_term): except CardinalityError: self.more_than_one_error('dataLicense') break + for _s, _p, o in self.graph.triples( + (doc_term, self.spdx_namespace['name'], None)): + try: + self.builder.set_doc_name(self.doc, six.text_type(o)) + except CardinalityError: + self.more_than_one_error('name') + break for _s, _p, o in self.graph.triples((doc_term, RDFS.comment, None)): try: self.builder.set_doc_comment(self.doc, six.text_type(o)) diff --git a/spdx/parsers/rdfbuilders.py b/spdx/parsers/rdfbuilders.py index a18ef6f15..878883377 100644 --- a/spdx/parsers/rdfbuilders.py +++ b/spdx/parsers/rdfbuilders.py @@ -70,6 +70,17 @@ def set_doc_data_lic(self, doc, res): else: raise CardinalityError('Document::License') + def set_doc_name(self, doc, name): + """ + Sets the document name, raises CardinalityError if already defined. + """ + if not self.doc_name_set: + doc.name = name + self.doc_name_set = True + return True + else: + raise CardinalityError('Document::Name') + def set_doc_comment(self, doc, comment): """Sets document comment, Raises CardinalityError if comment already set. @@ -88,6 +99,7 @@ def reset_document(self): self.doc_version_set = False self.doc_comment_set = False self.doc_data_lics_set = False + self.doc_name_set = False class EntityBuilder(tagvaluebuilders.EntityBuilder): diff --git a/spdx/parsers/tagvalue.py b/spdx/parsers/tagvalue.py index dee664436..9b868b5ab 100644 --- a/spdx/parsers/tagvalue.py +++ b/spdx/parsers/tagvalue.py @@ -39,6 +39,7 @@ 'DOC_LICENSE_VALUE_TYPE': 'DataLicense must be CC0-1.0, line: {0}', 'DOC_VERSION_VALUE': 'Invalid SPDXVersion \'{0}\' must be SPDX-M.N where M and N are numbers. Line: {1}', 'DOC_VERSION_VALUE_TYPE': 'Invalid SPDXVersion value, must be SPDX-M.N where M and N are numbers. Line: {0}', + 'DOC_NAME_VALUE': 'DocumentName must be single line of text, line: {0}', 'DOC_COMMENT_VALUE_TYPE': 'DocumentComment value must be free form text between tags, line:{0}', 'REVIEWER_VALUE_TYPE': 'Invalid Reviewer value must be a Person, Organization or Tool. Line: {0}', 'CREATOR_VALUE_TYPE': 'Invalid Reviewer value must be a Person, Organization or Tool. Line: {0}', @@ -106,6 +107,7 @@ def p_start_2(self, p): def p_attrib(self, p): """attrib : spdx_version | data_lics + | doc_name | doc_comment | creator | created @@ -1098,6 +1100,23 @@ def p_data_license_2(self, p): msg = ERROR_MESSAGES['DOC_LICENSE_VALUE_TYPE'].format(p.lineno(1)) self.logger.log(msg) + def p_doc_name_1(self, p): + """doc_name : DOC_NAME LINE""" + try: + if six.PY2: + value = p[2].decode(encoding='utf-8') + else: + value = p[2] + self.builder.set_doc_name(self.document, value) + except CardinalityError: + self.more_than_one_error('DocumentName', p.lineno(1)) + + def p_doc_name_2(self, p): + """doc_name : DOC_NAME error""" + self.error = True + msg = ERROR_MESSAGES['DOC_NAME_VALUE'].format(p.lineno(1)) + self.logger.log(msg) + def p_spdx_version_1(self, p): """spdx_version : DOC_VERSION LINE""" try: diff --git a/spdx/parsers/tagvaluebuilders.py b/spdx/parsers/tagvaluebuilders.py index 385c51c4e..5ceeb154c 100644 --- a/spdx/parsers/tagvaluebuilders.py +++ b/spdx/parsers/tagvaluebuilders.py @@ -102,6 +102,17 @@ def set_doc_data_lics(self, doc, lics): else: raise CardinalityError('Document::DataLicense') + def set_doc_name(self, doc, name): + """Sets the document name. + Raises CardinalityError if already defined. + """ + if not self.doc_name_set: + doc.name = name + self.doc_name_set = True + return True + else: + raise CardinalityError('Document::Name') + def set_doc_comment(self, doc, comment): """Sets document comment, Raises CardinalityError if comment already set. @@ -123,6 +134,7 @@ def reset_document(self): self.doc_version_set = False self.doc_comment_set = False self.doc_data_lics_set = False + self.doc_name_set = False class EntityBuilder(object): diff --git a/spdx/parsers/validations.py b/spdx/parsers/validations.py index 11ac460ea..07f1ee861 100644 --- a/spdx/parsers/validations.py +++ b/spdx/parsers/validations.py @@ -55,6 +55,10 @@ def validate_data_lics(value): return value == 'CC0-1.0' +def validate_doc_name(value, optional=False): + return validate_tool_name(value, optional) + + def validate_pkg_supplier(value, optional=False): if optional and value is None: return True diff --git a/spdx/writers/rdf.py b/spdx/writers/rdf.py index f6f3199bd..772402b25 100644 --- a/spdx/writers/rdf.py +++ b/spdx/writers/rdf.py @@ -521,6 +521,8 @@ def create_doc(self): # Data license data_lics = URIRef(self.document.data_license.url) self.graph.add((doc_node, self.spdx_namespace.dataLicense, data_lics)) + doc_name = URIRef(self.document.name) + self.graph.add((doc_node, self.spdx_namespace.name, doc_name)) return doc_node def write(self): diff --git a/spdx/writers/tagvalue.py b/spdx/writers/tagvalue.py index 9af92ef1e..7dc978790 100644 --- a/spdx/writers/tagvalue.py +++ b/spdx/writers/tagvalue.py @@ -236,6 +236,7 @@ def write_document(document, out, validate=True): out.write('# Document Information\n\n') write_value('SPDXVersion', str(document.version), out) write_value('DataLicense', document.data_license.identifier, out) + write_value('DocumentName', document.name, out) if document.has_comment: write_text_value('DocumentComment', document.comment, out) write_separators(out) diff --git a/tests/data/doc_write/rdf-simple-plus.json b/tests/data/doc_write/rdf-simple-plus.json index 1b792534a..83f24875e 100644 --- a/tests/data/doc_write/rdf-simple-plus.json +++ b/tests/data/doc_write/rdf-simple-plus.json @@ -25,7 +25,10 @@ "ns1:specVersion": "SPDX-2.1", "ns1:dataLicense": { "@rdf:resource": "http://spdx.org/licenses/CC0-1.0" - }, + }, + "ns1:name": { + "@rdf:resource": "Sample_Document-V2.1" + }, "ns1:referencesFile": { "ns1:File": { "ns1:fileName": "./some/path/tofile", diff --git a/tests/data/doc_write/rdf-simple.json b/tests/data/doc_write/rdf-simple.json index ff436589c..f9c552bfa 100644 --- a/tests/data/doc_write/rdf-simple.json +++ b/tests/data/doc_write/rdf-simple.json @@ -25,7 +25,10 @@ "ns1:specVersion": "SPDX-2.1", "ns1:dataLicense": { "@rdf:resource": "http://spdx.org/licenses/CC0-1.0" - }, + }, + "ns1:name": { + "@rdf:resource": "Sample_Document-V2.1" + }, "ns1:referencesFile": { "ns1:File": { "ns1:licenseInfoInFile": { diff --git a/tests/data/doc_write/tv-simple-plus.tv b/tests/data/doc_write/tv-simple-plus.tv index 77baa7d89..25acbcaea 100644 --- a/tests/data/doc_write/tv-simple-plus.tv +++ b/tests/data/doc_write/tv-simple-plus.tv @@ -1,6 +1,7 @@ # Document Information SPDXVersion: SPDX-2.1 DataLicense: CC0-1.0 +DocumentName: Sample_Document-V2.1 # Creation Info # Package PackageName: some/path diff --git a/tests/data/doc_write/tv-simple.tv b/tests/data/doc_write/tv-simple.tv index a41928039..162e6ca39 100644 --- a/tests/data/doc_write/tv-simple.tv +++ b/tests/data/doc_write/tv-simple.tv @@ -1,6 +1,7 @@ # Document Information SPDXVersion: SPDX-2.1 DataLicense: CC0-1.0 +DocumentName: Sample_Document-V2.1 # Creation Info # Package PackageName: some/path diff --git a/tests/test_builder.py b/tests/test_builder.py index 93a32dd54..e89381a2c 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -63,6 +63,17 @@ def test_data_lics_cardinality(self): self.builder.set_doc_data_lics(self.document, lics_str) self.builder.set_doc_data_lics(self.document, lics_str) + def test_correct_name(self): + name_str = 'Sample_Document-V2.1' + self.builder.set_doc_name(self.document, name_str) + assert self.document.name == name_str + + @testing_utils.raises(builders.CardinalityError) + def test_name_cardinality(self): + name_str = 'Sample_Document-V2.1' + self.builder.set_doc_name(self.document, name_str) + self.builder.set_doc_name(self.document, name_str) + def test_correct_data_comment(self): comment_str = 'This is a comment.' comment_text = '' + comment_str + '' diff --git a/tests/test_document.py b/tests/test_document.py index 69c7dd4b3..4cb2ac672 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -66,7 +66,8 @@ def test_creation(self): assert document.data_license.identifier == 'AFL-1.1' def test_document_validate_failures_returns_informative_messages(self): - doc = Document(Version(2, 1), License.from_identifier('CC0-1.0')) + doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'), + 'Sample_Document-V2.1') pack = doc.package = Package('some/path', NoAssert()) file1 = File('./some/path/tofile') file1.name = './some/path/tofile' @@ -83,7 +84,8 @@ def test_document_validate_failures_returns_informative_messages(self): assert expected == messages def test_document_is_valid_when_using_or_later_licenses(self): - doc = Document(Version(2, 1), License.from_identifier('CC0-1.0')) + doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'), + 'Sample_Document-V2.1') doc.creation_info.add_creator(Tool('ScanCode')) doc.creation_info.set_created_now() @@ -113,7 +115,8 @@ def test_document_is_valid_when_using_or_later_licenses(self): class TestWriters(TestCase): def _get_lgpl_doc(self, or_later=False): - doc = Document(Version(2, 1), License.from_identifier('CC0-1.0')) + doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'), + 'Sample_Document-V2.1') doc.creation_info.add_creator(Tool('ScanCode')) doc.creation_info.set_created_now() diff --git a/tests/test_tag_value_parser.py b/tests/test_tag_value_parser.py index 9b1ec3e5f..0fd899dc7 100644 --- a/tests/test_tag_value_parser.py +++ b/tests/test_tag_value_parser.py @@ -35,6 +35,7 @@ def test_document(self): SPDXVersion: SPDX-2.1 # Comment. DataLicense: CC0-1.0 + DocumentName: Sample_Document-V2.1 DocumentComment: This is a sample spreadsheet ''' self.l.input(data) @@ -42,8 +43,13 @@ def test_document(self): self.token_assert_helper(self.l.token(), 'LINE', 'SPDX-2.1', 2) self.token_assert_helper(self.l.token(), 'DOC_LICENSE', 'DataLicense', 4) self.token_assert_helper(self.l.token(), 'LINE', 'CC0-1.0', 4) - self.token_assert_helper(self.l.token(), 'DOC_COMMENT', 'DocumentComment', 5) - self.token_assert_helper(self.l.token(), 'TEXT', 'This is a sample spreadsheet', 5) + self.token_assert_helper(self.l.token(), 'DOC_NAME', 'DocumentName', 5) + self.token_assert_helper(self.l.token(), 'LINE', 'Sample_Document-V2.1', + 5) + self.token_assert_helper(self.l.token(), 'DOC_COMMENT', + 'DocumentComment', 6) + self.token_assert_helper(self.l.token(), 'TEXT', + 'This is a sample spreadsheet', 6) def test_creation_info(self): data = ''' @@ -105,6 +111,7 @@ class TestParser(TestCase): document_str = '\n'.join([ 'SPDXVersion: SPDX-2.1', 'DataLicense: CC0-1.0', + 'DocumentName: Sample_Document-V2.1', 'DocumentComment: Sample Comment' ]) @@ -169,6 +176,7 @@ def test_doc(self): assert not error assert document.version == Version(major=2, minor=1) assert document.data_license.identifier == 'CC0-1.0' + assert document.name == 'Sample_Document-V2.1' assert document.comment == 'Sample Comment' def test_creation_info(self):