CycloneDX / cyclonedx-python-lib

Python implementation of OWASP CycloneDX
https://cyclonedx.org/
Apache License 2.0
70 stars 40 forks source link

Preferred way for outputting #702

Closed hakandilek closed 1 month ago

hakandilek commented 1 month ago

This is probably both a bug report and a question.

Since cyclonedx.output.get_instance() has been removed via #493 the examples for the outputting in the docs are not valid anymore(bug report):

from cyclonedx.output import get_instance, BaseOutput, OutputFormat

outputter: BaseOutput = get_instance(bom=bom, output_format=OutputFormat.JSON)
bom_json: str = outputter.output_as_string()

Here comes the question part:

:a: The preferred way (as suggested in the deprecation message) would be using the cyclonedx.output.make_outputter() like this :

from cyclonedx.output import make_outputter, BaseOutput, OutputFormat, SchemaVersion

outputter: BaseOutput = make_outputter(bom=bom, output_format=OutputFormat.JSON, schema_version=SchemaVersion.V1_6)
bom_json: str = outputter.output_as_string()

:b: There is also a much simpler way to use the predefined format+schema combined outputs:

from cyclonedx.output.json import JsonV1Dot6

outputter = JsonV1Dot6(bom=bom)
bom_json: str = outputter.output_as_string()

Which one of :a: or :b: would be the preferred way?

If we agree on it, I can create a PR to resolve the bug in the docs.

jkowalleck commented 1 month ago

Foremost, thanks for reporting the incorrect docs. Feel free to pullrequest improvements. The doc-sources are here: https://github.com/CycloneDX/cyclonedx-python-lib/tree/main/docs


in https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/examples/complex_serialize.py we use the following options:

both examples correlate with yours.

Which one of 🅰️ or 🅱️ would be the preferred way?

We do not have one.
But we have one for certain situations:

For example, let's say I had a script that always outputs CycloneDX 1.5 as XML - then I would go with 🅱️. If I had a CLI tool that takes the output_format and schema_version from command line arguments, then I would go with 🅰️, since it gives me more flexibility without writing large behavior-maps or if/elif/match/case code blocks.


Let's also look at the internals.

🅰️'s make_outputter is just a fancy wrapper with a maintained behavior map for 🅱️. https://github.com/CycloneDX/cyclonedx-python-lib/blob/a210809efb34c2dc895fc0c6d96a3412a9097625/cyclonedx/output/__init__.py#L126-L138

We found that some downstream users implemented a behavior-map like that, and when we introduced breaking changes, they often did not upgrade because they would need to change these mappings that were buried somewhere deep in their code. So we decided to provide and maintain these mappings in the library, so that users don't have to. This led to the API cyclonedx.output.make_outputter().

history: v6 API change:

  • Removed deprecated function output.get_instance() (via [#493])
    Use function output.make_outputter() instead.
hakandilek commented 1 month ago

Feel free to pullrequest improvements. The doc-sources are here: https://github.com/CycloneDX/cyclonedx-python-lib/tree/main/docs

@jkowalleck Thanks for the great explanation.

I've tried to combine the gist of it in the PR #709. Feel free to give your feedback on it.