uv Ecosystem — Package Manager Best Practices
Entrypoints, tool isolation, packaging hygiene, and anti-patterns for Python projects managed with uv. Distilled from Pokemon TCG MLX migration and other projects.
uv Ecosystem — Best Practices
Dense reference for Python packaging and tooling via uv. Every rule here was learned the hard way.
The Golden Rule
If you are writing pip install, python script.py, or requirements.txt — stop. Use uv.
If you are writing PYTHONPATH=. — stop. Fix the project structure instead.
Derived from preferencias-tecnicas (the directive that mandates uv). Crystallized during the pokemon_tcg_mlx_migration project.
Core Principles
1. uv run Is the Universal Runner
Never invoke python or python3 directly. Always route through uv.
# Bad
python script.py
python3 -m scripts.bc.bc_train
# Good
uv run python script.py
uv run --env-file .env script.py
uv run my-command
uv run --with ruff ruff check .
uv resolves the venv, Python version, and dependencies from pyproject.toml automatically. Direct python bypasses all of that.
2. [project.scripts] Entry Points Over PYTHONPATH
PYTHONPATH=. is a code smell. It means the project structure is wrong. Fix it with proper packaging.
In pyproject.toml:
[project.scripts]
bc-train = "scripts.bc.bc_train:main"
evaluate = "scripts.evaluate:main"
build-dataset = "scripts.bc.build_bc_dataset:main"
Then:
uv run bc-train data/bc_data/bc_2026_07_21 --d-model 128
uv run evaluate -n 50
This works from any directory, no path hacking required. After editing pyproject.toml, always run uv sync to register the entry points.
3. uv tool install for Standalone CLI Tools
Global CLI tools (like kaggle, ruff, gh) should be installed via uv tool install, not pip install. This isolates them from the project venv.
uv tool install kaggle
uv tool install ruff
Each tool gets its own isolated environment. No venv pollution, no version conflicts.
4. uvx for One-Off Tool Runs
Run a tool without installing it permanently:
uvx ruff check .
uvx mypy src/
uvx pytest tests/
Equivalent to uv run --with <tool> <tool> but shorter. Use for tools you do not need continuously.
5. uv add vs uv pip install
| Operation | Command | Effect |
|---|---|---|
| Project dependency | uv add numpy | Modifies pyproject.toml + locks |
| Dev dependency | uv add --dev pytest | Adds to [dependency-groups] |
| Temp install | uv pip install numpy | Installed in venv only, no lock change |
| Install from lock | uv sync | Installs everything from lockfile |
Rule: for project dependencies, always uv add. uv pip install is for temporary experiments only.
6. uv sync After Changing pyproject.toml
Entry points, new deps, and version changes only take effect after:
uv sync
This is the step people forget. If uv run my-command says "command not found", you forgot to sync.
Anti-Patterns
PYTHONPATH Hacks
# BAD — this means the project is not a proper Python package
PYTHONPATH=. python scripts/bc/bc_train.py
Fix chain:
- Add
__init__.pyto every importable directory (scripts/,scripts/bc/,rl/, etc.) - Add
[project.scripts]entry point inpyproject.toml - Use relative imports within the package
- Run via
uv run <entrypoint>
Legitimate exception: Kaggle sandbox where __file__ is not defined. That is a runtime constraint, not a hack.
sys.path.insert(0, ...)
# BAD
import sys
sys.path.insert(0, os.path.dirname(__file__))
Same fix chain as PYTHONPATH. If you see this in code, the package structure is broken.
Direct python/python3 Invocation
# BAD
python3 -u scripts/bc/bc_train.py data/bc_data/bc_2026_07_21
# GOOD
uv run python -u scripts/bc/bc_train.py data/bc_data/bc_2026_07_21
Direct invocation bypasses the uv-managed venv and Python version.
Package Structure Checklist
Every Python directory that contains importable modules must have __init__.py:
project/
├── pyproject.toml
├── src/
│ └── mypackage/
│ ├── __init__.py
│ ├── module_a.py
│ └── subpackage/
│ ├── __init__.py
│ └── module_b.py
└── scripts/
├── __init__.py # if scripts are importable
└── bc/
├── __init__.py
└── bc_train.py
Directories without __init__.py are not packages. They cannot be imported. PYTHONPATH tricks do not make them proper packages; they just paper over the structural problem.
Subagent Instructions
When spawning subagents for Python tasks, explicitly instruct them to use uv run in every command. Example:
"Use
uv runfor every Python command in this project. Never invokepythonorpython3directly."
This prevents subagents from falling back to system Python or activating the wrong venv.
pyproject.toml Template
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"numpy>=2.0",
]
[project.optional-dependencies]
dev = ["pytest", "ruff"]
[project.scripts]
my-command = "mypackage.module:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Key points:
requires-pythonpins the minimum Python version- Entry points in
[project.scripts] - Dev deps in
[project.optional-dependencies]or[dependency-groups] - No
setup.py, nosetup.cfg, norequirements.txt
Checkpoint File Handling
For ML projects with large binary files (.npy, .pkl, .pt, .safetensors):
- Do not track them in git (add to
.gitignore) - Use
uvfor Python tooling only; binary data lives outside the package manager - For checkpoint loading/saving, prefer pickle (
.pkl) overnp.savezwhen working with MLX (avoids flatten/unflatten name mismatch issues) - Document checkpoint format in code comments or architecture docs
Related: pokemon_tcg_mlx_migration (checkpoint format decisions).
Quick Reference
| Task | Command |
|---|---|
| Run a script | uv run script.py |
| Run a project command | uv run my-command |
| Run a one-off tool | uvx ruff check . |
| Install a project dep | uv add numpy |
| Install a global CLI | uv tool install kaggle |
| Sync after toml change | uv sync |
| Run with env file | uv run --env-file .env script.py |
| Run tests | uv run pytest |
| Format code | uv run ruff format . |
| Lint code | uv run ruff check . |
Related Pages
- preferencias-tecnicas — The directive that mandates uv as package manager
- pokemon_tcg_mlx_migration — Project where PYTHONPATH hacks were eliminated
- editorial-workflow — Wikifita editorial rules
- co-fita-infrastructure — Docker + uv patterns in Co-Fita stack