Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
cython
Commits
20326322
Commit
20326322
authored
Nov 08, 2017
by
Robert Bradshaw
Committed by
GitHub
Nov 08, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1927 from robertwb/multi-inheritance
Multiple inheritance
parents
59d95f6d
edbd9db3
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
338 additions
and
185 deletions
+338
-185
CHANGES.rst
CHANGES.rst
+4
-0
Cython/Compiler/ModuleNode.py
Cython/Compiler/ModuleNode.py
+4
-103
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+209
-64
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+9
-14
Cython/Compiler/PyrexTypes.py
Cython/Compiler/PyrexTypes.py
+2
-0
Cython/Utility/ImportExport.c
Cython/Utility/ImportExport.c
+61
-0
tests/errors/builtin_type_inheritance.pyx
tests/errors/builtin_type_inheritance.pyx
+3
-3
tests/errors/subtyping_final_class.pyx
tests/errors/subtyping_final_class.pyx
+1
-1
tests/run/cdef_multiple_inheritance.pyx
tests/run/cdef_multiple_inheritance.pyx
+45
-0
No files found.
CHANGES.rst
View file @
20326322
...
...
@@ -8,6 +8,10 @@ Cython Changelog
Features added
--------------
* Cdef classes can now multiply inherit from ordinary Python classes.
(The primary base must still be a c class, possibly ``object``, and
the other bases must *not* be cdef classes.)
* Type inference is now supported for Pythran compiled NumPy expressions.
Patch by Nils Braun. (Github issue #1954)
...
...
Cython/Compiler/ModuleNode.py
View file @
20326322
...
...
@@ -2948,8 +2948,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
else
:
self
.
generate_base_type_import_code
(
env
,
entry
,
code
)
self
.
generate_exttype_vtable_init_code
(
entry
,
code
)
self
.
generate_type_ready_code
(
env
,
entry
,
code
)
self
.
generate_typeptr_assignment
_code
(
entry
,
code
)
if
entry
.
type
.
early_init
:
self
.
generate_type_ready
_code
(
entry
,
code
)
def
generate_base_type_import_code
(
self
,
env
,
entry
,
code
):
base_type
=
entry
.
type
.
base_type
...
...
@@ -3023,98 +3023,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
not
type
.
is_external
or
type
.
is_subclassed
,
error_code
))
def
generate_type_ready_code
(
self
,
env
,
entry
,
code
):
# Generate a call to PyType_Ready for an extension
# type defined in this module.
type
=
entry
.
type
typeobj_cname
=
type
.
typeobj_cname
scope
=
type
.
scope
if
scope
:
# could be None if there was an error
if
entry
.
visibility
!=
'extern'
:
for
slot
in
TypeSlots
.
slot_table
:
slot
.
generate_dynamic_init_code
(
scope
,
code
)
code
.
putln
(
"if (PyType_Ready(&%s) < 0) %s"
%
(
typeobj_cname
,
code
.
error_goto
(
entry
.
pos
)))
# Don't inherit tp_print from builtin types, restoring the
# behavior of using tp_repr or tp_str instead.
code
.
putln
(
"%s.tp_print = 0;"
%
typeobj_cname
)
# Fix special method docstrings. This is a bit of a hack, but
# unless we let PyType_Ready create the slot wrappers we have
# a significant performance hit. (See trac #561.)
for
func
in
entry
.
type
.
scope
.
pyfunc_entries
:
is_buffer
=
func
.
name
in
(
'__getbuffer__'
,
'__releasebuffer__'
)
if
(
func
.
is_special
and
Options
.
docstrings
and
func
.
wrapperbase_cname
and
not
is_buffer
):
slot
=
TypeSlots
.
method_name_to_slot
[
func
.
name
]
preprocessor_guard
=
slot
.
preprocessor_guard_code
()
if
preprocessor_guard
:
code
.
putln
(
preprocessor_guard
)
code
.
putln
(
'#if CYTHON_COMPILING_IN_CPYTHON'
)
code
.
putln
(
"{"
)
code
.
putln
(
'PyObject *wrapper = PyObject_GetAttrString((PyObject *)&%s, "%s"); %s'
%
(
typeobj_cname
,
func
.
name
,
code
.
error_goto_if_null
(
'wrapper'
,
entry
.
pos
)))
code
.
putln
(
"if (Py_TYPE(wrapper) == &PyWrapperDescr_Type) {"
)
code
.
putln
(
"%s = *((PyWrapperDescrObject *)wrapper)->d_base;"
%
(
func
.
wrapperbase_cname
))
code
.
putln
(
"%s.doc = %s;"
%
(
func
.
wrapperbase_cname
,
func
.
doc_cname
))
code
.
putln
(
"((PyWrapperDescrObject *)wrapper)->d_base = &%s;"
%
(
func
.
wrapperbase_cname
))
code
.
putln
(
"}"
)
code
.
putln
(
"}"
)
code
.
putln
(
'#endif'
)
if
preprocessor_guard
:
code
.
putln
(
'#endif'
)
if
type
.
vtable_cname
:
code
.
putln
(
"if (__Pyx_SetVtable(%s.tp_dict, %s) < 0) %s"
%
(
typeobj_cname
,
type
.
vtabptr_cname
,
code
.
error_goto
(
entry
.
pos
)))
code
.
globalstate
.
use_utility_code
(
UtilityCode
.
load_cached
(
'SetVTable'
,
'ImportExport.c'
))
if
not
type
.
scope
.
is_internal
and
not
type
.
scope
.
directives
[
'internal'
]:
# scope.is_internal is set for types defined by
# Cython (such as closures), the 'internal'
# directive is set by users
code
.
putln
(
'if (PyObject_SetAttrString(%s, "%s", (PyObject *)&%s) < 0) %s'
%
(
Naming
.
module_cname
,
scope
.
class_name
,
typeobj_cname
,
code
.
error_goto
(
entry
.
pos
)))
weakref_entry
=
scope
.
lookup_here
(
"__weakref__"
)
if
not
scope
.
is_closure_class_scope
else
None
if
weakref_entry
:
if
weakref_entry
.
type
is
py_object_type
:
tp_weaklistoffset
=
"%s.tp_weaklistoffset"
%
typeobj_cname
if
type
.
typedef_flag
:
objstruct
=
type
.
objstruct_cname
else
:
objstruct
=
"struct %s"
%
type
.
objstruct_cname
code
.
putln
(
"if (%s == 0) %s = offsetof(%s, %s);"
%
(
tp_weaklistoffset
,
tp_weaklistoffset
,
objstruct
,
weakref_entry
.
cname
))
else
:
error
(
weakref_entry
.
pos
,
"__weakref__ slot must be of type 'object'"
)
if
scope
.
lookup_here
(
"__reduce_cython__"
)
if
not
scope
.
is_closure_class_scope
else
None
:
# Unfortunately, we cannot reliably detect whether a
# superclass defined __reduce__ at compile time, so we must
# do so at runtime.
code
.
globalstate
.
use_utility_code
(
UtilityCode
.
load_cached
(
'SetupReduce'
,
'ExtensionTypes.c'
))
code
.
putln
(
'if (__Pyx_setup_reduce((PyObject*)&%s) < 0) %s'
%
(
typeobj_cname
,
code
.
error_goto
(
entry
.
pos
)))
def
generate_type_ready_code
(
self
,
entry
,
code
):
Nodes
.
CClassDefNode
.
generate_type_ready_code
(
entry
,
code
)
def
generate_exttype_vtable_init_code
(
self
,
entry
,
code
):
# Generate code to initialise the C method table of an
...
...
@@ -3145,15 +3055,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
cast
,
meth_entry
.
func_cname
))
def
generate_typeptr_assignment_code
(
self
,
entry
,
code
):
# Generate code to initialise the typeptr of an extension
# type defined in this module to point to its type object.
type
=
entry
.
type
if
type
.
typeobj_cname
:
code
.
putln
(
"%s = &%s;"
%
(
type
.
typeptr_cname
,
type
.
typeobj_cname
))
def
generate_cfunction_declaration
(
entry
,
env
,
code
,
definition
):
from_cy_utility
=
entry
.
used
and
entry
.
utility_code_definition
if
entry
.
used
and
entry
.
inline_func_in_pxd
or
(
not
entry
.
in_cinclude
and
(
...
...
Cython/Compiler/Nodes.py
View file @
20326322
...
...
@@ -4453,36 +4453,13 @@ class PyClassDefNode(ClassDefNode):
if
self
.
is_py3_style_class
:
error
(
self
.
classobj
.
pos
,
"Python3 style class could not be represented as C class"
)
return
bases
=
self
.
classobj
.
bases
.
args
if
len
(
bases
)
==
0
:
base_class_name
=
None
base_class_module
=
None
elif
len
(
bases
)
==
1
:
base
=
bases
[
0
]
path
=
[]
from
.ExprNodes
import
AttributeNode
,
NameNode
while
isinstance
(
base
,
AttributeNode
):
path
.
insert
(
0
,
base
.
attribute
)
base
=
base
.
obj
if
isinstance
(
base
,
NameNode
):
path
.
insert
(
0
,
base
.
name
)
base_class_name
=
path
[
-
1
]
if
len
(
path
)
>
1
:
base_class_module
=
u'.'
.
join
(
path
[:
-
1
])
else
:
base_class_module
=
None
else
:
error
(
self
.
classobj
.
bases
.
args
.
pos
,
"Invalid base class"
)
else
:
error
(
self
.
classobj
.
bases
.
args
.
pos
,
"C class may only have one base class"
)
return
None
from
.
import
ExprNodes
return
CClassDefNode
(
self
.
pos
,
visibility
=
'private'
,
module_name
=
None
,
class_name
=
self
.
name
,
base_class_module
=
base_class_module
,
base_class_name
=
base_class_name
,
bases
=
self
.
classobj
.
bases
or
ExprNodes
.
TupleNode
(
self
.
pos
,
args
=
[]),
decorators
=
self
.
decorators
,
body
=
self
.
body
,
in_pxd
=
False
,
...
...
@@ -4574,8 +4551,7 @@ class CClassDefNode(ClassDefNode):
# module_name string or None For import of extern type objects
# class_name string Unqualified name of class
# as_name string or None Name to declare as in this scope
# base_class_module string or None Module containing the base class
# base_class_name string or None Name of the base class
# bases TupleNode Base class(es)
# objstruct_name string or None Specified C name of object struct
# typeobj_name string or None Specified C name of type object
# in_pxd boolean Is in a .pxd file
...
...
@@ -4655,43 +4631,33 @@ class CClassDefNode(ClassDefNode):
self
.
module
.
has_extern_class
=
1
env
.
add_imported_module
(
self
.
module
)
if
self
.
base_class_name
:
if
self
.
base_class_module
:
base_class_scope
=
env
.
find_imported_module
(
self
.
base_class_module
.
split
(
'.'
),
self
.
pos
)
if
not
base_class_scope
:
error
(
self
.
pos
,
"'%s' is not a cimported module"
%
self
.
base_class_module
)
return
else
:
base_class_scope
=
env
if
self
.
base_class_name
==
'object'
:
# extension classes are special and don't need to inherit from object
if
base_class_scope
is
None
or
base_class_scope
.
lookup
(
'object'
)
is
None
:
self
.
base_class_name
=
None
self
.
base_class_module
=
None
if
self
.
bases
.
args
:
base
=
self
.
bases
.
args
[
0
]
base_type
=
base
.
analyse_as_type
(
env
)
if
base_type
in
(
PyrexTypes
.
c_int_type
,
PyrexTypes
.
c_long_type
,
PyrexTypes
.
c_float_type
):
# Use the Python rather than C variant of these types.
base_type
=
env
.
lookup
(
base_type
.
sign_and_name
()).
type
if
base_type
is
None
:
error
(
base
.
pos
,
"First base of '%s' is not an extension type"
%
self
.
class_name
)
elif
base_type
==
PyrexTypes
.
py_object_type
:
base_class_scope
=
None
if
base_class_scope
:
base_class_entry
=
base_class_scope
.
find
(
self
.
base_class_name
,
self
.
pos
)
if
base_class_entry
:
if
not
base_class_entry
.
is_type
:
error
(
self
.
pos
,
"'%s' is not a type name"
%
self
.
base_class_name
)
elif
not
base_class_entry
.
type
.
is_extension_type
and
\
not
(
base_class_entry
.
type
.
is_builtin_type
and
base_class_entry
.
type
.
objstruct_cname
):
error
(
self
.
pos
,
"'%s' is not an extension type"
%
self
.
base_class_name
)
elif
not
base_class_entry
.
type
.
is_complete
():
error
(
self
.
pos
,
"Base class '%s' of type '%s' is incomplete"
%
(
self
.
base_class_name
,
self
.
class_name
))
elif
base_class_entry
.
type
.
scope
and
base_class_entry
.
type
.
scope
.
directives
and
\
base_class_entry
.
type
.
is_final_type
:
error
(
self
.
pos
,
"Base class '%s' of type '%s' is final"
%
(
self
.
base_class_name
,
self
.
class_name
))
elif
base_class_entry
.
type
.
is_builtin_type
and
\
base_class_entry
.
type
.
name
in
(
'tuple'
,
'str'
,
'bytes'
):
error
(
self
.
pos
,
"inheritance from PyVarObject types like '%s' is not currently supported"
%
base_class_entry
.
type
.
name
)
else
:
self
.
base_type
=
base_class_entry
.
type
if
env
.
directives
.
get
(
'freelist'
,
0
)
>
0
:
elif
not
base_type
.
is_extension_type
and
\
not
(
base_type
.
is_builtin_type
and
base_type
.
objstruct_cname
):
error
(
base
.
pos
,
"'%s' is not an extension type"
%
base_type
)
elif
not
base_type
.
is_complete
():
error
(
base
.
pos
,
"Base class '%s' of type '%s' is incomplete"
%
(
base_type
.
name
,
self
.
class_name
))
elif
base_type
.
scope
and
base_type
.
scope
.
directives
and
\
base_type
.
is_final_type
:
error
(
base
.
pos
,
"Base class '%s' of type '%s' is final"
%
(
base_type
,
self
.
class_name
))
elif
base_type
.
is_builtin_type
and
\
base_type
.
name
in
(
'tuple'
,
'str'
,
'bytes'
):
error
(
base
.
pos
,
"inheritance from PyVarObject types like '%s' is not currently supported"
%
base_type
.
name
)
else
:
self
.
base_type
=
base_type
if
env
.
directives
.
get
(
'freelist'
,
0
)
>
0
and
base_type
!=
PyrexTypes
.
py_object_type
:
warning
(
self
.
pos
,
"freelists cannot be used on subtypes, only the base class can manage them"
,
1
)
has_body
=
self
.
body
is
not
None
...
...
@@ -4752,6 +4718,31 @@ class CClassDefNode(ClassDefNode):
else
:
scope
.
implemented
=
1
if
len
(
self
.
bases
.
args
)
>
1
:
if
not
has_body
or
self
.
in_pxd
:
error
(
self
.
bases
.
args
[
1
].
pos
,
"Only declare first base in declaration."
)
for
other_base
in
self
.
bases
.
args
[
1
:]:
if
other_base
.
analyse_as_type
(
env
):
# TODO(robertwb): We may also want to enforce some checks
# at runtime.
error
(
other_base
.
pos
,
"Only one extension type base class allowed."
)
if
not
self
.
scope
.
lookup
(
"__dict__"
):
#TODO(robertwb): See if this can be safely removed.
error
(
self
.
pos
,
"Extension types with multiple bases must have a __dict__ attribute"
)
self
.
entry
.
type
.
early_init
=
0
from
.
import
ExprNodes
self
.
type_init_args
=
ExprNodes
.
TupleNode
(
self
.
pos
,
args
=
[
ExprNodes
.
IdentifierStringNode
(
self
.
pos
,
value
=
self
.
class_name
),
self
.
bases
,
ExprNodes
.
DictNode
(
self
.
pos
,
key_value_pairs
=
[])])
elif
self
.
base_type
:
self
.
entry
.
type
.
early_init
=
self
.
base_type
.
is_external
or
self
.
base_type
.
early_init
self
.
type_init_args
=
None
else
:
self
.
entry
.
type
.
early_init
=
1
self
.
type_init_args
=
None
env
.
allocate_vtable_names
(
self
.
entry
)
for
thunk
in
self
.
entry
.
type
.
defered_declarations
:
...
...
@@ -4761,6 +4752,8 @@ class CClassDefNode(ClassDefNode):
if
self
.
body
:
scope
=
self
.
entry
.
type
.
scope
self
.
body
=
self
.
body
.
analyse_expressions
(
scope
)
if
self
.
type_init_args
:
self
.
type_init_args
.
analyse_expressions
(
env
)
return
self
def
generate_function_definitions
(
self
,
env
,
code
):
...
...
@@ -4774,8 +4767,160 @@ class CClassDefNode(ClassDefNode):
code
.
mark_pos
(
self
.
pos
)
if
self
.
body
:
self
.
body
.
generate_execution_code
(
code
)
if
not
self
.
entry
.
type
.
early_init
:
if
self
.
type_init_args
:
self
.
type_init_args
.
generate_evaluation_code
(
code
)
bases
=
"PyTuple_GET_ITEM(%s, 1)"
%
self
.
type_init_args
.
result
()
first_base
=
"((PyTypeObject*)PyTuple_GET_ITEM(%s, 0))"
%
bases
# Let Python do the base types compatibility checking.
trial_type
=
code
.
funcstate
.
allocate_temp
(
PyrexTypes
.
py_object_type
,
True
)
code
.
putln
(
"%s = PyType_Type.tp_new(&PyType_Type, %s, NULL);"
%
(
trial_type
,
self
.
type_init_args
.
result
()))
code
.
putln
(
code
.
error_goto_if_null
(
trial_type
,
self
.
pos
))
code
.
put_gotref
(
trial_type
)
code
.
putln
(
"if (((PyTypeObject*) %s)->tp_base != %s) {"
%
(
trial_type
,
first_base
))
code
.
putln
(
"PyErr_Format(PyExc_TypeError,
\
"
best base '%s' must be equal to first base '%s'
\
"
,"
)
code
.
putln
(
" ((PyTypeObject*) %s)->tp_base->tp_name, %s->tp_name);"
%
(
trial_type
,
first_base
))
code
.
putln
(
code
.
error_goto
(
self
.
pos
))
code
.
putln
(
"}"
)
code
.
funcstate
.
release_temp
(
trial_type
)
code
.
put_incref
(
bases
,
PyrexTypes
.
py_object_type
)
code
.
put_giveref
(
bases
)
code
.
putln
(
"%s.tp_bases = %s;"
%
(
self
.
entry
.
type
.
typeobj_cname
,
bases
))
code
.
put_decref_clear
(
trial_type
,
PyrexTypes
.
py_object_type
)
self
.
type_init_args
.
generate_disposal_code
(
code
)
self
.
type_init_args
.
free_temps
(
code
)
self
.
generate_type_ready_code
(
self
.
entry
,
code
,
True
)
# Also called from ModuleNode for early init types.
@
staticmethod
def
generate_type_ready_code
(
entry
,
code
,
heap_type_bases
=
False
):
# Generate a call to PyType_Ready for an extension
# type defined in this module.
type
=
entry
.
type
typeobj_cname
=
type
.
typeobj_cname
scope
=
type
.
scope
if
not
scope
:
# could be None if there was an error
return
if
entry
.
visibility
!=
'extern'
:
for
slot
in
TypeSlots
.
slot_table
:
slot
.
generate_dynamic_init_code
(
scope
,
code
)
if
heap_type_bases
:
# As of https://bugs.python.org/issue22079
# PyType_Ready enforces that all bases of a non-heap type
# are non-heap. We know this is the case for the solid base,
# but other bases may be heap allocated and are kept alive
# though the bases reference.
# Other than this check, this flag is unused in this method.
code
.
putln
(
"#if PY_VERSION_HEX >= 0x03050000"
)
code
.
putln
(
"%s.tp_flags |= Py_TPFLAGS_HEAPTYPE;"
%
typeobj_cname
)
code
.
putln
(
"#endif"
)
code
.
putln
(
"if (PyType_Ready(&%s) < 0) %s"
%
(
typeobj_cname
,
code
.
error_goto
(
entry
.
pos
)))
if
heap_type_bases
:
code
.
putln
(
"#if PY_VERSION_HEX >= 0x03050000"
)
code
.
putln
(
"%s.tp_flags &= ~Py_TPFLAGS_HEAPTYPE;"
%
typeobj_cname
)
code
.
putln
(
"#endif"
)
# Don't inherit tp_print from builtin types, restoring the
# behavior of using tp_repr or tp_str instead.
code
.
putln
(
"%s.tp_print = 0;"
%
typeobj_cname
)
# Fix special method docstrings. This is a bit of a hack, but
# unless we let PyType_Ready create the slot wrappers we have
# a significant performance hit. (See trac #561.)
for
func
in
entry
.
type
.
scope
.
pyfunc_entries
:
is_buffer
=
func
.
name
in
(
'__getbuffer__'
,
'__releasebuffer__'
)
if
(
func
.
is_special
and
Options
.
docstrings
and
func
.
wrapperbase_cname
and
not
is_buffer
):
slot
=
TypeSlots
.
method_name_to_slot
[
func
.
name
]
preprocessor_guard
=
slot
.
preprocessor_guard_code
()
if
preprocessor_guard
:
code
.
putln
(
preprocessor_guard
)
code
.
putln
(
'#if CYTHON_COMPILING_IN_CPYTHON'
)
code
.
putln
(
"{"
)
code
.
putln
(
'PyObject *wrapper = PyObject_GetAttrString((PyObject *)&%s, "%s"); %s'
%
(
typeobj_cname
,
func
.
name
,
code
.
error_goto_if_null
(
'wrapper'
,
entry
.
pos
)))
code
.
putln
(
"if (Py_TYPE(wrapper) == &PyWrapperDescr_Type) {"
)
code
.
putln
(
"%s = *((PyWrapperDescrObject *)wrapper)->d_base;"
%
(
func
.
wrapperbase_cname
))
code
.
putln
(
"%s.doc = %s;"
%
(
func
.
wrapperbase_cname
,
func
.
doc_cname
))
code
.
putln
(
"((PyWrapperDescrObject *)wrapper)->d_base = &%s;"
%
(
func
.
wrapperbase_cname
))
code
.
putln
(
"}"
)
code
.
putln
(
"}"
)
code
.
putln
(
'#endif'
)
if
preprocessor_guard
:
code
.
putln
(
'#endif'
)
if
type
.
vtable_cname
:
code
.
globalstate
.
use_utility_code
(
UtilityCode
.
load_cached
(
'SetVTable'
,
'ImportExport.c'
))
code
.
putln
(
"if (__Pyx_SetVtable(%s.tp_dict, %s) < 0) %s"
%
(
typeobj_cname
,
type
.
vtabptr_cname
,
code
.
error_goto
(
entry
.
pos
)))
if
heap_type_bases
:
code
.
globalstate
.
use_utility_code
(
UtilityCode
.
load_cached
(
'MergeVTables'
,
'ImportExport.c'
))
code
.
putln
(
"if (__Pyx_MergeVtables(&%s) < 0) %s"
%
(
typeobj_cname
,
code
.
error_goto
(
entry
.
pos
)))
if
not
type
.
scope
.
is_internal
and
not
type
.
scope
.
directives
[
'internal'
]:
# scope.is_internal is set for types defined by
# Cython (such as closures), the 'internal'
# directive is set by users
code
.
putln
(
'if (PyObject_SetAttrString(%s, "%s", (PyObject *)&%s) < 0) %s'
%
(
Naming
.
module_cname
,
scope
.
class_name
,
typeobj_cname
,
code
.
error_goto
(
entry
.
pos
)))
weakref_entry
=
scope
.
lookup_here
(
"__weakref__"
)
if
not
scope
.
is_closure_class_scope
else
None
if
weakref_entry
:
if
weakref_entry
.
type
is
py_object_type
:
tp_weaklistoffset
=
"%s.tp_weaklistoffset"
%
typeobj_cname
if
type
.
typedef_flag
:
objstruct
=
type
.
objstruct_cname
else
:
objstruct
=
"struct %s"
%
type
.
objstruct_cname
code
.
putln
(
"if (%s == 0) %s = offsetof(%s, %s);"
%
(
tp_weaklistoffset
,
tp_weaklistoffset
,
objstruct
,
weakref_entry
.
cname
))
else
:
error
(
weakref_entry
.
pos
,
"__weakref__ slot must be of type 'object'"
)
if
scope
.
lookup_here
(
"__reduce_cython__"
)
if
not
scope
.
is_closure_class_scope
else
None
:
# Unfortunately, we cannot reliably detect whether a
# superclass defined __reduce__ at compile time, so we must
# do so at runtime.
code
.
globalstate
.
use_utility_code
(
UtilityCode
.
load_cached
(
'SetupReduce'
,
'ExtensionTypes.c'
))
code
.
putln
(
'if (__Pyx_setup_reduce((PyObject*)&%s) < 0) %s'
%
(
typeobj_cname
,
code
.
error_goto
(
entry
.
pos
)))
# Generate code to initialise the typeptr of an extension
# type defined in this module to point to its type object.
if
type
.
typeobj_cname
:
code
.
putln
(
"%s = &%s;"
%
(
type
.
typeptr_cname
,
type
.
typeobj_cname
))
def
annotate
(
self
,
code
):
if
self
.
type_init_args
:
self
.
type_init_args
.
annotate
(
code
)
if
self
.
body
:
self
.
body
.
annotate
(
code
)
...
...
Cython/Compiler/Parsing.py
View file @
20326322
...
...
@@ -3435,19 +3435,15 @@ def p_c_class_definition(s, pos, ctx):
as_name
=
class_name
objstruct_name
=
None
typeobj_name
=
None
base_class_module
=
None
base_class_name
=
None
bases
=
None
if
s
.
sy
==
'('
:
s
.
next
()
base_class_path
=
[
p_ident
(
s
)]
while
s
.
sy
==
'.'
:
s
.
next
()
base_class_path
.
append
(
p_ident
(
s
))
if
s
.
sy
==
','
:
s
.
error
(
"C class may only have one base class"
,
fatal
=
False
)
s
.
expect
(
')'
)
base_class_module
=
"."
.
join
(
base_class_path
[:
-
1
])
base_class_name
=
base_class_path
[
-
1
]
positional_args
,
keyword_args
=
p_call_parse_args
(
s
,
allow_genexp
=
False
)
if
keyword_args
:
s
.
error
(
"C classes cannot take keyword bases."
)
bases
,
_
=
p_call_build_packed_args
(
pos
,
positional_args
,
keyword_args
)
if
bases
is
None
:
bases
=
ExprNodes
.
TupleNode
(
pos
,
args
=
[])
if
s
.
sy
==
'['
:
if
ctx
.
visibility
not
in
(
'public'
,
'extern'
)
and
not
ctx
.
api
:
error
(
s
.
position
(),
"Name options only allowed for 'public', 'api', or 'extern' C class"
)
...
...
@@ -3487,8 +3483,7 @@ def p_c_class_definition(s, pos, ctx):
module_name
=
"."
.
join
(
module_path
),
class_name
=
class_name
,
as_name
=
as_name
,
base_class_module
=
base_class_module
,
base_class_name
=
base_class_name
,
bases
=
bases
,
objstruct_name
=
objstruct_name
,
typeobj_name
=
typeobj_name
,
in_pxd
=
ctx
.
level
==
'module_pxd'
,
...
...
Cython/Compiler/PyrexTypes.py
View file @
20326322
...
...
@@ -1302,10 +1302,12 @@ class PyExtensionType(PyObjectType):
# vtabstruct_cname string Name of C method table struct
# vtabptr_cname string Name of pointer to C method table
# vtable_cname string Name of C method table definition
# early_init boolean Whether to initialize early (as opposed to during module execution).
# defered_declarations [thunk] Used to declare class hierarchies in order
is_extension_type
=
1
has_attributes
=
1
early_init
=
1
objtypedef_cname
=
None
...
...
Cython/Utility/ImportExport.c
View file @
20326322
...
...
@@ -656,6 +656,67 @@ bad:
}
/////////////// MergeVTables.proto ///////////////
//@requires: GetVTable
static
int
__Pyx_MergeVtables
(
PyTypeObject
*
type
);
/*proto*/
/////////////// MergeVTables ///////////////
static
int
__Pyx_MergeVtables
(
PyTypeObject
*
type
)
{
int
i
;
void
**
base_vtables
;
void
*
unknown
=
(
void
*
)
-
1
;
PyObject
*
bases
=
type
->
tp_bases
;
int
base_depth
=
0
;
{
PyTypeObject
*
base
=
type
->
tp_base
;
while
(
base
)
{
base_depth
+=
1
;
base
=
base
->
tp_base
;
}
}
base_vtables
=
(
void
**
)
malloc
(
sizeof
(
void
*
)
*
(
base_depth
+
1
));
base_vtables
[
0
]
=
unknown
;
// Could do MRO resolution of individual methods in the future, assuming
// compatible vtables, but for now simply require a common vtable base.
// Note that if the vtables of various bases are extended separately,
// resolution isn't possible and we must reject it just as when the
// instance struct is so extended. (It would be good to also do this
// check when a multiple-base class is created in pure Python as well.)
for
(
i
=
1
;
i
<
PyTuple_GET_SIZE
(
bases
);
i
++
)
{
void
*
base_vtable
=
__Pyx_GetVtable
(((
PyTypeObject
*
)
PyTuple_GET_ITEM
(
bases
,
i
))
->
tp_dict
);
if
(
base_vtable
!=
NULL
)
{
int
j
;
PyTypeObject
*
base
=
type
->
tp_base
;
for
(
j
=
0
;
j
<
base_depth
;
j
++
)
{
if
(
base_vtables
[
j
]
==
unknown
)
{
base_vtables
[
j
]
=
__Pyx_GetVtable
(
base
->
tp_dict
);
base_vtables
[
j
+
1
]
=
unknown
;
}
if
(
base_vtables
[
j
]
==
base_vtable
)
{
break
;
}
else
if
(
base_vtables
[
j
]
==
NULL
)
{
// No more potential matching bases (with vtables).
goto
bad
;
}
base
=
base
->
tp_base
;
}
}
}
PyErr_Clear
();
free
(
base_vtables
);
return
0
;
bad:
PyErr_Format
(
PyExc_TypeError
,
"multiple bases have vtable conflict: '%s' and '%s'"
,
type
->
tp_base
->
tp_name
,
((
PyTypeObject
*
)
PyTuple_GET_ITEM
(
bases
,
i
))
->
tp_name
);
free
(
base_vtables
);
return
-
1
;
}
/////////////// ImportNumPyArray.proto ///////////////
static
PyObject
*
__pyx_numpy_ndarray
=
NULL
;
...
...
tests/errors/builtin_type_inheritance.pyx
View file @
20326322
...
...
@@ -12,7 +12,7 @@ cdef class MyStr(str): # only in Py2, but can't know that during compilation
pass
_ERRORS
=
"""
5:
5
: inheritance from PyVarObject types like 'tuple' is not currently supported
8:
5
: inheritance from PyVarObject types like 'bytes' is not currently supported
11:
5
: inheritance from PyVarObject types like 'str' is not currently supported
5:
19
: inheritance from PyVarObject types like 'tuple' is not currently supported
8:
19
: inheritance from PyVarObject types like 'bytes' is not currently supported
11:
17
: inheritance from PyVarObject types like 'str' is not currently supported
"""
tests/errors/subtyping_final_class.pyx
View file @
20326322
...
...
@@ -10,5 +10,5 @@ cdef class SubType(FinalClass):
pass
_ERRORS
=
"""
9:
5
: Base class 'FinalClass' of type 'SubType' is final
9:
19
: Base class 'FinalClass' of type 'SubType' is final
"""
tests/run/cdef_multiple_inheritance.pyx
0 → 100644
View file @
20326322
cdef
class
CBase
(
object
):
cdef
int
a
cdef
c_method
(
self
):
return
"CBase"
cpdef
cpdef
_method
(
self
):
return
"CBase"
class
PyBase
(
object
):
def
py_method
(
self
):
return
"PyBase"
cdef
class
Both
(
CBase
,
PyBase
):
cdef
dict
__dict__
"""
>>> b = Both()
>>> b.py_method()
'PyBase'
>>> b.cp_method()
'Both'
>>> b.call_c_method()
'Both'
>>> isinstance(b, CBase)
True
>>> isinstance(b, PyBase)
True
"""
cdef
c_method
(
self
):
return
"Both"
cpdef
cp_method
(
self
):
return
"Both"
def
call_c_method
(
self
):
return
self
.
c_method
()
cdef
class
BothSub
(
Both
):
"""
>>> b = BothSub()
>>> b.py_method()
'PyBase'
>>> b.cp_method()
'Both'
>>> b.call_c_method()
'Both'
"""
pass
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment