Skip to main content

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

tooly_dev-1.6.0.tar.gz (48.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tooly_dev-1.6.0-py3-none-any.whl (43.7 kB view details)

Uploaded Python 3

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

Hashes for tooly_dev-1.6.0.tar.gz
Algorithm Hash digest
SHA256 4c19aa30c9cd2be4795733216117d6180c310c8414fa2342beb4b8c9c6584afe
MD5 c26a29dab2aa604b2f9cab19ec11c693
BLAKE2b-256 326d3d3dc81edd626dde88a3563caa250e97f0c863db1afd3587bbf136b36132

See more details on using hashes here.

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

Hashes for tooly_dev-1.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5b82c0406bdf9567bbbc61d5f631257b7d0a91cc95a9c9ccd689e9c7d65e195f
MD5 fd9309652e5cb7b313dedee2ea1e5119
BLAKE2b-256 827196ca845775d10e5fa35bf1d260329fa9a4ef579523fb725938d1f986ac07

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page