Python Command Line Interface Tools for colored output, progress bars, text formatting, and argument handling
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Unix pipe detection, path expansion utilities, collection testing, and directory creation helpers. This module provides essential utility functions for file system operations, data type checking, and string manipulation commonly needed in CLI applications.
Detect and read data piped to the application via stdin, enabling CLI tools to work in Unix pipe chains.
def piped_in():
"""
Detect and return data piped to the application via stdin.
Returns:
str or None: Content from stdin if data is piped, None otherwise
Usage:
Enables CLI applications to work in Unix pipe chains like:
cat file.txt | python app.py
echo "data" | python app.py
"""Usage Examples:
from clint.pipes import piped_in
from clint.textui import puts
# Check for piped input
input_data = piped_in()
if input_data:
puts(f"Received piped data: {len(input_data)} characters")
# Process the piped data
for line in input_data.splitlines():
puts(f"Processing: {line}")
else:
puts("No piped input detected")
# CLI tool that works with or without pipes
def main():
piped_data = piped_in()
if piped_data:
# Process piped data
process_input(piped_data)
else:
# Interactive mode or file processing
filename = input("Enter filename: ")
with open(filename) as f:
process_input(f.read())Expand user paths, environment variables, directories, and glob patterns into complete file lists.
def expand_path(path):
"""
Expand directories and globs in given path to list of files.
Args:
path (str): Path with potential user shortcuts, env vars, directories, or globs
Returns:
list: List of expanded file paths matching the input pattern
Expansion includes:
- User home directory (~)
- Environment variables ($HOME, etc.)
- Directory traversal (returns all files in directory)
- Glob patterns (*.txt, **/*.py, etc.)
"""Usage Examples:
from clint.utils import expand_path
# Expand user home directory
files = expand_path('~/Documents/*.txt')
# Returns: ['/home/user/Documents/file1.txt', '/home/user/Documents/file2.txt']
# Expand environment variables
files = expand_path('$HOME/projects/**/*.py')
# Returns all Python files in projects directory and subdirectories
# Expand directory to all contained files
files = expand_path('/etc/nginx/')
# Returns: ['/etc/nginx/nginx.conf', '/etc/nginx/sites-available/default', ...]
# Glob patterns
files = expand_path('/var/log/*.log')
# Returns: ['/var/log/system.log', '/var/log/app.log', ...]
# Use in CLI applications
import sys
from clint.arguments import Args
args = Args()
for pattern in args.all:
for filepath in expand_path(pattern):
print(f"Processing: {filepath}")
# Process each fileTest whether an object is a collection type, excluding strings which are treated as atomic values.
def is_collection(obj):
"""
Test if an object is a collection. Strings don't count as collections.
Args:
obj: Any Python object to test
Returns:
bool: True if object is a collection (list, tuple, set, etc.) but not a string
Note:
Strings are explicitly excluded because they are iterable but typically
treated as atomic values in CLI contexts rather than collections.
"""Usage Examples:
from clint.utils import is_collection
# Test various types
print(is_collection([1, 2, 3])) # True - list
print(is_collection((1, 2, 3))) # True - tuple
print(is_collection({1, 2, 3})) # True - set
print(is_collection({'a': 1, 'b': 2})) # True - dict
print(is_collection("hello")) # False - string
print(is_collection(42)) # False - int
print(is_collection(range(5))) # True - range object
# Use in argument processing
def process_args(*args):
for arg in args:
if is_collection(arg):
# Handle collections (lists, tuples, etc.)
for item in arg:
process_item(item)
else:
# Handle atomic values (strings, numbers, etc.)
process_item(arg)
# CLI application usage
from clint.arguments import Args
args = Args()
for arg_value in args.all:
if is_collection(arg_value):
print(f"Collection with {len(arg_value)} items")
else:
print(f"Single value: {arg_value}")Create directories recursively, similar to the Unix mkdir -p command.
def mkdir_p(path):
"""
Create directory and any necessary parent directories (like 'mkdir -p').
Args:
path (str): Directory path to create
Side Effects:
Creates the directory and all parent directories as needed
Does nothing if directory already exists (no error)
Raises:
OSError: If directory creation fails for reasons other than already existing
"""Usage Examples:
from clint.utils import mkdir_p
# Create nested directories
mkdir_p('/tmp/my-app/data/logs') # Creates all intermediate directories
# Safe to call on existing directories
mkdir_p('/tmp/my-app/data/logs') # No error, does nothing
# Use in application setup
import os
from clint import resources
def setup_app_directories():
app_base = os.path.expanduser('~/.myapp')
mkdir_p(os.path.join(app_base, 'config'))
mkdir_p(os.path.join(app_base, 'cache'))
mkdir_p(os.path.join(app_base, 'logs'))
mkdir_p(os.path.join(app_base, 'plugins'))
# Error handling
try:
mkdir_p('/root/protected/directory')
except OSError as e:
print(f"Failed to create directory: {e}")Advanced string splitting and chunking utilities for text processing.
def tsplit(string, delimiters):
"""
Split string using multiple delimiters (like str.split but with multiple delimiters).
Args:
string (str): String to split
delimiters (tuple or list): Multiple delimiter characters/strings
Returns:
list: List of string parts split by any of the delimiters
"""
def schunk(string, size):
"""
Split string into fixed-size chunks.
Args:
string (str): String to split into chunks
size (int): Size of each chunk
Returns:
list: List of string chunks, each of specified size (last may be shorter)
"""Usage Examples:
from clint.utils import tsplit, schunk
# Split by multiple delimiters
text = "apple,banana;orange:grape"
parts = tsplit(text, (',', ';', ':'))
# Returns: ['apple', 'banana', 'orange', 'grape']
# Parse CSV-like data with multiple separators
data = "name=John|age=25;city=NYC,country=USA"
fields = tsplit(data, ('|', ';', ','))
# Returns: ['name=John', 'age=25', 'city=NYC', 'country=USA']
# Process log entries with various separators
log_line = "2023-01-01 12:00:00 ERROR: Database connection failed"
parts = tsplit(log_line, (' ', ':', '-'))
# Returns: ['2023', '01', '01', '12', '00', '00', 'ERROR', '', 'Database', 'connection', 'failed']
# Chunk large strings for processing
large_text = "A" * 1000
chunks = schunk(large_text, 100)
# Returns: ['A' * 100, 'A' * 100, ..., remaining A's]
# Process data in fixed-size blocks
def process_in_chunks(data, chunk_size=1024):
chunks = schunk(data, chunk_size)
for i, chunk in enumerate(chunks):
print(f"Processing chunk {i+1}/{len(chunks)}: {len(chunk)} characters")
# Process each chunk
# Format data for display
def format_hex_dump(data, width=16):
hex_chars = data.encode('hex') if hasattr(data, 'encode') else data.hex()
chunks = schunk(hex_chars, width * 2) # 2 hex chars per byte
for i, chunk in enumerate(chunks):
offset = i * width
formatted_hex = ' '.join(schunk(chunk, 2))
print(f"{offset:08x}: {formatted_hex}")#!/usr/bin/env python
from clint.pipes import piped_in
from clint.utils import expand_path, is_collection
from clint.arguments import Args
from clint.textui import puts, colored
def main():
args = Args()
# Check for piped input first
piped_data = piped_in()
if piped_data:
puts(colored.green("Processing piped data..."))
process_text(piped_data)
return
# Process file arguments
if args.files:
for file_pattern in args.files:
file_paths = expand_path(file_pattern)
for file_path in file_paths:
puts(f"Processing: {file_path}")
with open(file_path) as f:
process_text(f.read())
else:
puts(colored.red("No input provided. Use: program < file.txt or program *.txt"))
def process_text(text):
# Process the text data
lines = text.splitlines()
puts(f"Processing {len(lines)} lines of text")
if __name__ == '__main__':
main()from clint.utils import mkdir_p, expand_path, is_collection
from clint import resources
import os
def setup_application(app_name, config_files=None):
"""Set up application directories and copy configuration files."""
# Initialize application directories
resources.init("MyCompany", app_name)
# Create additional subdirectories
mkdir_p(resources.user.path + "/plugins")
mkdir_p(resources.user.path + "/themes")
mkdir_p(resources.cache.path + "/downloads")
# Copy configuration files if provided
if config_files:
if not is_collection(config_files):
config_files = [config_files]
for config_pattern in config_files:
config_paths = expand_path(config_pattern)
for config_path in config_paths:
filename = os.path.basename(config_path)
with open(config_path) as f:
resources.user.write(f"config/{filename}", f.read())
print(f"Copied config: {filename}")
# Usage
setup_application("MyApp", ["~/.myapp-configs/*.ini", "/etc/myapp/defaults/*"])Install with Tessl CLI
npx tessl i tessl/pypi-clint