Skip to content

samedit66/coloredstrings

Repository files navigation

coloredstrings

Python package PyPI Downloads PyPI version Supported Python versions Licence Code style: Ruff

Colorize Different

A tiny utility that patches Python's built-in str with convenient ANSI color / style helpers so you can write "hello".red() instead of juggling escape sequences or long constant concatenations. Inspired by the Rust text-colorizer crate — ergonomic, expressive, and surprisingly pleasant to type.


Installation

Stable:

pip install coloredstrings

Latest:

pip install git+https://github.com/samedit66/coloredstrings.git

Quick start

Run a demo:

python -m coloredstrings

Test in code:

import coloredstrings as cs

# Patched `str` methods are available only within the context
def warn(msg: str) -> None:
    with cs.patched():
        print("warning:".yellow().bold(), msg)

# Same idea, but using a decorator
@cs.patched
def info(msg: str) -> None:
    print("[info]:".blue(), msg)

# If you're brave enough and really want it, you can patch `str` globally
cs.patch()

print("ok".green())
print("warn".yellow().bold())
print("bad".red(), "on green".on_green())

# 24-bit RGB:
print("custom".rgb(123, 45, 200))

# 256-color:
print("teal-ish".color256(37))

# And don't forget to unpatch it afterwards
cs.unpatch()

If you like coloredstrings but fear to patch str globally for some reasons, you can always colorize directly with little help from colors module:

from coloredstrings import colors

print(colors.blue("It's also possible to use `colors` module!"))
print(colors.underline("But it's kinda longer tho..."))

Why use this? Isn't patching str un-Pythonic?

Patching builtins is a controversial choice, and at first glance it may look un-Pythonic. Libraries like colorama require you to import constants and build strings by concatenation:

from colorama import Fore, Style

print(Fore.RED + "error: " + Style.RESET_ALL + "something went wrong")

That works fine, but it forces you to manage constants and remember to reset, and your code quickly becomes noisy with + and RESET tokens.

Another example using the termcolor package:

from termcolor import colored

print(colored("error:", "red"), "something went wrong")

termcolor offers a nice function colored with a bunch of arguments, but personally, I still find it lacking.

With coloredstrings the color becomes a readable method on the string itself:

import coloredstrings

# `patched()` patches `str` to have awesome `red()` method
with coloredstrings.patched():
    print("error:".red(), "something went wrong")

This reads more like natural prose and keeps color usage local to the value being displayed.


API

Patch str

  • patch() -- attach methods to str
  • unpatch() -- remove the attached methods
  • patched() -- automatically calls patch() and unpatch() in a given context

Foreground colors (basic & bright)

  • black() (aliases: grey() and gray())
  • red()
  • green()
  • yellow()
  • blue()
  • magenta()
  • cyan()
  • white()
  • bright_black()
  • bright_red()
  • bright_green()
  • bright_yellow()
  • bright_blue()
  • bright_magenta()
  • bright_cyan()
  • bright_white()

Background helpers

  • on_black() (aliases: on_grey() and on_gray())
  • on_red()
  • on_green()
  • on_yellow()
  • on_blue()
  • on_magenta()
  • on_cyan()
  • on_white()
  • on_bright_black() (aliases: on_bright_grey() and on_bright_gray())
  • on_bright_red()
  • on_bright_green()
  • on_bright_yellow()
  • on_bright_blue()
  • on_bright_magenta()
  • on_bright_cyan()
  • on_bright_white()

24-bit / 256-color helpers

  • rgb(r, g, b) -- 24-bit truecolor foreground (clamped to 0..255)
  • color256(idx) -- 256-color foreground (clamped to 0..255)
  • on_rgb(r, g, b) -- set a 24-bit background color (clamped to 0..255)
  • on_color256(idx) -- set a 256-color background color (clamped to 0..255)

Text attributes / styles

  • bold()
  • dim() (aliases: faint() and dark())
  • italic()
  • underline()
  • blink() (alias: slow_blink())
  • rapid_blink()
  • inverse() (alias: reverse())
  • hidden() (aliases: concealed() and password())
  • strike()

Chaining behavior

One important detail of coloredstrings is how styles and colors combine when you chain methods:

  • "ok".green().bold() vs "ok".bold().green()
    The order does not matter. Both calls produce the same result: green text with bold style.
    Internally, coloredstrings merges the styles into a single escape sequence, so you don’t get duplicated codes.

  • "error".red().blue()
    Only the last foreground color applies. In this case, the text will be blue, not red. Foreground/background colors are mutually exclusive, so the newer one replaces the older one.

  • "title".underline().underline()
    Repeating the same style does not add multiple codes. The library ensures there’s only one underline escape sequence, keeping the output clean and deterministic.

This merging logic makes chained styling predictable: attributes (bold, italic, underline, etc.) stack, while foreground/background colors override each other.


Limitations

Under the hood coloredstrings uses forbiddenfruit package, as a result it also has the same limitations:

Forbbiden Fruit is tested on CPython 3.7-3.13. Since Forbidden Fruit is fundamentally dependent on the C API, this library won't work on other python implementations, such as Jython, pypy, etc.