Advanced Application Framework for Python with a focus on Command Line Interfaces
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Cement provides a comprehensive collection of utility functions for common operations including file system operations, shell command execution, configuration helpers, and testing utilities.
Collection of general-purpose utility functions for common operations.
def init_defaults(*sections: str) -> Dict[str, Any]:
"""
Create a standard dictionary for application configuration defaults.
Creates a nested dictionary structure with the specified sections.
Commonly used for setting up application configuration defaults.
Args:
*sections: Section names to create in the defaults dictionary
Returns:
Dictionary with nested sections for configuration defaults
Example:
config = init_defaults('myapp', 'database')
# Returns: {'myapp': {}, 'database': {}}
"""
def minimal_logger(namespace: str) -> logging.Logger:
"""
Create a basic logger instance for framework use.
Creates a minimal logger with basic configuration suitable for
framework internal logging and simple application logging needs.
Args:
namespace: Logger namespace/name
Returns:
Configured Logger instance
"""
def rando(salt: str = None) -> str:
"""
Generate a random hash string for various purposes.
Useful for testing, unique identifiers, or any time a random
string is required. Uses SHA256 for compatibility.
Args:
salt: Optional salt string. If None, uses random.random()
Returns:
Random hash string (32 characters)
Example:
random_id = rando('my_salt_string')
test_id = rando() # Uses random salt
"""
def is_true(item: Any) -> bool:
"""
Test if a value should be considered True.
Provides consistent boolean evaluation for configuration values
and user input, handling strings like 'true', 'yes', '1', etc.
Args:
item: Value to test for truthiness
Returns:
True if item should be considered True, False otherwise
Example:
is_true('true') # True
is_true('yes') # True
is_true('1') # True
is_true('false') # False
is_true(0) # False
"""
def wrap(text: str, width: int = 77, indent: str = '', long_words: bool = False, hyphens: bool = False) -> str:
"""
Wrap text to specified width with optional indentation.
Provides text wrapping functionality with control over line width,
indentation, and handling of long words and hyphenation.
Args:
text: Text to wrap
width: Maximum line width
indent: String to indent each line
long_words: Whether to break long words
hyphens: Whether to use hyphenation
Returns:
Wrapped text string
"""
def get_random_string(length: int = 12) -> str:
"""
Generate a random string of specified length.
Creates random strings suitable for temporary identifiers,
test data, or other purposes requiring random text.
Args:
length: Length of random string to generate
Returns:
Random string of specified length
"""File system operations and temporary file/directory management.
class Tmp:
"""
Context manager for temporary directory and file creation with cleanup.
Provides creation and automatic cleanup of temporary directories and files.
Designed to be used with the 'with' statement for automatic resource management.
"""
def __init__(self, cleanup: bool = True, suffix: str = '', prefix: str = '', dir: str = None) -> None:
"""
Initialize temporary file/directory manager.
Args:
cleanup: Whether to delete temp directory/file on exit
suffix: Suffix for temp directory and file names
prefix: Prefix for temp directory and file names
dir: Parent directory for temp directory/file creation
"""
def __enter__(self) -> 'Tmp':
"""
Enter context manager and create temporary directory/file.
Returns:
Self with populated dir and file attributes
"""
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
"""
Exit context manager and cleanup temporary directory/file if requested.
Args:
exc_type: Exception type (if any)
exc_val: Exception value (if any)
exc_tb: Exception traceback (if any)
"""
@property
def dir(self) -> str:
"""Path to the temporary directory."""
@property
def file(self) -> str:
"""Path to the temporary file."""
def abspath(path: str) -> str:
"""
Get absolute path with user directory expansion.
Expands user directory (~) and converts to absolute path.
Args:
path: File or directory path
Returns:
Absolute path string
"""
def backup(path: str, suffix: str = '.bak') -> str:
"""
Create a backup copy of a file or directory.
Creates a backup copy with the specified suffix. If backup
already exists, appends a number to make it unique.
Args:
path: Path to file or directory to backup
suffix: Suffix to append to backup name
Returns:
Path to created backup
"""
def cleanup(*paths: str) -> None:
"""
Remove files and directories.
Safely removes the specified files and directories,
handling both files and directories recursively.
Args:
*paths: Paths to files/directories to remove
"""Shell command execution and process management utilities.
def cmd(command: str, capture: bool = True, *args: Any, **kwargs: Any) -> Union[Tuple[str, str, int], int]:
"""
Execute shell command with optional output capture.
Wrapper around exec_cmd and exec_cmd2 that provides a unified
interface for shell command execution with flexible output handling.
Args:
command: Command string to execute
capture: Whether to capture and return output
*args: Additional arguments passed to Popen
**kwargs: Additional keyword arguments passed to Popen
Returns:
Tuple of (stdout, stderr, return_code) if capture=True
Return code only if capture=False
Example:
# Capture output
out, err, code = cmd('ls -la')
# Just get return code
code = cmd('touch /tmp/test', capture=False)
"""
def exec_cmd(command: str, *args: Any, **kwargs: Any) -> Tuple[str, str, int]:
"""
Execute command and capture output.
Executes shell command and captures both stdout and stderr,
returning all output along with the exit code.
Args:
command: Command string to execute
*args: Additional arguments passed to Popen
**kwargs: Additional keyword arguments passed to Popen
Returns:
Tuple of (stdout, stderr, return_code)
"""
def exec_cmd2(command: str, *args: Any, **kwargs: Any) -> int:
"""
Execute command without capturing output.
Executes shell command allowing output to go directly to
console, returning only the exit code.
Args:
command: Command string to execute
*args: Additional arguments passed to Popen
**kwargs: Additional keyword arguments passed to Popen
Returns:
Command exit code
"""
class Prompt:
"""
Interactive prompting utilities for user input.
Provides methods for prompting users for various types of input
including text, boolean choices, and multiple choice selections.
"""
def prompt(self, text: str, default: str = None) -> str:
"""
Prompt user for text input.
Args:
text: Prompt text to display
default: Default value if user enters nothing
Returns:
User input string or default value
"""
def prompt_bool(self, text: str, default: bool = None) -> bool:
"""
Prompt user for boolean (yes/no) input.
Args:
text: Prompt text to display
default: Default boolean value
Returns:
Boolean value based on user input
"""
def prompt_options(self, text: str, options: List[str], default: str = None) -> str:
"""
Prompt user to select from multiple options.
Args:
text: Prompt text to display
options: List of available options
default: Default option if user enters nothing
Returns:
Selected option string
"""Utilities specifically designed for testing cement applications.
class TestApp(App):
"""
Application class designed for testing cement applications.
Extends the base App class with testing-specific functionality
and simplified configuration for test environments.
"""
def with_app(app_class: Type[App] = None, **app_kwargs: Any) -> Callable:
"""
Decorator for testing functions that need an application instance.
Provides a cement application instance to test functions,
handling setup and cleanup automatically.
Args:
app_class: Application class to instantiate (defaults to TestApp)
**app_kwargs: Keyword arguments passed to app constructor
Returns:
Decorator function that injects app instance
Example:
@with_app()
def test_my_function(app):
app.setup()
# Test code here
app.run()
"""Version information and management utilities.
def get_version() -> str:
"""
Get the cement framework version string.
Returns the current version of the cement framework.
Returns:
Version string (e.g., '3.0.14')
"""
VERSION: Tuple[int, int, int] = (3, 0, 14)
"""Framework version tuple (major, minor, patch)"""from cement.utils import fs
import os
# Using temporary directory context manager
with fs.Tmp() as tmp:
print(f"Temp directory: {tmp.dir}")
print(f"Temp file: {tmp.file}")
# Create some files in temp directory
test_file = os.path.join(tmp.dir, 'test.txt')
with open(test_file, 'w') as f:
f.write('Hello World!')
# Use temp file
with open(tmp.file, 'w') as f:
f.write('Temporary data')
# Directory and file are automatically cleaned up on exit
# Working with paths
home_config = fs.abspath('~/.myapp.conf')
print(f"Config path: {home_config}")
# Creating backups
original_file = '/path/to/important.txt'
if os.path.exists(original_file):
backup_path = fs.backup(original_file)
print(f"Created backup: {backup_path}")
# Cleanup multiple paths
fs.cleanup('/tmp/file1.txt', '/tmp/file2.txt', '/tmp/test_dir')from cement.utils import shell
# Execute command and capture output
stdout, stderr, exit_code = shell.cmd('ls -la /tmp')
print(f"Output: {stdout}")
print(f"Errors: {stderr}")
print(f"Exit code: {exit_code}")
# Execute command without capturing output (output goes to console)
exit_code = shell.cmd('echo "Hello World"', capture=False)
print(f"Command completed with exit code: {exit_code}")
# Using exec_cmd directly
out, err, code = shell.exec_cmd('ps aux | grep python')
if code == 0:
print("Python processes found:")
print(out)
# Using exec_cmd2 for commands that need user interaction
exit_code = shell.exec_cmd2('vim /tmp/test.txt') # Opens vim directlyfrom cement.utils.shell import Prompt
prompt = Prompt()
# Basic text input
name = prompt.prompt('Enter your name: ', default='Anonymous')
print(f"Hello, {name}!")
# Boolean input
confirm = prompt.prompt_bool('Continue? [y/N]: ', default=False)
if confirm:
print("Continuing...")
else:
print("Aborted.")
# Multiple choice
options = ['production', 'staging', 'development']
env = prompt.prompt_options('Select environment: ', options, default='development')
print(f"Selected environment: {env}")from cement.utils.misc import init_defaults, is_true, rando
# Initialize configuration structure
config = init_defaults('app', 'database', 'logging')
config['app']['debug'] = False
config['app']['name'] = 'MyApp'
config['database']['host'] = 'localhost'
config['database']['port'] = 5432
config['logging']['level'] = 'INFO'
print(f"Config structure: {config}")
# Test boolean values from various sources
user_input = 'yes'
debug_enabled = is_true(user_input)
print(f"Debug enabled: {debug_enabled}")
# Generate random identifiers
session_id = rando('user_session')
test_id = rando()
print(f"Session ID: {session_id}")
print(f"Test ID: {test_id}")from cement.utils.test import TestApp, with_app
from cement import Controller, ex
class TestController(Controller):
class Meta:
label = 'test'
@ex(help='test command')
def hello(self):
return 'Hello from test!'
# Using TestApp directly
def test_app_functionality():
with TestApp() as app:
app.setup()
assert app.config.get('myapp', 'debug') == True
app.run()
# Using with_app decorator
@with_app(TestApp, handlers=[TestController])
def test_controller(app):
app.setup()
# Test controller registration
assert 'test' in app.handler.list('controller')
# Get controller instance
controller = app.handler.get('controller', 'test')(app)
result = controller.hello()
assert result == 'Hello from test!'
# Run tests
test_app_functionality()
test_controller()from cement.utils.misc import wrap, get_random_string
# Text wrapping
long_text = "This is a very long line of text that needs to be wrapped to fit within specific line length constraints for better readability and formatting."
wrapped = wrap(long_text, width=50, indent=' ')
print("Wrapped text:")
print(wrapped)
# Generate random strings for testing
test_data = []
for i in range(5):
random_str = get_random_string(8)
test_data.append(f"test_{random_str}")
print(f"Test data: {test_data}")from cement.utils.version import get_version, VERSION
# Get version information
version_string = get_version()
print(f"Cement version: {version_string}")
major, minor, patch = VERSION
print(f"Version components: {major}.{minor}.{patch}")
# Version comparison
if VERSION >= (3, 0, 0):
print("Using cement 3.0+")Install with Tessl CLI
npx tessl i tessl/pypi-cement