common-python-utils
Common Python utilities shared across Kaiano's projects.
Import namespace:
mini_app_polisNote: Import usingmini_app_polis(for example,from mini_app_polis.google import GoogleAPI)
Installation
Pin to a specific release tag in your pyproject.toml:
[tool.uv.sources]
mini_app_polis = { git = "https://github.com/mini-app-polis/common-python-utils", tag = "v1.0.0" }
[project]
dependencies = [
"mini_app_polis",
]
To use the LLM module, add the llm extra:
dependencies = [
"mini_app_polis[llm]",
]
Modules
| Module | Import | Description |
|---|---|---|
api/ |
from mini_app_polis.api import KaianoApiClient |
HTTP client for internal FastAPI services |
config.py |
from mini_app_polis import config |
Env-var driven shared config (Spotify, Google, VDJ) |
google/ |
from mini_app_polis.google import GoogleAPI |
Drive + Sheets facade |
llm/ |
from mini_app_polis.llm import build_llm, LLMMessage |
OpenAI + Anthropic clients (optional extra) |
mp3/ |
from mini_app_polis.mp3 import ... |
AcoustID identification, tagging, renaming |
music/ |
from mini_app_polis.music import normalize_for_matching |
Music data normalization utilities |
spotify/ |
from mini_app_polis.spotify import SpotifyAPI |
Spotipy wrapper |
vdj/ |
from mini_app_polis.vdj.m3u import ParseFacade |
VirtualDJ M3U parsing |
Usage
KaianoApiClient
from mini_app_polis.api import KaianoApiClient
# Set KAIANO_API_BASE_URL and this machine's own key
# (deejay-cog -> DEEJAY_COG_API_KEY) in the environment.
# The machine name is what selects the key variable, and the key is
# what names this caller to the API. There is no fallback: without it,
# every call returns 401.
client = KaianoApiClient.from_env("deejay-cog")
result = client.post("/sets", {"name": "My Set"})
LLM (requires llm extra)
from mini_app_polis.llm import build_llm, LLMMessage
llm = build_llm(provider="anthropic", model="claude-3-5-sonnet-20241022")
result = llm.generate_json(
messages=[
LLMMessage(role="system", content="Return JSON only."),
LLMMessage(role="user", content="Extract the artist and title."),
],
json_schema={
"type": "object",
"properties": {
"artist": {"type": "string"},
"title": {"type": "string"},
},
"required": ["artist", "title"],
},
)
print(result.output_json) # {"artist": "...", "title": "..."}
Logger
from mini_app_polis import logger
logger.info("Starting pipeline")
logger.error("Something went wrong: %s", err)
# Or get the logger instance directly
log = logger.get_logger()
Set LOGGING_LEVEL=INFO (or DEBUG/WARNING/ERROR) in your environment.
Development
Prerequisites
- uv — Python package manager
curl -LsSf https://astral.sh/uv/install.sh | sh
First-time setup
Clone the repo and run these commands once in order:
# 1. Install all dependencies including dev and optional extras
uv sync --all-extras
# 2. Install pre-commit hooks into git
uv run pre-commit install
That's it. Pre-commit will now run automatically on every git commit.
Daily workflow
# Run tests
uv run pytest
# Run tests with coverage detail
uv run pytest --cov=mini_app_polis --cov-report=term-missing
# Lint (auto-fix where possible)
uv run ruff check src/ tests/ --fix
# Format
uv run ruff format src/ tests/
# Type check
uv run mypy src/
# Run all pre-commit hooks manually against all files
uv run pre-commit run --all-files
What pre-commit does
On every git commit, the following run automatically:
- ruff — lints and auto-fixes what it can
- ruff-format — formats code
- python-check-mock-methods — catches incorrect mock usage
- python-use-type-annotations — flags old-style type comments
If any hook fails, the commit is blocked. Ruff will auto-fix in place — just git add . and re-commit.
Environment variables
No .env file is required to run tests. For local development against real services, copy .env.example to .env and fill in values:
cp .env.example .env
Key variables:
| Variable | Used by | Required for |
|---|---|---|
KAIANO_API_BASE_URL |
KaianoApiClient |
Calling internal FastAPI services |
<MACHINE_NAME>_API_KEY |
KaianoApiClient |
This machine's own named key, e.g. DEEJAY_COG_API_KEY. Derived from the machine name by machine_key_env_var() |
KAIANO_API_KEY |
KaianoApiClient |
Unnamed fallback for a caller that declares no machine name. Authenticates, but its writes are unattributable |
LOGGING_LEVEL |
logger |
Log verbosity (DEBUG default) |
GOOGLE_CREDENTIALS_JSON |
GoogleAPI |
Google Drive + Sheets access |
SPOTIPY_CLIENT_ID |
SpotifyAPI |
Spotify operations |
SPOTIPY_CLIENT_SECRET |
SpotifyAPI |
Spotify operations |
SPOTIPY_REFRESH_TOKEN |
SpotifyAPI |
Spotify operations |
ANTHROPIC_API_KEY |
llm extra |
Anthropic LLM calls |
OPENAI_API_KEY |
llm extra |
OpenAI LLM calls |
Releasing
Releases are automated via semantic-release on push to main.
| Commit format | Bump | Example result |
|---|---|---|
fix: ... |
patch | v1.0.0 -> v1.0.1 |
feat: ... |
minor | v1.0.0 -> v1.1.0 |
feat!: ... |
major | v1.0.0 -> v2.0.0 |
Important: Semantic-release only recognizes Conventional Commits format. These will NOT trigger a release:
breaking change: ...- unrecognized typefeat: breaking change ...- the word "breaking" in the message does not count- Free-form messages with no type prefix
For major bumps, prefer the BREAKING CHANGE footer in the commit body as it is more reliably parsed than feat!:
feat: your message here
BREAKING CHANGE: description of what changed and why it breaks
The footer format (BREAKING CHANGE: in the body) is the more battle-tested path across different versions of semantic-release. feat! should work per spec but has been known to behave inconsistently depending on plugin versions. Document both and lean on the footer.
Import Namespace
Use mini_app_polis as the import namespace across all modules (for example, from mini_app_polis.api import KaianoApiClient).
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 miniapppolis_common_utils-5.0.1.tar.gz.
File metadata
- Download URL: miniapppolis_common_utils-5.0.1.tar.gz
- Upload date:
- Size: 233.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbb109b7cd49afe97b5e62f7bdd4b17f7512ec0470f1a04411c29d72ab0bd67b
|
|
| MD5 |
6095058742d415dc4d6366c35880725b
|
|
| BLAKE2b-256 |
b3e10547e60ae88388ec85d975b8e723dc993cbeb4356c78963c54cb6cb89c0c
|
File details
Details for the file miniapppolis_common_utils-5.0.1-py3-none-any.whl.
File metadata
- Download URL: miniapppolis_common_utils-5.0.1-py3-none-any.whl
- Upload date:
- Size: 78.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9239b154f7684feec48a56df5ef54c74dabc1f044a8e37a63091fbf53873920b
|
|
| MD5 |
0eb80f19e159467ee372fc37c7308044
|
|
| BLAKE2b-256 |
69af3871224cdcca43d146652f41b1a7ef151ac773b0fd9d50582d747572d5e3
|