Skip to content

Commit 1f07e39

Browse files
committed
ENH: implement editable wheels without having to run meson install
This implementation uses a .pth file to install a custom module finder. The module finder uses the Meson installation plan introspection data to build a virtual view of how files would be laid out in a wheel and looks up the modules from there. Not having to copy files from the source or build directory to the installation directory, rebuilds are faster. Executing directly from the build and source directory is more true to the spirit of editable wheels. There is no risk for the user to accidentally edit the code in the installation directory and have their edits to be overwritten at the next reload. I haven't verified this, but this approach has also the potential to allow to set debugger breakpoints from the code in the source directory and have them work at execution time (this should work if the code is imported before being executed).
1 parent e96a3e7 commit 1f07e39

File tree

12 files changed

+355
-228
lines changed

12 files changed

+355
-228
lines changed

mesonpy/__init__.py

Lines changed: 16 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
import mesonpy._util
5454
import mesonpy._wheelfile
5555

56-
from mesonpy._compat import Collection, Iterable, Mapping, cached_property, read_binary
56+
from mesonpy._compat import Collection, Mapping, cached_property, read_binary
5757

5858

5959
if typing.TYPE_CHECKING: # pragma: no cover
@@ -194,16 +194,6 @@ def _setup_cli() -> None:
194194
colorama.init() # fix colors on windows
195195

196196

197-
def _as_python_declaration(value: Any) -> str:
198-
if isinstance(value, str):
199-
return f"r'{value}'"
200-
elif isinstance(value, os.PathLike):
201-
return _as_python_declaration(os.fspath(value))
202-
elif isinstance(value, Iterable):
203-
return '[' + ', '.join(map(_as_python_declaration, value)) + ']'
204-
raise NotImplementedError(f'Unsupported type: {type(value)}')
205-
206-
207197
class Error(RuntimeError):
208198
def __str__(self) -> str:
209199
return str(self.args[0])
@@ -529,56 +519,30 @@ def build_editable(self, directory: Path, verbose: bool = False) -> pathlib.Path
529519

530520
wheel_file = pathlib.Path(directory, f'{self.name}.whl')
531521

532-
install_path = self._source_dir / '.mesonpy' / 'editable' / 'install'
533-
rebuild_commands = self._project.build_commands(install_path)
534-
535-
import_paths = set()
536-
for name, raw_path in mesonpy._introspection.SYSCONFIG_PATHS.items():
537-
if name not in ('purelib', 'platlib'):
538-
continue
539-
path = pathlib.Path(raw_path)
540-
import_paths.add(install_path / path.relative_to(path.anchor))
541-
542-
install_path.mkdir(parents=True, exist_ok=True)
543-
544522
with mesonpy._wheelfile.WheelFile(wheel_file, 'w') as whl:
545523
self._wheel_write_metadata(whl)
546524
whl.writestr(
547525
f'{self.distinfo_dir}/direct_url.json',
548-
self._source_dir.as_uri().encode(),
526+
self._source_dir.as_uri().encode('utf-8'),
549527
)
550528

551-
# install hook module
552-
hook_module_name = f'_mesonpy_hook_{self.normalized_name.replace(".", "_")}'
553-
hook_install_code = textwrap.dedent(f'''
554-
MesonpyFinder.install(
555-
project_name={_as_python_declaration(self._project.name)},
556-
hook_name={_as_python_declaration(hook_module_name)},
557-
project_path={_as_python_declaration(self._source_dir)},
558-
build_path={_as_python_declaration(self._build_dir)},
559-
import_paths={_as_python_declaration(import_paths)},
560-
top_level_modules={_as_python_declaration(self.top_level_modules)},
561-
rebuild_commands={_as_python_declaration(rebuild_commands)},
562-
verbose={verbose},
563-
)
564-
''').strip().encode()
529+
# install loader module
530+
loader_module_name = f'_{self.normalized_name.replace(".", "_")}_editable_loader'
531+
build_cmd = [self._project._ninja, *self._project._meson_args['compile']]
565532
whl.writestr(
566-
f'{hook_module_name}.py',
567-
read_binary('mesonpy', '_editable.py') + hook_install_code,
568-
)
533+
f'{loader_module_name}.py',
534+
read_binary('mesonpy', '_editable.py') + textwrap.dedent(f'''
535+
install(
536+
{self.top_level_modules!r},
537+
{os.fspath(self._build_dir)!r},
538+
{build_cmd},
539+
{verbose!r},
540+
)''').encode('utf-8'))
541+
569542
# install .pth file
570543
whl.writestr(
571-
f'{self.normalized_name}-editable-hook.pth',
572-
f'import {hook_module_name}'.encode(),
573-
)
574-
575-
# install non-code schemes
576-
for scheme in _SCHEME_MAP:
577-
if scheme in ('purelib', 'platlib', 'mesonpy-libs'):
578-
continue
579-
for destination, origin in self._wheel_files[scheme]:
580-
destination = pathlib.Path(self.data_dir, scheme, destination)
581-
whl.write(origin, destination.as_posix())
544+
f'{self.normalized_name}-editable.pth',
545+
f'import {loader_module_name}'.encode('utf-8'))
582546

583547
return wheel_file
584548

0 commit comments

Comments
 (0)