codekingpro/portable-devtools
114k
1# ext/mypy/names.py
2# Copyright (C) 2021-2026 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
8from __future__ import annotations
9
10from typing import Dict
11from typing import List
12from typing import Optional
13from typing import Set
14from typing import Tuple
15from typing import Union
16
17from mypy.nodes import ARG_POS
18from mypy.nodes import CallExpr
19from mypy.nodes import ClassDef
20from mypy.nodes import Decorator
21from mypy.nodes import Expression
22from mypy.nodes import FuncDef
23from mypy.nodes import MemberExpr
24from mypy.nodes import NameExpr
25from mypy.nodes import OverloadedFuncDef
26from mypy.nodes import SymbolNode
27from mypy.nodes import TypeAlias
28from mypy.nodes import TypeInfo
29from mypy.plugin import SemanticAnalyzerPluginInterface
30from mypy.types import CallableType
31from mypy.types import get_proper_type
32from mypy.types import Instance
33from mypy.types import UnboundType
34
35from ... import util
36
37COLUMN: int = util.symbol("COLUMN")
38RELATIONSHIP: int = util.symbol("RELATIONSHIP")
39REGISTRY: int = util.symbol("REGISTRY")
40COLUMN_PROPERTY: int = util.symbol("COLUMN_PROPERTY")
41TYPEENGINE: int = util.symbol("TYPEENGNE")
42MAPPED: int = util.symbol("MAPPED")
43DECLARATIVE_BASE: int = util.symbol("DECLARATIVE_BASE")
44DECLARATIVE_META: int = util.symbol("DECLARATIVE_META")
45MAPPED_DECORATOR: int = util.symbol("MAPPED_DECORATOR")
46SYNONYM_PROPERTY: int = util.symbol("SYNONYM_PROPERTY")
47COMPOSITE_PROPERTY: int = util.symbol("COMPOSITE_PROPERTY")
48DECLARED_ATTR: int = util.symbol("DECLARED_ATTR")
49MAPPER_PROPERTY: int = util.symbol("MAPPER_PROPERTY")
50AS_DECLARATIVE: int = util.symbol("AS_DECLARATIVE")
51AS_DECLARATIVE_BASE: int = util.symbol("AS_DECLARATIVE_BASE")
52DECLARATIVE_MIXIN: int = util.symbol("DECLARATIVE_MIXIN")
53QUERY_EXPRESSION: int = util.symbol("QUERY_EXPRESSION")
54
55# names that must succeed with mypy.api.named_type
56NAMED_TYPE_BUILTINS_OBJECT = "builtins.object"
57NAMED_TYPE_BUILTINS_STR = "builtins.str"
58NAMED_TYPE_BUILTINS_LIST = "builtins.list"
59NAMED_TYPE_SQLA_MAPPED = "sqlalchemy.orm.base.Mapped"
60
61_RelFullNames = {
62 "sqlalchemy.orm.relationships.Relationship",
63 "sqlalchemy.orm.relationships.RelationshipProperty",
64 "sqlalchemy.orm.relationships._RelationshipDeclared",
65 "sqlalchemy.orm.Relationship",
66 "sqlalchemy.orm.RelationshipProperty",
67}
68
69_lookup: Dict[str, Tuple[int, Set[str]]] = {
70 "Column": (
71 COLUMN,
72 {
73 "sqlalchemy.sql.schema.Column",
74 "sqlalchemy.sql.Column",
75 },
76 ),
77 "Relationship": (RELATIONSHIP, _RelFullNames),
78 "RelationshipProperty": (RELATIONSHIP, _RelFullNames),
79 "_RelationshipDeclared": (RELATIONSHIP, _RelFullNames),
80 "registry": (
81 REGISTRY,
82 {
83 "sqlalchemy.orm.decl_api.registry",
84 "sqlalchemy.orm.registry",
85 },
86 ),
87 "ColumnProperty": (
88 COLUMN_PROPERTY,
89 {
90 "sqlalchemy.orm.properties.MappedSQLExpression",
91 "sqlalchemy.orm.MappedSQLExpression",
92 "sqlalchemy.orm.properties.ColumnProperty",
93 "sqlalchemy.orm.ColumnProperty",
94 },
95 ),
96 "MappedSQLExpression": (
97 COLUMN_PROPERTY,
98 {
99 "sqlalchemy.orm.properties.MappedSQLExpression",
100 "sqlalchemy.orm.MappedSQLExpression",
101 "sqlalchemy.orm.properties.ColumnProperty",
102 "sqlalchemy.orm.ColumnProperty",
103 },
104 ),
105 "Synonym": (
106 SYNONYM_PROPERTY,
107 {
108 "sqlalchemy.orm.descriptor_props.Synonym",
109 "sqlalchemy.orm.Synonym",
110 "sqlalchemy.orm.descriptor_props.SynonymProperty",
111 "sqlalchemy.orm.SynonymProperty",
112 },
113 ),
114 "SynonymProperty": (
115 SYNONYM_PROPERTY,
116 {
117 "sqlalchemy.orm.descriptor_props.Synonym",
118 "sqlalchemy.orm.Synonym",
119 "sqlalchemy.orm.descriptor_props.SynonymProperty",
120 "sqlalchemy.orm.SynonymProperty",
121 },
122 ),
123 "Composite": (
124 COMPOSITE_PROPERTY,
125 {
126 "sqlalchemy.orm.descriptor_props.Composite",
127 "sqlalchemy.orm.Composite",
128 "sqlalchemy.orm.descriptor_props.CompositeProperty",
129 "sqlalchemy.orm.CompositeProperty",
130 },
131 ),
132 "CompositeProperty": (
133 COMPOSITE_PROPERTY,
134 {
135 "sqlalchemy.orm.descriptor_props.Composite",
136 "sqlalchemy.orm.Composite",
137 "sqlalchemy.orm.descriptor_props.CompositeProperty",
138 "sqlalchemy.orm.CompositeProperty",
139 },
140 ),
141 "MapperProperty": (
142 MAPPER_PROPERTY,
143 {
144 "sqlalchemy.orm.interfaces.MapperProperty",
145 "sqlalchemy.orm.MapperProperty",
146 },
147 ),
148 "TypeEngine": (TYPEENGINE, {"sqlalchemy.sql.type_api.TypeEngine"}),
149 "Mapped": (MAPPED, {NAMED_TYPE_SQLA_MAPPED}),
150 "declarative_base": (
151 DECLARATIVE_BASE,
152 {
153 "sqlalchemy.ext.declarative.declarative_base",
154 "sqlalchemy.orm.declarative_base",
155 "sqlalchemy.orm.decl_api.declarative_base",
156 },
157 ),
158 "DeclarativeMeta": (
159 DECLARATIVE_META,
160 {
161 "sqlalchemy.ext.declarative.DeclarativeMeta",
162 "sqlalchemy.orm.DeclarativeMeta",
163 "sqlalchemy.orm.decl_api.DeclarativeMeta",
164 },
165 ),
166 "mapped": (
167 MAPPED_DECORATOR,
168 {
169 "sqlalchemy.orm.decl_api.registry.mapped",
170 "sqlalchemy.orm.registry.mapped",
171 },
172 ),
173 "as_declarative": (
174 AS_DECLARATIVE,
175 {
176 "sqlalchemy.ext.declarative.as_declarative",
177 "sqlalchemy.orm.decl_api.as_declarative",
178 "sqlalchemy.orm.as_declarative",
179 },
180 ),
181 "as_declarative_base": (
182 AS_DECLARATIVE_BASE,
183 {
184 "sqlalchemy.orm.decl_api.registry.as_declarative_base",
185 "sqlalchemy.orm.registry.as_declarative_base",
186 },
187 ),
188 "declared_attr": (
189 DECLARED_ATTR,
190 {
191 "sqlalchemy.orm.decl_api.declared_attr",
192 "sqlalchemy.orm.declared_attr",
193 },
194 ),
195 "declarative_mixin": (
196 DECLARATIVE_MIXIN,
197 {
198 "sqlalchemy.orm.decl_api.declarative_mixin",
199 "sqlalchemy.orm.declarative_mixin",
200 },
201 ),
202 "query_expression": (
203 QUERY_EXPRESSION,
204 {
205 "sqlalchemy.orm.query_expression",
206 "sqlalchemy.orm._orm_constructors.query_expression",
207 },
208 ),
209}
210
211
212def has_base_type_id(info: TypeInfo, type_id: int) -> bool:
213 for mr in info.mro:
214 check_type_id, fullnames = _lookup.get(mr.name, (None, None))
215 if check_type_id == type_id:
216 break
217 else:
218 return False
219
220 if fullnames is None:
221 return False
222
223 return mr.fullname in fullnames
224
225
226def mro_has_id(mro: List[TypeInfo], type_id: int) -> bool:
227 for mr in mro:
228 check_type_id, fullnames = _lookup.get(mr.name, (None, None))
229 if check_type_id == type_id:
230 break
231 else:
232 return False
233
234 if fullnames is None:
235 return False
236
237 return mr.fullname in fullnames
238
239
240def type_id_for_unbound_type(
241 type_: UnboundType, cls: ClassDef, api: SemanticAnalyzerPluginInterface
242) -> Optional[int]:
243 sym = api.lookup_qualified(type_.name, type_)
244 if sym is not None:
245 if isinstance(sym.node, TypeAlias):
246 target_type = get_proper_type(sym.node.target)
247 if isinstance(target_type, Instance):
248 return type_id_for_named_node(target_type.type)
249 elif isinstance(sym.node, TypeInfo):
250 return type_id_for_named_node(sym.node)
251
252 return None
253
254
255def type_id_for_callee(callee: Expression) -> Optional[int]:
256 if isinstance(callee, (MemberExpr, NameExpr)):
257 if isinstance(callee.node, Decorator) and isinstance(
258 callee.node.func, FuncDef
259 ):
260 if callee.node.func.type and isinstance(
261 callee.node.func.type, CallableType
262 ):
263 ret_type = get_proper_type(callee.node.func.type.ret_type)
264
265 if isinstance(ret_type, Instance):
266 return type_id_for_fullname(ret_type.type.fullname)
267
268 return None
269
270 elif isinstance(callee.node, OverloadedFuncDef):
271 if (
272 callee.node.impl
273 and callee.node.impl.type
274 and isinstance(callee.node.impl.type, CallableType)
275 ):
276 ret_type = get_proper_type(callee.node.impl.type.ret_type)
277
278 if isinstance(ret_type, Instance):
279 return type_id_for_fullname(ret_type.type.fullname)
280
281 return None
282 elif isinstance(callee.node, FuncDef):
283 if callee.node.type and isinstance(callee.node.type, CallableType):
284 ret_type = get_proper_type(callee.node.type.ret_type)
285
286 if isinstance(ret_type, Instance):
287 return type_id_for_fullname(ret_type.type.fullname)
288
289 return None
290 elif isinstance(callee.node, TypeAlias):
291 target_type = get_proper_type(callee.node.target)
292 if isinstance(target_type, Instance):
293 return type_id_for_fullname(target_type.type.fullname)
294 elif isinstance(callee.node, TypeInfo):
295 return type_id_for_named_node(callee)
296 return None
297
298
299def type_id_for_named_node(
300 node: Union[NameExpr, MemberExpr, SymbolNode],
301) -> Optional[int]:
302 type_id, fullnames = _lookup.get(node.name, (None, None))
303
304 if type_id is None or fullnames is None:
305 return None
306 elif node.fullname in fullnames:
307 return type_id
308 else:
309 return None
310
311
312def type_id_for_fullname(fullname: str) -> Optional[int]:
313 tokens = fullname.split(".")
314 immediate = tokens[-1]
315
316 type_id, fullnames = _lookup.get(immediate, (None, None))
317
318 if type_id is None or fullnames is None:
319 return None
320 elif fullname in fullnames:
321 return type_id
322 else:
323 return None
324
325
326def expr_to_mapped_constructor(expr: Expression) -> CallExpr:
327 column_descriptor = NameExpr("__sa_Mapped")
328 column_descriptor.fullname = NAMED_TYPE_SQLA_MAPPED
329 member_expr = MemberExpr(column_descriptor, "_empty_constructor")
330 return CallExpr(
331 member_expr,
332 [expr],
333 [ARG_POS],
334 ["arg1"],
335 )
336 