Lightweight terminal utilities
Project description
tooly
Lightweight terminal utilities for Python.
A single-file library that covers everything you'd reach for in a CLI project — colors, spinners, progress bars, logging, input handling, system info, file ops, and more. No heavy dependencies, just tooly.py.
pip install tooly-dev
Or drop tooly.py directly into your project.
Quick Start
import tooly
# Colors
colors = tooly.ColorSystem()
print(colors.success("Done!"))
print(colors.error("Something went wrong"))
print(colors.info("Loading..."))
# Spinner
with tooly.spinner("Working", done_msg="Finished"):
time.sleep(2)
# Progress bar
for item in tooly.progress(range(100), label="Processing"):
...
# Timing
with tooly.measure("database query"):
result = db.fetch_all()
Reference
Output & Colors
ColorSystem
colors = tooly.ColorSystem()
# Named colors
colors.red("text")
colors.green("text")
colors.yellow("text")
colors.blue("text")
colors.cyan("text")
colors.magenta("text")
colors.grey("text")
# Semantic helpers
colors.success("All done!") # ✓ green
colors.error("Failed!") # X red
colors.warning("Watch out!") # ! yellow
colors.info("FYI") # i blue
colors.debug("trace") # ~ grey
colors.critical("ABORT") # bold red background
# Styles
colors.bold("text")
colors.italic("text")
colors.underline("text")
colors.dim("text")
colors.strikethrough("text")
# Background colors
colors.bg_red("text")
colors.bg_green("text")
# ... and more
# RGB / 256-color
colors.rgb("text", 255, 128, 0)
colors.bg_rgb("text", 30, 30, 30)
colors.color256("text", "214")
# Gradient across characters
colors.gradient("Hello!", start=(255, 0, 0), end=(0, 0, 255))
# Syntax highlighting
colors.json({"key": "value", "count": 42})
colors.python("def hello(): return 42")
# Utilities
colors.highlight("find these keywords here", ["keywords", "these"], "yellow")
colors.indent("file.py", level=1) # colored tree-style indent
typewrite(text, delay=0.1, end="\n")
Types text character-by-character with a configurable delay.
tooly.typewrite(colors.info("Loading config..."), delay=0.04)
banner(text, style="block", color="blue", align="center", width=None)
Draws a bordered banner. Styles: "block", "thin", "dots", or plain.
tooly.banner("tooly v1.5", style="thin", color="cyan")
sparkline(values)
Returns a Unicode sparkline string from a list of numbers.
print(tooly.sparkline([1, 3, 2, 5, 4, 7, 6])) # ▁▃▂▅▄▇▆
triangle(char="█", delay=0.02, color="cyan")
Draws an animated full-screen triangle, then clears it.
Timing & Control Flow
measure(label, precision=3)
Context manager that prints elapsed time.
with tooly.measure("build step"):
compile_project()
# [⏲] build step: 1.243s
spinner(label, frames, done_msg)
Animated spinner context manager.
with tooly.spinner("Fetching data", done_msg="Got it"):
data = api.get()
countdown(seconds, label, done_msg)
Counts down to zero, returns True if completed, False if interrupted.
if tooly.countdown(10, label="Starting in"):
launch()
watch(func, interval=2.0, title, show_timestamp)
Repeatedly calls func() and refreshes the terminal output, like watch in Linux.
tooly.watch(lambda: f"Queue size: {queue.qsize()}", interval=1.0)
Input
userinput(prompt, validator, error_msg, max_attempts, strip)
Prompts the user with optional validation loop.
age = tooly.userinput("Age: ", validator=str.isdigit, error_msg="Digits only")
password(prompt, confirm, min_length, max_length, validator, mask)
Masked password input with optional confirmation and validation.
pwd = tooly.password("Password: ", confirm=True, min_length=8)
menu(items, title, loop, input_mode, clear)
Interactive arrow-key or digit menu. Returns the selected item or None.
choice = tooly.menu(["Start", "Settings", "Quit"], title="Main Menu")
confirm(prompt, yes_values, no_values)
Single-keypress yes/no prompt. Returns bool.
if tooly.confirm("Delete this file?"):
os.remove(path)
Progress
progress(iterable=None, total=None, label, width)
Progress bar that wraps an iterable or works as a manual counter.
# Wrap an iterable
for item in tooly.progress(files, label="Uploading"):
upload(item)
# Manual control
with tooly.progress(total=100) as p:
for i in range(100):
do_work()
p.update()
Logging
log
A global logger instance with leveled output and optional file sink.
tooly.log.info("Server started")
tooly.log.success("Job completed")
tooly.log.warn("Disk space low")
tooly.log.error("Connection failed")
tooly.log.debug("x =", x)
# Custom tag
tooly.log("SRV", "Listening on :8080", color="cyan")
# File output
tooly.log.set_file("app.log")
tooly.log.unset_file()
# Silence/restore
tooly.log.mute()
tooly.log.unmute()
# Toggle timestamps
tooly.log.timestamps(False)
Retry
retry(attempts=3, delay=1.0, backoff=1.0, exceptions, on_fail, label)
Decorator or context manager for automatic retries with optional backoff.
# Decorator
@tooly.retry(attempts=3, delay=0.5, backoff=2.0)
def fetch():
return requests.get(url).json()
# Context manager
with tooly.retry(attempts=5, delay=1.0) as r:
result = risky_operation()
Scheduling
every(seconds, func=None, start_immediately=True)
Runs a function on a background thread at a fixed interval. Returns a handle.
@tooly.every(seconds=5)
def heartbeat():
print("ping")
# Later:
heartbeat.pause()
heartbeat.resume()
heartbeat.stop()
System
Platform / on_platform(...)
Detect the current platform and dispatch platform-specific callbacks.
name = tooly.on_platform(
windows=lambda: "Windows",
linux=lambda: "Linux",
macos=lambda: "macOS",
android=lambda: "Android",
default=lambda: "Unknown",
)
Platforms: windows, linux, macos, android, ios, freebsd.
ram()
Returns a RamInfo(total, used, free, percent) namedtuple (bytes).
r = tooly.ram()
print(f"RAM: {tooly.humanize(r.used, 'bytes')} / {tooly.humanize(r.total, 'bytes')} ({r.percent}%)")
cpu(interval=0.1)
Returns a CpuInfo(count, percent, freq_mhz, model) namedtuple.
c = tooly.cpu()
print(f"CPU: {c.model} × {c.count} cores @ {c.freq_mhz} MHz — {c.percent}% used")
hwid(stable=True)
Returns a SHA-256 hardware fingerprint as a hex string.
print(tooly.hwid()) # e.g. "A3F9C2..."
is_admin()
Returns True if the current process has admin/root privileges.
uptime()
Returns system uptime in seconds as a float.
pkill(name=None, pid=None, force=False, signal=15)
Kill a process by name or PID.
tooly.pkill("chrome")
tooly.pkill(pid=1234, force=True)
plist(name=None, pid=None)
List running processes, optionally filtered.
procs = tooly.plist(name="python")
for p in procs:
print(p["pid"], p["name"])
notify(title, message, urgency)
Send a desktop notification (uses notify-send / osascript / MessageBox).
tooly.notify("Build done", "All tests passed ✓")
Power / Session
tooly.shutdown(delay=30) # shutdown in 30 seconds
tooly.reboot(force=True) # force reboot
tooly.hibernate()
tooly.lock_device()
tooly.cancel_shutdown()
Environment
env(name, default=None, required=False, dotenv=None)
Read an environment variable, with auto .env loading.
db_url = tooly.env("DATABASE_URL", required=True)
debug = tooly.env("DEBUG", default="false")
File & Data
run(cmd, live=False, timeout=None, spinner=True, cwd=None)
Run a shell command and return a RunResult(returncode, stdout, stderr, success).
result = tooly.run("git status")
if result.success:
print(result.stdout)
# Stream output live
tooly.run("npm install", live=True)
download(url, dest=None, filename=None, overwrite=False, retries=3, progress=True)
Download a file with a progress bar and retry logic.
result = tooly.download("https://example.com/data.csv", dest="/tmp")
if result:
print("Saved to", result.path)
unzip(path, dest=None, overwrite=True, password=None, members=None, progress=True)
Extract archives: .zip, .tar.*, .gz, .bz2, .xz, .rar, .7z.
result = tooly.unzip("archive.tar.gz", dest="./out")
print(f"Extracted {result.count} files")
remove(path, recursive=False, missing_ok=True, trash=False)
Delete a file or directory, optionally sending it to the system trash.
tooly.remove("temp/", recursive=True)
tooly.remove("old_file.txt", trash=True)
md5(data)
Return the MD5 hex digest of a string, bytes, or file path.
tooly.md5("hello") # hash of string
tooly.md5(b"\x00\x01") # hash of bytes
tooly.md5("/path/to/file.bin") # hash of file
tempdir(suffix, prefix, dir)
Context manager that creates and auto-deletes a temporary directory.
with tooly.tempdir() as tmp:
open(f"{tmp}/scratch.txt", "w").write("data")
# directory is deleted here
humanize(value, kind="num")
Format numbers, bytes, or durations as human-readable strings.
tooly.humanize(1_500_000) # "1.5M"
tooly.humanize(2_048, kind="bytes") # "2.0 KB"
tooly.humanize(3661, kind="seconds") # "1h 1m 1s"
ensure_package(name, version=None, upgrade=False, quiet=False)
Install a PyPI package at runtime if not already present.
tooly.ensure_package("rich")
tooly.ensure_package("requests", version="2.31.0")
package_version(name)
Return the installed version of a package, or None.
Persistence
saves
A simple key-value save manager backed by JSON or pickle files.
tooly.saves.save("config", {"theme": "dark", "lang": "en"})
config = tooly.saves.load("config")
tooly.saves.exists("config") # True
tooly.saves.list() # [{"key": "config", "fmt": "json", ...}]
tooly.saves.delete("config")
tooly.saves.clear() # delete all saves
Default save location: ~/.tooly/saves/. Pass folder= to customize.
Debugging
patch(obj, label=None, show_types=False, on_change=None)
Context manager that logs attribute changes on any object in real time.
class Config:
debug = False
workers = 4
cfg = Config()
with tooly.patch(cfg, label="Config") as p:
p.debug = True
p.workers = 8
# prints timestamped diffs of every assignment
recorder(log_file="session.log")
Record all terminal input/output to a log file.
with tooly.recorder("session.log"):
name = input("Name: ")
print(f"Hello, {name}!")
Fake Data
lorem
Generate placeholder text, names, emails, addresses, and more.
tooly.lorem.words(10)
tooly.lorem.sentences(3)
tooly.lorem.paragraph()
tooly.lorem.name() # "Jordan Smith"
tooly.lorem.name(gender="female", locale="ru")
tooly.lorem.email()
tooly.lorem.phone("+44")
tooly.lorem.address()
tooly.lorem.company()
tooly.lorem.job_title()
tooly.lorem.uuid()
tooly.lorem.ip(version=4)
tooly.lorem.url()
tooly.lorem.date(past=365)
tooly.lorem.datetime()
Locales: "en", "ru". Genders: "male", "female".
Diff
diff_highlight(a, b, mode, label_a, label_b)
Show colored diffs between two strings.
tooly.diff_highlight(
"SELECT id, name FROM users",
"SELECT id, email FROM users LIMIT 100",
tooly.DiffMode.WORD,
)
Modes: DiffMode.WORD, DiffMode.CHAR, DiffMode.LINE.
Activity Calendar
calendar(data, title, color_mode, show_legend, show_stats, max_weeks)
Render a GitHub-style contribution heatmap.
data = {"2026-06-01": 5, "2026-06-02": 2, "2026-06-03": 8}
tooly.calendar(data, title="Commits", color_mode="blue")
Color modes: "green", "blue", "purple", "orange".
Audio
music
Play audio files in a background thread (requires ffplay, mpv, mplayer, afplay, or cvlc).
tooly.music.play("track.mp3", loop=True)
tooly.music.pause()
tooly.music.resume()
tooly.music.volume(0.5)
tooly.music.stop()
info = tooly.music.info() # {"current", "is_playing", "is_paused", "volume"}
Misc
tooly.cls() # clear the terminal
tooly.is_admin() # True if running as root/admin
License
MIT © 2026 SuperDragon777
Project details
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 tooly_dev-1.6.0.tar.gz.
File metadata
- Download URL: tooly_dev-1.6.0.tar.gz
- Upload date:
- Size: 48.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c19aa30c9cd2be4795733216117d6180c310c8414fa2342beb4b8c9c6584afe
|
|
| MD5 |
c26a29dab2aa604b2f9cab19ec11c693
|
|
| BLAKE2b-256 |
326d3d3dc81edd626dde88a3563caa250e97f0c863db1afd3587bbf136b36132
|
File details
Details for the file tooly_dev-1.6.0-py3-none-any.whl.
File metadata
- Download URL: tooly_dev-1.6.0-py3-none-any.whl
- Upload date:
- Size: 43.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b82c0406bdf9567bbbc61d5f631257b7d0a91cc95a9c9ccd689e9c7d65e195f
|
|
| MD5 |
fd9309652e5cb7b313dedee2ea1e5119
|
|
| BLAKE2b-256 |
827196ca845775d10e5fa35bf1d260329fa9a4ef579523fb725938d1f986ac07
|