names.py 10 KB

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