Skip to content

Commit

Permalink
Replace custom import sequence with MetaPathFinder
Browse files Browse the repository at this point in the history
This should address review comments and increase the robustness of the
solution.
  • Loading branch information
abravalheri committed Feb 24, 2023
1 parent b22169e commit f14e5df
Showing 1 changed file with 28 additions and 28 deletions.
56 changes: 28 additions & 28 deletions src/pyproject_hooks/_in_process/_in_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import traceback
from glob import glob
from importlib import import_module
from importlib.util import module_from_spec
from importlib.machinery import PathFinder
from os.path import join as pjoin

Expand Down Expand Up @@ -65,40 +64,41 @@ def _build_backend():

if backend_path:
extra_pathitems = backend_path.split(os.pathsep)
obj = _load_module_from_path(mod_path, extra_pathitems)
else:
try:
obj = import_module(mod_path)
except ImportError:
msg = f"Cannot import {mod_path!r}"
raise BackendUnavailable(msg, traceback.format_exc())
sys.meta_path.insert(0, _BackendPathFinder(extra_pathitems, mod_path))

try:
obj = import_module(mod_path)
except ImportError:
msg = f"Cannot import {mod_path!r}"
raise BackendUnavailable(msg, traceback.format_exc())

if obj_path:
for path_part in obj_path.split("."):
obj = getattr(obj, path_part)
return obj


def _load_module_from_path(fullname, pathitems):
"""Given a set of sys.path-like entries, load a module from it"""
sys.path[:0] = pathitems # Still required for other imports.
parts = fullname.split(".")
# Parent packages need to be imported to ensure everything comes from pathitems.
for i in range(len(parts)):
module_name = ".".join(parts[: i + 1])
spec = _find_spec_in_path(module_name, pathitems)
module = module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module


def _find_spec_in_path(fullname, pathitems):
"""Given sys.path-like entries, find a module spec or raise an exception"""
spec = PathFinder.find_spec(fullname, path=pathitems)
if not spec:
raise BackendUnavailable(f"Cannot find module {fullname!r} in {pathitems!r}")
return spec
class _BackendPathFinder:
"""Implements the MetaPathFinder interface to locate modules in ``backend-path``.
Since the environment provided by the frontend can contain all sorts of
MetaPathFinders, the only way to ensure the backend is loaded from the
right place is to prepend our own.
"""

def __init__(self, backend_path, backend_module):
self.backend_path = backend_path
self.backend_module = backend_module

def find_spec(self, fullname, _path, _target=None):
# Ignore other items in _path or sys.path and use backend_path instead:
spec = PathFinder.find_spec(fullname, path=self.backend_path)
if spec is None and fullname == self.backend_module:
# According to the spec, the backend MUST be loaded from backend-path.
# Therefore, we can halt the import machinery and raise a clean error.
msg = f"Cannot find module {self.backend_module!r} in {self.backend_path!r}"
raise BackendUnavailable(msg)
return spec


def _supported_features():
Expand Down

0 comments on commit f14e5df

Please sign in to comment.