Commit 7ace0059 authored by Xavier Thompson's avatar Xavier Thompson

Support pyclass wrappers for multiple inheritance

parent 404a99a4
This diff is collapsed.
......@@ -5246,7 +5246,6 @@ class CClassDefNode(ClassDefNode):
if scope is not None and self.is_cyp_wrapper:
# > correct a cypclass wrapper scope's name
# scope.name = self.wrapped_cypclass.name
scope.qualified_name = scope.qualifying_scope().qualify_name(self.wrapped_nested_name)
scope.class_name = self.wrapped_nested_name
......@@ -5550,152 +5549,6 @@ class CypclassWrapperDefNode(CClassDefNode):
# > remember the cname of the wrapped type
self.entry.type.wrapped_decl = self.wrapped_cypclass.entry.type.empty_declaration_code()
def analyse_declarations(self, env):
# > analyse declarations before inserting methods
super(CypclassWrapperDefNode, self).analyse_declarations(env)
# > insert and analyse each method wrapper
self.insert_cypclass_method_wrappers(env)
def insert_cypclass_method_wrappers(self, env):
if self.wrapped_cypclass.attributes is None:
return
for attr in self.wrapped_cypclass.attributes:
if isinstance(attr, CFuncDefNode):
py_method_wrapper = self.synthesize_cypclass_method_wrapper(attr, env)
if py_method_wrapper:
self.body.stats.append(py_method_wrapper)
py_method_wrapper.analyse_declarations(self.scope)
def synthesize_cypclass_method_wrapper(self, cfunc_method, env):
if cfunc_method.is_static_method:
return # for now skip static methods
alternatives = cfunc_method.entry.all_alternatives()
# > consider only the alternatives that are actually defined in this wrapped cypclass
alternatives = list(filter(lambda e: e.mro_index == 0, alternatives))
if len(alternatives) > 1:
return # for now skip overloaded methods
cfunc_declarator = cfunc_method.cfunc_declarator
# > c++ methods have an implict 'this', so the 'self' argument is skipped in the declarator
skipped_self = cfunc_declarator.skipped_self
if not skipped_self:
return # if this ever happens (?), skip non-static methods without a self argument
cfunc_type = cfunc_method.type
cfunc_return_type = cfunc_type.return_type
# we pass the global scope as argument, should not affect the result (?)
if not cfunc_return_type.can_coerce_to_pyobject(env.global_scope()):
return # skip c methods with Python-incompatible return types
for argtype in cfunc_type.args:
if not argtype.type.can_coerce_to_pyobject(env.global_scope()):
return # skip c methods with Python-incompatible argument types
from .CypclassWrapper import underlying_name
from . import ExprNodes
# > name of the wrapping method: same name as in the original code
cfunc_name = cfunc_declarator.base.name
py_name = cfunc_name
# > self argument of the wrapper method: same name, but type of the wrapper cclass
self_name, self_type, self_pos, self_arg = skipped_self
py_self_arg = CArgDeclNode(
self_pos,
base_type = CSimpleBaseTypeNode(
self_pos,
name = self.class_name,
module_path = [],
signed = 1,
is_basic_c_type = 0,
longness = 0,
is_self_arg = 0, # only true for C methods
templates = None
),
declarator = CNameDeclaratorNode(self_pos, name=self_name, cname=None),
not_none = 0,
or_none = 0,
default = None,
annotation = None,
kw_only = 0
)
# > all arguments of the wrapper method declaration
py_args = [py_self_arg]
for arg in cfunc_declarator.args:
py_args.append(arg.clone_node())
# > same docstring
py_doc = cfunc_method.doc
# > names of the arguments passed when calling the underlying method; self not included
arg_objs = [ExprNodes.NameNode(arg.pos, name=arg.name) for arg in cfunc_declarator.args]
# > reference to the self argument of the wrapper method
self_obj = ExprNodes.NameNode(self_pos, name=self_name)
# > access the underlying cyobject from the self argument of the wrapper method
underlying_obj = ExprNodes.AttributeNode(cfunc_method.pos, obj=self_obj, attribute=underlying_name)
# > cast the underlying object back to this type
underlying_type = self.wrapped_cypclass.entry.type
cast_operation = ExprNodes.TypecastNode(
cfunc_method.pos,
type = underlying_type,
operand = underlying_obj,
typecheck = False
)
cast_underlying_obj = ExprNodes.NameNode(self_pos, name=EncodedString("cast_cyobject"))
cast_assignment = SingleAssignmentNode(self_pos, lhs=cast_underlying_obj, rhs=cast_operation)
# > access the method of the underlying object
cfunc = ExprNodes.AttributeNode(cfunc_method.pos, obj=cast_underlying_obj, attribute=cfunc_name)
# > call to the underlying method
c_call = ExprNodes.SimpleCallNode(
cfunc_method.pos,
function=cfunc,
args=arg_objs
)
# > return the result of the call if the underlying return type is not void
if cfunc_return_type.is_void:
py_stat = ExprStatNode(cfunc_method.pos, expr=c_call)
else:
py_stat = ReturnStatNode(cfunc_method.pos, return_type=PyrexTypes.py_object_type, value=c_call)
py_body = StatListNode(cfunc_method.pos, stats=[cast_assignment, py_stat])
# > lock around the call in checklock mode
if self.wrapped_cypclass.lock_mode == 'checklock':
need_wlock = not cfunc_type.is_const_method
lock_node = LockCypclassNode(
cfunc_method.pos,
state = 'wlocked' if need_wlock else 'rlocked',
obj = cast_underlying_obj,
body = py_body
)
py_body = lock_node
# > the wrapper method
return DefNode(
cfunc_method.pos,
name = py_name,
args = py_args,
star_arg = None,
starstar_arg = None,
doc = py_doc,
body = py_body,
decorators = None,
is_async_def = 0,
return_type_annotation = None
)
class PropertyNode(StatNode):
# Definition of a property in an extension type.
......
......@@ -4141,6 +4141,12 @@ class CypClassType(CppClassType):
self.wrapped_base_type = base_type.wrapped_base_type
break
# iterate over the bases that support wrapping
def iter_wrapped_base_types(self):
for base_type in self.base_classes:
if base_type.is_cyp_class and base_type.support_wrapper:
yield base_type
# Return the MRO for this cypclass
# Compute all the mro needed when a previous computation is not available
# based on https://mail.python.org/pipermail/python-dev/2002-October/029176.html
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment