Repository containing howto guides for python development and shared linting and formatting settings using ruff and mypy.
- Using UV for Python Project & Environment Management
- Python formatting and linting
- Private GitHub dependencies in Docker builds
This guide explains how to install UV, set up a project, manage environments, and install dependencies using modern Python packaging practices.
UV is a fast Python package and environment manager from Astral. Install it using one of the options below.
brew install uvcurl -LsSf https://astral.sh/uv/install.sh | shUV can generate a clean project structure following modern Python packaging standards.
Create a new Python project (in this example we call the project cyclops-dev but this is arbitrary) managed by uv:
uv init cyclops-devs --packageTo specify a particular Python version:
uv init cyclops-devs --package --python=3.12After initialization, your folder will look like this:
cyclops-dev
βββ pyproject.toml
βββ README.md
βββ src
βββ cyclops_dev
βββ __init__.py
pyproject.tomlβ stores dependencies and project metadata.src/β contains your Python source code.
Keep notebooks separate from your package code to avoid mixing experimental work with production modules.
cyclops-dev
βββ notebooks
β βββ exploration.ipynb
βββ pyproject.toml
βββ README.md
βββ src
βββ cyclops_dev
βββ __init__.py
UV automatically creates an isolated environment and installs dependencies in one step. From your project root:
uv syncThis generates:
cyclops-dev
βββ .venv
βββ pyproject.toml
βββ README.md
βββ src
This will:
-
Create a
.venv/folder -
Install dependencies from
pyproject.toml
Activate the environment:
source .venv/bin/activateAdd all the required dependency needed to deploy and run your code. This excludes development dependencies like jupyterlab and potentially matplotlib. These development dependencies might be used for research and development but are not required when deploying the code.
uv add numpyCreate a separate dependency list for additional dependencies that are only needed for your development environment.
uv add jupyterlab --devIn case you want migrate your dependencies from a requirements.txt file (used by pip) to a pyproject.toml (used by uv), you can use the following command:
uv add -r requirements.txtuv automatically updates your pyproject.toml with these dependencies.
Configuring Ruff locally allows developers to catch and resolve issues in real-time as they write code. Ruff is used for code formatting. Below the steps to setup Ruff for local development.
-
Install Ruff: Here we assume that you are in a repository that already contains the
pyproject.tomlfile withruffandmypylisted as dependencies. First thing to do is sync your environment so the dependencies are installed in your local virutal environment:uv sync
In case ruff and mypy are not listed as dependencies you can add them the
lintinggroup:uv add --group linting ruff mypy
-
Copy the Ruff configuration file: The Ruff formatting rules are specified in the
ruff.tomlfile in this repository (python-dev-standards). Copy theruff.tomlfile from this repository to root directory of your own repository.
You can run Ruff from your terminal to format code or check for issues.
- Format the entire project:
ruff format . - Format a specific folder:
ruff format path/to/your/folder/
- Format a single file:
ruff format path/to/your/file.py
- Check the entire project and apply automatic fixes:
ruff check . --fix - Check a specific folder (without fixing):
ruff check path/to/your/folder/
- Check a single file (without fixing):
ruff check path/to/your/file.py
-
Install the Ruff Extension: Install the official Ruff extension from the VS Code Marketplace.
-
Configure VS Code Settings: Create or open the
.vscode/settings.jsonfile in your project's root directory and add the following configuration. This ensures everyone on the team uses the same settings for this project.{ // Enable format on save for all files "editor.formatOnSave": true, // Set Ruff as the default formatter for Python files "[python]": { "editor.defaultFormatter": "charliermarsh.ruff" }, // Run Ruff's "fixAll" and "organizeImports" actions on save. // This applies linting fixes before formatting. "editor.codeActionsOnSave": { "source.fixAll": "explicit", "source.organizeImports": "explicit" } }
This project uses Mypy for static type checking, which helps ensure type safety and prevent common bugs. The configuration is defined in the mypy.ini file. Copy the mypyp.ini file to your repositories root directory.
All linting dependencies and stubs for mypy (types-* packages), are managed in the pyproject.toml file under the [dependency-groups.linting] group.
To add a new type stub for a library (e.g., requests) to the linting group, use the uv add command:
uv add types-requests --group lintingThis will automatically update your pyproject.toml file.
To run mypy:
- Check the entire project:
mypy . - Check a specific folder:
mypy path/to/your/folder/
- Check a single file:
mypy path/to/your/file.py
- Install the Mypy Type Checker Extension: Install the official Mypy Type Checker extension from the VS Code Marketplace.
Our repositories use pre-commit to automatically check code formatting and type annotations before committing changes. The hooks configured in the .pre-commit-config.yaml file ensure that:
- Ruff runs to check for code style issues and automatically fix them.
- Mypy checks for type annotations and ensures type safety.
-
Install Pre-commit: If you haven't already, install the
pre-commitpackage:pip install pre-commit
-
Install the Hooks: Run the following command to install the hooks defined in your
.pre-commit-config.yaml:pre-commit install
Once the hooks are installed, they will automatically run every time you attempt to commit changes. This means you don't have to manually run ruff or mypyβthe pre-commit hooks will handle it for you. If there are any issues found by Ruff or Mypy, the commit will be blocked until those issues are resolved.
If you want to run the hooks manually at any time, you can do so with the following commands:
-
Run all hooks on all files:
pre-commit run --all-files
-
Run a specific hook (e.g., Mypy):
pre-commit run mypy --all-files
-
Run a specific hook (e.g., Ruff):
pre-commit run ruff --all-files
Projects that call the reusable deploy-prefect-flow.yml workflow can install private org git dependencies during Docker image builds using a GitHub App token β no SSH deploy keys required.
Configure these at the GitHub organization level:
- Variable
GH_APP_IDβ GitHub App ID - Secret
GH_APP_PRIVATE_KEYβ GitHub App private key (PEM) - GitHub App installed org-wide with Contents: Read access to private repositories
After all consumer repos are migrated, retire the legacy DEPLOY_SSH_KEY secret.
Keep private git dependencies as normal HTTPS or SSH URLs. Do not commit tokens or placeholders β the Dockerfile injects authentication at build time.
[project]
dependencies = ["my-private-lib"]
[tool.uv.sources]
my-private-lib = { git = "https://github.com/org/my-private-lib.git", tag = "v1.0.0" }
# or, if migrating from SSH:
# my-private-lib = { git = "ssh://git@github.com/org/my-private-lib.git", tag = "v1.0.0" }Local development continues to work via SSH keys or gh auth setup-git; CI and Docker use the build-secret pattern below.
Replace RUN --mount=type=ssh (and any openssh-client / known_hosts setup) with:
# syntax=docker/dockerfile:1
COPY pyproject.toml .
RUN --mount=type=secret,id=github_token \
GITHUB_TOKEN=$(cat /run/secrets/github_token) && \
sed -i "s|ssh://git@github.com/|https://x-access-token:${GITHUB_TOKEN}@github.com/|g" pyproject.toml && \
sed -i "s|https://github.com/|https://x-access-token:${GITHUB_TOKEN}@github.com/|g" pyproject.toml && \
uv pip install --system -r pyproject.tomlNotes:
- The
github_tokenbuild secret is injected bydeploy-prefect-flow.yml; consumer repos do not define it in their own workflows. GITHUB_TOKENis a shell variable scoped to that singleRUNstep; the secret file is never copied into the image.- The two
sedcommands cover both legacy SSH git URLs and plain HTTPS URLs inpyproject.toml. - Set
ENV UV_SYSTEM_PYTHON=1(or use--systemas shown) when installing into the system Python in slim base images. - Copy
pyproject.tomlbefore this step; avoid re-copying an unmodifiedpyproject.tomlover the sed-modified file in later layers if subsequent install steps depend on authenticated URLs.
If a project uses uv sync instead of uv pip install, apply the same --mount=type=secret,id=github_token + sed pattern in the same RUN as the sync command (sed first, then uv sync).
For each repo calling deploy-prefect-flow.yml:
- Ensure private git deps in
pyproject.tomlusehttps://github.com/...orssh://git@github.com/...(no embedded tokens). - Update
deployment/Dockerfile: drop SSH mounts; add the sed +uv pip install --system -r pyproject.tomlpattern (or sed +uv syncif that is what the Dockerfile uses). - Re-run the deploy workflow to verify the image build succeeds.
- Remove per-repo deploy keys /
DEPLOY_SSH_KEYonce all repos are migrated.