Skip to main content

Textstyle is a lightweight Python library that makes terminal text styling effortless. With support for colors, backgrounds, text styles, and an intuitive markup format, you can create beautiful CLI applications with just a few lines of code.

Reason this release was yanked:

Textstyle is now available as Vargula. Try visiting the Vargula library!

Project description

textstyle

Simple, powerful, cross-platform terminal text styling for Python

Python Version License Cross Platform

textstyle is a lightweight Python library that makes terminal text styling effortless. With support for colors, backgrounds, text styles, and an intuitive markup format, you can create beautiful CLI applications with just a few lines of code.


โœจ Features

  • Rich Color Support - Named colors, hex codes (#FF5733), and RGB tuples
  • Text Styling - Bold, italic, underline, strikethrough, and more
  • Markup Format - Intuitive XML-style tags for easy formatting
  • Theme System - Built-in themes (dark/light) and custom themes
  • Utility Functions - Strip tags, clean ANSI codes, measure visible length
  • Cross-Platform - Works seamlessly on Linux, macOS, and Windows
  • NO_COLOR Support - Respects NO_COLOR and FORCE_COLOR environment variables
  • Zero Dependencies - Pure Python, no external packages required
  • Composable - Mix and match styles, nest tags, create reusable components

๐Ÿ“ฆ Installation

pip install textstyle

Or install from source:

git clone https://github.com/crystallinecore/textstyle.git
cd textstyle
pip install -e .

๐Ÿš€ Quick Start

import textstyle as ts

# Basic styling
print(ts.style("Error", color="red", look="bold"))
print(ts.style("Success", color="green", look="bold"))

# Markup format
print(ts.format("This is <red>red</red> and <bold>bold</bold>!"))

# Hex colors
print(ts.style("Custom", color="#FF5733", look="bold"))

# Create custom styles
ts.create("error", color="red", look="bold")
print(ts.format("An <error>error</error> occurred!"))

# Use themes
ts.set_theme("dark")
print(ts.format("<error>Error:</error> Connection failed"))

๐Ÿ“– Documentation

Basic Usage

style(text, color=None, bg=None, look=None)

Apply styling to text directly.

import textstyle as ts

# Simple colors
print(ts.style("Hello", color="red"))
print(ts.style("World", color="blue", look="bold"))

# Background colors
print(ts.style("Alert", color="white", bg="red", look="bold"))

# Hex colors
print(ts.style("Brand", color="#FF5733"))

# RGB tuples
print(ts.style("Custom", color=(255, 87, 51)))

# Multiple looks
print(ts.style("Fancy", color="cyan", look=["bold", "underline"]))

Available colors:

  • black, red, green, yellow, blue, magenta, cyan, white
  • bright_black, bright_red, bright_green, bright_yellow, bright_blue, bright_magenta, bright_cyan, bright_white

Available looks:

  • bold, dim, italic, underline, blink, reverse, hidden, strikethrough

Background colors:

  • Prefix any color name with bg_ (e.g., bg="red" or bg="bright_blue")

format(text)

Format text using markup-style tags.

import textstyle as ts

# Predefined color tags
print(ts.format("This is <red>red</red> text"))

# Predefined look tags
print(ts.format("This is <bold>bold</bold> text"))

# Background tags
print(ts.format("Alert: <bg_red><white>WARNING</white></bg_red>"))

# Hex color tags
print(ts.format("Brand: <#FF5733>orange</#FF5733>"))

# Nested tags
print(ts.format("<red><bold>Bold Red</bold></red>"))

# Complex combinations
print(ts.format(
    "Status: <green>Connected</green> | "
    "Warnings: <yellow>2</yellow> | "
    "Errors: <red>0</red>"
))

Custom Styles

create(name, color=None, bg=None, look=None)

Create reusable custom styles.

import textstyle as ts

# Create custom styles
ts.create("error", color="red", look="bold")
ts.create("success", color="green", look="bold")
ts.create("warning", color="yellow", look="bold")
ts.create("highlight", color="black", bg="yellow")

# Use in format()
print(ts.format("<error>ERROR:</error> Connection failed"))
print(ts.format("<success>SUCCESS:</success> File saved"))
print(ts.format("Please <highlight>note</highlight> this"))

# Use as function (dynamic attribute access)
print(ts.error("Direct error styling"))
print(ts.success("Direct success styling"))

delete(name)

Remove a custom style.

ts.delete("error")  # Returns True if deleted, False if not found

Theme System

set_theme(theme)

Apply a theme with multiple predefined styles.

Built-in themes:

import textstyle as ts

# Dark theme (bright colors)
ts.set_theme("dark")
print(ts.format("<error>Error</error> <success>Success</success>"))

# Light theme (standard colors)
ts.set_theme("light")
print(ts.format("<warning>Warning</warning> <info>Info</info>"))

Custom themes:

ts.set_theme({
    "primary": {"color": "#007AFF", "look": "bold"},
    "secondary": {"color": "#5856D6"},
    "danger": {"color": "white", "bg": "#FF3B30", "look": "bold"},
    "success": {"color": "#34C759", "look": "bold"},
    "muted": {"color": "bright_black"}
})

print(ts.format("<primary>Primary Button</primary>"))
print(ts.format("<danger>Delete Account</danger>"))

Built-in theme styles:

  • error - Error messages
  • success - Success messages
  • warning - Warning messages
  • info - Informational messages
  • debug - Debug output
  • critical - Critical alerts

Temporary Styles

temporary(name, color=None, bg=None, look=None)

Context manager for temporary styles.

import textstyle as ts

# Temporary style exists only in context
with ts.temporary("temp", color="magenta", look="italic"):
    print(ts.format("<temp>This is temporary</temp>"))
    print(ts.format("Still using <temp>temp</temp>"))

# After context, style is automatically deleted
print(ts.format("<temp>This is plain text</temp>"))

Utility Functions

strip(text)

Remove all markup tags from text.

import textstyle as ts

markup = "<red>Hello</red> <bold>World</bold>"
print(ts.strip(markup))  # Output: "Hello World"

clean(text)

Remove all ANSI escape codes from text.

import textstyle as ts

styled = ts.style("Hello", color="red", look="bold")
print(ts.clean(styled))  # Output: "Hello"

length(text)

Calculate visible text length (ignoring ANSI codes).

import textstyle as ts

styled = ts.style("Hello", color="red", look="bold")
print(len(styled))        # Output: 19 (includes ANSI codes)
print(ts.length(styled))  # Output: 5 (visible length)

enable() / disable()

Globally enable or disable styling.

import textstyle as ts

print(ts.style("Styled", color="red"))  # Colored output

ts.disable()
print(ts.style("Plain", color="red"))   # Plain text

ts.enable()
print(ts.style("Styled", color="red"))  # Colored output again

๐ŸŽฏ Real-World Examples

Logging System

import textstyle as ts
import time

ts.set_theme({
    "timestamp": {"color": "bright_black"},
    "info": {"color": "cyan"},
    "success": {"color": "green", "look": "bold"},
    "error": {"color": "red", "look": "bold"},
})

def log(level, message):
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
    print(ts.format(
        f"<timestamp>[{timestamp}]</timestamp> "
        f"<{level}>{level.upper():<8}</{level}> {message}"
    ))

log("info", "Application started")
log("success", "Database connected")
log("error", "Failed to load configuration")

CLI Menu

import textstyle as ts

ts.set_theme({
    "title": {"color": "cyan", "look": "bold"},
    "option": {"color": "green"},
    "key": {"color": "yellow", "look": "bold"},
})

menu = """
<title>โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—</title>
<title>โ•‘     MAIN MENU             โ•‘</title>
<title>โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•</title>

<key>[1]</key> <option>New Project</option>
<key>[2]</key> <option>Open Project</option>
<key>[3]</key> <option>Settings</option>
<key>[Q]</key> <option>Quit</option>
"""

print(ts.format(menu))

Progress Bar

import textstyle as ts
import time

ts.set_theme({
    "filled": {"color": "green", "look": "bold"},
    "empty": {"color": "bright_black"},
    "percent": {"color": "cyan", "look": "bold"},
})

def progress(current, total):
    percent = int((current / total) * 100)
    filled = int((current / total) * 20)
    empty = 20 - filled
    
    bar = ts.format(
        f"<filled>{'โ–ˆ' * filled}</filled>"
        f"<empty>{'โ–‘' * empty}</empty>"
    )
    
    print(f"\r{bar} {ts.format(f'<percent>{percent}%</percent>')}", 
          end="", flush=True)

for i in range(101):
    progress(i, 100)
    time.sleep(0.05)

Data Table

import textstyle as ts

ts.set_theme({
    "header": {"color": "cyan", "look": "bold"},
    "good": {"color": "green"},
    "warn": {"color": "yellow"},
    "error": {"color": "red"},
})

print(ts.format("""
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ <header>Server</header>      โ”‚ <header>Status</header>  โ”‚ <header>CPU</header>  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ web-01      โ”‚ <good>โ— Up</good>    โ”‚ 23%  โ”‚
โ”‚ web-02      โ”‚ <warn>โ— Warn</warn>  โ”‚ 78%  โ”‚
โ”‚ db-01       โ”‚ <error>โ—‹ Down</error>  โ”‚ 95%  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
"""))

Form Validation

import textstyle as ts

ts.set_theme({
    "field": {"color": "cyan", "look": "bold"},
    "valid": {"color": "green"},
    "invalid": {"color": "red"},
})

validation = """
<field>Email:</field> user@example.com <valid>โœ“ Valid</valid>
<field>Password:</field> ********** <valid>โœ“ Strong</valid>
<field>Username:</field> ab <invalid>โœ— Too short</invalid>
<field>Phone:</field> 555-1234 <valid>โœ“ Valid</valid>
"""

print(ts.format(validation))

Git-Style Output

import textstyle as ts

ts.set_theme({
    "branch": {"color": "cyan", "look": "bold"},
    "added": {"color": "green"},
    "modified": {"color": "yellow"},
    "deleted": {"color": "red"},
})

output = """
On branch <branch>main</branch>

Changes to be committed:
  <added>new file:   src/app.py</added>
  <added>new file:   README.md</added>

Changes not staged:
  <modified>modified:   config.yaml</modified>
  <deleted>deleted:    old_file.py</deleted>
"""

print(ts.format(output))

๐ŸŽจ Color Reference

Named Colors

# Standard colors
black, red, green, yellow, blue, magenta, cyan, white

# Bright colors
bright_black, bright_red, bright_green, bright_yellow,
bright_blue, bright_magenta, bright_cyan, bright_white

Hex Colors

# Full hex
print(ts.style("Text", color="#FF5733"))

# Short hex
print(ts.style("Text", color="#F00"))

# In markup
print(ts.format("<#FF5733>Colored text</#FF5733>"))

RGB Colors

# RGB tuples
print(ts.style("Text", color=(255, 87, 51)))
print(ts.style("Text", bg=(0, 128, 255)))

๐ŸŒ Environment Variables

textstyle respects standard terminal environment variables:

NO_COLOR

Disable all styling when set (any value):

export NO_COLOR=1
python your_script.py  # No colors or styles

FORCE_COLOR

Force enable styling even in non-TTY environments:

export FORCE_COLOR=1
python your_script.py | tee output.log  # Colors preserved

๐Ÿ”ง Advanced Usage

Nested Tags

import textstyle as ts

print(ts.format(
    "<red><bold>Error:</bold> <underline>Connection failed</underline></red>"
))

print(ts.format(
    "<bg_blue><white>Info: <bold>Processing...</bold></white></bg_blue>"
))

Combining Multiple Styles

import textstyle as ts

# Multiple looks in style()
print(ts.style("Text", color="red", look=["bold", "underline"]))

# Combining custom styles
ts.create("alert", color="white", bg="red", look="bold")
ts.create("code", color="cyan", look="italic")

print(ts.format(
    "<alert>Warning:</alert> Check <code>config.yaml</code>"
))

Dynamic Styling

import textstyle as ts

def status_color(value):
    if value < 50:
        return "green"
    elif value < 80:
        return "yellow"
    else:
        return "red"

cpu_usage = 75
print(ts.style(
    f"CPU: {cpu_usage}%",
    color=status_color(cpu_usage),
    look="bold"
))

Working with Alignment

import textstyle as ts

# Align with visible length
def align_text(text, width):
    visible_len = ts.length(text)
    padding = width - visible_len
    return text + " " * padding

styled = ts.style("Status", color="green", look="bold")
print(align_text(styled, 20) + "OK")

๐Ÿงช Testing

Run the comprehensive test suite:

python -m textstyle

Or create your own tests:

import textstyle as ts

# Disable for testing
ts.disable()
assert ts.style("Test", color="red") == "Test"

# Re-enable
ts.enable()
assert "\033[" in ts.style("Test", color="red")

๐Ÿค Contributing

Contributions are welcome! Here's how you can help:

  1. Report bugs - Open an issue with details
  2. Suggest features - Describe your use case
  3. Submit PRs - Fork, create a branch, and submit
  4. Improve docs - Help make documentation clearer

Development Setup

git clone https://github.com/crystallinecore/textstyle.git
cd textstyle
pip install -e .

# Run examples
python examples/usage_examples.py

# Run main module tests
python -m textstyle

๐Ÿ“‹ Requirements

  • Python 3.6+
  • No external dependencies

Platform Support

Platform Status Notes
Linux โœ… Full support All features work
macOS โœ… Full support All features work
Windows โœ… Full support ANSI enabled automatically

๐Ÿ“„ License

MIT License - see LICENSE file for details.


Additional Resources


๐Ÿ’ก Tips & Tricks

Disable Styling in Production

import os
import textstyle as ts

if os.getenv("PRODUCTION"):
    ts.disable()

Custom Logger Integration

import logging
import textstyle as ts

class ColoredFormatter(logging.Formatter):
    COLORS = {
        'DEBUG': 'bright_black',
        'INFO': 'cyan',
        'WARNING': 'yellow',
        'ERROR': 'red',
        'CRITICAL': 'white'
    }
    
    def format(self, record):
        levelname = record.levelname
        msg = super().format(record)
        
        if levelname in self.COLORS:
            color = self.COLORS[levelname]
            bg = 'red' if levelname == 'CRITICAL' else None
            look = 'bold' if levelname in ['ERROR', 'CRITICAL'] else None
            
            parts = msg.split(levelname, 1)
            if len(parts) == 2:
                styled_level = ts.style(levelname, color=color, bg=bg, look=look)
                msg = parts[0] + styled_level + parts[1]
        
        return msg

handler = logging.StreamHandler()
handler.setFormatter(ColoredFormatter('%(levelname)s: %(message)s'))
logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

Performance Considerations

import textstyle as ts

# For high-frequency output, disable if not needed
if not sys.stdout.isatty():
    ts.disable()

# Reuse styled strings
HEADER = ts.style("Header", color="cyan", look="bold")
ERROR = ts.style("ERROR", color="red", look="bold")

# Use format() for complex markup
# It's more efficient than multiple style() calls

๐ŸŽ“ Learning Path

  1. Beginner - Start with style() function
  2. Intermediate - Use format() with markup tags
  3. Advanced - Create themes and custom styles
  4. Expert - Build reusable components and utilities

Made with โค๏ธ by Sivaprasad Murali

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

textstyle-1.0.1.tar.gz (22.8 kB view details)

Uploaded Source

Built Distribution

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

textstyle-1.0.1-py3-none-any.whl (19.4 kB view details)

Uploaded Python 3

File details

Details for the file textstyle-1.0.1.tar.gz.

File metadata

  • Download URL: textstyle-1.0.1.tar.gz
  • Upload date:
  • Size: 22.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.2

File hashes

Hashes for textstyle-1.0.1.tar.gz
Algorithm Hash digest
SHA256 bb248984f250a869401492395feedbc8b6555af172161d807a37f3620a4b7e5a
MD5 049eee6ff430e275762543bce0ab9fc4
BLAKE2b-256 96d0d0fbb28816a1e2b47337745082c2d23bed7999a49b7babe49cf4c52757eb

See more details on using hashes here.

File details

Details for the file textstyle-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: textstyle-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 19.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.2

File hashes

Hashes for textstyle-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 80db0286ecf2dad78d4cf5991ece6b715b5bb6c54969ee97175bf580c6bd997f
MD5 853ed434e4e0bcda2b35bd1ea4b9a006
BLAKE2b-256 127a0ed0acc1ce63440646219130bbbf4ceef1d86d0f640898f9c80853638cde

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