예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
    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)
예제 #4
0
    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
예제 #5
0
    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
예제 #6
0
    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
예제 #7
0
    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)
예제 #8
0
    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)
예제 #9
0
    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
예제 #10
0
    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)
예제 #11
0
    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)
예제 #12
0
    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
예제 #13
0
    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)
예제 #14
0
    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
예제 #15
0
 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
예제 #16
0
    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
예제 #17
0
 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
예제 #18
0
    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
예제 #19
0
    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
예제 #20
0
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
예제 #21
0
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
예제 #22
0
    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
예제 #23
0
    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)
예제 #24
0
    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