-
Notifications
You must be signed in to change notification settings - Fork 1.7k
asn1 : Add support for CHOICE fields #14183
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
Conversation
eb91e8c to
f528eba
Compare
Signed-off-by: Facundo Tuesca <facundo.tuesca@trailofbits.com>
f528eba to
222b08b
Compare
|
I was going back through our notes, and we decided not to do |
Do you mean something like this?: U = typing.TypeVar("U")
Tag = typing.TypeVar("Tag")
@dataclasses.dataclass(frozen=True)
class Variant(typing.Generic[U, Tag]):
value: U
tag: str
@dataclasses.dataclass(frozen=True)
class MyClass:
foo: (
Annotated[Variant[int, typing.Literal["Tag1"]], asn1.Implicit(0)]
| Annotated[Variant[int, typing.Literal["Tag2"]], asn1.Implicit(1)]
| Annotated[str, asn1.Implicit(2)]
| Annotated[bool, asn1.Implicit(3)]
)
# creating a value for encoding
a = MyClass(foo=Variant(3, "Tag1"))
# looking at a decoded value to see which variant it is
if isinstance(a.foo.value, int) and a.foo.tag == "Tag1":
# Tag1 logic
elif isinstance(a.foo.value, int) and a.foo.tag == "Tag2":
# Tag2 logic
elif isinstance(a.foo.value, str):
# str logic
elif isinstance(a.foo.value, bool):
# bool logic |
|
Something like that, yeah.
…On Sat, Jan 24, 2026 at 5:53 AM Facundo Tuesca ***@***.***> wrote:
*facutuesca* left a comment (pyca/cryptography#14183)
<#14183 (comment)>
I was going back through our notes, and we decided not to do
asn1.Variant[int] because you wouldn't know which variant you selected,
but is there a reason we can't do asn1.Varaint[int, "some tag"] and then
you get an instanec of asn1.Variant[int](value, "some tag") or something?
I guess I hate that, but I also hate every other API we've considered.
Do you mean something like this?:
U = typing.TypeVar("U")Tag = typing.TypeVar("Tag")
@dataclasses.dataclass(frozen=True)class Variant(typing.Generic[U, Tag]):
value: U
tag: str
@dataclasses.dataclass(frozen=True)class MyClass:
foo: (
Variant[int, typing.Literal["Tag1"]]
| Variant[int, typing.Literal["Tag2"]]
)
a = MyClass(foo=Variant(3, "myIntA"))
print(typing.get_type_hints(MyClass))
# out: {'foo': __main__.Variant[int, typing.Literal['Tag1']] | __main__.Variant[int, typing.Literal['Tag2']]}k
—
Reply to this email directly, view it on GitHub
<#14183 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAAGBAJG23H4ZLUIWE7TXD4INFKVAVCNFSM6AAAAACSPHLLDGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTOOJUGQ2TSNBRGY>
.
You are receiving this because you commented.Message ID:
***@***.***>
--
All that is necessary for evil to succeed is for good people to do nothing.
|
PR open here: #14201 |
|
closing in favor of #14201 |
API
This adds support for CHOICE fields using Python union type annotations.
Since unions remove repeated/redundant types, we need a way to represent a CHOICE with multiple variants of the same type.
We do this by creating a trivial wrapper class, so that each variant is a different type:
These can now be used in a union:
And then used to encode/decode:
Details
@asn1.variantdecorator can only have one field, it must be namedvalue, and it should not have any annotations.@asn1.variantdecorator adds a constructor so that the class can be initialized with just the value (e.g:MyInt(42))type_to_tag()was replaced withexpected_tags_for_type(), which does the same thing except it returns a list of tags. This is due to CHOICE types, where the expected tag can be one of many.Part of #12283