Feat(env) dynamic virtual env support for advanced users

This commit is contained in:
Zappandy
2025-10-11 12:31:11 +02:00
parent ad0efbb434
commit e96d821fa1
12 changed files with 62 additions and 21 deletions

12
.envrc
View File

@@ -1,20 +1,24 @@
# Python Virtual Environment Auto-Activation Template
# Copy this file as .envrc to any Python project directory
# Default usage:
# Then run: direnv allow
VENV_PATH=$(python -c "import json; print(json.load(open('.tinyrc')).get('venv_path', '.venv'))")
echo "Using virtual env in ${VENV_PATH}"
export VENV_PATH
# Check if .venv exists, create if it doesn't
if [[ ! -d ".venv" ]]; then
if [[ ! -d "$VENV_PATH" ]]; then
echo "🔧 Creating Python virtual environment..."
python3 -m venv .venv
python3 -m venv "$VENV_PATH"
echo "📦 Installing basic dependencies..."
source .venv/bin/activate
source "$VENV_PATH/bin/activate"
pip install --upgrade pip
# Uncomment the next line if you have a requirements.txt
# pip install -r requirements.txt
fi
# Activate the virtual environment
source .venv/bin/activate
source "$VENV_PATH/bin/activate"
# Set common Python environment variables
export PYTHONPATH="${PWD}:${PYTHONPATH}"

3
.tinyrc Normal file
View File

@@ -0,0 +1,3 @@
{
"venv_path": ".venv"
}

View File

@@ -1,10 +1,14 @@
#!/bin/bash
# Tiny🔥Torch Environment Activation & Setup
# Allow users to pass a path to existing virtual env
VENV_PATH=${1:-".venv"}
export VENV_PATH
# Check if virtual environment exists, create if not
if [ ! -d ".venv" ]; then
if [ ! -d "$VENV_PATH" ]; then
echo "🆕 First time setup - creating environment..."
python3 -m venv .venv || {
python3 -m venv "$VENV_PATH" || {
echo "❌ Failed to create virtual environment"
exit 1
}
@@ -17,7 +21,7 @@ if [ ! -d ".venv" ]; then
fi
echo "🔥 Activating Tiny🔥Torch environment..."
source .venv/bin/activate
source "$VENV_PATH/bin/activate"
# Create tito alias for convenience
alias tito="python3 bin/tito"

View File

@@ -5,9 +5,11 @@ Base command class for TinyTorch CLI.
from abc import ABC, abstractmethod
from argparse import ArgumentParser, Namespace
from typing import Optional
from pathlib import Path
import logging
from ..core.config import CLIConfig
from ..core.virtual_env_manager import get_venv_path
from ..core.console import get_console
from ..core.exceptions import TinyTorchCLIError
@@ -26,6 +28,11 @@ class BaseCommand(ABC):
def name(self) -> str:
"""Return the command name."""
pass
@property
def venv_path(self) -> Path:
"""Return the command name."""
return get_venv_path()
@property
@abstractmethod

View File

@@ -41,8 +41,7 @@ class DoctorCommand(BaseCommand):
env_table.add_row("Python", "[green]✅ OK[/green]", f"{sys.version.split()[0]} ({sys.platform})")
# Virtual environment - check if it exists and if we're using it
venv_path = Path(".venv")
venv_exists = venv_path.exists()
venv_exists = self.venv_path.exists()
in_venv = (
# Method 1: Check VIRTUAL_ENV environment variable (most reliable for activation)
os.environ.get('VIRTUAL_ENV') is not None or
@@ -58,7 +57,7 @@ class DoctorCommand(BaseCommand):
venv_status = "[yellow]✅ Ready (Not Active)[/yellow]"
else:
venv_status = "[red]❌ Not Found[/red]"
env_table.add_row("Virtual Environment", venv_status, ".venv")
env_table.add_row("Virtual Environment", venv_status, f"{self.venv_path}")
# Dependencies
dependencies = [

View File

@@ -478,7 +478,7 @@ class ExportCommand(BaseCommand):
# Get the project root directory (where .venv should be)
project_root = Path(__file__).parent.parent.parent
venv_jupytext = project_root / ".venv" / "bin" / "jupytext"
venv_jupytext = self.venv_path / "bin" / "jupytext"
if venv_jupytext.exists():
# Test venv jupytext first

View File

@@ -409,8 +409,8 @@ class HelpCommand(BaseCommand):
"```bash\n"
"git clone https://github.com/mlsysbook/TinyTorch.git\n"
"cd TinyTorch\n"
"python -m venv .venv\n"
"source .venv/bin/activate # Windows: .venv\\Scripts\\activate\n"
f"python -m venv {self.venv_path}\n"
f"source {self.venv_path}/bin/activate # Windows: .venv\\Scripts\\activate\n"
"pip install -r requirements.txt\n"
"pip install -e .\n"
"```",

View File

@@ -3,7 +3,6 @@ Info command for TinyTorch CLI: shows system information and course navigation.
"""
from argparse import ArgumentParser, Namespace
from pathlib import Path
import sys
import os
from rich.console import Console
@@ -39,8 +38,7 @@ class InfoCommand(BaseCommand):
info_text.append(f"Working Directory: {os.getcwd()}\n", style="cyan")
# Virtual environment check
venv_path = Path(".venv")
venv_exists = venv_path.exists()
venv_exists = self.venv_path.exists()
in_venv = (
os.environ.get('VIRTUAL_ENV') is not None or
(hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix) or

View File

@@ -194,6 +194,7 @@ echo "✅ No auto-generated files being committed"
vscode_dir = Path(".vscode")
vscode_dir.mkdir(exist_ok=True)
python_default_interpreter = str(self.venv_path) + "/bin/python"
vscode_settings = {
"_comment_protection": "🛡️ TinyTorch Student Protection",
"files.readonlyInclude": {
@@ -204,7 +205,7 @@ echo "✅ No auto-generated files being committed"
"files.decorations.badges": True,
"explorer.decorations.colors": True,
"explorer.decorations.badges": True,
"python.defaultInterpreterPath": "./.venv/bin/python",
"python.defaultInterpreterPath": python_default_interpreter,
"python.terminal.activateEnvironment": True
}

View File

@@ -8,6 +8,7 @@ from pathlib import Path
from typing import Dict, Any, Optional, List
from dataclasses import dataclass
@dataclass
class CLIConfig:
"""Configuration for TinyTorch CLI."""
@@ -55,7 +56,7 @@ class CLIConfig:
bin_dir=project_root / 'bin'
)
def validate(self) -> List[str]:
def validate(self, venv_path=Optional[Path]) -> List[str]:
"""Validate the configuration and return any issues."""
issues = []
@@ -73,10 +74,10 @@ class CLIConfig:
# Method 3: Check for sys.real_prefix (older Python versions)
hasattr(sys, 'real_prefix') or
# Method 4: Check if .venv directory exists and packages are available
(Path('.venv').exists() and self._packages_available())
(venv_path.exists() and self._packages_available())
)
if not in_venv:
issues.append("Virtual environment not activated. Run: source .venv/bin/activate")
issues.append(f"Virtual environment not activated. Run: source {venv_path}/bin/activate")
# Check required directories
if not self.assignments_dir.exists():

View File

@@ -0,0 +1,23 @@
import os, json
from pathlib import Path
DEFAULT_VENV = ".venv"
CONFIG_FILE = ".tinyrc"
def get_venv_path() -> Path:
"""
Fetch venv in case users have a custom path
"""
print(f"running this from {os.getcwd()}")
if "VENV_PATH" in os.environ:
return Path(os.environ["VENV_PATH"]).expanduser().resolve()
if Path(CONFIG_FILE).exists():
try:
cfg = json.load(open(CONFIG_FILE))
return Path(cfg.get("venv_path", DEFAULT_VENV)).expanduser().resolve()
except Exception:
pass
return Path(DEFAULT_VENV).resolve()

View File

@@ -17,6 +17,7 @@ from pathlib import Path
from typing import Dict, Type, Optional, List
from .core.config import CLIConfig
from .core.virtual_env_manager import get_venv_path
from .core.console import get_console, print_banner, print_error, print_ascii_logo
from .core.exceptions import TinyTorchCLIError
from rich.panel import Panel
@@ -158,7 +159,7 @@ Examples:
def validate_environment(self) -> bool:
"""Validate the environment and show issues if any."""
issues = self.config.validate()
issues = self.config.validate(get_venv_path())
if issues:
print_error(