Repository: spdx/tools-python

Issue:
extract license class to separate file
To be able to use type hints properly in `file.py` the `License` class should be extracted from `document.py` to a separate file.

Discussion:

Patch:
diff --git a/examples/write_tv.py b/examples/write_tv.py
index 3ed332e..094cec5 100755
--- a/examples/write_tv.py
+++ b/examples/write_tv.py
@@ -7,7 +7,8 @@ if __name__ == "__main__":
     import codecs
     from spdx.writers.tagvalue import write_document, InvalidDocumentError
     from spdx.parsers.loggers import ErrorMessages
-    from spdx.document import Document, License, LicenseConjunction, ExtractedLicense
+    from spdx.document import Document
+    from spdx.license import License, LicenseConjunction, ExtractedLicense
     from spdx.version import Version
     from spdx.creationinfo import Person
     from spdx.review import Review
diff --git a/spdx/document.py b/spdx/document.py
index e7aa544..0a371a5 100644
--- a/spdx/document.py
+++ b/spdx/document.py
@@ -8,15 +8,13 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+from spdx.license import ExtractedLicense
 from spdx.parsers.loggers import ErrorMessages
 
 import warnings
 
 from functools import total_ordering
 
-from spdx import config
-
 
 @total_ordering
 class ExternalDocumentRef(object):
@@ -72,199 +70,6 @@ class ExternalDocumentRef(object):
             messages.append("ExternalDocumentRef has no Checksum.")
 
 
-def _add_parens(required, text):
-    """
-    Add parens around a license expression if `required` is True, otherwise
-    return `text` unmodified.
-    """
-    return "({})".format(text) if required else text
-
-
-@total_ordering
-class License(object):
-    def __init__(self, full_name, identifier):
-        """if one of the argument is None, we try to map as much as possible
-        """
-        self._full_name = None
-        self._identifier = None
-        self.set_full_name(full_name)
-        self.set_identifier(identifier)
-
-    @classmethod
-    def from_identifier(cls, identifier):
-        """If identifier exists in config.LICENSE_MAP
-        the full_name is retrieved from it. Otherwise
-        the full_name is the same as the identifier.
-        """
-        return cls(None, identifier)
-
-    @classmethod
-    def from_full_name(cls, full_name):
-        """
-        Return a new License for a full_name. If the full_name exists in
-        config.LICENSE_MAP the identifier is retrieved from it.
-        Otherwise the identifier is the same as the full_name.
-        """
-        return cls(full_name, None)
-
-    @property
-    def url(self):
-        return "http://spdx.org/licenses/{0}".format(self.identifier)
-
-    @property
-    def full_name(self):
-        return self._full_name
-
-    @full_name.setter
-    def full_name(self, value):
-        self.set_full_name(value)
-
-    def set_full_name(self, value):
-
-        if value is None:
-            return
-        if self._identifier is None:
-            if value in config.LICENSE_MAP:
-                self._identifier = config.LICENSE_MAP[value]
-            else:
-                self._identifier = value
-        self._full_name = value
-
-    @property
-    def identifier(self):
-        return self._identifier
-
-    @identifier.setter
-    def identifier(self, value):
-        self.set_identifier(value)
-
-    def set_identifier(self, value):
-        if value is None:
-            return
-        if self._full_name is None:
-            if value in config.LICENSE_MAP:
-                self._full_name = config.LICENSE_MAP[value]
-            else:
-                self._full_name = value
-
-        self._identifier = value
-
-
-    def __eq__(self, other):
-        return (
-            isinstance(other, License)
-            and self.identifier == other.identifier
-            and self.full_name == other.full_name
-        )
-
-    def __lt__(self, other):
-        return isinstance(other, License) and self.identifier < other.identifier
-
-    def __str__(self):
-        return self.identifier
-
-    def __hash__(self):
-        return self.identifier.__hash__()
-
-
-class LicenseConjunction(License):
-    """
-    A conjunction of two licenses.
-    """
-
-    def __init__(self, license_1, license_2):
-        self.license_1 = license_1
-        self.license_2 = license_2
-        super(LicenseConjunction, self).__init__(self.full_name, self.identifier)
-
-    @property
-    def full_name(self):
-        license_1_complex = type(self.license_1) == LicenseDisjunction
-        license_2_complex = type(self.license_2) == LicenseDisjunction
-
-        return "{0} AND {1}".format(
-            _add_parens(license_1_complex, self.license_1.full_name),
-            _add_parens(license_2_complex, self.license_2.full_name),
-        )
-
-    @property
-    def identifier(self):
-        license_1_complex = type(self.license_1) == LicenseDisjunction
-        license_2_complex = type(self.license_2) == LicenseDisjunction
-
-        return "{0} AND {1}".format(
-            _add_parens(license_1_complex, self.license_1.identifier),
-            _add_parens(license_2_complex, self.license_2.identifier),
-        )
-
-
-class LicenseDisjunction(License):
-    """
-    A disjunction of two licenses.
-    """
-
-    def __init__(self, license_1, license_2):
-        self.license_1 = license_1
-        self.license_2 = license_2
-        super(LicenseDisjunction, self).__init__(self.full_name, self.identifier)
-
-    @property
-    def full_name(self):
-        license_1_complex = type(self.license_1) == LicenseConjunction
-        license_2_complex = type(self.license_2) == LicenseConjunction
-
-        return "{0} OR {1}".format(
-            _add_parens(license_1_complex, self.license_1.full_name),
-            _add_parens(license_2_complex, self.license_2.full_name),
-        )
-
-    @property
-    def identifier(self):
-        license_1_complex = type(self.license_1) == LicenseConjunction
-        license_2_complex = type(self.license_2) == LicenseConjunction
-
-        return "{0} OR {1}".format(
-            _add_parens(license_1_complex, self.license_1.identifier),
-            _add_parens(license_2_complex, self.license_2.identifier),
-        )
-
-
-@total_ordering
-class ExtractedLicense(License):
-    """
-    Represent an ExtractedLicense with its additional attributes:
-    - text: Extracted text, str. Mandatory.
-    - cross_ref: list of cross references.
-    - comment: license comment, str.
-    - full_name: license name. str or utils.NoAssert.
-    """
-
-    def __init__(self, identifier):
-        super(ExtractedLicense, self).__init__(None, identifier)
-        self.text = None
-        self.cross_ref = []
-        self.comment = None
-
-    def __eq__(self, other):
-        return (
-            isinstance(other, ExtractedLicense)
-            and self.identifier == other.identifier
-            and self.full_name == other.full_name
-        )
-
-    def __lt__(self, other):
-        return (
-            isinstance(other, ExtractedLicense) and self.identifier < other.identifier
-        )
-
-    def add_xref(self, ref):
-        self.cross_ref.append(ref)
-
-    def validate(self, messages):
-        if self.text is None:
-            messages.append("ExtractedLicense text can not be None")
-
-
 class Document(object):
     """
     Represent an SPDX document with these fields:
@@ -486,4 +291,4 @@ class Document(object):
                 messages.append(
                     "Document extracted licenses must be of type "
                     "spdx.document.ExtractedLicense and not " + type(lic)
-                )
\ No newline at end of file
+                )
diff --git a/spdx/file.py b/spdx/file.py
index 7910e36..937d2a1 100644
--- a/spdx/file.py
+++ b/spdx/file.py
@@ -13,7 +13,7 @@ from functools import total_ordering
 import hashlib
 
 from spdx import checksum
-from spdx import document
+from spdx import license
 from spdx import utils
 
 
@@ -43,10 +43,10 @@ class File(object):
     - type: one of FileType.SOURCE, FileType.BINARY, FileType.ARCHIVE
       and FileType.OTHER, optional zero or one.
     - chksum: SHA1, Mandatory one.
-    - conc_lics: Mandatory one. document.License or utils.NoAssert or utils.SPDXNone.
+    - conc_lics: Mandatory one. license.License or utils.NoAssert or utils.SPDXNone.
     - licenses_in_file: list of licenses found in file, mandatory one or more.
-      document.License or utils.SPDXNone or utils.NoAssert.
-    - document.license or utils.NoAssert or utils.SPDXNone.
+      license.License or utils.SPDXNone or utils.NoAssert.
+    - license.License or utils.NoAssert or utils.SPDXNone.
     - license_comment: Optional.
     - copyright: Copyright text, Mandatory one. utils.NoAssert or utils.SPDXNone or str.
     - notice: optional One, str.
@@ -140,7 +140,7 @@ class File(object):
         ):
             messages.append(
                 "File copyright must be str or unicode or "
-                "utils.NoAssert or utils.SPDXNone"
+                "spdx.utils.NoAssert or spdx.utils.SPDXNone"
             )
 
         return messages
@@ -158,24 +158,24 @@ class File(object):
     def validate_licenses_in_file(self, messages):
         for license_in_file in self.licenses_in_file:
             if not isinstance(
-                license_in_file, (utils.SPDXNone, utils.NoAssert, document.License)
+                license_in_file, (utils.SPDXNone, utils.NoAssert, license.License)
             ):
                 messages.append(
                     "License in file must be instance of "
                     "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
-                    "spdx.document.License"
+                    "spdx.license.License"
                 )
 
         return messages
 
     def validate_concluded_license(self, messages):
         if self.conc_lics and not isinstance(
-            self.conc_lics, (utils.SPDXNone, utils.NoAssert, document.License)
+            self.conc_lics, (utils.SPDXNone, utils.NoAssert, license.License)
         ):
             messages.append(
                 "File concluded license must be instance of "
                 "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
-                "spdx.document.License"
+                "spdx.license.License"
             )
 
         return messages
diff --git a/spdx/license.py b/spdx/license.py
new file mode 100644
index 0000000..ea973f5
--- /dev/null
+++ b/spdx/license.py
@@ -0,0 +1,207 @@
+# Copyright (c) 2022 spdx contributors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#     http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from functools import total_ordering
+
+from spdx import config
+
+
+@total_ordering
+class License(object):
+    def __init__(self, full_name, identifier):
+        """if one of the argument is None, we try to map as much as possible
+        """
+        self._full_name = None
+        self._identifier = None
+        self.set_full_name(full_name)
+        self.set_identifier(identifier)
+
+    @classmethod
+    def from_identifier(cls, identifier):
+        """If identifier exists in config.LICENSE_MAP
+        the full_name is retrieved from it. Otherwise
+        the full_name is the same as the identifier.
+        """
+        return cls(None, identifier)
+
+    @classmethod
+    def from_full_name(cls, full_name):
+        """
+        Return a new License for a full_name. If the full_name exists in
+        config.LICENSE_MAP the identifier is retrieved from it.
+        Otherwise the identifier is the same as the full_name.
+        """
+        return cls(full_name, None)
+
+    @property
+    def url(self):
+        return "http://spdx.org/licenses/{0}".format(self.identifier)
+
+    @property
+    def full_name(self):
+        return self._full_name
+
+    @full_name.setter
+    def full_name(self, value):
+        self.set_full_name(value)
+
+    def set_full_name(self, value):
+
+        if value is None:
+            return
+        if self._identifier is None:
+            if value in config.LICENSE_MAP:
+                self._identifier = config.LICENSE_MAP[value]
+            else:
+                self._identifier = value
+        self._full_name = value
+
+    @property
+    def identifier(self):
+        return self._identifier
+
+    @identifier.setter
+    def identifier(self, value):
+        self.set_identifier(value)
+
+    def set_identifier(self, value):
+        if value is None:
+            return
+        if self._full_name is None:
+            if value in config.LICENSE_MAP:
+                self._full_name = config.LICENSE_MAP[value]
+            else:
+                self._full_name = value
+
+        self._identifier = value
+
+
+    def __eq__(self, other):
+        return (
+            isinstance(other, License)
+            and self.identifier == other.identifier
+            and self.full_name == other.full_name
+        )
+
+    def __lt__(self, other):
+        return isinstance(other, License) and self.identifier < other.identifier
+
+    def __str__(self):
+        return self.identifier
+
+    def __hash__(self):
+        return self.identifier.__hash__()
+
+
+class LicenseConjunction(License):
+    """
+    A conjunction of two licenses.
+    """
+
+    def __init__(self, license_1, license_2):
+        self.license_1 = license_1
+        self.license_2 = license_2
+        super(LicenseConjunction, self).__init__(self.full_name, self.identifier)
+
+    @property
+    def full_name(self):
+        license_1_complex = type(self.license_1) == LicenseDisjunction
+        license_2_complex = type(self.license_2) == LicenseDisjunction
+
+        return "{0} AND {1}".format(
+            _add_parens(license_1_complex, self.license_1.full_name),
+            _add_parens(license_2_complex, self.license_2.full_name),
+        )
+
+    @property
+    def identifier(self):
+        license_1_complex = type(self.license_1) == LicenseDisjunction
+        license_2_complex = type(self.license_2) == LicenseDisjunction
+
+        return "{0} AND {1}".format(
+            _add_parens(license_1_complex, self.license_1.identifier),
+            _add_parens(license_2_complex, self.license_2.identifier),
+        )
+
+
+class LicenseDisjunction(License):
+    """
+    A disjunction of two licenses.
+    """
+
+    def __init__(self, license_1, license_2):
+        self.license_1 = license_1
+        self.license_2 = license_2
+        super(LicenseDisjunction, self).__init__(self.full_name, self.identifier)
+
+    @property
+    def full_name(self):
+        license_1_complex = type(self.license_1) == LicenseConjunction
+        license_2_complex = type(self.license_2) == LicenseConjunction
+
+        return "{0} OR {1}".format(
+            _add_parens(license_1_complex, self.license_1.full_name),
+            _add_parens(license_2_complex, self.license_2.full_name),
+        )
+
+    @property
+    def identifier(self):
+        license_1_complex = type(self.license_1) == LicenseConjunction
+        license_2_complex = type(self.license_2) == LicenseConjunction
+
+        return "{0} OR {1}".format(
+            _add_parens(license_1_complex, self.license_1.identifier),
+            _add_parens(license_2_complex, self.license_2.identifier),
+        )
+
+
+@total_ordering
+class ExtractedLicense(License):
+    """
+    Represent an ExtractedLicense with its additional attributes:
+    - text: Extracted text, str. Mandatory.
+    - cross_ref: list of cross references.
+    - comment: license comment, str.
+    - full_name: license name. str or utils.NoAssert.
+    """
+
+    def __init__(self, identifier):
+        super(ExtractedLicense, self).__init__(None, identifier)
+        self.text = None
+        self.cross_ref = []
+        self.comment = None
+
+    def __eq__(self, other):
+        return (
+            isinstance(other, ExtractedLicense)
+            and self.identifier == other.identifier
+            and self.full_name == other.full_name
+        )
+
+    def __lt__(self, other):
+        return (
+            isinstance(other, ExtractedLicense) and self.identifier < other.identifier
+        )
+
+    def add_xref(self, ref):
+        self.cross_ref.append(ref)
+
+    def validate(self, messages):
+        if self.text is None:
+            messages.append("ExtractedLicense text can not be None")
+
+
+def _add_parens(required, text):
+    """
+    Add parens around a license expression if `required` is True, otherwise
+    return `text` unmodified.
+    """
+    return "({})".format(text) if required else text
diff --git a/spdx/package.py b/spdx/package.py
index d6727bd..976e2e9 100644
--- a/spdx/package.py
+++ b/spdx/package.py
@@ -17,7 +17,7 @@ from typing import List, Optional
 
 from spdx import checksum
 from spdx import creationinfo
-from spdx import document
+from spdx import license
 from spdx import utils
 from spdx.parsers.loggers import ErrorMessages
 
@@ -62,13 +62,13 @@ class Package(object):
      we allow this to be Optional even when files_analyzed is True/None.
      - check_sum: Optional , spdx.checksum.Algorithm.
      - source_info: Optional string.
-     - conc_lics: Mandatory spdx.document.License or spdx.utils.SPDXNone or
-     - spdx.utils.NoAssert.
-     - license_declared: Mandatory spdx.document.License or spdx.utils.SPDXNone or
-     spdx.utils.NoAssert.
+     - conc_lics: Mandatory license.License or utils.SPDXNone or
+     utils.NoAssert.
+     - license_declared: Mandatory license.License or utils.SPDXNone or
+     utils.NoAssert.
      - license_comment: optional string.
-     - licenses_from_files: list of spdx.document.License or spdx.utils.SPDXNone or
-     - spdx.utils.NoAssert.
+     - licenses_from_files: list of license.License or utils.SPDXNone or
+     utils.NoAssert.
      - cr_text: Copyright text, string , utils.NoAssert or utils.SPDXNone. Mandatory.
      - summary: Optional str.
      - description: Optional str.
@@ -204,31 +204,31 @@ class Package(object):
             )
 
         if self.conc_lics and not isinstance(
-            self.conc_lics, (utils.SPDXNone, utils.NoAssert, document.License)
+            self.conc_lics, (utils.SPDXNone, utils.NoAssert, license.License)
         ):
             messages.append(
                 "Package concluded license must be instance of "
                 "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
-                "spdx.document.License"
+                "spdx.license.License"
             )
 
         if self.license_declared and not isinstance(
-            self.license_declared, (utils.SPDXNone, utils.NoAssert, document.License)
+            self.license_declared, (utils.SPDXNone, utils.NoAssert, license.License)
         ):
             messages.append(
                 "Package declared license must be instance of "
                 "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
-                "spdx.document.License"
+                "spdx.license.License"
             )
 
         license_from_file_check = lambda prev, el: prev and isinstance(
-            el, (document.License, utils.SPDXNone, utils.NoAssert)
+            el, (license.License, utils.SPDXNone, utils.NoAssert)
         )
         if not reduce(license_from_file_check, self.licenses_from_files, True):
             messages.append(
                 "Each element in licenses_from_files must be instance of "
                 "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
-                "spdx.document.License"
+                "spdx.license.License"
             )
 
         return messages
diff --git a/spdx/parsers/jsonyamlxml.py b/spdx/parsers/jsonyamlxml.py
index 33f5000..1850d12 100644
--- a/spdx/parsers/jsonyamlxml.py
+++ b/spdx/parsers/jsonyamlxml.py
@@ -14,8 +14,7 @@ from typing import List, Dict, Tuple
 
 from spdx import document
 from spdx import utils
-from spdx.document import LicenseConjunction
-from spdx.document import LicenseDisjunction
+from spdx.license import LicenseConjunction, LicenseDisjunction
 from spdx.package import ExternalPackageRef, PackagePurpose
 from spdx.parsers import rdf
 from spdx.parsers.builderexceptions import SPDXValueError, CardinalityError, OrderError
diff --git a/spdx/parsers/rdf.py b/spdx/parsers/rdf.py
index 289ba8d..317b0c1 100644
--- a/spdx/parsers/rdf.py
+++ b/spdx/parsers/rdf.py
@@ -19,6 +19,7 @@ from rdflib import RDF
 from rdflib import RDFS
 
 from spdx import document
+from spdx import license
 from spdx import utils
 from spdx.parsers.builderexceptions import CardinalityError
 from spdx.parsers.builderexceptions import SPDXValueError
@@ -144,7 +145,7 @@ class LicenseParser(BaseParser):
             if special == lics:
                 if self.LICS_REF_REGEX.match(lics):
                     # Is a license ref i.e LicenseRef-1
-                    return document.License.from_identifier(str(lics))
+                    return license.License.from_identifier(str(lics))
                 else:
                     # Not a known license form
                     raise SPDXValueError("License")
@@ -153,7 +154,7 @@ class LicenseParser(BaseParser):
                 return special
         else:
             # license url
-            return document.License.from_identifier(lics[ident_start:])
+            return license.License.from_identifier(lics[ident_start:])
 
     def get_extr_license_ident(self, extr_lic):
         """
@@ -251,7 +252,7 @@ class LicenseParser(BaseParser):
 
         # Set fields
         # FIXME: the constructor of the license should always accept a name
-        lic = document.ExtractedLicense(ident)
+        lic = license.ExtractedLicense(ident)
         if text is not None:
             lic.text = text
         if name is not None:
@@ -296,14 +297,14 @@ class LicenseParser(BaseParser):
         Return a license representing the conjunction from a list of
         license resources or None.
         """
-        return self._handle_license_list(lics_set, cls=document.LicenseConjunction)
+        return self._handle_license_list(lics_set, cls=license.LicenseConjunction)
 
     def handle_disjunctive_list(self, lics_set):
         """
         Return a license representing the disjunction from a list of
         license resources or None.
         """
-        return self._handle_license_list(lics_set, cls=document.LicenseDisjunction)
+        return self._handle_license_list(lics_set, cls=license.LicenseDisjunction)
 
 
 class PackageParser(LicenseParser):
diff --git a/spdx/parsers/rdfbuilders.py b/spdx/parsers/rdfbuilders.py
index 1a99663..c32dce4 100644
--- a/spdx/parsers/rdfbuilders.py
+++ b/spdx/parsers/rdfbuilders.py
@@ -12,7 +12,7 @@
 import re
 
 from spdx import checksum
-from spdx import document
+from spdx import license
 from spdx import package
 from spdx import version
 from spdx.parsers.builderexceptions import CardinalityError
@@ -60,7 +60,7 @@ class DocBuilder(object):
             res_parts = res.split("/")
             if len(res_parts) != 0:
                 identifier = res_parts[-1]
-                doc.data_license = document.License.from_identifier(identifier)
+                doc.data_license = license.License.from_identifier(identifier)
             else:
                 raise SPDXValueError("Document::License")
         else:
diff --git a/spdx/parsers/tagvalue.py b/spdx/parsers/tagvalue.py
index 081de4f..2444d3f 100644
--- a/spdx/parsers/tagvalue.py
+++ b/spdx/parsers/tagvalue.py
@@ -12,7 +12,9 @@
 import re
 
 from ply import yacc
+
 from spdx import config
+from spdx import license
 from spdx import utils
 from spdx.parsers.builderexceptions import CardinalityError
 from spdx.parsers.builderexceptions import OrderError
@@ -538,7 +540,7 @@ class Parser(object):
     def p_file_lic_info_value_3(self, p):
         """file_lic_info_value : LINE"""
         value = p[1]
-        p[0] = document.License.from_identifier(value)
+        p[0] = license.License.from_identifier(value)
 
     def p_conc_license_1(self, p):
         """conc_license : NO_ASSERT"""
@@ -553,7 +555,7 @@ class Parser(object):
         value = p[1]
         ref_re = re.compile("LicenseRef-.+", re.UNICODE)
         if (p[1] in config.LICENSE_MAP.keys()) or (ref_re.match(p[1]) is not None):
-            p[0] = document.License.from_identifier(value)
+            p[0] = license.License.from_identifier(value)
         else:
             p[0] = self.license_list_parser.parse(value)
 
@@ -853,7 +855,7 @@ class Parser(object):
     def p_pkg_lic_ff_value_3(self, p):
         """pkg_lic_ff_value : LINE"""
         value = p[1]
-        p[0] = document.License.from_identifier(value)
+        p[0] = license.License.from_identifier(value)
 
     def p_pkg_lic_ff_2(self, p):
         """pkg_lic_ff : PKG_LICS_FFILE error"""
@@ -1388,7 +1390,7 @@ class Parser(object):
     def p_snip_lic_info_value_3(self, p):
         """snip_lic_info_value : LINE"""
         value = p[1]
-        p[0] = document.License.from_identifier(value)
+        p[0] = license.License.from_identifier(value)
 
     def p_reviewer_1(self, p):
         """reviewer : REVIEWER entity"""
diff --git a/spdx/parsers/tagvaluebuilders.py b/spdx/parsers/tagvaluebuilders.py
index 59a2b21..292cbcc 100644
--- a/spdx/parsers/tagvaluebuilders.py
+++ b/spdx/parsers/tagvaluebuilders.py
@@ -14,8 +14,8 @@ import re
 from spdx import annotation
 from spdx import checksum
 from spdx import creationinfo
-from spdx import document
 from spdx import file
+from spdx import license
 from spdx import package
 from spdx import relationship
 from spdx import review
@@ -100,7 +100,7 @@ class DocBuilder(object):
             raise SPDXValueError("Document::DataLicense")
 
         self.doc_data_lics_set = True
-        doc.data_license = document.License.from_identifier(lics)
+        doc.data_license = license.License.from_identifier(lics)
         return True
 
     def set_doc_name(self, doc, name):
@@ -1396,7 +1396,7 @@ class LicenseBuilder(object):
         if not validations.validate_extracted_lic_id(lic_id):
             raise SPDXValueError("ExtractedLicense::id")
 
-        doc.add_extr_lic(document.ExtractedLicense(lic_id))
+        doc.add_extr_lic(license.ExtractedLicense(lic_id))
         return True
 
     def set_lic_text(self, doc, text):
diff --git a/spdx/parsers/validations.py b/spdx/parsers/validations.py
index a788569..32823c7 100644
--- a/spdx/parsers/validations.py
+++ b/spdx/parsers/validations.py
@@ -12,9 +12,10 @@
 import re
 
 import rdflib
+
 from spdx import creationinfo
+from spdx import license
 from spdx import utils
-from spdx import document
 
 
 def validate_is_free_form_text(value, optional=False):
@@ -247,7 +248,7 @@ def validate_file_cpyright(value, optional=False):
 def validate_lics_from_file(value, optional=False):
     if value is None:
         return optional
-    elif isinstance(value, (document.License, utils.SPDXNone, utils.NoAssert)):
+    elif isinstance(value, (license.License, utils.SPDXNone, utils.NoAssert)):
         return True
     else:
         return False
@@ -260,7 +261,7 @@ def validate_file_notice(value, optional=False):
 def validate_lics_conc(value, optional=False):
     if value is None:
         return optional
-    elif isinstance(value, (utils.NoAssert, utils.SPDXNone, document.License)):
+    elif isinstance(value, (utils.NoAssert, utils.SPDXNone, license.License)):
         return True
     else:
         return False
@@ -269,7 +270,7 @@ def validate_lics_conc(value, optional=False):
 def validate_file_lics_in_file(value, optional=False):
     if value is None:
         return optional
-    elif isinstance(value, (utils.NoAssert, utils.SPDXNone, document.License)):
+    elif isinstance(value, (utils.NoAssert, utils.SPDXNone, license.License)):
         return True
     else:
         return False
@@ -329,7 +330,7 @@ def validate_snip_file_spdxid(value, optional=False):
 def validate_snip_lics_info(value, optional=False):
     if value is None:
         return optional
-    elif isinstance(value, (utils.NoAssert, utils.SPDXNone, document.License)):
+    elif isinstance(value, (utils.NoAssert, utils.SPDXNone, license.License)):
         return True
     else:
         return False
diff --git a/spdx/snippet.py b/spdx/snippet.py
index 3402cc4..cd79e3b 100644
--- a/spdx/snippet.py
+++ b/spdx/snippet.py
@@ -10,7 +10,7 @@
 # limitations under the License.
 from typing import Tuple, Optional
 
-from spdx import document
+from spdx import license
 from spdx import utils
 
 
@@ -30,10 +30,10 @@ class Snippet(object):
      which this snippet is associated with. Mandatory, one. Type: str.
      - conc_lics: Contains the license the SPDX file creator has concluded as
      governing the snippet or alternative values if the governing license
-     cannot be determined. Mandatory one. Type: document.License or
+     cannot be determined. Mandatory one. Type: license.License or
      utils.NoAssert or utils.SPDXNone.
      - licenses_in_snippet: The list of licenses found in the snippet.
-     Mandatory, one or more. Type: document.License or utils.SPDXNone or
+     Mandatory, one or more. Type: license.License or utils.SPDXNone or
      utils.NoAssert.
      - attribution_text: optional string.
      - byte_range: Defines the byte range in the original host file that the
@@ -83,7 +83,7 @@ class Snippet(object):
             (str, utils.NoAssert, utils.SPDXNone),
         ):
             messages.append(
-                "Snippet copyright must be str or unicode or utils.NoAssert or utils.SPDXNone"
+                "Snippet copyright must be str or unicode or spdx.utils.NoAssert or spdx.utils.SPDXNone"
             )
 
     def validate_snip_from_file_spdxid(self, messages):
@@ -92,23 +92,23 @@ class Snippet(object):
 
     def validate_concluded_license(self, messages):
         if self.conc_lics and not isinstance(
-            self.conc_lics, (utils.SPDXNone, utils.NoAssert, document.License)
+            self.conc_lics, (utils.SPDXNone, utils.NoAssert, license.License)
         ):
             messages.append(
                 "Snippet concluded license must be instance of "
                 "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
-                "spdx.document.License"
+                "spdx.license.License"
             )
 
     def validate_licenses_in_snippet(self, messages):
         for lic in self.licenses_in_snippet:
             if not isinstance(
-                lic, (document.License, utils.NoAssert, utils.SPDXNone)
+                lic, (license.License, utils.NoAssert, utils.SPDXNone)
             ):
                 messages.append(
                     "License in snippet must be instance of "
                     "spdx.utils.SPDXNone or spdx.utils.NoAssert or "
-                    "spdx.document.License"
+                    "spdx.license.License"
                 )
 
     def has_optional_field(self, field):
diff --git a/spdx/utils.py b/spdx/utils.py
index fa1756a..d691edb 100644
--- a/spdx/utils.py
+++ b/spdx/utils.py
@@ -15,7 +15,7 @@ import re
 from ply import lex
 from ply import yacc
 
-from spdx import document
+from spdx import license
 
 
 def datetime_iso_format(date):
@@ -163,7 +163,7 @@ class LicenseListParser(object):
     def p_disjunction_1(self, p):
         """disjunction : disjunction OR conjunction
         """
-        p[0] = document.LicenseDisjunction(p[1], p[3])
+        p[0] = license.LicenseDisjunction(p[1], p[3])
 
     def p_disjunction_2(self, p):
         """disjunction : conjunction
@@ -173,7 +173,7 @@ class LicenseListParser(object):
     def p_conjunction_1(self, p):
         """conjunction : conjunction AND license_atom
         """
-        p[0] = document.LicenseConjunction(p[1], p[3])
+        p[0] = license.LicenseConjunction(p[1], p[3])
 
     def p_conjunction_2(self, p):
         """conjunction : license_atom
@@ -183,7 +183,7 @@ class LicenseListParser(object):
     def p_license_atom_1(self, p):
         """license_atom : LICENSE
         """
-        p[0] = document.License.from_identifier(p[1])
+        p[0] = license.License.from_identifier(p[1])
 
     def p_license_atom_2(self, p):
         """license_atom : LP disjunction RP
diff --git a/spdx/writers/jsonyamlxml.py b/spdx/writers/jsonyamlxml.py
index be2dc50..a33f583 100644
--- a/spdx/writers/jsonyamlxml.py
+++ b/spdx/writers/jsonyamlxml.py
@@ -12,7 +12,7 @@ from typing import Dict
 
 from rdflib import Literal
 
-from spdx import document, utils
+from spdx import license, utils
 from spdx.package import ExternalPackageRef
 
 
@@ -33,11 +33,11 @@ class BaseWriter(object):
         Return a string representation of a license or spdx.utils special object
         """
         if isinstance(
-            license_field, (document.LicenseDisjunction, document.LicenseConjunction)
+            license_field, (license.LicenseDisjunction, license.LicenseConjunction)
         ):
             return "({})".format(license_field)
 
-        if isinstance(license_field, document.License):
+        if isinstance(license_field, license.License):
             license_str = license_field.identifier.__str__()
         else:
             license_str = license_field.__str__()
diff --git a/spdx/writers/rdf.py b/spdx/writers/rdf.py
index e700fad..79e46bc 100644
--- a/spdx/writers/rdf.py
+++ b/spdx/writers/rdf.py
@@ -18,9 +18,9 @@ from rdflib import RDFS
 from rdflib import URIRef
 from rdflib.compare import to_isomorphic
 
-from spdx import file
-from spdx import document
 from spdx import config
+from spdx import file
+from spdx import license
 from spdx import utils
 from spdx.package import Package
 from spdx.parsers.loggers import ErrorMessages
@@ -85,7 +85,7 @@ class LicenseWriter(BaseWriter):
 
     def licenses_from_tree_helper(self, current, licenses):
         if isinstance(
-            current, (document.LicenseConjunction, document.LicenseDisjunction)
+            current, (license.LicenseConjunction, license.LicenseDisjunction)
         ):
             self.licenses_from_tree_helper(current.license_1, licenses)
             self.licenses_from_tree_helper(current.license_2, licenses)
@@ -133,7 +133,7 @@ class LicenseWriter(BaseWriter):
         Handle single(no conjunction/disjunction) licenses.
         Return the created node.
         """
-        if isinstance(lic, document.ExtractedLicense):
+        if isinstance(lic, license.ExtractedLicense):
             return self.create_extracted_license(lic)
         if lic.identifier.rstrip("+") in config.LICENSE_MAP:
             return URIRef(lic.url)
@@ -146,7 +146,7 @@ class LicenseWriter(BaseWriter):
             if len(matches) != 0:
                 return self.create_extracted_license(matches[0])
             else:
-                lic = document.ExtractedLicense(lic.identifier)
+                lic = license.ExtractedLicense(lic.identifier)
                 warnings.warn(
                     "Missing extracted license: {0}".format(lic.identifier)
                 )
@@ -203,9 +203,9 @@ class LicenseWriter(BaseWriter):
         Could be a single license (extracted or part of license list.) or
         a conjunction/disjunction of licenses.
         """
-        if isinstance(lic, document.LicenseConjunction):
+        if isinstance(lic, license.LicenseConjunction):
             return self.create_conjunction_node(lic)
-        elif isinstance(lic, document.LicenseDisjunction):
+        elif isinstance(lic, license.LicenseDisjunction):
             return self.create_disjunction_node(lic)
         else:
             return self.create_license_helper(lic)
diff --git a/spdx/writers/tagvalue.py b/spdx/writers/tagvalue.py
index a47f20b..b4a2629 100644
--- a/spdx/writers/tagvalue.py
+++ b/spdx/writers/tagvalue.py
@@ -11,7 +11,7 @@
 
 from itertools import zip_longest
 
-from spdx import document, utils
+from spdx import license, utils
 from spdx import file as spdx_file
 from spdx.parsers.loggers import ErrorMessages
 
@@ -121,7 +121,7 @@ def write_file(spdx_file, out):
     write_value("FileChecksum", spdx_file.chksum.to_tv(), out)
     if spdx_file.has_optional_field("conc_lics"):
         if isinstance(
-            spdx_file.conc_lics, (document.LicenseConjunction, document.LicenseDisjunction)
+            spdx_file.conc_lics, (license.LicenseConjunction, license.LicenseDisjunction)
         ):
             write_value("LicenseConcluded", "({0})".format(spdx_file.conc_lics), out)
         else:
@@ -189,7 +189,7 @@ def write_snippet(snippet, out):
         write_text_value("SnippetAttributionText", snippet.attribution_text, out)
     if snippet.has_optional_field("conc_lics"):
         if isinstance(
-            snippet.conc_lics, (document.LicenseConjunction, document.LicenseDisjunction)
+            snippet.conc_lics, (license.LicenseConjunction, license.LicenseDisjunction)
         ):
             write_value("SnippetLicenseConcluded", "({0})".format(snippet.conc_lics), out)
         else:
@@ -248,7 +248,7 @@ def write_package(package, out):
     if package.has_optional_field("license_declared"):
         if isinstance(
             package.license_declared,
-            (document.LicenseConjunction, document.LicenseDisjunction),
+            (license.LicenseConjunction, license.LicenseDisjunction),
         ):
             write_value(
                 "PackageLicenseDeclared", "({0})".format(package.license_declared), out
@@ -258,7 +258,7 @@ def write_package(package, out):
 
     if package.has_optional_field("conc_lics"):
         if isinstance(
-            package.conc_lics, (document.LicenseConjunction, document.LicenseDisjunction)
+            package.conc_lics, (license.LicenseConjunction, license.LicenseDisjunction)
         ):
             write_value("PackageLicenseConcluded", "({0})".format(package.conc_lics), out)
         else:


Test patch:
diff --git a/tests/test_builder.py b/tests/test_builder.py
index 1aea1fd..ec135b7 100644
--- a/tests/test_builder.py
+++ b/tests/test_builder.py
@@ -13,7 +13,8 @@ from unittest import TestCase
 
 import tests.testing_utils as testing_utils
 
-from spdx.document import Document, License
+from spdx.document import Document
+from spdx.license import License
 import spdx.parsers.tagvaluebuilders as builders
 from spdx.version import Version
 
diff --git a/tests/test_document.py b/tests/test_document.py
index ab0f791..3a55baf 100644
--- a/tests/test_document.py
+++ b/tests/test_document.py
@@ -19,7 +19,7 @@ from spdx.checksum import Algorithm
 from spdx.config import LICENSE_MAP, EXCEPTION_MAP
 from spdx.creationinfo import Tool
 from spdx.document import Document, ExternalDocumentRef
-from spdx.document import License
+from spdx.license import License
 from spdx.file import File
 from spdx.package import Package, PackagePurpose
 from spdx.parsers.loggers import ErrorMessages
diff --git a/tests/test_rdf_writer.py b/tests/test_rdf_writer.py
index 03584ef..267ecd2 100644
--- a/tests/test_rdf_writer.py
+++ b/tests/test_rdf_writer.py
@@ -3,7 +3,8 @@ import os
 import pytest
 from rdflib import URIRef
 
-from spdx.document import Document, License
+from spdx.document import Document
+from spdx.license import License
 from spdx.package import Package, ExternalPackageRef
 from spdx.parsers.loggers import StandardLogger
 from spdx.parsers.parse_anything import parse_file
diff --git a/tests/utils_test.py b/tests/utils_test.py
index 7f66f43..ce60852 100644
--- a/tests/utils_test.py
+++ b/tests/utils_test.py
@@ -267,8 +267,8 @@ class TestParserUtils(object):
     @classmethod
     def license_to_dict(cls, license):
         """
-        Represents spdx.document.License, spdx.document.LicenseConjunction or
-        spdx.document.LicenseDisjunction as a Python dictionary
+        Represents spdx.license.License, spdx.license.LicenseConjunction or
+        spdx.license.LicenseDisjunction as a Python dictionary
         """
         CONJ_SEP = re.compile(' AND | and ')
         DISJ_SEP = re.compile(' OR | or ')
@@ -276,10 +276,10 @@ class TestParserUtils(object):
             return None
         license_dict = OrderedDict()
 
-        if isinstance(license, spdx.document.LicenseConjunction):
+        if isinstance(license, spdx.license.LicenseConjunction):
             license_dict['type'] = 'Conjunction'
             sep_regex = CONJ_SEP
-        elif isinstance(license, spdx.document.LicenseDisjunction):
+        elif isinstance(license, spdx.license.LicenseDisjunction):
             license_dict['type'] = 'Disjunction'
             sep_regex = DISJ_SEP
         else: