A lightweight Python library for coloring terminal output with zero dependencies.
Project description
TinTerm
A lightweight Python library for styling terminal output with colors and text modifiers.
Why Another Terminal Color Library?
TinTerm takes a different approach to terminal styling with three key design principles:
-
Strict separation of style and text: Style information is kept separate from your text content, making it easy to work with the plain text while keeping styles consistent.
-
Lazy rendering: Styled objects don't generate ANSI codes until you explicitly render them, giving you full control over when and where colors are applied.
-
Zero-configuration color control: Toggle colors on/off globally with a single call. Perfect for logging to files, CI environments, or respecting
NO_COLORconventions.
Getting Started For Regular Users
Install TinTerm using pip:
pip install tinterm
Note: On Windows, the colorama package is automatically installed as a dependency to enable ANSI color support.
Getting Started For Developers
If you want to build and install TinTerm from source:
1. Clone the repository
git clone git@github.com:TobiasHafner/tinterm.git
cd tinterm
2. Create a virtual environment
python3 -m venv .venv
3. Activate the virtual environment
Linux / macOS
source .venv/bin/activate
Windows In cmd:
.venv\Scripts\activate
In powershell:
.venv\Scripts\Activate.ps1
4. Install in development mode
pip install -e .
This installs the package in "editable" mode, so changes to the source code are immediately reflected without reinstalling.
5. Install development dependencies
pip install -e ".[dev]"
6. Build the package
python -m build
This creates distribution files in the dist/ directory:
tinterm-x.x.x-py3-none-any.whl(wheel format)tinterm-x.x.x.tar.gz(source distribution)
7. Install from the built package
pip install dist/tinterm-x.x.x-py3-none-any.whl
8. Run the Demo
To verify the installation and to see TinTerm's features in action, you can run the included demo application:
# From the repository root directory
python demo/demo.py
The demo displays:
- All 16 foreground colors (standard and bright variants)
- All background colors with contrasting text
- All 7 text modifiers (bold, dim, italic, underline, blink, reverse, strikethrough)
- Practical examples like error/success/warning messages
- Creative combinations like rainbow text
Basic Concepts
Understanding these core concepts will help you use TinTerm effectively.
Colors
TinTerm supports two types of colors: foreground (text color) and background (background color). Colors are defined using the Color enum and can be applied to text through the style dictionary.
TinTerm provides 16 colors total: 8 standard colors and 8 bright variants.
Standard Colors:
Color.BLACK- Standard blackColor.RED- Standard redColor.GREEN- Standard greenColor.YELLOW- Standard yellowColor.BLUE- Standard blueColor.MAGENTA- Standard magentaColor.CYAN- Standard cyanColor.WHITE- Standard white
Bright Colors:
Color.BRIGHT_BLACK- Bright black (gray)Color.BRIGHT_RED- Bright redColor.BRIGHT_GREEN- Bright greenColor.BRIGHT_YELLOW- Bright yellowColor.BRIGHT_BLUE- Bright blueColor.BRIGHT_MAGENTA- Bright magentaColor.BRIGHT_CYAN- Bright cyanColor.BRIGHT_WHITE- Bright white
Modifiers
Modifiers change how text appears beyond just color. They can make text bold, underlined, italic, and more. Multiple modifiers can be combined on the same text.
Available Modifiers:
| Modifier | Effect | ANSI Code | Terminal Support |
|---|---|---|---|
Modifier.BOLD |
Bold/bright text | 1 | Universal |
Modifier.DIM |
Dimmed text | 2 | Most terminals |
Modifier.ITALIC |
Italic text | 3 | Modern terminals |
Modifier.UNDERLINE |
Underlined text | 4 | Universal |
Modifier.BLINK |
Blinking text | 5 | Limited support |
Modifier.REVERSE |
Inverted foreground/background | 7 | Universal |
Modifier.STRIKETHROUGH |
Strikethrough text | 9 | Modern terminals |
Styles
A style is a dictionary that defines how text should appear. It uses StyleKey enum values as keys and specifies colors and modifiers as values. All style keys are optional. You can specify just a foreground color, just modifiers, or any combination.
Example:
style = {
StyleKey.FOREGROUND: Color.RED, # Optional: text color
StyleKey.BACKGROUND: Color.WHITE, # Optional: background color
StyleKey.MODIFIERS: [Modifier.BOLD] # Optional: text modifiers (list)
}
Important Note About Modifiers: Modifiers should be provided as a list (not a tuple):
# Correct
style = {StyleKey.MODIFIERS: [Modifier.BOLD, Modifier.UNDERLINE]}
# Also works, but list is preferred
style = {StyleKey.MODIFIERS: (Modifier.BOLD, Modifier.UNDERLINE)}
Reusing Styles: It's good practice to define styles once and reuse them throughout your application:
# Define your application's style guide
STYLES = {
'error': {
StyleKey.FOREGROUND: Color.RED,
StyleKey.MODIFIERS: [Modifier.BOLD]
},
'success': {
StyleKey.FOREGROUND: Color.GREEN,
StyleKey.MODIFIERS: [Modifier.BOLD]
},
'info': {
StyleKey.FOREGROUND: Color.BLUE
},
'warning': {
StyleKey.FOREGROUND: Color.YELLOW
}
}
# Use them consistently
error_msg = StyledString("Error occurred", style=STYLES['error'])
success_msg = StyledString("Task completed", style=STYLES['success'])
Styled Strings
A StyledString is the fundamental building block of TinTerm. It's a string with associated styling information.
Key Characteristics:
- Stores text and style separately: The text is stored in the
textattribute, and styling in thestyledictionary - Lightweight: Uses
__slots__for memory efficiency - String representation: The
__str__()method returns only the plain text without styling - Immutable styling: Once created, the style doesn't change (but you can create new styled strings)
- Composable: Can be concatenated with other styled strings or plain strings/objects
Creating Styled Strings:
# With style
my_style = {
StyleKey.FOREGROUND: Color.RED,
StyleKey.MODIFIERS: [Modifier.BOLD]
}
styled = StyledString("Hello", style=my_style)
# Without style (plain text)
plain = StyledString("Hello", style={})
# or simply
plain = StyledString("Hello")
Accessing Properties:
s = StyledString("hello", style={StyleKey.FOREGROUND: Color.BLUE})
# Access the plain text
s.text # "hello"
str(s) # "hello" (same as s.text)
# Access the style dictionary
s.style # {StyleKey.FOREGROUND: Color.BLUE}
# Get length
len(s) # 5 (length of the text)
Concatenation:
When you concatenate StyledString objects, you create a StyledText object:
red = StyledString("Red", style={StyleKey.FOREGROUND: Color.RED})
blue = StyledString("Blue", style={StyleKey.FOREGROUND: Color.BLUE})
# All of these create StyledText objects
combined1 = red + blue # Two StyledStrings
combined2 = red + " " # StyledString + plain string
combined3 = "Prefix: " + red # Plain string + StyledString
Notes:
- The
StyledStringclass doesn't provide string manipulation methods likeupper(),lower(),split(), etc. You need to manipulate the text yourself and create newStyledStringobjects if needed - Converting to string with
str()returns only the plain text without any styling or ANSI codes - Empty strings can have styles:
StyledString("", style={StyleKey.FOREGROUND: Color.RED})
Styled Text
A StyledText object represents multiple parts concatenated together, where each part can have its own independent styling. Each part is stored internally as a StyledString.
Creating Styled Texts:
You don't typically create StyledText objects directly using the constructor. They're automatically created when you concatenate StyledString objects or mix styled strings with plain strings/objects:
part1 = StyledString("Error", style={StyleKey.FOREGROUND: Color.RED})
part2 = StyledString(": ", style={})
part3 = StyledString("Connection failed", style={StyleKey.FOREGROUND: Color.YELLOW})
# This automatically creates a StyledText with three parts
message = part1 + part2 + part3
# Mixing with plain strings also works
mixed = part1 + " " + part2 # The " " becomes a StyledString internally
How StyledText._from_parts() Works:
The _from_parts() static method intelligently handles different input types:
StyledTextobjects are flattened (their parts are extracted and added individually)StyledStringobjects are added directly- Other objects are converted to strings and wrapped in a
StyledStringwith no styling
# These all work
text1 = StyledString("Hello") + StyledString("World")
text2 = StyledString("Hello") + " World" # String becomes StyledString
text3 = StyledString("Count: ") + 42 # Number becomes StyledString("42")
Structure:
A StyledText maintains a list of StyledString parts accessible via the parts property:
# Accessing parts
for part in message.parts:
print(f"Text: {part.text}, Style: {part.style}")
Operations:
StyledText supports several operations:
text = red_string + " " + blue_string + " " + green_string
# Concatenation (returns new StyledText)
more_text = text + StyledString(" More", style={StyleKey.FOREGROUND: Color.YELLOW})
more_text = text + " More" # Also works
# Length (total length of all parts)
len(text)
# Iteration over parts
for part in text:
print(render(part))
# String representation (plain text only, no styling)
str(text) # Concatenated plain text from all parts
Why StyledText Matters:
StyledText allows you to build complex, multi-colored output while keeping each part's styling independent:
# Build a colorful log message
timestamp = StyledString("[2024-01-12 10:30:15]", style={
StyleKey.FOREGROUND: Color.BRIGHT_BLACK
})
level = StyledString(" ERROR ", style={
StyleKey.FOREGROUND: Color.WHITE,
StyleKey.BACKGROUND: Color.RED,
StyleKey.MODIFIERS: [Modifier.BOLD]
})
message = StyledString(" Database connection failed", style={
StyleKey.FOREGROUND: Color.RED
})
log_line = timestamp + level + message
print(render(log_line))
The Render Function
The render() function converts your styled objects (StyledString or StyledText) into a string with ANSI escape codes that can be printed to the terminal.
Usage:
from tinterm.render import render
styled = StyledString("Hello", style={StyleKey.FOREGROUND: Color.RED})
output = render(styled)
print(output) # Prints "Hello" in red
How It Works: The render function processes styled objects using a stack-based approach:
- For
StyledTextobjects, it processes each part individually - For
StyledStringobjects, it extracts color and modifier information from the style dictionary and generates appropriate ANSI codes - ANSI codes are wrapped around the text:
\033[<codes>m<text>\033[0m
Color Control: You can globally enable or disable color rendering:
from tinterm.render import enable_colors, disable_colors
# Disable colors (returns plain text without ANSI codes)
disable_colors()
print(render(styled)) # Prints plain "Hello" without colors
# Re-enable colors
enable_colors()
print(render(styled)) # Prints "Hello" in red again
When colors are disabled, render() returns only the plain text content, which is useful for:
- Logging to files
- Running in environments without ANSI support
- Testing
- Piping output to other programs
Performance: Rendering is lightweight, but if you're rendering the same styled text repeatedly in a loop, consider rendering once and reusing the result:
# Less efficient
for i in range(1000):
print(render(styled_text))
# More efficient
rendered = render(styled_text)
for i in range(1000):
print(rendered)
Complete Example
Here's a complete example showing how to use TinTerm:
from tinterm.styled import StyledString, StyledText
from tinterm.attributes import Color, Modifier, StyleKey
from tinterm.render import render
# Define some styles
error_style = {
StyleKey.FOREGROUND: Color.RED,
StyleKey.MODIFIERS: [Modifier.BOLD]
}
success_style = {
StyleKey.FOREGROUND: Color.GREEN,
StyleKey.MODIFIERS: [Modifier.BOLD]
}
info_style = {
StyleKey.FOREGROUND: Color.CYAN
}
# Create styled strings
header = StyledString("=== System Status ===", style={
StyleKey.FOREGROUND: Color.BRIGHT_WHITE,
StyleKey.MODIFIERS: [Modifier.BOLD, Modifier.UNDERLINE]
})
error_msg = StyledString("ERROR: ", style=error_style) + "Database connection failed"
success_msg = StyledString("SUCCESS: ", style=success_style) + "Server started on port 8080"
info_msg = StyledString("INFO: ", style=info_style) + "Loading configuration..."
# Render and print
print(render(header))
print(render(error_msg))
print(render(success_msg))
print(render(info_msg))
Project details
Release history Release notifications | RSS feed
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 tinterm-0.1.0.tar.gz.
File metadata
- Download URL: tinterm-0.1.0.tar.gz
- Upload date:
- Size: 27.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd5f9d899279186d38e58ad602f525a42e7c1ec46cedd7da4ea50d888e56ae5d
|
|
| MD5 |
66261e97e9c7a8158618f472da3882d3
|
|
| BLAKE2b-256 |
a05c2eaaae684a5ee10cd0c240ded206d8e0fd561e9cf2d6aba0d29be43a7f7f
|
File details
Details for the file tinterm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: tinterm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
18f3bab70679be5318def487f6b32429ba02596c1d59e68643508ae676e24fdf
|
|
| MD5 |
8842dfff27af385058c7147a7f074184
|
|
| BLAKE2b-256 |
e1e06f3a022f3ab4d7731423e3aa8b515d62aab80412062c3b4991b50950e9c6
|