New submission from Daniel Urban <urban.dani...@gmail.com>:

As Nick Coghlan proposed [1, 2], there should be a way to dynamically create 
classes, which handles metaclasses correctly (see also issue1294232).

Here is my first attempt at creating an operator.build_class method. It only 
includes very simple tests and no documentation, but I will write them if 
needed.

With this patch there are two functions for creating a class object:
1. __build_class__ (no change)
2. operator.build_class(name, bases=(), kwds=None, eval_body=None): finds the 
correct metaclass and calls its __prepare__. If eval_body is given, calls it 
with the namespace returned by __prepare__. Then calls the correct metaclass, 
and returns the created class object.

Both of these functions (after parsing their arguments) call 
_PyType_BuildClass, a new C API function. The first argument of this function 
is a callable, that will be called with the namespace returned by __prepare__ 
(it also can be NULL, in that case nothing will be called). __build_class__ 
passes the function that is the body of the class statement. 
operator.build_class passes the callable given by the user (or NULL, if the 
user didn't pass the eval_body argument). The implementation of 
_PyType_BuildClass is approximately the following:

def _PyType_BuildClass(func=None, name, bases, kwds={}):
    meta = kwds.pop('metaclass', None)
    if meta is None:
        if not bases:
            meta = type
        else:
            meta = type(bases[0])
    ns, meta = prepare_namespace(name, meta, bases, kwds)
    if func is not None:
        func(ns)
    return meta(name, bases, ns, kwds)

(Actually the return value of the func is used if it's a cell object. I'm not 
sure, why and when this is needed, this code comes from __build_class__.)

The changes are in the following files:

1. object.h: the exported function is _PyType_BuildClass instead of 
_PyType_CalculateMetaclass (that doesn't need to be in the include file 
anymore).

2. operator.c: the build_class method checks its arguments, then calls 
_PyType_BuildClass.

3. typeobject.c:

_PyType_CalculateMetaclass is renamed to calculate_metaclass, because now it is 
only called from this file.

prepare_namespace calls calculate_metaclass to determine the correct metaclass, 
then calls its __prepare__ method. (This code is moved here mostly from 
__build_class__). It also passes back the correct metaclass to its caller.

_PyType_BuildClass gets the starting metaclass from its arguments. Then it 
calls prepare_namespace to get the namespace and the correct metaclass. If it 
received a non-NULL first argument (the function that is the class body or the 
eval_body argument of operator.build_class), then calls it, passing the 
namespace. Then it calls the correct metaclass. (Most of this code is also from 
__build_class__.)

4. bltinmodule.c: builtin___build_class__ now only parses its arguments, and 
simply calls _PyType_BuildClass.

5. test_operator.py: a simple test for operator.build_class


[1] http://mail.python.org/pipermail/python-dev/2011-April/110874.html
[2] http://mail.python.org/pipermail/python-dev/2012-April/118732.html

----------
components: Extension Modules, Interpreter Core
files: operator_build_class.patch
keywords: patch
messages: 158382
nosy: durban, ncoghlan
priority: normal
severity: normal
status: open
title: PEP 3115 compliant dynamic class creation
type: enhancement
versions: Python 3.3
Added file: http://bugs.python.org/file25231/operator_build_class.patch

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue14588>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to