def create_special_iadd_method(self, cdcl, mdcl): L.info(" create wrapper for operator+") assert len(mdcl.arguments) == 1, "operator+ has wrong signature" (__, t), = mdcl.arguments name = cdcl.name assert t.base_type == name, "can only add to myself" assert mdcl.result_type.base_type == name, "can only return same type" cy_t = self.cr.cython_type(t) code = Code.Code() code.add( """ | |def __iadd__($name self, $name other not None): | cdef $cy_t * this = self.inst.get() | cdef $cy_t * that = other.inst.get() | _iadd(this, that) | return self """, locals()) tl = Code.Code() tl.add( """ |cdef extern from "autowrap_tools.hpp": | void _iadd($cy_t *, $cy_t *) """, locals()) self.top_level_code.append(tl) return code
def create_special_getitem_method(self, mdcl): L.info(" create wrapper for operator[]") meth_code = Code.Code() (call_arg,), cleanups, (in_type,) =\ self._create_fun_decl_and_input_conversion(meth_code, "__getitem__", mdcl) meth_code.add( """ | cdef long _idx = $call_arg """, locals()) if in_type.is_unsigned: meth_code.add( """ | if _idx < 0: | raise IndexError("invalid index %d" % _idx) """, locals()) size_guard = mdcl.cpp_decl.annotations.get("wrap-upper-limit") if size_guard: meth_code.add( """ | if _idx >= self.inst.get().$size_guard: | raise IndexError("invalid index %d" % _idx) """, locals()) # call wrapped method and convert result value back to python cy_call_str = "deref(self.inst.get())[%s]" % call_arg res_t = mdcl.result_type out_converter = self.cr.get(res_t) full_call_stmt = out_converter.call_method(res_t, cy_call_str) if isinstance(full_call_stmt, basestring): meth_code.add( """ | $full_call_stmt """, locals()) else: meth_code.add(full_call_stmt) for cleanup in reversed(cleanups): if not cleanup: continue if isinstance(cleanup, basestring): cleanup = Code.Code().add(cleanup) meth_code.add(cleanup) out_var = "py_result" to_py_code = out_converter.output_conversion(res_t, "_r", out_var) if to_py_code is not None: # for non void return value if isinstance(to_py_code, basestring): to_py_code = " %s" % to_py_code meth_code.add(to_py_code) meth_code.add(" return $out_var", locals()) return meth_code
def create_wrapper_for_enum(self, decl): self.wrapped_enums_cnt += 1 if decl.cpp_decl.annotations.get("wrap-attach"): name = "__" + decl.name else: name = decl.name L.info("create wrapper for enum %s" % name) code = Code.Code() enum_pxd_code = Code.Code() enum_pxd_code.add(""" | |cdef class $name: | pass """, name=name) code.add(""" | |cdef class $name: """, name=name) for (name, value) in decl.items: code.add(" $name = $value", name=name, value=value) self.class_codes[decl.name] = code self.class_pxd_codes[decl.name] = enum_pxd_code for class_name in decl.cpp_decl.annotations.get("wrap-attach", []): code = Code.Code() display_name = decl.cpp_decl.annotations.get( "wrap-as", [decl.name])[0] code.add("%s = %s" % (display_name, "__" + decl.name)) self.class_codes[class_name].add(code)
def create_wrapper_for_nonoverloaded_method(self, cdcl, py_name, method): L.info(" create wrapper for %s ('%s')" % (py_name, method)) meth_code = Code.Code() call_args, cleanups, in_types = self._create_fun_decl_and_input_conversion( meth_code, py_name, method) # call wrapped method and convert result value back to python cpp_name = method.cpp_decl.name call_args_str = ", ".join(call_args) cy_call_str = "self.inst.get().%s(%s)" % (cpp_name, call_args_str) res_t = method.result_type out_converter = self.cr.get(res_t) full_call_stmt = out_converter.call_method(res_t, cy_call_str) if method.with_nogil: meth_code.add(""" | with nogil: """) indented = Code.Code() else: indented = meth_code if isinstance(full_call_stmt, basestring): indented.add( """ | $full_call_stmt """, locals()) else: indented.add(full_call_stmt) for cleanup in reversed(cleanups): if not cleanup: continue if isinstance(cleanup, basestring): cleanup = " %s" % cleanup indented.add(cleanup) to_py_code = out_converter.output_conversion(res_t, "_r", "py_result") if to_py_code is not None: # for non void return value if isinstance(to_py_code, basestring): to_py_code = " %s" % to_py_code indented.add(to_py_code) indented.add(" return py_result") if method.with_nogil: meth_code.add(indented) return meth_code
def _create_iter_methods(self, iterators, instance_mapping, local_mapping): """ Create Iterator methods using the Python yield keyword """ codes = [] for name, (begin_decl, end_decl, res_type) in iterators.items(): L.info(" create wrapper for iter %s" % name) meth_code = Code.Code() begin_name = begin_decl.name end_name = end_decl.name # TODO: this step is duplicated from DeclResolver.py # can we combine both maps to one single map ? res_type = res_type.transformed(local_mapping) res_type = res_type.inv_transformed(instance_mapping) cy_type = self.cr.cython_type(res_type) base_type = res_type.base_type meth_code.add( """ | |def $name(self): | it = self.inst.get().$begin_name() | cdef $base_type out | while it != self.inst.get().$end_name(): | out = $base_type.__new__($base_type) | out.inst = + shared_ptr[$cy_type](new $cy_type(deref(it))) | yield out | inc(it) """, locals()) codes.append(meth_code) return codes
def create_wrapper_for_nonoverloaded_constructor(self, class_decl, py_name, cons_decl): """ py_name is the name for constructor, as we dispatch overloaded constructors in __init__() the name of the method calling the C++ constructor is variable and given by `py_name`. """ L.info(" create wrapper for non overloaded constructor %s" % py_name) cons_code = Code.Code() call_args, cleanups, in_types =\ self._create_fun_decl_and_input_conversion(cons_code, py_name, cons_decl) wrap_pass = cons_decl.cpp_decl.annotations.get("wrap-pass-constructor", False) if wrap_pass: cons_code.add(" pass") return cons_code # create instance of wrapped class call_args_str = ", ".join(call_args) name = class_decl.name cy_type = self.cr.cython_type(name) cons_code.add( """ self.inst = shared_ptr[$cy_type](new $cy_type($call_args_str))""", locals()) for cleanup in reversed(cleanups): if not cleanup: continue if isinstance(cleanup, basestring): cleanup = " %s" % cleanup cons_code.add(cleanup) return cons_code
def create_std_cimports(self): code = Code.Code() code.add(""" |#cython: c_string_encoding=ascii # for cython>=0.19 |from libcpp.string cimport string as libcpp_string |from libcpp.string cimport string as libcpp_utf8_string |from libcpp.set cimport set as libcpp_set |from libcpp.vector cimport vector as libcpp_vector |from libcpp.pair cimport pair as libcpp_pair |from libcpp.map cimport map as libcpp_map |from smart_ptr cimport shared_ptr |from AutowrapRefHolder cimport AutowrapRefHolder |from AutowrapPtrHolder cimport AutowrapPtrHolder |from AutowrapConstPtrHolder cimport AutowrapConstPtrHolder |from libcpp cimport bool |from libc.string cimport const_char |from cython.operator cimport dereference as deref, + preincrement as inc, address as address """) if self.extra_cimports is not None: for stmt in self.extra_cimports: code.add(stmt) self.top_level_code.append(code)
def create_includes(self): code = Code.Code() code.add(""" |cdef extern from "autowrap_tools.hpp": | char * _cast_const_away(char *) """) self.top_level_code.append(code)
def _create_overloaded_method_decl(self, py_name, dispatched_m_names, methods, use_return, use_kwargs=False): L.info(" create wrapper decl for overloaded method %s" % py_name) method_code = Code.Code() kwargs = "" if use_kwargs: kwargs = ", **kwargs" method_code.add( """ | |def $py_name(self, *args $kwargs): """, locals()) first_iteration = True for (dispatched_m_name, method) in zip(dispatched_m_names, methods): args = augment_arg_names(method) if not args: check_expr = "not args" # Special case for empty constructors with a pass if method.cpp_decl.annotations.get("wrap-pass-constructor", False): assert use_kwargs, "Cannot use wrap-pass-constructor without setting kwargs (e.g. outside a constructor)" check_expr = 'kwargs.get("__createUnsafeObject__") is True' else: tns = [(t, "args[%d]" % i) for i, (t, n) in enumerate(args)] checks = ["len(args)==%d" % len(tns)] checks += [ self.cr.get(t).type_check_expression(t, n) for (t, n) in tns ] check_expr = " and ".join("(%s)" % c for c in checks) return_ = "return" if use_return else "" if_elif = "if" if first_iteration else "elif" method_code.add( """ | $if_elif $check_expr: | $return_ self.$dispatched_m_name(*args) """, locals()) first_iteration = False method_code.add(""" else: | raise + Exception('can not handle type of %s' % (args,))""") return method_code
def create_foreign_cimports(self): """Iterate over foreign modules and import all relevant classes from them It is necessary to let Cython know about other autowrap-created classes that may reside in other modules, basically any "cdef" definitions that we may be using in this compilation unit. Since we are passing objects as arguments quite frequently, we need to know about all other wrapped classes and we need to cimport them. E.g. if we have module1 containing classA, classB and want to access it through the pxd header, then we need to add: from module1 import classA, classB """ code = Code.Code() L.info("Create foreign imports for module %s" % self.target_path) for module in self.allDecl: # We skip our own module if os.path.basename(self.target_path).split(".pyx")[0] != module: for resolved in self.allDecl[module]["decls"]: # We need to import classes and enums that could be used in # the Cython code in the current module # use Cython name, which correctly imports template classes (instead of C name) name = resolved.name if resolved.__class__ in (ResolvedEnum, ): if resolved.cpp_decl.annotations.get("wrap-attach"): # No need to import attached classes as they are # usually in the same pxd file and should not be # globally exported. pass else: code.add("from $module cimport $name", locals()) if resolved.__class__ in (ResolvedClass, ): # Skip classes that explicitely should not have a pxd # import statement (abstract base classes and the like) if not resolved.no_pxd_import: if resolved.cpp_decl.annotations.get( "wrap-attach"): code.add("from $module cimport __$name", locals()) else: code.add("from $module cimport $name", locals()) else: L.info("Skip imports from self (own module %s)" % module) self.top_level_code.append(code)
def create_wrapper_for_free_function(self, decl): L.info("create wrapper for free function %s" % decl.name) self.wrapped_methods_cnt += 1 static_clz = decl.cpp_decl.annotations.get("wrap-attach") if static_clz is None: code = self._create_wrapper_for_free_function(decl) else: code = Code.Code() static_name = "__static_%s_%s" % (static_clz, decl.name) code.add("%s = %s" % (decl.name, static_name)) self.class_codes[static_clz].add(code) code = self._create_wrapper_for_free_function(decl, static_name) self.top_level_code.append(code)
def create_wrapper_for_constructor(self, class_decl, constructors): real_constructors = [] codes = [] for cons in constructors: if len(cons.arguments) == 1: (n, t), = cons.arguments if t.base_type == class_decl.name and t.is_ref: code = self.create_special_copy_method(class_decl) codes.append(code) real_constructors.append(cons) if len(real_constructors) == 1: if real_constructors[0].cpp_decl.annotations.get( "wrap-pass-constructor", False): # We have a single constructor that cannot be called (except # with the magic keyword), simply check the magic word cons_code = Code.Code() cons_code.add( """ | |def __init__(self, *args, **kwargs): | if not kwargs.get("__createUnsafeObject__") is True: | raise Exception("Cannot call this constructor") """, locals()) codes.append(cons_code) return codes code = self.create_wrapper_for_nonoverloaded_constructor( class_decl, "__init__", real_constructors[0]) codes.append(code) else: dispatched_cons_names = [] for (i, constructor) in enumerate(real_constructors): dispatched_cons_name = "_init_%d" % i dispatched_cons_names.append(dispatched_cons_name) code = self.create_wrapper_for_nonoverloaded_constructor( class_decl, dispatched_cons_name, constructor) codes.append(code) code = self._create_overloaded_method_decl("__init__", dispatched_cons_names, constructors, False, True) codes.append(code) return codes
def create_cimports(self): self.create_std_cimports() code = Code.Code() for resolved in self.resolved: import_from = resolved.pxd_import_path name = resolved.name if resolved.__class__ in (ResolvedEnum, ): code.add("from $import_from cimport $name as _$name", locals()) elif resolved.__class__ in (ResolvedClass, ): name = resolved.cpp_decl.name code.add("from $import_from cimport $name as _$name", locals()) elif resolved.__class__ in (ResolvedFunction, ): mangled_name = "_" + name + "_" + import_from code.add("from $import_from cimport $name as $mangled_name", locals()) elif resolved.__class__ in (ResolvedTypeDef, ): code.add("from $import_from cimport $name", locals()) self.top_level_code.append(code)
def _create_wrapper_for_free_function(self, decl, name=None): if name is None: name = decl.name fun_code = Code.Code() call_args, cleanups, in_types =\ self._create_fun_decl_and_input_conversion(fun_code, name, decl, is_free_fun=True) call_args_str = ", ".join(call_args) mangled_name = "_" + decl.name + "_" + decl.pxd_import_path cy_call_str = "%s(%s)" % (mangled_name, call_args_str) res_t = decl.result_type out_converter = self.cr.get(res_t) full_call_stmt = out_converter.call_method(res_t, cy_call_str) if isinstance(full_call_stmt, basestring): fun_code.add( """ | $full_call_stmt """, locals()) else: fun_code.add(full_call_stmt) for cleanup in reversed(cleanups): if not cleanup: continue if isinstance(cleanup, basestring): cleanup = " %s" % cleanup fun_code.add(cleanup) to_py_code = out_converter.output_conversion(res_t, "_r", "py_result") out_vars = ["py_result"] if to_py_code is not None: # for non void return value if isinstance(to_py_code, basestring): to_py_code = " %s" % to_py_code fun_code.add(to_py_code) fun_code.add(" return %s" % (", ".join(out_vars))) return fun_code
def create_special_add_method(self, cdcl, mdcl): L.info(" create wrapper for operator+") assert len(mdcl.arguments) == 1, "operator+ has wrong signature" (__, t), = mdcl.arguments name = cdcl.name assert t.base_type == name, "can only add to myself" assert mdcl.result_type.base_type == name, "can only return same type" cy_t = self.cr.cython_type(t) code = Code.Code() code.add( """ | |def __add__($name self, $name other not None): | cdef $cy_t * this = self.inst.get() | cdef $cy_t * that = other.inst.get() | cdef $cy_t added = deref(this) + deref(that) | cdef $name result = $name.__new__($name) | result.inst = shared_ptr[$cy_t](new $cy_t(added)) | return result """, locals()) return code
def create_cast_methods(self, mdecls): py_names = [] for mdcl in mdecls: name = mdcl.cpp_decl.annotations.get("wrap-cast") if name is None: raise Exception("need wrap-cast annotation for %s" % mdcl) if name in py_names: raise Exception("wrap-cast annotation not unique for %s" % mdcl) py_names.append(name) codes = [] for (py_name, mdecl) in zip(py_names, mdecls): code = Code.Code() res_t = mdecl.result_type cy_t = self.cr.cython_type(res_t) out_converter = self.cr.get(res_t) code.add( """ | |def $py_name(self):""", locals()) call_stmt = "<%s>(deref(self.inst.get()))" % cy_t full_call_stmt = out_converter.call_method(res_t, call_stmt) if isinstance(full_call_stmt, basestring): code.add( """ | $full_call_stmt """, locals()) else: code.add(full_call_stmt) to_py_code = out_converter.output_conversion(res_t, "_r", "py_res") if isinstance(to_py_code, basestring): to_py_code = " %s" % to_py_code code.add(to_py_code) code.add(""" return py_res""") codes.append(code) return codes
def create_special_copy_method(self, class_decl): L.info(" create wrapper __copy__") meth_code = Code.Code() name = class_decl.name cy_type = self.cr.cython_type(name) meth_code.add( """ | |def __copy__(self): | cdef $name rv = $name.__new__($name) | rv.inst = shared_ptr[$cy_type](new $cy_type(deref(self.inst.get()))) | return rv """, locals()) meth_code.add( """ | |def __deepcopy__(self, memo): | cdef $name rv = $name.__new__($name) | rv.inst = shared_ptr[$cy_type](new $cy_type(deref(self.inst.get()))) | return rv """, locals()) return meth_code
def create_default_cimports(self): code = Code.Code() # Using embedsignature here does not help much as it is only the Python # signature which does not really specify the argument types. We have # to use a docstring for each method. code.add(""" |#cython: c_string_encoding=ascii # for cython>=0.19 |#cython: embedsignature=False |from libcpp.string cimport string as libcpp_string |from libcpp.string cimport string as libcpp_utf8_string |from libcpp.set cimport set as libcpp_set |from libcpp.vector cimport vector as libcpp_vector |from libcpp.pair cimport pair as libcpp_pair |from libcpp.map cimport map as libcpp_map |from libcpp cimport bool |from libc.string cimport const_char |from cython.operator cimport dereference as deref, + preincrement as inc, address as address """) if self.include_refholder: code.add(""" |from AutowrapRefHolder cimport AutowrapRefHolder |from AutowrapPtrHolder cimport AutowrapPtrHolder |from AutowrapConstPtrHolder cimport AutowrapConstPtrHolder """) if self.include_shared_ptr: code.add(""" |from smart_ptr cimport shared_ptr """) if self.include_numpy: code.add(""" |cimport numpy as np |import numpy as np |cimport numpy as numpy |import numpy as numpy """) return code
def create_special_cmp_method(self, cdcl, ops): L.info(" create wrapper __richcmp__") meth_code = Code.Code() name = cdcl.name op_code_map = { '<': 0, '==': 2, '>': 4, '<=': 1, '!=': 3, '>=': 5, } inv_op_code_map = dict((v, k) for (k, v) in op_code_map.items()) implemented_op_codes = tuple(op_code_map[k] for (k, v) in ops.items() if v) meth_code.add( """ | |def __richcmp__(self, other, op): | if op not in $implemented_op_codes: | op_str = $inv_op_code_map[op] | raise Exception("comparions operator %s not implemented" % op_str) | if not isinstance(other, $name): | return False | cdef $name other_casted = other | cdef $name self_casted = self """, locals()) for op in implemented_op_codes: op_sign = inv_op_code_map[op] meth_code.add( """ if op==$op: | return deref(self_casted.inst.get()) + $op_sign deref(other_casted.inst.get())""", locals()) return meth_code
def test(): Code = autowrap.Code.Code c = Code() c.add("def $name(x):", name="fun") inner = Code() inner.add("""if x + == 3: | return + 4 """) inner.add("else:") inner2 = Code() inner2.add("""return + 2*x""") inner.add(inner2) c.add(inner) result = c.render() lines = [line.rstrip() for line in result.split("\n")] assert lines[0] == "def fun(x):", repr(lines[0]) assert lines[1] == " if x == 3:", repr(lines[1]) assert lines[2] == " return 4", repr(lines[2]) assert lines[3] == " else:", repr(lines[3]) assert lines[4] == " return 2*x", repr(lines[4]) assert len(lines) == 5
def test(): Code = autowrap.Code.Code c = Code() c.add("def $name(x):", name="fun") inner = Code() inner.add("""if x + == 3: | return + 4 """) inner.add("else:") inner2 = Code() inner2.add("""return + 2*x""") inner.add(inner2) c.add(inner) result = c.render() lines = [line.rstrip() for line in result.split("\n")] assert lines[0] == "def fun(x):", repr(lines[0]) assert lines[1] == " if x == 3:", repr(lines[1]) assert lines[2] == " return 4", repr(lines[2]) assert lines[3] == " else:", repr(lines[3]) assert lines[4] == " return 2*x", repr(lines[4]) assert len(lines) == 5
def create_wrapper_for_class(self, r_class): """Create Cython code for a single class Note that the cdef class definition and the member variables go into the .pxd file while the Python-level implementation goes into the .pyx file. This allows us to cimport these classes later across modules. """ self.wrapped_classes_cnt += 1 self.wrapped_methods_cnt += len(r_class.methods) cname = r_class.name if r_class.cpp_decl.annotations.get("wrap-attach"): pyname = "__" + r_class.name else: pyname = cname L.info("create wrapper for class %s" % cname) cy_type = self.cr.cython_type(cname) class_pxd_code = Code.Code() class_code = Code.Code() if r_class.methods: shared_ptr_inst = "cdef shared_ptr[%s] inst" % cy_type if len(r_class.wrap_manual_memory ) != 0 and r_class.wrap_manual_memory[0] != "__old-model": shared_ptr_inst = r_class.wrap_manual_memory[0] if self.write_pxd: class_pxd_code.add( """ | |cdef class $pyname: | | $shared_ptr_inst | """, locals()) shared_ptr_inst = "# see .pxd file for cdef of inst ptr" # do not implement in pyx file, only in pxd file if len(r_class.wrap_manual_memory) != 0: class_code.add( """ | |cdef class $pyname: | """, locals()) else: class_code.add( """ | |cdef class $pyname: | | $shared_ptr_inst | | def __dealloc__(self): | self.inst.reset() | """, locals()) else: # Deal with pure structs (no methods) class_pxd_code.add( """ | |cdef class $pyname: | | pass | """, locals()) class_code.add( """ | |cdef class $pyname: | """, locals()) if len(r_class.wrap_hash) != 0: class_code.add( """ | | def __hash__(self): | # The only required property is that objects which compare equal have | # the same hash value: | return hash(deref(self.inst.get()).%s ) | """ % r_class.wrap_hash[0], locals()) self.class_pxd_codes[cname] = class_pxd_code self.class_codes[cname] = class_code cons_created = False for attribute in r_class.attributes: if not attribute.wrap_ignore: class_code.add(self._create_wrapper_for_attribute(attribute)) iterators, non_iter_methods = self.filterout_iterators(r_class.methods) for (name, methods) in non_iter_methods.items(): if name == r_class.name: codes = self.create_wrapper_for_constructor(r_class, methods) cons_created = True else: codes = self.create_wrapper_for_method(r_class, name, methods) for ci in codes: class_code.add(ci) has_ops = dict() for ops in ["==", "!=", "<", "<=", ">", ">="]: has_op = ("operator%s" % ops) in non_iter_methods has_ops[ops] = has_op if any(v for v in has_ops.values()): code = self.create_special_cmp_method(r_class, has_ops) class_code.add(code) codes = self._create_iter_methods(iterators, r_class.instance_map, r_class.local_map) for ci in codes: class_code.add(ci) extra_methods_code = self.manual_code.get(cname) if extra_methods_code: class_code.add(extra_methods_code) for class_name in r_class.cpp_decl.annotations.get("wrap-attach", []): code = Code.Code() display_name = r_class.cpp_decl.annotations.get( "wrap-as", [r_class.name])[0] code.add("%s = %s" % (display_name, "__" + r_class.name)) tmp = self.class_codes_extra.get(class_name, []) tmp.append(code) self.class_codes_extra[class_name] = tmp
def create_wrapper_for_class(self, r_class): """Create Cython code for a single class""" self.wrapped_classes_cnt += 1 self.wrapped_methods_cnt += len(r_class.methods) cname = r_class.name L.info("create wrapper for class %s" % cname) cy_type = self.cr.cython_type(cname) class_code = Code.Code() if r_class.methods and not r_class.wrap_manual_memory: class_code.add( """ | |cdef class $cname: | | cdef shared_ptr[$cy_type] inst | | def __dealloc__(self): | self.inst.reset() | """, locals()) else: class_code.add( """ | |cdef class $cname: | """, locals()) if len(r_class.wrap_hash) != 0: class_code.add( """ | | def __hash__(self): | # The only required property is that objects which compare equal have | # the same hash value: | return hash(deref(self.inst.get()).%s ) | """ % r_class.wrap_hash[0], locals()) self.class_codes[cname] = class_code cons_created = False for attribute in r_class.attributes: if not attribute.wrap_ignore: class_code.add(self._create_wrapper_for_attribute(attribute)) iterators, non_iter_methods = self.filterout_iterators(r_class.methods) for (name, methods) in non_iter_methods.items(): if name == r_class.name: codes = self.create_wrapper_for_constructor(r_class, methods) cons_created = True else: codes = self.create_wrapper_for_method(r_class, name, methods) for ci in codes: class_code.add(ci) has_ops = dict() for ops in ["==", "!=", "<", "<=", ">", ">="]: has_op = ("operator%s" % ops) in non_iter_methods has_ops[ops] = has_op if any(v for v in has_ops.values()): code = self.create_special_cmp_method(r_class, has_ops) class_code.add(code) codes = self._create_iter_methods(iterators, r_class.instance_map, r_class.local_map) for ci in codes: class_code.add(ci) extra_methods_code = self.manual_code.get(cname) if extra_methods_code: class_code.add(extra_methods_code)
def _create_wrapper_for_attribute(self, attribute): code = Code.Code() name = attribute.name wrap_as = attribute.cpp_decl.annotations.get("wrap-as", name) t = attribute.type_ converter = self.cr.get(t) py_type = converter.matching_python_type(t) conv_code, call_as, cleanup = converter.input_conversion(t, name, 0) code.add( """ | |property $wrap_as: | | def __set__(self, $py_type $name): """, locals()) # TODO: add mit indent level indented = Code.Code() indented.add(conv_code) code.add(indented) code.add( """ | self.inst.get().$name = $call_as """, locals()) indented = Code.Code() if isinstance(cleanup, basestring): cleanup = " %s" % cleanup indented.add(cleanup) code.add(indented) to_py_code = converter.output_conversion(t, "_r", "py_result") access_stmt = converter.call_method(t, "self.inst.get().%s" % name) cy_type = self.cr.cython_type(t) if isinstance(to_py_code, basestring): to_py_code = " %s" % to_py_code if isinstance(access_stmt, basestring): access_stmt = " %s" % access_stmt if t.is_ptr: # For pointer types, we need to guard against unsafe access code.add( """ | | def __get__(self): | if self.inst.get().%s is NULL: | raise Exception("Cannot access pointer that is NULL") """ % name, locals()) else: code.add( """ | | def __get__(self): """, locals()) # increase indent: indented = Code.Code() indented.add(access_stmt) indented.add(to_py_code) code.add(indented) code.add(" return py_result") return code