Skip to main content

Pony stubs

Python type hint stubs for Pony ORM

Goals

  1. Provide type hints for Pony ORM that support both MyPy and Pyright on their strictest modes
  2. Integrate the contents of this package into the official Pony ORM repository (self-deprecation)
  3. Focus primarily on the aspects that users of Pony ORM most often run into (defining models, querying them)

Usage

Install the package with your preferred Python dependency manager:

pip install pony-stubs

pipenv install pony-stubs

poetry add -D pony-stubs

Then define your models:

# We need this to avoid `TypeError: 'type' object is not subscriptable`
# later on when forward declaring relation types with strings
from __future__ import annotations

from pony.orm import (
    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.Entity


class Customer(DbEntity):  # type: ignore
    # If we want the type checkers to know about the autogenerated ID
    # field, we need to annotate it
    id: int
    # Otherwise, using `Required` allows `Customer.email` to be inferred
    # as `str` later on
    email = 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 correctly
    orders: Set["Order"] = Set("Order")

class Order(DbEntity):  # type: ignore
    # We can also declare the primary key with Pony constructors and
    # infer the type that way
    id = 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 annotations
    customer = Required(Customer)

class Product(DbEntity):  # type: ignore
    id: int
    name = 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 inferred
result = select(c for c in Customer if c.country != "USA")[:]
result = select(c for c in Customer if count(c.orders) > 1)[:]

# Here result infers as `Decimal`
result = max(p.price for p in Product)

# 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(c for c in Customer)
    .order_by(lambda c: desc(sum(c.orders.total_price)))
    .first()
)

Limitations

  1. We need misdirection with db.Entity for pyright, and # type: ignore for mypy
  2. When forward declaring relations (relation to a model defined later in the file) we an additional manual annotation (field: Set["RelatedObject"])
  3. "Attribute lifting" of related fields is typed as Any. Pony would allow us to access attributes of Set[Order] as though it's type was Order, 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)
  4. Query.where() (docs) loses type information and it's results are typed as Any, as python doesn't keep track of generator expressions' initial iterables: (o.customer for o in Order) is inferred as Generator[Customer, None, None], without any native way of storing the Order type in a generic for inferring.

Contributing

Contributions 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 possible

The development environment for this package requires poetry (https://python-poetry.org/docs/master/#installing-with-the-official-installer)

Using VSCode as the editor is recommended!

Setting up the repo

  1. Clone the repo
    • git clone git@github.com:Jonesus/pony-stubs.git
  2. Install dependencies
    • poetry install
  3. Install commit hooks
    • poetry run pre-commit install --install-hooks
  4. Type ahead!

License

This project is licensed under the MIT license (see LICENSE.md)

Metadata

Release files for pony-stubs 0.5.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pony-stubs 0.5.2
File Size Uploaded
pony-stubs-0.5.2.tar.gz 28.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pony-stubs 0.5.2
File Interpreter ABI Platform
pony_stubs-0.5.2-py3-none-any.whl Python 3 none any Details

Total release size: 64.3 kB

Release files / pony-stubs-0.5.2.tar.gz

Download URL pony-stubs-0.5.2.tar.gz
Size 28.5 kB
Tags Source
SHA-256 checksum
How to use checksums
65a2739d4c6848eb00e5d8c74c8f25a92f960809b011e3138694665f8a4a5adc
BLAKE2b-256 checksum
How to use checksums
db1607daf8f5c69018663f9e7679371d471aee35627f70a5b591200cf5389408
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.13 CPython/3.10.4 Linux/5.13.0-1021-azure

Release files / pony_stubs-0.5.2-py3-none-any.whl

Download URL pony_stubs-0.5.2-py3-none-any.whl
Size 35.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c446481b1e6699676e8fc5c22b59429c18e00d1e663990cd98dae843acaf395a
BLAKE2b-256 checksum
How to use checksums
5273e04a44e996636053dddbd56fbd3e2138db68b9d4adba73819a561f979bdc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.1.13 CPython/3.10.4 Linux/5.13.0-1021-azure

Release history Release notifications | RSS feed

This release

0.5.2 This release

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page