Utilities
A lightweight toolkit for the plumbing every project rewrites: where settings live, how logs are set up, what a batch shows while it runs, and where the time goes. Four independent areas, one shared folder on the machine.
pip install utilities-toolkit
# or
uv add utilities-toolkit
Charts need an extra: pip install "utilities-toolkit[plot]".
What is in it
| Configuration | INI, JSON, YAML and Python settings under one root, reached by attribute access. |
| Logging | One call sets up a Rich console, rotating files, and three custom levels. |
| Frames & sparklines | Put a distribution or a DataFrame in the log, readable over SSH. |
| Terminal charts | plotext charts at their own log level. No image, no artefact. |
| Probing | Latency and memory instrumentation that costs nothing when off. |
Full API index and cost tables: docs/.
Configuration
from utilities import Config
Config.ensure_initialized("my_project", {"PATHS": {"DATA": "/mnt/data"}})
value = Config.my_project.PATHS.DATA # case-insensitive throughout
Files live in ~/utilities/config/ (C:/utilities/config on Windows), so
several projects on the same machine share one place instead of scattering
dotfiles. INI, JSON, YAML and .py are all read the same way.
→ docs/config.md
Logging
from utilities import LoggingConfigurator
LoggingConfigurator.configure(project="my_project", level="INFO")
Rich console, a daily-rotating my_project.log, optional JSONL, Rich tracebacks.
Idempotent: calling it twice cannot stack handlers. Overridable from the
environment (LOG_LEVEL, LOG_DIR, LOG_JSON…), and it understands the custom
level names below.
LoggingConfigurator.watch(project="my_project", module=__name__, level="DEBUG")
adds a dedicated file for one module without disturbing anything else. → docs/logging.md
Three custom levels
6 PLOT ~1.7 ms per chart
8 DATAFRAME ~80 µs to ~340 µs
10 DEBUG
15 PERF ~1.15 µs per probe
20 INFO
Ordered by increasing cost as you go down: lowering the level means agreeing to
pay more. Because levels are ordered, PLOT also emits DataFrames — to get one
without the other, use logger namespaces (myapp.data, myapp.plot). Every
helper takes the logger as its first argument so that choice stays yours.
The package never adds a handler and never touches the host's logging configuration.
Sparklines and DataFrames in the log
Start with spark(). ~12 µs for 300 points, no dependency, one line, and
still greppable — 145× cheaper than a chart.
from utilities import spark, log_frame
log.info("nav n=%d %s [%.2f, %.2f]", len(nav), spark(nav), min(nav), max(nav))
nav n=300 ▂▂▂▂▂▂▁▂▃▃▃▅▅▄▅▆▆▆▇██▇▆▆ [96.32, 120.61]
log_frame(data_logger, positions, mode="describe", label="positions")
Four modes — head, tail, full, describe — with a hard row cap on full,
scoped display settings, and a LazyFrame guard that logs the query plan instead
of ever calling collect(). polars and pandas are supported but never imported.
A multi-line record is no longer greppable — that trade-off is the reason
spark() comes first.
→ docs/frames.md
Terminal charts
from utilities.log.plots import plot_line
plot_line(plot_logger, nav, title="NAV")
NAV
┌─────────────────────────────────────────────────────────────────┐
120.6┤ ▐▙ ▄▖▄ │
│ ▄▟ ▌▐▙▞▀▜ ▛▖ │
For a nightly batch whose log is read over SSH from a phone: no image, no
artefact to fetch, the log is the only surface. Needs console_width=120 to
avoid being wrapped — why.
→ docs/plots.md
Probing
from utilities import probed
from utilities.probe import probe, report
@probed()
def fetch_rows(cursor, sql):
cursor.execute(sql)
return cursor.fetchall()
with probe("materialize"):
rows = [dict(r) for r in raw]
PROBE_TIER=1 python my_script.py
label n wall self wait mean p95
-------------------------------------------------------------------------------------------------
db.execute 4 4.310s 4.310s 4.290s 1077.50ms 1131.02ms
transform 1 0.412s 0.412s 0.001s 412.30ms 412.30ms
wait = wall - cpu— the column that decides what to do next. Close towall, you are waiting on a database or a network share and rewriting Python will gain you nothing. Close to zero, the code is the problem.self = wall - children— cProfile'stottime. A function that only delegates tops thewallranking while being innocent.
Zero cost when off: at PROBE_TIER=0, probed() returns your function
unchanged — 0.0 ns measured, no wrapper, not even a flag test. That is what makes
it safe to leave in production code. The tier is read once at import and cannot
be changed at runtime; that is the price of the guarantee.
Only instrument blocks of 400 µs or more, and never read latency from a
PROBE_TIER=2 run — tracemalloc slows the whole process 11×.
→ docs/probe.md
probe tells you which block costs. To find out why, inside it:
uv run --with pyinstrument pyinstrument my_script.py
Examples
Every script in examples/ runs on its own with no arguments.
examples/probe/— the three tiers side by side, the overhead measured on your machine, CPU vs I/O, nesting, asyncio, output formatsexamples/log/— levels and namespaces, the four frame modes, the four charts, costs, three traps, and a batch as it reads in atail -f
Two playgrounds exist as both a notebook and a # %%-cell script:
log_playground and
probe_playground.
Structure
src/utilities/
├── config/ configuration store
├── log/ setup, levels, frames, plots, helpers
├── probe/ latency and memory instrumentation
└── utils/ folder helpers
docs/ full documentation
examples/ runnable demos and notebooks
tests/ test suite
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 utilities_toolkit-1.0.0.tar.gz.
File metadata
- Download URL: utilities_toolkit-1.0.0.tar.gz
- Upload date:
- Size: 28.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","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 |
79119066598737b6e72afb99df7ffca5b100382d00840e5613a9f324316fbc2c
|
|
| MD5 |
10d73e1fac7b4ebfa4e0ab10692f4eca
|
|
| BLAKE2b-256 |
6534c7134bbbd720113c1e4511b0d4d0065df85dd95b5771ee0c3d6106c05e38
|
File details
Details for the file utilities_toolkit-1.0.0-py3-none-any.whl.
File metadata
- Download URL: utilities_toolkit-1.0.0-py3-none-any.whl
- Upload date:
- Size: 35.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","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 |
fb2db892a3b921c60bf5781aeb6ebafd10428d9974b850379515e6740e03c09b
|
|
| MD5 |
017c915156ac283d3fae4ad3efd58c26
|
|
| BLAKE2b-256 |
2b613f9ccbbab93cf0e15f2bc943dd57a3c2b7104eae298f77492e8e8981c7fc
|