You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
7.5KB

  1. # ext/mypy/names.py
  2. # Copyright (C) 2021 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: http://www.opensource.org/licenses/mit-license.php
  7. from typing import Dict
  8. from typing import List
  9. from typing import Optional
  10. from typing import Set
  11. from typing import Tuple
  12. from typing import Union
  13. from mypy.nodes import ClassDef
  14. from mypy.nodes import Expression
  15. from mypy.nodes import FuncDef
  16. from mypy.nodes import MemberExpr
  17. from mypy.nodes import NameExpr
  18. from mypy.nodes import SymbolNode
  19. from mypy.nodes import TypeAlias
  20. from mypy.nodes import TypeInfo
  21. from mypy.plugin import SemanticAnalyzerPluginInterface
  22. from mypy.types import CallableType
  23. from mypy.types import get_proper_type
  24. from mypy.types import Instance
  25. from mypy.types import UnboundType
  26. from ... import util
  27. COLUMN: int = util.symbol("COLUMN") # type: ignore
  28. RELATIONSHIP: int = util.symbol("RELATIONSHIP") # type: ignore
  29. REGISTRY: int = util.symbol("REGISTRY") # type: ignore
  30. COLUMN_PROPERTY: int = util.symbol("COLUMN_PROPERTY") # type: ignore
  31. TYPEENGINE: int = util.symbol("TYPEENGNE") # type: ignore
  32. MAPPED: int = util.symbol("MAPPED") # type: ignore
  33. DECLARATIVE_BASE: int = util.symbol("DECLARATIVE_BASE") # type: ignore
  34. DECLARATIVE_META: int = util.symbol("DECLARATIVE_META") # type: ignore
  35. MAPPED_DECORATOR: int = util.symbol("MAPPED_DECORATOR") # type: ignore
  36. COLUMN_PROPERTY: int = util.symbol("COLUMN_PROPERTY") # type: ignore
  37. SYNONYM_PROPERTY: int = util.symbol("SYNONYM_PROPERTY") # type: ignore
  38. COMPOSITE_PROPERTY: int = util.symbol("COMPOSITE_PROPERTY") # type: ignore
  39. DECLARED_ATTR: int = util.symbol("DECLARED_ATTR") # type: ignore
  40. MAPPER_PROPERTY: int = util.symbol("MAPPER_PROPERTY") # type: ignore
  41. AS_DECLARATIVE: int = util.symbol("AS_DECLARATIVE") # type: ignore
  42. AS_DECLARATIVE_BASE: int = util.symbol("AS_DECLARATIVE_BASE") # type: ignore
  43. DECLARATIVE_MIXIN: int = util.symbol("DECLARATIVE_MIXIN") # type: ignore
  44. _lookup: Dict[str, Tuple[int, Set[str]]] = {
  45. "Column": (
  46. COLUMN,
  47. {
  48. "sqlalchemy.sql.schema.Column",
  49. "sqlalchemy.sql.Column",
  50. },
  51. ),
  52. "RelationshipProperty": (
  53. RELATIONSHIP,
  54. {
  55. "sqlalchemy.orm.relationships.RelationshipProperty",
  56. "sqlalchemy.orm.RelationshipProperty",
  57. },
  58. ),
  59. "registry": (
  60. REGISTRY,
  61. {
  62. "sqlalchemy.orm.decl_api.registry",
  63. "sqlalchemy.orm.registry",
  64. },
  65. ),
  66. "ColumnProperty": (
  67. COLUMN_PROPERTY,
  68. {
  69. "sqlalchemy.orm.properties.ColumnProperty",
  70. "sqlalchemy.orm.ColumnProperty",
  71. },
  72. ),
  73. "SynonymProperty": (
  74. SYNONYM_PROPERTY,
  75. {
  76. "sqlalchemy.orm.descriptor_props.SynonymProperty",
  77. "sqlalchemy.orm.SynonymProperty",
  78. },
  79. ),
  80. "CompositeProperty": (
  81. COMPOSITE_PROPERTY,
  82. {
  83. "sqlalchemy.orm.descriptor_props.CompositeProperty",
  84. "sqlalchemy.orm.CompositeProperty",
  85. },
  86. ),
  87. "MapperProperty": (
  88. MAPPER_PROPERTY,
  89. {
  90. "sqlalchemy.orm.interfaces.MapperProperty",
  91. "sqlalchemy.orm.MapperProperty",
  92. },
  93. ),
  94. "TypeEngine": (TYPEENGINE, {"sqlalchemy.sql.type_api.TypeEngine"}),
  95. "Mapped": (MAPPED, {"sqlalchemy.orm.attributes.Mapped"}),
  96. "declarative_base": (
  97. DECLARATIVE_BASE,
  98. {
  99. "sqlalchemy.ext.declarative.declarative_base",
  100. "sqlalchemy.orm.declarative_base",
  101. "sqlalchemy.orm.decl_api.declarative_base",
  102. },
  103. ),
  104. "DeclarativeMeta": (
  105. DECLARATIVE_META,
  106. {
  107. "sqlalchemy.ext.declarative.DeclarativeMeta",
  108. "sqlalchemy.orm.DeclarativeMeta",
  109. "sqlalchemy.orm.decl_api.DeclarativeMeta",
  110. },
  111. ),
  112. "mapped": (
  113. MAPPED_DECORATOR,
  114. {
  115. "sqlalchemy.orm.decl_api.registry.mapped",
  116. "sqlalchemy.orm.registry.mapped",
  117. },
  118. ),
  119. "as_declarative": (
  120. AS_DECLARATIVE,
  121. {
  122. "sqlalchemy.ext.declarative.as_declarative",
  123. "sqlalchemy.orm.decl_api.as_declarative",
  124. "sqlalchemy.orm.as_declarative",
  125. },
  126. ),
  127. "as_declarative_base": (
  128. AS_DECLARATIVE_BASE,
  129. {
  130. "sqlalchemy.orm.decl_api.registry.as_declarative_base",
  131. "sqlalchemy.orm.registry.as_declarative_base",
  132. },
  133. ),
  134. "declared_attr": (
  135. DECLARED_ATTR,
  136. {
  137. "sqlalchemy.orm.decl_api.declared_attr",
  138. "sqlalchemy.orm.declared_attr",
  139. },
  140. ),
  141. "declarative_mixin": (
  142. DECLARATIVE_MIXIN,
  143. {
  144. "sqlalchemy.orm.decl_api.declarative_mixin",
  145. "sqlalchemy.orm.declarative_mixin",
  146. },
  147. ),
  148. }
  149. def _has_base_type_id(info: TypeInfo, type_id: int) -> bool:
  150. for mr in info.mro:
  151. check_type_id, fullnames = _lookup.get(mr.name, (None, None))
  152. if check_type_id == type_id:
  153. break
  154. else:
  155. return False
  156. if fullnames is None:
  157. return False
  158. return mr.fullname in fullnames
  159. def _mro_has_id(mro: List[TypeInfo], type_id: int) -> bool:
  160. for mr in mro:
  161. check_type_id, fullnames = _lookup.get(mr.name, (None, None))
  162. if check_type_id == type_id:
  163. break
  164. else:
  165. return False
  166. if fullnames is None:
  167. return False
  168. return mr.fullname in fullnames
  169. def _type_id_for_unbound_type(
  170. type_: UnboundType, cls: ClassDef, api: SemanticAnalyzerPluginInterface
  171. ) -> Optional[int]:
  172. type_id = None
  173. sym = api.lookup_qualified(type_.name, type_)
  174. if sym is not None:
  175. if isinstance(sym.node, TypeAlias):
  176. target_type = get_proper_type(sym.node.target)
  177. if isinstance(target_type, Instance):
  178. type_id = _type_id_for_named_node(target_type.type)
  179. elif isinstance(sym.node, TypeInfo):
  180. type_id = _type_id_for_named_node(sym.node)
  181. return type_id
  182. def _type_id_for_callee(callee: Expression) -> Optional[int]:
  183. if isinstance(callee, (MemberExpr, NameExpr)):
  184. if isinstance(callee.node, FuncDef):
  185. return _type_id_for_funcdef(callee.node)
  186. elif isinstance(callee.node, TypeAlias):
  187. target_type = get_proper_type(callee.node.target)
  188. if isinstance(target_type, Instance):
  189. type_id = _type_id_for_fullname(target_type.type.fullname)
  190. elif isinstance(callee.node, TypeInfo):
  191. type_id = _type_id_for_named_node(callee)
  192. else:
  193. type_id = None
  194. return type_id
  195. def _type_id_for_funcdef(node: FuncDef) -> Optional[int]:
  196. if node.type and isinstance(node.type, CallableType):
  197. ret_type = get_proper_type(node.type.ret_type)
  198. if isinstance(ret_type, Instance):
  199. return _type_id_for_fullname(ret_type.type.fullname)
  200. return None
  201. def _type_id_for_named_node(
  202. node: Union[NameExpr, MemberExpr, SymbolNode]
  203. ) -> Optional[int]:
  204. type_id, fullnames = _lookup.get(node.name, (None, None))
  205. if type_id is None or fullnames is None:
  206. return None
  207. elif node.fullname in fullnames:
  208. return type_id
  209. else:
  210. return None
  211. def _type_id_for_fullname(fullname: str) -> Optional[int]:
  212. tokens = fullname.split(".")
  213. immediate = tokens[-1]
  214. type_id, fullnames = _lookup.get(immediate, (None, None))
  215. if type_id is None or fullnames is None:
  216. return None
  217. elif fullname in fullnames:
  218. return type_id
  219. else:
  220. return None