plugin.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896
  1. """Plugin system for extending mypy.
  2. At large scale the plugin system works as following:
  3. * Plugins are collected from the corresponding mypy config file option
  4. (either via paths to Python files, or installed Python modules)
  5. and imported using importlib.
  6. * Every module should get an entry point function (called 'plugin' by default,
  7. but may be overridden in the config file) that should accept a single string
  8. argument that is a full mypy version (includes git commit hash for dev
  9. versions) and return a subclass of mypy.plugins.Plugin.
  10. * All plugin class constructors should match the signature of mypy.plugin.Plugin
  11. (i.e. should accept an mypy.options.Options object), and *must* call
  12. super().__init__().
  13. * At several steps during semantic analysis and type checking mypy calls
  14. special `get_xxx` methods on user plugins with a single string argument that
  15. is a fully qualified name (full name) of a relevant definition
  16. (see mypy.plugin.Plugin method docstrings for details).
  17. * The plugins are called in the order they are passed in the config option.
  18. Every plugin must decide whether to act on a given full name. The first
  19. plugin that returns non-None object will be used.
  20. * The above decision should be made using the limited common API specified by
  21. mypy.plugin.CommonPluginApi.
  22. * The callback returned by the plugin will be called with a larger context that
  23. includes relevant current state (e.g. a default return type, or a default
  24. attribute type) and a wider relevant API provider (e.g.
  25. SemanticAnalyzerPluginInterface or CheckerPluginInterface).
  26. * The result of this is used for further processing. See various `XxxContext`
  27. named tuples for details about which information is given to each hook.
  28. Plugin developers should ensure that their plugins work well in incremental and
  29. daemon modes. In particular, plugins should not hold global state, and should
  30. always call add_plugin_dependency() in plugin hooks called during semantic
  31. analysis. See the method docstring for more details.
  32. There is no dedicated cache storage for plugins, but plugins can store
  33. per-TypeInfo data in a special .metadata attribute that is serialized to the
  34. mypy caches between incremental runs. To avoid collisions between plugins, they
  35. are encouraged to store their state under a dedicated key coinciding with
  36. plugin name in the metadata dictionary. Every value stored there must be
  37. JSON-serializable.
  38. ## Notes about the semantic analyzer
  39. Mypy 0.710 introduced a new semantic analyzer that changed how plugins are
  40. expected to work in several notable ways (from mypy 0.730 the old semantic
  41. analyzer is no longer available):
  42. 1. The order of processing AST nodes in modules is different. The old semantic
  43. analyzer processed modules in textual order, one module at a time. The new
  44. semantic analyzer first processes the module top levels, including bodies of
  45. any top-level classes and classes nested within classes. ("Top-level" here
  46. means "not nested within a function/method".) Functions and methods are
  47. processed only after module top levels have been finished. If there is an
  48. import cycle, all module top levels in the cycle are processed before
  49. processing any functions or methods. Each unit of processing (a module top
  50. level or a function/method) is called a *target*.
  51. This also means that function signatures in the same module have not been
  52. analyzed yet when analyzing the module top level. If you need access to
  53. a function signature, you'll need to explicitly analyze the signature first
  54. using `anal_type()`.
  55. 2. Each target can be processed multiple times. This may happen if some forward
  56. references are not ready yet, for example. This means that semantic analyzer
  57. related plugin hooks can be called multiple times for the same full name.
  58. These plugin methods must thus be idempotent.
  59. 3. The `anal_type` API function returns None if some part of the type is not
  60. available yet. If this happens, the current target being analyzed will be
  61. *deferred*, which means that it will be processed again soon, in the hope
  62. that additional dependencies will be available. This may happen if there are
  63. forward references to types or inter-module references to types within an
  64. import cycle.
  65. Note that if there is a circular definition, mypy may decide to stop
  66. processing to avoid an infinite number of iterations. When this happens,
  67. `anal_type` will generate an error and return an `AnyType` type object
  68. during the final iteration (instead of None).
  69. 4. There is a new API method `defer()`. This can be used to explicitly request
  70. the current target to be reprocessed one more time. You don't need this
  71. to call this if `anal_type` returns None, however.
  72. 5. There is a new API property `final_iteration`, which is true once mypy
  73. detected no progress during the previous iteration or if the maximum
  74. semantic analysis iteration count has been reached. You must never
  75. defer during the final iteration, as it will cause a crash.
  76. 6. The `node` attribute of SymbolTableNode objects may contain a reference to
  77. a PlaceholderNode object. This object means that this definition has not
  78. been fully processed yet. If you encounter a PlaceholderNode, you should
  79. defer unless it's the final iteration. If it's the final iteration, you
  80. should generate an error message. It usually means that there's a cyclic
  81. definition that cannot be resolved by mypy. PlaceholderNodes can only refer
  82. to references inside an import cycle. If you are looking up things from
  83. another module, such as the builtins, that is outside the current module or
  84. import cycle, you can safely assume that you won't receive a placeholder.
  85. When testing your plugin, you should have a test case that forces a module top
  86. level to be processed multiple times. The easiest way to do this is to include
  87. a forward reference to a class in a top-level annotation. Example:
  88. c: C # Forward reference causes second analysis pass
  89. class C: pass
  90. Note that a forward reference in a function signature won't trigger another
  91. pass, since all functions are processed only after the top level has been fully
  92. analyzed.
  93. You can use `api.options.new_semantic_analyzer` to check whether the new
  94. semantic analyzer is enabled (it's always true in mypy 0.730 and later).
  95. """
  96. from __future__ import annotations
  97. from abc import abstractmethod
  98. from typing import Any, Callable, NamedTuple, TypeVar
  99. from mypy_extensions import mypyc_attr, trait
  100. from mypy.errorcodes import ErrorCode
  101. from mypy.lookup import lookup_fully_qualified
  102. from mypy.message_registry import ErrorMessage
  103. from mypy.messages import MessageBuilder
  104. from mypy.nodes import (
  105. ArgKind,
  106. CallExpr,
  107. ClassDef,
  108. Context,
  109. Expression,
  110. MypyFile,
  111. SymbolTableNode,
  112. TypeInfo,
  113. )
  114. from mypy.options import Options
  115. from mypy.tvar_scope import TypeVarLikeScope
  116. from mypy.types import (
  117. CallableType,
  118. FunctionLike,
  119. Instance,
  120. ProperType,
  121. Type,
  122. TypeList,
  123. UnboundType,
  124. )
  125. @trait
  126. class TypeAnalyzerPluginInterface:
  127. """Interface for accessing semantic analyzer functionality in plugins.
  128. Methods docstrings contain only basic info. Look for corresponding implementation
  129. docstrings in typeanal.py for more details.
  130. """
  131. # An options object. Note: these are the cloned options for the current file.
  132. # This might be different from Plugin.options (that contains default/global options)
  133. # if there are per-file options in the config. This applies to all other interfaces
  134. # in this file.
  135. options: Options
  136. @abstractmethod
  137. def fail(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None:
  138. """Emit an error message at given location."""
  139. raise NotImplementedError
  140. @abstractmethod
  141. def named_type(self, name: str, args: list[Type]) -> Instance:
  142. """Construct an instance of a builtin type with given name."""
  143. raise NotImplementedError
  144. @abstractmethod
  145. def analyze_type(self, typ: Type) -> Type:
  146. """Analyze an unbound type using the default mypy logic."""
  147. raise NotImplementedError
  148. @abstractmethod
  149. def analyze_callable_args(
  150. self, arglist: TypeList
  151. ) -> tuple[list[Type], list[ArgKind], list[str | None]] | None:
  152. """Find types, kinds, and names of arguments from extended callable syntax."""
  153. raise NotImplementedError
  154. # A context for a hook that semantically analyzes an unbound type.
  155. class AnalyzeTypeContext(NamedTuple):
  156. type: UnboundType # Type to analyze
  157. context: Context # Relevant location context (e.g. for error messages)
  158. api: TypeAnalyzerPluginInterface
  159. @mypyc_attr(allow_interpreted_subclasses=True)
  160. class CommonPluginApi:
  161. """
  162. A common plugin API (shared between semantic analysis and type checking phases)
  163. that all plugin hooks get independently of the context.
  164. """
  165. # Global mypy options.
  166. # Per-file options can be only accessed on various
  167. # XxxPluginInterface classes.
  168. options: Options
  169. @abstractmethod
  170. def lookup_fully_qualified(self, fullname: str) -> SymbolTableNode | None:
  171. """Lookup a symbol by its full name (including module).
  172. This lookup function available for all plugins. Return None if a name
  173. is not found. This function doesn't support lookup from current scope.
  174. Use SemanticAnalyzerPluginInterface.lookup_qualified() for this."""
  175. raise NotImplementedError
  176. @trait
  177. class CheckerPluginInterface:
  178. """Interface for accessing type checker functionality in plugins.
  179. Methods docstrings contain only basic info. Look for corresponding implementation
  180. docstrings in checker.py for more details.
  181. """
  182. msg: MessageBuilder
  183. options: Options
  184. path: str
  185. # Type context for type inference
  186. @property
  187. @abstractmethod
  188. def type_context(self) -> list[Type | None]:
  189. """Return the type context of the plugin"""
  190. raise NotImplementedError
  191. @abstractmethod
  192. def fail(
  193. self, msg: str | ErrorMessage, ctx: Context, *, code: ErrorCode | None = None
  194. ) -> None:
  195. """Emit an error message at given location."""
  196. raise NotImplementedError
  197. @abstractmethod
  198. def named_generic_type(self, name: str, args: list[Type]) -> Instance:
  199. """Construct an instance of a builtin type with given type arguments."""
  200. raise NotImplementedError
  201. @trait
  202. class SemanticAnalyzerPluginInterface:
  203. """Interface for accessing semantic analyzer functionality in plugins.
  204. Methods docstrings contain only basic info. Look for corresponding implementation
  205. docstrings in semanal.py for more details.
  206. # TODO: clean-up lookup functions.
  207. """
  208. modules: dict[str, MypyFile]
  209. # Options for current file.
  210. options: Options
  211. cur_mod_id: str
  212. msg: MessageBuilder
  213. @abstractmethod
  214. def named_type(self, fullname: str, args: list[Type] | None = None) -> Instance:
  215. """Construct an instance of a builtin type with given type arguments."""
  216. raise NotImplementedError
  217. @abstractmethod
  218. def builtin_type(self, fully_qualified_name: str) -> Instance:
  219. """Legacy function -- use named_type() instead."""
  220. # NOTE: Do not delete this since many plugins may still use it.
  221. raise NotImplementedError
  222. @abstractmethod
  223. def named_type_or_none(self, fullname: str, args: list[Type] | None = None) -> Instance | None:
  224. """Construct an instance of a type with given type arguments.
  225. Return None if a type could not be constructed for the qualified
  226. type name. This is possible when the qualified name includes a
  227. module name and the module has not been imported.
  228. """
  229. raise NotImplementedError
  230. @abstractmethod
  231. def basic_new_typeinfo(self, name: str, basetype_or_fallback: Instance, line: int) -> TypeInfo:
  232. raise NotImplementedError
  233. @abstractmethod
  234. def parse_bool(self, expr: Expression) -> bool | None:
  235. """Parse True/False literals."""
  236. raise NotImplementedError
  237. @abstractmethod
  238. def parse_str_literal(self, expr: Expression) -> str | None:
  239. """Parse string literals."""
  240. @abstractmethod
  241. def fail(
  242. self,
  243. msg: str,
  244. ctx: Context,
  245. serious: bool = False,
  246. *,
  247. blocker: bool = False,
  248. code: ErrorCode | None = None,
  249. ) -> None:
  250. """Emit an error message at given location."""
  251. raise NotImplementedError
  252. @abstractmethod
  253. def anal_type(
  254. self,
  255. t: Type,
  256. *,
  257. tvar_scope: TypeVarLikeScope | None = None,
  258. allow_tuple_literal: bool = False,
  259. allow_unbound_tvars: bool = False,
  260. report_invalid_types: bool = True,
  261. third_pass: bool = False,
  262. ) -> Type | None:
  263. """Analyze an unbound type.
  264. Return None if some part of the type is not ready yet. In this
  265. case the current target being analyzed will be deferred and
  266. analyzed again.
  267. """
  268. raise NotImplementedError
  269. @abstractmethod
  270. def class_type(self, self_type: Type) -> Type:
  271. """Generate type of first argument of class methods from type of self."""
  272. raise NotImplementedError
  273. @abstractmethod
  274. def lookup_fully_qualified(self, name: str) -> SymbolTableNode:
  275. """Lookup a symbol by its fully qualified name.
  276. Raise an error if not found.
  277. """
  278. raise NotImplementedError
  279. @abstractmethod
  280. def lookup_fully_qualified_or_none(self, name: str) -> SymbolTableNode | None:
  281. """Lookup a symbol by its fully qualified name.
  282. Return None if not found.
  283. """
  284. raise NotImplementedError
  285. @abstractmethod
  286. def lookup_qualified(
  287. self, name: str, ctx: Context, suppress_errors: bool = False
  288. ) -> SymbolTableNode | None:
  289. """Lookup symbol using a name in current scope.
  290. This follows Python local->non-local->global->builtins rules.
  291. """
  292. raise NotImplementedError
  293. @abstractmethod
  294. def add_plugin_dependency(self, trigger: str, target: str | None = None) -> None:
  295. """Specify semantic dependencies for generated methods/variables.
  296. If the symbol with full name given by trigger is found to be stale by mypy,
  297. then the body of node with full name given by target will be re-checked.
  298. By default, this is the node that is currently analyzed.
  299. For example, the dataclass plugin adds a generated __init__ method with
  300. a signature that depends on types of attributes in ancestor classes. If any
  301. attribute in an ancestor class gets stale (modified), we need to reprocess
  302. the subclasses (and thus regenerate __init__ methods).
  303. This is used by fine-grained incremental mode (mypy daemon). See mypy/server/deps.py
  304. for more details.
  305. """
  306. raise NotImplementedError
  307. @abstractmethod
  308. def add_symbol_table_node(self, name: str, stnode: SymbolTableNode) -> Any:
  309. """Add node to global symbol table (or to nearest class if there is one)."""
  310. raise NotImplementedError
  311. @abstractmethod
  312. def qualified_name(self, n: str) -> str:
  313. """Make qualified name using current module and enclosing class (if any)."""
  314. raise NotImplementedError
  315. @abstractmethod
  316. def defer(self) -> None:
  317. """Call this to defer the processing of the current node.
  318. This will request an additional iteration of semantic analysis.
  319. """
  320. raise NotImplementedError
  321. @property
  322. @abstractmethod
  323. def final_iteration(self) -> bool:
  324. """Is this the final iteration of semantic analysis?"""
  325. raise NotImplementedError
  326. @property
  327. @abstractmethod
  328. def is_stub_file(self) -> bool:
  329. raise NotImplementedError
  330. @abstractmethod
  331. def analyze_simple_literal_type(self, rvalue: Expression, is_final: bool) -> Type | None:
  332. raise NotImplementedError
  333. # A context for querying for configuration data about a module for
  334. # cache invalidation purposes.
  335. class ReportConfigContext(NamedTuple):
  336. id: str # Module name
  337. path: str # Module file path
  338. is_check: bool # Is this invocation for checking whether the config matches
  339. # A context for a function signature hook that infers a better signature for a
  340. # function. Note that argument types aren't available yet. If you need them,
  341. # you have to use a method hook instead.
  342. class FunctionSigContext(NamedTuple):
  343. args: list[list[Expression]] # Actual expressions for each formal argument
  344. default_signature: CallableType # Original signature of the method
  345. context: Context # Relevant location context (e.g. for error messages)
  346. api: CheckerPluginInterface
  347. # A context for a function hook that infers the return type of a function with
  348. # a special signature.
  349. #
  350. # A no-op callback would just return the inferred return type, but a useful
  351. # callback at least sometimes can infer a more precise type.
  352. class FunctionContext(NamedTuple):
  353. arg_types: list[list[Type]] # List of actual caller types for each formal argument
  354. arg_kinds: list[list[ArgKind]] # Ditto for argument kinds, see nodes.ARG_* constants
  355. # Names of formal parameters from the callee definition,
  356. # these will be sufficient in most cases.
  357. callee_arg_names: list[str | None]
  358. # Names of actual arguments in the call expression. For example,
  359. # in a situation like this:
  360. # def func(**kwargs) -> None:
  361. # pass
  362. # func(kw1=1, kw2=2)
  363. # callee_arg_names will be ['kwargs'] and arg_names will be [['kw1', 'kw2']].
  364. arg_names: list[list[str | None]]
  365. default_return_type: Type # Return type inferred from signature
  366. args: list[list[Expression]] # Actual expressions for each formal argument
  367. context: Context # Relevant location context (e.g. for error messages)
  368. api: CheckerPluginInterface
  369. # A context for a method signature hook that infers a better signature for a
  370. # method. Note that argument types aren't available yet. If you need them,
  371. # you have to use a method hook instead.
  372. # TODO: document ProperType in the plugin changelog/update issue.
  373. class MethodSigContext(NamedTuple):
  374. type: ProperType # Base object type for method call
  375. args: list[list[Expression]] # Actual expressions for each formal argument
  376. default_signature: CallableType # Original signature of the method
  377. context: Context # Relevant location context (e.g. for error messages)
  378. api: CheckerPluginInterface
  379. # A context for a method hook that infers the return type of a method with a
  380. # special signature.
  381. #
  382. # This is very similar to FunctionContext (only differences are documented).
  383. class MethodContext(NamedTuple):
  384. type: ProperType # Base object type for method call
  385. arg_types: list[list[Type]] # List of actual caller types for each formal argument
  386. # see FunctionContext for details about names and kinds
  387. arg_kinds: list[list[ArgKind]]
  388. callee_arg_names: list[str | None]
  389. arg_names: list[list[str | None]]
  390. default_return_type: Type # Return type inferred by mypy
  391. args: list[list[Expression]] # Lists of actual expressions for every formal argument
  392. context: Context
  393. api: CheckerPluginInterface
  394. # A context for an attribute type hook that infers the type of an attribute.
  395. class AttributeContext(NamedTuple):
  396. type: ProperType # Type of object with attribute
  397. default_attr_type: Type # Original attribute type
  398. context: Context # Relevant location context (e.g. for error messages)
  399. api: CheckerPluginInterface
  400. # A context for a class hook that modifies the class definition.
  401. class ClassDefContext(NamedTuple):
  402. cls: ClassDef # The class definition
  403. reason: Expression # The expression being applied (decorator, metaclass, base class)
  404. api: SemanticAnalyzerPluginInterface
  405. # A context for dynamic class definitions like
  406. # Base = declarative_base()
  407. class DynamicClassDefContext(NamedTuple):
  408. call: CallExpr # The r.h.s. of dynamic class definition
  409. name: str # The name this class is being assigned to
  410. api: SemanticAnalyzerPluginInterface
  411. @mypyc_attr(allow_interpreted_subclasses=True)
  412. class Plugin(CommonPluginApi):
  413. """Base class of all type checker plugins.
  414. This defines a no-op plugin. Subclasses can override some methods to
  415. provide some actual functionality.
  416. All get_ methods are treated as pure functions (you should assume that
  417. results might be cached). A plugin should return None from a get_ method
  418. to give way to other plugins.
  419. Look at the comments of various *Context objects for additional information on
  420. various hooks.
  421. """
  422. def __init__(self, options: Options) -> None:
  423. self.options = options
  424. self.python_version = options.python_version
  425. # This can't be set in __init__ because it is executed too soon in build.py.
  426. # Therefore, build.py *must* set it later before graph processing starts
  427. # by calling set_modules().
  428. self._modules: dict[str, MypyFile] | None = None
  429. def set_modules(self, modules: dict[str, MypyFile]) -> None:
  430. self._modules = modules
  431. def lookup_fully_qualified(self, fullname: str) -> SymbolTableNode | None:
  432. assert self._modules is not None
  433. return lookup_fully_qualified(fullname, self._modules)
  434. def report_config_data(self, ctx: ReportConfigContext) -> Any:
  435. """Get representation of configuration data for a module.
  436. The data must be encodable as JSON and will be stored in the
  437. cache metadata for the module. A mismatch between the cached
  438. values and the returned will result in that module's cache
  439. being invalidated and the module being rechecked.
  440. This can be called twice for each module, once after loading
  441. the cache to check if it is valid and once while writing new
  442. cache information.
  443. If is_check in the context is true, then the return of this
  444. call will be checked against the cached version. Otherwise the
  445. call is being made to determine what to put in the cache. This
  446. can be used to allow consulting extra cache files in certain
  447. complex situations.
  448. This can be used to incorporate external configuration information
  449. that might require changes to typechecking.
  450. """
  451. return None
  452. def get_additional_deps(self, file: MypyFile) -> list[tuple[int, str, int]]:
  453. """Customize dependencies for a module.
  454. This hook allows adding in new dependencies for a module. It
  455. is called after parsing a file but before analysis. This can
  456. be useful if a library has dependencies that are dynamic based
  457. on configuration information, for example.
  458. Returns a list of (priority, module name, line number) tuples.
  459. The line number can be -1 when there is not a known real line number.
  460. Priorities are defined in mypy.build (but maybe shouldn't be).
  461. 10 is a good choice for priority.
  462. """
  463. return []
  464. def get_type_analyze_hook(self, fullname: str) -> Callable[[AnalyzeTypeContext], Type] | None:
  465. """Customize behaviour of the type analyzer for given full names.
  466. This method is called during the semantic analysis pass whenever mypy sees an
  467. unbound type. For example, while analysing this code:
  468. from lib import Special, Other
  469. var: Special
  470. def func(x: Other[int]) -> None:
  471. ...
  472. this method will be called with 'lib.Special', and then with 'lib.Other'.
  473. The callback returned by plugin must return an analyzed type,
  474. i.e. an instance of `mypy.types.Type`.
  475. """
  476. return None
  477. def get_function_signature_hook(
  478. self, fullname: str
  479. ) -> Callable[[FunctionSigContext], FunctionLike] | None:
  480. """Adjust the signature of a function.
  481. This method is called before type checking a function call. Plugin
  482. may infer a better type for the function.
  483. from lib import Class, do_stuff
  484. do_stuff(42)
  485. Class()
  486. This method will be called with 'lib.do_stuff' and then with 'lib.Class'.
  487. """
  488. return None
  489. def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None:
  490. """Adjust the return type of a function call.
  491. This method is called after type checking a call. Plugin may adjust the return
  492. type inferred by mypy, and/or emit some error messages. Note, this hook is also
  493. called for class instantiation calls, so that in this example:
  494. from lib import Class, do_stuff
  495. do_stuff(42)
  496. Class()
  497. This method will be called with 'lib.do_stuff' and then with 'lib.Class'.
  498. """
  499. return None
  500. def get_method_signature_hook(
  501. self, fullname: str
  502. ) -> Callable[[MethodSigContext], FunctionLike] | None:
  503. """Adjust the signature of a method.
  504. This method is called before type checking a method call. Plugin
  505. may infer a better type for the method. The hook is also called for special
  506. Python dunder methods except __init__ and __new__ (use get_function_hook to customize
  507. class instantiation). This function is called with the method full name using
  508. the class where it was _defined_. For example, in this code:
  509. from lib import Special
  510. class Base:
  511. def method(self, arg: Any) -> Any:
  512. ...
  513. class Derived(Base):
  514. ...
  515. var: Derived
  516. var.method(42)
  517. x: Special
  518. y = x[0]
  519. this method is called with '__main__.Base.method', and then with
  520. 'lib.Special.__getitem__'.
  521. """
  522. return None
  523. def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None:
  524. """Adjust return type of a method call.
  525. This is the same as get_function_hook(), but is called with the
  526. method full name (again, using the class where the method is defined).
  527. """
  528. return None
  529. def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None:
  530. """Adjust type of an instance attribute.
  531. This method is called with attribute full name using the class of the instance where
  532. the attribute was defined (or Var.info.fullname for generated attributes).
  533. For classes without __getattr__ or __getattribute__, this hook is only called for
  534. names of fields/properties (but not methods) that exist in the instance MRO.
  535. For classes that implement __getattr__ or __getattribute__, this hook is called
  536. for all fields/properties, including nonexistent ones (but still not methods).
  537. For example:
  538. class Base:
  539. x: Any
  540. def __getattr__(self, attr: str) -> Any: ...
  541. class Derived(Base):
  542. ...
  543. var: Derived
  544. var.x
  545. var.y
  546. get_attribute_hook is called with '__main__.Base.x' and '__main__.Base.y'.
  547. However, if we had not implemented __getattr__ on Base, you would only get
  548. the callback for 'var.x'; 'var.y' would produce an error without calling the hook.
  549. """
  550. return None
  551. def get_class_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None:
  552. """
  553. Adjust type of a class attribute.
  554. This method is called with attribute full name using the class where the attribute was
  555. defined (or Var.info.fullname for generated attributes).
  556. For example:
  557. class Cls:
  558. x: Any
  559. Cls.x
  560. get_class_attribute_hook is called with '__main__.Cls.x' as fullname.
  561. """
  562. return None
  563. def get_class_decorator_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None:
  564. """Update class definition for given class decorators.
  565. The plugin can modify a TypeInfo _in place_ (for example add some generated
  566. methods to the symbol table). This hook is called after the class body was
  567. semantically analyzed, but *there may still be placeholders* (typically
  568. caused by forward references).
  569. NOTE: Usually get_class_decorator_hook_2 is the better option, since it
  570. guarantees that there are no placeholders.
  571. The hook is called with full names of all class decorators.
  572. The hook can be called multiple times per class, so it must be
  573. idempotent.
  574. """
  575. return None
  576. def get_class_decorator_hook_2(
  577. self, fullname: str
  578. ) -> Callable[[ClassDefContext], bool] | None:
  579. """Update class definition for given class decorators.
  580. Similar to get_class_decorator_hook, but this runs in a later pass when
  581. placeholders have been resolved.
  582. The hook can return False if some base class hasn't been
  583. processed yet using class hooks. It causes all class hooks
  584. (that are run in this same pass) to be invoked another time for
  585. the file(s) currently being processed.
  586. The hook can be called multiple times per class, so it must be
  587. idempotent.
  588. """
  589. return None
  590. def get_metaclass_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None:
  591. """Update class definition for given declared metaclasses.
  592. Same as get_class_decorator_hook() but for metaclasses. Note:
  593. this hook will be only called for explicit metaclasses, not for
  594. inherited ones.
  595. TODO: probably it should also be called on inherited metaclasses.
  596. """
  597. return None
  598. def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None:
  599. """Update class definition for given base classes.
  600. Same as get_class_decorator_hook() but for base classes. Base classes
  601. don't need to refer to TypeInfos, if a base class refers to a variable with
  602. Any type, this hook will still be called.
  603. """
  604. return None
  605. def get_customize_class_mro_hook(
  606. self, fullname: str
  607. ) -> Callable[[ClassDefContext], None] | None:
  608. """Customize MRO for given classes.
  609. The plugin can modify the class MRO _in place_. This method is called
  610. with the class full name before its body was semantically analyzed.
  611. """
  612. return None
  613. def get_dynamic_class_hook(
  614. self, fullname: str
  615. ) -> Callable[[DynamicClassDefContext], None] | None:
  616. """Semantically analyze a dynamic class definition.
  617. This plugin hook allows one to semantically analyze dynamic class definitions like:
  618. from lib import dynamic_class
  619. X = dynamic_class('X', [])
  620. For such definition, this hook will be called with 'lib.dynamic_class'.
  621. The plugin should create the corresponding TypeInfo, and place it into a relevant
  622. symbol table, e.g. using ctx.api.add_symbol_table_node().
  623. """
  624. return None
  625. T = TypeVar("T")
  626. class ChainedPlugin(Plugin):
  627. """A plugin that represents a sequence of chained plugins.
  628. Each lookup method returns the hook for the first plugin that
  629. reports a match.
  630. This class should not be subclassed -- use Plugin as the base class
  631. for all plugins.
  632. """
  633. # TODO: Support caching of lookup results (through a LRU cache, for example).
  634. def __init__(self, options: Options, plugins: list[Plugin]) -> None:
  635. """Initialize chained plugin.
  636. Assume that the child plugins aren't mutated (results may be cached).
  637. """
  638. super().__init__(options)
  639. self._plugins = plugins
  640. def set_modules(self, modules: dict[str, MypyFile]) -> None:
  641. for plugin in self._plugins:
  642. plugin.set_modules(modules)
  643. def report_config_data(self, ctx: ReportConfigContext) -> Any:
  644. config_data = [plugin.report_config_data(ctx) for plugin in self._plugins]
  645. return config_data if any(x is not None for x in config_data) else None
  646. def get_additional_deps(self, file: MypyFile) -> list[tuple[int, str, int]]:
  647. deps = []
  648. for plugin in self._plugins:
  649. deps.extend(plugin.get_additional_deps(file))
  650. return deps
  651. def get_type_analyze_hook(self, fullname: str) -> Callable[[AnalyzeTypeContext], Type] | None:
  652. return self._find_hook(lambda plugin: plugin.get_type_analyze_hook(fullname))
  653. def get_function_signature_hook(
  654. self, fullname: str
  655. ) -> Callable[[FunctionSigContext], FunctionLike] | None:
  656. return self._find_hook(lambda plugin: plugin.get_function_signature_hook(fullname))
  657. def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None:
  658. return self._find_hook(lambda plugin: plugin.get_function_hook(fullname))
  659. def get_method_signature_hook(
  660. self, fullname: str
  661. ) -> Callable[[MethodSigContext], FunctionLike] | None:
  662. return self._find_hook(lambda plugin: plugin.get_method_signature_hook(fullname))
  663. def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None:
  664. return self._find_hook(lambda plugin: plugin.get_method_hook(fullname))
  665. def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None:
  666. return self._find_hook(lambda plugin: plugin.get_attribute_hook(fullname))
  667. def get_class_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None:
  668. return self._find_hook(lambda plugin: plugin.get_class_attribute_hook(fullname))
  669. def get_class_decorator_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None:
  670. return self._find_hook(lambda plugin: plugin.get_class_decorator_hook(fullname))
  671. def get_class_decorator_hook_2(
  672. self, fullname: str
  673. ) -> Callable[[ClassDefContext], bool] | None:
  674. return self._find_hook(lambda plugin: plugin.get_class_decorator_hook_2(fullname))
  675. def get_metaclass_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None:
  676. return self._find_hook(lambda plugin: plugin.get_metaclass_hook(fullname))
  677. def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None:
  678. return self._find_hook(lambda plugin: plugin.get_base_class_hook(fullname))
  679. def get_customize_class_mro_hook(
  680. self, fullname: str
  681. ) -> Callable[[ClassDefContext], None] | None:
  682. return self._find_hook(lambda plugin: plugin.get_customize_class_mro_hook(fullname))
  683. def get_dynamic_class_hook(
  684. self, fullname: str
  685. ) -> Callable[[DynamicClassDefContext], None] | None:
  686. return self._find_hook(lambda plugin: plugin.get_dynamic_class_hook(fullname))
  687. def _find_hook(self, lookup: Callable[[Plugin], T]) -> T | None:
  688. for plugin in self._plugins:
  689. hook = lookup(plugin)
  690. if hook:
  691. return hook
  692. return None