Skip to content

Commit 97ed561

Browse files
authored
Merge branch 'main' into ci/test-macos-latest
Signed-off-by: Jan Kowalleck <[email protected]>
2 parents d66c041 + 9a5e6d8 commit 97ed561

30 files changed

+320
-254
lines changed

.github/workflows/python.yml

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
- python-version: '3.13' # latest
7878
os: ubuntu-latest
7979
toxenv-factors: '-current'
80-
- python-version: '3.8' # lowest
80+
- python-version: '3.9' # lowest
8181
os: ubuntu-latest
8282
toxenv-factors: '-lowest'
8383
steps:
@@ -169,15 +169,7 @@ jobs:
169169
- "3.12"
170170
- "3.11"
171171
- "3.10"
172-
- "3.9"
173-
- "3.8" # lowest supported -- handled in include
174-
exclude:
175-
- # macos-latest might be incompatible to py38 - see https://github.com/CycloneDX/cyclonedx-python-lib/pull/599#issuecomment-2077462142
176-
os: macos-latest
177-
python-version: "3.8"
178-
include:
179-
- os: macos-13
180-
python-version: "3.8"
172+
- "3.9" # lowest supported -- handled in include
181173
steps:
182174
- name: Checkout
183175
# see https://github.com/actions/checkout

CHANGELOG.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,59 @@
22

33

44

5+
## v5.5.0 (2025-04-23)
6+
7+
### Feature
8+
9+
* feat: support runtime-dependency `packaging ^25` (#882)
10+
11+
Signed-off-by: Jan Kowalleck &lt;[email protected]&gt; ([`4fa5a35`](https://github.com/CycloneDX/cyclonedx-python/commit/4fa5a35ad8419f352c2436f86efd070b8729c5af))
12+
13+
14+
## v5.4.0 (2025-04-23)
15+
16+
### Documentation
17+
18+
* docs: reword common CLI switches (#877)
19+
20+
Signed-off-by: Jan Kowalleck &lt;[email protected]&gt; ([`3c86517`](https://github.com/CycloneDX/cyclonedx-python/commit/3c86517a9e9986270cf7d2c51a2d62957fbdb712))
21+
22+
* docs: showcase usage with `uv` (#858)
23+
24+
25+
26+
---------
27+
28+
Signed-off-by: Jan Kowalleck &lt;[email protected]&gt; ([`efd45b1`](https://github.com/CycloneDX/cyclonedx-python/commit/efd45b1f6f4aaebf70a9d645636626636145de26))
29+
30+
* docs: install instructions for `uv`
31+
32+
Signed-off-by: Jan Kowalleck &lt;[email protected]&gt; ([`07d9bcc`](https://github.com/CycloneDX/cyclonedx-python/commit/07d9bccea8bd5cefa34dec0cb930da719a7dac97))
33+
34+
### Feature
35+
36+
* feat: support `cyclonedx-python-lib ^10` (#880)
37+
38+
Signed-off-by: Jan Kowalleck &lt;[email protected]&gt; ([`545dde0`](https://github.com/CycloneDX/cyclonedx-python/commit/545dde0cfd380748f711e159ecb2a7c4fb9cf81b))
39+
40+
* feat: deprecate CLI switch `--outfile`; use new `--output-file` instead (#875)
41+
42+
43+
44+
Signed-off-by: Jan Kowalleck &lt;[email protected]&gt; ([`fb30ee0`](https://github.com/CycloneDX/cyclonedx-python/commit/fb30ee098f10ba805212bb6463ec7933676592c1))
45+
46+
* feat: deprecate CLI switch `--schema-version`; use new `--spec-version` instead (#871)
47+
48+
49+
Signed-off-by: Jan Kowalleck &lt;[email protected]&gt; ([`bbae05f`](https://github.com/CycloneDX/cyclonedx-python/commit/bbae05f3130c79c442f67f3ee544a7e4701d5a86))
50+
51+
### Unknown
52+
53+
* docs
54+
55+
Signed-off-by: Jan Kowalleck &lt;[email protected]&gt; ([`4837c99`](https://github.com/CycloneDX/cyclonedx-python/commit/4837c990c82a5ec0901ef1a23508d4be92537805))
56+
57+
558
## v5.3.0 (2025-02-26)
659

760
### Feature

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Read the full [documentation][link_rtfd] for more details.
4343

4444
## Requirements
4545

46-
* Python `>=3.8,<4`
46+
* Python `>=3.9,<4`
4747

4848
However, there are older versions of this tool available, which
4949
support Python `>=2.7`.

bandit.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
exclude_dirs:
55
- docs
66
- .venv
7+
- .tox
78

89
skips:
910
- B101

cyclonedx_py/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
# !! version is managed by `semantic_release`
1919
# do not use typing here, or else `semantic_release` might have issues finding the variable
20-
__version__ = "5.3.0" # noqa:Q000
20+
__version__ = "5.5.0" # noqa:Q000
2121

2222
# There is no stable/public API.
2323
# However, you might call the stable CLI instead, like so:

cyclonedx_py/_internal/cli.py

Lines changed: 65 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717

1818
import logging
1919
import sys
20-
from argparse import ArgumentParser, FileType, RawDescriptionHelpFormatter
20+
from argparse import ArgumentParser, BooleanOptionalAction, FileType, RawDescriptionHelpFormatter
21+
from collections.abc import Sequence
2122
from itertools import chain
22-
from typing import TYPE_CHECKING, Any, Dict, List, NoReturn, Optional, Sequence, TextIO, Type, Union
23+
from typing import TYPE_CHECKING, Any, NoReturn, Optional, TextIO, Union
2324

2425
from cyclonedx.model import Property
2526
from cyclonedx.output import make_outputter
@@ -35,19 +36,12 @@
3536
from .utils.args import argparse_type4enum, choices4enum
3637

3738
if TYPE_CHECKING: # pragma: no cover
38-
from argparse import Action
39-
4039
from cyclonedx.model.bom import Bom
4140
from cyclonedx.model.component import Component
4241

4342
from . import BomBuilder
4443

45-
BooleanOptionalAction: Optional[Type[Action]]
46-
47-
if sys.version_info >= (3, 9):
48-
from argparse import BooleanOptionalAction
49-
else:
50-
BooleanOptionalAction = None
44+
OPTION_OUTPUT_STDOUT = '-'
5145

5246

5347
class Command:
@@ -74,61 +68,61 @@ def make_argument_parser(cls, sco: ArgumentParser, **kwargs: Any) -> ArgumentPar
7468
action='store_true',
7569
dest='short_purls',
7670
default=False)
77-
op.add_argument('-o', '--outfile',
78-
metavar='<file>',
79-
help='Output file path for your SBOM'
80-
' (set to "-" to output to <stdout>)'
81-
' (default: %(default)s)',
82-
type=FileType('wt', encoding='utf8'),
83-
dest='outfile',
84-
default='-')
85-
op.add_argument('--sv', '--schema-version',
71+
op.add_argument('--schema-version', # DEPRECATED
8672
metavar='<version>',
87-
help='The CycloneDX schema version for your SBOM'
88-
f' {{choices: {", ".join(sorted((v.to_version() for v in SchemaVersion), reverse=True))}}}'
89-
' (default: %(default)s)',
90-
dest='schema_version',
73+
help='DEPRECATED alias for option "--spec-version".',
74+
dest='spec_version',
9175
choices=SchemaVersion,
9276
type=SchemaVersion.from_version,
9377
default=SchemaVersion.V1_5.to_version())
78+
op.add_argument('--sv', '--spec-version',
79+
metavar='<version>',
80+
help='Which version of CycloneDX to use.'
81+
f' {{choices: {", ".join(sorted((v.to_version() for v in SchemaVersion), reverse=True))}}}'
82+
' (default: %(default)s)',
83+
dest='spec_version',
84+
choices=SchemaVersion,
85+
type=SchemaVersion.from_version,
86+
default=SchemaVersion.V1_5.to_version())
87+
op.add_argument('--output-reproducible',
88+
help='Whether to go the extra mile and make the output reproducible.\n'
89+
'This might result in loss of time- and random-based values.',
90+
action='store_true',
91+
dest='output_reproducible',
92+
default=False)
9493
op.add_argument('--of', '--output-format',
9594
metavar='<format>',
96-
help='The output format for your SBOM'
97-
f' {choices4enum(OutputFormat)}'
98-
' (default: %(default)s)',
95+
help='Which output format to use.'
96+
f' {choices4enum(OutputFormat)}'
97+
' (default: %(default)s)',
9998
dest='output_format',
10099
choices=OutputFormat,
101100
type=argparse_type4enum(OutputFormat),
102101
default=OutputFormat.JSON.name)
103-
op.add_argument('--output-reproducible',
104-
help='Whether to go the extra mile and make the output reproducible.\n'
105-
'This might result in loss of time- and random-based-values.',
106-
action='store_true',
107-
dest='output_reproducible',
108-
default=False)
109-
if BooleanOptionalAction:
110-
op.add_argument('--validate',
111-
help='Whether validate the result before outputting'
112-
' (default: %(default)s)',
113-
action=BooleanOptionalAction,
114-
dest='should_validate',
115-
default=True)
116-
else:
117-
vg = op.add_mutually_exclusive_group()
118-
vg.add_argument('--validate',
119-
help='Validate the result before outputting'
120-
' (default: %(default)s)',
121-
action='store_true',
122-
dest='should_validate',
123-
default=True)
124-
vg.add_argument('--no-validate',
125-
help='Do not validate the result before outputting',
126-
dest='should_validate',
127-
action='store_false')
102+
op.add_argument('--outfile', # DEPRECATED
103+
metavar='<file>',
104+
help='DEPRECATED alias for "--output-file".',
105+
type=FileType('wt', encoding='utf8'),
106+
dest='output_file',
107+
default=OPTION_OUTPUT_STDOUT)
108+
op.add_argument('-o', '--output-file',
109+
metavar='<file>',
110+
help='Path to the output file.'
111+
f' (set to "{OPTION_OUTPUT_STDOUT}" to output to <stdout>)'
112+
' (default: %(default)s)',
113+
type=FileType('wt', encoding='utf8'),
114+
dest='output_file',
115+
default=OPTION_OUTPUT_STDOUT)
116+
op.add_argument('--validate',
117+
help='Whether to validate resulting BOM before outputting.'
118+
' (default: %(default)s)',
119+
action=BooleanOptionalAction,
120+
dest='should_validate',
121+
default=True)
128122

129-
scbbc: Type['BomBuilder']
123+
scbbc: type['BomBuilder']
130124
sct: str
131-
scta: List[str]
125+
scta: list[str]
132126
for scbbc, sct, *scta in (
133127
(EnvironmentBB, 'environment', 'env', 'venv'),
134128
(RequirementsBB, 'requirements'),
@@ -150,28 +144,28 @@ def make_argument_parser(cls, sco: ArgumentParser, **kwargs: Any) -> ArgumentPar
150144

151145
__OWN_ARGS = {
152146
# the arg keywords from __init__()
153-
'logger', 'short_purls', 'output_format', 'schema_version', 'output_reproducible', 'should_validate',
147+
'logger', 'short_purls', 'output_format', 'spec_version', 'output_reproducible', 'should_validate',
154148
# the arg keywords from __call__()
155-
'outfile'
149+
'output_file'
156150
}
157151

158152
@classmethod
159-
def _clean_kwargs(cls, kwargs: Dict[str, Any]) -> Dict[str, Any]:
153+
def _clean_kwargs(cls, kwargs: dict[str, Any]) -> dict[str, Any]:
160154
return {k: kwargs[k] for k in kwargs if k not in cls.__OWN_ARGS}
161155

162156
def __init__(self, *,
163157
logger: logging.Logger,
164158
short_purls: bool,
165159
output_format: OutputFormat,
166-
schema_version: SchemaVersion,
160+
spec_version: SchemaVersion,
167161
output_reproducible: bool,
168162
should_validate: bool,
169-
_bbc: Type['BomBuilder'],
163+
_bbc: type['BomBuilder'],
170164
**kwargs: Any) -> None:
171165
self._logger = logger
172166
self._short_purls = short_purls
173167
self._output_format = output_format
174-
self._schema_version = schema_version
168+
self._spec_version = spec_version
175169
self._output_reproducible = output_reproducible
176170
self._should_validate = should_validate
177171
self._bbc = _bbc(**self._clean_kwargs(kwargs),
@@ -206,17 +200,17 @@ def _validate(self, output: str) -> bool:
206200
self._logger.warning('Validation skipped.')
207201
return False
208202

209-
self._logger.info('Validating result to schema: %s/%s',
210-
self._schema_version.to_version(), self._output_format.name)
203+
self._logger.info('Validating result to spec: %s/%s',
204+
self._spec_version.to_version(), self._output_format.name)
211205

212206
validation_error = make_schemabased_validator(
213207
self._output_format,
214-
self._schema_version
208+
self._spec_version
215209
).validate_str(output)
216210
if validation_error:
217211
self._logger.debug('Validation Errors: %r', validation_error.data)
218212
self._logger.error('The result is invalid to schema '
219-
f'{self._schema_version.to_version()}/{self._output_format.name}')
213+
f'{self._spec_version.to_version()}/{self._output_format.name}')
220214
self._logger.warning('Please report the issue and provide all input data to: '
221215
'https://github.com/CycloneDX/cyclonedx-python/issues/new?'
222216
'template=ValidationError-report.md&'
@@ -225,14 +219,14 @@ def _validate(self, output: str) -> bool:
225219
self._logger.debug('result is schema-valid')
226220
return True
227221

228-
def _write(self, output: str, outfile: TextIO) -> int:
229-
self._logger.info('Writing to: %s', outfile.name)
230-
written = outfile.write(output)
231-
self._logger.debug('Wrote %i bytes to %s', written, outfile.name)
222+
def _write(self, output: str, output_file: TextIO) -> int:
223+
self._logger.info('Writing to: %s', output_file.name)
224+
written = output_file.write(output)
225+
self._logger.debug('Wrote %i bytes to %s', written, output_file.name)
232226
return written
233227

234228
def _make_output(self, bom: 'Bom') -> str:
235-
self._logger.info('Serializing SBOM: %s/%s', self._schema_version.to_version(), self._output_format.name)
229+
self._logger.info('Serializing SBOM: %s/%s', self._spec_version.to_version(), self._output_format.name)
236230

237231
if self._output_reproducible:
238232
bom.metadata.properties.add(Property(name=PropertyName.Reproducible.value,
@@ -244,22 +238,22 @@ def _make_output(self, bom: 'Bom') -> str:
244238
return make_outputter(
245239
bom,
246240
self._output_format,
247-
self._schema_version
241+
self._spec_version
248242
).output_as_string(indent=2)
249243

250244
def _make_bom(self, **kwargs: Any) -> 'Bom':
251245
self._logger.info('Generating SBOM ...')
252246
return self._bbc(**self._clean_kwargs(kwargs))
253247

254248
def __call__(self,
255-
outfile: TextIO,
249+
output_file: TextIO,
256250
**kwargs: Any) -> None:
257251
bom = self._make_bom(**kwargs)
258252
self._shorten_purls(bom)
259253
output = self._make_output(bom)
260254
del bom
261255
self._validate(output)
262-
self._write(output, outfile)
256+
self._write(output, output_file)
263257

264258

265259
def run(*, argv: Optional[Sequence[str]] = None, **kwargs: Any) -> Union[int, NoReturn]:

cyclonedx_py/_internal/cli_common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def add_argument_mc_type(p: 'ArgumentParser') -> 'Action':
4242
ComponentType.LIBRARY]
4343
return p.add_argument('--mc-type',
4444
metavar='<type>',
45-
help='Type of the main component'
45+
help='Type of the main component.'
4646
f' {{choices: {", ".join(t.value for t in choices)}}}'
4747
' (default: %(default)s)',
4848
dest='mc_type',

0 commit comments

Comments
 (0)