Enforce proper dependency declaration in shared Python environments
Project description
Depyty
/ˈdɛpaɪti/ as in the english word "deputy", but for Python dependencies.
Enforce proper dependency declaration in shared Python environments.
Usage
Install the package using
uv add --dev depyty
then run it via
uv run depyty "packages/*"
You can pass glob.glob strings that lead to all pyproject.toml files in your repository that you want to check.
Note the use of "s to prevent your shell from expanding the references.
Example
Given the following pyproject.toml file
name = "my-awesome-api"
version = "1.2.3"
dependencies = ["fastapi"]
from fastapi import FastAPI
✅ This is fine, we explicitly declared this module as a dependency
import pandas as pd
❌ This will very likely fail at runtime.
We have not declared a dependency on pandas in our pyproject.toml file.
from pydantic import BaseModel
⚠️ This is also not allowed, even though it will likely not crash at runtime.
Pydantic will be installed, as it is a dependency of fastapi.
However, since you imported it in your code, you'll likely also depend on Pydantics API.
If they decide to publish a breaking change, and FastAPI increments their minimum Pydantic version to the new one, your code can easily break.
By explicitly declaring a version and dependency upon pydantic in your pyproject.toml, you are in full control.
Motivation
Tools like uv make it very convenient to create monorepos.
Scenario
Imagine this hypothetical scenario, where our main application is a REST API, and we have a few lambdas that do auxiliary tasks. All of them live in a shared monorepo:
depyty-demo-api
├── lambdas
│ ├── cleanup-old-db-entries-lambda
│ │ ├── main.py
│ │ └── pyproject.toml
│ └── data-warehouse-export-lambda
│ ├── main.py
│ └── pyproject.toml
├── packages
│ └── demo-database-models
│ ├── pyproject.toml
│ └── src
│ └── demo_database_models
│ ├── __init__.py
│ └── py.typed
├── pyproject.toml
└── src
└── depyty_demo_api
├── __init__.py
└── api.py
The lambdas may execute tasks like cleaning up database tables, or exporting them to S3.
They contain dependencies (e.g. boto3 to interact with S3), that are not needed in your main application, so you declare them separately.
At the same time, they share code with your main application, e.g. the database models.
The pyproject.toml files would then look like this for the main application
# pyproject.toml
name = "depyty-demo-api"
dependencies = [
"demo-database-models",
"fastapi>=0.115.12",
]
[dependency-groups]
dev = ["pytest>=8.3.5"]
[tool.uv.workspace]
members = ["packages/*", "lambdas/*"]
[tool.uv.sources]
demo-database-models = { workspace = true }
and like this for a lambda
# lambdas/data-warehouse-export-lambda/pyproject.toml
[project]
name = "data-warehouse-export-lambda"
dependencies = [
"boto3>=1.37.23",
"demo-database-models",
]
[tool.uv.sources]
demo-database-models = { workspace = true }
Again, we don't want to inlcude boto3 in our main application, so during deployment we run
uv sync
for our main application and
uv sync --package data-warehouse-export-lambda
for the exporter lambda.
Problem
Imagine you add a new feature, which you decide to develop in a new library under packages/my-new-utility-package, since it contains very generic functionality.
Once its done, you run uv add at the project root and integrate it into the main application.
The next day, your intuition turns out to be right, and you need the functionality in one of the lambdas.
You immediately adjust the source code, your IDE happily autocompletes everything.
You also write unit tests, run pytest, and everything works like a charm.
Then you push everything, and everything fails ✨spectacularly✨.
Turns out you did not run uv add my-new-utility-package for your lambda, so it was never included as a dependency in its pyproject.toml.
Since you use uv sync --all-packages during local development, the dependencies of all packages are available locally.
But during deployment, you only install everything listed in each pyproject.toml.
Solution
This is exactly what depyty solves.
It analyzes the packages available in the current environment, and checks the source files of each provided package, that it only imports modules that are also declared in its pyproject.toml.
To prevent an error like this, we'd just need to add
uv run depyty "lambdas/*" "."
to our CI and we should not get nasty surprises when deploying the next time. At least not due to unspecified dependencies.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file depyty-0.2.0.tar.gz.
File metadata
- Download URL: depyty-0.2.0.tar.gz
- Upload date:
- Size: 17.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bea56fec94bb89a582c12bf42287337a6f6dec1b034f30e59fcbef8084969de9
|
|
| MD5 |
fe382509f733381b69acb555e54da429
|
|
| BLAKE2b-256 |
0352785c15d6ecf69eff4c3d89635bc91b94dbbe4e3353312026c3e97f508941
|
File details
Details for the file depyty-0.2.0-py3-none-any.whl.
File metadata
- Download URL: depyty-0.2.0-py3-none-any.whl
- Upload date:
- Size: 18.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
009326a9a294f99fcf618070dec7247c3077a2f55068cb0a8f656b23bae7e2a9
|
|
| MD5 |
e36ac6d1266897ba16cb784c8f3353a6
|
|
| BLAKE2b-256 |
3b61c80816f46307f0e955443a24367ecc587f1f7ed534fccdd6fed013f437ec
|