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
The foundation system provides the core application framework classes that serve as the entry point and container for all cement-based applications. The App class manages the complete application lifecycle, while TestApp provides specialized functionality for testing.
The main application framework class that serves as the central container for all application components and functionality.
class App:
"""
Main application class that handles the entire application lifecycle.
The App class manages setup, execution, and cleanup of cement applications.
It serves as the central container for all handlers, interfaces, extensions,
and configuration management.
"""
def __init__(self, label: str = None, **kw: Any) -> None:
"""
Initialize the application.
Args:
label: Application label/name
**kw: Additional keyword arguments passed to Meta
"""
def setup(self) -> None:
"""
Initialize and setup the application for execution.
This method handles:
- Loading configuration files
- Setting up logging
- Loading extensions and plugins
- Registering handlers
- Running pre_setup and post_setup hooks
"""
def run(self) -> None:
"""
Execute the application.
This method handles:
- Parsing command-line arguments
- Running pre_run hooks
- Executing the appropriate controller/command
- Running post_run hooks
- Exception handling and signal management
"""
def close(self) -> None:
"""
Cleanup and shutdown the application.
This method handles:
- Running pre_close and post_close hooks
- Cleaning up resources
- Proper application shutdown
"""
def render(self, data: Dict[str, Any], template: str = None) -> str:
"""
Render data using the configured output handler.
Args:
data: Dictionary of data to render
template: Optional template name for template-based output
Returns:
Rendered output string
"""
def extend(self, extension: str, app: 'App') -> None:
"""
Register a framework extension.
Args:
extension: Extension name/identifier
app: Application instance to extend
"""
@property
def _meta(self) -> Any:
"""Application meta-data configuration object."""Specialized application class designed for unit testing cement applications with additional testing utilities and simplified configuration options.
class TestApp(App):
"""
Application class designed specifically for testing cement applications.
Inherits all functionality from App but provides additional testing
conveniences and simplified configuration for test environments.
"""
def __init__(self, label: str = None, **kw: Any) -> None:
"""
Initialize the test application.
Args:
label: Application label/name
**kw: Additional keyword arguments passed to Meta
"""Application behavior is controlled through the Meta class which can be defined as a nested class or passed as keyword arguments during initialization.
class Meta:
"""
Application meta-data configuration.
Controls application behavior including handler selection,
configuration options, and framework settings.
"""
label: str = None
"""Application label/name"""
debug: bool = False
"""Debug mode flag"""
config_handler: str = 'configparser'
"""Configuration handler to use"""
config_file_suffix: str = '.conf'
"""Configuration file suffix"""
config_files: List[str] = []
"""List of configuration files to load"""
config_dirs: List[str] = []
"""List of configuration directories to search"""
config_defaults: Dict[str, Any] = {}
"""Default configuration values"""
argv: List[str] = None
"""Override sys.argv for argument parsing"""
catch_signals: List[int] = [signal.SIGTERM, signal.SIGINT]
"""List of signals to catch and handle"""
signal_handler: str = 'signal'
"""Signal handler to use"""
pre_setup_hooks: List[Callable] = []
"""List of pre-setup hook functions"""
post_setup_hooks: List[Callable] = []
"""List of post-setup hook functions"""
pre_run_hooks: List[Callable] = []
"""List of pre-run hook functions"""
post_run_hooks: List[Callable] = []
"""List of post-run hook functions"""
pre_close_hooks: List[Callable] = []
"""List of pre-close hook functions"""
post_close_hooks: List[Callable] = []
"""List of post-close hook functions"""
handlers: List[Handler] = []
"""List of handler classes to register"""
extensions: List[str] = []
"""List of extensions to load"""
plugins: List[str] = []
"""List of plugins to load"""
plugin_config_dirs: List[str] = []
"""List of plugin configuration directories"""
plugin_dirs: List[str] = []
"""List of plugin directories to search"""
template_handler: str = 'dummy'
"""Template handler to use"""
template_dirs: List[str] = []
"""List of template directories"""
output_handler: str = 'dummy'
"""Output handler to use"""
log_handler: str = 'logging'
"""Log handler to use"""
cache_handler: str = 'dummy'
"""Cache handler to use"""
mail_handler: str = 'dummy'
"""Mail handler to use"""
argument_handler: str = 'argparse'
"""Argument handler to use"""
base_controller: str = None
"""Base controller class name"""from cement import App, Controller, ex
class BaseController(Controller):
class Meta:
label = 'base'
@ex(help='say hello')
def hello(self):
print('Hello World!')
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
handlers = [BaseController]
def main():
with MyApp() as app:
app.run()
if __name__ == '__main__':
main()from cement import App, init_defaults
CONFIG = init_defaults('myapp')
CONFIG['myapp']['debug'] = False
CONFIG['myapp']['some_option'] = 'default_value'
class MyApp(App):
class Meta:
label = 'myapp'
config_defaults = CONFIG
config_files = ['/etc/myapp.conf', '~/.myapp.conf']
with MyApp() as app:
app.setup()
print(f"Debug mode: {app.config.get('myapp', 'debug')}")
print(f"Option value: {app.config.get('myapp', 'some_option')}")
app.run()from cement import TestApp
def test_myapp():
with TestApp() as app:
app.setup()
# Test application functionality
assert app.config.get('myapp', 'debug') == True
app.run()from cement import App
class MyApp(App):
class Meta:
label = 'myapp'
extensions = ['colorlog', 'jinja2', 'yaml']
config_handler = 'yaml'
template_handler = 'jinja2'
log_handler = 'colorlog'
with MyApp() as app:
app.setup()
app.log.info('Application started with extensions')
app.run()The recommended pattern is to use applications as context managers to ensure proper setup and cleanup:
from cement import App
# Recommended approach - automatic setup/cleanup
with MyApp() as app:
app.run()
# Manual approach - requires explicit calls
app = MyApp()
try:
app.setup()
app.run()
finally:
app.close()Install with Tessl CLI
npx tessl i tessl/pypi-cement