Coder Social home page Coder Social logo

sqlalchemy2-stubs's Introduction

SQLAlchemy 2 Stubs (Legacy)

These are interim PEP-484 typing stubs for the SQLAlchemy 1.4 release series only. They are released concurrently along with a Mypy extension which is designed to work with these stubs, which assists primarily in the area of ORM mappings.

The stubs replace the use of the "sqlalchemy-stubs" package published by Dropbox. Differences include that these stubs are generated against 1.4's API as well as some adjustments to the use of generics. The sqlalchemy2-stubs stubs package will not work with SQLAlchemy 2.0, and will need to be explicitly / manually uninstalled when upgrading to SQLAlchemy 2.0.

This project should be considered alpha level and is not as mature as the Dropbox stubs for the initial release.

See the Mypy plugin documentation at https://docs.sqlalchemy.org/en/14/orm/extensions/mypy.html for an overview of how to use PEP-484 annotations with ORM mapped classes.

For SQLAlchemy 2.0, when released, the sqlalchemy2-stubs project is not compatible. SQLAlchemy 2.0 will be / is fully pep-484 typed inline and will not be compatible with any stubs. There unfortunately appears to be no standard approach in Python for the problem of a library that upgrades to include typing annotations inline, as typing tools will still consider any installed stubs to take precedence. Therefore sqlalchemy2-stubs will need to be manually uninstalled when upgrading to SQLAlchemy 2.0.

Code of Conduct

Above all, SQLAlchemy places great emphasis on polite, thoughtful, and constructive communication between users and developers. Please see our current Code of Conduct at Code of Conduct.

License

SQLAlchemy is distributed under the MIT license.

sqlalchemy2-stubs's People

Contributors

amisadmin avatar andersk avatar blacklionz avatar bryanforbes avatar caselit avatar fasterspeeding avatar heckad avatar huwcbjones avatar hydrargyrum avatar iamnoah avatar iyanging avatar izeberg avatar joshvoigts avatar jwilges avatar kasium avatar layday avatar maicotimmerman avatar mehdigmira avatar mjpieters avatar morikko avatar mrcljx avatar parnassius avatar peteris-zealid avatar peterschutt avatar rouge8 avatar stefano-maggiolo avatar tapetersen avatar tasn avatar tilmank avatar zzzeek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sqlalchemy2-stubs's Issues

Select mising return values for `@_generative` methods

Describe the bug
When using methods of Select decorated with @_generative, type checkers will assume they return None.
This is expected behavior as they are annotated as returning None, but in fact they don't.

Expected behavior
The annotations should represent the actual behavior, ie returning a Select instance.
Compare to the dropbox/sqlalchemy-stubs annotations for Select.

To Reproduce
Register the plugin with mypy and run it against the following code:

from sqlalchemy.sql.selectable import Select

def bar(stmt: Select):
    return stmt.offset(5).limit(5)

Error

test.py:4: error: "offset" of "GenerativeSelect" does not return a value

Versions.

  • OS: Ubuntu 20.04.2 LTS
  • Python: 3.7.9
  • SQLAlchemy: 1.4.3
  • Database: -
  • DBAPI: -

relationship's order_by doesn't allow multiple clauses

Describe the bug

The typing of relationship's order_by parameter doesn't allow passing multiple 'order by' clauses.

Expected behavior

The typing should reflect the runtime behaviour, which allows passing multiple clauses to order the relationship.

I noticed other order_by parameters/functions seems to just take Any as the clauses, but potentially all could be updated to something like:

Union[Literal[False], str, Column, Callable[[], Column], List[Union[str, Column]]]

(with or without the Literal[False], as appropriate.)

To Reproduce

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship, declarative_base
from typing import List, Optional

Base = declarative_base()


class Foo(Base):
    __tablename__ = "foo"
    id = Column(Integer(), primary_key=True)
    bar_id: Optional[int] = Column(ForeignKey("bar.id"))

    a = Column(Integer())
    b = Column(Integer())


class Bar(Base):
    __tablename__ = "bar"
    id = Column(Integer(), primary_key=True)

    foos: List[Foo] = relationship(Foo, uselist=True, order_by=[Foo.a, Foo.b])

Error

order_by.py:21: error: Argument "order_by" to "RelationshipProperty" has incompatible type "List[Mapped[Optional[int]]]"; expected "Union[Literal[False], str, Column[Any], Callable[[], Column[Any]]]"
Found 1 error in 1 file (checked 1 source file)

Versions.

  • OS: n/a
  • Python: 3.8.7
  • SQLAlchemy: see requirements below
  • Database: n/a
  • DBAPI: n/a

Additional context

requirements:

greenlet==1.0.0
mypy==0.812
mypy-extensions==0.4.3
SQLAlchemy==1.4.11
sqlalchemy2-stubs==0.0.1a4
typed-ast==1.4.3
typing-extensions==3.7.4.3

Unexpected keyword argument with python property

Describe the bug

When use @property and pass value in __init__, mypy show error: Unexpedted keyword argument "<property>" for "<entity>"

To Reproduce

from hashlib import md5

import sqlalchemy as sa
from sqlalchemy import orm

Base = orm.declarative_base()


class User(Base):
    __tablename__ = 'user'

    id = sa.Column(sa.Integer, primary_key=True)
    nickname = sa.Column(sa.Unicode(20), nullable=False)
    pwd_hash = sa.Column(sa.Unicode(200))

    @property
    def password(self):
        raise AttributeError('Cannot read password.')

    @password.setter
    def password(self, value: str):
        self.pwd_hash = md5(value.encode('utf-8')).hexdigest()


if __name__ == '__main__':
    user = User(nickname='kenny', password='Aa1234')  # <-- mypy: Unexpected keyword argument "password" for "User"

Error

app\__init__.py:25: error: Unexpected keyword argument "password" for "User"

Versions.

  • OS: Windows 10 / Debian 10
  • Python: 3.9.1
  • SQLAlchemy: 1.4.15
  • mypy: 0.812
  • SQLAlchemy2-stubs: 0.0.2a1

Have a nice day!

Missing `metadata` attribute on `Table` objects

Describe the bug
It appears that the stubs are missing the metadata attribute on Table objects. When running mypy, I get error: "Table" has no attribute "metadata"; maybe "tometadata" or "to_metadata"? [attr-defined].

Expected behavior
The metadata field on Table objects should be defined, most likely as Optional[MetaData].

To Reproduce

from sqlalchemy import MetaData, Table, Column, String

meta = MetaData()
table = Table('table', meta, Column('column', String))

assert table.metadata is meta

Error

`error: "Table" has no attribute "metadata"; maybe "tometadata" or "to_metadata"?  [attr-defined]`.

Versions.

  • OS: Ubuntu 20.10
  • Python: 3.8.6
  • SQLAlchemy: 1.4.7
  • Database: N/A
  • DBAPI: N/A
  • SQLAlchemy2-stubs: 0.0.1a4
  • mypy: 0.812

Column is incompatible with UserDefinedType

Describe the bug
Columns with a UserDefinedType raise an error

error: Value of type variable "_TE" of "Column" cannot be <UserDefinedType>

Expected behavior
UserDefinedTypes are handled like builtin types.

To Reproduce

from typing import Callable

from sqlalchemy import Column, Integer, MetaData
import sqlalchemy.types as types
from sqlalchemy.dialects.postgresql.base import PGDialect
from sqlalchemy.orm import declarative_base, Mapped

#########
# SETUP #
#########

class CITEXT(types.Concatenable, types.UserDefinedType):
    """Case Insensitive Text Type"""

    def literal_processor(self, dialect: PGDialect) -> Callable:
        def process(value: str) -> str:
            value = value.replace("'", "''")

            if dialect.identifier_preparer._double_percents:
                value = value.replace("%", "%%")

            return "'%s'" % value
        return process

    def get_col_spec(self) -> str:
        return "CITEXT"

    def bind_processor(self, dialect: PGDialect) -> Callable:
        def process(value: str) -> str:
            return value
        return process

    def result_processor(self, dialect: PGDialect, coltype: int) -> Callable:
        def process(value: str) -> str:
            return value
        return process

#########
# USAGE #
#########

Base = declarative_base(metadata=MetaData())

class User(Base):
    id = Column(Integer(), primary_key=True)
    email: Mapped[str] = Column(CITEXT) # error: Value of type variable "_TE" of "Column" cannot be "CITEXT" 

Error

# error: Value of type variable "_TE" of "Column" cannot be "CITEXT"

Versions.

  • OS: macOS 10.15.4
  • Python: 3.8.2
  • SQLAlchemy: 1.4.6
  • Database: all

Context

This appears to be happening because Column expects its type to be a subclass of TypeEngine

_TE = TypeVar("_TE", bound=TypeEngine[Any])

but the stub for UserDefinedType is not

In contrast, the definition in sqlalchemy proper is

class UserDefinedType(util.with_metaclass(VisitableCheckKWArg, TypeEngine)):

If its as simple as adding TypeEngine as a base I'd be happy to open a PR, but I'm guessing something to do with the metaclasses is a problem for mypy?

__version__ is missing in __init__.pyi

Describe the bug
Mypy tells me that __version__ doesn't exist for sqlalchemy, but it's just missing in the __init__.pyi

Expected behavior
__version__ should be defined

To Reproduce

import sqlalchemy

print(sqlalchemy.__version__)

Versions.

  • OS: Linux
  • Python: 3.7.1
  • SQLAlchemy: 1.4.18
  • mypy: 0.902
  • SQLAlchemy2-stubs: 0.0.2a2

scoped_session __call__ returns Any

Describe the bug
Calling scoped_session returns Any which is should not be the case

Expected behavior
A concrete type should be returned

To Reproduce

session_factory = sessionmaker(bind=create_engine("foo", future=True))
reveal_type(scoped_session(session_factory)())

Error

Revealed type is "Any"

Versions.

  • OS: Linux
  • Python: 3.7.1
  • SQLAlchemy: 1.4.22
  • mypy: 0.910
  • SQLAlchemy2-stubs: 0.0.2a6

Wrong Session.no_autoflush return type annotation

Describe the bug

The Session.no_autoflush property is annotated as returning None.

Expected behavior

The Session.no_autoflush property should be annotated as returning a context manager that yields a Session.

To Reproduce

# no_autoflush.py

from sqlalchemy.orm import Session

def test(session: Session):
    with session.no_autoflush:
        pass

Error

$ mypy no_autoflush.py 
no_autoflush.py:5: error: "None" has no attribute "__enter__"
no_autoflush.py:5: error: "None" has no attribute "__exit__"
Found 2 errors in 1 file (checked 1 source file)

Versions

  • OS: Debian 10
  • Python: 3.8
  • SQLAlchemy: 1.4.4
  • Database: N/A
  • DBAPI: N/A

Mypy crashes with AssertionError in sqlalchemy/ext/mypy/util.py

Describe the bug
Mypy outputs "Module service.models.Person is not valid as a type" and then crashes with the following traceback:

version: 0.902
Traceback (most recent call last):
  File "mypy/semanal.py", line 4872, in accept
  File "mypy/nodes.py", line 950, in accept
  File "mypy/semanal.py", line 1056, in visit_class_def
  File "mypy/semanal.py", line 1134, in analyze_class
  File "mypy/semanal.py", line 1143, in analyze_class_body_common
  File "mypy/semanal.py", line 1203, in apply_class_plugin_hooks
  File "/opt/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/plugin.py", line 199, in _base_cls_hook
    decl_class._scan_declarative_assignments_and_apply_types(ctx.cls, ctx.api)
  File "/opt/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/decl_class.py", line 90, in _scan_declarative_assignments_and_apply_types
    _scan_for_mapped_bases(cls, api, cls_metadata)
  File "/opt/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/decl_class.py", line 476, in _scan_for_mapped_bases
    _scan_declarative_assignments_and_apply_types(
  File "/opt/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/decl_class.py", line 90, in _scan_declarative_assignments_and_apply_types
    _scan_for_mapped_bases(cls, api, cls_metadata)
  File "/opt/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/decl_class.py", line 476, in _scan_for_mapped_bases
    _scan_declarative_assignments_and_apply_types(
  File "/opt/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/decl_class.py", line 51, in _scan_declarative_assignments_and_apply_types
    info = util._info_for_cls(cls, api)
  File "/opt/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/util.py", line 211, in _info_for_cls
assert sym and isinstance(sym.node, TypeInfo)
AssertionError: 

sym seems to be None. Also, Person is a class, not a module. It is imported in init.py from person.py.
I am using from __future__ import annotations to avoid circular import issues when typing relationships in declarative models (they are in different files and reference each other).

Expected behavior
To not crash and to know that Person is a class.

To Reproduce
I am not sure how to reproduce it. It seems to happen only to one out of several similar models.

Versions.

  • OS: Linux Docker
  • Python: 3.8.10
  • SQLAlchemy: sqlalchemy[mypy]==1.4.18
  • mypy: 0.902

Have a nice day!

How to type Array of Enum?

I cannot find the right typing for Array of Enums. It is also doesn't work with Array of Strings, so I haven't any workaround :(

Example

import sqlalchemy as sa
import enum
from typing import List
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class TokenType(str, enum.Enum):
    a = "a"

class Model(Base):
    token_types: List[TokenType] = sa.Column(
        sa.ARRAY(sa.Enum(TokenType)),
        nullable=False,
    )

Complete stack trace, if applicable

app/models.py:305: error: [SQLAlchemy Mypy plugin] Left hand assignment
'token_types: "List[TokenType]"' not compatible with ORM mapped expression of
type "Mapped[List[_TE]]"  [misc]
        token_types: List[TokenType] = sa.Column(

Versions

  • OS: Big Sure 11.4
  • Python: 3.8
  • SQLAlchemy: 1.4.20
  • mypy: 0.902
  • SQLAlchemy2-stubs: 0.0.2a4

filter and options methods of Query object aren't correctly detected

Good Morning,

I was hoping my issue would be solved by #39 , sadly it isn't: you might want to have another look.

Describe the bug
Create a query using filter or options.

You will get
error: "filter" of "Query" does not return a value
or
error: "options" of "Query" does not return a value

Expected behavior
No mypy message.

To Reproduce
Not a complete example, but I guess it's clear:

session.query(User).filter(User.name == "John").options(joinedload(User.address)).all()

Versions.

  • OS:
  • Python: 3.8.6
  • SQLAlchemy: 1.4.9
  • Database:
  • DBAPI:

Have a nice day!
U2 :D

`AsyncEngine.begin()` returns `_trans_ctx` instead of expected `AsyncTransaction`

AsyncConnection.begin() returns AsyncTransaction, like one would expect:

def begin(self) -> AsyncTransaction: ...

however, AsyncEngine.begin() returns _trans_ctx:

def begin(self) -> _trans_ctx: ...

This leads to following errors, when user declares argument type to AsyncTransaction.

error: Argument "bar" to "foo" has incompatible type "_trans_ctx"; expected "AsyncTransaction"

Improve session.query typings

Currently the session.query returns Query[Any] in all cases.

Some overloads could probably be added:

def query(self, model: T, ...) -> Query[T]: ...
def query(self, model: Union[TypeEngins, Type[TypeEngine]], ...) -> Query[Any]: ...
def query(self, model: T, model2: T2) -> Query[Tuple[T, T2]] # not sure it's actually correct
def query(self, thing: TableClause)->Query[tuple]

I'm not sure if these above are possible with python, or 100% correct

AsyncEngine._trans_ctx typed as returning self on aenter rather than AsyncConn

Describe the bug
AsyncEngine._trans_ctx is typed as returning itself on __aenter__.

Expected behavior
AsyncEngine._trans_ctx should be typed as returning AsyncConnection on __aenter__.

To Reproduce
Please try to provide a Minimal, Complete, and Verifiable example.
See also Reporting Bugs on the website, and some example issues.

Please do not use Flask-SQLAlchemy or any other third-party extensions or dependencies in test cases. The test case must illustrate the problem without using any third party SQLAlchemy extensions. Otherwise, please report the bug to those projects first.

from sqlalchemy.ext import asyncio


async def test() -> None:
    database = asyncio.create_async_engine("", future=True)

    trans_ctx = database.begin()
    reveal_type(trans_ctx)
    async with trans_ctx as connection:
        await connection.execute(...)

Error

PS C:\Users\Lucina\PycharmProjects\PTF> mypy test1.py
test1.py: note: In function "test":
test1.py:8:17: note: Revealed type is 'sqlalchemy.ext.asyncio.engine.AsyncEngine._trans_ctx'
test1.py:10:15: error: "_trans_ctx" has no attribute "execute"  [attr-defined]
            await connection.execute(...)
                  ^
Found 1 error in 1 file (checked 1 source file)

Versions.

  • OS: Windows 10
  • Python: 3.9.4
  • SQLAlchemy: 1.4.14
  • mypy: 0.812
  • SQLAlchemy2-stubs: 0.0.2a1

Additional context
This feels similar to #93 except that issue seems to focus on the return type of AsyncEngine.begin() without actually raising the fact that the typed behaviour of AsyncEngine._trans_ctx is erroneous.

Have a nice day!

Publish to pypi as wheel

Is your feature request related to a problem? Please describe.
Currently the package on pypi is published as a tarball. Installing usually requires first building the wheel and then installing

Describe the solution you'd like
Publish the package to pypi as a wheel

hybrid_property: Name "my_property" already defined

Describe the bug
Using hybrid_property leads to several errors

  • redefinition of methods
  • incompatible assignment types

Expected behavior
No errors are returned

To Reproduce

from sqlalchemy import Column, Date, Time
from sqlalchemy.ext.hybrid import hybrid_property
from datetime import datetime

class DummyTable:
    create_date = Column("create_date", Date)
    create_time = Column("create_time", Time)

    @hybrid_property
    def create_timestamp(self) -> datetime:
        """create time property"""
        return datetime.combine(self.create_date, self.create_time)

    @create_timestamp.setter
    def create_timestamp(self, value:datetime) -> None:
        """create time setter"""
        self.create_date = value.date()
        self.create_time = value.time()


DummyTable().create_timestamp = datetime.now()

Error

src/x.py:14:6: error: Name "create_timestamp" already defined on line 9  [no-redef]
        @create_timestamp.setter
         ^
src/x.py:21:1: error: Cannot assign to a method  [assignment]
    DummyTable().create_timestamp = datetime.now()
    ^
src/x.py:21:33: error: Incompatible types in assignment (expression has type "datetime", variable has type "Callable[..., Any]")  [assignment]
    DummyTable().create_timestamp = datetime.now()

Versions.

  • OS: Linux
  • Python: 3.7.1
  • SQLAlchemy: 1.4.22
  • mypy: 0.910
  • SQLAlchemy2-stubs: 0.0.2a6

"LoaderOption" not callable when using joinedload()

Describe the bug
When using joinedload the way it is described in the current documentation I get the following error:
"LoaderOption" not callable

Expected behavior
No error.

To Reproduce

Import joinedload as folows:

from sqlalchemy.orm import joinedload

And then use it in a query:

users = session.query(User).options(joinedload(User.address)).all()

Versions.

  • OS:
  • Python: 3.8.6
  • SQLAlchemy: 1.4.9
  • Database:
  • DBAPI:

Have a nice day!
Again: you too :)

dataclass constructor expects `Mapped[]` values when using _mypy_mapped_attrs

Describe the bug

Following the mypy support instructions here and trying variations from here, I cannot find a way to get type checking to work for both the fields as clause elements and the dataclass constructor. Removing _mypy_mapped_attrs results in a usable constructor, but unusable fields for constructing a query.

Expected behavior

That the dataclass constructor would be type checked according to the actual field type annotations. Instead, the presence of _mypy_mapped_attrs causes mypy to expect Mapped values to be passed to the constructor, so creating an instance does not pass type checking.

To Reproduce

from dataclasses import dataclass, field

from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import registry

mapper_registry: registry = registry()

@mapper_registry.mapped
@dataclass
class Foo:
    __tablename__ = "foo"
    __table_args__ = {"schema": "public"}

    __sa_dataclass_metadata_key__ = "sa"

    uuid: str = field(
        metadata={
            "sa": Column(
                UUID,
                primary_key=True,
                nullable=False,
            )
        }
    )

    _mypy_mapped_attrs = [uuid]


foo = Foo(
    uuid="123",
)
[mypy]
plugins = sqlalchemy.ext.mypy.plugin

Error

mypy_test.py:31: error: Argument "uuid" to "Foo" has incompatible type "str"; expected "Mapped[str]"

Versions.

  • OS: Mac OS X
  • Python: 3.9.5
  • SQLAlchemy: 1.4.20
  • mypy: 0.910
  • SQLAlchemy2-stubs: 0.0.2a4

Additional context

Alternative mapping that produces the same error:

from dataclasses import dataclass, field
from typing import TYPE_CHECKING

from sqlalchemy import Column, Table
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import registry

mapper_registry: registry = registry()


@mapper_registry.mapped
@dataclass
class Foo:
    __table__ = Table(
        "foo",
        mapper_registry.metadata,
        Column(
            UUID,
            primary_key=True,
            nullable=False,
        ),
    )
    uuid: str = field()

    if TYPE_CHECKING:
        _mypy_mapped_attrs = [uuid]


foo = Foo(
    uuid="123",
)

Synchronize_session in update and delete methods doesn't accept bool

Describe the bug
Synchronize_session argument in update and delete methods is currently not acceting a bool

Expected behavior
Accepts a bool.

To Reproduce

session.query(User).update({"name": John}, synchronize_session=False)

Error

error: Argument "synchronize_session" to "update" of "Query" has incompatible type "bool"; expected "str"

Versions.

  • OS:
  • Python: 3.8.6
  • SQLAlchemy: 1.4.9
  • Database:
  • DBAPI:

Additional context

Have a nice day!
It's saturday, guys!

Datetime desc in Index results in arg-type error

Describe the bug
mypy reports an error when datetime_col.desc() is used as an Index second argument; datetime_col (without desc) works as intended.

Expected behavior
mypy does not produce an error

To Reproduce

from sqlalchemy import Column, DateTime, Integer, Index
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class Model(Base):
    
    id = Column(Integer, primary_key=True)
    created_at = Column(DateTime)

    __tablename__ = 'model'
    __table_args__ = (
        # error: Argument 2 to "Index" has incompatible type "ClauseElement"; expected "ColumnClause[Any]"  [arg-type]
        Index('created_at_idx', created_at.desc()),
    )

Error

error: Argument 2 to "Index" has incompatible type "ClauseElement"; expected "ColumnClause[Any]"  [arg-type]

Versions.

  • OS: dockerized python:3.9.4-buster
  • Python: 3.9.4
  • SQLAlchemy: 1.4.12

Have a nice day!

Can't infer type from @declared_attr on function __tablename__

Describe the bug
Receiving an error when using @declared_attr decorator on __tablenane__ class method.

Expected behavior
Not receiving an error.

To Reproduce

from uuid import UUID, uuid4

from sqlalchemy import Column
from sqlalchemy.orm import as_declarative, declared_attr
from sqlalchemy_utils import Timestamp, UUIDType, generic_repr


@as_declarative()
@generic_repr
class BaseModel(Timestamp):
    __name__: str
    uuid: UUID = Column(
        UUIDType(native=True),
        primary_key=True,
        default=uuid4,
        unique=True,
        index=True,
    )

    @declared_attr
    def __tablename__(cls) -> str:
        return f"{cls.__name__}s"

Error

[SQLAlchemy Mypy plugin] Can't infer type from @declared_attr on function '__tablename__';  please specify a return type from this function that is one of: Mapped[<python type>], relationship[<target class>], Column[<TypeEngine>], MapperProperty[<python type>]

Versions.

  • OS: Linux Mint 20.1
  • Python: 3.7.10
  • SQLAlchemy: 1.4.17
  • mypy: 0.812
  • SQLAlchemy2-stubs: 0.0.2a1

Additional context
It also happens for columns with UUID (same example) if you don't specify a type on left hand side. It happens when using UUIDType from sqlalchemy_utils or UUID from sqlalchemy.dialects.postgresql.

Have a nice day!

attempt to use pep-646 variadic generics for Row, Result, select(), others

it's only in Python 3.10, the ability to make our own types that are like tuples:

https://www.python.org/dev/peps/pep-0646/

what we would want is:


stmt: Select[User, str, int]  = select(User, Address.name, Address.id).join_from(User, Address)

result: Result[User, str, int] = session.execute(stmt)

for row: Row[User, str, int] in result:
    # ...

that is, every ReturnsRows class as well as Result and Row become some kind of covariant variadic generic. is that every word ?

Typing error in Index

When Index outside table
error: Argument 3 to "Index" has incompatible type "ColumnElement[Any]"; expected "ColumnClause[Any]"
When Index is inline
error: Argument 2 to "Index" has incompatible type "str"; expected "ColumnClause[Any]"

sqlalchemy.func.* all infer to NullType

Describe the bug
When functions are called from the sqlalchemy.func import they are all of type sqlalchemy.sql.functions.Function[sqlalchemy.sql.sqltypes.NullType].

Expected behavior
Have the same type when importing from:

  • sqlalchemy.func
  • sqlalchemy.sql.functions

To Reproduce
Please try to provide a Minimal, Complete, and Verifiable example.
See also Reporting Bugs on the website, and some example issues.

Please do not use Flask-SQLAlchemy or any other third-party extensions or dependencies in test cases. The test case must illustrate the problem without using any third party SQLAlchemy extensions. Otherwise, please report the bug to those projects first.

from sqlalchemy import func
from sqlalchemy.sql import functions

reveal_type(func.now())
reveal_type(functions.now())

Error

column_server_default_ticket_81.py:8: note: Revealed type is 'sqlalchemy.sql.functions.Function[sqlalchemy.sql.sqltypes.NullType]'
column_server_default_ticket_81.py:9: note: Revealed type is 'sqlalchemy.sql.functions.now[sqlalchemy.sql.sqltypes.DateTime]'

Have a nice day!

A `sessionmaker` overload is missing the `**kw` argument

Describe the bug
The annotation for one of sessionmaker's overloads seems to be missing the **kw: Any parameter, causing the code below to fail.

Expected behavior
Mypy shouldn't complain when calling sessionmaker with the future=True flag.

To Reproduce

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///', future=True)

Session = sessionmaker(engine, future=True)

Error

foo.py:6: error: No overload variant of "sessionmaker" matches argument types "Engine", "bool"
foo.py:6: note: Possible overload variants:
foo.py:6: note:     def [_TSessionMakerType] __init__(self, bind: Union[Connection, Engine, None] = ..., class_: None = ..., autoflush: bool = ..., autocommit: bool = ..., expire_on_commit: bool = ..., info: Optional[Mapping[Any, Any]] = ...) -> sessionmaker[Session]
foo.py:6: note:     def [_TSessionMakerType] __init__(self, bind: Union[Connection, Engine, AsyncConnection, AsyncEngine, None], class_: Type[_TSessionMakerType], autoflush: bool = ..., autocommit: bool = ..., expire_on_commit: bool = ..., info: Optional[Mapping[Any, Any]] = ..., **kw: Any) -> sessionmaker[_TSessionMakerType]
foo.py:6: note:     def [_TSessionMakerType] __init__(self, *, bind: Union[Connection, Engine, AsyncConnection, AsyncEngine, None] = ..., class_: Type[_TSessionMakerType], autoflush: bool = ..., autocommit: bool = ..., expire_on_commit: bool = ..., info: Optional[Mapping[Any, Any]] = ..., **kw: Any) -> sessionmaker[_TSessionMakerType]
Found 1 error in 1 file (checked 1 source file)

Versions.

  • OS: Debian 10
  • Python: 3.7.10, 3.8.9, 3.9.4
  • SQLAlchemy: 1.4.12
  • mypy: 0.812
  • SQLAlchemy2-stubs: 0.0.1a5

Have a nice day!

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

'DeclarativeMeta' is not defined

Describe your question
When creating a Base class with @as_declarative the following errors shows up using mypy error: Name 'DeclarativeMeta' is not defined. Am I doing something wrong or is there a better way?

Example - please use the Minimal, Complete, and Verifiable guidelines if possible
Using the example as provided in the documentation, it works without a problem:

Base = declarative_base()

class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    name = Column(String)

    addresses: Mapped[List["Address"]] = relationship("Address", back_populates="user")

class Address(Base):
    __tablename__ = 'address'

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))

    user: Mapped[User] = relationship(User, back_populates="addresses")
main  mypy --config mypy.ini --strict simple-test.py
Success: no issues found in 1 source file

When trying to use it as following it shows the error:

@as_declarative()
class Base(object):
    id = Column(Integer, primary_key=True)


class User(Base):
    __tablename__ = "user"

    name = Column(String)

    addresses: Mapped[List["Address"]] = relationship("Address", back_populates="user")


class Address(Base):
    __tablename__ = "address"

    user_id: int = Column(ForeignKey("user.id"))

    user: Mapped[User] = relationship(User, back_populates="addresses")
main  mypy --config mypy.ini --strict simple-test.py
simple-test.py: error: Name 'DeclarativeMeta' is not defined

mypy config

[mypy]
plugins = sqlalchemy.ext.mypy.plugin

Versions

  • OS: MacOS 11.0
  • Python: Python 3.9.2
  • Mypy: mypy 0.812
  • SQLAlchemy: 1.4.3
  • sqlalchemy2-stub: 0.0.1a4
  • Database: n/a
  • DBAPI: n/a

Unsupported left operand type for ClauseElement

Describe the bug

mypy complaining about & and | operators in where clauses.

To Reproduce

test_table = Table(
    "test_table",
    metadata,
    Column("i", UUID(as_uuid=True), nullable=False, primary_key=True),
    Column("x", Integer, index=True),
    Column("y", Integer, index=True),
    Index(
        "ix_xy_unique",
        "x",
        "y",
        unique=True,
    ),
)
select([test_table]).where((test_table.c.x == 2) & (test_table.c.x == 2))

Error

Unsupported left operand type for | ("ClauseElement")
Unsupported left operand type for & ("ClauseElement")

Versions.

  • OS: Linux Mint 19.3 Cinnamon
  • Python: 3.7.10
  • SQLAlchemy: 1.4.15
  • Database: PostgreSQL
  • DBAPI: asyncpg

Additional context

Have a nice day!

Optional Columns Types Not Being Inferred

On 1.3 + Dropbox Stubs, mypy allowed me to assign None to this field:

my_int = Column(Integer, nullable=True, index=True)

however on 1.4 + the new stubs, I need to annotate:

my_int: Column[Optional[int]] = Column(Integer, nullable=True, index=True

otherwise it complains about assigning None to an int

implement support to intercept as_declarative()

I installed the the type stubs and configured the mypy plugin as directed in the documentation

My database is defined like this:

@as_declarative()
class Base:
    def __repr__(self):
        name = self.__class__.__name__
        attrs = (
            "%s=%r" % (attr, getattr(self, attr))
            for attr in self._sa_class_manager.keys()
            if not (attr[-2:] == "id" or isinstance(getattr(self, attr), list))
        )
        return name + "(%s)" % ", ".join(attrs)


class Source(Base):
    __tablename__ = "sources"
    id = sa.Column(sa.Integer, primary_key=True)
    address = sa.Column(sa.String, index=True)
    match_id = sa.Column(sa.Integer, sa.ForeignKey("matches.id"))

    match = relationship("Match", back_populates="sources")


class Match(Base):
    __tablename__ = "matches"
    id = sa.Column(sa.Integer, primary_key=True)
    original_id = sa.Column(sa.Integer, sa.ForeignKey("original.id"))
    romanized_id = sa.Column(sa.Integer, sa.ForeignKey("romanized.id"))
    count = sa.Column(sa.Integer)

    original = relationship("Original", back_populates="matches")
    romanized = relationship("Romanized", back_populates="matches")
    sources = relationship("Source", back_populates="match")


sa.Index("matches_idx", Match.original_id, Match.romanized_id, unique=True)


class Standard(Base):
    __tablename__ = "standards"
    id = sa.Column(sa.Integer, primary_key=True)
    st = sa.Column(sa.String, index=True, unique=True)


class Original(Base):
    __tablename__ = "original"
    id = sa.Column(sa.Integer, primary_key=True)
    form = sa.Column(sa.String, index=True, unique=True)

    matches = relationship("Match", back_populates="original")


class Romanized(Base):
    __tablename__ = "romanized"
    id = sa.Column(sa.Integer, primary_key=True)
    form = sa.Column(sa.String, index=True)
    standard_id = sa.Column(sa.Integer, sa.ForeignKey("standards.id"))

    standard = relationship(Standard)
    matches = relationship("Match", back_populates="romanized")


sa.Index("standard_form", Romanized.form, Romanized.standard_id, unique=True)

When I run mypy, I get these errors:

$ mypy deromanize 
deromanize/cacheutils.py:201: error: Need type annotation for 'match'
deromanize/cacheutils.py:211: error: Need type annotation for 'original'
deromanize/cacheutils.py:212: error: Need type annotation for 'romanized'
deromanize/cacheutils.py:213: error: Need type annotation for 'sources'
deromanize/cacheutils.py:230: error: Need type annotation for 'matches'
deromanize/cacheutils.py:239: error: Need type annotation for 'standard'
deromanize/cacheutils.py:240: error: Need type annotation for 'matches'
Found 7 errors in 1 file (checked 6 source files)

This does not match the documented error message:

test3.py:22: error: [SQLAlchemy Mypy plugin] Can't infer scalar or
collection for ORM mapped expression assigned to attribute 'user'
if both 'uselist' and 'collection_class' arguments are absent from the
relationship(); please specify a type annotation on the left hand side.
Found 1 error in 1 file (checked 1 source file)

Furthermore, when I attempt to add annotations, that doesn't work either:

class Source(Base):
    __tablename__ = "sources"
    id = sa.Column(sa.Integer, primary_key=True)
    address = sa.Column(sa.String, index=True)
    match_id = sa.Column(sa.Integer, sa.ForeignKey("matches.id"))

    match: 'Match' = relationship("Match", back_populates="sources")
deromanize/cacheutils.py:201: error: Incompatible types in assignment (expression has type "RelationshipProperty[<nothing>]", variable has type "Match")

Am I doing something wrong or is this a bug?

Issue Following: Creating an Explicit Base Non-Dynamically

On SQLA 1.3 + the Dropbox stubs, I use Base = declarative_base(metadata=meta) for my models' base class and everything works fine. When I upgrade to 1.4 + the new stubs, using that Base for my models generates the following mypy warning:

api/models.py:1121: error: Variable "api.database.Base" is not valid as a type
api/models.py:1121: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases

So first I tried:

from sqlalchemy.orm import registry
....
mapper_registry = registry(metadata=meta)
Base = mapper_registry.generate_base()

however I get the same warning. I do get an additional warning:

error: Module 'sqlalchemy.orm' has no attribute 'registry'

I then tried:

mapper_registry = registry(metadata=meta)

class Base(metaclass=DeclarativeMeta):
    __abstract__ = True

    registry = mapper_registry
    metadata = mapper_registry.metadata

however I then get:

 mypy . --show-traceback
api/database.py:16: error: Module 'sqlalchemy.orm' has no attribute 'registry'
./api/models.py:34: error: INTERNAL ERROR -- Please try using mypy master on Github:
https://mypy.rtfd.io/en/latest/common_issues.html#using-a-development-mypy-build
Please report a bug at https://github.com/python/mypy/issues
version: 0.812
Traceback (most recent call last):
  File "mypy/semanal.py", line 4835, in accept
  File "mypy/nodes.py", line 950, in accept
  File "mypy/semanal.py", line 1048, in visit_class_def
  File "mypy/semanal.py", line 1125, in analyze_class
  File "mypy/semanal.py", line 1134, in analyze_class_body_common
  File "mypy/semanal.py", line 1194, in apply_class_plugin_hooks
  File "/Users/alex/.pyenv/versions/3.8.2/envs/api/lib/python3.8/site-packages/sqlalchemy/ext/mypy/plugin.py", line 193, in _base_cls_hook
    _add_globals(ctx)
  File "/Users/alex/.pyenv/versions/3.8.2/envs/api/lib/python3.8/site-packages/sqlalchemy/ext/mypy/plugin.py", line 184, in _add_globals
    util.add_global(ctx, "sqlalchemy.orm.attributes", "Mapped", "__sa_Mapped")
  File "/Users/alex/.pyenv/versions/3.8.2/envs/api/lib/python3.8/site-packages/sqlalchemy/ext/mypy/util.py", line 79, in add_global
    lookup_sym: SymbolTableNode = ctx.api.modules[module].names[
KeyError: 'Mapped'
./api/models.py:34: : note: use --pdb to drop into pdb

Cannot unpack Row instance

Describe the bug
mypy is complaining about unpacking Row object, even though it works.

To Reproduce

test_table = Table(
    "test_table",
    metadata,
    Column("i", UUID(as_uuid=True), nullable=False, primary_key=True),
    Column("x", UUID(as_uuid=True), index=True),
    Column("y", UUID(as_uuid=True), index=True),
)
query = test_table.select()
async with engine.connect() as connection:
  result = await connection.execute(query)
row = result.fetchone()
if row:
  my_dict = {**row}

Error

Argument after ** must be a mapping, not "Row"

Versions.

  • OS: Linux Mint 19.3 Cinnamon
  • Python: 3.7.10
  • SQLAlchemy: 1.4.15
  • Database: PostgreSQL
  • DBAPI: asyncpg

Non nullable fields are treated like Optional

Describe the bug
Non nullable columns are treated as optional.

Expected behavior
Non nullable columns are treated as non optional.

To Reproduce

from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import MetaData
from sqlalchemy.orm import declarative_base

Base = declarative_base(metadata=MetaData())


class U(Base):
    __tablename__ = "x"
    id = Column(Integer, primary_key=True)
    my_int = Column(Integer, nullable=False, index=True)


def x(my_int: int):
  pass

u1 = U(my_int = 2)

x(u1.my_int)

Error

error: Argument 1 to "x" has incompatible type "Optional[int]"; expected "int"

Versions.

  • OS: Linux Mint 19
  • Python: 3.7.10
  • SQLAlchemy: 1.4.22
  • mypy: 0.910
  • SQLAlchemy2-stubs: 0.0.2a4 & 0.0.2a6

Additional context
Trying to test on

  • 0.0.2a6

Seems to work when you manually type my_int: int = Column(Integer, nullable=False, index=True)

Have a nice day!

relationship types not detected when passing related class as str

I was just trying out the new stubs on an existing project if mine, and it looks like you did some awesome work there - I can get rid of a lot of workarounds. Thanks! Nevertheless, there's one issue I wanted to share with you.

Describe the bug
There are two ways to pass the first parameter of a relationship: by class or by str. When using the class directly everything works as expected, the type is detected when using uselist or relationsship. And I don't find any false positives afterwards.

When I use a str to configure my relationship things get pretty messy. First, I need to type the annotations manually, I wasn't able to find any way to configure it such that auto-detection works.

[SQLAlchemy Mypy plugin] Can't infer type from ORM mapped expression assigned to attribute 'addresses'; please specify a Python type or Mapped[<python type>] on the left hand side.

Second, even when annotating it manually, I'll have to cast it every single time I use it (when it's some kind of Iterable as shown in the example below).

error: "List?[Address?]" has no attribute "__iter__" (not iterable)
error: "List?[Address?]" has no attribute "append"

Expected behavior
Type detection works for relationship independent of type of first argument.

To Reproduce

from __future__ import annotations

from typing import List

from sqlalchemy import Integer, Column, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship

Base = declarative_base()


class User(Base):

    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String)

    addresses: List[Address] = relationship("Address", back_populates="user", uselist=True)

    def clone(self) -> User:
        new_user = User(name=self.name)

        for existing_addr in self.addresses:
            new_user.addresses.append(
                Address(address=existing_addr.address)
            )
        return new_user


class Address(Base):

    __tablename__ = "addresses"

    id = Column(Integer, primary_key=True)
    address = Column(String)
    
    user_id = Column(Integer, ForeignKey("users.id"))
    
    user = relationship(User, back_populates="addresses", uselist=False)

Versions.

  • OS: WIn10
  • Python: 3.8.6
  • SQLAlchemy: 1.4.9
  • Database:
  • DBAPI:

Have a nice day!
You too ;)

AssertionError: could not extract Python type from node

Describe the bug
Running mypy . --show-traceback I am getting this stacktrace:

version: 0.812
Traceback (most recent call last):
  File "mypy/semanal.py", line 4835, in accept
  File "mypy/nodes.py", line 950, in accept
  File "mypy/semanal.py", line 1048, in visit_class_def
  File "mypy/semanal.py", line 1125, in analyze_class
  File "mypy/semanal.py", line 1134, in analyze_class_body_common
  File "mypy/semanal.py", line 1194, in apply_class_plugin_hooks
  File "/Users/alex/.pyenv/versions/3.8.2/envs/my-app/lib/python3.8/site-packages/sqlalchemy/ext/mypy/plugin.py", line 159, in _base_cls_hook
    decl_class._scan_declarative_assignments_and_apply_types(ctx.cls, ctx.api)
  File "/Users/alex/.pyenv/versions/3.8.2/envs/my-app/lib/python3.8/site-packages/sqlalchemy/ext/mypy/decl_class.py", line 123, in _scan_declarative_assignments_and_apply_types
    _scan_declarative_assignment_stmt(cls, api, stmt, cls_metadata)
  File "/Users/alex/.pyenv/versions/3.8.2/envs/my-app/lib/python3.8/site-packages/sqlalchemy/ext/mypy/decl_class.py", line 380, in _scan_declarative_assignment_stmt
    python_type_for_type = _infer_type_from_decl_column(
  File "/Users/alex/.pyenv/versions/3.8.2/envs/my-app/lib/python3.8/site-packages/sqlalchemy/ext/mypy/decl_class.py", line 714, in _infer_type_from_decl_column
    python_type_for_type = _extract_python_type_from_typeengine(
  File "/Users/alex/.pyenv/versions/3.8.2/envs/my-app/lib/python3.8/site-packages/sqlalchemy/ext/mypy/decl_class.py", line 989, in _extract_python_type_from_typeengine
    assert False, "could not extract Python type from node: %s" % node
AssertionError: could not extract Python type from node: TypeInfo(
  Name(sqlalchemy.sql.sqltypes.Boolean)
  Bases(sqlalchemy.sql.type_api.Emulated, sqlalchemy.sql.type_api.TypeEngine[builtins.bool], sqlalchemy.sql.sqltypes.SchemaType)
  Mro(sqlalchemy.sql.sqltypes.Boolean, sqlalchemy.sql.type_api.Emulated, sqlalchemy.sql.type_api.TypeEngine, sqlalchemy.sql.visitors.Traversible, sqlalchemy.sql.sqltypes.SchemaType, sqlalchemy.sql.base.SchemaEventTarget, builtins.object)
  Names(
    __init__
    __visit_name__ (builtins.str)
    bind_processor
    create_constraint (Any)
    literal_processor
    name (Any)
    native (builtins.bool)
    python_type
    result_processor))

Expected behavior
mypy runs without dying.

To Reproduce
I haven't yet figured out a stripped down example. Working on it

Versions.

sqlalchemy2-stubs==0.0.1a4
sqlalchemy[postgresql]==1.4.2

Additional context

Have a nice day!

'No module named '_contextvars'' error when using a pipenv environment

Following this, I'm getting the following error:

setup.cfg:2: error: Error importing plugin "sqlalchemy.ext.mypy.plugin": No module named '_contextvars'
Found 1 error in 1 file (errors prevented further checking)

setup.cfg:

[mypy]
plugins = sqlalchemy.ext.mypy.plugin

Expected behavior
mypy should work in a pipenv virtualenv :)

Versions.

  • OS: Windows 10
  • Python: 3.7.0
  • SQLAlchemy: 1.4.18
  • mypy: 0.901
  • SQLAlchemy2-stubs: 0.0.2a2

TypeDecorator.cache_ok is missing

Describe the bug
TypeDecorator.cache_ok is missing in the stubs

Expected behavior
TypeDecorator.cache_ok is present

To Reproduce

from sqlalchemy.types import TypeDecorator

class MyType(TypeDecorator):
  cache_ok = True

Versions.

  • OS: Linux
  • Python: 3.7.1
  • SQLAlchemy: 1.4.19
  • mypy: 0.910
  • SQLAlchemy2-stubs: 0.0.2a2

Assigning functions to orm attributes

Describe the bug
Introduced by #40, sql.functions are no longer able to be assigned to ORM attributes. I couldn't find documented cases of directly assigning functions to attributes. Is it even a supported use-case? Or should update() always be used.

Expected behavior
Type checking to pass, as it works at runtime

To Reproduce

from sqlalchemy import Column, DateTime
from sqlalchemy import Integer
from sqlalchemy.orm import registry
from sqlalchemy.sql.functions import now

mapper_registry: registry = registry()

@mapper_registry.mapped
class A:
    __tablename__ = "a"
    id = Column(Integer, primary_key=True)
    date_time = Column(DateTime())

a = A()
a.date_time = now()

Error

test/ext/mypy/files/function_now.py:15: error: Incompatible types in assignment (expression has type "now[<nothing>]", variable has type "Optional[datetime]")```

Have a nice day!

Mypy complains extension is not a directory

Describe the bug

mypy config

$cat mypy.ini
[mypy]
plugins = sqlalchemy.ext.mypy.plugin

I'm trying annotate flask-sqlalchemy library in out directory and when I run mypy like
MYPYPATH="out" prun mypy example.py. Here prun is alias to poetry run. the command produces following output
example.py contains no flask sqlalchemy imports

Error constructing plugin instance of SQLAlchemyPlugin

Traceback (most recent call last):
  File "/Users/user/Library/Caches/pypoetry/virtualenvs/flask-sqlalchemy-KzL4aHKU-py3.9/bin/mypy", line 8, in <module>
    sys.exit(console_entry())
  File "/Users/user/Library/Caches/pypoetry/virtualenvs/flask-sqlalchemy-KzL4aHKU-py3.9/lib/python3.9/site-packages/mypy/__main__.py", line 11, in console_entry
    main(None, sys.stdout, sys.stderr)
  File "mypy/main.py", line 90, in main
  File "mypy/build.py", line 179, in build
  File "mypy/build.py", line 228, in _build
  File "mypy/build.py", line 472, in load_plugins
  File "mypy/build.py", line 451, in load_plugins_from_config
  File "mypy/build.py", line 491, in take_module_snapshot
NotADirectoryError: [Errno 20] Not a directory: '/Users/user/Library/Caches/pypoetry/virtualenvs/flask-sqlalchemy-KzL4aHKU-py3.9/lib/python3.9/site-packages/SQLAlchemy-1.4.18-py3.9-macosx-11-x86_64.egg/sqlalchemy/ext/mypy/plugin.py'

Expected behavior

The mypy shouldn't complain sqlalchemy plugin is invalid.

To Reproduce
Please try to provide a Minimal, Complete, and Verifiable example.
See also Reporting Bugs on the website, and some example issues.

Please do not use Flask-SQLAlchemy or any other third-party extensions or dependencies in test cases. The test case must illustrate the problem without using any third party SQLAlchemy extensions. Otherwise, please report the bug to those projects first.

# Insert code here
from sqlalchemy import orm

Error

poetry run mypy example.py

Python 2.7 will no longer be supported in the next feature release of Poetry (1.2).
You should consider updating your Python version to a supported one.

Note that you will still be able to manage Python 2.7 projects by using the env command.
See https://python-poetry.org/docs/managing-environments/ for more information.

Error constructing plugin instance of SQLAlchemyPlugin

Traceback (most recent call last):
  File "/Users/user/Library/Caches/pypoetry/virtualenvs/flask-sqlalchemy-KzL4aHKU-py3.9/bin/mypy", line 8, in <module>
    sys.exit(console_entry())
  File "/Users/user/Library/Caches/pypoetry/virtualenvs/flask-sqlalchemy-KzL4aHKU-py3.9/lib/python3.9/site-packages/mypy/__main__.py", line 11, in console_entry
    main(None, sys.stdout, sys.stderr)
  File "mypy/main.py", line 90, in main
  File "mypy/build.py", line 179, in build
  File "mypy/build.py", line 228, in _build
  File "mypy/build.py", line 472, in load_plugins
  File "mypy/build.py", line 451, in load_plugins_from_config
  File "mypy/build.py", line 491, in take_module_snapshot
NotADirectoryError: [Errno 20] Not a directory: '/Users/user/Library/Caches/pypoetry/virtualenvs/flask-sqlalchemy-KzL4aHKU-py3.9/lib/python3.9/site-packages/SQLAlchemy-1.4.18-py3.9-macosx-11-x86_64.egg/sqlalchemy/ext/mypy/plugin.py'
# Copy error here. Please include the full stack trace.

Versions.

  • OS: Mac OSX
  • Python: 3.9.4
  • SQLAlchemy: 1.4.18
  • mypy: 0.812/0.902
  • SQLAlchemy2-stubs: 0.0.2a2

Additional context

I tried a different versions of mypy like 0.812 and 0.902. The error remains same. Not sure where I should report here or in mypy repository, starting here.

Have a nice day!

Static typing using Cast expects two type arguments

Describe the bug
We have a statically typed dictionary that has values of type Cast[Decimal] & it seems to be valid, since we cast column value to FLOAT. But the type annotation expects two type arguments. I think it uses this stub: https://github.com/sqlalchemy/sqlalchemy2-stubs/blob/master/sqlalchemy-stubs/sql/elements.pyi#L333
whereas it's for a method cast on ColumnElement.

Expected behavior
Static typing using Cast[Decimal] works fine :P

To Reproduce

from uuid import uuid4, UUID
from typing import Dict
from sqlalchemy.sql.elements import Cast
from decimal import Decimal
from sqlalchemy import FLOAT, cast


class X:
  y: Optional[float]
x =x()
x.y = 2.0

payload: Dict[UUID, Cast[Decimal]] = {
    uuid4(): cast(x.y, FLOAT)
}

Error

error: "Cast" expects 2 type arguments, but 1 given

Versions.

  • OS: Linux Mint 19.3 Cinnamon
  • Python: 3.7.10
  • SQLAlchemy: 1.4.15
  • Database: PostgreSQL
  • DBAPI: asyncpg

Additional context
Maybe we should actually not use cast in this case? We are using it because y is an Optional[float] & not float, but we are 100% sure that it's not None?

Have a nice day!

"order_by" of "Query" does not return a value (same for limit)

Describe the bug

total = query.order_by(None).count()
items = query.limit(10).offset(10).all()
mypy --strict test.py
error: "order_by" of "Query" does not return a value
error: "limit" of "Query" does not return a value

Expected behavior

This is a valid usage of Query. The doc states that ORM Query is considered legacy in v2, it is still usable.

To Reproduce

from typing import Any

from sqlalchemy.orm import Query


def paginate(query: Query) -> Any:
    total = query.order_by(None).count()
    items = query.limit(10).offset(10).all()
    return [total, items]

Error

mypy --strict test.py
test.py:7: error: "order_by" of "Query" does not return a value
test.py:8: error: "limit" of "Query" does not return a value
Found 2 errors in 1 file (checked 1 source file)

Versions.

❯ pip list
Package           Version
----------------- -------
greenlet          1.0.0
mypy              0.812
mypy-extensions   0.4.3
pip               21.0.1
setuptools        54.2.0
SQLAlchemy        1.4.11
sqlalchemy2-stubs 0.0.1a4
typed-ast         1.4.3
typing-extensions 3.7.4.3

Have a nice day and thanks for the amazing work!

Add types for new AsyncSession methods

sqlalchemy/sqlalchemy@97d9226 implements new methods:

:meth:`_asyncio.AsyncSession.in_nested_transaction`,
:meth:`_asyncio.AsyncSession.get_transaction`,
:meth:`_asyncio.AsyncSession.get_nested_transaction`.
:func:`_asyncio.async_object_session`,
:func:`_asyncio.async_session` as well as a new

:class:`_orm.InstanceState` attribute :attr:`_orm.InstanceState.asyncio_session`

Cannot define declarative base class as attribute of object

Describe the bug
I'd like to switch from the dropbox/sqlalchemy stubs to the official stubs, however when the declarative base is defined as attribute of an instance, it is not recognized as valid base class.

The Dropbox stubs give similar errors regarding the subclassing, however the types are still correctly inferred.

Expected behavior
Type definitions to be picked up as the builtin types: test.py:20: note: Revealed type is 'Union[builtins.int, None]'

To Reproduce

from sqlalchemy.schema import Column
from sqlalchemy.types import Integer, String
from sqlalchemy.orm.decl_api import DeclarativeMeta, declarative_base

db = DB()

class DB():
    def __init__(self):
        self.Base = declarative_base()


class A(db.Base):
    __tablename__ = "a"
    id = Column(Integer, primary_key=True)

a = A()
reveal_type(a.id)

Error

test2.py:12: error: Name 'db.Base' is not defined
test2.py:17: note: Revealed type is 'sqlalchemy.sql.schema.Column[sqlalchemy.sql.sqltypes.Integer*]'
Found 1 error in 1 file (checked 1 source file)

Versions.

  • OS: Arch linux
  • Python: 3.9
  • SQLAlchemy: 1.4.5 + mypy plugin
  • Database: n/a
  • DBAPI: n/a

Additional context
The problem seems similar to one described on StackOverflow. However, I expected that replacing the direct subclassing with a typed variable would resort the problem.

Example code
from sqlalchemy.schema import Column
from sqlalchemy.types import Integer, String
from sqlalchemy.orm.decl_api import DeclarativeMeta, declarative_base

db = DB()

class DB():
    def __init__(self):
        self.Base = declarative_base()

Base: DeclarativeMeta = db.Base

class A(Base):
    __tablename__ = "a"
    id = Column(Integer, primary_key=True)

a = A()
reveal_type(a.id)

Have a nice day!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.