@@ -1091,12 +1091,18 @@ async def ast_while(self, arg):
10911091 async def ast_classdef (self , arg ):
10921092 """Evaluate class definition."""
10931093 bases = [(await self .aeval (base )) for base in arg .bases ]
1094+ keywords = {kw .arg : await self .aeval (kw .value ) for kw in arg .keywords }
1095+ metaclass = keywords .pop ("metaclass" , type (bases [0 ]) if bases else type )
1096+
10941097 if self .curr_func and arg .name in self .curr_func .global_names :
10951098 sym_table_assign = self .global_sym_table
10961099 else :
10971100 sym_table_assign = self .sym_table
10981101 sym_table_assign [arg .name ] = EvalLocalVar (arg .name )
1099- sym_table = {}
1102+ if hasattr (metaclass , "__prepare__" ):
1103+ sym_table = metaclass .__prepare__ (arg .name , tuple (bases ), ** keywords )
1104+ else :
1105+ sym_table = {}
11001106 self .sym_table_stack .append (self .sym_table )
11011107 self .sym_table = sym_table
11021108 for arg1 in arg .body :
@@ -1107,11 +1113,17 @@ async def ast_classdef(self, arg):
11071113 raise SyntaxError (f"{ val .name ()} statement outside loop" )
11081114 self .sym_table = self .sym_table_stack .pop ()
11091115
1116+ decorators = [await self .aeval (dec ) for dec in arg .decorator_list ]
11101117 sym_table ["__init__evalfunc_wrap__" ] = None
11111118 if "__init__" in sym_table :
11121119 sym_table ["__init__evalfunc_wrap__" ] = sym_table ["__init__" ]
11131120 del sym_table ["__init__" ]
1114- sym_table_assign [arg .name ].set (type (arg .name , tuple (bases ), sym_table ))
1121+ cls = metaclass (arg .name , tuple (bases ), sym_table , ** keywords )
1122+ if inspect .iscoroutine (cls ):
1123+ cls = await cls
1124+ for dec_func in reversed (decorators ):
1125+ cls = await self .call_func (dec_func , None , cls )
1126+ sym_table_assign [arg .name ].set (cls )
11151127
11161128 async def ast_functiondef (self , arg , async_func = False ):
11171129 """Evaluate function definition."""
@@ -1488,7 +1500,11 @@ async def ast_augassign(self, arg):
14881500 await self .recurse_assign (arg .target , new_val )
14891501
14901502 async def ast_annassign (self , arg ):
1491- """Execute type hint assignment statement (just ignore the type hint)."""
1503+ """Execute type hint assignment statement and track __annotations__."""
1504+ if isinstance (arg .target , ast .Name ):
1505+ annotations = self .sym_table .setdefault ("__annotations__" , {})
1506+ if arg .annotation :
1507+ annotations [arg .target .id ] = await self .aeval (arg .annotation )
14921508 if arg .value is not None :
14931509 rhs = await self .aeval (arg .value )
14941510 await self .recurse_assign (arg .target , rhs )
@@ -1962,19 +1978,25 @@ async def call_func(self, func, func_name, *args, **kwargs):
19621978 if isinstance (func , (EvalFunc , EvalFuncVar )):
19631979 return await func .call (self , * args , ** kwargs )
19641980 if inspect .isclass (func ) and hasattr (func , "__init__evalfunc_wrap__" ):
1965- inst = func ()
1981+ has_init_wrapper = getattr (func , "__init__evalfunc_wrap__" ) is not None
1982+ inst = func (* args , ** kwargs ) if not has_init_wrapper else func ()
19661983 #
19671984 # we use weak references when we bind the method calls to the instance inst;
19681985 # otherwise these self references cause the object to not be deleted until
19691986 # it is later garbage collected
19701987 #
19711988 inst_weak = weakref .ref (inst )
19721989 for name in dir (inst ):
1973- value = getattr (inst , name )
1990+ try :
1991+ value = getattr (inst , name )
1992+ except AttributeError :
1993+ # same effect as hasattr (which also catches AttributeError)
1994+ # dir() may list names that aren't actually accessible attributes
1995+ continue
19741996 if type (value ) is not EvalFuncVar :
19751997 continue
19761998 setattr (inst , name , EvalFuncVarClassInst (value .get_func (), value .get_ast_ctx (), inst_weak ))
1977- if getattr ( func , "__init__evalfunc_wrap__" ) is not None :
1999+ if has_init_wrapper :
19782000 #
19792001 # since our __init__ function is async, call the renamed one
19802002 #
0 commit comments