angreal.integrations.venv
Virtual environment and package management powered by UV.
Overview
Section titled “Overview”Angreal’s virtual environment integration uses UV to create virtual environments, install packages, and manage Python versions. UV is automatically installed when first used, requiring no additional setup.
Installation Requirements
Section titled “Installation Requirements”UV is automatically installed when first used. No manual installation required.
Supported Platforms:
- macOS (via curl)
- Linux (via curl)
- Windows (via PowerShell)
Functions
Section titled “Functions”venv_required
Section titled “venv_required”Decorator that wraps a function in a virtual environment before execution. The virtual environment is activated before the function runs and deactivated afterward, ensuring packages installed in the venv are available for import.
@venv_required(path, requirements=None)Parameters:
path(str): The path to the virtual environment (or where it should be created if it doesn’t exist)requirements(str | List[str], optional): A string containing a single module, a list of module names, or a string containing a requirements file path. Defaults to None.
Example:
from angreal.integrations.venv import venv_required
@venv_required("/path/to/venv", requirements=["requests", "pandas"])def process_data(): # These imports work because the venv is activated import requests import pandas as pd
# Your code here - runs in the activated virtual environment df = pd.DataFrame({'data': [1, 2, 3]}) response = requests.get('https://api.example.com')
# After the function completes, the original environment is restoredClasses
Section titled “Classes”VirtualEnv
Section titled “VirtualEnv”Manage virtual environments using UV’s ultra-fast operations.
from angreal.integrations.venv import VirtualEnv
venv = VirtualEnv(path="/path/to/venv")Constructor
Section titled “Constructor”VirtualEnv(path, python=None, requirements=None, now=True)Parameters:
path(str | Path): The path to the virtual environment. Relative paths are resolved to absolute paths based on the current working directory.python(str, optional): Python version to use (e.g., “3.11”, “3.12”)requirements(str | List[str], optional): Requirements to installnow(bool, optional): Create environment immediately. Defaults to True.
Properties
Section titled “Properties”exists
Section titled “exists”Check if the virtual environment exists.
@propertydef exists(self) -> boolReturns:
bool: True if the virtual environment exists, False otherwise
python_executable
Section titled “python_executable”Get the Python executable path for this virtual environment.
@propertydef python_executable(self) -> PathReturns:
Path: Path to the Python executable
Instance Methods
Section titled “Instance Methods”install_requirements
Section titled “install_requirements”Install the requirements set during initialization.
def install_requirements(self) -> NoneRaises:
TypeError: If requirements is not a file path, list, or stringRuntimeError: If UV fails to install the requirements
install
Section titled “install”Install packages or requirements file.
def install(self, packages: Union[str, List[str], Path]) -> NoneParameters:
packages: Package names, requirements file path, or list of packages
Example:
# Install single packagevenv.install("requests")
# Install multiple packagesvenv.install(["pandas", "numpy", "matplotlib"])
# Install from requirements filevenv.install("requirements.txt")activate
Section titled “activate”Activate the virtual environment in the current Python process.
def activate(self) -> NoneThis method modifies the current Python process’s sys.prefix, sys.exec_prefix, and sys.path so the virtual environment’s packages are importable. It also updates os.environ, setting VIRTUAL_ENV to the environment’s path and prepending the environment’s bin (Scripts on Windows) directory to PATH. Because environment variables are inherited by child processes, subprocesses spawned after activation run with the virtual environment on PATH.
Example:
venv = VirtualEnv("myenv", now=True)venv.install("requests")
# Before activation, requests might not be importablevenv.activate()
# Now requests can be importedimport requestsresponse = requests.get("https://api.github.com")
# Remember to deactivate when donevenv.deactivate()Raises:
RuntimeError: If the virtual environment does not exist
deactivate
Section titled “deactivate”Restore the original Python environment.
def deactivate(self) -> NoneThis method restores the sys state (sys.prefix, sys.exec_prefix, sys.path) and the os.environ state (PATH, and VIRTUAL_ENV — removed if it was unset before activation) to their values prior to activation. Safe to call multiple times.
Example:
venv = VirtualEnv("myenv", now=True)venv.activate()# Use the virtual environmentvenv.deactivate() # Restore original environmentContext Manager Support
Section titled “Context Manager Support”VirtualEnv supports the context manager protocol for automatic activation/deactivation:
with VirtualEnv("myenv", now=True) as venv: venv.install("numpy")
# Virtual environment is activated here import numpy as np array = np.array([1, 2, 3])
# Virtual environment is automatically deactivated hereClass Methods
Section titled “Class Methods”discover_available_pythons
Section titled “discover_available_pythons”Discover all Python installations on the system using UV.
@classmethoddef discover_available_pythons(cls) -> List[tuple[str, str]]Returns:
List[tuple[str, str]]: List of (version, path) tuples for available Python installations
Example:
pythons = VirtualEnv.discover_available_pythons()for version, path in pythons: print(f"Python {version}: {path}")ensure_python
Section titled “ensure_python”Ensure a specific Python version is available, installing if needed.
@classmethoddef ensure_python(cls, version: str) -> strParameters:
version(str): Python version to ensure (e.g., “3.11”, “3.12”)
Returns:
str: Path to the Python installation
Example:
python_path = VirtualEnv.ensure_python("3.11")print(f"Python 3.11 available at: {python_path}")version
Section titled “version”Get UV version information.
@classmethoddef version(cls) -> strReturns:
str: UV version string
Module-level Functions
Section titled “Module-level Functions”The following functions are exported at the top level of the angreal module. They operate directly on UV and virtual environment paths without constructing a VirtualEnv instance.
ensure_uv_installed
Section titled “ensure_uv_installed”Ensure the UV binary is installed, installing it if not present.
def ensure_uv_installed() -> NoneRaises:
RuntimeError: If UV installation fails
uv_version
Section titled “uv_version”Get the installed UV version.
def uv_version() -> strReturns:
str: UV version string
Raises:
RuntimeError: If the UV version cannot be determined
create_virtualenv
Section titled “create_virtualenv”Create a virtual environment at the given path.
def create_virtualenv(path: str, python_version: str | None = None) -> NoneParameters:
path(str): Path at which to create the virtual environmentpython_version(str, optional): Python version to use (e.g., “3.11”)
Raises:
RuntimeError: If creation fails
install_packages
Section titled “install_packages”Install a list of packages into an existing virtual environment.
def install_packages(venv_path: str, packages: list[str]) -> NoneParameters:
venv_path(str): Path to the virtual environmentpackages(list[str]): Package names to install
Raises:
RuntimeError: If installation fails
install_requirements
Section titled “install_requirements”Install packages from a requirements file into an existing virtual environment.
def install_requirements(venv_path: str, requirements_file: str) -> NoneParameters:
venv_path(str): Path to the virtual environmentrequirements_file(str): Path to the requirements file
Raises:
RuntimeError: If installation fails
discover_pythons
Section titled “discover_pythons”Discover available Python installations using UV.
def discover_pythons() -> list[tuple[str, str]]Returns:
list[tuple[str, str]]: List of (version, path) tuples
Raises:
RuntimeError: If discovery fails
install_python
Section titled “install_python”Ensure a Python version is installed, installing it if needed.
def install_python(version: str) -> strParameters:
version(str): Python version to ensure (e.g., “3.11”)
Returns:
str: Path to the Python installation
Raises:
RuntimeError: If the version cannot be installed
get_venv_activation_info
Section titled “get_venv_activation_info”Get activation details for a virtual environment.
def get_venv_activation_info(venv_path: str) -> ActivationInfoParameters:
venv_path(str): Path to the virtual environment
Returns:
ActivationInfo: Activation details for the environment
Raises:
RuntimeError: If the information cannot be determined
ActivationInfo
Section titled “ActivationInfo”Activation details for a virtual environment. All attributes are read-only.
Attributes:
venv_path(str): Path to the virtual environmentvenv_prefix(str): Prefix (sys.prefix) for the virtual environmentsite_packages(str): Path to the environment’s site-packages directorypython_executable(str): Path to the environment’s Python executable
Activation Behavior
Section titled “Activation Behavior”How Activation Works
Section titled “How Activation Works”Activation modifies both in-process Python state and the process environment:
- Modifying
sys.prefixandsys.exec_prefixto point to the virtual environment - Updating
sys.pathto prioritize the virtual environment’s site-packages, soimports resolve against the venv - Setting
os.environ["VIRTUAL_ENV"]to the environment’s path and prepending the environment’sbin(Scriptson Windows) directory toos.environ["PATH"] - Preserving the original
sysandos.environstate for restoration during deactivation
Activation Notes
Section titled “Activation Notes”- In-Process State: Activation modifies the running Python process’s
sys.prefix,sys.exec_prefix, andsys.path. It does not modify the parent shell or its prompt - Import Availability: After activation, packages installed in the venv become importable
- Environment Variables: Activation also sets
VIRTUAL_ENVand prepends the venv’sbin(Scriptson Windows) directory toPATHinos.environ - Subprocess Behavior: Because
os.environis inherited by child processes, subprocesses started after activation (e.g. viasubprocess.run([...])) run with the venv onPATH. For fully explicit, robust invocation that does not depend onPATHordering or activation state, pass the venv’spython_executabledirectly - Multiple Activations: Only one virtual environment can be active at a time. Activating a second venv will override the first
- Thread Safety: Activation modifies global Python state (
sys.prefix,sys.path) and process state (os.environ) and is not thread-safe
Limitations
Section titled “Limitations”- No Nested Activation: The current implementation does not support nested virtual environment activation. Deactivating always restores the original environment, not any previously activated environment
- No Shell Integration: This activation does not affect your shell environment or terminal prompt
- Module Caching: Python’s module import cache may retain modules from before activation. Use
importlib.reload()if needed
Examples
Section titled “Examples”Basic Usage
Section titled “Basic Usage”from angreal.integrations.venv import VirtualEnv
# Create virtual environment with Python 3.11venv = VirtualEnv("/path/to/myenv", python="3.11")
# Check if it existsif venv.exists: print("Virtual environment is ready") print(f"Python executable: {venv.python_executable}")With Requirements
Section titled “With Requirements”from angreal.integrations.venv import VirtualEnv
# Create venv with a list of packagesvenv = VirtualEnv( "/path/to/myenv", python="3.11", requirements=["django", "pillow", "requests"])venv.install_requirements()
# Or with a requirements filevenv = VirtualEnv( "/path/to/myenv", requirements="requirements.txt")venv.install_requirements()Using the Decorator
Section titled “Using the Decorator”from angreal.integrations.venv import venv_requiredimport angreal
@angreal.command(name="analyze", about="Analyze data with pandas")@venv_required(".venv", requirements=["pandas", "numpy"])def analyze_command(): """This command runs in an isolated virtual environment.""" import pandas as pd import numpy as np
# Your analysis code here data = pd.DataFrame(np.random.rand(100, 4)) print(data.describe())Python Version Management
Section titled “Python Version Management”from angreal.integrations.venv import VirtualEnv
# Discover available Python versionspythons = VirtualEnv.discover_available_pythons()print("Available Python installations:")for version, path in pythons: print(f" {version}: {path}")
# Ensure Python 3.12 is availablepython_path = VirtualEnv.ensure_python("3.12")print(f"Python 3.12 ready at: {python_path}")
# Create environment with specific Python versionvenv = VirtualEnv("myproject-venv", python="3.12")Manual Environment Management
Section titled “Manual Environment Management”from angreal.integrations.venv import VirtualEnv
# Create but don't activate immediatelyvenv = VirtualEnv("/path/to/env", now=False)
# Check and create if needed manuallyif not venv.exists: venv.create()
# Install specific packagesvenv.install(["flask", "sqlalchemy", "pytest"])
# Install from requirements filevenv.install("requirements.txt")Programmatic Activation
Section titled “Programmatic Activation”from angreal.integrations.venv import VirtualEnvimport sys
# Create and set up environmentvenv = VirtualEnv("data-science-env", now=True)venv.install(["numpy", "pandas", "matplotlib"])
print(f"Before activation - sys.prefix: {sys.prefix}")
# Activate the environmentvenv.activate()print(f"After activation - sys.prefix: {sys.prefix}")
# Now we can import the installed packagesimport numpy as npimport pandas as pdimport matplotlib.pyplot as plt
# Do some work with the packagesdata = pd.DataFrame(np.random.randn(100, 4), columns=['A', 'B', 'C', 'D'])data.plot()
# Deactivate when donevenv.deactivate()print(f"After deactivation - sys.prefix: {sys.prefix}")
# Using context manager for automatic cleanupwith VirtualEnv("analysis-env", now=True) as venv: venv.install("scikit-learn")
# This import works because we're in the activated environment from sklearn.datasets import load_iris iris = load_iris() print(f"Loaded {len(iris.data)} samples")
# Environment is automatically deactivated hereQuerying UV Version
Section titled “Querying UV Version”from angreal.integrations.venv import VirtualEnv
# Check UV versionprint(f"Using UV version: {VirtualEnv.version()}")Performance Characteristics
Section titled “Performance Characteristics”UV operations run as separate processes and clean up automatically.
Error Handling
Section titled “Error Handling”Common Errors
Section titled “Common Errors”UV Installation Failed:
RuntimeError: UV installation failedSolution: Check network connectivity or install UV manually
Python Version Not Found:
RuntimeError: Python 3.11 installed but not foundSolution: Specify exact version or use discover_available_pythons() to see available versions
Package Installation Failed:
RuntimeError: Failed to install packages: network timeoutSolution: Check network connectivity and package names
UV Binary Management
Section titled “UV Binary Management”Automatic Installation
Section titled “Automatic Installation”UV is automatically installed when:
- Any VirtualEnv operation is performed
- The venv module is imported
- UV is not found in the system PATH
Manual UV Operations
Section titled “Manual UV Operations”While not typically needed, you can check UV status:
# This is handled automatically - shown for referencefrom angreal import ensure_uv_installed, uv_version
# Ensure UV is availableensure_uv_installed()
# Check versionversion = uv_version()print(f"UV version: {version}")Architecture Notes
Section titled “Architecture Notes”- Binary Integration: Uses UV as external binary for maximum performance and stability
- Subprocess Safety: All UV operations use secure subprocess calls with proper argument handling
- Cross-Platform: Supports Windows, macOS, and Linux with platform-specific installation methods
- Backward Compatibility: 100% compatible with existing venv API - drop-in replacement
See Also
Section titled “See Also”- UV Integration Architecture - Architectural decisions and implementation details
- Working with Virtual Environments - How-to guide
- Python Utils - Other Python utilities
- UV Documentation - Official UV documentation