# Instrument Control for QPS Optics Lab ### General rules Please use initials and kebab case when opening a branch. For example if your name is John Doe, name your branch jd-branch-name. # Python Environments with `uv` — Lab Repo Guide (Written by Claude AI) --- ## Installing `uv` `uv` is a modern Python package and environment manager — much faster than `pip` and handles everything in one tool (environments, dependencies, Python versions). Install it with: **macOS / Linux:** ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` **Windows:** ```powershell powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.sh | iex" ``` After installing, restart your terminal and verify it worked: ```bash uv --version ``` --- ## If you have other Python installations on your machine If you installed Python previously (via `python.org`, `brew`, `conda`, `pyenv`, etc.), those installations still exist and your terminal may default to one of them. This is fine — `uv` does not conflict with them. However, to avoid confusion: - **Do not use `pip install` globally anymore.** It installs packages into your system Python, which creates a mess over time. Use `uv add` inside a project instead (see below). - **Check what Python your terminal defaults to** at any time with `which python` or `python --version`. This tells you what runs when you type `python` without using `uv`. - **`uv` manages its own Python versions independently.** When you pin a project to Python 3.12, `uv` will download and use that version for the project regardless of what Python is installed system-wide. You don't need to uninstall anything. If you want to see all Python versions `uv` knows about: ```bash uv python list ``` --- ## Philosophy Each project subfolder gets its own fully isolated environment — its own packages, its own Python version. Nothing bleeds between projects. You always stay at the **repo root** for Git operations and use `--project` to point `uv` at the right subfolder. This way `git push`, `git pull`, and `git status` always work from one place. ``` INSTRUMENT-CONTROL/ ├── .gitignore ├── README.md ├── your-project-folder/ │ ├── pyproject.toml ← declares your dependencies (commit this) │ ├── uv.lock ← locks exact versions (commit this) │ ├── .python-version ← pins the Python version (commit this) │ └── .venv/ ← the actual environment (gitignore this) └── ptis-analysis/ ├── pyproject.toml ├── uv.lock ├── .python-version └── .venv/ ``` --- ## Which Python version to use Use **Python 3.12** for all new projects. Python 3.9 reached end-of-life in October 2025 and no longer receives security patches. Python 3.12 is fully supported by the entire scientific stack (numpy, scipy, matplotlib, pyvisa, etc.) and has meaningful performance improvements. Avoid the absolute latest release (e.g. 3.14) as some libraries take a few months to catch up. Pick one version and stick to it across all projects unless you have a specific reason not to — it makes debugging much simpler. --- ## Setting up a new project From the **repo root**, run: ```bash uv init --python 3.12 your-project-folder ``` This creates the following inside `your-project-folder/`: - `pyproject.toml` — the project's metadata and dependency list - `uv.lock` — a locked snapshot of the full dependency tree (including sub-dependencies) - `.python-version` — a one-line file that just says `3.12` The `.venv/` folder doesn't exist yet — it gets created the first time you add a package or run something. --- ## Adding packages ```bash uv --project your-project-folder add numpy matplotlib scipy ``` This does three things at once: updates `pyproject.toml` with the new dependencies, resolves the full dependency tree into `uv.lock`, and installs everything into `.venv/`. You never need to manually `pip install` anything. --- ## Running a script ```bash uv --project your-project-folder run python your-project-folder/measure.py ``` `uv run` automatically uses the project's environment — no need to activate anything first for one-off script runs. --- ## Running Jupyter First add Jupyter to the project: ```bash uv --project your-project-folder add jupyter ``` Then activate the environment and launch: ```bash source your-project-folder/.venv/bin/activate jupyter lab ``` You need to activate here (rather than use `uv run`) because Jupyter is an interactive session, not a one-off command. After activating, your terminal prompt will change to show `(.venv)` — this means all `python`, `jupyter`, and other commands will use the project's environment until you run `deactivate`. Create or open any `.ipynb` file inside `your-project-folder/` and it will have access to all the project's packages. Before committing notebooks, clear the outputs (`Kernel → Restart & Clear Output`) to keep your Git diffs clean. --- ## Checking the Python version To confirm which Python a project is using: ```bash uv --project your-project-folder run python --version # or just read the pin file directly: cat your-project-folder/.python-version ``` To change it: ```bash uv --project your-project-folder python pin 3.12 ``` --- ## The full daily workflow ```bash # Pull latest changes git pull # Add a new package uv --project your-project-folder add pyvisa # Run your script uv --project your-project-folder run python your-project-folder/measure.py # Commit the updated dependency files git add your-project-folder/pyproject.toml your-project-folder/uv.lock git commit -m "add pyvisa dependency" git push ``` --- ## Syncing after a clone or pull The `.venv/` folder is gitignored, so it doesn't exist on a fresh clone. `uv sync` recreates it from `uv.lock`: ```bash uv --project your-project-folder sync ``` You need this in two situations: 1. **Fresh clone on a new machine** — `.venv/` doesn't exist yet 2. **After a `git pull`** where someone else added a dependency — your `.venv/` is now out of date If you're the one adding packages with `uv add`, you never need to sync — it handles installation automatically. > **Mental model:** `uv.lock` is the source of truth that lives in Git. `.venv/` is a local build of it. `uv sync` = "rebuild `.venv/` from `uv.lock`." --- ## Why `.venv/` is not committed `.venv/` contains compiled binaries and symlinks built specifically for your OS and CPU. If you pushed it and pulled it on a different machine, it simply wouldn't work. It's also hundreds of MB — Git isn't designed for that. `pyproject.toml` and `uv.lock` together are everything needed to recreate the exact same environment anywhere: | File | Commit? | Why | |---|---|---| | `pyproject.toml` | ✅ Yes | Declares your dependencies | | `uv.lock` | ✅ Yes | Locks exact versions — guarantees reproducibility | | `.python-version` | ✅ Yes | Pins the Python version | | `.venv/` | ❌ No | Local build artifact — regenerated from the above | --- ## Quick reference | Task | Command | |---|---| | Create new project | `uv init --python 3.12 ` | | Add a package | `uv --project add ` | | Run a script | `uv --project run python