
This issue trackerhas been migrated toGitHub, and is currentlyread-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.
Created on2012-04-16 00:23 bydaniel.urban, last changed2022-04-11 14:57 byadmin. This issue is nowclosed.
| 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)*![]() | 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 alsoissue1294232).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)*![]() | 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)*![]() | 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)*![]() | Date: 2012-04-19 15:50 | |
Fair enough. | |||
| msg158792 -(view) | Author: Alyssa Coghlan (ncoghlan)*![]() | 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)*![]() | 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)*![]() | 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)*![]() | 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 implementingPEP 3115 for their class statements.[1]http://mail.python.org/pipermail/python-dev/2012-May/119318.html | |||
| msg160409 -(view) | Author: Éric Araujo (eric.araujo)*![]() | 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)*![]() | 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_classtypes._prepare_ns -> prepare_namespace in typeobject.ctypes._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)![]() | Date: 2012-05-19 16:34 | |
New changesetbefd56673c80 by Nick Coghlan in branch 'default':Close#14588: added aPEP 3115 compliant dynamic type creation mechanismhttp://hg.python.org/cpython/rev/befd56673c80 | |||
| msg161157 -(view) | Author: Éric Araujo (eric.araujo)*![]() | 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 | |