Implement PEP 3115 -- new metaclass syntax and semantics.
The compiler package hasn't been updated yet; test_compiler.py fails.
Otherwise all tests seem to be passing now. There are no occurrences
of __metaclass__ left in the standard library.
Docs have not been updated.
diff --git a/Grammar/Grammar b/Grammar/Grammar
index 0277799..0e459ca 100644
--- a/Grammar/Grammar
+++ b/Grammar/Grammar
@@ -119,7 +119,7 @@
testlist: test (',' test)* [',']
dictsetmaker: (test ':' test (',' test ':' test)* [',']) | (test (',' test)* [','])
-classdef: 'class' NAME ['(' [testlist] ')'] ':' suite
+classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test)
argument: test [gen_for] | test '=' test # Really [keyword '='] test
diff --git a/Include/Python-ast.h b/Include/Python-ast.h
index 233a576..e07f025 100644
--- a/Include/Python-ast.h
+++ b/Include/Python-ast.h
@@ -82,6 +82,9 @@
struct {
identifier name;
asdl_seq *bases;
+ asdl_seq *keywords;
+ expr_ty starargs;
+ expr_ty kwargs;
asdl_seq *body;
} ClassDef;
@@ -380,8 +383,9 @@
stmt_ty _Py_FunctionDef(identifier name, arguments_ty args, asdl_seq * body,
asdl_seq * decorators, expr_ty returns, int lineno, int
col_offset, PyArena *arena);
-#define ClassDef(a0, a1, a2, a3, a4, a5) _Py_ClassDef(a0, a1, a2, a3, a4, a5)
-stmt_ty _Py_ClassDef(identifier name, asdl_seq * bases, asdl_seq * body, int
+#define ClassDef(a0, a1, a2, a3, a4, a5, a6, a7, a8) _Py_ClassDef(a0, a1, a2, a3, a4, a5, a6, a7, a8)
+stmt_ty _Py_ClassDef(identifier name, asdl_seq * bases, asdl_seq * keywords,
+ expr_ty starargs, expr_ty kwargs, asdl_seq * body, int
lineno, int col_offset, PyArena *arena);
#define Return(a0, a1, a2, a3) _Py_Return(a0, a1, a2, a3)
stmt_ty _Py_Return(expr_ty value, int lineno, int col_offset, PyArena *arena);
diff --git a/Include/opcode.h b/Include/opcode.h
index 316ba4f..662fbb4 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -59,8 +59,9 @@
#define BINARY_OR 66
#define INPLACE_POWER 67
#define GET_ITER 68
-
+#define STORE_LOCALS 69
#define PRINT_EXPR 70
+#define LOAD_BUILD_CLASS 71
#define INPLACE_LSHIFT 75
#define INPLACE_RSHIFT 76
@@ -69,14 +70,13 @@
#define INPLACE_OR 79
#define BREAK_LOOP 80
#define WITH_CLEANUP 81
-#define LOAD_LOCALS 82
+
#define RETURN_VALUE 83
#define IMPORT_STAR 84
#define MAKE_BYTES 85
#define YIELD_VALUE 86
#define POP_BLOCK 87
#define END_FINALLY 88
-#define BUILD_CLASS 89
#define HAVE_ARGUMENT 90 /* Opcodes from here have an argument: */
@@ -120,10 +120,10 @@
#define RAISE_VARARGS 130 /* Number of raise arguments (1, 2 or 3) */
/* CALL_FUNCTION_XXX opcodes defined below depend on this definition */
#define CALL_FUNCTION 131 /* #args + (#kwargs<<8) */
-#define MAKE_FUNCTION 132 /* #defaults */
+#define MAKE_FUNCTION 132 /* #defaults + #kwdefaults<<8 + #annotations<<16 */
#define BUILD_SLICE 133 /* Number of items */
-#define MAKE_CLOSURE 134 /* #free vars */
+#define MAKE_CLOSURE 134 /* same as MAKE_FUNCTION */
#define LOAD_CLOSURE 135 /* Load free variable from closure */
#define LOAD_DEREF 136 /* Load and dereference from closure cell */
#define STORE_DEREF 137 /* Store into cell */
diff --git a/Lib/build_class.py b/Lib/build_class.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/build_class.py
diff --git a/Lib/ctypes/_endian.py b/Lib/ctypes/_endian.py
index 6de0d47..61ac334 100644
--- a/Lib/ctypes/_endian.py
+++ b/Lib/ctypes/_endian.py
@@ -42,18 +42,16 @@
LittleEndianStructure = Structure
- class BigEndianStructure(Structure):
+ class BigEndianStructure(Structure, metaclass=_swapped_meta):
"""Structure with big endian byte order"""
- __metaclass__ = _swapped_meta
_swappedbytes_ = None
elif sys.byteorder == "big":
_OTHER_ENDIAN = "__ctype_le__"
BigEndianStructure = Structure
- class LittleEndianStructure(Structure):
+ class LittleEndianStructure(Structure, metaclass=_swapped_meta):
"""Structure with little endian byte order"""
- __metaclass__ = _swapped_meta
_swappedbytes_ = None
else:
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 69982f2..61b288a 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -98,8 +98,10 @@
def_op('BINARY_OR', 66)
def_op('INPLACE_POWER', 67)
def_op('GET_ITER', 68)
+def_op('STORE_LOCALS', 69)
def_op('PRINT_EXPR', 70)
+def_op('LOAD_BUILD_CLASS', 71)
def_op('INPLACE_LSHIFT', 75)
def_op('INPLACE_RSHIFT', 76)
@@ -108,14 +110,13 @@
def_op('INPLACE_OR', 79)
def_op('BREAK_LOOP', 80)
def_op('WITH_CLEANUP', 81)
-def_op('LOAD_LOCALS', 82)
+
def_op('RETURN_VALUE', 83)
def_op('IMPORT_STAR', 84)
def_op('MAKE_BYTES', 85)
def_op('YIELD_VALUE', 86)
def_op('POP_BLOCK', 87)
def_op('END_FINALLY', 88)
-def_op('BUILD_CLASS', 89)
HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
diff --git a/Lib/string.py b/Lib/string.py
index 8281936..6aafb65 100644
--- a/Lib/string.py
+++ b/Lib/string.py
@@ -119,9 +119,8 @@
cls.pattern = _re.compile(pattern, _re.IGNORECASE | _re.VERBOSE)
-class Template:
+class Template(metaclass=_TemplateMetaclass):
"""A string class for supporting $-substitutions."""
- __metaclass__ = _TemplateMetaclass
delimiter = '$'
idpattern = r'[_a-z][_a-z0-9]*'
diff --git a/Lib/test/crashers/modify_dict_attr.py b/Lib/test/crashers/modify_dict_attr.py
index 2a8fce0..ac1f0a8 100644
--- a/Lib/test/crashers/modify_dict_attr.py
+++ b/Lib/test/crashers/modify_dict_attr.py
@@ -7,11 +7,10 @@
class type_with_modifiable_dict(Y, type):
pass
-class MyClass(object):
+class MyClass(object, metaclass=type_with_modifiable_dict):
"""This class has its __dict__ attribute completely exposed:
user code can read, reassign and even delete it.
"""
- __metaclass__ = type_with_modifiable_dict
if __name__ == '__main__':
diff --git a/Lib/test/leakers/test_selftype.py b/Lib/test/leakers/test_selftype.py
index 4207c32..12f2934 100644
--- a/Lib/test/leakers/test_selftype.py
+++ b/Lib/test/leakers/test_selftype.py
@@ -6,8 +6,8 @@
def leak():
class T(type):
pass
- class U(type):
- __metaclass__ = T
+ class U(type, metaclass=T):
+ pass
U.__class__ = U
del U
gc.collect(); gc.collect(); gc.collect()
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index cb4b431..b10c57d 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -88,8 +88,8 @@
class metaclass(type):
pass
-class use_metaclass(object):
- __metaclass__ = metaclass
+class use_metaclass(object, metaclass=metaclass):
+ pass
# DATA0 .. DATA2 are the pickles we expect under the various protocols, for
# the object returned by create_data().
diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index 5d22053..c702ab1 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -144,13 +144,19 @@
(eval_tests, eval_results, "eval")):
for i, o in itertools.izip(input, output):
ast_tree = compile(i, "?", kind, 0x400)
+ if to_tuple(ast_tree) != o:
+ print("i=", i)
+ print("o=", o)
+ print("kind=", kind)
+ print("tree=", ast_tree)
+ print("tuple=", to_tuple(ast_tree))
assert to_tuple(ast_tree) == o
test_order(ast_tree, (0, 0))
#### EVERYTHING BELOW IS GENERATED #####
exec_results = [
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, [], None, None, [], []), [('Pass', (1, 9))], [], None)]),
-('Module', [('ClassDef', (1, 0), 'C', [], [('Pass', (1, 8))])]),
+('Module', [('ClassDef', (1, 0), 'C', [], [], None, None, [('Pass', (1, 8))])]),
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, [], None, None, [], []), [('Return', (1, 8), ('Num', (1, 15), 1))], [], None)]),
('Module', [('Delete', (1, 0), [('Name', (1, 4), 'v', ('Del',))])]),
('Module', [('Assign', (1, 0), [('Name', (1, 0), 'v', ('Store',))], ('Num', (1, 4), 1))]),
diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py
index fd6109c..dbca158 100644
--- a/Lib/test/test_copy.py
+++ b/Lib/test/test_copy.py
@@ -191,8 +191,8 @@
# type.
class Meta(type):
pass
- class C:
- __metaclass__ = Meta
+ class C(metaclass=Meta):
+ pass
self.assertEqual(copy.deepcopy(C), C)
def test_deepcopy_deepcopy(self):
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index e8db29e..6cd8ccd 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -616,9 +616,8 @@
vereq(a[100:200], (100,200))
def metaclass():
- if verbose: print("Testing __metaclass__...")
- class C:
- __metaclass__ = type
+ if verbose: print("Testing metaclass...")
+ class C(metaclass=type):
def __init__(self):
self.__state = 0
def getstate(self):
@@ -629,9 +628,10 @@
vereq(a.getstate(), 0)
a.setstate(10)
vereq(a.getstate(), 10)
- class D:
- class __metaclass__(type):
- def myself(cls): return cls
+ class _metaclass(type):
+ def myself(cls): return cls
+ class D(metaclass=_metaclass):
+ pass
vereq(D.myself(), D)
d = D()
verify(d.__class__ is D)
@@ -639,8 +639,8 @@
def __new__(cls, name, bases, dict):
dict['__spam__'] = 1
return type.__new__(cls, name, bases, dict)
- class C:
- __metaclass__ = M1
+ class C(metaclass=M1):
+ pass
vereq(C.__spam__, 1)
c = C()
vereq(c.__spam__, 1)
@@ -663,8 +663,7 @@
continue
setattr(it, key, self.dict[key].__get__(it, self))
return it
- class C:
- __metaclass__ = M2
+ class C(metaclass=M2):
def spam(self):
return 42
vereq(C.name, 'C')
@@ -690,8 +689,7 @@
name = "__super"
setattr(cls, name, super(cls))
return cls
- class A:
- __metaclass__ = autosuper
+ class A(metaclass=autosuper):
def meth(self):
return "A"
class B(A):
@@ -729,8 +727,7 @@
dict[key] = property(get, set)
return super(autoproperty, metaclass).__new__(metaclass,
name, bases, dict)
- class A:
- __metaclass__ = autoproperty
+ class A(metaclass=autoproperty):
def _get_x(self):
return -self.__x
def _set_x(self, x):
@@ -744,8 +741,7 @@
class multimetaclass(autoproperty, autosuper):
# Merge of multiple cooperating metaclasses
pass
- class A:
- __metaclass__ = multimetaclass
+ class A(metaclass=multimetaclass):
def _get_x(self):
return "A"
class B(A):
@@ -764,8 +760,8 @@
counter = 0
def __init__(self, *args):
T.counter += 1
- class C:
- __metaclass__ = T
+ class C(metaclass=T):
+ pass
vereq(T.counter, 1)
a = C()
vereq(type(a), C)
@@ -1273,8 +1269,8 @@
# Test comparison of classes with dynamic metaclasses
class dynamicmetaclass(type):
pass
- class someclass:
- __metaclass__ = dynamicmetaclass
+ class someclass(metaclass=dynamicmetaclass):
+ pass
verify(someclass != object)
def errors():
@@ -1505,36 +1501,39 @@
L = type.mro(cls)
L.reverse()
return L
- class X(D,B,C,A):
- __metaclass__ = PerverseMetaType
+ class X(D,B,C,A, metaclass=PerverseMetaType):
+ pass
vereq(X.__mro__, (object, A, C, B, D, X))
vereq(X().f(), "A")
try:
- class X(object):
- class __metaclass__(type):
- def mro(self):
- return [self, dict, object]
+ class _metaclass(type):
+ def mro(self):
+ return [self, dict, object]
+ class X(object, metaclass=_metaclass):
+ pass
except TypeError:
pass
else:
raise TestFailed, "devious mro() return not caught"
try:
- class X(object):
- class __metaclass__(type):
- def mro(self):
- return [1]
+ class _metaclass(type):
+ def mro(self):
+ return [1]
+ class X(object, metaclass=_metaclass):
+ pass
except TypeError:
pass
else:
raise TestFailed, "non-class mro() return not caught"
try:
- class X(object):
- class __metaclass__(type):
- def mro(self):
- return 1
+ class _metaclass(type):
+ def mro(self):
+ return 1
+ class X(object, metaclass=_metaclass):
+ pass
except TypeError:
pass
else:
@@ -3575,11 +3574,11 @@
class E(D):
pass
- class F(D):
- __metaclass__ = WorkOnce
+ class F(D, metaclass=WorkOnce):
+ pass
- class G(D):
- __metaclass__ = WorkAlways
+ class G(D, metaclass=WorkAlways):
+ pass
# Immediate subclasses have their mro's adjusted in alphabetical
# order, so E's will get adjusted before adjusting F's fails. We
@@ -3690,15 +3689,15 @@
def dict_type_with_metaclass():
if verbose:
- print("Testing type of __dict__ when __metaclass__ set...")
+ print("Testing type of __dict__ when metaclass set...")
class B(object):
pass
class M(type):
pass
- class C:
+ class C(metaclass=M):
# In 2.3a1, C.__dict__ was a real dict rather than a dict proxy
- __metaclass__ = M
+ pass
veris(type(C.__dict__), type(B.__dict__))
def meth_class_get():
diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py
new file mode 100644
index 0000000..df81079
--- /dev/null
+++ b/Lib/test/test_metaclass.py
@@ -0,0 +1,218 @@
+doctests = """
+
+Basic class construction.
+
+ >>> class C:
+ ... def meth(self): print("Hello")
+ ...
+ >>> C.__class__ is type
+ True
+ >>> a = C()
+ >>> a.__class__ is C
+ True
+ >>> a.meth()
+ Hello
+ >>>
+
+Use *args notation for the bases.
+
+ >>> class A: pass
+ >>> class B: pass
+ >>> bases = (A, B)
+ >>> class C(*bases): pass
+ >>> C.__bases__ == bases
+ True
+ >>>
+
+Use a trivial metaclass.
+
+ >>> class M(type):
+ ... pass
+ ...
+ >>> class C(metaclass=M):
+ ... def meth(self): print("Hello")
+ ...
+ >>> C.__class__ is M
+ True
+ >>> a = C()
+ >>> a.__class__ is C
+ True
+ >>> a.meth()
+ Hello
+ >>>
+
+Use **kwds notation for the metaclass keyword.
+
+ >>> kwds = {'metaclass': M}
+ >>> class C(**kwds): pass
+ ...
+ >>> C.__class__ is M
+ True
+ >>> a = C()
+ >>> a.__class__ is C
+ True
+ >>>
+
+Use a metaclass with a __prepare__ static method.
+
+ >>> class M(type):
+ ... @staticmethod
+ ... def __prepare__(*args, **kwds):
+ ... print("Prepare called:", args, kwds)
+ ... return dict()
+ ... def __new__(cls, name, bases, namespace, **kwds):
+ ... print("New called:", kwds)
+ ... return type.__new__(cls, name, bases, namespace)
+ ...
+ >>> class C(metaclass=M):
+ ... def meth(self): print("Hello")
+ ...
+ Prepare called: ('C', ()) {}
+ New called: {}
+ >>>
+
+Also pass another keyword.
+
+ >>> class C(object, metaclass=M, other="haha"):
+ ... pass
+ ...
+ Prepare called: ('C', (<type 'object'>,)) {'other': 'haha'}
+ New called: {'other': 'haha'}
+ >>> C.__class__ is M
+ True
+ >>> C.__bases__ == (object,)
+ True
+ >>> a = C()
+ >>> a.__class__ is C
+ True
+ >>>
+
+Check that build_class doesn't mutate the kwds dict.
+
+ >>> kwds = {'metaclass': type}
+ >>> class C(**kwds): pass
+ ...
+ >>> kwds == {'metaclass': type}
+ True
+ >>>
+
+Use various combinations of explicit keywords and **kwds.
+
+ >>> bases = (object,)
+ >>> kwds = {'metaclass': M, 'other': 'haha'}
+ >>> class C(*bases, **kwds): pass
+ ...
+ Prepare called: ('C', (<type 'object'>,)) {'other': 'haha'}
+ New called: {'other': 'haha'}
+ >>> C.__class__ is M
+ True
+ >>> C.__bases__ == (object,)
+ True
+ >>> class B: pass
+ >>> kwds = {'other': 'haha'}
+ >>> class C(B, metaclass=M, *bases, **kwds): pass
+ ...
+ Prepare called: ('C', (<class 'test.test_metaclass.B'>, <type 'object'>)) {'other': 'haha'}
+ New called: {'other': 'haha'}
+ >>> C.__class__ is M
+ True
+ >>> C.__bases__ == (B, object)
+ True
+ >>>
+
+Check for duplicate keywords.
+
+ >>> class C(metaclass=type, metaclass=type): pass
+ ...
+ Traceback (most recent call last):
+ [...]
+ TypeError: __build_class__() got multiple values for keyword argument 'metaclass'
+ >>>
+
+Another way.
+
+ >>> kwds = {'metaclass': type}
+ >>> class C(metaclass=type, **kwds): pass
+ ...
+ Traceback (most recent call last):
+ [...]
+ TypeError: __build_class__() got multiple values for keyword argument 'metaclass'
+ >>>
+
+Use a __prepare__ method that returns an instrumented dict.
+
+ >>> class LoggingDict(dict):
+ ... def __setitem__(self, key, value):
+ ... print("d[%r] = %r" % (key, value))
+ ... dict.__setitem__(self, key, value)
+ ...
+ >>> class Meta(type):
+ ... @staticmethod
+ ... def __prepare__(name, bases):
+ ... return LoggingDict()
+ ...
+ >>> class C(metaclass=Meta):
+ ... foo = 2+2
+ ... foo = 42
+ ... bar = 123
+ ...
+ d['__module__'] = 'test.test_metaclass'
+ d['foo'] = 4
+ d['foo'] = 42
+ d['bar'] = 123
+ >>>
+
+Use a metaclass that doesn't derive from type.
+
+ >>> def meta(name, bases, namespace, **kwds):
+ ... print("meta:", name, bases)
+ ... print("ns:", sorted(namespace.items()))
+ ... print("kw:", sorted(kwds.items()))
+ ... return namespace
+ ...
+ >>> class C(metaclass=meta):
+ ... a = 42
+ ... b = 24
+ ...
+ meta: C ()
+ ns: [('__module__', 'test.test_metaclass'), ('a', 42), ('b', 24)]
+ kw: []
+ >>> type(C) is dict
+ True
+ >>> print(sorted(C.items()))
+ [('__module__', 'test.test_metaclass'), ('a', 42), ('b', 24)]
+ >>>
+
+And again, with a __prepare__ attribute.
+
+ >>> def prepare(name, bases, **kwds):
+ ... print("prepare:", name, bases, sorted(kwds.items()))
+ ... return LoggingDict()
+ ...
+ >>> meta.__prepare__ = prepare
+ >>> class C(metaclass=meta, other="booh"):
+ ... a = 1
+ ... a = 2
+ ... b = 3
+ ...
+ prepare: C () [('other', 'booh')]
+ d['__module__'] = 'test.test_metaclass'
+ d['a'] = 1
+ d['a'] = 2
+ d['b'] = 3
+ meta: C ()
+ ns: [('__module__', 'test.test_metaclass'), ('a', 2), ('b', 3)]
+ kw: [('other', 'booh')]
+ >>>
+
+"""
+
+__test__ = {'doctests' : doctests}
+
+def test_main(verbose=False):
+ from test import test_support
+ from test import test_metaclass
+ test_support.run_doctest(test_metaclass, verbose)
+
+if __name__ == "__main__":
+ test_main(verbose=True)
diff --git a/Lib/unittest.py b/Lib/unittest.py
index cde310f..eab0372 100644
--- a/Lib/unittest.py
+++ b/Lib/unittest.py
@@ -84,9 +84,6 @@
# Test framework core
##############################################################################
-# All classes defined herein are 'new-style' classes, allowing use of 'super()'
-__metaclass__ = type
-
def _strclass(cls):
return "%s.%s" % (cls.__module__, cls.__name__)
diff --git a/Modules/_bsddb.c b/Modules/_bsddb.c
index c3d3415..b3e33c5 100644
--- a/Modules/_bsddb.c
+++ b/Modules/_bsddb.c
@@ -5991,6 +5991,10 @@
* from both DBError and KeyError, since the API only supports
* using one base class. */
PyDict_SetItemString(d, "KeyError", PyExc_KeyError);
+ {
+ PyObject *builtin_mod = PyImport_ImportModule("__builtin__");
+ PyDict_SetItemString(d, "__builtins__", builtin_mod);
+ }
PyRun_String("class DBNotFoundError(DBError, KeyError): pass\n"
"class DBKeyEmptyError(DBError, KeyError): pass",
Py_file_input, d, d);
diff --git a/Parser/Python.asdl b/Parser/Python.asdl
index 3dc3c60..7c2c907 100644
--- a/Parser/Python.asdl
+++ b/Parser/Python.asdl
@@ -11,7 +11,12 @@
stmt = FunctionDef(identifier name, arguments args,
stmt* body, expr* decorators, expr? returns)
- | ClassDef(identifier name, expr* bases, stmt* body)
+ | ClassDef(identifier name,
+ expr* bases,
+ keyword* keywords,
+ expr? starargs,
+ expr? kwargs,
+ stmt* body)
| Return(expr? value)
| Delete(expr* targets)
diff --git a/Python/Python-ast.c b/Python/Python-ast.c
index c024a62..76c86c1 100644
--- a/Python/Python-ast.c
+++ b/Python/Python-ast.c
@@ -49,6 +49,9 @@
static char *ClassDef_fields[]={
"name",
"bases",
+ "keywords",
+ "starargs",
+ "kwargs",
"body",
};
static PyTypeObject *Return_type;
@@ -477,7 +480,7 @@
FunctionDef_type = make_type("FunctionDef", stmt_type,
FunctionDef_fields, 5);
if (!FunctionDef_type) return 0;
- ClassDef_type = make_type("ClassDef", stmt_type, ClassDef_fields, 3);
+ ClassDef_type = make_type("ClassDef", stmt_type, ClassDef_fields, 6);
if (!ClassDef_type) return 0;
Return_type = make_type("Return", stmt_type, Return_fields, 1);
if (!Return_type) return 0;
@@ -835,8 +838,9 @@
}
stmt_ty
-ClassDef(identifier name, asdl_seq * bases, asdl_seq * body, int lineno, int
- col_offset, PyArena *arena)
+ClassDef(identifier name, asdl_seq * bases, asdl_seq * keywords, expr_ty
+ starargs, expr_ty kwargs, asdl_seq * body, int lineno, int col_offset,
+ PyArena *arena)
{
stmt_ty p;
if (!name) {
@@ -850,6 +854,9 @@
p->kind = ClassDef_kind;
p->v.ClassDef.name = name;
p->v.ClassDef.bases = bases;
+ p->v.ClassDef.keywords = keywords;
+ p->v.ClassDef.starargs = starargs;
+ p->v.ClassDef.kwargs = kwargs;
p->v.ClassDef.body = body;
p->lineno = lineno;
p->col_offset = col_offset;
@@ -1974,6 +1981,21 @@
if (PyObject_SetAttrString(result, "bases", value) == -1)
goto failed;
Py_DECREF(value);
+ value = ast2obj_list(o->v.ClassDef.keywords, ast2obj_keyword);
+ if (!value) goto failed;
+ if (PyObject_SetAttrString(result, "keywords", value) == -1)
+ goto failed;
+ Py_DECREF(value);
+ value = ast2obj_expr(o->v.ClassDef.starargs);
+ if (!value) goto failed;
+ if (PyObject_SetAttrString(result, "starargs", value) == -1)
+ goto failed;
+ Py_DECREF(value);
+ value = ast2obj_expr(o->v.ClassDef.kwargs);
+ if (!value) goto failed;
+ if (PyObject_SetAttrString(result, "kwargs", value) == -1)
+ goto failed;
+ Py_DECREF(value);
value = ast2obj_list(o->v.ClassDef.body, ast2obj_stmt);
if (!value) goto failed;
if (PyObject_SetAttrString(result, "body", value) == -1)
diff --git a/Python/ast.c b/Python/ast.c
index c8357b1..d4c8967 100644
--- a/Python/ast.c
+++ b/Python/ast.c
@@ -2092,28 +2092,6 @@
return ast_for_testlist(c, n);
}
-/* like ast_for_testlist() but returns a sequence */
-static asdl_seq*
-ast_for_class_bases(struct compiling *c, const node* n)
-{
- /* testlist: test (',' test)* [','] */
- assert(NCH(n) > 0);
- REQ(n, testlist);
- if (NCH(n) == 1) {
- expr_ty base;
- asdl_seq *bases = asdl_seq_new(1, c->c_arena);
- if (!bases)
- return NULL;
- base = ast_for_expr(c, CHILD(n, 0));
- if (!base)
- return NULL;
- asdl_seq_SET(bases, 0, base);
- return bases;
- }
-
- return seq_for_testlist(c, n);
-}
-
static stmt_ty
ast_for_expr_stmt(struct compiling *c, const node *n)
{
@@ -3032,9 +3010,10 @@
static stmt_ty
ast_for_classdef(struct compiling *c, const node *n)
{
- /* classdef: 'class' NAME ['(' testlist ')'] ':' suite */
- asdl_seq *bases, *s;
-
+ /* classdef: 'class' NAME ['(' arglist ')'] ':' suite */
+ asdl_seq *s;
+ expr_ty call, dummy;
+
REQ(n, classdef);
if (!strcmp(STR(CHILD(n, 1)), "None")) {
@@ -3042,32 +3021,36 @@
return NULL;
}
- if (NCH(n) == 4) {
+ if (NCH(n) == 4) { /* class NAME ':' suite */
s = ast_for_suite(c, CHILD(n, 3));
if (!s)
return NULL;
- return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, s, LINENO(n),
- n->n_col_offset, c->c_arena);
+ return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, NULL, NULL, NULL, s,
+ LINENO(n), n->n_col_offset, c->c_arena);
}
- /* check for empty base list */
- if (TYPE(CHILD(n,3)) == RPAR) {
+
+ if (TYPE(CHILD(n, 3)) == RPAR) { /* class NAME '(' ')' ':' suite */
s = ast_for_suite(c, CHILD(n,5));
if (!s)
return NULL;
- return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, s, LINENO(n),
- n->n_col_offset, c->c_arena);
+ return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, NULL, NULL, NULL, s,
+ LINENO(n), n->n_col_offset, c->c_arena);
}
- /* else handle the base class list */
- bases = ast_for_class_bases(c, CHILD(n, 3));
- if (!bases)
+ /* class NAME '(' arglist ')' ':' suite */
+ /* build up a fake Call node so we can extract its pieces */
+ dummy = Name(NEW_IDENTIFIER(CHILD(n, 1)), Load, LINENO(n), n->n_col_offset, c->c_arena);
+ call = ast_for_call(c, CHILD(n, 3), dummy);
+ if (!call)
return NULL;
-
s = ast_for_suite(c, CHILD(n, 6));
if (!s)
return NULL;
- return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), bases, s, LINENO(n),
- n->n_col_offset, c->c_arena);
+
+ return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)),
+ call->v.Call.args, call->v.Call.keywords,
+ call->v.Call.starargs, call->v.Call.kwargs, s,
+ LINENO(n), n->n_col_offset, c->c_arena);
}
static stmt_ty
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 5d87744..4aa9c62 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -31,6 +31,113 @@
static PyObject *filtertuple (PyObject *, PyObject *);
static PyObject *
+builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *func, *name, *bases, *mkw, *meta, *prep, *ns, *res;
+ Py_ssize_t nargs, nbases;
+
+ assert(args != NULL);
+ if (!PyTuple_Check(args)) {
+ PyErr_SetString(PyExc_TypeError,
+ "__build_class__: args is not a tuple");
+ return NULL;
+ }
+ nargs = PyTuple_GET_SIZE(args);
+ if (nargs < 2) {
+ PyErr_SetString(PyExc_TypeError,
+ "__build_class__: not enough arguments");
+ return NULL;
+ }
+ func = PyTuple_GET_ITEM(args, 0); /* Better be callable */
+ name = PyTuple_GET_ITEM(args, 1);
+ if (!PyString_Check(name)) {
+ PyErr_SetString(PyExc_TypeError,
+ "__build_class__: name is not a string");
+ return NULL;
+ }
+ bases = PyTuple_GetSlice(args, 2, nargs);
+ if (bases == NULL)
+ return NULL;
+ nbases = nargs - 2;
+
+ if (kwds == NULL) {
+ meta = NULL;
+ mkw = NULL;
+ }
+ else {
+ mkw = PyDict_Copy(kwds); /* Don't modify kwds passed in! */
+ if (mkw == NULL) {
+ Py_DECREF(bases);
+ return NULL;
+ }
+ meta = PyDict_GetItemString(mkw, "metaclass");
+ if (meta != NULL) {
+ Py_INCREF(meta);
+ if (PyDict_DelItemString(mkw, "metaclass") < 0) {
+ Py_DECREF(meta);
+ Py_DECREF(mkw);
+ Py_DECREF(bases);
+ return NULL;
+ }
+ }
+ }
+ if (meta == NULL) {
+ if (PyTuple_GET_SIZE(bases) == 0)
+ meta = (PyObject *) (&PyType_Type);
+ else {
+ PyObject *base0 = PyTuple_GET_ITEM(bases, 0);
+ meta = (PyObject *) (base0->ob_type);
+ }
+ Py_INCREF(meta);
+ }
+ prep = PyObject_GetAttrString(meta, "__prepare__");
+ if (prep == NULL) {
+ PyErr_Clear();
+ ns = PyDict_New();
+ }
+ else {
+ PyObject *pargs = Py_BuildValue("OO", name, bases);
+ if (pargs == NULL) {
+ Py_DECREF(prep);
+ Py_DECREF(meta);
+ Py_XDECREF(mkw);
+ Py_DECREF(bases);
+ return NULL;
+ }
+ ns = PyEval_CallObjectWithKeywords(prep, pargs, mkw);
+ Py_DECREF(pargs);
+ Py_DECREF(prep);
+ if (ns == NULL) {
+ Py_DECREF(meta);
+ Py_XDECREF(mkw);
+ Py_DECREF(bases);
+ return NULL;
+ }
+ }
+ res = PyObject_CallFunctionObjArgs(func, ns, NULL);
+ if (res != NULL) {
+ PyObject *margs;
+ Py_DECREF(res);
+ res = NULL;
+ margs = Py_BuildValue("OOO", name, bases, ns);
+ if (margs != NULL) {
+ res = PyEval_CallObjectWithKeywords(meta, margs, mkw);
+ Py_DECREF(margs);
+ }
+ }
+ Py_DECREF(ns);
+ Py_DECREF(meta);
+ Py_XDECREF(mkw);
+ Py_DECREF(bases);
+ return res;
+}
+
+PyDoc_STRVAR(build_class_doc,
+"__build_class__(func, name, *bases, metaclass=None, **kwds) -> class\n\
+\n\
+Internal helper function used by the class statement.");
+
+static PyObject *
builtin___import__(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"name", "globals", "locals", "fromlist",
@@ -2103,6 +2210,8 @@
static PyMethodDef builtin_methods[] = {
+ {"__build_class__", (PyCFunction)builtin___build_class__,
+ METH_VARARGS | METH_KEYWORDS, build_class_doc},
{"__import__", (PyCFunction)builtin___import__, METH_VARARGS | METH_KEYWORDS, import_doc},
{"abs", builtin_abs, METH_O, abs_doc},
{"all", builtin_all, METH_O, all_doc},
diff --git a/Python/ceval.c b/Python/ceval.c
index 5cad632..5a3fc59 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -117,7 +117,6 @@
static PyObject * cmp_outcome(int, PyObject *, PyObject *);
static PyObject * import_from(PyObject *, PyObject *);
static int import_all_from(PyObject *, PyObject *);
-static PyObject * build_class(PyObject *, PyObject *, PyObject *);
static void set_exc_info(PyThreadState *, PyObject *, PyObject *, PyObject *);
static void reset_exc_info(PyThreadState *);
static void format_exc_check_arg(PyObject *, char *, PyObject *);
@@ -1532,14 +1531,12 @@
}
break;
- case LOAD_LOCALS:
- if ((x = f->f_locals) != NULL) {
- Py_INCREF(x);
- PUSH(x);
- continue;
- }
- PyErr_SetString(PyExc_SystemError, "no locals");
- break;
+ case STORE_LOCALS:
+ x = POP();
+ v = f->f_locals;
+ Py_XDECREF(v);
+ f->f_locals = x;
+ continue;
case RETURN_VALUE:
retval = POP();
@@ -1586,16 +1583,16 @@
Py_DECREF(v);
break;
- case BUILD_CLASS:
- u = TOP();
- v = SECOND();
- w = THIRD();
- STACKADJ(-2);
- x = build_class(u, v, w);
- SET_TOP(x);
- Py_DECREF(u);
- Py_DECREF(v);
- Py_DECREF(w);
+ case LOAD_BUILD_CLASS:
+ x = PyDict_GetItemString(f->f_builtins,
+ "__build_class__");
+ if (x == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "__build_class__ not found");
+ break;
+ }
+ Py_INCREF(x);
+ PUSH(x);
break;
case STORE_NAME:
@@ -4023,60 +4020,6 @@
return err;
}
-static PyObject *
-build_class(PyObject *methods, PyObject *bases, PyObject *name)
-{
- PyObject *metaclass = NULL, *result, *base;
-
- if (PyDict_Check(methods))
- metaclass = PyDict_GetItemString(methods, "__metaclass__");
- if (metaclass != NULL)
- Py_INCREF(metaclass);
- else if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {
- base = PyTuple_GET_ITEM(bases, 0);
- metaclass = PyObject_GetAttrString(base, "__class__");
- if (metaclass == NULL) {
- PyErr_Clear();
- metaclass = (PyObject *)base->ob_type;
- Py_INCREF(metaclass);
- }
- }
- else {
- PyObject *g = PyEval_GetGlobals();
- if (g != NULL && PyDict_Check(g))
- metaclass = PyDict_GetItemString(g, "__metaclass__");
- if (metaclass == NULL)
- metaclass = (PyObject *) &PyType_Type;
- Py_INCREF(metaclass);
- }
- result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,
- NULL);
- Py_DECREF(metaclass);
- if (result == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) {
- /* A type error here likely means that the user passed
- in a base that was not a class (such the random module
- instead of the random.random type). Help them out with
- by augmenting the error message with more information.*/
-
- PyObject *ptype, *pvalue, *ptraceback;
-
- PyErr_Fetch(&ptype, &pvalue, &ptraceback);
- if (PyString_Check(pvalue)) {
- PyObject *newmsg;
- newmsg = PyString_FromFormat(
- "Error when calling the metaclass bases\n"
- " %s",
- PyString_AS_STRING(pvalue));
- if (newmsg != NULL) {
- Py_DECREF(pvalue);
- pvalue = newmsg;
- }
- }
- PyErr_Restore(ptype, pvalue, ptraceback);
- }
- return result;
-}
-
static void
format_exc_check_arg(PyObject *exc, char *format_str, PyObject *obj)
{
diff --git a/Python/compile.c b/Python/compile.c
index 1e4dddf..4c22441 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -176,6 +176,11 @@
static int expr_constant(expr_ty e);
static int compiler_with(struct compiler *, stmt_ty);
+static int compiler_call_helper(struct compiler *c, int n,
+ asdl_seq *args,
+ asdl_seq *keywords,
+ expr_ty starargs,
+ expr_ty kwargs);
static PyCodeObject *assemble(struct compiler *, int addNone);
static PyObject *__doc__;
@@ -734,6 +739,8 @@
case PRINT_EXPR:
return -1;
+ case LOAD_BUILD_CLASS:
+ return 1;
case INPLACE_LSHIFT:
case INPLACE_RSHIFT:
case INPLACE_AND:
@@ -744,8 +751,8 @@
return 0;
case WITH_CLEANUP:
return -1; /* XXX Sometimes more */
- case LOAD_LOCALS:
- return 1;
+ case STORE_LOCALS:
+ return -1;
case RETURN_VALUE:
return -1;
case IMPORT_STAR:
@@ -757,8 +764,6 @@
return 0;
case END_FINALLY:
return -1; /* or -2 or -3 if exception occurred */
- case BUILD_CLASS:
- return -2;
case STORE_NAME:
return -1;
@@ -1509,54 +1514,107 @@
static int
compiler_class(struct compiler *c, stmt_ty s)
{
- int n;
+ static PyObject *build_class = NULL;
+ static PyObject *locals = NULL;
PyCodeObject *co;
PyObject *str;
- /* push class name on stack, needed by BUILD_CLASS */
- ADDOP_O(c, LOAD_CONST, s->v.ClassDef.name, consts);
- /* push the tuple of base classes on the stack */
- n = asdl_seq_LEN(s->v.ClassDef.bases);
- if (n > 0)
- VISIT_SEQ(c, expr, s->v.ClassDef.bases);
- ADDOP_I(c, BUILD_TUPLE, n);
- if (!compiler_enter_scope(c, s->v.ClassDef.name, (void *)s,
- s->lineno))
- return 0;
- c->u->u_private = s->v.ClassDef.name;
- Py_INCREF(c->u->u_private);
- str = PyString_InternFromString("__name__");
- if (!str || !compiler_nameop(c, str, Load)) {
- Py_XDECREF(str);
- compiler_exit_scope(c);
- return 0;
- }
-
- Py_DECREF(str);
- str = PyString_InternFromString("__module__");
- if (!str || !compiler_nameop(c, str, Store)) {
- Py_XDECREF(str);
- compiler_exit_scope(c);
- return 0;
- }
- Py_DECREF(str);
+ PySTEntryObject *ste;
- if (!compiler_body(c, s->v.ClassDef.body)) {
- compiler_exit_scope(c);
- return 0;
+ /* initialize statics */
+ if (build_class == NULL) {
+ build_class = PyString_FromString("__build_class__");
+ if (build_class == NULL)
+ return 0;
+ }
+ if (locals == NULL) {
+ locals = PyString_FromString("__locals__");
+ if (locals == NULL)
+ return 0;
}
- ADDOP_IN_SCOPE(c, LOAD_LOCALS);
- ADDOP_IN_SCOPE(c, RETURN_VALUE);
- co = assemble(c, 1);
+ /* ultimately generate code for:
+ <name> = __build_class__(<func>, <name>, *<bases>, **<keywords>)
+ where:
+ <func> is a function/closure created from the class body
+ <name> is the class name
+ <bases> is the positional arguments and *varargs argument
+ <keywords> is the keyword arguments and **kwds argument
+ This borrows from compiler_call.
+ */
+
+ /* 0. Create a fake variable named __locals__ */
+ ste = PySymtable_Lookup(c->c_st, s);
+ if (ste == NULL)
+ return 0;
+ assert(PyList_Check(ste->ste_varnames));
+ if (PyList_Append(ste->ste_varnames, locals) < 0)
+ return 0;
+
+ /* 1. compile the class body into a code object */
+ if (!compiler_enter_scope(c, s->v.ClassDef.name, (void *)s, s->lineno))
+ return 0;
+ /* this block represents what we do in the new scope */
+ {
+ /* use the class name for name mangling */
+ Py_INCREF(s->v.ClassDef.name);
+ c->u->u_private = s->v.ClassDef.name;
+ /* force it to have one mandatory argument */
+ c->u->u_argcount = 1;
+ /* load the first argument ... */
+ ADDOP_I(c, LOAD_FAST, 0);
+ /* ... and store it into f_locals */
+ ADDOP_IN_SCOPE(c, STORE_LOCALS);
+ /* load __name__ ... */
+ str = PyString_InternFromString("__name__");
+ if (!str || !compiler_nameop(c, str, Load)) {
+ Py_XDECREF(str);
+ compiler_exit_scope(c);
+ return 0;
+ }
+ Py_DECREF(str);
+ /* ... and store it as __module__ */
+ str = PyString_InternFromString("__module__");
+ if (!str || !compiler_nameop(c, str, Store)) {
+ Py_XDECREF(str);
+ compiler_exit_scope(c);
+ return 0;
+ }
+ Py_DECREF(str);
+ /* compile the body proper */
+ if (!compiler_body(c, s->v.ClassDef.body)) {
+ compiler_exit_scope(c);
+ return 0;
+ }
+ /* return None */
+ ADDOP_O(c, LOAD_CONST, Py_None, consts);
+ ADDOP_IN_SCOPE(c, RETURN_VALUE);
+ /* create the code object */
+ co = assemble(c, 1);
+ }
+ /* leave the new scope */
compiler_exit_scope(c);
if (co == NULL)
return 0;
+ /* 2. load the 'build_class' function */
+ ADDOP(c, LOAD_BUILD_CLASS);
+
+ /* 3. load a function (or closure) made from the code object */
compiler_make_closure(c, co, 0);
Py_DECREF(co);
- ADDOP_I(c, CALL_FUNCTION, 0);
- ADDOP(c, BUILD_CLASS);
+ /* 4. load class name */
+ ADDOP_O(c, LOAD_CONST, s->v.ClassDef.name, consts);
+
+ /* 5. generate the rest of the code for the call */
+ if (!compiler_call_helper(c, 2,
+ s->v.ClassDef.bases,
+ s->v.ClassDef.keywords,
+ s->v.ClassDef.starargs,
+ s->v.ClassDef.kwargs))
+ return 0;
+
+ /* 6. store into <name> */
if (!compiler_nameop(c, s->v.ClassDef.name, Store))
return 0;
return 1;
@@ -2613,21 +2671,37 @@
static int
compiler_call(struct compiler *c, expr_ty e)
{
- int n, code = 0;
-
VISIT(c, expr, e->v.Call.func);
- n = asdl_seq_LEN(e->v.Call.args);
- VISIT_SEQ(c, expr, e->v.Call.args);
- if (e->v.Call.keywords) {
- VISIT_SEQ(c, keyword, e->v.Call.keywords);
- n |= asdl_seq_LEN(e->v.Call.keywords) << 8;
+ return compiler_call_helper(c, 0,
+ e->v.Call.args,
+ e->v.Call.keywords,
+ e->v.Call.starargs,
+ e->v.Call.kwargs);
+}
+
+/* shared code between compiler_call and compiler_class */
+static int
+compiler_call_helper(struct compiler *c,
+ int n, /* Args already pushed */
+ asdl_seq *args,
+ asdl_seq *keywords,
+ expr_ty starargs,
+ expr_ty kwargs)
+{
+ int code = 0;
+
+ n += asdl_seq_LEN(args);
+ VISIT_SEQ(c, expr, args);
+ if (keywords) {
+ VISIT_SEQ(c, keyword, keywords);
+ n |= asdl_seq_LEN(keywords) << 8;
}
- if (e->v.Call.starargs) {
- VISIT(c, expr, e->v.Call.starargs);
+ if (starargs) {
+ VISIT(c, expr, starargs);
code |= 1;
}
- if (e->v.Call.kwargs) {
- VISIT(c, expr, e->v.Call.kwargs);
+ if (kwargs) {
+ VISIT(c, expr, kwargs);
code |= 2;
}
switch (code) {
diff --git a/Python/graminit.c b/Python/graminit.c
index 5c7fb6a..1287219 100644
--- a/Python/graminit.c
+++ b/Python/graminit.c
@@ -1635,7 +1635,7 @@
{23, 4},
};
static arc arcs_76_3[2] = {
- {9, 5},
+ {14, 5},
{15, 6},
};
static arc arcs_76_4[1] = {
diff --git a/Python/import.c b/Python/import.c
index fae60cf..33953c7 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -72,9 +72,10 @@
3030 (added keyword-only parameters)
3040 (added signature annotations)
3050 (print becomes a function)
+ 3060 (PEP 3115 metaclass syntax)
.
*/
-#define MAGIC (3050 | ((long)'\r'<<16) | ((long)'\n'<<24))
+#define MAGIC (3060 | ((long)'\r'<<16) | ((long)'\n'<<24))
/* Magic word as global; note that _PyImport_Init() can change the
value of this global to accommodate for alterations of how the
diff --git a/Python/symtable.c b/Python/symtable.c
index 5bac2a2..e9c9391 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -983,6 +983,11 @@
if (!symtable_add_def(st, s->v.ClassDef.name, DEF_LOCAL))
return 0;
VISIT_SEQ(st, expr, s->v.ClassDef.bases);
+ VISIT_SEQ(st, keyword, s->v.ClassDef.keywords);
+ if (s->v.ClassDef.starargs)
+ VISIT(st, expr, s->v.ClassDef.starargs);
+ if (s->v.ClassDef.kwargs)
+ VISIT(st, expr, s->v.ClassDef.kwargs);
if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock,
(void *)s, s->lineno))
return 0;