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
02a57572
Commit
02a57572
authored
Jul 01, 2020
by
Xavier Thompson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow more than one extension type base class when the others all have empty layout
parent
5aaef58d
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
49 additions
and
105 deletions
+49
-105
Cython/Compiler/CypclassWrapper.py
Cython/Compiler/CypclassWrapper.py
+4
-97
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+11
-2
Cython/Utility/ExtensionTypes.c
Cython/Utility/ExtensionTypes.c
+9
-3
tests/run/cdef_multiple_inheritance_errors.srctree
tests/run/cdef_multiple_inheritance_errors.srctree
+25
-3
No files found.
Cython/Compiler/CypclassWrapper.py
View file @
02a57572
...
...
@@ -120,7 +120,6 @@ def NAME(self, ARGDECLS):
self
.
synthesized
=
set
()
self
.
nesting_stack
=
[]
self
.
module_scope
=
node
.
scope
self
.
cimport_cython
=
True
self
.
visitchildren
(
node
)
self
.
inject_cypclass_wrappers
(
node
)
return
node
...
...
@@ -183,24 +182,10 @@ def NAME(self, ARGDECLS):
nested_name
=
"_"
.
join
(
nested_names
)
cclass_name
=
self
.
create_unique_name
(
"%s_cyp_cclass_wrapper"
%
nested_name
)
pyclass_name
=
self
.
create_unique_name
(
"%s_cyp_pyclass_wrapper"
%
nested_name
)
self
.
type_to_names
[
node
.
entry
.
type
]
=
qualified_name
,
cclass_name
,
pyclass_name
self
.
type_to_names
[
node
.
entry
.
type
]
=
qualified_name
,
cclass_name
def
inject_cypclass_wrappers
(
self
,
module_node
):
if
self
.
cimport_cython
:
# cimport cython to access the @cython.binding decorator
# use a unique name for "cimport cython as <name>" if necessary
as_name
=
self
.
create_unique_name
(
"cython"
)
self
.
cython_as_name
=
as_name
cimport_stmt
=
Nodes
.
CImportStatNode
(
module_node
.
pos
,
module_name
=
EncodedString
(
"cython"
),
as_name
=
None
if
as_name
==
"cython"
else
as_name
,
is_absolute
=
True
)
self
.
wrappers
.
append
(
cimport_stmt
)
for
collected
in
self
.
collected_cypclasses
:
self
.
synthesize_wrappers
(
collected
)
...
...
@@ -224,7 +209,7 @@ def NAME(self, ARGDECLS):
self
.
base_type_to_deferred
[
wrapped_base_type
].
append
(
lambda
:
self
.
synthesize_wrappers
(
node
))
return
qualified_name
,
cclass_name
,
pyclass_name
=
self
.
type_to_names
[
node_type
]
qualified_name
,
cclass_name
=
self
.
type_to_names
[
node_type
]
cclass
=
self
.
synthesize_wrapper_cclass
(
node
,
cclass_name
,
qualified_name
)
...
...
@@ -234,25 +219,7 @@ def NAME(self, ARGDECLS):
# forward declare the cclass wrapper
cclass
.
declare
(
self
.
module_scope
)
pyclass
=
self
.
synthesize_wrapper_pyclass
(
node
,
cclass
,
qualified_name
,
cclass_name
,
pyclass_name
)
# allow the cclass methods to bind on instance of the pyclass
binding_decorator
=
Nodes
.
DecoratorNode
(
node
.
pos
,
decorator
=
ExprNodes
.
SimpleCallNode
(
node
.
pos
,
function
=
ExprNodes
.
AttributeNode
(
node
.
pos
,
attribute
=
EncodedString
(
"binding"
),
obj
=
ExprNodes
.
NameNode
(
node
.
pos
,
name
=
self
.
cython_as_name
)
),
args
=
[
ExprNodes
.
BoolNode
(
node
.
pos
,
value
=
True
)]
)
)
cclass
.
decorators
=
[
binding_decorator
]
self
.
wrappers
.
append
(
cclass
)
self
.
wrappers
.
append
(
pyclass
)
# synthesize deferred dependent subclasses
for
thunk
in
self
.
base_type_to_deferred
[
node_type
]:
...
...
@@ -263,24 +230,8 @@ def NAME(self, ARGDECLS):
bases_args
=
[]
wrapped_bases_iterator
=
node_type
.
iter_wrapped_base_types
()
try
:
# consume the first wrapped base from the iterator
first_wrapped_base
=
next
(
wrapped_bases_iterator
)
first_base_cclass_name
=
first_wrapped_base
.
wrapper_type
.
name
wrapped_first_base
=
ExprNodes
.
NameNode
(
node
.
pos
,
name
=
first_base_cclass_name
)
bases_args
.
append
(
wrapped_first_base
)
# use the pyclass wrapper for the other bases
for
other_base
in
wrapped_bases_iterator
:
_
,
__
,
other_base_pyclass_name
=
self
.
type_to_names
[
other_base
]
other_base_arg
=
ExprNodes
.
NameNode
(
node
.
pos
,
name
=
other_base_pyclass_name
)
bases_args
.
append
(
other_base_arg
)
except
StopIteration
:
# no bases
pass
for
base
in
node_type
.
iter_wrapped_base_types
():
bases_args
.
append
(
ExprNodes
.
NameNode
(
node
.
pos
,
name
=
base
.
wrapper_type
.
name
))
return
ExprNodes
.
TupleNode
(
node
.
pos
,
args
=
bases_args
)
...
...
@@ -427,50 +378,6 @@ def NAME(self, ARGDECLS):
return
method_wrapper
def
synthesize_empty_slots
(
self
,
node
):
lhs
=
ExprNodes
.
NameNode
(
node
.
pos
,
name
=
EncodedString
(
"__slots__"
))
rhs
=
ExprNodes
.
TupleNode
(
node
.
pos
,
args
=
[])
stat
=
Nodes
.
SingleAssignmentNode
(
node
.
pos
,
lhs
=
lhs
,
rhs
=
rhs
)
return
stat
def
synthesize_wrapper_pyclass
(
self
,
node
,
cclass_wrapper
,
qualified_name
,
cclass_name
,
pyclass_name
):
py_bases
=
self
.
synthesize_base_tuple
(
node
)
# declare '__slots__ = ()' to allow this pyclass to be a base class
# of an extension type that doesn't define a 'cdef dict __dict__'.
py_empty_slots
=
self
.
synthesize_empty_slots
(
node
)
py_stats
=
[
py_empty_slots
]
for
defnode
in
cclass_wrapper
.
body
.
stats
:
if
isinstance
(
defnode
,
Nodes
.
DefNode
):
def_name
=
defnode
.
name
lhs
=
ExprNodes
.
NameNode
(
defnode
.
pos
,
name
=
def_name
)
rhs_obj
=
ExprNodes
.
NameNode
(
defnode
.
pos
,
name
=
cclass_name
)
rhs
=
ExprNodes
.
AttributeNode
(
defnode
.
pos
,
obj
=
rhs_obj
,
attribute
=
def_name
)
stat
=
Nodes
.
SingleAssignmentNode
(
defnode
.
pos
,
lhs
=
lhs
,
rhs
=
rhs
)
py_stats
.
append
(
stat
)
py_body
=
Nodes
.
StatListNode
(
cclass_wrapper
.
pos
,
stats
=
py_stats
)
py_class_node
=
Nodes
.
PyClassDefNode
(
cclass_wrapper
.
pos
,
name
=
pyclass_name
,
bases
=
py_bases
,
keyword_args
=
None
,
doc
=
cclass_wrapper
.
doc
,
body
=
py_body
,
decorators
=
None
,
)
return
py_class_node
#
# Utilities for cypclasses
...
...
Cython/Compiler/Nodes.py
View file @
02a57572
...
...
@@ -5278,8 +5278,17 @@ class CClassDefNode(ClassDefNode):
# At runtime, we check that the other bases are heap types
# and that a __dict__ is added if required.
for
other_base
in
self
.
bases
.
args
[
1
:]:
if
other_base
.
analyse_as_type
(
env
):
error
(
other_base
.
pos
,
"Only one extension type base class allowed."
)
base_extension_type
=
other_base
.
analyse_as_type
(
env
)
if
base_extension_type
:
if
base_extension_type
.
attributes_known
():
base_scope
=
base_extension_type
.
scope
# Allow other extension type bases if they only
# have python function or python property members.
npyfunc
=
len
(
base_scope
.
pyfunc_entries
)
npyproperty
=
len
([
e
for
e
in
base_scope
.
property_entries
if
not
e
.
is_cproperty
])
if
npyfunc
+
npyproperty
==
len
(
base_scope
.
entries
):
continue
error
(
other_base
.
pos
,
"Only one extension type base class with non-empty C layout allowed."
)
self
.
entry
.
type
.
early_init
=
0
from
.
import
ExprNodes
self
.
type_init_args
=
ExprNodes
.
TupleNode
(
...
...
Cython/Utility/ExtensionTypes.c
View file @
02a57572
...
...
@@ -38,9 +38,15 @@ static int __Pyx_PyType_Ready(PyTypeObject *t) {
b
=
(
PyTypeObject
*
)
b0
;
if
(
!
PyType_HasFeature
(
b
,
Py_TPFLAGS_HEAPTYPE
))
{
PyErr_Format
(
PyExc_TypeError
,
"base class '%.200s' is not a heap type"
,
b
->
tp_name
);
return
-
1
;
// Allow non-heap base types that strictly have the layout of a PyObject,
// as there is no chance of layout-conflict in that case.
if
(
(
b
->
tp_basicsize
!=
sizeof
(
PyObject
))
||
(
b
->
tp_itemsize
!=
0
)
)
{
PyErr_Format
(
PyExc_TypeError
,
"base class '%.200s' is not a heap type "
"and has non-empty C layout"
,
b
->
tp_name
);
return
-
1
;
}
}
if
(
t
->
tp_dictoffset
==
0
&&
b
->
tp_dictoffset
)
{
...
...
tests/run/cdef_multiple_inheritance_errors.srctree
View file @
02a57572
...
...
@@ -13,9 +13,25 @@ setup(ext_modules=cythonize("*.pyx"))
cdef class Base:
pass
Obj = type(objec
t())
NonEmptyLayout = type(lis
t())
cdef class Foo(Base, Obj):
cdef class Foo(Base, NonEmptyLayout):
pass
######## notheaptype2.pyx ########
cdef class Base:
cdef int somelayout
cdef class Left(Base):
pass
cdef class Right(Base):
pass
Other = type(Right())
cdef class Diamond(Left, Other):
pass
######## wrongbase.pyx ########
...
...
@@ -67,7 +83,13 @@ try:
import notheaptype
assert False
except TypeError as msg:
assert str(msg) == "base class 'object' is not a heap type"
assert str(msg) == "best base 'list' must be equal to first base 'notheaptype.Base'"
try:
import notheaptype2
assert False
except TypeError as msg:
assert str(msg) == "base class 'notheaptype2.Right' is not a heap type and has non-empty C layout"
try:
import wrongbase
...
...
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