diff --git a/README.rst b/README.rst index 49a6670..90f8ec2 100644 --- a/README.rst +++ b/README.rst @@ -36,7 +36,9 @@ Usage Setup ----- -``defer-imports`` hooks into the Python import system with a path hook. That path hook needs to be registered before code using the import-delaying context manager, ``defer_imports.until_use``, is parsed. To do that, include the following somewhere such that it will be executed before your code: +This library uses a ``.pth`` file to register an import hook on interpreter startup. The hook is put in front of the built-in file finder's `path hook `_ on `sys.path_hooks `_, and is responsible for the instrumentation side of this library. That should work fine if you're using a regular setup with site packages, where that ``.pth`` file should end up. + +However, if your environment is atypical, you might need to manually register that path hook to have your code be correctly processed by this package. Do so in a file away from the rest of your code, before any of it executes. For example: .. code:: python @@ -44,6 +46,8 @@ Setup defer_imports.install_defer_import_hook() + import your_code + Example ------- diff --git a/pyproject.toml b/pyproject.toml index 8bb00b8..4094baf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,17 @@ Source = "https://github.com/Sachaa-Thanasius/defer-imports" [tool.hatch.build.targets.wheel] packages = ["src/defer_imports"] +[tool.hatch.build.targets.wheel.hooks.autorun] +# Install defer-import's path hook at startup. With luck, this'll bypass any need for cache invalidation. +dependencies = ["hatch-autorun"] +code = """ +try: + import defer_imports +except ImportError: + pass +else: + defer_imports.install_defer_import_hook(invalidate_caches=False) +""" # -------- Benchmark config diff --git a/src/defer_imports/_core.py b/src/defer_imports/_core.py index 82355ba..28a5f84 100644 --- a/src/defer_imports/_core.py +++ b/src/defer_imports/_core.py @@ -20,7 +20,7 @@ from . import _typing as _tp -__version__ = "0.0.1" +__version__ = "0.0.1dev" # region -------- Compile-time hook @@ -664,10 +664,15 @@ def deferred___import__( # noqa: ANN202 # region -------- Public API -def install_defer_import_hook() -> None: +def install_defer_import_hook(*, invalidate_caches: bool = True) -> None: """Insert defer_imports's path hook right before the default FileFinder one in sys.path_hooks. This can be called in a few places, e.g. __init__.py of a package, a .pth file in site packages, etc. + + Parameters + ---------- + invalidate_caches: bool, default=True + Whether to invalidate the caches on all path entry finders. """ if DEFERRED_PATH_HOOK in sys.path_hooks: @@ -678,7 +683,8 @@ def install_defer_import_hook() -> None: for i, hook in enumerate(sys.path_hooks): if hook.__qualname__.startswith("FileFinder.path_hook"): sys.path_hooks.insert(i, DEFERRED_PATH_HOOK) - PathFinder.invalidate_caches() + if invalidate_caches: + PathFinder.invalidate_caches() return diff --git a/tests/test_deferred.py b/tests/test_deferred.py index 08f249f..fbef96b 100644 --- a/tests/test_deferred.py +++ b/tests/test_deferred.py @@ -183,29 +183,27 @@ def test_instrumentation(before: str, after: str): def test_path_hook_installation(): """Test the API for putting/removing the defer_imports path hook from sys.path_hooks.""" - # It shouldn't be on there by default. - assert DEFERRED_PATH_HOOK not in sys.path_hooks - before_length = len(sys.path_hooks) - - # It should be present after calling install. - install_defer_import_hook() + before_path_hooks = list(sys.path_hooks) + # Thanks to the .pth file, it should be on there by default. assert DEFERRED_PATH_HOOK in sys.path_hooks - assert len(sys.path_hooks) == before_length + 1 + before_length = len(sys.path_hooks) # Calling install shouldn't do anything if it's already on sys.path_hooks. install_defer_import_hook() assert DEFERRED_PATH_HOOK in sys.path_hooks - assert len(sys.path_hooks) == before_length + 1 + assert len(sys.path_hooks) == before_length # Calling uninstall should remove it. uninstall_defer_import_hook() assert DEFERRED_PATH_HOOK not in sys.path_hooks - assert len(sys.path_hooks) == before_length + assert len(sys.path_hooks) == before_length - 1 # Calling uninstall if it's not present should do nothing to sys.path_hooks. uninstall_defer_import_hook() assert DEFERRED_PATH_HOOK not in sys.path_hooks - assert len(sys.path_hooks) == before_length + assert len(sys.path_hooks) == before_length - 1 + + sys.path_hooks = before_path_hooks def test_empty(tmp_path: Path):