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
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
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
Expand all
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
This diff is collapsed.
Click to expand it.
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