Posted on
Artificial Intelligence

Artificial Intelligence Python Best Practices

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

AI in Python on Linux: Bash-first Best Practices That Prevent “Works on My Machine” Moments

You can train a model that beats a benchmark—but if your coworkers can’t reproduce your results or deploy your code, the win won’t ship. This post shows you how to build AI/ML Python projects on Linux the way seasoned engineers do: reproducible environments, pinned dependencies, automated quality checks, and simple Bash-first workflows.

You’ll leave with copy-pasteable commands that work on Debian/Ubuntu, Fedora/RHEL, and openSUSE, and a minimal project template you can adapt today.

Why this matters

  • Reproducibility beats heroics: locked dependencies and scripted setups avoid “it runs only on my laptop.”

  • Speed with confidence: linters, formatters, and tests catch issues before you burn time on long training jobs.

  • Collaboration at scale: a predictable layout and one-command setup lowers the barrier for teammates and CI.


Prerequisites: install system packages

Install Python, git, build tools (for packages with native extensions), and pipx (to manage CLI tools cleanly).

  • Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y python3 python3-pip python3-venv pipx git build-essential pkg-config python3-dev
pipx ensurepath
  • Fedora/RHEL (dnf):
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y python3 python3-pip pipx git python3-devel pkgconf-pkg-config
pipx ensurepath
  • openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y python3 python3-pip python3-venv pipx git gcc gcc-c++ make pkg-config python3-devel
pipx ensurepath

Open a new shell if pipx was just installed so ~/.local/bin is on PATH.


1) Isolate and script your environment

Use venv for isolation and a tiny Bash script so anyone can set up and run the project the same way.

  • Create a project layout:
mkdir -p ai-project/{src/ai_project,tests}
cd ai-project
git init
  • Minimal run script (create venv if missing, install locked deps, run):
cat > run.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail

VENV="${VENV:-.venv}"
PY="${PYTHON:-python3}"

if [ ! -d "$VENV" ]; then
  "$PY" -m venv "$VENV"
  "$VENV/bin/python" -m pip install --upgrade pip wheel
fi

if [ -f requirements.txt ]; then
  "$VENV/bin/pip" install -r requirements.txt
fi

exec "$VENV/bin/python" -m ai_project.main "$@"
EOF
chmod +x run.sh
  • A tiny main program:
cat > src/ai_project/main.py << 'EOF'
def main():
    print("Hello, reproducible AI world!")

if __name__ == "__main__":
    main()
EOF
  • Make it importable:
echo "__version__ = '0.1.0'" > src/ai_project/__init__.py

Run it:

./run.sh

Why this helps:

  • venv avoids polluting system Python.

  • One script means fewer “what did you do to set this up?” Slack messages.


2) Pin and lock dependencies

Use pip-tools to manage exact versions for deterministic installs.

  • Install pip-tools once with pipx:
pipx install pip-tools
  • Declare top-level deps (unpinned) in requirements.in:
cat > requirements.in << 'EOF'
numpy
pandas
scikit-learn
EOF
  • Compile a fully pinned lockfile:
pip-compile --generate-hashes -o requirements.txt requirements.in
  • Sync your venv to match the lockfile exactly:
./.venv/bin/python -m pip install --upgrade pip
./.venv/bin/pip install --require-hashes -r requirements.txt || ./run.sh

(Hint: if .venv doesn’t exist yet, ./run.sh will create it, then you can rerun the pip install command. Or just rely on run.sh to install from requirements.txt automatically.)

  • For development tools, keep them separate:
cat > requirements-dev.in << 'EOF'
pytest
pytest-cov
mypy
EOF

pip-compile --generate-hashes -o requirements-dev.txt requirements-dev.in
  • Install dev-only deps when you need them:
./.venv/bin/pip install --require-hashes -r requirements-dev.txt

Why this helps:

  • requirements.in stays human-curated, requirements.txt is machine-pinned.

  • Hashes protect against supply-chain tampering.

Alternative: Prefer Poetry? Install via pipx and use pyproject.toml:

pipx install poetry
poetry init -n
poetry add numpy pandas scikit-learn
poetry lock
poetry run python -m ai_project.main

3) Automate code quality (ruff, black, mypy) with pre-commit

  • Install CLI tools with pipx (keeps them isolated from your project env):
pipx install ruff
pipx install black
pipx install mypy
pipx install pre-commit
  • Add a pre-commit config:
cat > .pre-commit-config.yaml << 'EOF'
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.5.5
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format
  - repo: https://github.com/psf/black
    rev: 24.4.2
    hooks:
      - id: black
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.10.0
    hooks:
      - id: mypy
        additional_dependencies: [types-requests]
EOF
  • Activate hooks:
pre-commit install
pre-commit run --all-files

Why this helps:

  • Fast feedback on style and correctness before you push.

  • The same checks run locally and in CI.


4) Test the parts that matter

  • Add a simple test:
cat > tests/test_main.py << 'EOF'
from ai_project.main import main

def test_main_prints(capsys):
    main()
    out, _ = capsys.readouterr()
    assert "Hello, reproducible AI world!" in out
EOF
  • Run tests:
./.venv/bin/pytest -q
  • Add coverage in CI runs later:
./.venv/bin/pytest --cov=ai_project --cov-report=term-missing

Why this helps:

  • Quick, focused tests catch refactors that break training or inference.

5) Reproducible experiments: config + seeding + provenance logs

  • Centralize randomness control:
cat > src/ai_project/repro.py << 'EOF'
import os, random
import numpy as np

def seed_everything(seed: int = 42):
    os.environ["PYTHONHASHSEED"] = str(seed)
    random.seed(seed)
    np.random.seed(seed)
    try:
        import torch
        torch.manual_seed(seed)
        torch.cuda.manual_seed_all(seed)
        torch.use_deterministic_algorithms(True)  # PyTorch 2.0+
    except Exception:
        pass
EOF
  • Minimal config pattern with argparse + YAML:
cat > src/ai_project/config.py << 'EOF'
import argparse, yaml
from pathlib import Path

def load_config():
    p = argparse.ArgumentParser()
    p.add_argument("--config", type=Path, default=Path("config.yaml"))
    args = p.parse_args()
    with args.config.open() as f:
        return yaml.safe_load(f)
EOF
  • Example config:
cat > config.yaml << 'EOF'
seed: 2024
model:
  type: "RandomForestClassifier"
  n_estimators: 200
data:
  path: "data/train.csv"
EOF
  • Wire it into main.py:
cat > src/ai_project/main.py << 'EOF'
from .repro import seed_everything
from .config import load_config

def main():
    cfg = load_config()
    seed_everything(cfg.get("seed", 42))
    print(f"Config: {cfg}")
    print("Hello, reproducible AI world!")

if __name__ == "__main__":
    main()
EOF
  • Log provenance (git commit + package versions):
cat > src/ai_project/provenance.py << 'EOF'
import subprocess, sys

def git_commit():
    try:
        return subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip()
    except Exception:
        return "unknown"

def pip_freeze():
    try:
        return subprocess.check_output([sys.executable, "-m", "pip", "freeze"], text=True)
    except Exception:
        return ""
EOF

Use it before/after training to write a small run.json or append to a log file. Even this lightweight logging makes debugging and papers/notes reproducible.


Real-world example: upgrading numpy safely

  • You want a newer numpy. Instead of pip install -U numpy (which can break dependencies):
    1. Edit requirements.in and bump the constraint, e.g., numpy>=2.0,<2.1
    2. Recompile and test:
pip-compile --upgrade --generate-hashes -o requirements.txt requirements.in
./.venv/bin/pip sync -r requirements.txt
pre-commit run --all-files
./.venv/bin/pytest -q
  1. Commit the updated requirements.txt with a clear message.

This keeps your team and CI on the exact same versions.


Optional: Python versions with pyenv

If you need multiple Python versions, install pyenv. It’s source-based, so you’ll need build deps (already installed above). Then:

curl https://pyenv.run | bash
# Add init lines to your shell (per installer output), then:
exec "$SHELL"
pyenv install 3.11.9
pyenv local 3.11.9
python -m venv .venv && . .venv/bin/activate
python -m pip install --upgrade pip wheel

This keeps your runtime aligned with production or specific libraries.


Conclusion and next step (CTA)

AI success isn’t just about models—it’s about shipping reliable, reproducible code. You now have:

  • A Bash-first workflow that bootstraps itself

  • Locked dependencies with pip-tools

  • Automated quality gates with ruff/black/mypy/pre-commit

  • Seeds, configs, and provenance for reproducible experiments

Next step: turn one of your existing notebooks into a script in src/ and wire it to run.sh. Commit a requirements.in, compile, and run tests. If you get stuck, start a fresh repo with the snippets above and migrate code piece by piece.

Your future self—and your teammates—will thank you.