-
Notifications
You must be signed in to change notification settings - Fork 275
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
Metadata API: Improve metadata.signed "typing" #1433
Comments
so this does not work:
because the "specific generic type" (Root in this case) is not an actual type available during runtime at all: there is no way to affect runtime based on that type (e.g. to raise an exception if it does not match). It might still work if the type is given as parameter or the method contains it as constant (since then we obviously have the type available at runtime):
The signatures for those alternative could be:
and I think here A) metadata.signed could be correctly typed and B) we could raise if the signed type does not match. Will have to test this... |
The purpose is two-fold: 1. When we deserialize metadata, we usually know what signed type we expect: make it easy to enforce that 2. When we use Metadata, it is helpful if the specific signed type (and all of the classes attribute types are correctly annotated Making Metadata Generic over T, where T = TypeVar("T", "Root", "Timestamp", "Snapshot", "Targets") allows both of these cases to work. Using Generics is completely optional so all existing code still works. For case 1, the following calls will now raise a Deserialization error if the expected type is incorrect: md = Metadata.from_bytes(data, signed_type=Snapshot) md = Metadata.from_file(filename, signed_type=Snapshot) For case 2, the return value md of those calls is now of type "Metadata[Snapshot]", and md.signed is now of type "Snapshot" allowing IDE annotations and static type checking. Adding a type argument is an unconventional way to do this: the reason for it is that the specific type (e.g. Snapshot) is not otherwise available at runtime. A call like this works fine and md is annotated: md = Metadata[Snapshot].from_bytes(data) but it's not possible to validate that "data" contains a "Snapshot", because the value "Snapshot" is not defined at runtime at all, it is purely an annotation. So an actual argument is needed. Fixes theupdateframework#1433 Signed-off-by: Jussi Kukkonen <[email protected]>
The purpose is two-fold: 1. When we deserialize metadata, we usually know what signed type we expect: make it easy to enforce that 2. When we use Metadata, it is helpful if the specific signed type (and all of the classes attribute types are correctly annotated Making Metadata Generic over T, where T = TypeVar("T", "Root", "Timestamp", "Snapshot", "Targets") allows both of these cases to work. Using Generics is completely optional so all existing code still works. For case 1, the following calls will now raise a Deserialization error if the expected type is incorrect: md = Metadata.from_bytes(data, signed_type=Snapshot) md = Metadata.from_file(filename, signed_type=Snapshot) For case 2, the return value md of those calls is now of type "Metadata[Snapshot]", and md.signed is now of type "Snapshot" allowing IDE annotations and static type checking. Adding a type argument is an unconventional way to do this: the reason for it is that the specific type (e.g. Snapshot) is not otherwise available at runtime. A call like this works fine and md is annotated: md = Metadata[Snapshot].from_bytes(data) but it's not possible to validate that "data" contains a "Snapshot", because the value "Snapshot" is not defined at runtime at all, it is purely an annotation. So an actual argument is needed. Fixes theupdateframework#1433 Signed-off-by: Jussi Kukkonen <[email protected]>
Forgot to mention here: there's a linked PR that implements the It's not the prettiest thing in the world with the type argument but implementation is not too complex and using it feels quite nice. |
When we use Metadata, it is helpful if the specific signed type (and all of the signed types attribute types are correctly annotated. Currently this is not possible. Making Metadata Generic with constraint T, where T = TypeVar("T", "Root", "Timestamp", "Snapshot", "Targets") allows these annotations. Using Generic annotations is completely optional so all existing code still works -- the changes in test code are done to make IDE annotations more useful in the test code, not because they are required. Examples: md = Metadata[Root].from_bytes(data) md:Root = Metadata.from_bytes(data) In both examples md.signed is now statically typed as "Root" allowing IDE annotations and static type checking by mypy. Note that it's not possible to validate that "data" actually contains a root metadata at runtime in these examples as the annotations are _not_ visible at runtime at all: new constructors would have to be added for that. Partially fixes theupdateframework#1433 Signed-off-by: Jussi Kukkonen <[email protected]>
When we use Metadata, it is helpful if the specific signed type (and all of the signed types attribute types are correctly annotated. Currently this is not possible. Making Metadata Generic with constraint T, where T = TypeVar("T", "Root", "Timestamp", "Snapshot", "Targets") allows these annotations. Using Generic annotations is completely optional so all existing code still works -- the changes in test code are done to make IDE annotations more useful in the test code, not because they are required. Examples: md = Metadata[Root].from_bytes(data) md:Metadata[Root] = Metadata.from_bytes(data) In both examples md.signed is now statically typed as "Root" allowing IDE annotations and static type checking by mypy. Note that it's not possible to validate that "data" actually contains a root metadata at runtime in these examples as the annotations are _not_ visible at runtime at all: new constructors would have to be added for that. Partially fixes theupdateframework#1433 Signed-off-by: Jussi Kukkonen <[email protected]>
When we use Metadata, it is helpful if the specific signed type (and all of the signed types attribute types are correctly annotated. Currently this is not possible. Making Metadata Generic with constraint T, where T = TypeVar("T", "Root", "Timestamp", "Snapshot", "Targets") allows these annotations. Using Generic annotations is completely optional so all existing code still works -- the changes in test code are done to make IDE annotations more useful in the test code, not because they are required. Examples: md = Metadata[Root].from_bytes(data) md:Metadata[Root] = Metadata.from_bytes(data) In both examples md.signed is now statically typed as "Root" allowing IDE annotations and static type checking by mypy. Note that it's not possible to validate that "data" actually contains a root metadata at runtime in these examples as the annotations are _not_ visible at runtime at all: new constructors would have to be added for that. Partially fixes theupdateframework#1433 Signed-off-by: Jussi Kukkonen <[email protected]>
When we use Metadata, it is helpful if the specific signed type (and all of the signed types attribute types are correctly annotated. Currently this is not possible. Making Metadata Generic with constraint T, where T = TypeVar("T", "Root", "Timestamp", "Snapshot", "Targets") allows these annotations. Using Generic annotations is completely optional so all existing code still works -- the changes in test code are done to make IDE annotations more useful in the test code, not because they are required. Examples: md = Metadata[Root].from_bytes(data) md:Metadata[Root] = Metadata.from_bytes(data) In both examples md.signed is now statically typed as "Root" allowing IDE annotations and static type checking by mypy. Note that it's not possible to validate that "data" actually contains a root metadata at runtime in these examples as the annotations are _not_ visible at runtime at all: new constructors would have to be added for that. from_file() is now a class method like from_bytes() to make sure both have the same definition of "T" when from_file() calls from_bytes(): This makes mypy happy. Partially fixes theupdateframework#1433 Signed-off-by: Jussi Kukkonen <[email protected]>
When we use Metadata, it is helpful if the specific signed type (and all of the signed types attribute types are correctly annotated. Currently this is not possible. Making Metadata Generic with constraint T, where T = TypeVar("T", "Root", "Timestamp", "Snapshot", "Targets") allows these annotations. Using Generic annotations is completely optional so all existing code still works -- the changes in test code are done to make IDE annotations more useful in the test code, not because they are required. Examples: md = Metadata[Root].from_bytes(data) md:Metadata[Root] = Metadata.from_bytes(data) In both examples md.signed is now statically typed as "Root" allowing IDE annotations and static type checking by mypy. Note that it's not possible to validate that "data" actually contains a root metadata at runtime in these examples as the annotations are _not_ visible at runtime at all: new constructors would have to be added for that. from_file() is now a class method like from_bytes() to make sure both have the same definition of "T" when from_file() calls from_bytes(): This makes mypy happy. Partially fixes theupdateframework#1433 Signed-off-by: Jussi Kukkonen <[email protected]>
There's two case I'd like to see improved in metadata signed handling:
Metadata.from_bytes()
) knowing what signed type should be, yet I can't enforce thisItem 1 could be fixed with just adding an optional type argument to the constructors (
Metadata.from_bytes(data, type=Root)
) and raising if it does not match.But I wonder if some Python wizard could fix both by making Metadata generic over Root/Snapshot/Timestamp/Targets:
The text was updated successfully, but these errors were encountered: