From dc556b10134d919ce740edce90b14e753dd2585d Mon Sep 17 00:00:00 2001 From: kurt spindler Date: Sat, 2 Feb 2019 10:14:27 -0800 Subject: [PATCH 1/2] add doc on how to define custom python type for column type inference --- docs/Inferring Custom Pythons Types.md | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/Inferring Custom Pythons Types.md diff --git a/docs/Inferring Custom Pythons Types.md b/docs/Inferring Custom Pythons Types.md new file mode 100644 index 0000000..b49dbd0 --- /dev/null +++ b/docs/Inferring Custom Pythons Types.md @@ -0,0 +1,35 @@ +`sqlalchemy-stubs` is able to infer the python type based on the column definition for SQLAlchemy models. +Thus, + +```python +class User(Base): + id = Column(Integer, primary_key=True) +``` + +allows `User().id` to have inferred type `int`. It can be useful to have custom Python types to strengthen type-checking guarantees, +especially for fields such as uuids, emails, phone numbers, transaction tokens, and more. To achieve this, do the following: + +```python +from sqlalchemy.dialects.postgresql import UUID # import your desired column type +from typing import NewType +from uuid import uuid4 as gen_uuid + +Uuid = NewType('Uuid', str) # define the custom Python type to use for typechecking + + +# define a new column field, giving it a type-decorator for your type, and inheriting from the column type. Order is important here. +class PgUuid(UUID): # type: PgUuid(sqltypes.TypeDecorator[Uuid], UUID) + pass + + +def uuid4(): + return str(gen_uuid()) + + +# Now define a model using the column. +class User(Base): + __tablename__ = 'users' + uuid = Column(PgUuid(), primary_key=True, default=uuid4) +``` + +With this class, `User().uuid` will now typecheck to `Uuid`. From 61b560793e53ece9bfb09340c82f025fe7d350ed Mon Sep 17 00:00:00 2001 From: kurt spindler Date: Sun, 3 Feb 2019 08:49:03 -0800 Subject: [PATCH 2/2] update --- docs/Inferring Custom Pythons Types.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/Inferring Custom Pythons Types.md b/docs/Inferring Custom Pythons Types.md index b49dbd0..3d35d07 100644 --- a/docs/Inferring Custom Pythons Types.md +++ b/docs/Inferring Custom Pythons Types.md @@ -11,15 +11,25 @@ especially for fields such as uuids, emails, phone numbers, transaction tokens, ```python from sqlalchemy.dialects.postgresql import UUID # import your desired column type -from typing import NewType +from sqlalchemy.sql import sqltypes +from typing import NewType, TYPE_CHECKING from uuid import uuid4 as gen_uuid Uuid = NewType('Uuid', str) # define the custom Python type to use for typechecking + +# use this type checking idiom bcause TypeDecorator doesn't support __getitem__ +# see [this mypy doc](https://mypy.readthedocs.io/en/latest/common_issues.html#using-classes-that-are-generic-in-stubs-but-not-at-runtime) for full details. +if TYPE_CHECKING: + BaseUuid = sqltypes.TypeDecorator[Uuid] +else: + BaseUuid = sqltypes.TypeDecorator + + # define a new column field, giving it a type-decorator for your type, and inheriting from the column type. Order is important here. -class PgUuid(UUID): # type: PgUuid(sqltypes.TypeDecorator[Uuid], UUID) - pass +class PgUuid(BaseUuid): + impl = Uuid def uuid4():