-
Notifications
You must be signed in to change notification settings - Fork 99
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
Add Typeshare support for Python #169
base: main
Are you sure you want to change the base?
Add Typeshare support for Python #169
Conversation
Thanks to the prior work of @adriangb: 1Password#25
core/Cargo.toml
Outdated
@@ -14,6 +14,9 @@ thiserror = "1" | |||
itertools = "0.12" | |||
lazy_format = "2" | |||
joinery = "2" | |||
topological-sort = { version = "0.2.2"} | |||
once_cell = { version = "1"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the built-in oncelock https://doc.rust-lang.org/std/sync/struct.OnceLock.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to not use the built-in type? https://doc.rust-lang.org/stable/std/sync/struct.LazyLock.html
Doing some review, sorry for the delay. I'd like to ask for a high-level description in this MR for how this integration works, with especial focus on how structs are handled (in particular how the JSON <-> struct boundary is handled). |
f4540ab
to
85634d4
Compare
85634d4
to
48a8adb
Compare
fbfde61
to
5c88f19
Compare
c901b1b
to
9935aaf
Compare
Forgot to mention but we currently do not support generics as we have yet to figure out on an ideal/idiomatic way to handle them in Python so we left it out of this PR. |
Thanks for the mention @MOmarMiraj 😄 That should be okay while Python support is still experimental (FWIW, Go doesn't support generics either) |
@Lucretiel does @MOmarMiraj 's comment above answer to your high-level description request? Otherwise, we're happy to provide more context on the choices that were made here. Thanks! |
…culea/typeshare into hculea/add-python-support-in-typeshare
Fix tuple variant generation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly this looks good. I had some concerns about algebraic enum representations, along with a few edge cases (mostly around serde renames) that appear to be handled incorrectly. Rust side of this looks fine, if quite allocation-heavy; I'll leave it be for now but there's a lot of low-hanging fruit for improvement if typeshare-python needs to be faster.
core/Cargo.toml
Outdated
@@ -14,6 +14,9 @@ thiserror = "1" | |||
itertools = "0.12" | |||
lazy_format = "2" | |||
joinery = "2" | |||
topological-sort = { version = "0.2.2"} | |||
once_cell = { version = "1"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to not use the built-in type? https://doc.rust-lang.org/stable/std/sync/struct.LazyLock.html
core/data/tests/test_algebraic_enum_case_name_support/output.py
Outdated
Show resolved
Hide resolved
|
||
|
||
class MyType(BaseModel): | ||
field: str |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider constr
to constrain the length here, since it's known to be a char
https://docs.pydantic.dev/1.10/usage/types/#arguments-to-constr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be using Annotated with Field instead.
OptionalU16 = int | ||
|
||
|
||
OptionalU32 = int | ||
|
||
|
||
class FooBar(BaseModel): | ||
foo: OptionalU32 | ||
bar: OptionalU16 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very wrong to me– the relevant rust type is Option<u32>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
""" | ||
This is a comment. | ||
""" | ||
a: int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I don't feel nearly as strongly about this as I do about by other feedback, but if you wanted to be fancy, you could use conint
to handle fixed-width integers with a min and max value: https://docs.pydantic.dev/1.10/usage/types/#arguments-to-conint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be using Annotated
with Field
instead, like so: Annotated[int, Field(strict=True, gt=0)]
// used by each individual field | ||
// We do this by exploring each field's type and comparing against the generics used by the enum | ||
// itself | ||
fn collect_generics_for_variant(variant_type: &RustType, generics: &[String]) -> Vec<String> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: consider using a wrapper that pushes to an &mut Vec<String>
here, to avoid the deep recursive frequent allocations and data copying between vectors. Also consider returning a HashSet
or BTreeSet
from this function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is unused from the original contributor. This PR won't cover generics so I'm keeping the function around for future references.
I will add a comment a mark the as unused.
Follow-up: it looks like most of the pydantic |
As a subsequent follow up, one idea for a way to handle unions would resemble this:
I'm still a novice around pydantic, so there could certainly be a better way that I'm not aware of. For all I know this pattern is fully built-in to pydantic somewhere and I just don't know where. |
After some experimentation and discussion with @MOmarMiraj, I think this is the best approach for externally tagged enums. I've refactored the PR to do so. |
KEBAB_CASE = "kebabCase" | ||
|
||
class AnonymousStructWithRenameList(BaseModel): | ||
type: AnonymousStructWithRenameTypes = AnonymousStructWithRenameTypes.LIST |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible I'm misunderstanding pydantic, but shouldn't this be a Literal
parameter? I had understood this =
to just set a default value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
This PR is adding initial support for Python in Typeshare (gated by a feature flag, like Go).
See @MOmarMiraj 's comment for a high-level description of the design considerations behind this work.
This was made possible thanks to the prior work of @adriangb: #25.