The vast majority of the time, a given call site will expect a particular message type. Previously, calling decode(data) would behave the same, regardless on which class it was called on, and inferred the message type from the CBOR tag.
This change uses the cls argument to enforce the correct message type. For example, Sign1Message.decode(data) will either return a Sign1Message or raise an exception if the message type is incorrect.
It is still possible to use CoseMessage.decode(data) if the message type is not known ahead of time, and then dynamically inspect the type of the return value.
This also fixes the type annotation of decode, which previously had an unconstrained CM type variable. This led to either errors where type checkers could not infer the return type, or it would allow the caller to annotate the call with any type it wanted.
Before:
# mypy error: Need type annotation for "msg"
msg = CoseMessage.decode(data)
# This type checks, regardless of the contents of data.
# At runtime, msg may have a different type if data contains a different
# message type.
msg: Sign1Message = CoseMessage.decode(data)
After:
# This type checks, and msg has type CoseMessage
msg = CoseMessage.decode(data)
# This type checks, and msg has type Sign1Message
# An exception is raised if data is not a valid Sign1Message
msg = Sign1Message.decode(data)
Note that even when using an explicit message type, this still requires the data to include a CBOR tag. As a next step, we could loosen this requirement, and make the tag optional is the message type is known.
Finally, as a minor change, the type of the key in CoseMessage is switched from the CK type variable to CoseKey. The former had odd semantics, and led to type checker errors:
msg = Sign1Message()
# error: Incompatible types in assignment (expression has type "EC2Key", variable has type "CK")
msg.key = EC2Key.generate_key(crv=P256)
The vast majority of the time, a given call site will expect a particular message type. Previously, calling
decode(data)
would behave the same, regardless on which class it was called on, and inferred the message type from the CBOR tag.This change uses the cls argument to enforce the correct message type. For example,
Sign1Message.decode(data)
will either return a Sign1Message or raise an exception if the message type is incorrect.It is still possible to use
CoseMessage.decode(data)
if the message type is not known ahead of time, and then dynamically inspect the type of the return value.This also fixes the type annotation of decode, which previously had an unconstrained
CM
type variable. This led to either errors where type checkers could not infer the return type, or it would allow the caller to annotate the call with any type it wanted.Before:
After:
Note that even when using an explicit message type, this still requires the data to include a CBOR tag. As a next step, we could loosen this requirement, and make the tag optional is the message type is known.
Finally, as a minor change, the type of the key in CoseMessage is switched from the CK type variable to CoseKey. The former had odd semantics, and led to type checker errors: