Commit a750a1ce authored by Vitja Makarov's avatar Vitja Makarov

Py3 metaclasses initial support.

parent 78a87802
...@@ -4480,14 +4480,23 @@ class ClassNode(ExprNode, ModuleNameMixin): ...@@ -4480,14 +4480,23 @@ class ClassNode(ExprNode, ModuleNameMixin):
# dict ExprNode Class dict (not owned by this node) # dict ExprNode Class dict (not owned by this node)
# doc ExprNode or None Doc string # doc ExprNode or None Doc string
# module_name EncodedString Name of defining module # module_name EncodedString Name of defining module
# keyword_args ExprNode or None Py3 Dict of keyword arguments, passed to __new__
# starstar_arg ExprNode or None Py3 Dict of extra keyword args, same here
subexprs = ['bases', 'doc'] subexprs = ['bases', 'keyword_args', 'starstar_arg', 'doc']
def analyse_types(self, env): def analyse_types(self, env):
self.bases.analyse_types(env) self.bases.analyse_types(env)
if self.doc: if self.doc:
self.doc.analyse_types(env) self.doc.analyse_types(env)
self.doc = self.doc.coerce_to_pyobject(env) self.doc = self.doc.coerce_to_pyobject(env)
if self.keyword_args:
self.keyword_args.analyse_types(env)
if self.starstar_arg:
self.starstar_arg.analyse_types(env)
if self.starstar_arg:
self.starstar_arg = \
self.starstar_arg.coerce_to_pyobject(env)
self.type = py_object_type self.type = py_object_type
self.is_temp = 1 self.is_temp = 1
env.use_utility_code(create_class_utility_code); env.use_utility_code(create_class_utility_code);
...@@ -4501,6 +4510,19 @@ class ClassNode(ExprNode, ModuleNameMixin): ...@@ -4501,6 +4510,19 @@ class ClassNode(ExprNode, ModuleNameMixin):
def generate_result_code(self, code): def generate_result_code(self, code):
cname = code.intern_identifier(self.name) cname = code.intern_identifier(self.name)
if self.keyword_args and self.starstar_arg:
code.put_error_if_neg(self.pos,
"PyDict_Update(%s, %s)" % (
self.keyword_args.py_result(),
self.starstar_arg.py_result()))
keyword_code = self.keyword_args.py_result()
elif self.keyword_args:
keyword_code = self.keyword_args.py_result()
elif self.starstar_arg:
keyword_code = self.starstar_arg.py_result()
else:
keyword_code = 'NULL'
if self.doc: if self.doc:
code.put_error_if_neg(self.pos, code.put_error_if_neg(self.pos,
'PyDict_SetItemString(%s, "__doc__", %s)' % ( 'PyDict_SetItemString(%s, "__doc__", %s)' % (
...@@ -4508,12 +4530,13 @@ class ClassNode(ExprNode, ModuleNameMixin): ...@@ -4508,12 +4530,13 @@ class ClassNode(ExprNode, ModuleNameMixin):
self.doc.py_result())) self.doc.py_result()))
py_mod_name = self.get_py_mod_name(code) py_mod_name = self.get_py_mod_name(code)
code.putln( code.putln(
'%s = __Pyx_CreateClass(%s, %s, %s, %s); %s' % ( '%s = __Pyx_CreateClass(%s, %s, %s, %s, %s); %s' % (
self.result(), self.result(),
self.bases.py_result(), self.bases.py_result(),
self.dict.py_result(), self.dict.py_result(),
cname, cname,
py_mod_name, py_mod_name,
keyword_code,
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())
...@@ -7159,45 +7182,106 @@ static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) { ...@@ -7159,45 +7182,106 @@ static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) {
create_class_utility_code = UtilityCode( create_class_utility_code = UtilityCode(
proto = """ proto = """
static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, PyObject *modname); /*proto*/ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name,
PyObject *modname, PyObject *kwargs); /*proto*/
""", """,
impl = """ impl = """
static int __Pyx_PrepareClass(PyObject *metaclass, PyObject *bases, PyObject *name, PyObject *mkw, PyObject *dict)
{
PyObject *prep;
PyObject *pargs;
PyObject *ns;
prep = PyObject_GetAttrString(metaclass, "__prepare__");
if (prep == NULL) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError))
return -1;
PyErr_Clear();
return 0;
}
pargs = PyTuple_Pack(2, name, bases);
if (pargs == NULL) {
Py_DECREF(prep);
return -1;
}
ns = PyEval_CallObjectWithKeywords(prep, pargs, mkw);
Py_DECREF(pargs);
Py_DECREF(prep);
if (ns == NULL)
return -1;
/* XXX: This is hack, merge namespace back to dict,
__prepare__ should be ran before dict initialization */
if (PyDict_Merge(dict, ns, 0)) {
Py_DECREF(ns);
return -1;
}
Py_DECREF(ns);
return 0;
}
static PyObject *__Pyx_CreateClass( static PyObject *__Pyx_CreateClass(
PyObject *bases, PyObject *methods, PyObject *name, PyObject *modname) PyObject *bases, PyObject *dict, PyObject *name, PyObject *modname, PyObject *kwargs)
{ {
PyObject *result = 0; PyObject *result = NULL;
#if PY_MAJOR_VERSION < 3 PyObject *metaclass = NULL;
PyObject *metaclass = 0, *base; PyObject *mkw = NULL;
#endif
if (PyDict_SetItemString(methods, "__module__", modname) < 0) if (PyDict_SetItemString(dict, "__module__", modname) < 0)
goto bad; return NULL;
#if PY_MAJOR_VERSION < 3
metaclass = PyDict_GetItemString(methods, "__metaclass__");
if (metaclass != NULL) /* Python3 metaclasses */
Py_INCREF(metaclass); if (kwargs) {
else if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) { mkw = PyDict_Copy(kwargs); /* Don't modify kwargs passed in! */
base = PyTuple_GET_ITEM(bases, 0); if (mkw == NULL)
metaclass = PyObject_GetAttrString(base, "__class__"); return NULL;
if (metaclass == NULL) { metaclass = PyDict_GetItemString(mkw, "metaclass");
PyErr_Clear(); if (metaclass) {
metaclass = (PyObject *)base->ob_type;
Py_INCREF(metaclass); Py_INCREF(metaclass);
if (PyDict_DelItemString(mkw, "metaclass") < 0)
goto bad;
if (__Pyx_PrepareClass(metaclass, bases, name, mkw, dict))
goto bad;
} }
} }
else { /* Python2 __metaclass__ */
metaclass = (PyObject *) &PyClass_Type; if (metaclass == NULL) {
metaclass = PyDict_GetItemString(dict, "__metaclass__");
if (metaclass)
Py_INCREF(metaclass);
}
/* Default metaclass */
if (metaclass == NULL) {
#if PY_MAJOR_VERSION < 3
if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {
PyObject *base = PyTuple_GET_ITEM(bases, 0);
metaclass = PyObject_GetAttrString(base, "__class__");
if (metaclass == NULL) {
PyErr_Clear();
metaclass = (PyObject *)base->ob_type;
}
} else
metaclass = (PyObject *) &PyClass_Type;
#else
if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {
PyObject *base = PyTuple_GET_ITEM(bases, 0);
metaclass = (PyObject *)base->ob_type;
} else
metaclass = (PyObject *) &PyType_Type;
#endif
Py_INCREF(metaclass); Py_INCREF(metaclass);
} }
if (mkw && PyDict_Size(mkw) > 0) {
result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods, NULL); PyObject *margs;
Py_DECREF(metaclass); margs = PyTuple_Pack(3, name, bases, dict, NULL);
#else result = PyEval_CallObjectWithKeywords(metaclass, margs, mkw);
/* it seems that python3+ handle __metaclass__ itself */ Py_DECREF(margs);
result = PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, name, bases, methods, NULL); } else {
#endif result = PyObject_CallFunctionObjArgs(metaclass, name, bases, dict, NULL);
}
bad: bad:
Py_DECREF(metaclass);
if (mkw)
Py_XDECREF(mkw);
return result; return result;
} }
""") """)
......
...@@ -2940,7 +2940,8 @@ class PyClassDefNode(ClassDefNode): ...@@ -2940,7 +2940,8 @@ class PyClassDefNode(ClassDefNode):
child_attrs = ["body", "dict", "classobj", "target"] child_attrs = ["body", "dict", "classobj", "target"]
decorators = None decorators = 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):
StatNode.__init__(self, pos) StatNode.__init__(self, pos)
self.name = name self.name = name
self.doc = doc self.doc = doc
...@@ -2955,7 +2956,8 @@ class PyClassDefNode(ClassDefNode): ...@@ -2955,7 +2956,8 @@ class PyClassDefNode(ClassDefNode):
else: else:
doc_node = None doc_node = None
self.classobj = ExprNodes.ClassNode(pos, name = name, self.classobj = ExprNodes.ClassNode(pos, name = name,
bases = bases, dict = self.dict, doc = doc_node) bases = bases, dict = self.dict, doc = doc_node,
keyword_args = keyword_args, starstar_arg = starstar_arg)
self.target = ExprNodes.NameNode(pos, name = name) self.target = ExprNodes.NameNode(pos, name = name)
def as_cclass(self): def as_cclass(self):
......
...@@ -381,7 +381,7 @@ def p_trailer(s, node1): ...@@ -381,7 +381,7 @@ def p_trailer(s, node1):
# arglist: argument (',' argument)* [','] # arglist: argument (',' argument)* [',']
# argument: [test '='] test # Really [keyword '='] test # argument: [test '='] test # Really [keyword '='] test
def p_call(s, function): def p_call_parse(s):
# s.sy == '(' # s.sy == '('
pos = s.position() pos = s.position()
s.next() s.next()
...@@ -428,29 +428,43 @@ def p_call(s, function): ...@@ -428,29 +428,43 @@ def p_call(s, function):
if s.sy == ',': if s.sy == ',':
s.next() s.next()
s.expect(')') s.expect(')')
return positional_args, keyword_args, star_arg, starstar_arg
def p_call_prepare_full(pos, positional_args, keyword_args, star_arg):
arg_tuple = None
keyword_dict = None
if positional_args or not star_arg:
arg_tuple = ExprNodes.TupleNode(pos,
args = positional_args)
if star_arg:
star_arg_tuple = ExprNodes.AsTupleNode(pos, arg = star_arg)
if arg_tuple:
arg_tuple = ExprNodes.binop_node(pos,
operator = '+', operand1 = arg_tuple,
operand2 = star_arg_tuple)
else:
arg_tuple = star_arg_tuple
if keyword_args:
keyword_args = [ExprNodes.DictItemNode(pos=key.pos, key=key, value=value)
for key, value in keyword_args]
keyword_dict = ExprNodes.DictNode(pos,
key_value_pairs = keyword_args)
return arg_tuple, keyword_dict
def p_call(s, function):
# s.sy == '('
pos = s.position()
positional_args, keyword_args, star_arg, starstar_arg = \
p_call_parse(s)
if not (keyword_args or star_arg or starstar_arg): if not (keyword_args or star_arg or starstar_arg):
return ExprNodes.SimpleCallNode(pos, return ExprNodes.SimpleCallNode(pos,
function = function, function = function,
args = positional_args) args = positional_args)
else: else:
arg_tuple = None arg_tuple, keyword_dict = p_call_prepare_full(pos,
keyword_dict = None positional_args, keyword_args, star_arg)
if positional_args or not star_arg:
arg_tuple = ExprNodes.TupleNode(pos,
args = positional_args)
if star_arg:
star_arg_tuple = ExprNodes.AsTupleNode(pos, arg = star_arg)
if arg_tuple:
arg_tuple = ExprNodes.binop_node(pos,
operator = '+', operand1 = arg_tuple,
operand2 = star_arg_tuple)
else:
arg_tuple = star_arg_tuple
if keyword_args:
keyword_args = [ExprNodes.DictItemNode(pos=key.pos, key=key, value=value)
for key, value in keyword_args]
keyword_dict = ExprNodes.DictNode(pos,
key_value_pairs = keyword_args)
return ExprNodes.GeneralCallNode(pos, return ExprNodes.GeneralCallNode(pos,
function = function, function = function,
positional_args = arg_tuple, positional_args = arg_tuple,
...@@ -2607,16 +2621,23 @@ def p_class_statement(s, decorators): ...@@ -2607,16 +2621,23 @@ def p_class_statement(s, decorators):
s.next() s.next()
class_name = EncodedString( p_ident(s) ) class_name = EncodedString( p_ident(s) )
class_name.encoding = s.source_encoding class_name.encoding = s.source_encoding
arg_tuple = None
keyword_dict = None
starstar_arg = None
if s.sy == '(': if s.sy == '(':
s.next() positional_args, keyword_args, star_arg, starstar_arg = \
base_list = p_simple_expr_list(s) p_call_parse(s)
s.expect(')') arg_tuple, keyword_dict = p_call_prepare_full(pos,
else: positional_args, keyword_args, star_arg)
base_list = [] if arg_tuple is None:
# XXX: empty arg_tuple
arg_tuple = ExprNodes.TupleNode(pos, args = [])
doc, body = p_suite(s, Ctx(level = 'class'), with_doc = 1) doc, body = p_suite(s, Ctx(level = 'class'), with_doc = 1)
return Nodes.PyClassDefNode(pos, return Nodes.PyClassDefNode(pos,
name = class_name, name = class_name,
bases = ExprNodes.TupleNode(pos, args = base_list), bases = arg_tuple,
keyword_args = keyword_dict,
starstar_arg = starstar_arg,
doc = doc, body = body, decorators = decorators) doc = doc, body = body, decorators = decorators)
def p_c_class_definition(s, pos, ctx): def p_c_class_definition(s, pos, ctx):
......
...@@ -11,3 +11,26 @@ class Foo(object): ...@@ -11,3 +11,26 @@ class Foo(object):
True True
""" """
__metaclass__ = Base __metaclass__ = Base
class Py3Base(type):
def __new__(cls, name, bases, attrs, foo=None):
attrs['foo'] = foo
return type.__new__(cls, name, bases, attrs)
def __init__(self, cls, attrs, obj, foo=None):
pass
@staticmethod
def __prepare__(name, bases, **kwargs):
return {'bar': 666, 'dirty': True}
class Py3Foo(object, metaclass=Py3Base, foo=123):
"""
>>> obj = Py3Foo()
>>> obj.foo
123
>>> obj.bar
666
>>> obj.dirty
False
"""
dirty = False
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