Skip to content

Commit 426ce06

Browse files
authored
Various small SQLAlchemy type improvements (#6623)
1 parent 8571207 commit 426ce06

File tree

6 files changed

+138
-103
lines changed

6 files changed

+138
-103
lines changed

stubs/SQLAlchemy/@tests/stubtest_allowlist.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ sqlalchemy.util.langhelpers._symbol.__new__
77
sqlalchemy.util._collections.*
88
sqlalchemy.util.compat.*
99

10+
# forwards arguments to another function
11+
sqlalchemy.ext.declarative.as_declarative
12+
1013
# stdlib re-exports with stubtest issues
1114
sqlalchemy.orm.collections.InstrumentedList.*
1215
sqlalchemy.orm.collections.InstrumentedSet.*

stubs/SQLAlchemy/sqlalchemy/exc.pyi

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
from typing import Any
1+
from typing import Any, ClassVar
22

33
class HasDescriptionCode:
4-
code: Any
5-
def __init__(self, *arg, **kw) -> None: ...
4+
code: str | None
5+
def __init__(self, *arg: Any, code: str | None = ..., **kw: Any) -> None: ...
66

77
class SQLAlchemyError(HasDescriptionCode, Exception):
8-
def __unicode__(self): ...
8+
def __unicode__(self) -> str: ...
99

1010
class ArgumentError(SQLAlchemyError): ...
1111

@@ -30,8 +30,8 @@ class UnsupportedCompilationError(CompileError):
3030
code: str
3131
compiler: Any
3232
element_type: Any
33-
message: Any
34-
def __init__(self, compiler, element_type, message: Any | None = ...) -> None: ...
33+
message: str | None
34+
def __init__(self, compiler, element_type, message: str | None = ...) -> None: ...
3535
def __reduce__(self): ...
3636

3737
class IdentifierError(SQLAlchemyError): ...
@@ -114,41 +114,26 @@ class DBAPIError(StatementError):
114114
ismulti: Any | None = ...,
115115
) -> None: ...
116116

117-
class InterfaceError(DBAPIError):
118-
code: str
119-
120-
class DatabaseError(DBAPIError):
121-
code: str
122-
123-
class DataError(DatabaseError):
124-
code: str
125-
126-
class OperationalError(DatabaseError):
127-
code: str
128-
129-
class IntegrityError(DatabaseError):
130-
code: str
131-
132-
class InternalError(DatabaseError):
133-
code: str
134-
135-
class ProgrammingError(DatabaseError):
136-
code: str
137-
138-
class NotSupportedError(DatabaseError):
139-
code: str
117+
class InterfaceError(DBAPIError): ...
118+
class DatabaseError(DBAPIError): ...
119+
class DataError(DatabaseError): ...
120+
class OperationalError(DatabaseError): ...
121+
class IntegrityError(DatabaseError): ...
122+
class InternalError(DatabaseError): ...
123+
class ProgrammingError(DatabaseError): ...
124+
class NotSupportedError(DatabaseError): ...
140125

141126
class SADeprecationWarning(HasDescriptionCode, DeprecationWarning):
142-
deprecated_since: Any
127+
deprecated_since: ClassVar[str | None]
143128

144129
class Base20DeprecationWarning(SADeprecationWarning):
145-
deprecated_since: str
130+
deprecated_since: ClassVar[str]
146131

147132
class LegacyAPIWarning(Base20DeprecationWarning): ...
148133
class RemovedIn20Warning(Base20DeprecationWarning): ...
149134
class MovedIn20Warning(RemovedIn20Warning): ...
150135

151136
class SAPendingDeprecationWarning(PendingDeprecationWarning):
152-
deprecated_since: Any
137+
deprecated_since: ClassVar[str | None]
153138

154139
class SAWarning(HasDescriptionCode, RuntimeWarning): ...

stubs/SQLAlchemy/sqlalchemy/ext/declarative/__init__.pyi

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
from ...orm.decl_api import DeclarativeMeta as DeclarativeMeta, declared_attr as declared_attr
1+
from ...orm.decl_api import (
2+
DeclarativeMeta as DeclarativeMeta,
3+
as_declarative as as_declarative,
4+
declarative_base as declarative_base,
5+
declared_attr as declared_attr,
6+
has_inherited_table as has_inherited_table,
7+
synonym_for as synonym_for,
8+
)
29
from .extensions import (
310
AbstractConcreteBase as AbstractConcreteBase,
411
ConcreteBase as ConcreteBase,
@@ -18,8 +25,3 @@ __all__ = [
1825
"DeclarativeMeta",
1926
"DeferredReflection",
2027
]
21-
22-
def declarative_base(*arg, **kw): ...
23-
def as_declarative(*arg, **kw): ...
24-
def has_inherited_table(*arg, **kw): ...
25-
def synonym_for(*arg, **kw): ...
Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,39 @@
1-
from typing import Any
1+
from _typeshed import Self
2+
from logging import Logger
3+
from typing import Any, TypeVar, overload
4+
from typing_extensions import Literal
5+
6+
_ClsT = TypeVar("_ClsT", bound=type)
7+
_EchoFlag = bool | Literal["debug"] | None
28

39
rootlogger: Any
410

5-
def class_logger(cls): ...
11+
def class_logger(cls: _ClsT) -> _ClsT: ...
612

713
class Identified:
8-
logging_name: Any
14+
logging_name: str | None
915

1016
class InstanceLogger:
11-
echo: Any
12-
logger: Any
13-
def __init__(self, echo, name) -> None: ...
17+
echo: _EchoFlag
18+
logger: Logger
19+
def __init__(self, echo: _EchoFlag, name: str | None) -> None: ...
1420
def debug(self, msg, *args, **kwargs) -> None: ...
1521
def info(self, msg, *args, **kwargs) -> None: ...
1622
def warning(self, msg, *args, **kwargs) -> None: ...
17-
warn: Any
23+
warn = warning
1824
def error(self, msg, *args, **kwargs) -> None: ...
1925
def exception(self, msg, *args, **kwargs) -> None: ...
2026
def critical(self, msg, *args, **kwargs) -> None: ...
2127
def log(self, level, msg, *args, **kwargs) -> None: ...
2228
def isEnabledFor(self, level): ...
2329
def getEffectiveLevel(self): ...
2430

25-
def instance_logger(instance, echoflag: Any | None = ...) -> None: ...
31+
def instance_logger(instance: Identified, echoflag: _EchoFlag = ...) -> None: ...
2632

2733
class echo_property:
2834
__doc__: str
29-
def __get__(self, instance, owner): ...
30-
def __set__(self, instance, value) -> None: ...
35+
@overload
36+
def __get__(self: Self, instance: None, owner: object) -> Self: ...
37+
@overload
38+
def __get__(self, instance: Identified, owner: object) -> _EchoFlag: ...
39+
def __set__(self, instance: Identified, value: _EchoFlag) -> None: ...

stubs/SQLAlchemy/sqlalchemy/util/_collections.pyi

Lines changed: 64 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
import collections.abc
22
import sys
3-
from typing import Any
3+
from _typeshed import Self, SupportsKeysAndGetItem
4+
from collections.abc import Callable, Iterable, Iterator
5+
from typing import Any, Generic, NoReturn, TypeVar, overload
46

57
from ..cimmutabledict import immutabledict as immutabledict
8+
from ..sql.elements import ColumnElement
9+
10+
_S = TypeVar("_S")
11+
_T = TypeVar("_T")
612

713
collections_abc = collections.abc
814

9-
EMPTY_SET: Any
15+
EMPTY_SET: frozenset[Any]
1016

1117
class ImmutableContainer:
12-
__delitem__: Any
13-
__setitem__: Any
14-
__setattr__: Any
18+
def __delitem__(self, *arg: object, **kw: object) -> NoReturn: ...
19+
def __setitem__(self, *arg: object, **kw: object) -> NoReturn: ...
20+
def __setattr__(self, *arg: object, **kw: object) -> NoReturn: ...
1521

16-
def coerce_to_immutabledict(d): ...
22+
def coerce_to_immutabledict(d) -> immutabledict: ...
1723

18-
EMPTY_DICT: Any
24+
EMPTY_DICT: immutabledict
1925

2026
class FacadeDict(ImmutableContainer, dict[Any, Any]):
2127
clear: Any
@@ -27,31 +33,34 @@ class FacadeDict(ImmutableContainer, dict[Any, Any]):
2733
def copy(self) -> None: ... # type: ignore[override]
2834
def __reduce__(self): ...
2935

30-
class Properties:
31-
def __init__(self, data) -> None: ...
32-
def __len__(self): ...
33-
def __iter__(self): ...
34-
def __dir__(self): ...
35-
def __add__(self, other): ...
36-
def __setitem__(self, key, obj) -> None: ...
37-
def __getitem__(self, key): ...
38-
def __delitem__(self, key) -> None: ...
39-
def __setattr__(self, key, obj) -> None: ...
40-
def __getattr__(self, key): ...
41-
def __contains__(self, key): ...
42-
def as_immutable(self): ...
43-
def update(self, value) -> None: ...
44-
def get(self, key, default: Any | None = ...): ...
45-
def keys(self): ...
46-
def values(self): ...
47-
def items(self): ...
48-
def has_key(self, key): ...
36+
class Properties(Generic[_T]):
37+
def __init__(self, data: dict[str, _T]) -> None: ...
38+
def __len__(self) -> int: ...
39+
def __iter__(self) -> Iterator[_T]: ...
40+
def __dir__(self) -> list[str]: ...
41+
def __add__(self, other: Iterable[_S]) -> list[_S | _T]: ...
42+
def __setitem__(self, key: str, obj: _T) -> None: ...
43+
def __getitem__(self, key: str) -> _T: ...
44+
def __delitem__(self, key: str) -> None: ...
45+
def __setattr__(self, key: str, obj: _T) -> None: ...
46+
def __getattr__(self, key: str) -> _T: ...
47+
def __contains__(self, key: str) -> bool: ...
48+
def as_immutable(self) -> ImmutableProperties[_T]: ...
49+
def update(self, value: Iterable[tuple[str, _T]] | SupportsKeysAndGetItem[str, _T]) -> None: ...
50+
@overload
51+
def get(self, key: str) -> _T | None: ...
52+
@overload
53+
def get(self, key: str, default: _S) -> _T | _S: ...
54+
def keys(self) -> list[str]: ...
55+
def values(self) -> list[_T]: ...
56+
def items(self) -> list[tuple[str, _T]]: ...
57+
def has_key(self, key: str) -> bool: ...
4958
def clear(self) -> None: ...
5059

51-
class OrderedProperties(Properties):
60+
class OrderedProperties(Properties[_T], Generic[_T]):
5261
def __init__(self) -> None: ...
5362

54-
class ImmutableProperties(ImmutableContainer, Properties): ...
63+
class ImmutableProperties(ImmutableContainer, Properties[_T], Generic[_T]): ...
5564

5665
if sys.version_info >= (3, 7):
5766
OrderedDict = dict
@@ -75,32 +84,32 @@ else:
7584

7685
def sort_dictionary(d, key: Any | None = ...): ...
7786

78-
class OrderedSet(set[Any]):
79-
def __init__(self, d: Any | None = ...) -> None: ...
80-
def add(self, element) -> None: ...
81-
def remove(self, element) -> None: ...
82-
def insert(self, pos, element) -> None: ...
83-
def discard(self, element) -> None: ...
87+
class OrderedSet(set[_T], Generic[_T]):
88+
def __init__(self, d: Iterable[_T] | None = ...) -> None: ...
89+
def add(self, element: _T) -> None: ...
90+
def remove(self, element: _T) -> None: ...
91+
def insert(self, pos: int, element: _T) -> None: ...
92+
def discard(self, element: _T) -> None: ...
8493
def clear(self) -> None: ...
85-
def __getitem__(self, key): ...
86-
def __iter__(self): ...
87-
def __add__(self, other): ...
88-
def update(self, iterable): ...
89-
__ior__: Any
90-
def union(self, other): ...
91-
__or__: Any
92-
def intersection(self, other): ...
93-
__and__: Any
94-
def symmetric_difference(self, other): ...
95-
__xor__: Any
96-
def difference(self, other): ...
97-
__sub__: Any
98-
def intersection_update(self, other): ...
99-
__iand__: Any
100-
def symmetric_difference_update(self, other): ...
101-
__ixor__: Any
102-
def difference_update(self, other): ...
103-
__isub__: Any
94+
def __getitem__(self, key: int) -> _T: ...
95+
def __iter__(self) -> Iterator[_T]: ...
96+
def __add__(self, other: Iterable[_S]) -> OrderedSet[_S | _T]: ...
97+
def update(self: Self, iterable: Iterable[_T]) -> Self: ... # type: ignore[override]
98+
__ior__ = update # type: ignore[assignment]
99+
def union(self, other: Iterable[_S]) -> OrderedSet[_S | _T]: ... # type: ignore[override]
100+
__or__ = union # type: ignore[assignment]
101+
def intersection(self: Self, other: Iterable[Any]) -> Self: ... # type: ignore[override]
102+
__and__ = intersection # type: ignore[assignment]
103+
def symmetric_difference(self, other: Iterable[_S]) -> OrderedSet[_S | _T]: ...
104+
__xor__ = symmetric_difference # type: ignore[assignment]
105+
def difference(self: Self, other: Iterable[Any]) -> Self: ... # type: ignore[override]
106+
__sub__ = difference # type: ignore[assignment]
107+
def intersection_update(self: Self, other: Iterable[Any]) -> Self: ... # type: ignore[override]
108+
__iand__ = intersection_update # type: ignore[assignment]
109+
def symmetric_difference_update(self: Self, other: Iterable[_T]) -> Self: ... # type: ignore[override]
110+
__ixor__ = symmetric_difference_update # type: ignore[assignment]
111+
def difference_update(self: Self, other: Iterable[Any]) -> Self: ... # type: ignore[override]
112+
__isub__ = difference_update # type: ignore[assignment]
104113

105114
class IdentitySet:
106115
def __init__(self, iterable: Any | None = ...) -> None: ...
@@ -164,9 +173,9 @@ class WeakPopulateDict(dict[Any, Any]):
164173

165174
column_set = set
166175
column_dict = dict
167-
ordered_column_set = OrderedSet
176+
ordered_column_set = OrderedSet[ColumnElement]
168177

169-
def unique_list(seq, hashfunc: Any | None = ...): ...
178+
def unique_list(seq: Iterable[_T], hashfunc: Callable[[_T], Any] | None = ...) -> list[_T]: ...
170179

171180
class UniqueAppender:
172181
data: Any

tests/pytype_exclude_list.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ stubs/SQLAlchemy/sqlalchemy/databases/__init__.pyi
4949
stubs/SQLAlchemy/sqlalchemy/dialects/__init__.pyi
5050
stubs/SQLAlchemy/sqlalchemy/dialects/mssql/__init__.pyi
5151
stubs/SQLAlchemy/sqlalchemy/dialects/mssql/base.pyi
52+
stubs/SQLAlchemy/sqlalchemy/dialects/mssql/information_schema.pyi
5253
stubs/SQLAlchemy/sqlalchemy/dialects/mssql/json.pyi
5354
stubs/SQLAlchemy/sqlalchemy/dialects/mssql/mxodbc.pyi
5455
stubs/SQLAlchemy/sqlalchemy/dialects/mssql/pymssql.pyi
@@ -58,6 +59,8 @@ stubs/SQLAlchemy/sqlalchemy/dialects/mysql/aiomysql.pyi
5859
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/asyncmy.pyi
5960
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/base.pyi
6061
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/cymysql.pyi
62+
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/dml.pyi
63+
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/expression.pyi
6164
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/json.pyi
6265
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/mariadb.pyi
6366
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/mariadbconnector.pyi
@@ -66,9 +69,13 @@ stubs/SQLAlchemy/sqlalchemy/dialects/mysql/mysqldb.pyi
6669
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/oursql.pyi
6770
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/pymysql.pyi
6871
stubs/SQLAlchemy/sqlalchemy/dialects/mysql/pyodbc.pyi
72+
stubs/SQLAlchemy/sqlalchemy/dialects/oracle/base.pyi
6973
stubs/SQLAlchemy/sqlalchemy/dialects/postgresql/__init__.pyi
7074
stubs/SQLAlchemy/sqlalchemy/dialects/postgresql/array.pyi
7175
stubs/SQLAlchemy/sqlalchemy/dialects/postgresql/asyncpg.pyi
76+
stubs/SQLAlchemy/sqlalchemy/dialects/postgresql/base.pyi
77+
stubs/SQLAlchemy/sqlalchemy/dialects/postgresql/dml.pyi
78+
stubs/SQLAlchemy/sqlalchemy/dialects/postgresql/ext.pyi
7279
stubs/SQLAlchemy/sqlalchemy/dialects/postgresql/hstore.pyi
7380
stubs/SQLAlchemy/sqlalchemy/dialects/postgresql/json.pyi
7481
stubs/SQLAlchemy/sqlalchemy/dialects/postgresql/pg8000.pyi
@@ -79,7 +86,27 @@ stubs/SQLAlchemy/sqlalchemy/dialects/postgresql/ranges.pyi
7986
stubs/SQLAlchemy/sqlalchemy/dialects/sqlite/__init__.pyi
8087
stubs/SQLAlchemy/sqlalchemy/dialects/sqlite/aiosqlite.pyi
8188
stubs/SQLAlchemy/sqlalchemy/dialects/sqlite/base.pyi
89+
stubs/SQLAlchemy/sqlalchemy/dialects/sqlite/dml.pyi
8290
stubs/SQLAlchemy/sqlalchemy/dialects/sqlite/json.pyi
8391
stubs/SQLAlchemy/sqlalchemy/dialects/sqlite/pysqlcipher.pyi
8492
stubs/SQLAlchemy/sqlalchemy/dialects/sqlite/pysqlite.pyi
93+
stubs/SQLAlchemy/sqlalchemy/engine/cursor.pyi
94+
stubs/SQLAlchemy/sqlalchemy/engine/result.pyi
8595
stubs/SQLAlchemy/sqlalchemy/engine/row.pyi
96+
stubs/SQLAlchemy/sqlalchemy/ext/asyncio/result.pyi
97+
stubs/SQLAlchemy/sqlalchemy/ext/horizontal_shard.pyi
98+
stubs/SQLAlchemy/sqlalchemy/orm/attributes.pyi
99+
stubs/SQLAlchemy/sqlalchemy/orm/descriptor_props.pyi
100+
stubs/SQLAlchemy/sqlalchemy/orm/dynamic.pyi
101+
stubs/SQLAlchemy/sqlalchemy/orm/mapper.pyi
102+
stubs/SQLAlchemy/sqlalchemy/orm/query.pyi
103+
stubs/SQLAlchemy/sqlalchemy/orm/strategy_options.pyi
104+
stubs/SQLAlchemy/sqlalchemy/orm/util.pyi
105+
stubs/SQLAlchemy/sqlalchemy/sql/compiler.pyi
106+
stubs/SQLAlchemy/sqlalchemy/sql/crud.pyi
107+
stubs/SQLAlchemy/sqlalchemy/sql/ddl.pyi
108+
stubs/SQLAlchemy/sqlalchemy/sql/elements.pyi
109+
stubs/SQLAlchemy/sqlalchemy/sql/functions.pyi
110+
stubs/SQLAlchemy/sqlalchemy/sql/lambdas.pyi
111+
stubs/SQLAlchemy/sqlalchemy/sql/schema.pyi
112+
stubs/SQLAlchemy/sqlalchemy/sql/selectable.pyi

0 commit comments

Comments
 (0)