diff --git a/cppimport/__init__.py b/cppimport/__init__.py index 1f76001..53d9e03 100644 --- a/cppimport/__init__.py +++ b/cppimport/__init__.py @@ -1,5 +1,10 @@ -from cppimport.config import set_quiet, force_rebuild, file_exts,\ - turn_off_strict_prototypes, set_rtld_flags +from cppimport.config import ( + set_quiet, + force_rebuild, + file_exts, + turn_off_strict_prototypes, + set_rtld_flags, +) from cppimport.importer import imp, imp_from_filepath, build from cppimport.templating import setup_pybind11 from cppimport.importer import imp as cppimport diff --git a/cppimport/build_module.py b/cppimport/build_module.py index c31dcc7..87ddcf7 100644 --- a/cppimport/build_module.py +++ b/cppimport/build_module.py @@ -16,6 +16,7 @@ else: import io + @contextlib.contextmanager def stdchannel_redirected(stdchannel): """ @@ -30,6 +31,7 @@ def stdchannel_redirected(stdchannel): finally: setattr(sys, stdchannel, old) + # Subclass setuptools Extension to add a parameter specifying where the shared # library should be placed after being compiled class ImportCppExt(setuptools.Extension): @@ -37,6 +39,7 @@ def __init__(self, libdest, *args, **kwargs): self.libdest = libdest setuptools.Extension.__init__(self, *args, **kwargs) + # Subclass setuptools build_ext to put the compiled shared library in the # appropriate place in the source tree. class BuildImportCppExt(setuptools.command.build_ext.build_ext): @@ -48,15 +51,23 @@ def copy_extensions_to_source(self): dest_filename = os.path.join(ext.libdest, os.path.basename(filename)) distutils.file_util.copy_file( - src_filename, dest_filename, - verbose = self.verbose, dry_run = self.dry_run + src_filename, dest_filename, verbose=self.verbose, dry_run=self.dry_run ) + # Patch for parallel compilation with distutils -# From: http://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils -def parallel_compile(self, sources, output_dir = None, macros = None, - include_dirs = None, debug = 0, extra_preargs = None, extra_postargs = None, - depends = None): +# From: http://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils # noqa: E501 +def parallel_compile( + self, + sources, + output_dir=None, + macros=None, + include_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + depends=None, +): # these lines are copied directly from distutils.ccompiler.CCompiler macros, objects, extra_postargs, pp_opts, build = self._setup_compile( @@ -70,6 +81,7 @@ def parallel_compile(self, sources, output_dir = None, macros = None, try: import multiprocessing import multiprocessing.pool + N = multiprocessing.cpu_count() except (ImportError, NotImplementedError): pass @@ -86,68 +98,66 @@ def _single_compile(obj): # print("took " + str(end - start) + " to compile " + str(obj)) # imap is evaluated on demand, converting to list() forces execution - list(multiprocessing.pool.ThreadPool(N).imap(_single_compile,objects)) + list(multiprocessing.pool.ThreadPool(N).imap(_single_compile, objects)) return objects + def build_module(module_data): build_path = tempfile.mkdtemp() - full_module_name = module_data['fullname'] - filepath = module_data['filepath'] - cfg = module_data['cfg'] + full_module_name = module_data["fullname"] + filepath = module_data["filepath"] + cfg = module_data["cfg"] - module_data['abs_include_dirs'] = [ - make_absolute(module_data['filedirname'], d) - for d in cfg.get('include_dirs', []) + module_data["abs_include_dirs"] = [ + make_absolute(module_data["filedirname"], d) + for d in cfg.get("include_dirs", []) ] + [os.path.dirname(filepath)] - module_data['abs_library_dirs'] = [ - make_absolute(module_data['filedirname'], d) - for d in cfg.get('library_dirs', []) + module_data["abs_library_dirs"] = [ + make_absolute(module_data["filedirname"], d) + for d in cfg.get("library_dirs", []) ] - module_data['dependency_dirs'] = ( - module_data['abs_include_dirs'] + [module_data['filedirname']] - ) - module_data['extra_source_filepaths'] = [ - make_absolute(module_data['filedirname'], s) - for s in cfg.get('sources', []) + module_data["dependency_dirs"] = module_data["abs_include_dirs"] + [ + module_data["filedirname"] + ] + module_data["extra_source_filepaths"] = [ + make_absolute(module_data["filedirname"], s) for s in cfg.get("sources", []) ] ext = ImportCppExt( os.path.dirname(filepath), full_module_name, - language = 'c++', - sources = ( - module_data['extra_source_filepaths'] + - [module_data['rendered_src_filepath']] + language="c++", + sources=( + module_data["extra_source_filepaths"] + + [module_data["rendered_src_filepath"]] ), - include_dirs = module_data['abs_include_dirs'], - extra_compile_args = cfg.get('extra_compile_args', []), - extra_link_args = cfg.get('extra_link_args', []), - library_dirs = module_data['abs_library_dirs'], - libraries = cfg.get('libraries', []) + include_dirs=module_data["abs_include_dirs"], + extra_compile_args=cfg.get("extra_compile_args", []), + extra_link_args=cfg.get("extra_link_args", []), + library_dirs=module_data["abs_library_dirs"], + libraries=cfg.get("libraries", []), ) - args = ['build_ext', '--inplace'] - args.append('--build-temp=' + build_path) - args.append('--build-lib=' + build_path) + args = ["build_ext", "--inplace"] + args.append("--build-temp=" + build_path) + args.append("--build-lib=" + build_path) if cppimport.config.quiet: - args.append('-q') + args.append("-q") else: - args.append('-v') + args.append("-v") setuptools_args = dict( - name = full_module_name, - ext_modules = [ext], - script_args = args, - cmdclass = { - 'build_ext': BuildImportCppExt - } + name=full_module_name, + ext_modules=[ext], + script_args=args, + cmdclass={"build_ext": BuildImportCppExt}, ) # Monkey patch in the parallel compiler if requested. py33orgreater = sys.version_info[0] >= 3 and sys.version_info[1] >= 3 - parallelize = cfg.get('parallel') and py33orgreater + parallelize = cfg.get("parallel") and py33orgreater if parallelize: old_compile = distutils.ccompiler.CCompiler.compile distutils.ccompiler.CCompiler.compile = parallel_compile diff --git a/cppimport/checksum.py b/cppimport/checksum.py index ba971c6..c0789a9 100644 --- a/cppimport/checksum.py +++ b/cppimport/checksum.py @@ -7,25 +7,28 @@ from cppimport.filepaths import make_absolute -_TAG = b'cppimport' -_FMT = struct.Struct('q' + str(len(_TAG)) + 's') +_TAG = b"cppimport" +_FMT = struct.Struct("q" + str(len(_TAG)) + "s") + def calc_cur_checksum(file_lst, module_data): text = b"" for filepath in file_lst: try: - with open(filepath, 'rb') as f: + with open(filepath, "rb") as f: text += f.read() except OSError as e: cppimport.config.quiet_print( "Checksummed file not found while checking cppimport checksum " - "(%s); rebuilding." % e) + "(%s); rebuilding." % e + ) return None return hashlib.md5(text).hexdigest() + def load_checksum_trailer(module_data): try: - with open(module_data['ext_path'], 'rb') as f: + with open(module_data["ext_path"], "rb") as f: f.seek(-_FMT.size, 2) json_len, tag = _FMT.unpack(f.read(_FMT.size)) if tag != _TAG: @@ -42,10 +45,12 @@ def load_checksum_trailer(module_data): except ValueError: cppimport.config.quiet_print( "Failed to load checksum trailer info from already existing " - "compiled extension; rebuilding.") + "compiled extension; rebuilding." + ) return None, None return deps, old_checksum + # Use a checksum to see if the file has been changed since the last compilation def is_checksum_current(module_data): deps, old_checksum = load_checksum_trailer(module_data) @@ -53,22 +58,24 @@ def is_checksum_current(module_data): return False # Already logged error in load_checksum_trailer. return old_checksum == calc_cur_checksum(deps, module_data) + def save_checksum_trailer(module_data, dep_filepaths, cur_checksum): # We can just append the checksum to the shared object; this is effectively # legal (see e.g. https://stackoverflow.com/questions/10106447). - dump = json.dumps([dep_filepaths, cur_checksum]).encode('ascii') + dump = json.dumps([dep_filepaths, cur_checksum]).encode("ascii") dump += _FMT.pack(len(dump), _TAG) - with open(module_data['ext_path'], 'ab') as file: + with open(module_data["ext_path"], "ab") as file: file.write(dump) + def checksum_save(module_data): dep_filepaths = ( [ - make_absolute(module_data['filedirname'], d) - for d in module_data['cfg'].get('dependencies', []) - ] + - module_data['extra_source_filepaths'] + - [module_data['filepath']] + make_absolute(module_data["filedirname"], d) + for d in module_data["cfg"].get("dependencies", []) + ] + + module_data["extra_source_filepaths"] + + [module_data["filepath"]] ) cur_checksum = calc_cur_checksum(dep_filepaths, module_data) save_checksum_trailer(module_data, dep_filepaths, cur_checksum) diff --git a/cppimport/config.py b/cppimport/config.py index 5e7e9e9..9fb28ac 100644 --- a/cppimport/config.py +++ b/cppimport/config.py @@ -2,29 +2,35 @@ quiet = True should_force_rebuild = False -file_exts = ['.cpp', '.c'] +file_exts = [".cpp", ".c"] rtld_flags = ctypes.RTLD_LOCAL + def set_quiet(to): global quiet quiet = to -def force_rebuild(to = True): + +def force_rebuild(to=True): global should_force_rebuild should_force_rebuild = to + def quiet_print(a): global quiet if not quiet: print(a) + def turn_off_strict_prototypes(): import distutils.sysconfig + cfg_vars = distutils.sysconfig.get_config_vars() for key, value in cfg_vars.items(): if type(value) == str: cfg_vars[key] = value.replace("-Wstrict-prototypes", "") + def set_rtld_flags(flags): global rtld_flags rtld_flags = flags diff --git a/cppimport/cpprun.py b/cppimport/cpprun.py index 01809c5..a8357ba 100644 --- a/cppimport/cpprun.py +++ b/cppimport/cpprun.py @@ -1,4 +1,3 @@ -import time import os import sys import argparse @@ -21,17 +20,16 @@ def cpp_run(): - parser = argparse.ArgumentParser(description='Run a C++ file with cppimport') - parser.add_argument('filename', help = 'The file to run.') + parser = argparse.ArgumentParser(description="Run a C++ file with cppimport") + parser.add_argument("filename", help="The file to run.") parser.add_argument( - '--add_main_caller', '-m', - action = 'store_true', - help = 'Add a pybind11 function that will call your main()' + "--add_main_caller", + "-m", + action="store_true", + help="Add a pybind11 function that will call your main()", ) parser.add_argument( - '--verbose', '-v', - action = 'store_true', - help = 'Tell me everything!' + "--verbose", "-v", action="store_true", help="Tell me everything!" ) args = parser.parse_args() @@ -44,11 +42,11 @@ def cpp_run(): module_name, file_extension = os.path.splitext(filebasename) if args.add_main_caller: - cpprun_dir = '.cpprunfiles' + cpprun_dir = ".cpprunfiles" if not os.path.exists(cpprun_dir): os.makedirs(cpprun_dir) src = os.path.join(cpprun_dir, filebasename) - open(src, 'w').write(open(filename, 'r').read() + footer) + open(src, "w").write(open(filename, "r").read() + footer) sys.path.append(cpprun_dir) else: sys.path.append(filedir) @@ -60,5 +58,5 @@ def cpp_run(): module.main() -if __name__ == '__main__': +if __name__ == "__main__": cpp_run() diff --git a/cppimport/filepaths.py b/cppimport/filepaths.py index 2156976..e4b4823 100644 --- a/cppimport/filepaths.py +++ b/cppimport/filepaths.py @@ -1,5 +1,6 @@ import os + def make_absolute(this_dir, s): if os.path.isabs(s): return s diff --git a/cppimport/find.py b/cppimport/find.py index 01977d6..863b930 100644 --- a/cppimport/find.py +++ b/cppimport/find.py @@ -3,9 +3,11 @@ import cppimport.config + def check_contains_cppimport(filepath): - with open(filepath, 'r') as f: - return 'cppimport' in f.readline() + with open(filepath, "r") as f: + return "cppimport" in f.readline() + def find_file_in_folders(filename, paths, opt_in): for d in paths: @@ -24,8 +26,9 @@ def find_file_in_folders(filename, paths, opt_in): return filepath return None + def find_matching_path_dirs(moduledir): - if moduledir is '': + if moduledir == "": return sys.path ds = [] @@ -35,11 +38,12 @@ def find_matching_path_dirs(moduledir): ds.append(test_path) return ds -def _find_module_cpppath(modulename, opt_in = False): - modulepath_without_ext = modulename.replace('.', os.sep) - moduledir = os.path.dirname(modulepath_without_ext + '.throwaway') + +def _find_module_cpppath(modulename, opt_in=False): + modulepath_without_ext = modulename.replace(".", os.sep) + moduledir = os.path.dirname(modulepath_without_ext + ".throwaway") matching_dirs = find_matching_path_dirs(moduledir) - matching_dirs = [os.getcwd() if d == '' else d for d in matching_dirs] + matching_dirs = [os.getcwd() if d == "" else d for d in matching_dirs] matching_dirs = [ d if os.path.isabs(d) else os.path.join(os.getcwd(), d) for d in matching_dirs ] @@ -52,12 +56,15 @@ def _find_module_cpppath(modulename, opt_in = False): return None -def find_module_cpppath(modulename, opt_in = False): + +def find_module_cpppath(modulename, opt_in=False): filepath = _find_module_cpppath(modulename, opt_in) if filepath is None: raise ImportError( - 'Couldn\'t find a file matching the module name: ' + - str(modulename) + - ' (note: opt_in = ' + str(opt_in) + ')' + "Couldn't find a file matching the module name: " + + str(modulename) + + " (note: opt_in = " + + str(opt_in) + + ")" ) return filepath diff --git a/cppimport/import_hook.py b/cppimport/import_hook.py index 518ffb1..4d5c8ad 100644 --- a/cppimport/import_hook.py +++ b/cppimport/import_hook.py @@ -1,27 +1,29 @@ import sys +import traceback import cppimport.importer + class Hook(object): def __init__(self): self._running = False self.print_exceptions = False - def find_module(self, fullname, path = None): + def find_module(self, fullname, path=None): # Prevent re-entry by the underlying importer if self._running: return try: self._running = True - cppimport.importer.imp(fullname, opt_in = True) - except ImportError as e: + cppimport.importer.imp(fullname, opt_in=True) + except ImportError: # ImportError should be quashed because that simply means cppimport # didn't find anything, and probably shouldn't have found anything! if self.print_exceptions: - import traceback print(traceback.format_exc()) finally: self._running = False + hook_obj = Hook() sys.meta_path.insert(0, hook_obj) diff --git a/cppimport/importer.py b/cppimport/importer.py index 211df0a..c56dfc3 100644 --- a/cppimport/importer.py +++ b/cppimport/importer.py @@ -9,32 +9,37 @@ quiet_print = cppimport.config.quiet_print + def get_module_name(full_module_name): - return full_module_name.split('.')[-1] + return full_module_name.split(".")[-1] + def get_extension_suffix(): - ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') + ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") if ext_suffix is None: - ext_suffix = sysconfig.get_config_var('SO') + ext_suffix = sysconfig.get_config_var("SO") return ext_suffix + def setup_module_data(fullname, filepath): module_data = dict() - module_data['fullname'] = fullname - module_data['filepath'] = filepath - module_data['filedirname'] = os.path.dirname(module_data['filepath']) - module_data['filebasename'] = os.path.basename(module_data['filepath']) - module_data['ext_name'] = get_module_name(fullname) + get_extension_suffix() - module_data['ext_path'] = os.path.join( - os.path.dirname(filepath), module_data['ext_name'] + module_data["fullname"] = fullname + module_data["filepath"] = filepath + module_data["filedirname"] = os.path.dirname(module_data["filepath"]) + module_data["filebasename"] = os.path.basename(module_data["filepath"]) + module_data["ext_name"] = get_module_name(fullname) + get_extension_suffix() + module_data["ext_path"] = os.path.join( + os.path.dirname(filepath), module_data["ext_name"] ) return module_data + def _load_module(module_data): - module_data['module'] = importlib.import_module(module_data['fullname']) + module_data["module"] = importlib.import_module(module_data["fullname"]) + def load_module(module_data): - if hasattr(sys, 'getdlopenflags'): + if hasattr(sys, "getdlopenflags"): old_flags = sys.getdlopenflags() new_flags = old_flags | cppimport.config.rtld_flags sys.setdlopenflags(new_flags) @@ -43,27 +48,34 @@ def load_module(module_data): else: _load_module(module_data) + def check_checksum(module_data): if cppimport.config.should_force_rebuild: return False if not cppimport.checksum.is_checksum_current(module_data): return False - quiet_print("Matching checksum for " + module_data['filepath'] + " --> not compiling") + quiet_print( + "Matching checksum for " + module_data["filepath"] + " --> not compiling" + ) return True + def try_load(module_data): try: load_module(module_data) return True except ImportError: quiet_print( - "ImportError during import with matching checksum. Trying to rebuild.") + "ImportError during import with matching checksum. Trying to rebuild." + ) return False + def template_and_build(filepath, module_data): # Don't import until here to reduce startup time. import cppimport.templating as templating import cppimport.build_module as build_module + quiet_print("Compiling " + filepath) templating.run_templating(module_data) build_module.build_module(module_data) @@ -71,20 +83,23 @@ def template_and_build(filepath, module_data): os.remove(module_data["rendered_src_filepath"]) cppimport.checksum.checksum_save(module_data) -def imp_from_filepath(filepath, fullname = None): + +def imp_from_filepath(filepath, fullname=None): if fullname is None: fullname = os.path.splitext(os.path.basename(filepath))[0] module_data = setup_module_data(fullname, filepath) if not check_checksum(module_data) or not try_load(module_data): template_and_build(filepath, module_data) load_module(module_data) - return module_data['module'] + return module_data["module"] + -def imp(fullname, opt_in = False): +def imp(fullname, opt_in=False): # Search through sys.path to find a file that matches the module filepath = cppimport.find.find_module_cpppath(fullname, opt_in) return imp_from_filepath(filepath, fullname) + def build(fullname): # Search through sys.path to find a file that matches the module filepath = cppimport.find.find_module_cpppath(fullname) @@ -93,4 +108,4 @@ def build(fullname): template_and_build(filepath, module_data) # Return the path to the built module - return module_data['ext_path'] + return module_data["ext_path"] diff --git a/cppimport/templating.py b/cppimport/templating.py index 4d89b1a..6357e17 100644 --- a/cppimport/templating.py +++ b/cppimport/templating.py @@ -1,15 +1,17 @@ import os import sys + def get_rendered_source_filepath(filepath): dirname = os.path.dirname(filepath) filename = os.path.basename(filepath) - return os.path.join(dirname, '.rendered.' + filename) + return os.path.join(dirname, ".rendered." + filename) + class BuildArgs(dict): _key_mapping = { - 'compiler_args': 'extra_compile_args', - 'linker_args': 'extra_link_args', + "compiler_args": "extra_compile_args", + "linker_args": "extra_link_args", } def __getitem__(self, key): @@ -18,11 +20,15 @@ def __getitem__(self, key): def __setitem__(self, key, value): super(BuildArgs, self).__setitem__(self._key_mapping.get(key, key), value) + def setup_pybind11(cfg): import pybind11 - cfg['include_dirs'] += [pybind11.get_include(), pybind11.get_include(True)] - # Prefix with c++11 arg instead of suffix so that if a user specifies c++14 (or later!) then it won't be overridden. - cfg['compiler_args'] = ['-std=c++11', '-fvisibility=hidden'] + cfg['compiler_args'] + + cfg["include_dirs"] += [pybind11.get_include(), pybind11.get_include(True)] + # Prefix with c++11 arg instead of suffix so that if a user specifies c++14 + # (or later!) then it won't be overridden. + cfg["compiler_args"] = ["-std=c++11", "-fvisibility=hidden"] + cfg["compiler_args"] + def run_templating(module_data): import mako.template @@ -35,32 +41,32 @@ def run_templating(module_data): else: import io - module_data['cfg'] = BuildArgs( - sources = [], - include_dirs = [], - extra_compile_args = [], - libraries = [], - library_dirs = [], - extra_link_args = [], - dependencies = [], - parallel = False, + module_data["cfg"] = BuildArgs( + sources=[], + include_dirs=[], + extra_compile_args=[], + libraries=[], + library_dirs=[], + extra_link_args=[], + dependencies=[], + parallel=False, ) - module_data['setup_pybind11'] = setup_pybind11 + module_data["setup_pybind11"] = setup_pybind11 buf = io.StringIO() ctx = mako.runtime.Context(buf, **module_data) - filepath = module_data['filepath'] + filepath = module_data["filepath"] lookup = mako.lookup.TemplateLookup(directories=[os.path.dirname(filepath)]) - tmpl = mako.template.Template(filename = filepath, lookup = lookup) + tmpl = mako.template.Template(filename=filepath, lookup=lookup) try: - rendered_src = tmpl.render_context(ctx) - except: + tmpl.render_context(ctx) + except Exception: print(mako.exceptions.text_error_template().render()) rendered_src_filepath = get_rendered_source_filepath(filepath) - with open(rendered_src_filepath, 'w', newline = '') as f: + with open(rendered_src_filepath, "w", newline="") as f: f.write(buf.getvalue()) - module_data['rendered_src_filepath'] = rendered_src_filepath + module_data["rendered_src_filepath"] = rendered_src_filepath diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..7a7870a --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 88 +extend-ignore = E203 +per-file-ignores= + cppimport/__init__.py:F401 diff --git a/setup.py b/setup.py index f9ceae4..e67b320 100644 --- a/setup.py +++ b/setup.py @@ -1,37 +1,32 @@ from setuptools import setup -version = open('VERSION').read() +version = open("VERSION").read() -description = open('README.md').read() +description = open("README.md").read() setup( - packages = ['cppimport'], - - install_requires = [ - 'mako', 'pybind11' + packages=["cppimport"], + install_requires=["mako", "pybind11"], + zip_safe=False, + name="cppimport", + version=version, + description="Import C++ files directly from Python!", + long_description=description, + long_description_content_type="text/markdown", + url="https://github.com/tbenthompson/cppimport", + author="T. Ben Thompson", + author_email="t.ben.thompson@gmail.com", + license="MIT", + platforms=["any"], + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Topic :: Software Development", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + "Programming Language :: C++", ], - zip_safe = False, - - name = 'cppimport', - version = version, - description = 'Import C++ files directly from Python!', - long_description = description, - long_description_content_type = 'text/markdown', - - url = 'https://github.com/tbenthompson/cppimport', - author = 'T. Ben Thompson', - author_email = 't.ben.thompson@gmail.com', - license = 'MIT', - platforms = ['any'], - classifiers = [ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Operating System :: OS Independent', - 'Operating System :: POSIX', - 'Topic :: Software Development', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 3', - 'Programming Language :: C++' - ] ) diff --git a/tests/test_cppimport.py b/tests/test_cppimport.py index f69878a..417b421 100644 --- a/tests/test_cppimport.py +++ b/tests/test_cppimport.py @@ -9,80 +9,91 @@ import cppimport.build_module import cppimport.templating import cppimport.import_hook + cppimport.set_quiet(False) + @contextlib.contextmanager def appended(filename, text): - orig = open(filename, 'r').read() - open(filename, 'a').write(text) + orig = open(filename, "r").read() + open(filename, "a").write(text) try: yield finally: - open(filename, 'w').write(orig) + open(filename, "w").write(orig) -def subprocess_check(test_code, returncode = 0): - p = subprocess.Popen([ - 'python', '-c', test_code - ], cwd = os.path.dirname(__file__)) + +def subprocess_check(test_code, returncode=0): + p = subprocess.Popen(["python", "-c", test_code], cwd=os.path.dirname(__file__)) p.wait() - assert(p.returncode == returncode) + assert p.returncode == returncode + def test_redirected_stream(): sys.stderr = io.StringIO() with cppimport.build_module.stdchannel_redirected("stdout") as s: with cppimport.build_module.stdchannel_redirected("stderr"): print("EEEP!") - assert(s.getvalue() == 'EEEP!\n') + assert s.getvalue() == "EEEP!\n" + def test_find_module_cpppath(): mymodule_loc = cppimport.find.find_module_cpppath("mymodule") mymodule_dir = os.path.dirname(mymodule_loc) - assert(os.path.basename(mymodule_loc) == "mymodule.cpp") + assert os.path.basename(mymodule_loc) == "mymodule.cpp" apackage = cppimport.find.find_module_cpppath("apackage.mymodule") - apackage_correct = os.path.join(mymodule_dir, 'apackage', 'mymodule.cpp') - assert(apackage == apackage_correct) + apackage_correct = os.path.join(mymodule_dir, "apackage", "mymodule.cpp") + assert apackage == apackage_correct inner = cppimport.find.find_module_cpppath("apackage.inner.mymodule") - inner_correct = os.path.join(mymodule_dir, 'apackage', 'inner', 'mymodule.cpp') - assert(inner == inner_correct) + inner_correct = os.path.join(mymodule_dir, "apackage", "inner", "mymodule.cpp") + assert inner == inner_correct + def test_get_rendered_source_filepath(): - rendered_path = cppimport.templating.get_rendered_source_filepath('abc.cpp') - assert(rendered_path == '.rendered.abc.cpp') + rendered_path = cppimport.templating.get_rendered_source_filepath("abc.cpp") + assert rendered_path == ".rendered.abc.cpp" -def module_tester(mod, cheer = False): - assert(mod.add(1,2) == 3) + +def module_tester(mod, cheer=False): + assert mod.add(1, 2) == 3 if cheer: mod.Thing().cheer() + def test_mymodule(): mymodule = cppimport.imp("mymodule") module_tester(mymodule) + def test_package_mymodule(): mymodule = cppimport.imp("apackage.mymodule") module_tester(mymodule) + def test_inner_package_mymodule(): mymodule = cppimport.imp("apackage.inner.mymodule") module_tester(mymodule) + def test_with_file_in_syspath(): orig_sys_path = copy.copy(sys.path) - sys.path.append(os.path.join(os.path.dirname(__file__), 'mymodule.cpp')) - mymodule = cppimport.imp("mymodule") + sys.path.append(os.path.join(os.path.dirname(__file__), "mymodule.cpp")) + cppimport.imp("mymodule") sys.path = orig_sys_path + def test_rebuild_after_failed_compile(): - mymodule = cppimport.imp("mymodule") - test_code = ''' + cppimport.imp("mymodule") + test_code = """ import cppimport; mymodule = cppimport.imp("mymodule");assert(mymodule.add(1,2) == 3) -''' - with appended('tests/mymodule.cpp', ";asdf;"): +""" + with appended("tests/mymodule.cpp", ";asdf;"): subprocess_check(test_code, 1) subprocess_check(test_code, 0) + add_to_thing = """ #include struct Thing { @@ -93,31 +104,39 @@ def test_rebuild_after_failed_compile(): #define THING_DEFINED """ + def test_no_rebuild_if_no_deps_change(): - mymodule = cppimport.imp("mymodule") - test_code = ''' + cppimport.imp("mymodule") + test_code = """ import cppimport; mymodule = cppimport.imp("mymodule"); assert(not hasattr(mymodule, 'Thing')) -''' - with appended('tests/thing2.h', add_to_thing): +""" + with appended("tests/thing2.h", add_to_thing): subprocess_check(test_code) + def test_rebuild_header_after_change(): - mymodule = cppimport.imp("mymodule") - test_code = ''' -import cppimport; cppimport.set_quiet(False); mymodule = cppimport.imp("mymodule"); mymodule.Thing().cheer() -''' - with appended('tests/thing.h', add_to_thing): + cppimport.imp("mymodule") + test_code = """ +import cppimport +cppimport.set_quiet(False) +mymodule = cppimport.imp("mymodule") +mymodule.Thing().cheer() +""" + with appended("tests/thing.h", add_to_thing): subprocess_check(test_code) + def test_raw_extensions(): raw_extension = cppimport.imp("raw_extension") - assert(raw_extension.add(1,2) == 3) + assert raw_extension.add(1, 2) == 3 + def test_extra_sources(): mod = cppimport.imp("extra_sources") - assert(mod.square_sum(3, 4) == 25) + assert mod.square_sum(3, 4) == 25 + # TODO: cpprun is incomplete and possibly not a good idea... # def test_cpprun(): @@ -127,9 +146,12 @@ def test_extra_sources(): # p.wait() # assert(b'HI!\n' == p.stdout.read()) + def test_import_hook(): # Force rebuild to make sure we're not just reloading the already compiled # module from disk cppimport.force_rebuild(True) import hook_test + cppimport.force_rebuild(False) + hook_test diff --git a/tests/thing.h b/tests/thing.h index e69de29..8352174 100644 --- a/tests/thing.h +++ b/tests/thing.h @@ -0,0 +1,8 @@ + +#include +struct Thing { + void cheer() { + std::cout << "WAHHOOOO" << std::endl; + } +}; +#define THING_DEFINED