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/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)