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
Gwenaël Samain
cython
Commits
becd5ce7
Commit
becd5ce7
authored
Oct 10, 2017
by
Robert Bradshaw
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow multiple bases for cdef classes.
parent
6a5a9c05
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
152 additions
and
92 deletions
+152
-92
Cython/Compiler/ModuleNode.py
Cython/Compiler/ModuleNode.py
+10
-12
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+85
-65
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+9
-14
Cython/Compiler/PyrexTypes.py
Cython/Compiler/PyrexTypes.py
+2
-0
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.
Cython/Compiler/ModuleNode.py
View file @
becd5ce7
...
...
@@ -2874,8 +2874,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
...
...
@@ -2949,7 +2949,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
):
@
staticmethod
def
generate_type_ready_code
(
entry
,
code
):
# Generate a call to PyType_Ready for an extension
# type defined in this module.
type
=
entry
.
type
...
...
@@ -3041,6 +3042,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
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
generate_exttype_vtable_init_code
(
self
,
entry
,
code
):
# Generate code to initialise the C method table of an
...
...
@@ -3071,15 +3078,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 @
becd5ce7
...
...
@@ -4410,36 +4410,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
,
...
...
@@ -4531,8 +4508,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
...
...
@@ -4612,42 +4588,29 @@ 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
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
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
.
name
)
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
:
warning
(
self
.
pos
,
"freelists cannot be used on subtypes, only the base class can manage them"
,
1
)
...
...
@@ -4709,6 +4672,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
.
StringNode
(
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
:
...
...
@@ -4718,6 +4706,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
):
...
...
@@ -4731,8 +4721,38 @@ 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
)
from
.
import
ModuleNode
ModuleNode
.
ModuleNode
.
generate_type_ready_code
(
self
.
entry
,
code
)
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 @
becd5ce7
...
...
@@ -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 @
becd5ce7
...
...
@@ -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
...
...
tests/errors/subtyping_final_class.pyx
View file @
becd5ce7
...
...
@@ -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 @
becd5ce7
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