diff --git a/rustimport/__init__.py b/rustimport/__init__.py index 1185be0..f9d718a 100644 --- a/rustimport/__init__.py +++ b/rustimport/__init__.py @@ -31,8 +31,8 @@ For more information check the Readme on GitHub: https://github.com/mityax/rustimport """ -from shutil import which import logging as _logging +from shutil import which from types import ModuleType from rustimport import settings @@ -43,19 +43,15 @@ def imp(fullname, opt_in: bool = False, force_rebuild: bool = settings.force_rebuild) -> ModuleType: """ - `imp` is the explicit alternative to using rustimport.import_hook. - - Parameters - ---------- - fullname : the name of the module to import. - opt_in : should we require rust files to opt in via adding "rustimport" to - the first line of the file? This is on by default for the - import hook, but is off by default for this function since the - intent to import a rust module is clearly specified. - - Returns - ------- - module : the compiled and loaded Python extension module + Explicit alternative to using rustimport.import_hook. + + @param fullname: The name of the module to import. + @param opt_in: Whether to require rust sources to opt in via adding "rustimport" to the first line of the file/crate. + Default is False since the intent to import a Rust module is explicitly stated. + @param force_rebuild: Whether to force re-compilation of the extension, even if it hasn't changed. Default is + derived from settings. + + @return: The compiled and loaded Python extension module. """ from rustimport.load import dlopen_flags @@ -77,18 +73,16 @@ def imp(fullname, opt_in: bool = False, force_rebuild: bool = settings.force_reb def imp_from_path(path, fullname=None, opt_in: bool = False, force_rebuild: bool = settings.force_rebuild) -> ModuleType: """ - `imp_from_path` serves the same purpose as `imp` except allows - specifying the exact path of the rust file or crate. - - Parameters - ---------- - filepath : the filepath to the C++ file to build and import. - fullname : the name of the module to import. This can be different from the - module name inferred from the filepath if desired. - - Returns - ------- - module : the compiled and loaded Python extension module + Imports a Rust module from a specified file path. + + @param path: The path to the Rust file or crate. + @param fullname: The name of the module to import. Defaults to inferring from the file/crate path if not specified. + @param opt_in: Whether to require rust sources to opt in via adding "rustimport" to the first line of the file/crate. + Default is False since the intent to import a Rust module is explicitly stated. + @param force_rebuild: Whether to force re-compilation of the extension, even if it hasn't changed. Default is + derived from settings. + + @return: The compiled and loaded Python extension module. """ from rustimport.load import dlopen_flags @@ -110,16 +104,16 @@ def imp_from_path(path, fullname=None, opt_in: bool = False, force_rebuild: bool def build(fullname, opt_in: bool = False, force_rebuild: bool = settings.force_rebuild, release: bool = settings.compile_release_binaries): """ - `build` builds a extension module like `imp` but does not import the - extension. + Builds a rust extension without importing it. - Parameters - ---------- - fullname : the name of the module to import. + @param fullname: The name of the rust file/crate to build. + @param opt_in: Whether to require rust sources to opt in via adding "rustimport" to the first line of the file/crate. + Default is False since the intent to import a Rust module is explicitly stated. + @param force_rebuild: Whether to force re-compilation of the extension, even if it hasn't changed. Default is + derived from settings. + @param release: Whether to build a release binary. Default is derived from settings. - Returns - ------- - ext_path : the path to the compiled extension. + @return: An [Importable] instance for the given extension. """ from rustimport.find import find_module_importable from rustimport.importable import should_rebuild @@ -127,23 +121,23 @@ def build(fullname, opt_in: bool = False, force_rebuild: bool = settings.force_r importable = find_module_importable(fullname, opt_in=opt_in) if should_rebuild(importable, force_rebuild=force_rebuild, force_release=release): importable.build(release=release) + return importable def build_filepath(path, opt_in: bool = False, force_rebuild: bool = settings.force_rebuild, release: bool = settings.compile_release_binaries): """ - `build_filepath` builds a extension module like `build` but allows - to directly specify a file path. + Builds a rust extension module from a specified file path, without importing it. - Parameters - ---------- - filepath : the filepath to the C++ file to build. - fullname : the name of the module to build. + @param path: The file path to the rust file or crate. + @param opt_in: Whether to require rust sources to opt in via adding "rustimport" to the first line of the file/crate. + Default is False since the intent to import a Rust module is explicitly stated. + @param force_rebuild: Whether to force re-compilation of the extension, even if it hasn't changed. Default is + derived from settings. + @param release: Whether to build a release binary. Default is derived from settings. - Returns - ------- - ext_path : the path to the compiled extension. + @return: An [Importable] instance for the given extension. """ from rustimport.importable import all_importables @@ -159,12 +153,17 @@ def build_filepath(path, opt_in: bool = False, force_rebuild: bool = settings.fo def build_all(root_directory, opt_in: bool = True, force_rebuild: bool = settings.force_rebuild, release: bool = settings.compile_release_binaries): """ - `build_all` builds a extension module like `build` for each eligible (that is, - containing the "rustimport" header) source file within the given `root_directory`. + Builds all eligible rust extensions modules in the specified directory. + + @param root_directory: The root directory to recursively search for rust source files/crates. + @param opt_in: Whether to require rust sources to opt in via adding "rustimport" to the first line of the file/crate. + Default is True. + @param force_rebuild: Whether to force re-compilation of the extension, even if it hasn't changed. Default is + derived from settings. + @param release: Whether to build a release binary. Default is derived from settings. - Parameters - ---------- - root_directory : the root directory to search for cpp source files in. + @return: A tuple of two lists of [Importable]s, one with the built [Importable]s and one with those + skipped: `(built, not_built)` """ import os from rustimport.importable import ( @@ -176,6 +175,7 @@ def build_all(root_directory, opt_in: bool = True, force_rebuild: bool = setting importables = [] _logger.info(f"Collecting rust extensions in {root_directory}…") + for directory, subdirs, files in os.walk(root_directory, topdown=True): if any(f.lower() == 'cargo.toml' for f in files): if i := CrateImportable.try_create(directory, opt_in=opt_in): @@ -190,6 +190,7 @@ def build_all(root_directory, opt_in: bool = True, force_rebuild: bool = setting importables.append(i) _logger.info(f"Found {len(importables)} {'extension' if len(importables) == 1 else 'extensions'}.") + not_built = [] for index, i in enumerate(importables): if should_rebuild(i, force_rebuild=force_rebuild, force_release=release): @@ -201,10 +202,14 @@ def build_all(root_directory, opt_in: bool = True, force_rebuild: bool = setting if not_built: _logger.info(f"Skipped building {len(not_built)} {'extension' if len(not_built) == 1 else 'extensions'} due" f" to unchanged source files. Re-run with `--force` to rebuild everything.") + _logger.info("Completed successfully.") + return [i for i in importables if i not in not_built], not_built + def load_ipython_extension(ipython): + """IPython magic entry point.""" rustc_is_installed = which("rustc") is not None if not rustc_is_installed: msg = "rustc must be installed to ust rustimport"