Skip to content
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

Open
wants to merge 45 commits into
base: main
Choose a base branch
from

Conversation

hculea
Copy link
Member

@hculea hculea commented Apr 30, 2024

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.

@hculea hculea marked this pull request as draft April 30, 2024 13:24
@kareid kareid requested a review from Lucretiel May 1, 2024 15:04
@hculea hculea marked this pull request as ready for review May 28, 2024 15:01
@Lucretiel
Copy link
Contributor

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).

@MOmarMiraj MOmarMiraj force-pushed the hculea/add-python-support-in-typeshare branch from f4540ab to 85634d4 Compare July 31, 2024 19:42
@MOmarMiraj MOmarMiraj force-pushed the hculea/add-python-support-in-typeshare branch from 85634d4 to 48a8adb Compare July 31, 2024 19:42
@amandayu1 amandayu1 force-pushed the hculea/add-python-support-in-typeshare branch from fbfde61 to 5c88f19 Compare August 22, 2024 18:16
@amandayu1 amandayu1 force-pushed the hculea/add-python-support-in-typeshare branch 3 times, most recently from c901b1b to 9935aaf Compare August 22, 2024 20:19
hculea and others added 10 commits October 1, 2024 10:39
* Add feature flag for Python TS

* Fix length of arrays

* Remove Available Languages and make it more scaleable

* make languages mutable
* init

* refactor functionality to its own method

* remove serde

* test signing

* YOUR_COMMIT_MESSAGE

* test signing

* YOUR_COMMIT_MESSAGE

* test signing

* test signing

* YOUR_COMMIT_MESSAGE

* YOUR_COMMIT_MESSAGE

* YOUR_COMMIT_MESSAGE

* YOUR_COMMIT_MESSAGE

* rewrite tests

* fix generation

* remove comma in types

* remove inner

* wip test

* setup test and fix tests

* fix

* enable and fix more tests

* add ctor for each variant class

* extract to method

* add support for ctor method for unit and tuple variants

* fix formatting

* apply omar's fixes

* Add more snapshot tests and remove unused function

* Add expected outputs for snapshot tests

* Change unit enums to pass as content and fix some unneeded imports

* fix config test

* Add newline for EOF

* Add snapshot test for serde default

* make code more readable

* fix single union, and rework to new pydantic standard

* fix unit tests

* remove useless tests

* remove empty unit class generation, remove unit variant from union

* update test

---------

Co-authored-by: Omar Miraj <omar.miraj@agilebits.com>
@MOmarMiraj
Copy link
Contributor

Hey @Lucretiel,

For the Python TS integration, we focused on incorporating Pydantic as our primary method for data validation and JSON serialization/deserialization.

Some reasons on why I chose pydantic over other famous packages are:

Active Support and Maintenance: The Pydantic package is well-supported and actively maintained, ensuring we can rely on its features and updates over time.

Robust Data Validation: Pydantic provides a strong data validation framework that allows us to define and enforce data types and structures in Python. This guarantees that the data types, number of fields, and overall structure align with our defined schema.

Seamless JSON Handling: The serialization and deserialization of JSON with Pydantic are incredibly smooth. It allows us to effortlessly convert our models to JSON while preserving their types and formatting, and it also validates incoming JSON data. This capability is something we currently utilize in our SDKs.(link to the SDKs doesn't work here lol)

Also, while newer Python versions are increasingly adopting advanced type hinting and validation features, Pydantic enables us to maintain compatibility with older Python versions that may not support these new features.

To serialize in pydantic is quite simple lets take a look below as an example:

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str
    email: str
    friends: Optional[List[str]] = []

In here we have a pydantic model that defines a couple of fields and pydantic will now serialize/deserialize according to this schema. We can serialize data in two ways, one by dumping it into a Dict or a str.

# We create our user
user = User(id=1, name='Alice', email='alice@example.com', friends=['Bob', 'Charlie'])

# Option 1 to serialize  as a str
user_json = user.model_dump_json()
print(user_json) -> {"id":1,"name":"Alice","email":"alice@example.com","friends":["Bob","Charlie"]}

# Option 2 to serialize as a dict
user_dict = user.model_dump()
print(user_dict) -> {'id': 1, 'name': 'Alice', 'email': 'alice@example.com', 'friends': ['Bob', 'Charlie']}

We can also add different arguments such as excluding fields or including only specific fields in the output and many more which can be found here:

# Exclude the 'friends' field
user_dict_excluded = user.model_dump(exclude={"friends"})
print(user_dict_excluded) -> {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'}

# Include only the 'name' and 'email' fields
user_dict_filtered = user.model_dump(include={"name", "email"})
print(user_dict_filtered) -> {'name': 'Alice', 'email': 'alice@example.com'}

For deserialization, we can deserialize according to the schema as follows:

user_json = {'id': 1, 'name': 'Jeff', 'email': 'jeff@jeff.com', 'friends': ['jeff', 'Charlie']}
let new_user = User.model_validate_json(user_json)

This will ensure that the JSON received matches against the schema and if not throws a pretty PydanticValidationError

bonus: Pydantic 2.x was written completely in Rust :)

@MOmarMiraj
Copy link
Contributor

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.

@hculea
Copy link
Member Author

hculea commented Nov 4, 2024

Thanks for the mention @MOmarMiraj 😄 That should be okay while Python support is still experimental (FWIW, Go doesn't support generics either)

@hculea
Copy link
Member Author

hculea commented Nov 4, 2024

@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!

@hculea hculea requested a review from Lucretiel November 4, 2024 10:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants