def test_templated(): target = os.path.join(test_files, "templated_wrapper.pyx") decls, instance_map = autowrap.parse(["templated.pxd"], root=test_files) co = autowrap.Code.Code() co.add("""def special(self): | return "hi" """) methods = dict(T=co) include_dirs = autowrap.generate_code(decls, instance_map, target=target, debug=True, manual_code=methods) cpp_source = os.path.join(test_files, "templated.cpp") cpp_sources = [] twrapped = autowrap.Utils.compile_and_import("twrapped", [target] + cpp_sources, include_dirs) os.remove(target) assert twrapped.__name__ == "twrapped" t = twrapped.T(42) assert t.special() == "hi" templated = twrapped.Templated(t) assert templated.get().get() == 42 assert templated.passs(templated) == templated in_ = [templated, templated] assert templated.summup(in_) == 42 + 42 __, __, tn = in_ assert tn.get().get() == 11 tn, __, __ = templated.reverse(in_) assert tn.get().get() == 11 y = twrapped.Y() _, __, tn = y.passs(in_) assert tn.get().get() == 11 # renamed attribute templated.f_att = 2 assert templated.f_att == 2.0 templated.f_att = 4 assert templated.f_att == 4.0 t13 = twrapped.T(13) templated._x = t13 assert templated._x.get() == 13 t17 = twrapped.T(17) templated._x = t17 assert templated._x.get() == 17 templated.xi = [t13, t17] assert templated.xi[0].get() == 13 assert templated.xi[1].get() == 17 templated.xi = [t17, t13] assert templated.xi[0].get() == 17 assert templated.xi[1].get() == 13 # Test second template (it adds 1 to everything) t_o = twrapped.T2(42) templated_o = twrapped.Templated_other(t_o) assert templated_o.get().get() == 43 assert templated_o.passs(templated_o) == templated_o # Try out the adding 1 thing t11 = twrapped.T2(10) t12 = twrapped.T2(11) templated_o.xi = [t11, t12] assert templated_o.xi[0].get() == 11 assert templated_o.xi[1].get() == 12 # Test free functions assert templated.computeSeven() == 7 assert templated_o.computeSeven() == 7
def run(pxds, addons, converters, out, extra_inc_dirs=None, extra_opts=None): decls, instance_map = autowrap.parse(pxds, ".") return create_wrapper_code(decls, instance_map, addons, converters, out, extra_inc_dirs, extra_opts)
def test_full_lib(tmpdir): """ Example with multi-file library and multi-file result. This shows a full run through of a case where multiple class files (A, B, C, D) with multiple classes in them (Aklass, A_second, etc.) need to be wrapped, a total of 10 different entities over 8 header files (4 hpp and 4 pxd files). Autowrap will generate a .pxd and a .pyx file for each module. We decided to wrap the library into three modules, A, B and CD to show the capability of autowrap to do that. Note that we have perform multiple steps: - Step 1: parse all header files *together* - all pxd files need to be parsed together so that declarations are properly resolved. - Step 2: Map the different parsed entities to the pxd files and the desired modules, we use a master dict here that can be consumed by autowrap and specifies which pxd files and which declarations make up which module. - Step 3: Generate Cython code for each module - Step 4: Generate C++ code for each module (note that Step 3 has to be completed first before we can start to generate C++ code) - Step 5: Compile (run setup.py) Note that autowrap gives you full control how many modules you want to produce and which classes go into which modules. It automatically generates correct cimport statements in each so that dependencies between the modules are not an issue. """ curdir = os.getcwd() os.chdir(tmpdir.strpath) try: mnames = ["moduleA", "moduleB", "moduleCD"] # Step 1: parse all header files PY_NUM_THREADS = 1 pxd_files = ["A.pxd", "B.pxd", "C.pxd", "D.pxd"] full_pxd_files = [os.path.join(test_files, f) for f in pxd_files] decls, instance_map = autowrap.parse(full_pxd_files, ".", num_processes=int(PY_NUM_THREADS)) assert len(decls) == 13, len(decls) # Step 2: Perform mapping pxd_decl_mapping = {} for de in decls: tmp = pxd_decl_mapping.get(de.cpp_decl.pxd_path, []) tmp.append(de) pxd_decl_mapping[de.cpp_decl.pxd_path] = tmp masterDict = {} masterDict[mnames[0]] = { "decls": pxd_decl_mapping[full_pxd_files[0]], "addons": [], "files": [full_pxd_files[0]], } masterDict[mnames[1]] = { "decls": pxd_decl_mapping[full_pxd_files[1]], "addons": [], "files": [full_pxd_files[1]], } masterDict[mnames[2]] = { "decls": pxd_decl_mapping[full_pxd_files[2]] + pxd_decl_mapping[full_pxd_files[3]], "addons": [], "files": [full_pxd_files[2]] + [full_pxd_files[3]], } # Step 3: Generate Cython code converters = [] for modname in mnames: m_filename = "%s.pyx" % modname cimports, manual_code = autowrap.Main.collect_manual_code( masterDict[modname]["addons"]) autowrap.Main.register_converters(converters) autowrap_include_dirs = autowrap.generate_code( masterDict[modname]["decls"], instance_map, target=m_filename, debug=False, manual_code=manual_code, extra_cimports=cimports, include_boost=True, allDecl=masterDict, ) masterDict[modname]["inc_dirs"] = autowrap_include_dirs # Step 4: Generate CPP code for modname in mnames: m_filename = "%s.pyx" % modname autowrap_include_dirs = masterDict[modname]["inc_dirs"] autowrap.Main.run_cython(inc_dirs=autowrap_include_dirs, extra_opts=None, out=m_filename) # Step 5: Compile all_pyx_files = ["%s.pyx" % modname for modname in mnames] all_pxd_files = ["%s.pxd" % modname for modname in mnames] include_dirs = masterDict[modname]["inc_dirs"] moduleA, moduleB, moduleCD = compile_and_import( mnames, all_pyx_files, include_dirs, extra_files=all_pxd_files) finally: os.chdir(curdir) Aobj = moduleA.Aalias(5) Asecond = moduleA.A_second(8) assert Asecond.i_ == 8 assert Aobj.i_ == 5 assert Aobj.KlassE is not None assert Aobj.KlassE.A1 is not None assert Aobj.KlassE.A2 is not None assert Aobj.KlassE.A3 is not None Bobj = moduleB.Bklass(5) assert Bobj.i_ == 5 # access through A_second Bobj.callA2() assert Bobj.i_ == 6 # access through A_second Bsecond = moduleB.B_second(8) assert Bsecond.i_ == 8 Bsecond.processA(Aobj) assert Bsecond.i_ == 15 assert Bobj.KlassE is not None assert Bobj.KlassE.B1 is not None assert Bobj.KlassE.B2 is not None assert Bobj.KlassE.B3 is not None assert Bobj.KlassKlass is not None # there are two different ways to get Bklass::KlassKlass, either through a # Bklass object or through the module Bobj_kk = Bobj.KlassKlass() Bobj_kk.k_ = 14 assert Bobj_kk.k_ == 14 Bobj_kk = moduleB.Bklass.KlassKlass() Bobj_kk.k_ = 14 assert Bobj_kk.k_ == 14 # Check doc string assert "Inherits from" in moduleB.Bklass.__doc__ assert "some doc!" in moduleB.Bklass.__doc__ assert len(moduleB.Bklass.__doc__) == 92, len(moduleB.Bklass.__doc__) Bsecond = moduleB.B_second(8) Dsecond = moduleCD.D_second(11) assert Dsecond.i_ == 11 Dsecond.runB(Bsecond) assert Dsecond.i_ == 8
def test_full_lib(tmpdir): """ Example with multi-file library and multi-file result. This shows a full run through of a case where multiple class files (A, B, C, D) with multiple classes in them (Aklass, A_second, etc.) need to be wrapped, a total of 10 different entities over 8 header files (4 hpp and 4 pxd files). Autowrap will generate a .pxd and a .pyx file for each module. We decided to wrap the library into three modules, A, B and CD to show the capability of autowrap to do that. Note that we have perform multiple steps: - Step 1: parse all header files *together* - all pxd files need to be parsed together so that declarations are properly resolved. - Step 2: Map the different parsed entities to the pxd files and the desired modules, we use a master dict here that can be consumed by autowrap and specifies which pxd files and which declarations make up which module. - Step 3: Generate Cython code for each module - Step 4: Generate C++ code for each module (note that Step 3 has to be completed first before we can start to generate C++ code) - Step 5: Compile (run setup.py) Note that autowrap gives you full control how many modules you want to produce and which classes go into which modules. It automatically generates correct cimport statements in each so that dependencies between the modules are not an issue. """ os.chdir(tmpdir.strpath) mnames = ["moduleA", "moduleB", "moduleCD"] # Step 1: parse all header files PY_NUM_THREADS = 1 pxd_files = ["A.pxd", "B.pxd", "C.pxd", "D.pxd"] full_pxd_files = [os.path.join(test_files, f) for f in pxd_files] decls, instance_map = autowrap.parse( full_pxd_files, ".", num_processes=int(PY_NUM_THREADS) ) assert len(decls) == 13, len(decls) # Step 2: Perform mapping pxd_decl_mapping = {} for de in decls: tmp = pxd_decl_mapping.get(de.cpp_decl.pxd_path, []) tmp.append(de) pxd_decl_mapping[de.cpp_decl.pxd_path] = tmp masterDict = {} masterDict[mnames[0]] = { "decls": pxd_decl_mapping[full_pxd_files[0]], "addons": [], "files": [full_pxd_files[0]], } masterDict[mnames[1]] = { "decls": pxd_decl_mapping[full_pxd_files[1]], "addons": [], "files": [full_pxd_files[1]], } masterDict[mnames[2]] = { "decls": pxd_decl_mapping[full_pxd_files[2]] + pxd_decl_mapping[full_pxd_files[3]], "addons": [], "files": [full_pxd_files[2]] + [full_pxd_files[3]], } # Step 3: Generate Cython code converters = [] for modname in mnames: m_filename = "%s.pyx" % modname cimports, manual_code = autowrap.Main.collect_manual_code( masterDict[modname]["addons"] ) autowrap.Main.register_converters(converters) autowrap_include_dirs = autowrap.generate_code( masterDict[modname]["decls"], instance_map, target=m_filename, debug=False, manual_code=manual_code, extra_cimports=cimports, include_boost=True, allDecl=masterDict, ) masterDict[modname]["inc_dirs"] = autowrap_include_dirs # Step 4: Generate CPP code for modname in mnames: m_filename = "%s.pyx" % modname autowrap_include_dirs = masterDict[modname]["inc_dirs"] autowrap.Main.run_cython( inc_dirs=autowrap_include_dirs, extra_opts=None, out=m_filename ) # Step 5: Compile all_pyx_files = ["%s.pyx" % modname for modname in mnames] all_pxd_files = ["%s.pxd" % modname for modname in mnames] include_dirs = masterDict[modname]["inc_dirs"] moduleA, moduleB, moduleCD = compile_and_import( mnames, all_pyx_files, include_dirs, extra_files=all_pxd_files ) Aobj = moduleA.Aalias(5) Asecond = moduleA.A_second(8) assert Asecond.i_ == 8 assert Aobj.i_ == 5 assert Aobj.KlassE is not None assert Aobj.KlassE.A1 is not None assert Aobj.KlassE.A2 is not None assert Aobj.KlassE.A3 is not None Bobj = moduleB.Bklass(5) Bsecond = moduleB.B_second(8) assert Bsecond.i_ == 8 Bsecond.processA(Aobj) assert Bsecond.i_ == 15 assert Bobj.KlassE is not None assert Bobj.KlassE.B1 is not None assert Bobj.KlassE.B2 is not None assert Bobj.KlassE.B3 is not None assert Bobj.KlassKlass is not None # there are two different ways to get Bklass::KlassKlass, either through a # Bklass object or through the module Bobj_kk = Bobj.KlassKlass() Bobj_kk.k_ = 14 assert Bobj_kk.k_ == 14 Bobj_kk = moduleB.Bklass.KlassKlass() Bobj_kk.k_ = 14 assert Bobj_kk.k_ == 14 Bsecond = moduleB.B_second(8) Dsecond = moduleCD.D_second(11) assert Dsecond.i_ == 11 Dsecond.runB(Bsecond) assert Dsecond.i_ == 8
addons = glob.glob(PYOPENMS_SRC_DIR + "/addons/*.pyx") converters = [j(PYOPENMS_SRC_DIR, "converters")] persisted_data_path = "include_dir.bin" extra_cimports = [] # We need to parse them all together but keep the association about which class # we found in which file (as they often need to be analyzed together) # TODO think about having a separate NUM_THREADS argument for parsing/cythonizing, since it is less # memory intensive than the actualy compilation into a module (done in setup.py). # Hide annoying redeclaration errors from unscoped enums by using warning level 2. # This might lead to a minimal amount of unseen errors, but with all the mess, we would not have spotted them anyway. # This can be removed as soon as autowrap supports Cython 3 (intodruced scoped enum support) and OpenMS scopes all enums (e.g. with enum class). decls, instance_map = autowrap.parse(pxd_files, ".", num_processes=int(PY_NUM_THREADS), cython_warn_level=2) # Perform mapping pxd_decl_mapping = {} for de in decls: tmp = pxd_decl_mapping.get(de.cpp_decl.pxd_path, []) tmp.append(de) pxd_decl_mapping[de.cpp_decl.pxd_path] = tmp # add __str__ if toString() method is declared: for d in decls: # enums, free functions, .. do not have a methods attribute methods = getattr(d, "methods", dict()) to_strings = [] for name, mdecls in methods.items():