1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Anuncie Aqui
    Anuncie aqui você Também: fdantas@4each.com.br

[Python] How to import an editable package?

Discussão em 'Python' iniciado por Stack, Novembro 21, 2025.

  1. Stack

    Stack Membro Participativo

    I've installed a package from a cloned repository, in "editable" mode (pip install -e .). I can't import the package in a script in the virtual environment where the package in installed (the import cannot be resolved).

    What I did:


    1. venv .venv and activate it


    2. git clone https://github.com/rm-hull/luma.lcd.git


    3. python -m pip install -e ./luma.lcd


    4. Create a test.py file in the root dir and try to import luma.lcd

    Importing luma.lcd gives an error. luma.core, a (non-editable) dependency of luma.lcd can be imported without a problem.

    The directory structure is:

    .
    ├── luma.lcd
    │ ├── luma
    │ │ └── lcd
    │ │ └── (…)
    │ └── luma.lcd.egg-info
    ├── test.py
    └── .venv
    └── lib
    └── python3.11
    └── site-packages
    └── luma
    └── core
    ├── __editable___luma_lcd_2_11_0_finder.py
    └── __editable__.luma_lcd-2.11.0.pth


    The file __editable___luma_lcd_2_11_0_finder.py contains:

    from __future__ import annotations
    import sys
    from importlib.machinery import ModuleSpec, PathFinder
    from importlib.machinery import all_suffixes as module_suffixes
    from importlib.util import spec_from_file_location
    from itertools import chain
    from pathlib import Path

    MAPPING: dict[str, str] = {'luma': '/home/user/luma/luma.lcd/luma'}
    NAMESPACES: dict[str, list[str]] = {'luma': ['/home/user/luma/luma.lcd/luma']}
    PATH_PLACEHOLDER = '__editable__.luma_lcd-2.11.0.finder' + ".__path_hook__"


    class _EditableFinder: # MetaPathFinder
    @classmethod
    def find_spec(cls, fullname: str, path=None, target=None) -> ModuleSpec | None: # type: ignore
    # Top-level packages and modules (we know these exist in the FS)
    if fullname in MAPPING:
    pkg_path = MAPPING[fullname]
    return cls._find_spec(fullname, Path(pkg_path))

    # Handle immediate children modules (required for namespaces to work)
    # To avoid problems with case sensitivity in the file system we delegate
    # to the importlib.machinery implementation.
    parent, _, child = fullname.rpartition(".")
    if parent and parent in MAPPING:
    return PathFinder.find_spec(fullname, path=[MAPPING[parent]])

    # Other levels of nesting should be handled automatically by importlib
    # using the parent path.
    return None

    @classmethod
    def _find_spec(cls, fullname: str, candidate_path: Path) -> ModuleSpec | None:
    init = candidate_path / "__init__.py"
    candidates = (candidate_path.with_suffix(x) for x in module_suffixes())
    for candidate in chain([init], candidates):
    if candidate.exists():
    return spec_from_file_location(fullname, candidate)
    return None


    class _EditableNamespaceFinder: # PathEntryFinder
    @classmethod
    def _path_hook(cls, path) -> type[_EditableNamespaceFinder]:
    if path == PATH_PLACEHOLDER:
    return cls
    raise ImportError

    @classmethod
    def _paths(cls, fullname: str) -> list[str]:
    paths = NAMESPACES[fullname]
    if not paths and fullname in MAPPING:
    paths = [MAPPING[fullname]]
    # Always add placeholder, for 2 reasons:
    # 1. __path__ cannot be empty for the spec to be considered namespace.
    # 2. In the case of nested namespaces, we need to force
    # import machinery to query _EditableNamespaceFinder again.
    return [*paths, PATH_PLACEHOLDER]

    @classmethod
    def find_spec(cls, fullname: str, target=None) -> ModuleSpec | None: # type: ignore
    if fullname in NAMESPACES:
    spec = ModuleSpec(fullname, None, is_package=True)
    spec.submodule_search_locations = cls._paths(fullname)
    return spec
    return None

    @classmethod
    def find_module(cls, _fullname) -> None:
    return None


    def install():
    if not any(finder == _EditableFinder for finder in sys.meta_path):
    sys.meta_path.append(_EditableFinder)

    if not NAMESPACES:
    return

    if not any(hook == _EditableNamespaceFinder._path_hook for hook in sys.path_hooks):
    # PathEntryFinder is needed to create NamespaceSpec without private APIS
    sys.path_hooks.append(_EditableNamespaceFinder._path_hook)
    if PATH_PLACEHOLDER not in sys.path:
    sys.path.append(PATH_PLACEHOLDER) # Used just to trigger the path hook


    sys.path returns:

    ['', '/usr/lib/python311.zip', '/usr/lib/python3.11', '/usr/lib/python3.11/lib-dynload', '/home/user/luma/.venv/lib/python3.11/site-packages', '__editable__.luma_lcd-2.11.0.finder.__path_hook__']

    Continue reading...

Compartilhe esta Página