summaryrefslogtreecommitdiff
path: root/Lib/types.py
diff options
context:
space:
mode:
authorNick Coghlan <ncoghlan@gmail.com>2012-05-20 02:34:13 +1000
committerNick Coghlan <ncoghlan@gmail.com>2012-05-20 02:34:13 +1000
commit7fc570a51e6d8647d73e152721b2e72add72d134 (patch)
treee735131f83155d73d136ef2d3d8e3c7ed1d653b3 /Lib/types.py
parent7c5ba513b924692e534e372a135add15e70ac0cb (diff)
downloadcpython-git-7fc570a51e6d8647d73e152721b2e72add72d134.tar.gz
Close #14588: added a PEP 3115 compliant dynamic type creation mechanism
Diffstat (limited to 'Lib/types.py')
-rw-r--r--Lib/types.py58
1 files changed, 58 insertions, 0 deletions
diff --git a/Lib/types.py b/Lib/types.py
index 08cbb83082..2bfcd9be8d 100644
--- a/Lib/types.py
+++ b/Lib/types.py
@@ -40,3 +40,61 @@ GetSetDescriptorType = type(FunctionType.__code__)
MemberDescriptorType = type(FunctionType.__globals__)
del sys, _f, _g, _C, # Not for export
+
+
+# Provide a PEP 3115 compliant mechanism for class creation
+def new_class(name, bases=(), kwds=None, exec_body=None):
+ """Create a class object dynamically using the appropriate metaclass."""
+ meta, ns, kwds = prepare_class(name, bases, kwds)
+ if exec_body is not None:
+ exec_body(ns)
+ return meta(name, bases, ns, **kwds)
+
+def prepare_class(name, bases=(), kwds=None):
+ """Call the __prepare__ method of the appropriate metaclass.
+
+ Returns (metaclass, namespace, kwds) as a 3-tuple
+
+ *metaclass* is the appropriate metaclass
+ *namespace* is the prepared class namespace
+ *kwds* is an updated copy of the passed in kwds argument with any
+ 'metaclass' entry removed. If no kwds argument is passed in, this will
+ be an empty dict.
+ """
+ if kwds is None:
+ kwds = {}
+ else:
+ kwds = dict(kwds) # Don't alter the provided mapping
+ if 'metaclass' in kwds:
+ meta = kwds.pop('metaclass')
+ else:
+ if bases:
+ meta = type(bases[0])
+ else:
+ meta = type
+ if isinstance(meta, type):
+ # when meta is a type, we first determine the most-derived metaclass
+ # instead of invoking the initial candidate directly
+ meta = _calculate_meta(meta, bases)
+ if hasattr(meta, '__prepare__'):
+ ns = meta.__prepare__(name, bases, **kwds)
+ else:
+ ns = {}
+ return meta, ns, kwds
+
+def _calculate_meta(meta, bases):
+ """Calculate the most derived metaclass."""
+ winner = meta
+ for base in bases:
+ base_meta = type(base)
+ if issubclass(winner, base_meta):
+ continue
+ if issubclass(base_meta, winner):
+ winner = base_meta
+ continue
+ # else:
+ raise TypeError("metaclass conflict: "
+ "the metaclass of a derived class "
+ "must be a (non-strict) subclass "
+ "of the metaclasses of all its bases")
+ return winner