Skip to content

Add 'namespace' field to the 'Document' class #70

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

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 1 addition & 1 deletion data/SPDXRdfExample.rdf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
xmlns:j.0="http://usefulinc.com/ns/doap#"
xmlns="http://spdx.org/rdf/terms#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<SpdxDocument rdf:about="http://www.spdx.org/tools#SPDXANALYSIS">
<SpdxDocument rdf:about="https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301">
<creationInfo>
<CreationInfo>
<created>2010-02-03T00:00:00Z</created>
Expand Down
1 change: 1 addition & 0 deletions data/SPDXSimpleTag.tag
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Document info
SPDXVersion: SPDX-2.1
DataLicense: CC0-1.0
DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301
DocumentComment: <text>Sample Comment</text>

# Creation info
Expand Down
1 change: 1 addition & 0 deletions data/SPDXTagExample.tag
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
SPDXVersion: SPDX-2.1
DataLicense: CC0-1.0
DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301
DocumentComment: <text>This is a sample spreadsheet</text>

## Creation Information
Expand Down
16 changes: 15 additions & 1 deletion spdx/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ class Document(object):
- version: Spec version. Mandatory, one - Type: Version.
- data_license: SPDX-Metadata license. Mandatory, one. Type: License.
- comment: Comments on the SPDX file, optional one. Type: str
- namespace: SPDX document specific namespace. Mandatory, one. Type: str
- creation_info: SPDX file creation info. Mandatory, one. Type: CreationInfo
- package: Package described by this document. Mandatory, one. Type: Package
- extracted_licenses: List of licenses extracted that are not part of the
Expand All @@ -198,12 +199,14 @@ 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, comment=None,
namespace=None, package=None):
# avoid recursive impor
from spdx.creationinfo import CreationInfo
self.version = version
self.data_license = data_license
self.comment = comment
self.namespace = namespace
self.creation_info = CreationInfo()
self.package = package
self.extracted_licenses = []
Expand Down Expand Up @@ -237,6 +240,7 @@ def validate(self, messages=None):

return (self.validate_version(messages)
and self.validate_data_lics(messages)
and self.validate_namespace(messages)
and self.validate_creation_info(messages)
and self.validate_package(messages)
and self.validate_extracted_licenses(messages)
Expand Down Expand Up @@ -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_namespace(self, messages=None):
# FIXME: messages should be returned
messages = messages if messages is not None else []

if self.namespace is None:
messages.append('Document has no namespace.')
return False
else:
return True

def validate_reviews(self, messages=None):
# FIXME: messages should be returned
messages = messages if messages is not None else []
Expand Down
1 change: 1 addition & 0 deletions spdx/parsers/lexers/tagvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Lexer(object):
'SPDXVersion': 'DOC_VERSION',
'DataLicense': 'DOC_LICENSE',
'DocumentComment': 'DOC_COMMENT',
'DocumentNamespace': 'DOC_NAMESPACE',
# Creation info
'Creator': 'CREATOR',
'Created': 'CREATED',
Expand Down
12 changes: 11 additions & 1 deletion spdx/parsers/rdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
ERROR_MESSAGES = {
'DOC_VERS_VALUE': 'Invalid specVersion \'{0}\' must be SPDX-M.N where M and N are numbers.',
'DOC_D_LICS': 'Invalid dataLicense \'{0}\' must be http://spdx.org/licenses/CC0-1.0.',
'DOC_NAMESPACE_VALUE': 'Invalid DocumentNamespace value {0}, must contain a scheme (e.g. "https:") '
'and should not contain the "#" delimiter.',
'LL_VALUE': 'Invalid licenseListVersion \'{0}\' must be of the format N.N where N is a number',
'CREATED_VALUE': 'Invalid created value \'{0}\' must be date in ISO 8601 format.',
'CREATOR_VALUE': 'Invalid creator value \'{0}\' must be Organization, Tool or Person.',
Expand Down Expand Up @@ -799,7 +801,15 @@ 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, comment, and namespace."""
try:
if doc_term.count('#', 0, len(doc_term)) <= 1:
doc_namespace = doc_term.split('#')[0]
self.builder.set_doc_namespace(self.doc, doc_namespace)
else:
self.value_error('DOC_NAMESPACE_VALUE', doc_term)
except SPDXValueError:
self.value_error('DOC_NAMESPACE_VALUE', doc_term)
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))
Expand Down
17 changes: 17 additions & 0 deletions spdx/parsers/rdfbuilders.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from spdx.parsers.builderexceptions import OrderError
from spdx.parsers.builderexceptions import SPDXValueError
from spdx.parsers import tagvaluebuilders
from spdx.parsers import validations


class DocBuilder(object):
Expand Down Expand Up @@ -80,13 +81,29 @@ def set_doc_comment(self, doc, comment):
else:
raise CardinalityError('Document::Comment')

def set_doc_namespace(self, doc, namespace):
"""Sets the document namespace.
Raise SPDXValueError if malformed value, CardinalityError
if already defined.
"""
if not self.doc_namespace_set:
self.doc_namespace_set = True
if validations.validate_doc_namespace(namespace):
doc.namespace = namespace
return True
else:
raise SPDXValueError('Document::Namespace')
else:
raise CardinalityError('Document::Comment')

def reset_document(self):
"""
Reset the internal state to allow building new document
"""
# FIXME: this state does not make sense
self.doc_version_set = False
self.doc_comment_set = False
self.doc_namespace_set = False
self.doc_data_lics_set = False


Expand Down
26 changes: 26 additions & 0 deletions spdx/parsers/tagvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
'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_COMMENT_VALUE_TYPE': 'DocumentComment value must be free form text between <text></text> tags, line:{0}',
'DOC_NAMESPACE_VALUE': 'Invalid DocumentNamespace value {0}, must contain a scheme (e.g. "https:") '
'and should not contain the "#" delimiter, line:{1}',
'DOC_NAMESPACE_VALUE_TYPE': 'Invalid DocumentNamespace value, must contain a scheme (e.g. "https:") '
'and should not contain the "#" delimiter, 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}',
'REVIEW_DATE_VALUE_TYPE': 'ReviewDate value must be date in ISO 8601 format, line: {0}',
Expand Down Expand Up @@ -107,6 +111,7 @@ def p_attrib(self, p):
"""attrib : spdx_version
| data_lics
| doc_comment
| doc_namespace
| creator
| created
| creator_comment
Expand Down Expand Up @@ -1077,6 +1082,27 @@ def p_doc_comment_2(self, p):
msg = ERROR_MESSAGES['DOC_COMMENT_VALUE_TYPE'].format(p.lineno(1))
self.logger.log(msg)

def p_doc_namespace_1(self, p):
"""doc_namespace : DOC_NAMESPACE LINE"""
try:
if six.PY2:
value = p[2].decode(encoding='utf-8')
else:
value = p[2]
self.builder.set_doc_namespace(self.document, value)
except SPDXValueError:
self.error = True
msg = ERROR_MESSAGES['DOC_NAMESPACE_VALUE'].format(p[2], p.lineno(2))
self.logger.log(msg)
except CardinalityError:
self.more_than_one_error('DocumentNamespace', p.lineno(1))

def p_doc_namespace_2(self, p):
"""doc_namespace : DOC_NAMESPACE error"""
self.error = True
msg = ERROR_MESSAGES['DOC_NAMESPACE_VALUE_TYPE'].format(p.lineno(1))
self.logger.log(msg)

def p_data_license_1(self, p):
"""data_lics : DOC_LICENSE LINE"""
try:
Expand Down
16 changes: 16 additions & 0 deletions spdx/parsers/tagvaluebuilders.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,27 @@ def set_doc_comment(self, doc, comment):
else:
raise CardinalityError('Document::Comment')

def set_doc_namespace(self, doc, namespace):
"""Sets the document namespace.
Raise SPDXValueError if malformed value, CardinalityError
if already defined.
"""
if not self.doc_namespace_set:
self.doc_namespace_set = True
if validations.validate_doc_namespace(namespace):
doc.namespace = namespace
return True
else:
raise SPDXValueError('Document::Namespace')
else:
raise CardinalityError('Document::Comment')

def reset_document(self):
"""Resets the state to allow building new documents"""
# FIXME: this state does not make sense
self.doc_version_set = False
self.doc_comment_set = False
self.doc_namespace_set = False
self.doc_data_lics_set = False


Expand Down
10 changes: 10 additions & 0 deletions spdx/parsers/validations.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ def validate_doc_comment(value, optional=False):
return validate_is_free_form_text(value, optional)


def validate_doc_namespace(value, optional=False):
if value is None:
return optional
elif ((value.startswith('http://') or value.startswith('https://') or
value.startswith('ftp://')) and ('#' not in value)):
return True
else:
return False


def validate_creator(value, optional=False):
if value is None:
return optional
Expand Down
1 change: 1 addition & 0 deletions spdx/writers/tagvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -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('DocumentNamespace', document.namespace, out)
if document.has_comment:
write_text_value('DocumentComment', document.comment, out)
write_separators(out)
Expand Down
1 change: 1 addition & 0 deletions tests/data/doc_write/tv-simple-plus.tv
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Document Information
SPDXVersion: SPDX-2.1
DataLicense: CC0-1.0
DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301
# Creation Info
# Package
PackageName: some/path
Expand Down
1 change: 1 addition & 0 deletions tests/data/doc_write/tv-simple.tv
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Document Information
SPDXVersion: SPDX-2.1
DataLicense: CC0-1.0
DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301
# Creation Info
# Package
PackageName: some/path
Expand Down
16 changes: 16 additions & 0 deletions tests/test_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ 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_doc_namespace(self):
doc_namespace_str = 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301'
self.builder.set_doc_namespace(self.document, doc_namespace_str)
assert self.document.namespace == doc_namespace_str

@testing_utils.raises(builders.SPDXValueError)
def test_doc_namespace_value(self):
doc_namespace_str = 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-DOCUMENT'
self.builder.set_doc_data_lics(self.document, doc_namespace_str)

@testing_utils.raises(builders.CardinalityError)
def test_doc_namespace_cardinality(self):
doc_namespace_str = 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301'
self.builder.set_doc_namespace(self.document, doc_namespace_str)
self.builder.set_doc_namespace(self.document, doc_namespace_str)

def test_correct_data_comment(self):
comment_str = 'This is a comment.'
comment_text = '<text>' + comment_str + '</text>'
Expand Down
9 changes: 6 additions & 3 deletions tests/test_document.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301')
pack = doc.package = Package('some/path', NoAssert())
file1 = File('./some/path/tofile')
file1.name = './some/path/tofile'
Expand All @@ -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'),
namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301')
doc.creation_info.add_creator(Tool('ScanCode'))
doc.creation_info.set_created_now()

Expand Down Expand Up @@ -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'),
namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301')
doc.creation_info.add_creator(Tool('ScanCode'))
doc.creation_info.set_created_now()

Expand Down
16 changes: 12 additions & 4 deletions tests/test_tag_value_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,21 @@ def test_document(self):
SPDXVersion: SPDX-2.1
# Comment.
DataLicense: CC0-1.0
DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301
DocumentComment: <text>This is a sample spreadsheet</text>
'''
self.l.input(data)
self.token_assert_helper(self.l.token(), 'DOC_VERSION', 'SPDXVersion', 2)
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', '<text>This is a sample spreadsheet</text>', 5)
self.token_assert_helper(self.l.token(), 'DOC_NAMESPACE',
'DocumentNamespace', 5)
self.token_assert_helper(self.l.token(), 'LINE',
'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301',
5)
self.token_assert_helper(self.l.token(), 'DOC_COMMENT', 'DocumentComment', 6)
self.token_assert_helper(self.l.token(), 'TEXT', '<text>This is a sample spreadsheet</text>', 6)

def test_creation_info(self):
data = '''
Expand Down Expand Up @@ -105,7 +111,8 @@ class TestParser(TestCase):
document_str = '\n'.join([
'SPDXVersion: SPDX-2.1',
'DataLicense: CC0-1.0',
'DocumentComment: <text>Sample Comment</text>'
'DocumentComment: <text>Sample Comment</text>',
'DocumentNamespace: https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301'
])

creation_str = '\n'.join([
Expand Down Expand Up @@ -170,6 +177,7 @@ def test_doc(self):
assert document.version == Version(major=2, minor=1)
assert document.data_license.identifier == 'CC0-1.0'
assert document.comment == 'Sample Comment'
assert document.namespace == 'https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301'

def test_creation_info(self):
document, error = self.p.parse(self.complete_str)
Expand Down Expand Up @@ -208,4 +216,4 @@ def test_file(self):


if __name__ == '__main__':
unittest.main()
unittest.main()