Skip to content

Conversation

@o-santi
Copy link

@o-santi o-santi commented Nov 14, 2025

What is the current behavior?

Adds python type generation support, piggy backing off of @ryanpeach's #808 pull request. The main difference between his approach is that I added a lot more structure by first creating the "AST", then serializing it through the serialize() method, instead of in place constructing strings everywhere.

Fixes #795.

Additional context

The idea is that the normal Public{table} class should be used to parse the result of select queries, while the Insert and Update objects should be passed directly into the .insert and .update postgrest methods. As such, they needed to be TypedDicts instead of BaseModels, for that's what the postgrest library expects in those functions.

This may change in the future, specially in a v3. Ideally, I'd love to be able to infer the type of the arguments for the function from the query builder itself, like TS already does, but I'm afraid that python's type checker is not strong enough to do that. I think a frankenstein can be stitched together from Literals and @overloads but not sure how scalable and user friendly that would be. I will certainly research more about it before doing a v3 rewrite. For now, this approach suffices.

@coveralls
Copy link

coveralls commented Nov 14, 2025

Pull Request Test Coverage Report for Build 19441158062

Details

  • 336 of 346 (97.11%) changed or added relevant lines in 4 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.7%) to 83.632%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/server/server.ts 0 3 0.0%
src/server/templates/python.ts 313 316 99.05%
src/server/routes/generators/python.ts 21 25 84.0%
Totals Coverage Status
Change from base Build 19279040591: 0.7%
Covered Lines: 6260
Relevant Lines: 7346

💛 - Coveralls

Copy link
Member

@avallete avallete left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. In terms of release, we could make a canary release from this PR, setup some client-side tests (even if basics just to ensure no regressions and working types e2e) on the supabase-py repo.

(A bit like it's done for the TS types here: https://github.com/supabase/supabase-js/blob/master/packages/core/postgrest-js/test/resource-embedding.test.ts#L133-L152)

Idea would be to use the canary image to generate types for a test database, and have some basics runtime + types testing to see if it work as expected before getting this merged to production. WDYT ?

Comment on lines 20 to 30
const py_tables = tables
.filter((table) => schemas.some((schema) => schema.name === table.schema))
.flatMap((table) => {
const py_class_and_methods = ctx.tableToClass(table)
return py_class_and_methods
})
const composite_types = types
.filter((type) => type.attributes.length > 0)
.map((type) => ctx.typeToClass(type))
const py_views = views.map((view) => ctx.viewToClass(view))
const py_matviews = materializedViews.map((matview) => ctx.matViewToClass(matview))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick

Tables on schemas that are excluded should also be excluded by introspection (not sure the filter is in need there).

If it isn't then in such case I guess you also want to apply the same filter for views and matviews ? (if they're declared on a schema not in the list then don't generate types for them).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@avallete can you elaborate more on this because the typescript version of this code seems to output every schema. I think this should be fixed in the typescript version first as PostgREST doesn't give access to non-exposed schemas like auth, storage, extensions, net by default and these should never be accessed through PostgREST which is what these types are generated for.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct.

Copy link
Member

@avallete avallete Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@avallete can you elaborate more on this because the typescript version of this code seems to output every schema.

Sorry wasn't clear.

It does, and the schemas included/excluded will be filtered at the introspection level (getGeneratorMetadata).

My question was about why there is this check:

  const py_tables = tables
    .filter((table) => schemas.some((schema) => schema.name === table.schema))

Maybe I've misunderstood but it seems like this is supposed to ensure that the table.schema is matching one of the included value in the included schemas. But this should already have been taken cares of at the introspection level (if a table is not within an included schema it won't be introspected / present in the list).

@silentworks
Copy link

After a small modification to the formatForPyClassName function, these are the pydantic models generated for my schemas that can be found here https://github.com/silentworks/flask-notes/tree/main/supabase/migrations. This is looking good but it currently doesn't generate table names starting with _ correctly, but I don't think this is much of an issue as developers shouldn't be naming table beginning with _ in the first place and adding a disclaimer about this should be enough.

model.py

@silentworks
Copy link

@o-santi please remember to add the necessary changes in the package.json scripts section and also update server.ts with the python template function you created here.

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.

Generating types in python format

5 participants