sqlalchemy2-stubs
sqlalchemy2-stubs copied to clipboard
Column wrapping functions not handled correctly
Describe the bug
If columns are created with functions wrapping sqlalchemy.Column (e.g. NotNullColumn = functools.partial(sqlalchemy.Column, nullable=False)) they are not recognised by the type checker in places such as initialisation.
Expected behavior
A function wrapping the creation of a sqlalchemy.Column object, should be treated the same by the type checker as the constructor of sqlalchemy.Column.
To Reproduce
from sqlalchemy import Column, Integer, orm
Base = orm.declarative_base()
def MyColumn(*args, **kwargs) -> Column:
return Column(*args, **kwargs)
class Foo(Base):
bar = MyColumn(Integer)
foo = Foo(bar=1)
Error
example.py:11: error: Unexpected keyword argument "bar" for "Foo"
Found 1 error in 1 file (checked 1 source file)
Versions.
- OS: MacOS
- Python: 3.8.2
- SQLAlchemy: 1.4.2
- Database: NA
- DBAPI: NA
hey there -
i can try to improve this and perhaps have your Column considered to be reutrning "Any", but I can't derive that this Column is against Integer because your function doesn't indicate this:
from sqlalchemy import Column, Integer, orm
def MyColumn(*args, **kwargs) -> Column:
return Column(*args, **kwargs)
x = MyColumn(Integer)
$ mypy test3.py --strict
test3.py:3: error: Function is missing a type annotation for one or more arguments
test3.py:3: error: Missing type parameters for generic type "Column"
I'm pretty backlogged now but will try to get it to work at least as:
class Foo(Base):
bar: int = MyColumn(Integer)
ah okay, I see the issue now. Would there be a way to annotate the wrapper function with a generic type? something like def MyColumn(col_type: T, *args, **kwargs) -> Column[T]:?
PS: Thanks so much for your work on this great project 💯 this isn't a serious issue for me atm, so I'm happy to wait a while for this to be addressed
Yes I think ideally your function would have to use a generic like that, then I could pick up the "T" part.
however great news! there's a workaround you can use for now, which is the _mypy_mapped_attrs attribute, this passes the SQLAlchemy part of this (the --strict thing above is still separate):
class Foo(Base):
bar: int = MyColumn(Integer)
_mypy_mapped_attrs = [bar]
foo = Foo(bar=1)
Another solution is to do
class MyInteger(Integer):
pass
class Foo(Base):
bar: int = Column(MyInteger())
And for isinstance checks you can use isinstance(col.type, MyInteger)