66from mypy .checker import TypeChecker
77from mypy .mro import calculate_mro
88from mypy .nodes import (
9- Block , CallExpr , ClassDef , Context , Expression , MemberExpr , MypyFile , NameExpr , PlaceholderNode , StrExpr ,
10- SymbolTable , SymbolTableNode , TypeInfo , Var ,
9+ GDEF , Argument , Block , CallExpr , ClassDef , Context , Expression , FuncDef , MemberExpr , MypyFile , NameExpr ,
10+ PlaceholderNode , StrExpr , SymbolTable , SymbolTableNode , TypeInfo , Var ,
1111)
1212from mypy .plugin import (
1313 AttributeContext , ClassDefContext , DynamicClassDefContext , FunctionContext , MethodContext ,
1414)
15+ from mypy .plugins .common import add_method_to_class
1516from mypy .semanal import SemanticAnalyzer
16- from mypy .types import AnyType , Instance , NoneTyp , ProperType
17+ from mypy .types import AnyType , CallableType , Instance , NoneTyp , ProperType
1718from mypy .types import Type as MypyType
1819from mypy .types import TypeOfAny , UnionType
1920
@@ -94,6 +95,80 @@ def lookup_typeinfo_or_defer(self, fullname: str, *,
9495
9596 return sym .node
9697
98+ def copy_method_to_another_class (
99+ self ,
100+ ctx : ClassDefContext ,
101+ self_type : Instance ,
102+ new_method_name : str ,
103+ method_node : FuncDef ) -> None :
104+ if method_node .type is None :
105+ if not self .defer_till_next_iteration (reason = 'method_node.type is None' ):
106+ raise new_helpers .TypeInfoNotFound (method_node .fullname )
107+
108+ arguments , return_type = build_unannotated_method_args (method_node )
109+ add_method_to_class (
110+ ctx .api ,
111+ ctx .cls ,
112+ new_method_name ,
113+ args = arguments ,
114+ return_type = return_type ,
115+ self_type = self_type )
116+ return
117+
118+ method_type = cast (CallableType , method_node .type )
119+ if not isinstance (method_type , CallableType ) and not self .defer_till_next_iteration (
120+ reason = 'method_node.type is not CallableType' ):
121+ raise new_helpers .TypeInfoNotFound (method_node .fullname )
122+
123+ arguments = []
124+ bound_return_type = self .semanal_api .anal_type (
125+ method_type .ret_type ,
126+ allow_placeholder = True )
127+
128+ if bound_return_type is None and self .defer_till_next_iteration ():
129+ raise new_helpers .TypeInfoNotFound (method_node .fullname + ' return type' )
130+
131+ assert bound_return_type is not None
132+
133+ if isinstance (bound_return_type , PlaceholderNode ):
134+ raise new_helpers .TypeInfoNotFound ('return type ' + method_node .fullname )
135+
136+ for arg_name , arg_type , original_argument in zip (
137+ method_type .arg_names [1 :],
138+ method_type .arg_types [1 :],
139+ method_node .arguments [1 :]):
140+ bound_arg_type = self .semanal_api .anal_type (arg_type , allow_placeholder = True )
141+ if bound_arg_type is None and not self .defer_till_next_iteration (reason = 'bound_arg_type is None' ):
142+ error_msg = 'of {} argument of {}' .format (arg_name , method_node .fullname )
143+ raise new_helpers .TypeInfoNotFound (error_msg )
144+
145+ assert bound_arg_type is not None
146+
147+ if isinstance (bound_arg_type , PlaceholderNode ) and self .defer_till_next_iteration (
148+ reason = 'bound_arg_type is None' ):
149+ raise new_helpers .TypeInfoNotFound ('of ' + arg_name + ' argument of ' + method_node .fullname )
150+
151+ var = Var (
152+ name = original_argument .variable .name ,
153+ type = arg_type )
154+ var .line = original_argument .variable .line
155+ var .column = original_argument .variable .column
156+ argument = Argument (
157+ variable = var ,
158+ type_annotation = bound_arg_type ,
159+ initializer = original_argument .initializer ,
160+ kind = original_argument .kind )
161+ argument .set_line (original_argument )
162+ arguments .append (argument )
163+
164+ add_method_to_class (
165+ ctx .api ,
166+ ctx .cls ,
167+ new_method_name ,
168+ args = arguments ,
169+ return_type = bound_return_type ,
170+ self_type = self_type )
171+
97172 def new_typeinfo (self , name : str , bases : List [Instance ], module_fullname : Optional [str ] = None ) -> TypeInfo :
98173 class_def = ClassDef (name , Block ([]))
99174 class_def .fullname = self .semanal_api .qualified_name (name )
@@ -118,11 +193,46 @@ def __call__(self, ctx: DynamicClassDefContext) -> None:
118193 self .semanal_api = cast (SemanticAnalyzer , ctx .api )
119194 self .create_new_dynamic_class ()
120195
196+ def generate_manager_info_and_module (self , base_manager_info : TypeInfo ) -> Tuple [TypeInfo , MypyFile ]:
197+ new_manager_info = self .semanal_api .basic_new_typeinfo (
198+ self .class_name ,
199+ basetype_or_fallback = Instance (
200+ base_manager_info ,
201+ [AnyType (TypeOfAny .unannotated )])
202+ )
203+ new_manager_info .line = self .call_expr .line
204+ new_manager_info .defn .line = self .call_expr .line
205+ new_manager_info .metaclass_type = new_manager_info .calculate_metaclass_type ()
206+
207+ current_module = self .semanal_api .cur_mod_node
208+ current_module .names [self .class_name ] = SymbolTableNode (
209+ GDEF ,
210+ new_manager_info ,
211+ plugin_generated = True )
212+ return new_manager_info , current_module
213+
121214 @abstractmethod
122215 def create_new_dynamic_class (self ) -> None :
123216 raise NotImplementedError
124217
125218
219+ class DynamicClassFromMethodCallback (DynamicClassPluginCallback ):
220+ callee : MemberExpr
221+
222+ def __call__ (self , ctx : DynamicClassDefContext ) -> None :
223+ self .class_name = ctx .name
224+ self .call_expr = ctx .call
225+
226+ assert ctx .call .callee is not None
227+ if not isinstance (ctx .call .callee , MemberExpr ):
228+ # throw error?
229+ return
230+ self .callee = ctx .call .callee
231+
232+ self .semanal_api = cast (SemanticAnalyzer , ctx .api )
233+ self .create_new_dynamic_class ()
234+
235+
126236class ClassDefPluginCallback (SemanalPluginCallback ):
127237 reason : Expression
128238 class_defn : ClassDef
@@ -396,3 +506,12 @@ def get_nested_meta_node_for_current_class(info: TypeInfo) -> Optional[TypeInfo]
396506 if metaclass_sym is not None and isinstance (metaclass_sym .node , TypeInfo ):
397507 return metaclass_sym .node
398508 return None
509+
510+
511+ def build_unannotated_method_args (method_node : FuncDef ) -> Tuple [List [Argument ], MypyType ]:
512+ prepared_arguments = []
513+ for argument in method_node .arguments [1 :]:
514+ argument .type_annotation = AnyType (TypeOfAny .unannotated )
515+ prepared_arguments .append (argument )
516+ return_type = AnyType (TypeOfAny .unannotated )
517+ return prepared_arguments , return_type
0 commit comments