@@ -1305,66 +1305,17 @@ public static void InitializeForFinalization(CodeContext/*!*/ context, object ne
13051305 iwr . SetFinalizer ( new WeakRefTracker ( iwr , nif , nif ) ) ;
13061306 }
13071307
1308- internal static object ? CallPrepare ( CodeContext /*!*/ context , PythonType meta , string name , PythonTuple bases , PythonDictionary ? keywords , PythonDictionary dict ) {
1309- object ? classdict = dict ;
1310-
1311- // if available, call the __prepare__ method to get the classdict (PEP 3115)
1312- if ( meta . TryLookupSlot ( context , "__prepare__" , out PythonTypeSlot pts ) ) {
1313- if ( pts . TryGetValue ( context , null , meta , out object value ) ) {
1314- if ( keywords is null || keywords . Count == 0 ) {
1315- classdict = PythonOps . CallWithContext ( context , value , name , bases ) ;
1316- } else {
1317- var args = new object [ ] { name , bases } ;
1318- classdict = PythonCalls . CallWithKeywordArgs ( context , value , args , keywords ) ;
1319- }
1320- // copy the contents of dict to the classdict
1321- foreach ( var pair in dict )
1322- context . LanguageContext . SetIndex ( classdict , pair . Key , pair . Value ) ;
1323- }
1324- }
1325-
1326- return classdict ;
1327- }
1328-
13291308 public static object MakeClass ( FunctionCode funcCode , Func < CodeContext , CodeContext > body , CodeContext /*!*/ parentContext , string name , PythonTuple bases , PythonDictionary ? keywords , string selfNames ) {
13301309 Func < CodeContext , CodeContext > func = GetClassCode ( parentContext , funcCode , body ) ;
13311310
1332- object ? metaclass = null ;
1333- if ( keywords is not null && keywords . TryGetValueNoMissing ( "metaclass" , out metaclass ) ) {
1334- keywords . RemoveDirect ( "metaclass" ) ; // keyword argument consumed
1335- if ( metaclass is null ) {
1336- throw TypeError ( "metaclass cannot be 'None'" ) ; // CPython: 'NoneType' object is not callable
1337- }
1338- }
1339-
1340- return MakeClass ( parentContext , name , bases , metaclass , keywords , selfNames , func ( parentContext ) . Dict ) ;
1341- }
1342-
1343- private static Func < CodeContext , CodeContext > GetClassCode ( CodeContext /*!*/ context , FunctionCode funcCode , Func < CodeContext , CodeContext > body ) {
1344- if ( body == null ) {
1345- if ( funcCode . Target == null ) {
1346- funcCode . UpdateDelegate ( context . LanguageContext , true ) ;
1347- }
1348- return ( Func < CodeContext , CodeContext > ) funcCode . Target ! ;
1349- } else {
1350- if ( funcCode . Target == null ) {
1351- funcCode . SetTarget ( body ) ;
1352- funcCode . _normalDelegate = body ;
1353- }
1354- return body ;
1355- }
1356- }
1357-
1358- private static object MakeClass ( CodeContext /*!*/ context , string name , PythonTuple bases , object ? metaclass , PythonDictionary ? keywords , string selfNames , PythonDictionary vars ) {
1359- Debug . Assert ( metaclass is null || keywords is not null ) ;
1360-
1311+ // Check and normalize bases
13611312 foreach ( object ? dt in bases ) {
13621313 if ( dt is TypeGroup ) {
13631314 object ? [ ] newBases = new object [ bases . Count ] ;
13641315 for ( int i = 0 ; i < bases . Count ; i ++ ) {
13651316 if ( bases [ i ] is TypeGroup tc ) {
13661317 if ( ! tc . TryGetNonGenericType ( out Type nonGenericType ) ) {
1367- throw PythonOps . TypeError ( "cannot derive from open generic types {0}" , Repr ( context , tc ) ) ;
1318+ throw PythonOps . TypeError ( "cannot derive from open generic types {0}" , Repr ( parentContext , tc ) ) ;
13681319 }
13691320 newBases [ i ] = DynamicHelpers . GetPythonTypeFromType ( nonGenericType ) ;
13701321 } else {
@@ -1382,6 +1333,23 @@ private static object MakeClass(CodeContext/*!*/ context, string name, PythonTup
13821333 }
13831334 }
13841335
1336+ // Discover metaclass
1337+ object ? metaclass = null ;
1338+ if ( keywords is not null && keywords . TryGetValueNoMissing ( "metaclass" , out metaclass ) ) {
1339+ keywords . RemoveDirect ( "metaclass" ) ; // keyword argument consumed
1340+ if ( metaclass is null ) {
1341+ throw TypeError ( "metaclass cannot be 'None'" ) ; // CPython: 'NoneType' object is not callable
1342+ }
1343+ }
1344+
1345+ if ( metaclass is null or PythonType ) {
1346+ metaclass = PythonType . FindMetaClass ( ( PythonType ? ) metaclass ?? TypeCache . PythonType , bases ) ;
1347+ if ( metaclass == TypeCache . PythonType ) {
1348+ metaclass = null ; // enables fasttrack below
1349+ }
1350+ } // else metaclass is expected to be a callable and overrides any inherited metaclass through any bases
1351+
1352+ // Fasttrack for metaclass == `type`
13851353 if ( metaclass is null ) {
13861354 if ( keywords != null && keywords . Count > 0 ) {
13871355 throw TypeError ( "type() takes no keyword arguments" ) ;
@@ -1390,36 +1358,82 @@ private static object MakeClass(CodeContext/*!*/ context, string name, PythonTup
13901358 if ( bases . Count == 0 ) {
13911359 bases = PythonTuple . MakeTuple ( DynamicHelpers . GetPythonTypeFromType ( typeof ( object ) ) ) ;
13921360 }
1393- return PythonType . __new__ ( context , TypeCache . PythonType , name , bases , vars , selfNames ) ;
1361+ PythonDictionary vars = func ( parentContext ) . Dict ;
1362+ return PythonType . __new__ ( parentContext , TypeCache . PythonType , name , bases , vars , selfNames ) ;
13941363 }
13951364
1396- object ? classdict = vars ;
1397-
1398- if ( metaclass is PythonType metatype ) {
1399- classdict = CallPrepare ( context , metatype , name , bases , keywords , vars ) ;
1400- }
1365+ // Prepare classdict
1366+ // TODO: prepared classdict should be used by `func` (PEP 3115)
1367+ object ? classdict = CallPrepare ( parentContext , metaclass , name , bases , keywords , func ( parentContext ) . Dict ) ;
14011368
1402- // eg:
1403- // def foo(*args): print(args)
1404- // class bar(metaclass=foo): pass
1369+ // Dispatch to the metaclass to do class creation and initialization
1370+ // metaclass could be simply a callable, eg:
1371+ // >>> def foo(*args): print(args)
1372+ // >>> class bar(metaclass=foo): pass
14051373 // calls our function...
1406- PythonContext pc = context . LanguageContext ;
1374+ PythonContext pc = parentContext . LanguageContext ;
14071375
14081376 object obj = pc . MetaClassCallSite . Target (
14091377 pc . MetaClassCallSite ,
1410- context ,
1378+ parentContext ,
14111379 metaclass ,
14121380 name ,
14131381 bases ,
14141382 classdict ,
1415- keywords
1383+ keywords ?? MakeEmptyDict ( )
14161384 ) ;
14171385
1386+ // Ensure the class derives from `object`
14181387 if ( obj is PythonType newType && newType . BaseTypes . Count == 0 ) {
14191388 newType . BaseTypes . Add ( DynamicHelpers . GetPythonTypeFromType ( typeof ( object ) ) ) ;
14201389 }
14211390
14221391 return obj ;
1392+ // ------------------------------------------------------------------------
1393+
1394+ static Func < CodeContext , CodeContext > GetClassCode ( CodeContext /*!*/ context , FunctionCode funcCode , Func < CodeContext , CodeContext > body ) {
1395+ if ( body == null ) {
1396+ if ( funcCode . Target == null ) {
1397+ funcCode . UpdateDelegate ( context . LanguageContext , true ) ;
1398+ }
1399+ return ( Func < CodeContext , CodeContext > ) funcCode . Target ! ;
1400+ } else {
1401+ if ( funcCode . Target == null ) {
1402+ funcCode . SetTarget ( body ) ;
1403+ funcCode . _normalDelegate = body ;
1404+ }
1405+ return body ;
1406+ }
1407+ }
1408+
1409+ static object ? CallPrepare ( CodeContext /*!*/ context , object meta , string name , PythonTuple bases , PythonDictionary ? keywords , PythonDictionary dict ) {
1410+ object ? classdict = dict ;
1411+
1412+ object ? prepareFunc = null ;
1413+ // if available, call the __prepare__ method to get the classdict (PEP 3115)
1414+ if ( meta is PythonType metatype ) {
1415+ if ( metatype . TryResolveSlot ( context , "__prepare__" , out PythonTypeSlot pts ) ) {
1416+ bool ok = pts . TryGetValue ( context , null , metatype , out prepareFunc ) ;
1417+ Debug . Assert ( ok ) ;
1418+ }
1419+ } else if ( ! TryGetBoundAttr ( context , meta , "__prepare__" , out prepareFunc ) ) {
1420+ prepareFunc = null ;
1421+ }
1422+
1423+ if ( prepareFunc is not null ) {
1424+ if ( keywords is null || keywords . Count == 0 ) {
1425+ classdict = PythonCalls . Call ( context , prepareFunc , name , bases ) ;
1426+ } else {
1427+ var args = new object [ ] { name , bases } ;
1428+ classdict = PythonCalls . CallWithKeywordArgs ( context , prepareFunc , args , keywords ) ;
1429+ }
1430+ // copy the contents of dict to the classdict
1431+ foreach ( var pair in dict )
1432+ context . LanguageContext . SetIndex ( classdict , pair . Key , pair . Value ) ;
1433+ }
1434+
1435+ return classdict ;
1436+ }
14231437 }
14241438
14251439 public static void RaiseAssertionError ( CodeContext context ) {
0 commit comments