Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mesonpy/_editable.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ def _rebuild(self) -> Node:

def _path_hook(self, path: str) -> MesonpyPathFinder:
if os.altsep:
path.replace(os.altsep, os.sep)
path = path.replace(os.altsep, os.sep)
path, _, key = path.rpartition(os.sep)
if path == __file__:
tree = self._rebuild()
Expand Down
42 changes: 42 additions & 0 deletions tests/test_editable.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,45 @@ def test_editable_rebuild_error(package_purelib_and_platlib, tmp_path, verbose):
def test_install_data(venv, editable_install_data, tmp_path):
venv.pip('install', os.fspath(editable_install_data))
venv.python('-c', 'import package')


@pytest.mark.skipif(not os.altsep, reason='os.altsep is only set on Windows')
@pytest.mark.skipif(FREE_THREADED_BUILD and CYTHON_VERSION < (3, 1, 0),
reason='Cython version too old, no free-threaded CPython support')
def test_path_hook_altsep_search_path(package_complex, tmp_path):
"""The path hook must match submodule search paths that use os.altsep.

After a real editable build, importlib/pkgutil look up subpackages via
path hooks. On Windows those paths may use '/' (PYTHONPATH, pathlib,
Meson) even though __file__ uses '\\'. The hook normalizes altsep
before rpartition(os.sep); discarding the replace means the hook
never matches __file__ and subpackage discovery fails.
"""
if os.altsep in _editable.__file__:
pytest.skip('__file__ already uses altsep; hook compares to it as-is')

project = mesonpy.Project(package_complex, tmp_path)
finder = _editable.MesonpyMetaFinder(
'complex', {'complex'}, os.fspath(tmp_path), project._build_command, True
)

try:
sys.meta_path.insert(0, finder)
sys.path_hooks.insert(0, finder._path_hook)

import complex
assert complex.__name__ == 'complex'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this assert for?


# Same location importlib would use, but spelled with os.altsep.
altsep_path = _editable.__file__.replace(os.sep, os.altsep) + os.altsep + 'complex'
importer = pkgutil.get_importer(altsep_path)
assert isinstance(importer, _editable.MesonpyPathFinder)

names = {module.name for module in pkgutil.iter_modules([altsep_path])}
assert 'more' in names
finally:
del sys.meta_path[0]
del sys.path_hooks[0]
for name in list(sys.modules):
if name == 'complex' or name.startswith('complex.'):

@dnicolodi dnicolodi Aug 15, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it required to remove modules whose name starts with complex.? What loaded these?

del sys.modules[name]
Loading