|
9 | 9 |
|
10 | 10 | from mypy.nodes import ( |
11 | 11 | Expression, StrExpr, IntExpr, UnaryExpr, Context, DictExpr, ClassDef, |
12 | | - TypeInfo, SymbolTableNode, MypyFile |
| 12 | + TypeInfo, SymbolTableNode, MypyFile, CallExpr |
13 | 13 | ) |
14 | 14 | from mypy.tvar_scope import TypeVarScope |
15 | 15 | from mypy.types import ( |
@@ -69,6 +69,7 @@ class SemanticAnalyzerPluginInterface: |
69 | 69 |
|
70 | 70 | modules = None # type: Dict[str, MypyFile] |
71 | 71 | options = None # type: Options |
| 72 | + cur_mod_id = None # type: str |
72 | 73 | msg = None # type: MessageBuilder |
73 | 74 |
|
74 | 75 | @abstractmethod |
@@ -117,6 +118,15 @@ def lookup_qualified(self, name: str, ctx: Context, |
117 | 118 | def add_plugin_dependency(self, trigger: str, target: Optional[str] = None) -> None: |
118 | 119 | raise NotImplementedError |
119 | 120 |
|
| 121 | + @abstractmethod |
| 122 | + def add_symbol_table_node(self, name: str, stnode: SymbolTableNode) -> None: |
| 123 | + """Add node to global symbol table (or to nearest class if there is one).""" |
| 124 | + raise NotImplementedError |
| 125 | + |
| 126 | + @abstractmethod |
| 127 | + def qualified_name(self, n: str) -> str: |
| 128 | + raise NotImplementedError |
| 129 | + |
120 | 130 |
|
121 | 131 | # A context for a function hook that infers the return type of a function with |
122 | 132 | # a special signature. |
@@ -165,12 +175,21 @@ def add_plugin_dependency(self, trigger: str, target: Optional[str] = None) -> N |
165 | 175 |
|
166 | 176 | # A context for a class hook that modifies the class definition. |
167 | 177 | ClassDefContext = NamedTuple( |
168 | | - 'ClassDecoratorContext', [ |
| 178 | + 'ClassDefContext', [ |
169 | 179 | ('cls', ClassDef), # The class definition |
170 | 180 | ('reason', Expression), # The expression being applied (decorator, metaclass, base class) |
171 | 181 | ('api', SemanticAnalyzerPluginInterface) |
172 | 182 | ]) |
173 | 183 |
|
| 184 | +# A context for dynamic class definitions like |
| 185 | +# Base = declarative_base() |
| 186 | +DynamicClassDefContext = NamedTuple( |
| 187 | + 'DynamicClassDefContext', [ |
| 188 | + ('call', CallExpr), # The r.h.s. of dynamic class definition |
| 189 | + ('name', str), # The name this class is being assigned to |
| 190 | + ('api', SemanticAnalyzerPluginInterface) |
| 191 | + ]) |
| 192 | + |
174 | 193 |
|
175 | 194 | class Plugin: |
176 | 195 | """Base class of all type checker plugins. |
@@ -225,6 +244,10 @@ def get_customize_class_mro_hook(self, fullname: str |
225 | 244 | ) -> Optional[Callable[[ClassDefContext], None]]: |
226 | 245 | return None |
227 | 246 |
|
| 247 | + def get_dynamic_class_hook(self, fullname: str |
| 248 | + ) -> Optional[Callable[[DynamicClassDefContext], None]]: |
| 249 | + return None |
| 250 | + |
228 | 251 |
|
229 | 252 | T = TypeVar('T') |
230 | 253 |
|
@@ -280,6 +303,10 @@ def get_customize_class_mro_hook(self, fullname: str |
280 | 303 | ) -> Optional[Callable[[ClassDefContext], None]]: |
281 | 304 | return self.plugin.get_customize_class_mro_hook(fullname) |
282 | 305 |
|
| 306 | + def get_dynamic_class_hook(self, fullname: str |
| 307 | + ) -> Optional[Callable[[DynamicClassDefContext], None]]: |
| 308 | + return self.plugin.get_dynamic_class_hook(fullname) |
| 309 | + |
283 | 310 |
|
284 | 311 | class ChainedPlugin(Plugin): |
285 | 312 | """A plugin that represents a sequence of chained plugins. |
@@ -337,6 +364,10 @@ def get_customize_class_mro_hook(self, fullname: str |
337 | 364 | ) -> Optional[Callable[[ClassDefContext], None]]: |
338 | 365 | return self._find_hook(lambda plugin: plugin.get_customize_class_mro_hook(fullname)) |
339 | 366 |
|
| 367 | + def get_dynamic_class_hook(self, fullname: str |
| 368 | + ) -> Optional[Callable[[DynamicClassDefContext], None]]: |
| 369 | + return self._find_hook(lambda plugin: plugin.get_dynamic_class_hook(fullname)) |
| 370 | + |
340 | 371 | def _find_hook(self, lookup: Callable[[Plugin], T]) -> Optional[T]: |
341 | 372 | for plugin in self._plugins: |
342 | 373 | hook = lookup(plugin) |
|
0 commit comments