From a072ad5a120f58e6cf51d48e26ab2ed51db6766f Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 10 Mar 2023 14:40:34 -0800 Subject: [PATCH] Add a public API via the `create()` function (#26) --- README.md | 12 +++++----- microvenv.py | 59 +++++++++++++++++++++++------------------------ pyproject.toml | 7 ++---- test_microvenv.py | 5 +--- 4 files changed, 38 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 306880b..92bf91b 100644 --- a/README.md +++ b/README.md @@ -10,23 +10,23 @@ In general, though, using the [`venv` module](https://docs.python.org/3/library/ ## Usage ```console -python microvenv.py [path=".venv"] +python microvenv.py [env_dir=".venv"] ``` If an argument is provided to the script, it is used as the path to create the virtual environment in. Otherwise, the virtual environment is created in `.venv`. -For programmatic usage, use the [`runpy` module](https://docs.python.org/3/library/runpy.html#module-runpy) to execute the script: +For programmatic usage, there is the `create()` function, which is analogous to the [`venv.create()` function](https://docs.python.org/3/library/venv.html#venv.create). ```python -runpy.run_path("microvenv.py", run_name="__main__") +def create(env_dir: os.PathLike = ".venv") -> None ``` -The contents of `microvenv.py` is also small enough to be passed in via the `-c` flag to `python`. +The `microvenv.py` file is also small enough to have its contents passed in via the `-c` flag to `python`. ## Differences compared to the [`venv` module](https://docs.python.org/3/library/venv.html#module-venv) The module operates similarly to `py -m venv --symlinks --without-pip .venv`, except that: -- There are no activation scripts (execute `python` in the virtual environment directly). -- Windows is not supported. +- There are no activation scripts (you can execute `python` in the virtual environment directly) +- Windows is not supported diff --git a/microvenv.py b/microvenv.py index eb7c297..f8d9a92 100644 --- a/microvenv.py +++ b/microvenv.py @@ -3,37 +3,40 @@ import sys import sysconfig -PYVENVCFG_TEMPLATE = f"""home = {{base_executable_parent}} +_BASE_EXECUTABLE = pathlib.Path(getattr(sys, "_base_executable", sys.executable)) + +_PYVENVCFG_TEMPLATE = f"""home = {_BASE_EXECUTABLE.parent} include-system-site-packages = false -version = {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} -executable = {{resolved_base_executable}} +version = {'.'.join(map(str, sys.version_info[:3]))} +executable = {_BASE_EXECUTABLE.resolve()} command = {{command}} """ -def _sysconfig_path(name, venv_dir): +def _sysconfig_path(name, env_dir): variables = { - "base": venv_dir, - "platbase": venv_dir, - "installed_base": venv_dir, - "installed_platbase": venv_dir, + "base": env_dir, + "platbase": env_dir, + "installed_base": env_dir, + "installed_platbase": env_dir, } return pathlib.Path(sysconfig.get_path(name, "venv", variables)) -def create(venv_dir): - base_executable = pathlib.Path(getattr(sys, "_base_executable", sys.executable)) - +# Analogous to `venv.create()`. +def create(env_dir=".venv"): + """Create a minimal virtual environment.""" + env_path = pathlib.Path(env_dir) try: - scripts_dir = _sysconfig_path("scripts", venv_dir) - include_dir = _sysconfig_path("include", venv_dir) - purelib_dir = _sysconfig_path("purelib", venv_dir) + scripts_dir = _sysconfig_path("scripts", env_path) + include_dir = _sysconfig_path("include", env_path) + purelib_dir = _sysconfig_path("purelib", env_path) except KeyError: - scripts_dir = venv_dir / "bin" - include_dir = venv_dir / "include" + scripts_dir = env_path / "bin" + include_dir = env_path / "include" purelib_dir = ( - venv_dir + env_path / "lib" / f"python{sys.version_info.major}.{sys.version_info.minor}" / "site-packages" @@ -42,38 +45,34 @@ def create(venv_dir): dir.mkdir(parents=True) if sys.maxsize > 2**32 and os.name == "posix" and sys.platform != "darwin": - (venv_dir / "lib64").symlink_to("lib", target_is_directory=True) + (env_path / "lib64").symlink_to("lib", target_is_directory=True) for executable_name in ( "python", f"python{sys.version_info.major}", f"python{sys.version_info.major}.{sys.version_info.minor}", ): - (scripts_dir / executable_name).symlink_to(base_executable) + (scripts_dir / executable_name).symlink_to(_BASE_EXECUTABLE) try: module_path = pathlib.Path(__file__).resolve() except NameError: command = f"{sys.executable} -c '...'" else: - command = f"{sys.executable} {module_path} {venv_dir.resolve()}" - (venv_dir / "pyvenv.cfg").write_text( - PYVENVCFG_TEMPLATE.format( - base_executable_parent=base_executable.parent, - resolved_base_executable=base_executable.resolve(), - command=command, - ), + command = f"{sys.executable} {module_path} {env_path.resolve()}" + (env_path / "pyvenv.cfg").write_text( + _PYVENVCFG_TEMPLATE.format(command=command), encoding="utf-8", ) if __name__ == "__main__": if len(sys.argv) > 2: - print("Usage: microvenv.py [venv_dir='.venv']", file=sys.stderr) + print("Usage: microvenv.py [env_dir='.venv']", file=sys.stderr) sys.exit(1) try: - venv_dir = sys.argv[1] + env_dir = sys.argv[1] except IndexError: - venv_dir = ".venv" + env_dir = ".venv" - create(pathlib.Path(venv_dir)) + create(env_dir) diff --git a/pyproject.toml b/pyproject.toml index 14632b4..93806f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi" [project] name = "microvenv" -version = "2023.0.1" +version = "2023.1.0" description = "A minimal re-implementation of Python's venv module" keywords = ["virtual environments", "venv"] readme = "README.md" @@ -15,15 +15,12 @@ requires-python = ">=3.7" classifiers = [ "Development Status :: 3 - Alpha", "License :: OSI Approved :: MIT License", - "Environment :: Console", "Intended Audience :: Developers", "Topic :: Software Development", "Topic :: Utilities", "Natural Language :: English", "Operating System :: MacOS :: MacOS X", - "Operating System :: POSIX :: Linux", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", + "Operating System :: POSIX", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", diff --git a/test_microvenv.py b/test_microvenv.py index 3782a36..75598c5 100644 --- a/test_microvenv.py +++ b/test_microvenv.py @@ -26,10 +26,7 @@ def executable(): @pytest.fixture(scope="session") def full_venv(tmp_path_factory): venv_path = tmp_path_factory.mktemp("venvs") / "full_venv" - venv_builder = venv.EnvBuilder( - symlinks=True, with_pip=False, system_site_packages=False - ) - venv_builder.create(venv_path) + venv.create(venv_path, symlinks=True, with_pip=False, system_site_packages=False) return venv_path