virtualenv-clone
virtualenv-clone is a Python package that provides a command-line script to clone existing non-relocatable virtual environments. It handles updating internal paths (like `VIRTUAL_ENV` in `activate` scripts) and shebangs in executables to point to the new location, aiming to make a copied virtualenv functional without reinstalling packages. The current version is 0.5.7, released in September 2021, suggesting a maintenance-level release cadence.
Warnings
- breaking Cloning virtual environments across different operating systems or CPU architectures is not supported and will likely result in a broken environment. Paths, binaries, and low-level libraries are specific to the system they were created on. virtualenv-clone primarily addresses relocation within the *same* system.
- gotcha While virtualenv-clone aims to handle path rewriting, cloning environments created with different *major* Python versions (e.g., cloning a Python 3.8 venv to a Python 3.9 venv) can lead to unexpected issues or errors, even if the tool theoretically supports both Python versions. It's best used for cloning environments based on the *same* Python executable.
- deprecated The project is currently listed with a 'Development Status :: 3 - Alpha' classifier on PyPI. While functional, this indicates it might not be considered fully stable or production-ready by its maintainers, and future breaking changes, while unlikely given its current maintenance status, could occur.
- gotcha virtualenv-clone is a command-line utility and does not expose a public Python API for direct import and programmatic use. Its functionality is accessed solely via the `virtualenv-clone` command in your shell.
Install
-
pip install virtualenv-clone
Quickstart
import os
import subprocess
import shutil
def run_command(cmd, shell=True, check=True):
print(f"Executing: {cmd}")
result = subprocess.run(cmd, shell=shell, check=check, capture_output=True, text=True)
print(result.stdout)
if result.stderr:
print(f"Stderr: {result.stderr}")
return result
original_venv_name = "my_original_venv"
cloned_venv_name = "my_cloned_venv"
# Cleanup any previous runs
if os.path.exists(original_venv_name):
shutil.rmtree(original_venv_name)
if os.path.exists(cloned_venv_name):
shutil.rmtree(cloned_venv_name)
print("1. Creating original virtual environment...")
run_command(f"python -m venv {original_venv_name}")
print("2. Activating original virtual environment and installing requests...")
# On Windows, activate is in Scripts; on Unix-like, it's in bin
activate_script = os.path.join(original_venv_name, 'Scripts', 'activate') if os.name == 'nt' else os.path.join(original_venv_name, 'bin', 'activate')
# For cross-platform activation and command execution within venv
# Using subprocess.run with full path to python/pip within the venv
python_executable = os.path.join(original_venv_name, 'Scripts', 'python') if os.name == 'nt' else os.path.join(original_venv_name, 'bin', 'python')
pip_executable = os.path.join(original_venv_name, 'Scripts', 'pip') if os.name == 'nt' else os.path.join(original_venv_name, 'bin', 'pip')
run_command(f"{pip_executable} install requests", shell=False) # Use full path, not shell activation
print(f"3. Verifying requests in {original_venv_name}...")
run_command(f"{python_executable} -c \"import requests; print('requests imported successfully in original venv')\"", shell=False)
print(f"4. Cloning {original_venv_name} to {cloned_venv_name}...")
# virtualenv-clone is expected to be installed globally or accessible in PATH
run_command(f"virtualenv-clone {original_venv_name} {cloned_venv_name}")
print(f"5. Activating cloned virtual environment and verifying requests...")
cloned_python_executable = os.path.join(cloned_venv_name, 'Scripts', 'python') if os.name == 'nt' else os.path.join(cloned_venv_name, 'bin', 'python')
run_command(f"{cloned_python_executable} -c \"import requests; print('requests imported successfully in cloned venv')\"", shell=False)
print("Quickstart complete: virtual environment cloned and verified.")
# Cleanup
print("Cleaning up...")
shutil.rmtree(original_venv_name)
shutil.rmtree(cloned_venv_name)
print("Cleanup complete.")