Commit 7a91570a authored by Stefan Behnel's avatar Stefan Behnel

implement __qualname__ special attribute on Python functions/classes (PEP 3155)

parent 8c941d9d
...@@ -8,6 +8,9 @@ Cython Changelog ...@@ -8,6 +8,9 @@ Cython Changelog
Features added Features added
-------------- --------------
* Python functions/classes provide the special attribute "__qualname__"
as defined by PEP 3155.
* Added a directive ``overflowcheck`` which raises an OverflowException when * Added a directive ``overflowcheck`` which raises an OverflowException when
arithmetic with C ints overflow. This has a modest performance penalty, but arithmetic with C ints overflow. This has a modest performance penalty, but
is much faster than using Python ints. is much faster than using Python ints.
......
...@@ -6017,13 +6017,22 @@ class DictItemNode(ExprNode): ...@@ -6017,13 +6017,22 @@ class DictItemNode(ExprNode):
class ModuleNameMixin(object): class ModuleNameMixin(object):
def set_mod_name(self, env): def set_qualified_name(self, env, self_name):
self.module_name = env.global_scope().qualified_name self.module_name = env.global_scope().qualified_name
prefix = env.qualified_name[len(self.module_name)+1:]
if prefix:
self_name = prefix + '.' + self_name
self.qualname = StringEncoding.EncodedString(self_name)
def get_py_mod_name(self, code): def get_py_mod_name(self, code):
return code.get_py_string_const( return code.get_py_string_const(
self.module_name, identifier=True) self.module_name, identifier=True)
def get_py_qualified_name(self, code):
return code.get_py_string_const(
self.qualname, identifier=True)
class ClassNode(ExprNode, ModuleNameMixin): class ClassNode(ExprNode, ModuleNameMixin):
# Helper class used in the implementation of Python # Helper class used in the implementation of Python
# class definitions. Constructs a class object given # class definitions. Constructs a class object given
...@@ -6046,7 +6055,7 @@ class ClassNode(ExprNode, ModuleNameMixin): ...@@ -6046,7 +6055,7 @@ class ClassNode(ExprNode, ModuleNameMixin):
self.is_temp = 1 self.is_temp = 1
env.use_utility_code(UtilityCode.load_cached("CreateClass", "ObjectHandling.c")) env.use_utility_code(UtilityCode.load_cached("CreateClass", "ObjectHandling.c"))
#TODO(craig,haoyu) This should be moved to a better place #TODO(craig,haoyu) This should be moved to a better place
self.set_mod_name(env) self.set_qualified_name(env, self.name)
def may_be_none(self): def may_be_none(self):
return True return True
...@@ -6062,12 +6071,14 @@ class ClassNode(ExprNode, ModuleNameMixin): ...@@ -6062,12 +6071,14 @@ class ClassNode(ExprNode, ModuleNameMixin):
self.dict.py_result(), self.dict.py_result(),
self.doc.py_result())) self.doc.py_result()))
py_mod_name = self.get_py_mod_name(code) py_mod_name = self.get_py_mod_name(code)
qualname = self.get_py_qualified_name(code)
code.putln( code.putln(
'%s = __Pyx_CreateClass(%s, %s, %s, %s); %s' % ( '%s = __Pyx_CreateClass(%s, %s, %s, %s, %s); %s' % (
self.result(), self.result(),
self.bases.py_result(), self.bases.py_result(),
self.dict.py_result(), self.dict.py_result(),
cname, cname,
qualname,
py_mod_name, py_mod_name,
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
...@@ -6264,7 +6275,7 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin): ...@@ -6264,7 +6275,7 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
self.type = py_object_type self.type = py_object_type
self.is_temp = 1 self.is_temp = 1
#TODO(craig,haoyu) This should be moved to a better place #TODO(craig,haoyu) This should be moved to a better place
self.set_mod_name(env) self.set_qualified_name(env, self.name)
def may_be_none(self): def may_be_none(self):
return True return True
...@@ -6272,16 +6283,18 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin): ...@@ -6272,16 +6283,18 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
def generate_result_code(self, code): def generate_result_code(self, code):
cname = code.intern_identifier(self.name) cname = code.intern_identifier(self.name)
py_mod_name = self.get_py_mod_name(code) py_mod_name = self.get_py_mod_name(code)
qualname = self.get_py_qualified_name(code)
if self.doc: if self.doc:
doc_code = self.doc.result() doc_code = self.doc.result()
else: else:
doc_code = '(PyObject *) NULL' doc_code = '(PyObject *) NULL'
code.putln( code.putln(
"%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s); %s" % ( "%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % (
self.result(), self.result(),
self.metaclass.result(), self.metaclass.result(),
self.bases.result(), self.bases.result(),
cname, cname,
qualname,
self.mkw.result(), self.mkw.result(),
py_mod_name, py_mod_name,
doc_code, doc_code,
...@@ -6449,7 +6462,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -6449,7 +6462,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
self.analyse_default_args(env) self.analyse_default_args(env)
#TODO(craig,haoyu) This should be moved to a better place #TODO(craig,haoyu) This should be moved to a better place
self.set_mod_name(env) self.set_qualified_name(env, self.def_node.name)
def analyse_default_args(self, env): def analyse_default_args(self, env):
""" """
...@@ -6575,15 +6588,15 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -6575,15 +6588,15 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
else: else:
flags = '0' flags = '0'
py_mod_name = self.get_py_mod_name(code)
code.putln( code.putln(
'%s = %s(&%s, %s, %s, %s, %s); %s' % ( '%s = %s(&%s, %s, %s, %s, %s, %s); %s' % (
self.result(), self.result(),
constructor, constructor,
self.pymethdef_cname, self.pymethdef_cname,
flags, flags,
self.get_py_qualified_name(code),
self.self_result_code(), self.self_result_code(),
py_mod_name, self.get_py_mod_name(code),
code_object_result, code_object_result,
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
......
...@@ -25,6 +25,7 @@ typedef struct { ...@@ -25,6 +25,7 @@ typedef struct {
PyObject *func_dict; PyObject *func_dict;
PyObject *func_weakreflist; PyObject *func_weakreflist;
PyObject *func_name; PyObject *func_name;
PyObject *func_qualname;
PyObject *func_doc; PyObject *func_doc;
PyObject *func_code; PyObject *func_code;
PyObject *func_closure; PyObject *func_closure;
...@@ -41,11 +42,11 @@ typedef struct { ...@@ -41,11 +42,11 @@ typedef struct {
static PyTypeObject *__pyx_CyFunctionType = 0; static PyTypeObject *__pyx_CyFunctionType = 0;
#define __Pyx_CyFunction_NewEx(ml, flags, self, module, code) \ #define __Pyx_CyFunction_NewEx(ml, flags, qualname, self, module, code) \
__Pyx_CyFunction_New(__pyx_CyFunctionType, ml, flags, self, module, code) __Pyx_CyFunction_New(__pyx_CyFunctionType, ml, flags, qualname, self, module, code)
static PyObject *__Pyx_CyFunction_New(PyTypeObject *, static PyObject *__Pyx_CyFunction_New(PyTypeObject *, PyMethodDef *ml,
PyMethodDef *ml, int flags, int flags, PyObject* qualname,
PyObject *self, PyObject *module, PyObject *self, PyObject *module,
PyObject* code); PyObject* code);
...@@ -127,6 +128,34 @@ __Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value) ...@@ -127,6 +128,34 @@ __Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value)
return 0; return 0;
} }
static PyObject *
__Pyx_CyFunction_get_qualname(__pyx_CyFunctionObject *op)
{
Py_INCREF(op->func_qualname);
return op->func_qualname;
}
static int
__Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value)
{
PyObject *tmp;
#if PY_MAJOR_VERSION >= 3
if (value == NULL || !PyUnicode_Check(value)) {
#else
if (value == NULL || !PyString_Check(value)) {
#endif
PyErr_SetString(PyExc_TypeError,
"__qualname__ must be set to a string object");
return -1;
}
tmp = op->func_qualname;
Py_INCREF(value);
op->func_qualname = value;
Py_XDECREF(tmp);
return 0;
}
static PyObject * static PyObject *
__Pyx_CyFunction_get_self(__pyx_CyFunctionObject *m, CYTHON_UNUSED void *closure) __Pyx_CyFunction_get_self(__pyx_CyFunctionObject *m, CYTHON_UNUSED void *closure)
{ {
...@@ -230,6 +259,7 @@ static PyGetSetDef __pyx_CyFunction_getsets[] = { ...@@ -230,6 +259,7 @@ static PyGetSetDef __pyx_CyFunction_getsets[] = {
{(char *) "__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0}, {(char *) "__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0},
{(char *) "func_name", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0}, {(char *) "func_name", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
{(char *) "__name__", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0}, {(char *) "__name__", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
{(char *) "__qualname__", (getter)__Pyx_CyFunction_get_qualname, (setter)__Pyx_CyFunction_set_qualname, 0, 0},
{(char *) "__self__", (getter)__Pyx_CyFunction_get_self, 0, 0, 0}, {(char *) "__self__", (getter)__Pyx_CyFunction_get_self, 0, 0, 0},
{(char *) "func_dict", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0}, {(char *) "func_dict", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0},
{(char *) "__dict__", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0}, {(char *) "__dict__", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0},
...@@ -269,7 +299,7 @@ static PyMethodDef __pyx_CyFunction_methods[] = { ...@@ -269,7 +299,7 @@ static PyMethodDef __pyx_CyFunction_methods[] = {
}; };
static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags, static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags, PyObject* qualname,
PyObject *closure, PyObject *module, PyObject* code) { PyObject *closure, PyObject *module, PyObject* code) {
__pyx_CyFunctionObject *op = PyObject_GC_New(__pyx_CyFunctionObject, type); __pyx_CyFunctionObject *op = PyObject_GC_New(__pyx_CyFunctionObject, type);
if (op == NULL) if (op == NULL)
...@@ -284,6 +314,8 @@ static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int f ...@@ -284,6 +314,8 @@ static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int f
op->func.m_module = module; op->func.m_module = module;
op->func_dict = NULL; op->func_dict = NULL;
op->func_name = NULL; op->func_name = NULL;
Py_INCREF(qualname);
op->func_qualname = qualname;
op->func_doc = NULL; op->func_doc = NULL;
op->func_classobj = NULL; op->func_classobj = NULL;
Py_XINCREF(code); Py_XINCREF(code);
...@@ -304,6 +336,7 @@ __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) ...@@ -304,6 +336,7 @@ __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m)
Py_CLEAR(m->func.m_module); Py_CLEAR(m->func.m_module);
Py_CLEAR(m->func_dict); Py_CLEAR(m->func_dict);
Py_CLEAR(m->func_name); Py_CLEAR(m->func_name);
Py_CLEAR(m->func_qualname);
Py_CLEAR(m->func_doc); Py_CLEAR(m->func_doc);
Py_CLEAR(m->func_code); Py_CLEAR(m->func_code);
Py_CLEAR(m->func_classobj); Py_CLEAR(m->func_classobj);
...@@ -338,6 +371,7 @@ static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, ...@@ -338,6 +371,7 @@ static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit,
Py_VISIT(m->func.m_module); Py_VISIT(m->func.m_module);
Py_VISIT(m->func_dict); Py_VISIT(m->func_dict);
Py_VISIT(m->func_name); Py_VISIT(m->func_name);
Py_VISIT(m->func_qualname);
Py_VISIT(m->func_doc); Py_VISIT(m->func_doc);
Py_VISIT(m->func_code); Py_VISIT(m->func_code);
Py_VISIT(m->func_classobj); Py_VISIT(m->func_classobj);
...@@ -378,14 +412,12 @@ static PyObject *__Pyx_CyFunction_descr_get(PyObject *func, PyObject *obj, PyObj ...@@ -378,14 +412,12 @@ static PyObject *__Pyx_CyFunction_descr_get(PyObject *func, PyObject *obj, PyObj
static PyObject* static PyObject*
__Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) __Pyx_CyFunction_repr(__pyx_CyFunctionObject *op)
{ {
PyObject *func_name = __Pyx_CyFunction_get_name(op);
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
return PyUnicode_FromFormat("<cyfunction %U at %p>", return PyUnicode_FromFormat("<cyfunction %U at %p>",
func_name, (void *)op); op->func_qualname, (void *)op);
#else #else
return PyString_FromFormat("<cyfunction %s at %p>", return PyString_FromFormat("<cyfunction %s at %p>",
PyString_AsString(func_name), (void *)op); PyString_AsString(op->func_qualname), (void *)op);
#endif #endif
} }
...@@ -555,11 +587,11 @@ typedef struct { ...@@ -555,11 +587,11 @@ typedef struct {
PyObject *self; PyObject *self;
} __pyx_FusedFunctionObject; } __pyx_FusedFunctionObject;
#define __pyx_FusedFunction_NewEx(ml, flags, self, module, code) \ #define __pyx_FusedFunction_NewEx(ml, flags, qualname, self, module, code) \
__pyx_FusedFunction_New(__pyx_FusedFunctionType, ml, flags, self, module, code) __pyx_FusedFunction_New(__pyx_FusedFunctionType, ml, flags, qualname, self, module, code)
static PyObject *__pyx_FusedFunction_New(PyTypeObject *type, static PyObject *__pyx_FusedFunction_New(PyTypeObject *type,
PyMethodDef *ml, int flags, PyMethodDef *ml, int flags,
PyObject *self, PyObject *module, PyObject *qualname, PyObject *self, PyObject *module,
PyObject *code); PyObject *code);
static PyTypeObject *__pyx_FusedFunctionType = NULL; static PyTypeObject *__pyx_FusedFunctionType = NULL;
...@@ -571,11 +603,12 @@ static int __pyx_FusedFunction_init(void); ...@@ -571,11 +603,12 @@ static int __pyx_FusedFunction_init(void);
//@requires: CythonFunction //@requires: CythonFunction
static PyObject * static PyObject *
__pyx_FusedFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags, PyObject *self, __pyx_FusedFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags,
PyObject *qualname, PyObject *self,
PyObject *module, PyObject *code) PyObject *module, PyObject *code)
{ {
__pyx_FusedFunctionObject *fusedfunc = __pyx_FusedFunctionObject *fusedfunc =
(__pyx_FusedFunctionObject *) __Pyx_CyFunction_New(type, ml, flags, (__pyx_FusedFunctionObject *) __Pyx_CyFunction_New(type, ml, flags, qualname,
self, module, code); self, module, code);
if (!fusedfunc) if (!fusedfunc)
return NULL; return NULL;
...@@ -632,6 +665,7 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type) ...@@ -632,6 +665,7 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
meth = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_NewEx( meth = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_NewEx(
((PyCFunctionObject *) func)->m_ml, ((PyCFunctionObject *) func)->m_ml,
((__pyx_CyFunctionObject *) func)->flags, ((__pyx_CyFunctionObject *) func)->flags,
((__pyx_CyFunctionObject *) func)->func_qualname,
((__pyx_CyFunctionObject *) func)->func_closure, ((__pyx_CyFunctionObject *) func)->func_closure,
((PyCFunctionObject *) func)->m_module, ((PyCFunctionObject *) func)->m_module,
((__pyx_CyFunctionObject *) func)->func_code); ((__pyx_CyFunctionObject *) func)->func_code);
......
...@@ -443,18 +443,20 @@ static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw) { ...@@ -443,18 +443,20 @@ static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw) {
/////////////// CreateClass.proto /////////////// /////////////// CreateClass.proto ///////////////
static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name,
PyObject *modname); /*proto*/ PyObject *qualname, PyObject *modname); /*proto*/
/////////////// CreateClass /////////////// /////////////// CreateClass ///////////////
//@requires: FindPy2Metaclass //@requires: FindPy2Metaclass
static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name,
PyObject *modname) { PyObject *qualname, PyObject *modname) {
PyObject *result; PyObject *result;
PyObject *metaclass; PyObject *metaclass;
if (PyDict_SetItemString(dict, "__module__", modname) < 0) if (PyDict_SetItemString(dict, "__module__", modname) < 0)
return NULL; return NULL;
if (PyDict_SetItemString(dict, "__qualname__", qualname) < 0)
return NULL;
/* Python2 __metaclass__ */ /* Python2 __metaclass__ */
metaclass = PyDict_GetItemString(dict, "__metaclass__"); metaclass = PyDict_GetItemString(dict, "__metaclass__");
...@@ -470,13 +472,13 @@ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *na ...@@ -470,13 +472,13 @@ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *na
/////////////// Py3ClassCreate.proto /////////////// /////////////// Py3ClassCreate.proto ///////////////
static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, PyObject *mkw, PyObject *modname, PyObject *doc); /*proto*/ static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, PyObject *qualname, PyObject *mkw, PyObject *modname, PyObject *doc); /*proto*/
static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObject *bases, PyObject *dict, PyObject *mkw); /*proto*/ static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObject *bases, PyObject *dict, PyObject *mkw); /*proto*/
/////////////// Py3ClassCreate /////////////// /////////////// Py3ClassCreate ///////////////
static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name,
PyObject *mkw, PyObject *modname, PyObject *doc) { PyObject *qualname, PyObject *mkw, PyObject *modname, PyObject *doc) {
PyObject *prep; PyObject *prep;
PyObject *pargs; PyObject *pargs;
PyObject *ns; PyObject *ns;
...@@ -519,6 +521,24 @@ static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, ...@@ -519,6 +521,24 @@ static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases,
return NULL; return NULL;
} }
Py_DECREF(str); Py_DECREF(str);
#if PY_MAJOR_VERSION >= 3
str = PyUnicode_FromString("__qualname__");
#else
str = PyString_FromString("__qualname__");
#endif
if (!str) {
Py_DECREF(ns);
return NULL;
}
if (PyObject_SetItem(ns, str, qualname) < 0) {
Py_DECREF(ns);
Py_DECREF(str);
return NULL;
}
Py_DECREF(str);
if (doc) { if (doc) {
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
str = PyUnicode_FromString("__doc__"); str = PyUnicode_FromString("__doc__");
......
...@@ -31,6 +31,38 @@ def test_name(): ...@@ -31,6 +31,38 @@ def test_name():
'foo' 'foo'
""" """
def test_qualname():
"""
>>> test_qualname.__qualname__
'test_qualname'
>>> test_qualname.__qualname__ = 123 #doctest:+ELLIPSIS
Traceback (most recent call last):
TypeError: __qualname__ must be set to a ... object
>>> test_qualname.__qualname__ = 'foo'
>>> test_qualname.__qualname__
'foo'
"""
def test_nested_qualname():
"""
>>> func = test_nested_qualname()
>>> func().__qualname__
'test_nested_qualname.outer.Test'
>>> func().test.__qualname__
'test_nested_qualname.outer.Test.test'
>>> func()().test.__qualname__
'test_nested_qualname.outer.Test.test'
"""
def outer():
class Test(object):
def test(self):
return 123
return Test
return outer
def test_doc(): def test_doc():
""" """
>>> del test_doc.__doc__ >>> del test_doc.__doc__
......
...@@ -49,7 +49,7 @@ class Py3ClassMCOnly(object, metaclass=Py3MetaclassPlusAttr): ...@@ -49,7 +49,7 @@ class Py3ClassMCOnly(object, metaclass=Py3MetaclassPlusAttr):
>>> obj.metaclass_was_here >>> obj.metaclass_was_here
True True
>>> obj._order >>> obj._order
['__module__', '__doc__', 'bar', 'metaclass_was_here'] ['__module__', '__qualname__', '__doc__', 'bar', 'metaclass_was_here']
""" """
bar = 321 bar = 321
...@@ -76,7 +76,7 @@ class Py3Foo(object, metaclass=Py3Base, foo=123): ...@@ -76,7 +76,7 @@ class Py3Foo(object, metaclass=Py3Base, foo=123):
>>> obj.bar >>> obj.bar
321 321
>>> obj._order >>> obj._order
['__module__', '__doc__', 'bar', 'foo'] ['__module__', '__qualname__', '__doc__', 'bar', 'foo']
""" """
bar = 321 bar = 321
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment