diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..b3523526 --- /dev/null +++ b/.flake8 @@ -0,0 +1,20 @@ +[flake8] +## https://flake8.pycqa.org/en/latest/user/configuration.html +## keep in sync with isort config - in `isort.cfg` file + +exclude = + build,dist,__pycache__,.eggs,*.egg-info*, + *_cache,*.cache, + .git,.tox,.venv,venv,.venv*,venv*, + _OLD,_TEST, + docs + +max-line-length = 120 + +max-complexity = 20 + +ignore = + # ignore `self`, `cls` markers of flake8-annotations>=2.0 + ANN101,ANN102 + # ignore ANN401 for dynamically typed *args and **kwargs + ANN401 diff --git a/.isort.cfg b/.isort.cfg index 7a30df96..85e125dc 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,6 +1,6 @@ [settings] ## read the docs: https://pycqa.github.io/isort/docs/configuration/options.html -## keep in sync with flake8 config - in `tox.ini` file +## keep in sync with flake8 config - in `.flake8` file known_first_party = cyclonedx skip_gitignore = false skip_glob = diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d2008a9..8b8a5a2a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,6 +26,10 @@ poetry run isort . poetry run autopep8 -ir cyclonedx/ tests/ typings/ examples/ ``` +This project prefers `f'strings'` over `'string'.format()`. +This project prefers `'single quotes'` over `"double quotes"`. +This project prefers `lower_snake_case` variable names. + ## Documentation This project uses [Sphinx] to generate documentation which is automatically published to [readthedocs.io]. diff --git a/cyclonedx/__init__.py b/cyclonedx/__init__.py index 6ff0623a..feb79616 100644 --- a/cyclonedx/__init__.py +++ b/cyclonedx/__init__.py @@ -20,4 +20,5 @@ # !! version is managed by semantic_release # do not use typing here, or else `semantic_release` might have issues finding the variable +# flake8: noqa __version__ = "5.0.0-rc.1" diff --git a/cyclonedx/exception/__init__.py b/cyclonedx/exception/__init__.py index 868373fd..ef1ce340 100644 --- a/cyclonedx/exception/__init__.py +++ b/cyclonedx/exception/__init__.py @@ -19,13 +19,13 @@ """ -class CycloneDxException(Exception): +class CycloneDxException(Exception): # noqa: N818 """ Root exception thrown by this library. """ pass -class MissingOptionalDependencyException(CycloneDxException): +class MissingOptionalDependencyException(CycloneDxException): # noqa: N818 """Validation did not happen, due to missing dependencies.""" pass diff --git a/cyclonedx/model/__init__.py b/cyclonedx/model/__init__.py index 695f3d28..5b5b0d8b 100644 --- a/cyclonedx/model/__init__.py +++ b/cyclonedx/model/__init__.py @@ -23,7 +23,7 @@ import serializable from sortedcontainers import SortedSet -from .. import __version__ as __ThisToolVersion +from .. import __version__ as __ThisToolVersion # noqa: N812 from ..exception.model import ( InvalidLocaleTypeException, InvalidUriException, @@ -57,7 +57,7 @@ def sha1sum(filename: str) -> str: """ h = sha1() with open(filename, 'rb') as f: - for byte_block in iter(lambda: f.read(4096), b""): + for byte_block in iter(lambda: f.read(4096), b''): h.update(byte_block) return h.hexdigest() @@ -102,10 +102,10 @@ class DataFlow(str, Enum): .. note:: See the CycloneDX Schema: https://cyclonedx.org/docs/1.4/xml/#type_dataFlowType """ - INBOUND = "inbound" - OUTBOUND = "outbound" - BI_DIRECTIONAL = "bi-directional" - UNKNOWN = "unknown" + INBOUND = 'inbound' + OUTBOUND = 'outbound' + BI_DIRECTIONAL = 'bi-directional' + UNKNOWN = 'unknown' @serializable.serializable_class @@ -325,16 +325,16 @@ def from_composite_str(composite_hash: str) -> 'HashType': ) elif algorithm_prefix[0:3] == 'sha': return HashType( - alg=getattr(HashAlgorithm, 'SHA_{}'.format(algorithm_prefix[3:])), + alg=getattr(HashAlgorithm, f'SHA_{algorithm_prefix[3:]}'), content=parts[1].lower() ) elif algorithm_prefix[0:6] == 'blake2': return HashType( - alg=getattr(HashAlgorithm, 'BLAKE2b_{}'.format(algorithm_prefix[6:])), + alg=getattr(HashAlgorithm, f'BLAKE2b_{algorithm_prefix[6:]}'), content=parts[1].lower() ) - raise UnknownHashTypeException(f"Unable to determine hash type from '{composite_hash}'") + raise UnknownHashTypeException(f'Unable to determine hash type from {composite_hash!r}') def __init__(self, *, alg: HashAlgorithm, content: str) -> None: self.alg = alg @@ -542,7 +542,7 @@ def type(self, type: ExternalReferenceType) -> None: @serializable.view(SchemaVersion1Dot3) @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'hash') - def hashes(self) -> "SortedSet[HashType]": + def hashes(self) -> 'SortedSet[HashType]': """ The hashes of the external reference (if applicable). @@ -779,9 +779,9 @@ def locale(self, locale: Optional[str]) -> None: if not re.search(Note._LOCALE_TYPE_REGEX, locale): self._locale = None raise InvalidLocaleTypeException( - f"Supplied locale '{locale}' is not a valid locale. " - f"Locale string should be formatted as the ISO-639 (or higher) language code and optional " - f"ISO-3166 (or higher) country code. according to ISO-639 format. Examples include: 'en', 'en-US'." + f'Supplied locale {locale!r} is not a valid locale.' + ' Locale string should be formatted as the ISO-639 (or higher) language code and optional' + " ISO-3166 (or higher) country code. according to ISO-639 format. Examples include: 'en', 'en-US'." ) def __eq__(self, other: object) -> bool: @@ -922,7 +922,7 @@ def name(self, name: Optional[str]) -> None: @serializable.json_name('url') @serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'url') @serializable.xml_sequence(2) - def urls(self) -> "SortedSet[XsUri]": + def urls(self) -> 'SortedSet[XsUri]': """ Get a list of URLs of the organization. Multiple URLs are allowed. @@ -939,7 +939,7 @@ def urls(self, urls: Iterable[XsUri]) -> None: @serializable.json_name('contact') @serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'contact') @serializable.xml_sequence(3) - def contacts(self) -> "SortedSet[OrganizationalContact]": + def contacts(self) -> 'SortedSet[OrganizationalContact]': """ Get a list of contact person at the organization. Multiple contacts are allowed. @@ -1037,7 +1037,7 @@ def version(self, version: Optional[str]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'hash') @serializable.xml_sequence(4) - def hashes(self) -> "SortedSet[HashType]": + def hashes(self) -> 'SortedSet[HashType]': """ The hashes of the tool (if applicable). @@ -1054,7 +1054,7 @@ def hashes(self, hashes: Iterable[HashType]) -> None: @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference') @serializable.xml_sequence(5) - def external_references(self) -> "SortedSet[ExternalReference]": + def external_references(self) -> 'SortedSet[ExternalReference]': """ External References provide a way to document systems, sites, and information that may be relevant but which are not included with the BOM. diff --git a/cyclonedx/model/bom.py b/cyclonedx/model/bom.py index 16c0d6c0..f1e642db 100644 --- a/cyclonedx/model/bom.py +++ b/cyclonedx/model/bom.py @@ -94,7 +94,7 @@ def timestamp(self, timestamp: datetime) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'tool') @serializable.xml_sequence(2) - def tools(self) -> "SortedSet[Tool]": + def tools(self) -> 'SortedSet[Tool]': """ Tools used to create this BOM. @@ -110,7 +110,7 @@ def tools(self, tools: Iterable[Tool]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'author') @serializable.xml_sequence(3) - def authors(self) -> "SortedSet[OrganizationalContact]": + def authors(self) -> 'SortedSet[OrganizationalContact]': """ The person(s) who created the BOM. @@ -207,7 +207,7 @@ def licenses(self, licenses: Iterable[License]) -> None: @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property') @serializable.xml_sequence(8) - def properties(self) -> "SortedSet[Property]": + def properties(self) -> 'SortedSet[Property]': """ Provides the ability to document properties in a key/value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. @@ -335,7 +335,7 @@ def metadata(self, metadata: BomMetaData) -> None: @serializable.include_none(SchemaVersion1Dot1) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'component') @serializable.xml_sequence(2) - def components(self) -> "SortedSet[Component]": + def components(self) -> 'SortedSet[Component]': """ Get all the Components currently in this Bom. @@ -348,7 +348,7 @@ def components(self) -> "SortedSet[Component]": def components(self, components: Iterable[Component]) -> None: self._components = SortedSet(components) - def get_component_by_purl(self, purl: Optional["PackageURL"]) -> Optional[Component]: + def get_component_by_purl(self, purl: Optional['PackageURL']) -> Optional[Component]: """ Get a Component already in the Bom by its PURL @@ -394,7 +394,7 @@ def has_component(self, component: Component) -> bool: @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'service') @serializable.xml_sequence(3) - def services(self) -> "SortedSet[Service]": + def services(self) -> 'SortedSet[Service]': """ Get all the Services currently in this Bom. @@ -414,7 +414,7 @@ def services(self, services: Iterable[Service]) -> None: @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference') @serializable.xml_sequence(4) - def external_references(self) -> "SortedSet[ExternalReference]": + def external_references(self) -> 'SortedSet[ExternalReference]': """ Provides the ability to document external references related to the BOM or to the project the BOM describes. @@ -438,7 +438,7 @@ def _get_all_components(self) -> Set[Component]: return components - def get_vulnerabilities_for_bom_ref(self, bom_ref: BomRef) -> "SortedSet[Vulnerability]": + def get_vulnerabilities_for_bom_ref(self, bom_ref: BomRef) -> 'SortedSet[Vulnerability]': """ Get all known Vulnerabilities that affect the supplied bom_ref. @@ -469,7 +469,7 @@ def has_vulnerabilities(self) -> bool: @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'vulnerability') @serializable.xml_sequence(8) - def vulnerabilities(self) -> "SortedSet[Vulnerability]": + def vulnerabilities(self) -> 'SortedSet[Vulnerability]': """ Get all the Vulnerabilities in this BOM. @@ -497,7 +497,7 @@ def version(self, version: int) -> None: @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'dependency') @serializable.xml_sequence(5) - def dependencies(self) -> "SortedSet[Dependency]": + def dependencies(self) -> 'SortedSet[Dependency]': return self._dependencies @dependencies.setter @@ -564,7 +564,7 @@ def validate(self) -> bool: f'The Component this BOM is describing {self.metadata.component.purl} has no defined dependencies ' f'which means the Dependency Graph is incomplete - you should add direct dependencies to this ' f'"root" Component to complete the Dependency Graph data.', - UserWarning + category=UserWarning, stacklevel=1 ) # 3. If a LicenseExpression is set, then there must be no other license. diff --git a/cyclonedx/model/component.py b/cyclonedx/model/component.py index bf8501a9..5aefa706 100644 --- a/cyclonedx/model/component.py +++ b/cyclonedx/model/component.py @@ -209,7 +209,7 @@ def licenses(self, licenses: Iterable[License]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'text') - def copyright(self) -> "SortedSet[Copyright]": + def copyright(self) -> 'SortedSet[Copyright]': """ Optional list of copyright statements. @@ -388,7 +388,7 @@ def diff(self, diff: Optional[Diff]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'issue') - def resolves(self) -> "SortedSet[IssueType]": + def resolves(self) -> 'SortedSet[IssueType]': """ Optional list of issues resolved by this patch. @@ -510,7 +510,7 @@ def variants(self, variants: Iterable['Component']) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'commit') @serializable.xml_sequence(4) - def commits(self) -> "SortedSet[Commit]": + def commits(self) -> 'SortedSet[Commit]': """ A list of zero or more commits which provide a trail describing how the component deviates from an ancestor, descendant, or variant. @@ -530,7 +530,7 @@ def commits(self, commits: Iterable[Commit]) -> None: @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'patch') @serializable.xml_sequence(5) - def patches(self) -> "SortedSet[Patch]": + def patches(self) -> 'SortedSet[Patch]': """ A list of zero or more patches describing how the component deviates from an ancestor, descendant, or variant. Patches may be complimentary to commits or may be used in place of commits. @@ -733,18 +733,18 @@ def for_file(absolute_file_path: str, path_for_bom: Optional[str]) -> 'Component `Component` representing the supplied file """ if not exists(absolute_file_path): - raise FileExistsError('Supplied file path \'{}\' does not exist'.format(absolute_file_path)) + raise FileExistsError(f'Supplied file path {absolute_file_path!r} does not exist') sha1_hash: str = sha1sum(filename=absolute_file_path) return Component( name=path_for_bom if path_for_bom else absolute_file_path, - version='0.0.0-{}'.format(sha1_hash[0:12]), + version=f'0.0.0-{sha1_hash[0:12]}', hashes=[ HashType(alg=HashAlgorithm.SHA_1, content=sha1_hash) ], type=ComponentType.FILE, purl=PackageURL( type='generic', name=path_for_bom if path_for_bom else absolute_file_path, - version='0.0.0-{}'.format(sha1_hash[0:12]) + version=f'0.0.0-{sha1_hash[0:12]}' ) ) @@ -795,15 +795,16 @@ def __init__(self, *, name: str, type: ComponentType = ComponentType.LIBRARY, if namespace: warnings.warn( '`namespace` is deprecated and has been replaced with `group` to align with the CycloneDX standard', - DeprecationWarning + category=DeprecationWarning, stacklevel=1 ) if not group: self.group = namespace if license_str: warnings.warn( - '`license_str` is deprecated and has been replaced with `licenses` to align with the CycloneDX ' - 'standard', DeprecationWarning + '`license_str` is deprecated and has been' + ' replaced with `licenses` to align with the CycloneDX standard', + category=DeprecationWarning, stacklevel=1 ) if not licenses: self.licenses = LicenseRepository([LicenseExpression(license_str)]) @@ -953,10 +954,10 @@ def name(self, name: str) -> None: self._name = name @property - @serializable.include_none(SchemaVersion1Dot0, "") - @serializable.include_none(SchemaVersion1Dot1, "") - @serializable.include_none(SchemaVersion1Dot2, "") - @serializable.include_none(SchemaVersion1Dot3, "") + @serializable.include_none(SchemaVersion1Dot0, '') + @serializable.include_none(SchemaVersion1Dot1, '') + @serializable.include_none(SchemaVersion1Dot2, '') + @serializable.include_none(SchemaVersion1Dot3, '') @serializable.xml_sequence(6) def version(self) -> Optional[str]: """ @@ -1009,7 +1010,7 @@ def scope(self, scope: Optional[ComponentScope]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'hash') @serializable.xml_sequence(9) - def hashes(self) -> "SortedSet[HashType]": + def hashes(self) -> 'SortedSet[HashType]': """ Optional list of hashes that help specify the integrity of this Component. @@ -1148,7 +1149,7 @@ def pedigree(self, pedigree: Optional[Pedigree]) -> None: @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference') @serializable.xml_sequence(17) - def external_references(self) -> "SortedSet[ExternalReference]": + def external_references(self) -> 'SortedSet[ExternalReference]': """ Provides the ability to document external references related to the component or to the project the component describes. @@ -1167,7 +1168,7 @@ def external_references(self, external_references: Iterable[ExternalReference]) @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property') @serializable.xml_sequence(18) - def properties(self) -> "SortedSet[Property]": + def properties(self) -> 'SortedSet[Property]': """ Provides the ability to document properties in a key/value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. @@ -1232,7 +1233,7 @@ def release_notes(self) -> Optional[ReleaseNotes]: def release_notes(self, release_notes: Optional[ReleaseNotes]) -> None: self._release_notes = release_notes - def get_all_nested_components(self, include_self: bool = False) -> Set["Component"]: + def get_all_nested_components(self, include_self: bool = False) -> Set['Component']: components = set() if include_self: components.add(self) @@ -1279,5 +1280,6 @@ def get_namespace(self) -> Optional[str]: Returns: Declared namespace of this Component as `str` if declared, else `None`. """ - warnings.warn('`Component.get_namespace()` is deprecated - use `Component.group`', DeprecationWarning) + warnings.warn('`Component.get_namespace()` is deprecated - use `Component.group`', + category=DeprecationWarning, stacklevel=1) return self._group diff --git a/cyclonedx/model/dependency.py b/cyclonedx/model/dependency.py index ee40fa6d..51c13d93 100644 --- a/cyclonedx/model/dependency.py +++ b/cyclonedx/model/dependency.py @@ -37,8 +37,8 @@ def serialize(cls, o: Any) -> List[str]: raise ValueError(f'Attempt to serialize a non-Dependency: {o.__class__}') @classmethod - def deserialize(cls, o: Any) -> Set["Dependency"]: - dependencies: Set["Dependency"] = set() + def deserialize(cls, o: Any) -> Set['Dependency']: + dependencies: Set['Dependency'] = set() if isinstance(o, list): for v in o: dependencies.add(Dependency(ref=BomRef(value=v))) @@ -54,7 +54,7 @@ class Dependency: See https://cyclonedx.org/docs/1.4/xml/#type_dependencyType """ - def __init__(self, ref: BomRef, dependencies: Optional[Iterable["Dependency"]] = None) -> None: + def __init__(self, ref: BomRef, dependencies: Optional[Iterable['Dependency']] = None) -> None: self.ref = ref self.dependencies = SortedSet(dependencies or []) @@ -72,11 +72,11 @@ def ref(self, ref: BomRef) -> None: @serializable.json_name('dependsOn') @serializable.type_mapping(DependencyDependencies) @serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'dependency') - def dependencies(self) -> "SortedSet[Dependency]": + def dependencies(self) -> 'SortedSet[Dependency]': return self._dependencies @dependencies.setter - def dependencies(self, dependencies: Iterable["Dependency"]) -> None: + def dependencies(self, dependencies: Iterable['Dependency']) -> None: self._dependencies = SortedSet(dependencies) def dependencies_as_bom_refs(self) -> Set[BomRef]: diff --git a/cyclonedx/model/issue.py b/cyclonedx/model/issue.py index 722cb5b6..03709b4f 100644 --- a/cyclonedx/model/issue.py +++ b/cyclonedx/model/issue.py @@ -196,7 +196,7 @@ def source(self, source: Optional[IssueTypeSource]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'url') @serializable.xml_sequence(5) - def references(self) -> "SortedSet[XsUri]": + def references(self) -> 'SortedSet[XsUri]': """ Any reference URLs related to this issue. diff --git a/cyclonedx/model/license.py b/cyclonedx/model/license.py index 70b76a8c..f091f698 100644 --- a/cyclonedx/model/license.py +++ b/cyclonedx/model/license.py @@ -46,7 +46,7 @@ def __init__(self, *, id: Optional[str] = None, name: Optional[str] = None, if id and name: warnings.warn( 'Both `id` and `name` have been supplied - `name` will be ignored!', - RuntimeWarning + category=RuntimeWarning, stacklevel=1 ) self._id = id self._name = name if not id else None diff --git a/cyclonedx/model/release_note.py b/cyclonedx/model/release_note.py index 4464f587..371c20a3 100644 --- a/cyclonedx/model/release_note.py +++ b/cyclonedx/model/release_note.py @@ -141,7 +141,7 @@ def timestamp(self, timestamp: Optional[datetime]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'alias') @serializable.xml_sequence(7) - def aliases(self) -> "SortedSet[str]": + def aliases(self) -> 'SortedSet[str]': """ One or more alternate names the release may be referred to. This may include unofficial terms used by development and marketing teams (e.g. code names). @@ -158,7 +158,7 @@ def aliases(self, aliases: Iterable[str]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'tag') @serializable.xml_sequence(8) - def tags(self) -> "SortedSet[str]": + def tags(self) -> 'SortedSet[str]': """ One or more tags that may aid in search or retrieval of the release note. @@ -174,7 +174,7 @@ def tags(self, tags: Iterable[str]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'issue') @serializable.xml_sequence(9) - def resolves(self) -> "SortedSet[IssueType]": + def resolves(self) -> 'SortedSet[IssueType]': """ A collection of issues that have been resolved. @@ -190,7 +190,7 @@ def resolves(self, resolves: Iterable[IssueType]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'note') @serializable.xml_sequence(10) - def notes(self) -> "SortedSet[Note]": + def notes(self) -> 'SortedSet[Note]': """ Zero or more release notes containing the locale and content. Multiple note elements may be specified to support release notes in a wide variety of languages. @@ -207,7 +207,7 @@ def notes(self, notes: Iterable[Note]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property') @serializable.xml_sequence(11) - def properties(self) -> "SortedSet[Property]": + def properties(self) -> 'SortedSet[Property]': """ Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike diff --git a/cyclonedx/model/service.py b/cyclonedx/model/service.py index b00b0714..708e874b 100644 --- a/cyclonedx/model/service.py +++ b/cyclonedx/model/service.py @@ -170,7 +170,7 @@ def description(self, description: Optional[str]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'endpoint') @serializable.xml_sequence(6) - def endpoints(self) -> "SortedSet[XsUri]": + def endpoints(self) -> 'SortedSet[XsUri]': """ A list of endpoints URI's this service provides. @@ -224,7 +224,7 @@ def x_trust_boundary(self, x_trust_boundary: Optional[bool]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'classification') @serializable.xml_sequence(9) - def data(self) -> "SortedSet[DataClassification]": + def data(self) -> 'SortedSet[DataClassification]': """ Specifies the data classification. @@ -256,7 +256,7 @@ def licenses(self, licenses: Iterable[License]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference') @serializable.xml_sequence(11) - def external_references(self) -> "SortedSet[ExternalReference]": + def external_references(self) -> 'SortedSet[ExternalReference]': """ Provides the ability to document external references related to the Service. @@ -310,7 +310,7 @@ def release_notes(self, release_notes: Optional[ReleaseNotes]) -> None: @serializable.view(SchemaVersion1Dot4) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property') @serializable.xml_sequence(12) - def properties(self) -> "SortedSet[Property]": + def properties(self) -> 'SortedSet[Property]': """ Provides the ability to document properties in a key/value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. diff --git a/cyclonedx/model/vulnerability.py b/cyclonedx/model/vulnerability.py index 53bcf453..b17fc117 100644 --- a/cyclonedx/model/vulnerability.py +++ b/cyclonedx/model/vulnerability.py @@ -108,7 +108,7 @@ def range(self, range: Optional[str]) -> None: def status(self) -> Optional[ImpactAnalysisAffectedStatus]: """ The vulnerability status for the version or range of versions. - """"" + """ return self._status @status.setter @@ -166,7 +166,7 @@ def ref(self, ref: str) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'version') @serializable.xml_sequence(2) - def versions(self) -> "SortedSet[BomTargetVersionRange]": + def versions(self) -> 'SortedSet[BomTargetVersionRange]': """ Zero or more individual versions or range of versions. @@ -253,7 +253,7 @@ def justification(self, justification: Optional[ImpactAnalysisJustification]) -> @serializable.json_name('response') @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'response') @serializable.xml_sequence(3) - def responses(self) -> "SortedSet[ImpactAnalysisResponse]": + def responses(self) -> 'SortedSet[ImpactAnalysisResponse]': """ A list of responses to the vulnerability by the manufacturer, supplier, or project responsible for the affected component or service. More than one response is allowed. Responses are strongly encouraged for @@ -631,10 +631,11 @@ def __init__(self, *, source: Optional[VulnerabilitySource] = None, score: Optio self.justification = justification if score_base: - warnings.warn('`score_base` is deprecated - use `score`', DeprecationWarning) + warnings.warn('`score_base` is deprecated - use `score`', + category=DeprecationWarning, stacklevel=1) if score: warnings.warn('Both `score` and `score_base` supplied - the deprecated `score_base` will be discarded', - DeprecationWarning) + category=DeprecationWarning, stacklevel=1) else: self.score = Decimal(score_base) @@ -761,7 +762,7 @@ def __init__(self, *, organizations: Optional[Iterable[OrganizationalEntity]] = @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'organization') @serializable.xml_sequence(1) - def organizations(self) -> "SortedSet[OrganizationalEntity]": + def organizations(self) -> 'SortedSet[OrganizationalEntity]': """ The organizations credited with vulnerability discovery. @@ -777,7 +778,7 @@ def organizations(self, organizations: Iterable[OrganizationalEntity]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'individual') @serializable.xml_sequence(2) - def individuals(self) -> "SortedSet[OrganizationalContact]": + def individuals(self) -> 'SortedSet[OrganizationalContact]': """ The individuals, not associated with organizations, that are credited with vulnerability discovery. @@ -856,12 +857,14 @@ def __init__(self, *, bom_ref: Optional[Union[str, BomRef]] = None, id: Optional self.properties = properties or [] # type: ignore if source_name or source_url: - warnings.warn('`source_name` and `source_url` are deprecated - use `source`', DeprecationWarning) + warnings.warn('`source_name` and `source_url` are deprecated - use `source`', + category=DeprecationWarning, stacklevel=1) if not source: self.source = VulnerabilitySource(name=source_name, url=XsUri(source_url) if source_url else None) if recommendations: - warnings.warn('`recommendations` is deprecated - use `recommendation`', DeprecationWarning) + warnings.warn('`recommendations` is deprecated - use `recommendation`', + category=DeprecationWarning, stacklevel=1) if not recommendation: self.recommendation = next(iter(recommendations)) @@ -914,7 +917,7 @@ def source(self, source: Optional[VulnerabilitySource]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference') @serializable.xml_sequence(3) - def references(self) -> "SortedSet[VulnerabilityReference]": + def references(self) -> 'SortedSet[VulnerabilityReference]': """ Zero or more pointers to vulnerabilities that are the equivalent of the vulnerability specified. Often times, the same vulnerability may exist in multiple sources of vulnerability intelligence, but have different @@ -933,7 +936,7 @@ def references(self, references: Iterable[VulnerabilityReference]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'rating') @serializable.xml_sequence(4) - def ratings(self) -> "SortedSet[VulnerabilityRating]": + def ratings(self) -> 'SortedSet[VulnerabilityRating]': """ List of vulnerability ratings. @@ -949,7 +952,7 @@ def ratings(self, ratings: Iterable[VulnerabilityRating]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'cwe') @serializable.xml_sequence(5) - def cwes(self) -> "SortedSet[int]": + def cwes(self) -> 'SortedSet[int]': """ A list of CWE (Common Weakness Enumeration) identifiers. @@ -1014,7 +1017,7 @@ def recommendation(self, recommendation: Optional[str]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'advisory') @serializable.xml_sequence(9) - def advisories(self) -> "SortedSet[VulnerabilityAdvisory]": + def advisories(self) -> 'SortedSet[VulnerabilityAdvisory]': """ Advisories relating to the Vulnerability. @@ -1093,7 +1096,7 @@ def credits(self, credits: Optional[VulnerabilityCredits]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'tool') @serializable.xml_sequence(14) - def tools(self) -> "SortedSet[Tool]": + def tools(self) -> 'SortedSet[Tool]': """ The tool(s) used to identify, confirm, or score the vulnerability. @@ -1124,7 +1127,7 @@ def analysis(self, analysis: Optional[VulnerabilityAnalysis]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'target') @serializable.xml_sequence(16) - def affects(self) -> "SortedSet[BomTarget]": + def affects(self) -> 'SortedSet[BomTarget]': """ The components or services that are affected by the vulnerability. @@ -1140,7 +1143,7 @@ def affects(self, affects_targets: Iterable[BomTarget]) -> None: @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property') @serializable.xml_sequence(17) - def properties(self) -> "SortedSet[Property]": + def properties(self) -> 'SortedSet[Property]': """ Provides the ability to document properties in a key/value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. diff --git a/cyclonedx/output/__init__.py b/cyclonedx/output/__init__.py index 8c590dae..f8245743 100644 --- a/cyclonedx/output/__init__.py +++ b/cyclonedx/output/__init__.py @@ -122,13 +122,13 @@ def make_outputter(bom: 'Bom', output_format: OutputFormat, schema_version: Sche :return: BaseOutput """ if TYPE_CHECKING: # pragma: no cover - BY_SCHEMA_VERSION: Mapping[SchemaVersion, Type[BaseOutput]] + BY_SCHEMA_VERSION: Mapping[SchemaVersion, Type[BaseOutput]] # noqa:N806 if OutputFormat.JSON is output_format: from .json import BY_SCHEMA_VERSION elif OutputFormat.XML is output_format: from .xml import BY_SCHEMA_VERSION else: - raise ValueError(f"Unexpected output_format: {output_format!r}") + raise ValueError(f'Unexpected output_format: {output_format!r}') klass = BY_SCHEMA_VERSION.get(schema_version, None) if klass is None: @@ -141,6 +141,6 @@ def get_instance(bom: 'Bom', output_format: OutputFormat = OutputFormat.XML, """DEPRECATED. use :func:`make_outputter` instead!""" warnings.warn( 'function `get_instance()` is deprecated, use `make_outputter()` instead.', - DeprecationWarning + category=DeprecationWarning, stacklevel=1 ) return make_outputter(bom, output_format, schema_version) diff --git a/cyclonedx/parser/__init__.py b/cyclonedx/parser/__init__.py index 6fdded1e..06990d47 100644 --- a/cyclonedx/parser/__init__.py +++ b/cyclonedx/parser/__init__.py @@ -40,7 +40,7 @@ def get_warning_message(self) -> str: return self._warning def __repr__(self) -> str: - return f'' + return f'' class BaseParser: diff --git a/cyclonedx/validation/__init__.py b/cyclonedx/validation/__init__.py index e66c1344..b7dda3f1 100644 --- a/cyclonedx/validation/__init__.py +++ b/cyclonedx/validation/__init__.py @@ -109,7 +109,7 @@ def make_schemabased_validator(output_format: OutputFormat, schema_version: 'Sch """ if TYPE_CHECKING: # pragma: no cover from typing import Type - Validator: Type[BaseSchemabasedValidator] + Validator: Type[BaseSchemabasedValidator] # noqa:N806 if OutputFormat.JSON is output_format: from .json import JsonValidator as Validator elif OutputFormat.XML is output_format: diff --git a/cyclonedx/validation/json.py b/cyclonedx/validation/json.py index 797bd3b9..f1ab46c1 100644 --- a/cyclonedx/validation/json.py +++ b/cyclonedx/validation/json.py @@ -31,13 +31,13 @@ _missing_deps_error: Optional[Tuple[MissingOptionalDependencyException, ImportError]] = None try: - from jsonschema.exceptions import ValidationError as JsonValidationError # type: ignore[import] - from jsonschema.validators import Draft7Validator # type: ignore[import] + from jsonschema.exceptions import ValidationError as JsonValidationError # type:ignore[import-untyped] + from jsonschema.validators import Draft7Validator # type:ignore[import-untyped] from referencing import Registry from referencing.jsonschema import DRAFT7 if TYPE_CHECKING: # pragma: no cover - from jsonschema.protocols import Validator as JsonSchemaValidator # type: ignore[import] + from jsonschema.protocols import Validator as JsonSchemaValidator # type:ignore[import-untyped] except ImportError as err: _missing_deps_error = MissingOptionalDependencyException( 'This functionality requires optional dependencies.\n' diff --git a/cyclonedx/validation/xml.py b/cyclonedx/validation/xml.py index 2fae6692..29567444 100644 --- a/cyclonedx/validation/xml.py +++ b/cyclonedx/validation/xml.py @@ -29,7 +29,7 @@ _missing_deps_error: Optional[Tuple[MissingOptionalDependencyException, ImportError]] = None try: - from lxml.etree import XMLParser, XMLSchema, fromstring as xml_fromstring # type: ignore[import] + from lxml.etree import XMLParser, XMLSchema, fromstring as xml_fromstring # type:ignore[import-untyped] except ImportError as err: _missing_deps_error = MissingOptionalDependencyException( 'This functionality requires optional dependencies.\n' diff --git a/examples/complex.py b/examples/complex.py index 1b9b068f..94799ec5 100644 --- a/examples/complex.py +++ b/examples/complex.py @@ -43,7 +43,7 @@ # region build the BOM bom = Bom() -bom.metadata.component = rootComponent = Component( +bom.metadata.component = root_component = Component( name='myApp', type=ComponentType.APPLICATION, licenses=[lc_factory.make_from_string('MIT')], @@ -64,7 +64,7 @@ purl=PackageURL('generic', 'acme', 'some-component', '1.33.7-beta.1') ) bom.components.add(component1) -bom.register_dependency(rootComponent, [component1]) +bom.register_dependency(root_component, [component1]) component2 = Component( type=ComponentType.LIBRARY, diff --git a/pyproject.toml b/pyproject.toml index 223f1035..0f3762aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,19 +74,20 @@ xml-validation = ["lxml"] [tool.poetry.dev-dependencies] ddt = "1.6.0" -coverage = "7.3.1" +coverage = "7.3.2" flake8 = { version="6.1.0", python=">=3.8.1" } flake8-annotations = { version="3.0.1", python=">=3.8.1" } flake8-bugbear = { version="23.9.16", python=">=3.8.1" } flake8-isort = "6.1.0 " +flake8-quotes = "3.3.2" +flake8-use-fstring = "1.4" +pep8-naming = "0.13.3" isort = "5.12.0" autopep8 = "2.0.4" -mypy = "1.5.1" +mypy = "1.6.1" tox = "4.11.3" xmldiff = "2.6.3" - - [tool.semantic_release] # see https://python-semantic-release.readthedocs.io/en/latest/configuration.html commit_author = "semantic-release " diff --git a/tests/__init__.py b/tests/__init__.py index f7081484..f0eaf33c 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -45,20 +45,21 @@ class SnapshotMixin: @staticmethod - def getSnapshotFile(snapshot_name: str) -> str: + def getSnapshotFile(snapshot_name: str) -> str: # noqa: N802 return join(SNAPSHOTS_DIRECTORY, f'{snapshot_name}.bin') @classmethod - def writeSnapshot(cls, snapshot_name: str, data: str) -> None: + def writeSnapshot(cls, snapshot_name: str, data: str) -> None: # noqa: N802 with open(cls.getSnapshotFile(snapshot_name), 'w') as s: s.write(data) @classmethod - def readSnapshot(cls, snapshot_name: str) -> str: + def readSnapshot(cls, snapshot_name: str) -> str: # noqa: N802 with open(cls.getSnapshotFile(snapshot_name), 'r') as s: return s.read() - def assertEqualSnapshot(self: Union[TestCase, 'SnapshotMixin'], actual: str, snapshot_name: str) -> None: + def assertEqualSnapshot(self: Union[TestCase, 'SnapshotMixin'], # noqa: N802 + actual: str, snapshot_name: str) -> None: if RECREATE_SNAPSHOTS: self.writeSnapshot(snapshot_name, actual) _omd = self.maxDiff @@ -71,36 +72,38 @@ def assertEqualSnapshot(self: Union[TestCase, 'SnapshotMixin'], actual: str, sna class DeepCompareMixin: - def assertDeepEqual(self: Union[TestCase, 'DeepCompareMixin'], first: Any, second: Any, + def assertDeepEqual(self: Union[TestCase, 'DeepCompareMixin'], # noqa: N802 + first: Any, second: Any, msg: Optional[str] = None) -> None: """costly compare, but very verbose""" _omd = self.maxDiff self.maxDiff = None try: self.maxDiff = None - dd1 = self.__deepDict(first) - dd2 = self.__deepDict(second) + dd1 = self.__deep_dict(first) + dd2 = self.__deep_dict(second) self.assertDictEqual(dd1, dd2, msg) finally: self.maxDiff = _omd - def __deepDict(self, o: Any) -> Any: + def __deep_dict(self, o: Any) -> Any: if isinstance(o, tuple): - return tuple(self.__deepDict(i) for i in o) + return tuple(self.__deep_dict(i) for i in o) if isinstance(o, list): - return list(self.__deepDict(i) for i in o) + return list(self.__deep_dict(i) for i in o) if isinstance(o, dict): - return {k: self.__deepDict(v) for k, v in o.items()} + return {k: self.__deep_dict(v) for k, v in o.items()} if isinstance(o, (set, SortedSet)): # this method returns dict. `dict` is not hashable, so use `tuple` instead. - return tuple(self.__deepDict(i) for i in sorted(o, key=hash)) + ('%conv:%set',) + return tuple(self.__deep_dict(i) for i in sorted(o, key=hash)) + ('%conv:%set',) if hasattr(o, '__dict__'): - d = {a: self.__deepDict(v) for a, v in o.__dict__.items() if '__' not in a} + d = {a: self.__deep_dict(v) for a, v in o.__dict__.items() if '__' not in a} d['%conv'] = str(type(o)) return d return o - def assertBomDeepEqual(self: Union[TestCase, 'DeepCompareMixin'], expected: 'Bom', actual: 'Bom', + def assertBomDeepEqual(self: Union[TestCase, 'DeepCompareMixin'], # noqa: N802 + expected: 'Bom', actual: 'Bom', msg: Optional[str] = None, *, fuzzy_deps: bool = True) -> None: # deps might have been upgraded on serialization, so they might differ @@ -112,12 +115,13 @@ def assertBomDeepEqual(self: Union[TestCase, 'DeepCompareMixin'], expected: 'Bom try: self.assertDeepEqual(expected, actual, msg) if fuzzy_deps: - self._assertDependenciesFuzzyEqual(edeps, adeps) + self.assertDependenciesFuzzyEqual(edeps, adeps) finally: expected.dependencies = edeps actual.dependencies = adeps - def _assertDependenciesFuzzyEqual(self: TestCase, a: Iterable['Dependency'], b: Iterable['Dependency']) -> None: + def assertDependenciesFuzzyEqual(self: TestCase, # noqa: N802 + a: Iterable['Dependency'], b: Iterable['Dependency']) -> None: delta = set(a) ^ set(b) for d in delta: # only actual relevant dependencies shall be taken into account. diff --git a/tests/_data/models.py b/tests/_data/models.py index 6a1e178c..bc66b512 100644 --- a/tests/_data/models.py +++ b/tests/_data/models.py @@ -107,7 +107,7 @@ BOM_TIMESTAMP = datetime.fromisoformat('2023-01-07 13:44:32.312678+00:00') -def _makeBom(**kwargs: Any) -> Bom: +def _make_bom(**kwargs: Any) -> Bom: bom = Bom(**kwargs) bom.serial_number = BOM_SERIAL_NUMBER bom.metadata.timestamp = BOM_TIMESTAMP @@ -115,29 +115,29 @@ def _makeBom(**kwargs: Any) -> Bom: def get_bom_with_component_setuptools_basic() -> Bom: - return _makeBom(components=[get_component_setuptools_simple()]) + return _make_bom(components=[get_component_setuptools_simple()]) def get_bom_with_component_setuptools_with_cpe() -> Bom: component = get_component_setuptools_simple() component.cpe = 'cpe:2.3:a:python:setuptools:50.3.2:*:*:*:*:*:*:*' - return _makeBom(components=[component]) + return _make_bom(components=[component]) def get_bom_with_component_setuptools_no_component_version() -> Bom: - return _makeBom(components=[get_component_setuptools_simple_no_version()]) + return _make_bom(components=[get_component_setuptools_simple_no_version()]) def get_bom_with_component_setuptools_with_release_notes() -> Bom: component = get_component_setuptools_simple() component.release_notes = get_release_notes() - return _makeBom(components=[component]) + return _make_bom(components=[component]) def get_bom_with_dependencies_valid() -> Bom: c1 = get_component_setuptools_simple() c2 = get_component_toml_with_hashes_with_references() - return _makeBom( + return _make_bom( components=[c1, c2], dependencies=[ Dependency(ref=c1.bom_ref, dependencies=[ Dependency(ref=c2.bom_ref) @@ -154,7 +154,7 @@ def get_bom_with_dependencies_hanging() -> Bom: """ c1 = get_component_setuptools_simple('setuptools') c2 = get_component_toml_with_hashes_with_references('toml') - bom = _makeBom( + bom = _make_bom( version=23, metadata=BomMetaData( component=Component(name='rootComponent', type=ComponentType.APPLICATION, bom_ref='root-component'), @@ -175,12 +175,12 @@ def get_bom_with_dependencies_unlinked_invalid() -> Bom: it is expected to throw on output. """ c1 = get_component_setuptools_simple() - return _makeBom(components=[c1], dependencies=[Dependency(ref=BomRef('link-to-ref-not-in-document'))]) + return _make_bom(components=[c1], dependencies=[Dependency(ref=BomRef('link-to-ref-not-in-document'))]) def get_bom_with_metadata_component_and_dependencies() -> Bom: cs = get_component_toml_with_hashes_with_references() - bom = _makeBom(components=[cs]) + bom = _make_bom(components=[cs]) bom.metadata.component = get_component_setuptools_simple() bom.dependencies.add( Dependency(ref=bom.metadata.component.bom_ref, dependencies=[ @@ -191,11 +191,11 @@ def get_bom_with_metadata_component_and_dependencies() -> Bom: def get_bom_with_component_setuptools_complete() -> Bom: - return _makeBom(components=[get_component_setuptools_complete()]) + return _make_bom(components=[get_component_setuptools_complete()]) def get_bom_with_component_setuptools_with_vulnerability() -> Bom: - bom = _makeBom() + bom = _make_bom() component = get_component_setuptools_simple() if not component.purl: raise ValueError('purl is required here') @@ -258,11 +258,11 @@ def get_bom_with_component_setuptools_with_vulnerability() -> Bom: def get_bom_with_component_toml_1() -> Bom: - return _makeBom(components=[get_component_toml_with_hashes_with_references()]) + return _make_bom(components=[get_component_toml_with_hashes_with_references()]) def get_bom_just_complete_metadata() -> Bom: - bom = _makeBom() + bom = _make_bom() bom.metadata.authors = [get_org_contact_1(), get_org_contact_2()] bom.metadata.component = get_component_setuptools_complete() bom.metadata.manufacture = get_org_entity_1() @@ -280,14 +280,14 @@ def get_bom_just_complete_metadata() -> Bom: def get_bom_with_external_references() -> Bom: - bom = _makeBom(external_references=[ + bom = _make_bom(external_references=[ get_external_reference_1(), get_external_reference_2() ]) return bom def get_bom_with_services_simple() -> Bom: - bom = _makeBom(services=[ + bom = _make_bom(services=[ Service(name='my-first-service', bom_ref='my-specific-bom-ref-for-my-first-service'), Service(name='my-second-service', bom_ref='my-specific-bom-ref-for-my-second-service') ]) @@ -299,7 +299,7 @@ def get_bom_with_services_simple() -> Bom: def get_bom_with_services_complex() -> Bom: - bom = _makeBom(services=[ + bom = _make_bom(services=[ Service( name='my-first-service', bom_ref='my-specific-bom-ref-for-my-first-service', provider=get_org_entity_1(), group='a-group', version='1.2.3', @@ -327,7 +327,7 @@ def get_bom_with_services_complex() -> Bom: def get_bom_with_nested_services() -> Bom: - bom = _makeBom(services=[ + bom = _make_bom(services=[ Service( name='my-first-service', bom_ref='my-specific-bom-ref-for-my-first-service', provider=get_org_entity_1(), group='a-group', version='1.2.3', @@ -381,10 +381,10 @@ def get_bom_for_issue_275_components() -> Bom: see https://github.com/CycloneDX/cyclonedx-python-lib/issues/275 """ - app = Component(bom_ref=MOCK_UUID[0], name="app", version="1.0.0") - comp_a = Component(bom_ref=MOCK_UUID[1], name="comp_a", version="1.0.0") - comp_b = Component(bom_ref=MOCK_UUID[2], name="comp_b", version="1.0.0") - comp_c = Component(bom_ref=MOCK_UUID[3], name="comp_c", version="1.0.0") + app = Component(bom_ref=MOCK_UUID[0], name='app', version='1.0.0') + comp_a = Component(bom_ref=MOCK_UUID[1], name='comp_a', version='1.0.0') + comp_b = Component(bom_ref=MOCK_UUID[2], name='comp_b', version='1.0.0') + comp_c = Component(bom_ref=MOCK_UUID[3], name='comp_c', version='1.0.0') comp_b.components.add(comp_c) # comp_b.dependencies.add(comp_c.bom_ref) @@ -393,7 +393,7 @@ def get_bom_for_issue_275_components() -> Bom: # app.dependencies.add(comp_a.bom_ref) # app.dependencies.add(comp_b.bom_ref) - bom = _makeBom(components=libs) + bom = _make_bom(components=libs) bom.metadata.component = app bom.register_dependency(target=app, depends_on=[comp_a, comp_b]) bom.register_dependency(target=comp_b, depends_on=[comp_c]) @@ -421,7 +421,7 @@ def get_bom_for_issue_328_components() -> Bom: """regression test for issue #328 see https://github.com/CycloneDX/cyclonedx-python-lib/issues/328 """ - bom = _makeBom() + bom = _make_bom() comp_root = Component(type=ComponentType.APPLICATION, name='my-project', version='1', bom_ref='my-project') @@ -583,7 +583,7 @@ def get_pedigree_1() -> Pedigree: get_component_toml_with_hashes_with_references(bom_ref='e7abdcca-5ba2-4f29-b2cf-b1e1ef788e66'), get_component_setuptools_simple(bom_ref='ded1d73e-1fca-4302-b520-f1bc53979958') ], - commits=[Commit(uid='a-random-uid', message="A commit message")], + commits=[Commit(uid='a-random-uid', message='A commit message')], patches=[Patch(type=PatchClassification.BACKPORT)], notes='Some notes here please' ) @@ -602,12 +602,12 @@ def get_release_notes() -> ReleaseNotes: ).decode(encoding='UTF-8') return ReleaseNotes( - type='major', title="Release Notes Title", + type='major', title='Release Notes Title', featured_image=XsUri('https://cyclonedx.org/theme/assets/images/CycloneDX-Twitter-Card.png'), social_image=XsUri('https://cyclonedx.org/cyclonedx-icon.png'), - description="This release is a test release", timestamp=MOCK_TIMESTAMP, + description='This release is a test release', timestamp=MOCK_TIMESTAMP, aliases=[ - "First Test Release" + 'First Test Release' ], tags=['test', 'alpha'], resolves=[get_issue_1()], @@ -662,7 +662,7 @@ def get_vulnerability_source_owasp() -> VulnerabilitySource: def get_bom_with_licenses() -> Bom: - return _makeBom( + return _make_bom( metadata=BomMetaData( licenses=[DisjunctiveLicense(id='CC-BY-1.0')], component=Component(name='app', type=ComponentType.APPLICATION, bom_ref='my-app', @@ -741,7 +741,7 @@ def get_bom_with_multiple_licenses() -> Bom: DisjunctiveLicense(id='MIT'), DisjunctiveLicense(name='foo license'), ) - return _makeBom( + return _make_bom( metadata=BomMetaData( licenses=multi_licenses, component=Component(name='app', type=ComponentType.APPLICATION, bom_ref='my-app', diff --git a/tests/test_component.py b/tests/test_component.py index 92c54c8c..e20be7ae 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -66,7 +66,7 @@ def test_from_xml_file_with_path_for_bom(self) -> None: test_file = join(OWN_DATA_DIRECTORY, 'xml', '1.4', 'bom_setuptools.xml') c = Component.for_file(absolute_file_path=test_file, path_for_bom='fixtures/bom_setuptools.xml') sha1_hash: str = sha1sum(filename=test_file) - expected_version = '0.0.0-{}'.format(sha1_hash[0:12]) + expected_version = f'0.0.0-{sha1_hash[0:12]}' self.assertEqual(c.name, 'fixtures/bom_setuptools.xml') self.assertEqual(c.version, expected_version) purl = PackageURL( diff --git a/tests/test_model.py b/tests/test_model.py index 97ffc4a9..75096f2a 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -503,10 +503,10 @@ def test_sort(self) -> None: # expected sort order: (uri) expected_order = [2, 1, 3, 0] uris = [ - XsUri(uri="d"), - XsUri(uri="b"), - XsUri(uri="a"), - XsUri(uri="c"), + XsUri(uri='d'), + XsUri(uri='b'), + XsUri(uri='a'), + XsUri(uri='c'), ] sorted_uris = sorted(uris) expected_uris = reorder(uris, expected_order) diff --git a/tests/test_model_component.py b/tests/test_model_component.py index ccee271e..0333b46f 100644 --- a/tests/test_model_component.py +++ b/tests/test_model_component.py @@ -63,16 +63,16 @@ def test_no_parameters(self) -> None: def test_same(self) -> None: ia_comitter = IdentifiableAction(timestamp=datetime.datetime.utcnow(), name='The Committer') - c1 = Commit(uid='a-random-uid', author=ia_comitter, committer=ia_comitter, message="A commit message") - c2 = Commit(uid='a-random-uid', author=ia_comitter, committer=ia_comitter, message="A commit message") + c1 = Commit(uid='a-random-uid', author=ia_comitter, committer=ia_comitter, message='A commit message') + c2 = Commit(uid='a-random-uid', author=ia_comitter, committer=ia_comitter, message='A commit message') self.assertEqual(hash(c1), hash(c2)) self.assertTrue(c1 == c2) def test_not_same(self) -> None: ia_author = IdentifiableAction(timestamp=datetime.datetime.utcnow(), name='The Author') ia_comitter = IdentifiableAction(timestamp=datetime.datetime.utcnow(), name='The Committer') - c1 = Commit(uid='a-random-uid', author=ia_comitter, committer=ia_comitter, message="A commit message") - c2 = Commit(uid='a-random-uid', author=ia_author, committer=ia_comitter, message="A commit message") + c1 = Commit(uid='a-random-uid', author=ia_comitter, committer=ia_comitter, message='A commit message') + c2 = Commit(uid='a-random-uid', author=ia_author, committer=ia_comitter, message='A commit message') self.assertNotEqual(hash(c1), hash(c2)) self.assertFalse(c1 == c2) @@ -259,15 +259,15 @@ def test_sort(self) -> None: Component(name='component-a', type=ComponentType.LIBRARY, group='group-2'), Component(name='component-a', type=ComponentType.FILE), Component(name='component-b', type=ComponentType.FILE), - Component(name='component-a', type=ComponentType.FILE, version="1.0.0"), + Component(name='component-a', type=ComponentType.FILE, version='1.0.0'), ] sorted_components = sorted(components) expected_components = reorder(components, expected_order) self.assertListEqual(sorted_components, expected_components) def test_nested_components_1(self) -> None: - comp_b = Component(name="comp_b") - comp_c = Component(name="comp_c") + comp_b = Component(name='comp_b') + comp_c = Component(name='comp_c') comp_b.components.add(comp_c) self.assertEqual(1, len(comp_b.components)) @@ -275,9 +275,9 @@ def test_nested_components_1(self) -> None: self.assertEqual(1, len(comp_b.get_all_nested_components(include_self=False))) def test_nested_components_2(self) -> None: - comp_a = Component(name="comp_a") - comp_b = Component(name="comp_b") - comp_c = Component(name="comp_c") + comp_a = Component(name='comp_a') + comp_b = Component(name='comp_b') + comp_c = Component(name='comp_c') comp_b.components.add(comp_c) comp_b.components.add(comp_a) diff --git a/tests/test_model_dependency.py b/tests/test_model_dependency.py index 4439d6aa..1b0d2f64 100644 --- a/tests/test_model_dependency.py +++ b/tests/test_model_dependency.py @@ -28,14 +28,14 @@ def test_sort(self) -> None: # expected sort order: (value) expected_order = [3, 2, 0, 1] deps = [ - Dependency(ref=BomRef(value="be2c6502-7e9a-47db-9a66-e34f729810a3"), dependencies=[ - Dependency(ref=BomRef(value="0b049d09-64c0-4490-a0f5-c84d9aacf857")), - Dependency(ref=BomRef(value="17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda")) + Dependency(ref=BomRef(value='be2c6502-7e9a-47db-9a66-e34f729810a3'), dependencies=[ + Dependency(ref=BomRef(value='0b049d09-64c0-4490-a0f5-c84d9aacf857')), + Dependency(ref=BomRef(value='17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda')) ]), - Dependency(ref=BomRef(value="cd3e9c95-9d41-49e7-9924-8cf0465ae789")), - Dependency(ref=BomRef(value="17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda")), - Dependency(ref=BomRef(value="0b049d09-64c0-4490-a0f5-c84d9aacf857"), dependencies=[ - Dependency(ref=BomRef(value="cd3e9c95-9d41-49e7-9924-8cf0465ae789")) + Dependency(ref=BomRef(value='cd3e9c95-9d41-49e7-9924-8cf0465ae789')), + Dependency(ref=BomRef(value='17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda')), + Dependency(ref=BomRef(value='0b049d09-64c0-4490-a0f5-c84d9aacf857'), dependencies=[ + Dependency(ref=BomRef(value='cd3e9c95-9d41-49e7-9924-8cf0465ae789')) ]) ] sorted_deps = sorted(deps) diff --git a/tests/test_model_issue.py b/tests/test_model_issue.py index b7a127ae..54816995 100644 --- a/tests/test_model_issue.py +++ b/tests/test_model_issue.py @@ -67,15 +67,15 @@ def test_no_params(self) -> None: IssueTypeSource() def test_same(self) -> None: - its_1 = IssueTypeSource(name="The Source", url=XsUri('https://cyclonedx.org')) - its_2 = IssueTypeSource(name="The Source", url=XsUri('https://cyclonedx.org')) + its_1 = IssueTypeSource(name='The Source', url=XsUri('https://cyclonedx.org')) + its_2 = IssueTypeSource(name='The Source', url=XsUri('https://cyclonedx.org')) self.assertNotEqual(id(its_1), id(its_2)) self.assertEqual(hash(its_1), hash(its_2)) self.assertTrue(its_1 == its_2) def test_not_same(self) -> None: - its_1 = IssueTypeSource(name="The Source", url=XsUri('https://cyclonedx.org')) - its_2 = IssueTypeSource(name="Not the Source", url=XsUri('https://cyclonedx.org')) + its_1 = IssueTypeSource(name='The Source', url=XsUri('https://cyclonedx.org')) + its_2 = IssueTypeSource(name='Not the Source', url=XsUri('https://cyclonedx.org')) self.assertNotEqual(id(its_1), id(its_2)) self.assertNotEqual(hash(its_1), hash(its_2)) self.assertFalse(its_1 == its_2) diff --git a/tests/test_model_release_note.py b/tests/test_model_release_note.py index c65f1ad2..fbbb4e94 100644 --- a/tests/test_model_release_note.py +++ b/tests/test_model_release_note.py @@ -41,18 +41,18 @@ def test_simple(self) -> None: def test_complete(self) -> None: timestamp: datetime.datetime = datetime.datetime.utcnow() rn = ReleaseNotes( - type='major', title="Release Notes Title", + type='major', title='Release Notes Title', featured_image=XsUri('https://cyclonedx.org/theme/assets/images/CycloneDX-Twitter-Card.png'), social_image=XsUri('https://cyclonedx.org/cyclonedx-icon.png'), - description="This release is a test release", timestamp=timestamp, + description='This release is a test release', timestamp=timestamp, aliases=[ - "First Test Release" + 'First Test Release' ], tags=['test', 'alpha'], resolves=[], notes=[] ) - rn.aliases.add("Release Alpha") + rn.aliases.add('Release Alpha') rn.tags.add('testing') self.assertEqual(rn.type, 'major') @@ -63,7 +63,7 @@ def test_complete(self) -> None: ) self.assertEqual(str(rn.social_image), 'https://cyclonedx.org/cyclonedx-icon.png') self.assertEqual(rn.description, 'This release is a test release') - self.assertSetEqual(rn.aliases, {"Release Alpha", "First Test Release"}) + self.assertSetEqual(rn.aliases, {'Release Alpha', 'First Test Release'}) self.assertSetEqual(rn.tags, {'test', 'testing', 'alpha'}) self.assertSetEqual(rn.resolves, set()) self.assertFalse(rn.notes) diff --git a/tests/test_output.py b/tests/test_output.py index aade691b..bfacdda3 100644 --- a/tests/test_output.py +++ b/tests/test_output.py @@ -45,7 +45,7 @@ def test_as_expected(self, of: OutputFormat, sv: SchemaVersion) -> None: *(('foo', sv, (ValueError, "Unexpected output_format: 'foo'")) for sv in SchemaVersion), ) @unpack - def test_fails_on_wrong_args(self, of: OutputFormat, sv: SchemaVersion, raisesRegex: Tuple) -> None: + def test_fails_on_wrong_args(self, of: OutputFormat, sv: SchemaVersion, raises_regex: Tuple) -> None: bom = Mock(spec=Bom) - with self.assertRaisesRegex(*raisesRegex): + with self.assertRaisesRegex(*raises_regex): make_outputter(bom, of, sv) diff --git a/tests/test_output_json.py b/tests/test_output_json.py index 0a291fa2..02f68c1b 100644 --- a/tests/test_output_json.py +++ b/tests/test_output_json.py @@ -39,9 +39,9 @@ class TestOutputJson(TestCase, SnapshotMixin): @data(*UNSUPPORTED_SV) def test_unsupported_schema_raises(self, sv: SchemaVersion) -> None: - outputterClass = BY_SCHEMA_VERSION[sv] - self.assertTrue(issubclass(outputterClass, Json)) - outputter = outputterClass(Mock(spec=Bom)) + outputter_class = BY_SCHEMA_VERSION[sv] + self.assertTrue(issubclass(outputter_class, Json)) + outputter = outputter_class(Mock(spec=Bom)) with self.assertRaises(FormatNotSupportedException): outputter.output_as_string() @@ -86,8 +86,8 @@ class TestFunctionalBySchemaVersion(TestCase): @idata(SchemaVersion) def test_get_outputter_expected(self, sv: SchemaVersion) -> None: - outputterClass = BY_SCHEMA_VERSION[sv] - self.assertTrue(issubclass(outputterClass, Json)) - outputter = outputterClass(Mock(spec=Bom)) + outputter_class = BY_SCHEMA_VERSION[sv] + self.assertTrue(issubclass(outputter_class, Json)) + outputter = outputter_class(Mock(spec=Bom)) self.assertIs(outputter.schema_version, sv) self.assertIs(outputter.output_format, OutputFormat.JSON) diff --git a/tests/test_output_xml.py b/tests/test_output_xml.py index c78a8755..6e580acc 100644 --- a/tests/test_output_xml.py +++ b/tests/test_output_xml.py @@ -74,8 +74,8 @@ class TestFunctionalBySchemaVersion(TestCase): @idata(SchemaVersion) def test_get_outputter_expected(self, sv: SchemaVersion) -> None: - outputterClass = BY_SCHEMA_VERSION[sv] - self.assertTrue(issubclass(outputterClass, Xml)) - outputter = outputterClass(Mock(spec=Bom)) + outputter_class = BY_SCHEMA_VERSION[sv] + self.assertTrue(issubclass(outputter_class, Xml)) + outputter = outputter_class(Mock(spec=Bom)) self.assertIs(outputter.schema_version, sv) self.assertIs(outputter.output_format, OutputFormat.XML) diff --git a/tests/test_validation.py b/tests/test_validation.py index 567dff31..8755191a 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -49,6 +49,6 @@ def test_as_expected(self, of: OutputFormat, sv: SchemaVersion) -> None: *((f, v, (ValueError, 'Unsupported schema_version')) for f, v in UNDEFINED_FORMAT_VERSION) ) @unpack - def test_fails_on_wrong_args(self, of: OutputFormat, sv: SchemaVersion, raisesRegex: Tuple) -> None: - with self.assertRaisesRegex(*raisesRegex): + def test_fails_on_wrong_args(self, of: OutputFormat, sv: SchemaVersion, raises_regex: Tuple) -> None: + with self.assertRaisesRegex(*raises_regex): make_schemabased_validator(of, sv) diff --git a/tools/schema-downloader.py b/tools/schema-downloader.py index 6027117f..ca70f85e 100644 --- a/tools/schema-downloader.py +++ b/tools/schema-downloader.py @@ -19,7 +19,7 @@ SOURCE_ROOT = 'https://raw.githubusercontent.com/CycloneDX/specification/master/schema/' TARGET_ROOT = join(dirname(__file__), '..', 'cyclonedx', 'schema', '_res') -bom_xsd = { +BOM_XSD = { 'versions': ['1.4', '1.3', '1.2', '1.1', '1.0'], 'sourcePattern': f'{SOURCE_ROOT}bom-%s.xsd', 'targetPattern': join(TARGET_ROOT, 'bom-%s.SNAPSHOT.xsd'), @@ -31,22 +31,22 @@ # "version" is not required but optional with a default value! # this is wrong in schema<1.5 -_bomSchemaEnumMatch = re.compile( +_BOM_SCHEMA_ENUM_RE = re.compile( r'("\$id": "(http://cyclonedx\.org/schema/bom.+?\.schema\.json)".*"enum": \[\s+")' r'http://cyclonedx\.org/schema/bom.+?\.schema\.json"', re.DOTALL) -_bomSchemaEnumReplace = r'\1\2"' +_BOM_SCHEMA_ENUM_REPL = r'\1\2"' # "version" is not required but optional with a default value! # this is wrong in schema<1.5 -_bomRequired = """ +_BOM_REQUIRED_S = """ "required": [ "bomFormat", "specVersion", "version" ],""" -_bomRequiredReplace = """ +_BOM_REQUIRED_R = """ "required": [ "bomFormat", "specVersion" @@ -55,20 +55,20 @@ # there was a case where the default value did not match the own pattern ... # this is wrong in schema<1.5 -_defaultWithPatternMatch = re.compile(r'\s+"default": "",(?![^}]*?"pattern": "\^\(\.\*\)\$")', re.MULTILINE) -_defaultWithPatternReplace = r'' +_DEFAULTS_WITH_PATTERN_RE = re.compile(r'\s+"default": "",(?![^}]*?"pattern": "\^\(\.\*\)\$")', re.MULTILINE) +_DEFAULTS_WITH_PATERN_REPL = r'' -bom_json_lax = { +BOM_JSON_LAX = { 'versions': ['1.4', '1.3', '1.2'], 'sourcePattern': f'{SOURCE_ROOT}bom-%s.schema.json', 'targetPattern': join(TARGET_ROOT, 'bom-%s.SNAPSHOT.schema.json'), 'replace': [ ('spdx.schema.json', 'spdx.SNAPSHOT.schema.json'), ('jsf-0.82.schema.json', 'jsf-0.82.SNAPSHOT.schema.json'), - (_bomRequired, _bomRequiredReplace), + (_BOM_REQUIRED_S, _BOM_REQUIRED_R), ], 'replaceRE': [ - (_bomSchemaEnumMatch, _bomSchemaEnumReplace), + (_BOM_SCHEMA_ENUM_RE, _BOM_SCHEMA_ENUM_REPL), # there was a case where the default value did not match the own pattern ... # this is wrong in schema<1.5 # with current SchemaValidator this is no longer required, as defaults are not applied @@ -76,21 +76,21 @@ ] } -bom_json_strict = { +BOM_JSON_STRICT = { 'versions': ['1.3', '1.2'], 'sourcePattern': f'{SOURCE_ROOT}bom-%s-strict.schema.json', 'targetPattern': join(TARGET_ROOT, 'bom-%s-strict.SNAPSHOT.schema.json'), - 'replace': bom_json_lax['replace'], - 'replaceRE': bom_json_lax['replaceRE'] + 'replace': BOM_JSON_LAX['replace'], + 'replaceRE': BOM_JSON_LAX['replaceRE'] } -other_downloadables = [ +OTHER_DOWNLOADABLES = [ (f'{SOURCE_ROOT}spdx.schema.json', join(TARGET_ROOT, 'spdx.SNAPSHOT.schema.json')), (f'{SOURCE_ROOT}spdx.xsd', join(TARGET_ROOT, 'spdx.SNAPSHOT.xsd')), (f'{SOURCE_ROOT}jsf-0.82.schema.json', join(TARGET_ROOT, 'jsf-0.82.SNAPSHOT.schema.json')), ] -for dspec in (bom_xsd, bom_json_lax, bom_json_strict): +for dspec in (BOM_XSD, BOM_JSON_LAX, BOM_JSON_STRICT): for version in dspec['versions']: source = dspec['sourcePattern'].replace('%s', version) target = dspec['targetPattern'].replace('%s', version) @@ -104,5 +104,5 @@ text = search.sub(replace, text) tarf.write(text) -for source, target in other_downloadables: +for source, target in OTHER_DOWNLOADABLES: urlretrieve(source, target) diff --git a/tox.ini b/tox.ini index 6b16c1c8..56aa09d3 100644 --- a/tox.ini +++ b/tox.ini @@ -37,20 +37,3 @@ commands = [testenv:flake8] commands = poetry run flake8 cyclonedx/ tests/ typings/ examples/ tools/ - -[flake8] -## keep in sync with isort config - in `isort.cfg` file -exclude = - build,dist,__pycache__,.eggs,*.egg-info*, - *_cache,*.cache, - .git,.tox,.venv,venv,.venv*,venv*, - _OLD,_TEST, - docs -max-line-length = 120 -ignore = - E305 - # ignore `self`, `cls` markers of flake8-annotations>=2.0 - ANN101,ANN102 - # ignore ANN401 for dynamically typed *args and **kwargs - ANN401 - B028