Commit c0c8f92b authored by Stefan Behnel's avatar Stefan Behnel

implement metaclass calculation/validation algorithm, make classes inherit their parents' metaclass

parent 25b78a7a
...@@ -52,6 +52,16 @@ Features added ...@@ -52,6 +52,16 @@ Features added
Bugs fixed Bugs fixed
---------- ----------
* The metaclass of a Python class was not inherited from its parent
class(es). It is now extracted from the list of base classes if not
provided explicitly using the Py3 ``metaclass`` keyword argument.
In Py2 compilation mode, a ``__metaclass__`` entry in the class
dict will still take precedence if not using Py3 metaclass syntax,
but only *after* creating the class dict (which may have been done
by a metaclass of a base class, see PEP 3115). It is generally
recommended to use the explicit Py3 syntax to define metaclasses
for Python types at compile time.
* The automatic C switch statement generation behaves more safely for * The automatic C switch statement generation behaves more safely for
heterogeneous value types (e.g. mixing enum and char), allowing for heterogeneous value types (e.g. mixing enum and char), allowing for
a slightly wider application and reducing corner cases. It now always a slightly wider application and reducing corner cases. It now always
...@@ -61,6 +71,9 @@ Bugs fixed ...@@ -61,6 +71,9 @@ Bugs fixed
Other changes Other changes
------------- -------------
* In Py3 compilation mode, Python2-style metaclasses declared by a
``__metaclass__`` class dict entry are ignored.
* In Py3.4+, the Cython generator type uses ``tp_finalize()`` for safer * In Py3.4+, the Cython generator type uses ``tp_finalize()`` for safer
cleanup instead of ``tp_del()``. cleanup instead of ``tp_del()``.
......
...@@ -6782,6 +6782,8 @@ class Py3ClassNode(ExprNode): ...@@ -6782,6 +6782,8 @@ class Py3ClassNode(ExprNode):
# name EncodedString Name of the class # name EncodedString Name of the class
# dict ExprNode Class dict (not owned by this node) # dict ExprNode Class dict (not owned by this node)
# module_name EncodedString Name of defining module # module_name EncodedString Name of defining module
# calculate_metaclass bool should call CalculateMetaclass()
# allow_py2_metaclass bool should look for Py2 metaclass
subexprs = [] subexprs = []
...@@ -6798,14 +6800,20 @@ class Py3ClassNode(ExprNode): ...@@ -6798,14 +6800,20 @@ class Py3ClassNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.globalstate.use_utility_code(UtilityCode.load_cached("Py3ClassCreate", "ObjectHandling.c")) code.globalstate.use_utility_code(UtilityCode.load_cached("Py3ClassCreate", "ObjectHandling.c"))
cname = code.intern_identifier(self.name) cname = code.intern_identifier(self.name)
if self.mkw:
mkw = self.mkw.py_result()
else:
mkw = 'NULL'
code.putln( code.putln(
'%s = __Pyx_Py3ClassCreate(%s, %s, %s, %s, %s); %s' % ( '%s = __Pyx_Py3ClassCreate(%s, %s, %s, %s, %s, %d, %d); %s' % (
self.result(), self.result(),
self.metaclass.result(), self.metaclass.result(),
cname, cname,
self.bases.py_result(), self.bases.py_result(),
self.dict.py_result(), self.dict.py_result(),
self.mkw.py_result(), mkw,
self.calculate_metaclass,
self.allow_py2_metaclass,
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
...@@ -6941,12 +6949,20 @@ class PyClassMetaclassNode(ExprNode): ...@@ -6941,12 +6949,20 @@ class PyClassMetaclassNode(ExprNode):
return True return True
def generate_result_code(self, code): def generate_result_code(self, code):
code.globalstate.use_utility_code(UtilityCode.load_cached("Py3MetaclassGet", "ObjectHandling.c")) if self.mkw:
code.putln( code.globalstate.use_utility_code(
"%s = __Pyx_Py3MetaclassGet(%s, %s); %s" % ( UtilityCode.load_cached("Py3MetaclassGet", "ObjectHandling.c"))
self.result(), call = "__Pyx_Py3MetaclassGet(%s, %s)" % (
self.bases.result(), self.bases.result(),
self.mkw.result(), self.mkw.result())
else:
code.globalstate.use_utility_code(
UtilityCode.load_cached("CalculateMetaclass", "ObjectHandling.c"))
call = "__Pyx_CalculateMetaclass(NULL, %s)" % (
self.bases.result())
code.putln(
"%s = %s; %s" % (
self.result(), call,
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
...@@ -6983,6 +6999,10 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin): ...@@ -6983,6 +6999,10 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
doc_code = self.doc.result() doc_code = self.doc.result()
else: else:
doc_code = '(PyObject *) NULL' doc_code = '(PyObject *) NULL'
if self.mkw:
mkw = self.mkw.py_result()
else:
mkw = '(PyObject *) NULL'
code.putln( code.putln(
"%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % ( "%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % (
self.result(), self.result(),
...@@ -6990,7 +7010,7 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin): ...@@ -6990,7 +7010,7 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
self.bases.result(), self.bases.result(),
cname, cname,
qualname, qualname,
self.mkw.result(), mkw,
py_mod_name, py_mod_name,
doc_code, doc_code,
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
......
...@@ -3915,25 +3915,29 @@ class PyClassDefNode(ClassDefNode): ...@@ -3915,25 +3915,29 @@ class PyClassDefNode(ClassDefNode):
"target", "class_cell", "decorators"] "target", "class_cell", "decorators"]
decorators = None decorators = None
class_result = None class_result = None
py3_style_class = False # Python3 style class (bases+kwargs) is_py3_style_class = False # Python3 style class (kwargs)
metaclass = None
mkw = None
def __init__(self, pos, name, bases, doc, body, decorators = None, def __init__(self, pos, name, bases, doc, body, decorators=None,
keyword_args = None, starstar_arg = None): keyword_args=None, starstar_arg=None, force_py3_semantics=False):
StatNode.__init__(self, pos) StatNode.__init__(self, pos)
self.name = name self.name = name
self.doc = doc self.doc = doc
self.body = body self.body = body
self.decorators = decorators self.decorators = decorators
self.bases = bases
import ExprNodes import ExprNodes
if self.doc and Options.docstrings: if self.doc and Options.docstrings:
doc = embed_position(self.pos, self.doc) doc = embed_position(self.pos, self.doc)
doc_node = ExprNodes.StringNode(pos, value = doc) doc_node = ExprNodes.StringNode(pos, value=doc)
else: else:
doc_node = None doc_node = None
allow_py2_metaclass = not force_py3_semantics
if keyword_args or starstar_arg: if keyword_args or starstar_arg:
self.py3_style_class = True allow_py2_metaclass = False
self.bases = bases self.is_py3_style_class = True
self.metaclass = None
if keyword_args and not starstar_arg: if keyword_args and not starstar_arg:
for i, item in list(enumerate(keyword_args.key_value_pairs))[::-1]: for i, item in list(enumerate(keyword_args.key_value_pairs))[::-1]:
if item.key.value == 'metaclass': if item.key.value == 'metaclass':
...@@ -3946,36 +3950,50 @@ class PyClassDefNode(ClassDefNode): ...@@ -3946,36 +3950,50 @@ class PyClassDefNode(ClassDefNode):
del keyword_args.key_value_pairs[i] del keyword_args.key_value_pairs[i]
if starstar_arg: if starstar_arg:
self.mkw = ExprNodes.KeywordArgsNode( self.mkw = ExprNodes.KeywordArgsNode(
pos, keyword_args = keyword_args and keyword_args.key_value_pairs or [], pos, keyword_args=keyword_args and keyword_args.key_value_pairs or [],
starstar_arg = starstar_arg) starstar_arg=starstar_arg)
elif keyword_args and keyword_args.key_value_pairs: elif keyword_args.key_value_pairs:
self.mkw = keyword_args self.mkw = keyword_args
else: else:
self.mkw = ExprNodes.NullNode(pos) assert self.metaclass is not None
if force_py3_semantics or self.bases or self.mkw or self.metaclass:
if self.metaclass is None: if self.metaclass is None:
if starstar_arg:
# **kwargs may contain 'metaclass' arg
mkdict = self.mkw
else:
mkdict = None
self.metaclass = ExprNodes.PyClassMetaclassNode( self.metaclass = ExprNodes.PyClassMetaclassNode(
pos, mkw = self.mkw, bases = self.bases) pos, mkw=mkdict, bases=self.bases)
self.dict = ExprNodes.PyClassNamespaceNode(pos, name = name, needs_metaclass_calculation = False
doc = doc_node, metaclass = self.metaclass, bases = self.bases, else:
mkw = self.mkw) needs_metaclass_calculation = True
self.classobj = ExprNodes.Py3ClassNode(pos, name = name,
bases = self.bases, dict = self.dict, doc = doc_node, self.dict = ExprNodes.PyClassNamespaceNode(
metaclass = self.metaclass, mkw = self.mkw) pos, name=name, doc=doc_node,
else: metaclass=self.metaclass, bases=self.bases, mkw=self.mkw)
self.dict = ExprNodes.DictNode(pos, key_value_pairs = []) self.classobj = ExprNodes.Py3ClassNode(
self.metaclass = None pos, name=name,
self.mkw = None bases=self.bases, dict=self.dict, doc=doc_node,
self.bases = None metaclass=self.metaclass, mkw=self.mkw,
self.classobj = ExprNodes.ClassNode(pos, name = name, calculate_metaclass=needs_metaclass_calculation,
bases = bases, dict = self.dict, doc = doc_node) allow_py2_metaclass=allow_py2_metaclass)
self.target = ExprNodes.NameNode(pos, name = name) else:
# no bases, no metaclass => old style class creation
self.dict = ExprNodes.DictNode(pos, key_value_pairs=[])
self.classobj = ExprNodes.ClassNode(
pos, name=name,
bases=bases, dict=self.dict, doc=doc_node)
self.target = ExprNodes.NameNode(pos, name=name)
self.class_cell = ExprNodes.ClassCellInjectorNode(self.pos) self.class_cell = ExprNodes.ClassCellInjectorNode(self.pos)
def as_cclass(self): def as_cclass(self):
""" """
Return this node as if it were declared as an extension class Return this node as if it were declared as an extension class
""" """
if self.py3_style_class: if self.is_py3_style_class:
error(self.classobj.pos, "Python3 style class could not be represented as C class") error(self.classobj.pos, "Python3 style class could not be represented as C class")
return return
bases = self.classobj.bases.args bases = self.classobj.bases.args
...@@ -4039,9 +4057,11 @@ class PyClassDefNode(ClassDefNode): ...@@ -4039,9 +4057,11 @@ class PyClassDefNode(ClassDefNode):
self.body.analyse_declarations(cenv) self.body.analyse_declarations(cenv)
def analyse_expressions(self, env): def analyse_expressions(self, env):
if self.py3_style_class: if self.bases:
self.bases = self.bases.analyse_expressions(env) self.bases = self.bases.analyse_expressions(env)
if self.metaclass:
self.metaclass = self.metaclass.analyse_expressions(env) self.metaclass = self.metaclass.analyse_expressions(env)
if self.mkw:
self.mkw = self.mkw.analyse_expressions(env) self.mkw = self.mkw.analyse_expressions(env)
self.dict = self.dict.analyse_expressions(env) self.dict = self.dict.analyse_expressions(env)
self.class_result = self.class_result.analyse_expressions(env) self.class_result = self.class_result.analyse_expressions(env)
...@@ -4059,9 +4079,11 @@ class PyClassDefNode(ClassDefNode): ...@@ -4059,9 +4079,11 @@ class PyClassDefNode(ClassDefNode):
def generate_execution_code(self, code): def generate_execution_code(self, code):
code.pyclass_stack.append(self) code.pyclass_stack.append(self)
cenv = self.scope cenv = self.scope
if self.py3_style_class: if self.bases:
self.bases.generate_evaluation_code(code) self.bases.generate_evaluation_code(code)
if self.mkw:
self.mkw.generate_evaluation_code(code) self.mkw.generate_evaluation_code(code)
if self.metaclass:
self.metaclass.generate_evaluation_code(code) self.metaclass.generate_evaluation_code(code)
self.dict.generate_evaluation_code(code) self.dict.generate_evaluation_code(code)
cenv.namespace_cname = cenv.class_obj_cname = self.dict.result() cenv.namespace_cname = cenv.class_obj_cname = self.dict.result()
...@@ -4075,11 +4097,13 @@ class PyClassDefNode(ClassDefNode): ...@@ -4075,11 +4097,13 @@ class PyClassDefNode(ClassDefNode):
self.target.generate_assignment_code(self.class_result, code) self.target.generate_assignment_code(self.class_result, code)
self.dict.generate_disposal_code(code) self.dict.generate_disposal_code(code)
self.dict.free_temps(code) self.dict.free_temps(code)
if self.py3_style_class: if self.metaclass:
self.mkw.generate_disposal_code(code)
self.mkw.free_temps(code)
self.metaclass.generate_disposal_code(code) self.metaclass.generate_disposal_code(code)
self.metaclass.free_temps(code) self.metaclass.free_temps(code)
if self.mkw:
self.mkw.generate_disposal_code(code)
self.mkw.free_temps(code)
if self.bases:
self.bases.generate_disposal_code(code) self.bases.generate_disposal_code(code)
self.bases.free_temps(code) self.bases.free_temps(code)
code.pyclass_stack.pop() code.pyclass_stack.pop()
......
...@@ -2962,12 +2962,13 @@ def p_class_statement(s, decorators): ...@@ -2962,12 +2962,13 @@ def p_class_statement(s, decorators):
# XXX: empty arg_tuple # XXX: empty arg_tuple
arg_tuple = ExprNodes.TupleNode(pos, args=[]) arg_tuple = ExprNodes.TupleNode(pos, args=[])
doc, body = p_suite_with_docstring(s, Ctx(level='class')) doc, body = p_suite_with_docstring(s, Ctx(level='class'))
return Nodes.PyClassDefNode(pos, return Nodes.PyClassDefNode(
name = class_name, pos, name=class_name,
bases = arg_tuple, bases=arg_tuple,
keyword_args = keyword_dict, keyword_args=keyword_dict,
starstar_arg = starstar_arg, starstar_arg=starstar_arg,
doc = doc, body = body, decorators = decorators) doc=doc, body=body, decorators=decorators,
force_py3_semantics=s.context.language_level >= 3)
def p_c_class_definition(s, pos, ctx): def p_c_class_definition(s, pos, ctx):
# s.sy == 'class' # s.sy == 'class'
......
...@@ -663,38 +663,93 @@ static CYTHON_INLINE PyObject* __Pyx_Py{{type}}_GetSlice( ...@@ -663,38 +663,93 @@ static CYTHON_INLINE PyObject* __Pyx_Py{{type}}_GetSlice(
{{endfor}} {{endfor}}
#endif #endif
/////////////// FindPy2Metaclass.proto ///////////////
static PyObject *__Pyx_FindPy2Metaclass(PyObject *bases); /*proto*/ /////////////// CalculateMetaclass.proto ///////////////
/////////////// FindPy2Metaclass /////////////// static PyObject *__Pyx_CalculateMetaclass(PyTypeObject *metaclass, PyObject *bases);
/////////////// CalculateMetaclass ///////////////
static PyObject *__Pyx_CalculateMetaclass(PyTypeObject *metaclass, PyObject *bases) {
Py_ssize_t i, nbases = PyTuple_GET_SIZE(bases);
for (i=0; i < nbases; i++) {
PyTypeObject *tmptype;
PyObject *tmp = PyTuple_GET_ITEM(bases, i);
tmptype = Py_TYPE(tmp);
#if PY_MAJOR_VERSION < 3
if (tmptype == &PyClass_Type)
continue;
#endif
if (!metaclass) {
metaclass = tmptype;
continue;
}
if (PyType_IsSubtype(metaclass, tmptype))
continue;
if (PyType_IsSubtype(tmptype, metaclass)) {
metaclass = tmptype;
continue;
}
// else:
PyErr_SetString(PyExc_TypeError,
"metaclass conflict: "
"the metaclass of a derived class "
"must be a (non-strict) subclass "
"of the metaclasses of all its bases");
return NULL;
}
if (!metaclass) {
#if PY_MAJOR_VERSION < 3
metaclass = &PyClass_Type;
#else
metaclass = &PyType_Type;
#endif
}
// make owned reference
Py_INCREF((PyObject*) metaclass);
return (PyObject*) metaclass;
}
/////////////// FindInheritedMetaclass.proto ///////////////
static PyObject *__Pyx_FindInheritedMetaclass(PyObject *bases); /*proto*/
/////////////// FindInheritedMetaclass ///////////////
//@requires: PyObjectGetAttrStr //@requires: PyObjectGetAttrStr
//@requires: CalculateMetaclass
static PyObject *__Pyx_FindPy2Metaclass(PyObject *bases) { static PyObject *__Pyx_FindInheritedMetaclass(PyObject *bases) {
PyObject *metaclass; PyObject *metaclass;
/* Default metaclass */
#if PY_MAJOR_VERSION < 3
if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) { if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {
PyTypeObject *metatype;
PyObject *base = PyTuple_GET_ITEM(bases, 0); PyObject *base = PyTuple_GET_ITEM(bases, 0);
metaclass = __Pyx_PyObject_GetAttrStr(base, PYIDENT("__class__")); #if PY_MAJOR_VERSION < 3
if (!metaclass) { PyObject* basetype = __Pyx_PyObject_GetAttrStr(base, PYIDENT("__class__"));
if (basetype) {
metatype = (PyType_Check(basetype)) ? ((PyTypeObject*) basetype) : NULL;
} else {
PyErr_Clear(); PyErr_Clear();
metaclass = (PyObject*) Py_TYPE(base); metatype = Py_TYPE(base);
Py_INCREF(metaclass); basetype = (PyObject*) metatype;
Py_INCREF(basetype);
} }
#else
metatype = Py_TYPE(base);
#endif
metaclass = __Pyx_CalculateMetaclass(metatype, bases);
#if PY_MAJOR_VERSION < 3
Py_DECREF(basetype);
#endif
} else { } else {
// no bases => use default metaclass
#if PY_MAJOR_VERSION < 3
metaclass = (PyObject *) &PyClass_Type; metaclass = (PyObject *) &PyClass_Type;
Py_INCREF(metaclass);
}
#else #else
if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {
PyObject *base = PyTuple_GET_ITEM(bases, 0);
metaclass = (PyObject*) Py_TYPE(base);
} else {
metaclass = (PyObject *) &PyType_Type; metaclass = (PyObject *) &PyType_Type;
}
Py_INCREF(metaclass);
#endif #endif
Py_INCREF(metaclass);
}
return metaclass; return metaclass;
} }
...@@ -703,7 +758,8 @@ static PyObject *__Pyx_FindPy2Metaclass(PyObject *bases) { ...@@ -703,7 +758,8 @@ static PyObject *__Pyx_FindPy2Metaclass(PyObject *bases) {
static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw); /*proto*/ static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw); /*proto*/
/////////////// Py3MetaclassGet /////////////// /////////////// Py3MetaclassGet ///////////////
//@requires: FindPy2Metaclass //@requires: FindInheritedMetaclass
//@requires: CalculateMetaclass
static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw) { static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw) {
PyObject *metaclass = PyDict_GetItem(mkw, PYIDENT("metaclass")); PyObject *metaclass = PyDict_GetItem(mkw, PYIDENT("metaclass"));
...@@ -713,9 +769,14 @@ static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw) { ...@@ -713,9 +769,14 @@ static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw) {
Py_DECREF(metaclass); Py_DECREF(metaclass);
return NULL; return NULL;
} }
if (PyType_Check(metaclass)) {
PyObject* orig = metaclass;
metaclass = __Pyx_CalculateMetaclass((PyTypeObject*) metaclass, bases);
Py_DECREF(orig);
}
return metaclass; return metaclass;
} }
return __Pyx_FindPy2Metaclass(bases); return __Pyx_FindInheritedMetaclass(bases);
} }
/////////////// CreateClass.proto /////////////// /////////////// CreateClass.proto ///////////////
...@@ -724,7 +785,8 @@ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *na ...@@ -724,7 +785,8 @@ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *na
PyObject *qualname, PyObject *modname); /*proto*/ PyObject *qualname, PyObject *modname); /*proto*/
/////////////// CreateClass /////////////// /////////////// CreateClass ///////////////
//@requires: FindPy2Metaclass //@requires: FindInheritedMetaclass
//@requires: CalculateMetaclass
static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name,
PyObject *qualname, PyObject *modname) { PyObject *qualname, PyObject *modname) {
...@@ -740,9 +802,16 @@ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *na ...@@ -740,9 +802,16 @@ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *na
metaclass = PyDict_GetItem(dict, PYIDENT("__metaclass__")); metaclass = PyDict_GetItem(dict, PYIDENT("__metaclass__"));
if (metaclass) { if (metaclass) {
Py_INCREF(metaclass); Py_INCREF(metaclass);
if (PyType_Check(metaclass)) {
PyObject* orig = metaclass;
metaclass = __Pyx_CalculateMetaclass((PyTypeObject*) metaclass, bases);
Py_DECREF(orig);
}
} else { } else {
metaclass = __Pyx_FindPy2Metaclass(bases); metaclass = __Pyx_FindInheritedMetaclass(bases);
} }
if (unlikely(!metaclass))
return NULL;
result = PyObject_CallFunctionObjArgs(metaclass, name, bases, dict, NULL); result = PyObject_CallFunctionObjArgs(metaclass, name, bases, dict, NULL);
Py_DECREF(metaclass); Py_DECREF(metaclass);
return result; return result;
...@@ -750,11 +819,14 @@ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *na ...@@ -750,11 +819,14 @@ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *na
/////////////// Py3ClassCreate.proto /////////////// /////////////// Py3ClassCreate.proto ///////////////
static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, PyObject *qualname, PyObject *mkw, PyObject *modname, PyObject *doc); /*proto*/ static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, PyObject *qualname,
static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObject *bases, PyObject *dict, PyObject *mkw); /*proto*/ PyObject *mkw, PyObject *modname, PyObject *doc); /*proto*/
static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObject *bases, PyObject *dict,
PyObject *mkw, int calculate_metaclass, int allow_py2_metaclass); /*proto*/
/////////////// Py3ClassCreate /////////////// /////////////// Py3ClassCreate ///////////////
//@requires: PyObjectGetAttrStr //@requires: PyObjectGetAttrStr
//@requires: CalculateMetaclass
static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name,
PyObject *qualname, PyObject *mkw, PyObject *modname, PyObject *doc) { PyObject *qualname, PyObject *mkw, PyObject *modname, PyObject *doc) {
...@@ -764,27 +836,28 @@ static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, ...@@ -764,27 +836,28 @@ static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases,
prep = __Pyx_PyObject_GetAttrStr(metaclass, PYIDENT("__prepare__")); prep = __Pyx_PyObject_GetAttrStr(metaclass, PYIDENT("__prepare__"));
if (!prep) { if (!prep) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError)) if (unlikely(!PyErr_ExceptionMatches(PyExc_AttributeError)))
return NULL; return NULL;
PyErr_Clear(); PyErr_Clear();
return PyDict_New(); ns = PyDict_New();
} } else {
pargs = PyTuple_Pack(2, name, bases); pargs = PyTuple_Pack(2, name, bases);
if (!pargs) { if (unlikely(!pargs)) {
Py_DECREF(prep); Py_DECREF(prep);
return NULL; return NULL;
} }
ns = PyObject_Call(prep, pargs, mkw); ns = PyObject_Call(prep, pargs, mkw);
Py_DECREF(prep); Py_DECREF(prep);
Py_DECREF(pargs); Py_DECREF(pargs);
}
if (ns == NULL) if (unlikely(!ns))
return NULL; return NULL;
/* Required here to emulate assignment order */ /* Required here to emulate assignment order */
if (PyObject_SetItem(ns, PYIDENT("__module__"), modname) < 0) goto bad; if (unlikely(PyObject_SetItem(ns, PYIDENT("__module__"), modname) < 0)) goto bad;
if (PyObject_SetItem(ns, PYIDENT("__qualname__"), qualname) < 0) goto bad; if (unlikely(PyObject_SetItem(ns, PYIDENT("__qualname__"), qualname) < 0)) goto bad;
if (doc && PyObject_SetItem(ns, PYIDENT("__doc__"), doc) < 0) goto bad; if (unlikely(doc && PyObject_SetItem(ns, PYIDENT("__doc__"), doc) < 0)) goto bad;
return ns; return ns;
bad: bad:
Py_DECREF(ns); Py_DECREF(ns);
...@@ -792,13 +865,48 @@ bad: ...@@ -792,13 +865,48 @@ bad:
} }
static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObject *bases, static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObject *bases,
PyObject *dict, PyObject *mkw) { PyObject *dict, PyObject *mkw,
PyObject *result; int calculate_metaclass, int allow_py2_metaclass) {
PyObject *margs = PyTuple_Pack(3, name, bases, dict); PyObject *result, *margs;
if (!margs) PyObject *py2_metaclass = NULL;
if (allow_py2_metaclass) {
/* honour Python2 __metaclass__ for backward compatibility */
py2_metaclass = PyObject_GetItem(dict, PYIDENT("__metaclass__"));
if (py2_metaclass) {
if (likely(PyType_Check(py2_metaclass))) {
metaclass = py2_metaclass;
calculate_metaclass = 1;
} else {
/* py2_metaclass != NULL => calculate_metaclass != 0 */
Py_DECREF(py2_metaclass);
py2_metaclass = NULL;
}
} else if (likely(PyErr_ExceptionMatches(PyExc_KeyError))) {
PyErr_Clear();
} else {
return NULL;
}
}
if (calculate_metaclass) {
if (py2_metaclass || PyType_Check(metaclass)) {
metaclass = __Pyx_CalculateMetaclass((PyTypeObject*) metaclass, bases);
Py_XDECREF(py2_metaclass);
if (unlikely(!metaclass))
return NULL; return NULL;
} else {
Py_XDECREF(py2_metaclass);
calculate_metaclass = 0;
}
}
margs = PyTuple_Pack(3, name, bases, dict);
if (unlikely(!margs)) {
result = NULL;
} else {
result = PyObject_Call(metaclass, margs, mkw); result = PyObject_Call(metaclass, margs, mkw);
Py_DECREF(margs); Py_DECREF(margs);
}
if (calculate_metaclass)
Py_DECREF(metaclass);
return result; return result;
} }
......
...@@ -24,7 +24,7 @@ def test_class_locals_and_dir(): ...@@ -24,7 +24,7 @@ def test_class_locals_and_dir():
>>> 'visible' in klass.locs and 'not_visible' not in klass.locs >>> 'visible' in klass.locs and 'not_visible' not in klass.locs
True True
>>> klass.names >>> klass.names
['visible'] ['__module__', '__qualname__', 'visible']
""" """
not_visible = 1234 not_visible = 1234
class Foo: class Foo:
......
...@@ -6,7 +6,7 @@ class Base(type): ...@@ -6,7 +6,7 @@ class Base(type):
attrs['metaclass_was_here'] = True attrs['metaclass_was_here'] = True
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
@cython.test_fail_if_path_exists("//PyClassMetaclassNode", "//Py3ClassNode") @cython.test_assert_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
class Foo(object): class Foo(object):
""" """
>>> obj = Foo() >>> obj = Foo()
...@@ -27,6 +27,7 @@ class ODict(dict): ...@@ -27,6 +27,7 @@ class ODict(dict):
class Py3MetaclassPlusAttr(type): class Py3MetaclassPlusAttr(type):
def __new__(cls, name, bases, attrs, **kwargs): def __new__(cls, name, bases, attrs, **kwargs):
assert isinstance(attrs, ODict), str(type(attrs))
for key, value in kwargs.items(): for key, value in kwargs.items():
attrs[key] = value attrs[key] = value
attrs['metaclass_was_here'] = True attrs['metaclass_was_here'] = True
...@@ -53,8 +54,21 @@ class Py3ClassMCOnly(object, metaclass=Py3MetaclassPlusAttr): ...@@ -53,8 +54,21 @@ class Py3ClassMCOnly(object, metaclass=Py3MetaclassPlusAttr):
""" """
bar = 321 bar = 321
class Py3InheritedMetaclass(Py3ClassMCOnly):
"""
>>> obj = Py3InheritedMetaclass()
>>> obj.bar
345
>>> obj.metaclass_was_here
True
>>> obj._order
['__module__', '__qualname__', '__doc__', 'bar', 'metaclass_was_here']
"""
bar = 345
class Py3Base(type): class Py3Base(type):
def __new__(cls, name, bases, attrs, **kwargs): def __new__(cls, name, bases, attrs, **kwargs):
assert isinstance(attrs, ODict), str(type(attrs))
for key, value in kwargs.items(): for key, value in kwargs.items():
attrs[key] = value attrs[key] = value
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
...@@ -80,6 +94,19 @@ class Py3Foo(object, metaclass=Py3Base, foo=123): ...@@ -80,6 +94,19 @@ class Py3Foo(object, metaclass=Py3Base, foo=123):
""" """
bar = 321 bar = 321
@cython.test_assert_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
class Py3FooInherited(Py3Foo, foo=567):
"""
>>> obj = Py3FooInherited()
>>> obj.foo
567
>>> obj.bar
321
>>> obj._order
['__module__', '__qualname__', '__doc__', 'bar', 'foo']
"""
bar = 321
kwargs = {'foo': 123, 'bar': 456} kwargs = {'foo': 123, 'bar': 456}
@cython.test_assert_path_exists("//PyClassMetaclassNode", "//Py3ClassNode") @cython.test_assert_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment