Setup#
Python is on every modern Linux distribution. The operator’s setup problem is rarely “install Python”, it is picking the right Python, getting it on PATH without touching the system interpreter, and standing up a project you can ship.
This page walks the two stages the operator actually does on a fresh host, install Python and set up the project.
Install#
Pick an interpreter, install it under the operator’s account, and leave the system Python alone.
1. Pick the interpreter. CPython 3.10 or later is the default. PyPy is for long-running CPU-bound services where the operator cannot rewrite the hot path. MicroPython is for embedded targets.
2. Install it with a version manager. uv python is the
operator’s default. It installs a managed interpreter under
~/.local/share/uv/python/ and never touches /usr/bin/.
pyenv is the fallback when uv is not allowed on the host.
Version Manager |
Command |
|---|---|
uv |
|
pyenv |
|
mise |
|
asdf |
|
$ uv python install 3.12
$ uv python list
$ pyenv install 3.12.4
$ pyenv global 3.12.4
3. Verify.
$ which python
$ python --version
/home/operator/.local/share/uv/python/cpython-3.12.4-linux-x86_64-gnu/bin/python
Python 3.12.4
Never replace /usr/bin/python3. The distribution depends on it.
Setup project#
With Python installed, stand up the project.
1. Bootstrap.
$ mkdir operator-tool && cd operator-tool
$ uv init
$ uv venv
$ source .venv/bin/activate
uv init writes pyproject.toml and .python-version.
uv venv creates .venv/ against the version pinned in the
project.
2. Add dependencies. Runtime first, dev next.
$ uv add requests click
$ uv add --dev pytest ruff mypy
This updates pyproject.toml and writes uv.lock with hash
digests for every transitive dependency.
3. Lay out the source. The src/ layout keeps imports
honest (the package is not importable until it is installed).
operator-tool/
├── pyproject.toml
├── .python-version
├── uv.lock
├── README.md
├── .gitignore
├── src/
│ └── operator_tool/
│ ├── __init__.py
│ └── main.py
└── tests/
└── test_main.py
4. Lock and sync. Lockfiles pin every transitive dependency
to a hash so uv sync reproduces the environment exactly on
every host.
$ uv lock
$ uv sync
Commit pyproject.toml and uv.lock. Re-audit the lockfile
on every dependency bump.
5. Run.
$ uv run python -m operator_tool
$ uv run pytest
Warning
PyPI is a supply-chain attack surface. Anyone can publish a package with any name. The operator must assume the registry is adversarial.
Typosquatting,
rqeusts/request3/requests-instead ofrequests. Type the name once and copy from there.Dependency confusion, attacker publishes a public package with the same name as one of the operator’s private internal packages; the resolver picks the public one. Pin an index URL or vendor internal packages.
Compromised maintainers, legitimate package gets a malicious release pushed (event-stream, ua-parser-js, ctx, and others). Pin versions; review changelogs before bumping.
Post-install hooks,
setup.pyruns arbitrary Python during install, including in CI. Prefer wheel-only installs (--only-binary=:all:).
Mitigations. Pin with hash digests in lockfiles. Audit with
pip-audit, safety, or trivy fs. Sandbox untrusted
packages in containers or fresh venvs.
Distro gotchas#
What bites the operator on each distro when installing Python.
Distro |
Gotcha |
|---|---|
Ubuntu / Debian |
System |
Fedora / RHEL |
Same PEP 668 protection. |
Alpine |
musl libc, not glibc. Many wheels are glibc-only and
compile from source on Alpine. Use |
macOS |
System Python 3 lags. |
Air-gapped install#
When the target has no internet, package wheels on the operator’s box and copy them across.
# On the connected box
$ uv pip download -r requirements.txt --dest ./wheels
$ pip download -r requirements.txt -d ./wheels \
--platform manylinux2014_x86_64 --only-binary=:all:
# On the air-gapped box
$ uv pip install --no-index --find-links ./wheels -r requirements.txt
Match the target’s platform tag (manylinux2014_x86_64,
musllinux, and so on) when downloading or the wheels will be
skipped.
Common Tasks#
Install a specific Python version.
$ uv python install 3.12
Bootstrap a new project in the current directory.
$ uv init
$ uv venv
$ uv add requests pytest
Pin and lock dependencies.
$ uv lock
$ uv sync
Run a script in an ephemeral environment.
$ uv run --with requests script.py
Install a CLI tool globally without polluting any venv.
$ pipx install ruff
$ pipx install httpie
Inspect what is installed in the active venv.
$ uv pip list
$ uv pip show requests
$ python -m pip freeze
Audit dependencies for known CVEs.
$ uv pip install pip-audit
$ pip-audit