Repository navigation
feat: decode an unknown proto enum number as a marked fallback case - #321
Conversation
6aa1acb to
66eee48
Compare
e1d283e to
eeab36c
Compare
66eee48 to
df16584
Compare
df16584 to
5d3c1c0
Compare
eeab36c to
2986e8f
Compare
5d3c1c0 to
fd46000
Compare
c7b747b to
08816c8
Compare
fd46000 to
412faaa
Compare
412faaa to
e6ab98d
Compare
|
|
||
| [error] | ||
| kind = "invalid-wit-directive" | ||
| contains = ["@nexus.fallback", "case `severity-unspecified`", "does not take arguments"] |
There was a problem hiding this comment.
There was feedback to try to keep this to single strings when possible since it is more readable. I added it to the agents instructions, but I think it wasn't in yet at the time.
There was a problem hiding this comment.
Thanks. Changed it in 5828c6a. Each kept fixture now asserts two full clauses, the directive with its location and then the reason, the same as experimental-arguments. The input path sits between them, so one string can't match on every machine.
| interface models { | ||
| /// @nexus.fallback | ||
| /// @nexus.proto "temporal.api.enums.v1.Severity" | ||
| enum alert-level { |
There was a problem hiding this comment.
Eeeh. I would review some of these negative cases. Like do we really need to test every annotation for being put in the wrong place? I am going to make a set of these as part of the issue for always checking the annotation is valid.
There was a problem hiding this comment.
Makes sense. I dropped the two placement fixtures, on the enum and on a record field, in 5828c6a. I kept the placement checks, so a misplaced fallback still fails at generation time until your generic check covers it. Happy to drop them too if you'd rather keep this PR smaller.
| ExecutionTypeNexusOperation = 3 | ||
|
|
||
|
|
||
| def _execution_type_from_number(value: int) -> ExecutionType: |
There was a problem hiding this comment.
Wouldn't call it from "number" in python.
There was a problem hiding this comment.
Renamed it to _<enum>_from_proto in 3a6fc36, to match from_transfer_type. I kept it a separate decode function and not _missing_ on the enum. The fallback should apply only to values that come from the wire, as in TS and .NET. With _missing_, ExecutionType(99) in user code would quietly give the fallback too. Happy to switch if you prefer _missing_.
moetemp
left a comment
There was a problem hiding this comment.
@tconley1428 Thanks for the comments.
|
|
||
| [error] | ||
| kind = "invalid-wit-directive" | ||
| contains = ["@nexus.fallback", "case `severity-unspecified`", "does not take arguments"] |
There was a problem hiding this comment.
Thanks. Changed it in 5828c6a. Each kept fixture now asserts two full clauses, the directive with its location and then the reason, the same as experimental-arguments. The input path sits between them, so one string can't match on every machine.
| interface models { | ||
| /// @nexus.fallback | ||
| /// @nexus.proto "temporal.api.enums.v1.Severity" | ||
| enum alert-level { |
There was a problem hiding this comment.
Makes sense. I dropped the two placement fixtures, on the enum and on a record field, in 5828c6a. I kept the placement checks, so a misplaced fallback still fails at generation time until your generic check covers it. Happy to drop them too if you'd rather keep this PR smaller.
| ExecutionTypeNexusOperation = 3 | ||
|
|
||
|
|
||
| def _execution_type_from_number(value: int) -> ExecutionType: |
There was a problem hiding this comment.
Renamed it to _<enum>_from_proto in 3a6fc36, to match from_transfer_type. I kept it a separate decode function and not _missing_ on the enum. The fallback should apply only to values that come from the wire, as in TS and .NET. With _missing_, ExecutionType(99) in user code would quietly give the fallback too. Happy to switch if you prefer _missing_.
Proto3 enums are open. Python fails the whole message on a Stream status that it doesn't know. TypeScript and .NET pass the number through, and their tests pin it.
Proto3 enums are open, so a newer server can send a case that an older reader doesn't know. `@nexus.fallback` on one case of a proto-backed enum makes Python, TypeScript and .NET decode such a number as that case. An enum without it keeps its behavior, so Python still raises.
The generic annotation check will cover placement. The kept fixtures assert the directive clause and the reason, since the input path sits between them.
3ed36a9 to
d2e8d90
Compare
It matches `from_transfer_type`. The fallback applies to decoded values only, so building the enum from an int stays strict. Co-authored-by: Tim Conley <timothy.conley@temporal.io>
d2e8d90 to
3a6fc36
Compare
What changed?
This PR adds
@nexus.fallback, which marks one case of a proto-backed enum. When a proto value converts to the model, a number that the enum doesn't know becomes that case, in Python, TypeScript and .NET. An enum without the mark keeps its behavior, so Python still raises.@nexus.proto, on a second case of one enum, and anywhere other than an enum case.unspecifiedcase of each enum that a server sends and can extend. In the stream output, those enums decode an unknown number as theirUnspecifiedcase.The first commit adds tests that decode a
DescribeStreamResponsewith status 9 and expect today's result, and the second is the change.Why?
Proto3 enums are open, so a newer server can send a case that an older reader doesn't know. In Python, that failed the whole message, so one new close status could fail a whole read page. The WIT names the case, so an enum that has no good default stays strict.
How did you test it?
Link to a test plan if any -
The stream tests in Python, TypeScript and .NET decode status 9 as
StreamStatusUnspecified. A Python test checks thatStreamReadStart, which has no fallback, still raises. Rejection fixtures cover the refusals.