This issue tracker has been migrated to GitHub ,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2012年04月16日 00:23 by daniel.urban, last changed 2022年04月11日 14:57 by admin. This issue is now closed.
| Files | ||||
|---|---|---|---|---|
| File name | Uploaded | Description | Edit | |
| operator_build_class.patch | daniel.urban, 2012年04月16日 00:23 | operator.build_class - 1st patch | review | |
| operator_build_class_2.patch | daniel.urban, 2012年04月18日 20:50 | operator.build_class - 2nd patch with more tests | review | |
| operator_build_class_3.patch | daniel.urban, 2012年04月20日 18:10 | operator.build_class - 3rd patch with small fixes | review | |
| types_new_class.patch | daniel.urban, 2012年05月12日 09:49 | types.new_class - 1st patch | review | |
| Messages (12) | |||
|---|---|---|---|
| msg158382 - (view) | Author: Daniel Urban (daniel.urban) * (Python triager) | Date: 2012年04月16日 00:23 | |
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 |
|||
| msg158660 - (view) | Author: Daniel Urban (daniel.urban) * (Python triager) | Date: 2012年04月18日 20:50 | |
I've attached a patch with more tests. I simply copied and modified the tests about metaclass calculation and __prepare__ in test_descr.py, to create the tested classes with operator.build_class (and not the class statement).
Although, there is one thing I'm not sure I like about the API in the current patch: the dictionary corresponding to the keyword arguments of the class statement cannot be passed as keyword arguments. For example, I can't write this:
C = operator.build_class('C', (A, B), metaclass=MyMeta)
I have to write this:
C = operator.build_class('C', (A, B), {'metaclass': MyMeta})
(The reason for this is that the eval_body argument is the last.)
What would you think about the following signature for build_class?
build_class(name, bases=(), eval_body=None, **kwargs)
The fist 3 argument could be positional only, and all keyword arguments would go into the dict. A downside is that the user would have to explicitly pass None as the 3rd argument, if they don't need an eval_body, but need keyword-arguments. Also, the 'bases' and the keyword arguments wouldn't be close to each other as in the class statement...
|
|||
| msg158673 - (view) | Author: Alyssa Coghlan (ncoghlan) * (Python committer) | Date: 2012年04月18日 22:09 | |
I thought about that, and I'd prefer a dedicated dictionary to avoid questions of name conflicts.
Wrapping the keyword args in a dict() call is still pretty clean:
C = operator.build_class('C', (A, B), dict(metaclass=MyMeta))
|
|||
| msg158734 - (view) | Author: Daniel Urban (daniel.urban) * (Python triager) | Date: 2012年04月19日 15:50 | |
Fair enough. |
|||
| msg158792 - (view) | Author: Alyssa Coghlan (ncoghlan) * (Python committer) | Date: 2012年04月20日 03:18 | |
It occurs to me that, for naming consistency, the callback arg should be documented as "exec_body" rather than "eval_body". I'll try to get to a proper patch review this weekend. |
|||
| msg158866 - (view) | Author: Daniel Urban (daniel.urban) * (Python triager) | Date: 2012年04月20日 18:10 | |
I've attached the third patch with the eval_body -> exec_body change; explicitly passing the default (None) now also allowed. I also fixed a refleak (I think). |
|||
| msg160134 - (view) | Author: Alyssa Coghlan (ncoghlan) * (Python committer) | Date: 2012年05月07日 11:05 | |
In going to add documentation for your patch, I realised the operator module is not the right place for this. The "types" module actually seems like the most appropriate home, but that will require adding a _types module to back it. I'll post to python-dev to get additional feedback. |
|||
| msg160394 - (view) | Author: Alyssa Coghlan (ncoghlan) * (Python committer) | Date: 2012年05月11日 01:02 | |
Based on the python-dev thread [1], the proposed name for this API is now "types.new_class()". This parallels the existing imp.new_module() naming scheme and avoids various problems with the idea of using a static method on type itself (descriptors on type behave strangely, and the type namespace is accessible through *all* type objects, which would be weird in this case). Since types is a Python module, we now have to choose between 3 implementation strategies: - reimplement in pure Python (my preferred choice) - implement in terms of __build_class__ (would work, but may not be portable to other implementations and/or serves as a de facto promotion of __build_class__ up to being part of the language specification) - move Daniel's existing operator module based solution over to a new _types C extension module (again, may not help other implementations) The reason I find the idea of a pure Python reimplementation appealing is that it can then serve as a cross-check for any other implementations implementing PEP 3115 for their class statements. [1] http://mail.python.org/pipermail/python-dev/2012-May/119318.html |
|||
| msg160409 - (view) | Author: Éric Araujo (eric.araujo) * (Python committer) | Date: 2012年05月11日 11:41 | |
Implementing in pure Python seems to have a lot of pros and no con to me. |
|||
| msg160466 - (view) | Author: Daniel Urban (daniel.urban) * (Python triager) | Date: 2012年05月12日 09:49 | |
Here is my first attempt at creating a pure Python version of the operator.build_class function (in my previous patch) as types.new_class. The three added functions (two private and one public) correspond to the following functions in my previous patch: types.new_class -> operator.build_class types._prepare_ns -> prepare_namespace in typeobject.c types._calculate_mcls -> calculate_metaclass in typeobject.c (currently _PyType_CalculateMetaclass) (In Python these functions are quite short, so they may be merged. But this separation may be better for documentation purposes...) The tests are mostly the same as in my previous patch. |
|||
| msg161135 - (view) | Author: Roundup Robot (python-dev) (Python triager) | Date: 2012年05月19日 16:34 | |
New changeset befd56673c80 by Nick Coghlan in branch 'default': Close #14588: added a PEP 3115 compliant dynamic type creation mechanism http://hg.python.org/cpython/rev/befd56673c80 |
|||
| msg161157 - (view) | Author: Éric Araujo (eric.araujo) * (Python committer) | Date: 2012年05月19日 20:57 | |
Great doc patch. I think it would be worthwhile to backport it. |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022年04月11日 14:57:29 | admin | set | github: 58793 |
| 2012年05月19日 20:57:00 | eric.araujo | set | messages: + msg161157 |
| 2012年05月19日 16:34:27 | python-dev | set | status: open -> closed nosy: + python-dev messages: + msg161135 resolution: fixed stage: resolved |
| 2012年05月12日 09:49:17 | daniel.urban | set | files:
+ types_new_class.patch messages: + msg160466 components: + Library (Lib) |
| 2012年05月11日 11:41:50 | eric.araujo | set | messages: + msg160409 |
| 2012年05月11日 01:02:54 | ncoghlan | set | messages: + msg160394 |
| 2012年05月07日 11:05:21 | ncoghlan | set | messages: + msg160134 |
| 2012年04月20日 18:10:45 | daniel.urban | set | files:
+ operator_build_class_3.patch messages: + msg158866 |
| 2012年04月20日 04:29:57 | r.david.murray | set | nosy:
+ r.david.murray |
| 2012年04月20日 03:18:44 | ncoghlan | set | messages: + msg158792 |
| 2012年04月19日 15:50:51 | daniel.urban | set | messages: + msg158734 |
| 2012年04月18日 22:09:21 | ncoghlan | set | messages: + msg158673 |
| 2012年04月18日 20:50:32 | daniel.urban | set | files:
+ operator_build_class_2.patch messages: + msg158660 |
| 2012年04月16日 13:31:15 | eric.araujo | set | nosy:
+ eric.araujo |
| 2012年04月16日 11:40:08 | r.david.murray | set | assignee: ncoghlan |
| 2012年04月16日 00:23:18 | daniel.urban | create | |