diff options
author | Guido van Rossum <guido@python.org> | 2007-03-18 15:41:51 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2007-03-18 15:41:51 (GMT) |
commit | 52cc1d838f4fee573e57b5b182d8e5f5db63240f (patch) | |
tree | 0ea50468d2b04ee12131c155c5918e8a70dafe1c /Lib | |
parent | ef17c16b366b09a78dfe5fc5171fe2b0b29f60e5 (diff) | |
download | cpython-52cc1d838f4fee573e57b5b182d8e5f5db63240f.zip cpython-52cc1d838f4fee573e57b5b182d8e5f5db63240f.tar.gz cpython-52cc1d838f4fee573e57b5b182d8e5f5db63240f.tar.bz2 |
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.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/build_class.py | 0 | ||||
-rw-r--r-- | Lib/ctypes/_endian.py | 6 | ||||
-rw-r--r-- | Lib/opcode.py | 5 | ||||
-rw-r--r-- | Lib/string.py | 3 | ||||
-rw-r--r-- | Lib/test/crashers/modify_dict_attr.py | 3 | ||||
-rw-r--r-- | Lib/test/leakers/test_selftype.py | 4 | ||||
-rw-r--r-- | Lib/test/pickletester.py | 4 | ||||
-rw-r--r-- | Lib/test/test_ast.py | 8 | ||||
-rw-r--r-- | Lib/test/test_copy.py | 4 | ||||
-rw-r--r-- | Lib/test/test_descr.py | 81 | ||||
-rw-r--r-- | Lib/test/test_metaclass.py | 218 | ||||
-rw-r--r-- | Lib/unittest.py | 3 |
12 files changed, 278 insertions, 61 deletions
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 @@ if sys.byteorder == "little": 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_XOR', 65) 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_XOR', 78) 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 @@ class _TemplateMetaclass(type): 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 Y(object): 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 @@ import gc 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 initarg(C): 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 @@ def run_tests(): (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 @@ class TestCopy(unittest.TestCase): # 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 @@ def pylists(): 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 @@ def metaclass(): 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 metaclass(): 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 @@ def metaclass(): 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 @@ def metaclass(): 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 @@ def metaclass(): 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 @@ def metaclass(): 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 @@ def metaclass(): 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 @@ def dynamics(): # 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 @@ def altmro(): 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 @@ def test_mutable_bases_with_failing_mro(): 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 subclass_right_op(): 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 @@ if sys.version_info[:2] < (2, 2): # 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__) |