-
-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ExternalReference url parameters that are not XsUri cause hard to find serialization issues #363
Comments
you are right about the type. cyclonedx-python-lib/cyclonedx/model/__init__.py Lines 495 to 500 in 723ae8e
Python is not hard typed. What prevents from passing the correct data type ( |
Nothing prevents that. So it is trash in -> trash out of course. It is just some not so nice user API design that the 'url' parameter requires XsUri and not just str. I can see why this is done to make the code nicely orthogonal. The serialization blows up with the unhelpful Exception in that case. Some better diagnostics at serialization time would be nice, to discover the part of the model that is in an invalid state. So feel free to close this, if better diagnostics is out of scope. |
how would a "nice user API design" look like? |
I migrated some code, and had a few instances of this pattern setting optional URLs, e.g. in License. License(
...
url=somedict.get('URL'),
) this works nicely when url takes a str, as in Optional[str] and keeps XsUri as an implementation detail thats just a fancy validator, but needs some extra License(
...
url=XsUri(somedict['URL']) if somedict.get('URL') else None
) In addition most external users of the class probably do not care about the XsUri type for an URL. Typically they will have some form of string at hand. On reading out the URL, they will cast that away to str to feed it into other APIs. On writing they need to add boilerplace XsUri to convert the str values they have. The only part needing that type is the serialization code and the validation step. So for most purposes visible to users of the class, the e.g. from typing import TypeAlias
urlstr: TypeAlias str
def __init__(self, *, type: ExternalReferenceType, url: urlstr, comment: Optional[str] = None,
hashes: Optional[Iterable[HashType]] = None) -> None:
self.url = url if isinstance(url, XsUri) else XsUri(str)
self.comment = comment
self.type = type
self.hashes = hashes or [] # type: ignore the setter/getters would obviously need to do something as well. But using the code gets nicer. If i pass in junk, it raises the proper exceptions. I can directly pass values in from other places without value wrappers and so on. This is a little different to the Purl case, where the PackageURL class actually carries more weight. If you see merit in improving this, i can try to do a pull request. |
so it would be more like (fixed typing) from typing import Optional, Union
from ... import XsUri, ExternalReferenceType, HashType, Iterable
def __init__(self, *,
type: ExternalReferenceType,
url: Union[XsUri, str],
comment: Optional[str] = None,
hashes: Optional[Iterable[HashType]] = None
) -> None:
self.url = url if isinstance(url, XsUri) else XsUri(str)
... would you create a pullrequest with the desired change? |
see #432 |
closed. downstream users may write themselves a wrapper, doing the exact same an in-bound code would do: applies like (based on given examples above) makeXsUriFromAny(v) -> XsUri | None:
return XsUri(v) if isinstance(v, str) else None
License(
...
url=makeXsUriFromAny(somedict['URL'])
) |
The 4.0 version is nice, but has changed a few things in the serialization.
In 3.x one could use:
In 4.0, this needs some boilerplate XsUri:
Omitting the XsUri in 4.0 this causes a Traceback on serialization:
It would be nice, if the
url
parameters, especially forExternalReference
check the type of the argument. The code has Mypy type annotations listing XsUri, yes, but that does not help to figure it out at runtime.The text was updated successfully, but these errors were encountered: