From 52cc1d838f4fee573e57b5b182d8e5f5db63240f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 18 Mar 2007 15:41:51 +0000 Subject: 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. --- Lib/test/test_metaclass.py | 218 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 Lib/test/test_metaclass.py (limited to 'Lib/test/test_metaclass.py') diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py new file mode 100644 index 0000000000..df81079d1b --- /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', (,)) {'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', (,)) {'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', (, )) {'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) -- cgit v1.2.1