diff --git a/maturin/import_hook.py b/maturin/import_hook.py new file mode 100644 index 000000000..102ed4145 --- /dev/null +++ b/maturin/import_hook.py @@ -0,0 +1,79 @@ +import importlib +import importlib.util +from importlib import abc +import os +import pathlib +import sys +import subprocess + +import toml + + +class Importer(abc.MetaPathFinder): + """A meta-path importer for the maturin based packages""" + + def find_spec(self, fullname, path, target=None): + if fullname in sys.modules: + return + mod_parts = fullname.split(".") + module_name = mod_parts[-1] + cargo_toml = pathlib.Path(os.getcwd()) / "Cargo.toml" + if os.path.exists(cargo_toml): + with open(cargo_toml) as f: + cargo = toml.load(f) + package_name = cargo.get("package", {}).get("name") + if ( + package_name == module_name + or package_name.replace("-", "_") == module_name + ): + build_module(cargo_toml) + loader = Loader(fullname) + return importlib.util.spec_from_loader(fullname, loader) + + +class Loader(abc.Loader): + def __init__(self, fullname): + self.fullname = fullname + + def load_module(self, fullname): + return importlib.import_module(self.fullname) + + +def build_module(manifest_path): + command = ["maturin", "develop", "-m", manifest_path] + result = subprocess.run(command, stdout=subprocess.PIPE) + sys.stdout.buffer.write(result.stdout) + sys.stdout.flush() + if result.returncode != 0: + sys.stderr.write( + f"Error: command {command} returned non-zero exit status {result.returncode}\n" + ) + raise ImportError("Failed to build module with maturin") + + +def _have_importer(): + for importer in sys.meta_path: + if isinstance(importer, Importer): + return True + return False + + +def install(): + """ + Install the import hook. + """ + if _have_importer(): + return + importer = Importer() + sys.meta_path.append(importer) + return importer + + +def uninstall(importer): + """ + Uninstall the import hook. + """ + try: + sys.meta_path.remove(importer) + except ValueError: + pass