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