Package: pony-stubs
Pony stubsPython type hint stubs forPony ORMGoalsProvide type hints for Pony ORM that support both MyPy and Pyright on their strictest modesIntegrate the contents of this package into the official Pony ORM repository (self-deprecation)Focus primarily on the aspects that users of Pony ORM most often run into (defining models, querying them)UsageInstall the package with your preferred Python dependency manager:pipinstallpony-stubs

pipenvinstallpony-stubs

poetryadd-Dpony-stubsThen define your models:# We need this to avoid `TypeError: 'type' object is not subscriptable`# later on when forward declaring relation types with stringsfrom__future__importannotationsfrompony.ormimport(Database,Required,Optional,Set,PrimaryKey,select,max,count,desc)db=Database("sqlite","store.sqlite",create_db=True)# Using `db.Entity` directly won't work, as both MyPy and Pyright won't# allow inheriting a class from a variable. For Pyright this declaration# is enough misdirection for it not to complain, but MyPy needs an extra# `type: ignore` comment above each model declaration to work.DbEntity=db.EntityclassCustomer(DbEntity):# type: ignore# If we want the type checkers to know about the autogenerated ID# field, we need to annotate itid:int# Otherwise, using `Required` allows `Customer.email` to be inferred# as `str` later onemail=Required(str,unique=True)password=Required(str)country=Required(str)# Using `Optional` marks the field attribute as `str | None`address=Optional(str)# When we forward declare a relation by using the class name as a# string, we also need to manually annotate the field so that the# type checkers can infer it correctlyorders:Set["Order"]=Set("Order")classOrder(DbEntity):# type: ignore# We can also declare the primary key with Pony constructors and# infer the type that wayid=PrimaryKey(int,auto=True)state=Required(str)total_price=Required(Decimal)# When declaring relationships after a class has been declared,# there's no need for annotationscustomer=Required(Customer)classProduct(DbEntity):# type: ignoreid:intname=Required(str)price=Required(Decimal)And use them in your code:# Here result infers as `QueryResult[Customer]` and all fields in the# generator expression inside `select` have proper types inferredresult=select(cforcinCustomerifc.country!="USA")[:]result=select(cforcinCustomerifcount(c.orders)>1)[:]# Here result infers as `Decimal`result=max(p.priceforpinProduct)# And here as `Customer | None` (as `.first()` might not find an object)# Here is also the first time we can't properly infer something:# `c.orders` is declared as `Set[Order]`, but Pony allows us to access# `c.orders.total_price` as though it was typed as a plain `Order`.# As Python doesn't yet support type intersections, we have yet to come# up with no choice other than to type each extra field of a `Set` as# `Any`result=(select(cforcinCustomer).order_by(lambdac:desc(sum(c.orders.total_price))).first())LimitationsWe need misdirection withdb.Entityforpyright, and# type: ignoreformypyWhen forward declaring relations (relation to a model defined later in the file) we an additional manual annotation (field: Set["RelatedObject"])"Attribute lifting" of related fields is typed asAny. Pony would allow us to access attributes ofSet[Order]as though it's type wasOrder, but python doesn't yet support type intersections so statically typing this seems to be impossible without a plugin (which would only fix the issue for MyPy but not Pyright)Query.where()(docs) loses type information and it's results are typed asAny, as python doesn't keep track of generator expressions' initial iterables:(o.customer for o in Order)is inferred asGenerator[Customer, None, None], without any native way of storing theOrdertype in a generic for inferring.ContributingContributions are always most welcome! Please run the pre-commit hooks before setting up a pull request, and in case the Github actions fail, please try to fix those issues so the review itself can go as smoothly as possibleThe development environment for this package requirespoetry(https://python-poetry.org/docs/master/#installing-with-the-official-installer)Using VSCode as the editor is recommended!Setting up the repoClone the repogit clone git@github.com:Jonesus/pony-stubs.gitInstall dependenciespoetry installInstall commit hookspoetry run pre-commit install --install-hooksType ahead!LicenseThis project is licensed under the MIT license (see LICENSE.md)