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):
...
@@ -2874,8 +2874,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
else
:
else
:
self
.
generate_base_type_import_code
(
env
,
entry
,
code
)
self
.
generate_base_type_import_code
(
env
,
entry
,
code
)
self
.
generate_exttype_vtable_init_code
(
entry
,
code
)
self
.
generate_exttype_vtable_init_code
(
entry
,
code
)
self
.
generate_type_ready_code
(
env
,
entry
,
code
)
if
entry
.
type
.
early_init
:
self
.
generate_typeptr_assignment
_code
(
entry
,
code
)
self
.
generate_type_ready
_code
(
entry
,
code
)
def
generate_base_type_import_code
(
self
,
env
,
entry
,
code
):
def
generate_base_type_import_code
(
self
,
env
,
entry
,
code
):
base_type
=
entry
.
type
.
base_type
base_type
=
entry
.
type
.
base_type
...
@@ -2949,7 +2949,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
...
@@ -2949,7 +2949,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
not
type
.
is_external
or
type
.
is_subclassed
,
not
type
.
is_external
or
type
.
is_subclassed
,
error_code
))
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
# Generate a call to PyType_Ready for an extension
# type defined in this module.
# type defined in this module.
type
=
entry
.
type
type
=
entry
.
type
...
@@ -3041,6 +3042,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
...
@@ -3041,6 +3042,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code
.
putln
(
'if (__Pyx_setup_reduce((PyObject*)&%s) < 0) %s'
%
(
code
.
putln
(
'if (__Pyx_setup_reduce((PyObject*)&%s) < 0) %s'
%
(
typeobj_cname
,
typeobj_cname
,
code
.
error_goto
(
entry
.
pos
)))
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
):
def
generate_exttype_vtable_init_code
(
self
,
entry
,
code
):
# Generate code to initialise the C method table of an
# Generate code to initialise the C method table of an
...
@@ -3071,15 +3078,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
...
@@ -3071,15 +3078,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
cast
,
cast
,
meth_entry
.
func_cname
))
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
):
def
generate_cfunction_declaration
(
entry
,
env
,
code
,
definition
):
from_cy_utility
=
entry
.
used
and
entry
.
utility_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
(
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):
...
@@ -4410,36 +4410,13 @@ class PyClassDefNode(ClassDefNode):
if
self
.
is_py3_style_class
:
if
self
.
is_py3_style_class
:
error
(
self
.
classobj
.
pos
,
"Python3 style class could not be represented as C class"
)
error
(
self
.
classobj
.
pos
,
"Python3 style class could not be represented as C class"
)
return
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
,
return
CClassDefNode
(
self
.
pos
,
visibility
=
'private'
,
visibility
=
'private'
,
module_name
=
None
,
module_name
=
None
,
class_name
=
self
.
name
,
class_name
=
self
.
name
,
base_class_module
=
base_class_module
,
bases
=
self
.
classobj
.
bases
or
ExprNodes
.
TupleNode
(
self
.
pos
,
args
=
[]),
base_class_name
=
base_class_name
,
decorators
=
self
.
decorators
,
decorators
=
self
.
decorators
,
body
=
self
.
body
,
body
=
self
.
body
,
in_pxd
=
False
,
in_pxd
=
False
,
...
@@ -4531,8 +4508,7 @@ class CClassDefNode(ClassDefNode):
...
@@ -4531,8 +4508,7 @@ class CClassDefNode(ClassDefNode):
# module_name string or None For import of extern type objects
# module_name string or None For import of extern type objects
# class_name string Unqualified name of class
# class_name string Unqualified name of class
# as_name string or None Name to declare as in this scope
# as_name string or None Name to declare as in this scope
# base_class_module string or None Module containing the base class
# bases TupleNode Base class(es)
# base_class_name string or None Name of the base class
# objstruct_name string or None Specified C name of object struct
# objstruct_name string or None Specified C name of object struct
# typeobj_name string or None Specified C name of type object
# typeobj_name string or None Specified C name of type object
# in_pxd boolean Is in a .pxd file
# in_pxd boolean Is in a .pxd file
...
@@ -4612,42 +4588,29 @@ class CClassDefNode(ClassDefNode):
...
@@ -4612,42 +4588,29 @@ class CClassDefNode(ClassDefNode):
self
.
module
.
has_extern_class
=
1
self
.
module
.
has_extern_class
=
1
env
.
add_imported_module
(
self
.
module
)
env
.
add_imported_module
(
self
.
module
)
if
self
.
base_class_name
:
if
self
.
bases
.
args
:
if
self
.
base_class_module
:
base
=
self
.
bases
.
args
[
0
]
base_class_scope
=
env
.
find_imported_module
(
self
.
base_class_module
.
split
(
'.'
),
self
.
pos
)
base_type
=
base
.
analyse_as_type
(
env
)
if
not
base_class_scope
:
if
base_type
is
None
:
error
(
self
.
pos
,
"'%s' is not a cimported module"
%
self
.
base_class_module
)
error
(
base
.
pos
,
"First base of '%s' is not an extension type"
%
self
.
class_name
)
return
elif
base_type
==
PyrexTypes
.
py_object_type
:
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
base_class_scope
=
None
base_class_scope
=
None
if
base_class_scope
:
elif
not
base_type
.
is_extension_type
and
\
base_class_entry
=
base_class_scope
.
find
(
self
.
base_class_name
,
self
.
pos
)
not
(
base_type
.
is_builtin_type
and
base_type
.
objstruct_cname
):
if
base_class_entry
:
error
(
base
.
pos
,
"'%s' is not an extension type"
%
base_type
.
name
)
if
not
base_class_entry
.
is_type
:
elif
not
base_type
.
is_complete
():
error
(
self
.
pos
,
"'%s' is not a type name"
%
self
.
base_class_name
)
error
(
base
.
pos
,
"Base class '%s' of type '%s' is incomplete"
%
(
elif
not
base_class_entry
.
type
.
is_extension_type
and
\
base_type
.
name
,
self
.
class_name
))
not
(
base_class_entry
.
type
.
is_builtin_type
and
elif
base_type
.
scope
and
base_type
.
scope
.
directives
and
\
base_class_entry
.
type
.
objstruct_cname
):
base_type
.
is_final_type
:
error
(
self
.
pos
,
"'%s' is not an extension type"
%
self
.
base_class_name
)
error
(
base
.
pos
,
"Base class '%s' of type '%s' is final"
%
(
elif
not
base_class_entry
.
type
.
is_complete
():
base_type
,
self
.
class_name
))
error
(
self
.
pos
,
"Base class '%s' of type '%s' is incomplete"
%
(
elif
base_type
.
is_builtin_type
and
\
self
.
base_class_name
,
self
.
class_name
))
base_type
.
name
in
(
'tuple'
,
'str'
,
'bytes'
):
elif
base_class_entry
.
type
.
scope
and
base_class_entry
.
type
.
scope
.
directives
and
\
error
(
base
.
pos
,
"inheritance from PyVarObject types like '%s' is not currently supported"
base_class_entry
.
type
.
is_final_type
:
%
base_type
.
name
)
error
(
self
.
pos
,
"Base class '%s' of type '%s' is final"
%
(
else
:
self
.
base_class_name
,
self
.
class_name
))
self
.
base_type
=
base_type
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
:
if
env
.
directives
.
get
(
'freelist'
,
0
)
>
0
:
warning
(
self
.
pos
,
"freelists cannot be used on subtypes, only the base class can manage them"
,
1
)
warning
(
self
.
pos
,
"freelists cannot be used on subtypes, only the base class can manage them"
,
1
)
...
@@ -4709,6 +4672,31 @@ class CClassDefNode(ClassDefNode):
...
@@ -4709,6 +4672,31 @@ class CClassDefNode(ClassDefNode):
else
:
else
:
scope
.
implemented
=
1
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
)
env
.
allocate_vtable_names
(
self
.
entry
)
for
thunk
in
self
.
entry
.
type
.
defered_declarations
:
for
thunk
in
self
.
entry
.
type
.
defered_declarations
:
...
@@ -4718,6 +4706,8 @@ class CClassDefNode(ClassDefNode):
...
@@ -4718,6 +4706,8 @@ class CClassDefNode(ClassDefNode):
if
self
.
body
:
if
self
.
body
:
scope
=
self
.
entry
.
type
.
scope
scope
=
self
.
entry
.
type
.
scope
self
.
body
=
self
.
body
.
analyse_expressions
(
scope
)
self
.
body
=
self
.
body
.
analyse_expressions
(
scope
)
if
self
.
type_init_args
:
self
.
type_init_args
.
analyse_expressions
(
env
)
return
self
return
self
def
generate_function_definitions
(
self
,
env
,
code
):
def
generate_function_definitions
(
self
,
env
,
code
):
...
@@ -4731,8 +4721,38 @@ class CClassDefNode(ClassDefNode):
...
@@ -4731,8 +4721,38 @@ class CClassDefNode(ClassDefNode):
code
.
mark_pos
(
self
.
pos
)
code
.
mark_pos
(
self
.
pos
)
if
self
.
body
:
if
self
.
body
:
self
.
body
.
generate_execution_code
(
code
)
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
):
def
annotate
(
self
,
code
):
if
self
.
type_init_args
:
self
.
type_init_args
.
annotate
(
code
)
if
self
.
body
:
if
self
.
body
:
self
.
body
.
annotate
(
code
)
self
.
body
.
annotate
(
code
)
...
...
Cython/Compiler/Parsing.py
View file @
becd5ce7
...
@@ -3435,19 +3435,15 @@ def p_c_class_definition(s, pos, ctx):
...
@@ -3435,19 +3435,15 @@ def p_c_class_definition(s, pos, ctx):
as_name
=
class_name
as_name
=
class_name
objstruct_name
=
None
objstruct_name
=
None
typeobj_name
=
None
typeobj_name
=
None
base_class_module
=
None
bases
=
None
base_class_name
=
None
if
s
.
sy
==
'('
:
if
s
.
sy
==
'('
:
s
.
next
()
positional_args
,
keyword_args
=
p_call_parse_args
(
s
,
allow_genexp
=
False
)
base_class_path
=
[
p_ident
(
s
)]
if
keyword_args
:
while
s
.
sy
==
'.'
:
s
.
error
(
"C classes cannot take keyword bases."
)
s
.
next
()
bases
,
_
=
p_call_build_packed_args
(
pos
,
positional_args
,
keyword_args
)
base_class_path
.
append
(
p_ident
(
s
))
if
bases
is
None
:
if
s
.
sy
==
','
:
bases
=
ExprNodes
.
TupleNode
(
pos
,
args
=
[])
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
]
if
s
.
sy
==
'['
:
if
s
.
sy
==
'['
:
if
ctx
.
visibility
not
in
(
'public'
,
'extern'
)
and
not
ctx
.
api
:
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"
)
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):
...
@@ -3487,8 +3483,7 @@ def p_c_class_definition(s, pos, ctx):
module_name
=
"."
.
join
(
module_path
),
module_name
=
"."
.
join
(
module_path
),
class_name
=
class_name
,
class_name
=
class_name
,
as_name
=
as_name
,
as_name
=
as_name
,
base_class_module
=
base_class_module
,
bases
=
bases
,
base_class_name
=
base_class_name
,
objstruct_name
=
objstruct_name
,
objstruct_name
=
objstruct_name
,
typeobj_name
=
typeobj_name
,
typeobj_name
=
typeobj_name
,
in_pxd
=
ctx
.
level
==
'module_pxd'
,
in_pxd
=
ctx
.
level
==
'module_pxd'
,
...
...
Cython/Compiler/PyrexTypes.py
View file @
becd5ce7
...
@@ -1302,10 +1302,12 @@ class PyExtensionType(PyObjectType):
...
@@ -1302,10 +1302,12 @@ class PyExtensionType(PyObjectType):
# vtabstruct_cname string Name of C method table struct
# vtabstruct_cname string Name of C method table struct
# vtabptr_cname string Name of pointer to C method table
# vtabptr_cname string Name of pointer to C method table
# vtable_cname string Name of C method table definition
# 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
# defered_declarations [thunk] Used to declare class hierarchies in order
is_extension_type
=
1
is_extension_type
=
1
has_attributes
=
1
has_attributes
=
1
early_init
=
1
objtypedef_cname
=
None
objtypedef_cname
=
None
...
...
tests/errors/subtyping_final_class.pyx
View file @
becd5ce7
...
@@ -10,5 +10,5 @@ cdef class SubType(FinalClass):
...
@@ -10,5 +10,5 @@ cdef class SubType(FinalClass):
pass
pass
_ERRORS
=
"""
_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