Simple framework for command line driven applications in Python 3.7
Project description
spafw37
A lightweight Python 3.7+ framework for building command-line applications with advanced configuration management, command orchestration, and execution control.
Repository: https://github.com/minouris/spafw37
Support Me: Support me on Patreon at Minouris Does Stuff! I'll post news and previews of all my projects, software and otherwise, including this one!
Features
- Flexible Parameter System - Typed parameters with aliases, defaults, validation, and persistence
- Declarative Command Definition - Define commands with actions, dependencies, and orchestration
- Command Orchestration - Automatic dependency resolution, sequencing, and triggers
- Multi-Phase Execution - Organize commands into setup, cleanup, execution, teardown, and end phases
- Cycle Support - Repeating command sequences with init/loop/finalization hooks
- Configuration Management - Persistent and runtime configuration with file I/O
- Integrated Logging - Built-in logging with levels, scopes, and file/console output
- Automatic Help System - Generated help for commands, parameters, and groups
Installation
From TestPyPI (Development Releases)
pip install -i https://test.pypi.org/simple/ spafw37
From Source
git clone https://github.com/minouris/spafw37.git
cd spafw37
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
Development Installation
pip install -e .[dev]
pytest
Quick Start
Create a simple CLI application in minutes:
"""greet.py - A simple greeting application"""
from spafw37 import core as spafw37
from spafw37.constants.param import (
PARAM_NAME,
PARAM_DESCRIPTION,
PARAM_ALIASES,
PARAM_DEFAULT,
)
from spafw37.constants.command import (
COMMAND_NAME,
COMMAND_DESCRIPTION,
COMMAND_ACTION,
)
# Define parameters
params = [
{
PARAM_NAME: 'name',
PARAM_DESCRIPTION: 'Name to greet',
PARAM_ALIASES: ['--name', '-n'],
PARAM_DEFAULT: 'World',
}
]
# Define command action
def greet():
name = spafw37.get_config_str('name')
print(f"Hello, {name}!")
# Define commands
commands = [
{
COMMAND_NAME: 'greet',
COMMAND_DESCRIPTION: 'Greet someone',
COMMAND_ACTION: greet,
}
]
# Register and run
spafw37.add_params(params)
spafw37.add_commands(commands)
if __name__ == '__main__':
spafw37.run_cli()
Run it:
python greet.py greet --name Alice
# Output: Hello, Alice!
python greet.py greet
# Output: Hello, World!
python greet.py --help
# Shows available commands and parameters
Documentation
Comprehensive guides are available in the doc/ directory:
- User Guide - Complete framework overview and getting started
- Parameters Guide - Parameter types, aliases, persistence, and validation
- Commands Guide - Command definition, dependencies, and orchestration
- Phases Guide - Multi-phase execution and lifecycle management
- Cycles Guide - Repeating command sequences and iteration patterns
- Configuration Guide - Configuration management and persistence
- Logging Guide - Logging system and configuration
- API Reference - Complete API documentation
Core Concepts
Parameters
Define parameters as dictionaries with type information, aliases, and behavior:
from spafw37.constants.param import *
{
PARAM_NAME: 'output-dir',
PARAM_DESCRIPTION: 'Output directory',
PARAM_ALIASES: ['--output', '-o'],
PARAM_TYPE: PARAM_TYPE_TEXT,
PARAM_DEFAULT: './output',
PARAM_REQUIRED: False,
}
Types: PARAM_TYPE_TEXT, PARAM_TYPE_NUMBER, PARAM_TYPE_TOGGLE, PARAM_TYPE_LIST
Examples:
params_basic.py- Text, number, aliases, defaultsparams_toggles.py- Boolean flags and mutually exclusive optionsparams_lists.py- Multi-value parametersparams_dict.py- Dictionary parameters for key-value pairsparams_file.py- File path parameters with validationparams_required.py- Globally required parametersparams_runtime.py- Runtime-only state managementparams_groups.py- Organized parameter display
Commands
Define commands with actions, dependencies, and execution control:
from spafw37.constants.command import *
{
COMMAND_NAME: 'deploy',
COMMAND_DESCRIPTION: 'Deploy application',
COMMAND_ACTION: deploy_function,
COMMAND_REQUIRED_PARAMS: ['environment'],
COMMAND_REQUIRE_BEFORE: ['build', 'test'],
COMMAND_PHASE: PHASE_EXECUTION,
}
Examples:
commands_basic.py- Simple command executioncommands_sequencing.py- Execution order controlcommands_dependencies.py- Prerequisite enforcementcommands_next.py- Automatic command chainingcommands_required.py- Command-specific required parameterscommands_trigger.py- Parameter-triggered commandscommands_visibility.py- Hidden and framework commands
Phases
Commands execute in phases (setup → cleanup → execution → teardown → end):
from spafw37.constants.phase import *
spafw37.set_phases_order([
PHASE_SETUP,
PHASE_CLEANUP,
PHASE_EXECUTION,
PHASE_TEARDOWN,
PHASE_END,
])
Examples:
phases_basic.py- Default phase systemphases_custom_order.py- Custom phase orderingphases_extended.py- Extending default phasesphases_custom.py- Completely custom phases
Cycles
Repeat command sequences with loop control:
from spafw37.constants.cycle import *
{
COMMAND_NAME: 'process-all',
COMMAND_CYCLE: {
CYCLE_INIT: init_processing,
CYCLE_LOOP: has_more_items,
CYCLE_LOOP_START: prepare_next_item,
CYCLE_END: finalize_processing,
CYCLE_COMMANDS: ['validate', 'transform', 'save'],
}
}
Examples:
cycles_basic.py- Simple iteration patternscycles_loop_start.py- Per-iteration preparationcycles_nested.py- Multi-level nested cycles
Configuration
Access configuration values in your command actions:
def my_command():
# Get typed configuration values
name = spafw37.get_config_str('name')
count = spafw37.get_config_int('count')
enabled = spafw37.get_config_bool('enabled')
items = spafw37.get_config_list('items')
# Set configuration values
spafw37.set_config_value('status', 'processing')
Configuration can be:
- Set via command-line parameters
- Loaded from persistent config files (
config.json) - Saved to user config files (
--save-config,--load-config) - Managed at runtime within commands
Examples:
config_basic.py- Runtime configurationconfig_persistence.py- Persistent configuration
Logging
Built-in logging system with multiple levels:
from spafw37 import core as spafw37
spafw37.log_trace('scope', 'Detailed trace information')
spafw37.log_debug('scope', 'Debug information')
spafw37.log_info('scope', 'General information')
spafw37.log_warning('scope', 'Warning message')
spafw37.log_error('scope', 'Error message')
Control logging via built-in parameters:
--no-logging- Disable all logging--verbose- Enable verbose output--log-dir- Specify log directory
Inline Definitions
Define parameters and commands inline without separate dictionaries:
Examples:
inline_definitions_basic.py- Basic inline parameter and command definitionsinline_definitions_advanced.py- Advanced inline definition patterns
Output Handling
Custom output formatting and handlers:
Examples:
output_basic.py- Basic output formatting and displayoutput_handlers.py- Custom output handlers and formatters
See the Examples README for detailed descriptions and usage instructions.
Testing
Run the test suite:
pytest tests/ -v --cov=spafw37 --cov-report=term-missing
Requires 80% test coverage to pass.
Requirements
- Python 3.7 or higher
- No external dependencies for core functionality
- Development dependencies: pytest, pytest-cov, typing_extensions
Python 3.7 Compatibility
This framework is specifically designed for Python 3.7 compatibility:
- No type hints on function parameters or return types
- No walrus operator (
:=) - No positional-only parameters
- Compatible with Python 3.7.0+
Known Issues
Thread Safety
This framework is designed for single-threaded CLI applications. Module-level state variables in command.py, cycle.py, param.py, and config.py are not thread-safe. If you need to use this framework in a multi-threaded context, you must implement external synchronization around framework operations.
Reporting Bugs
Found a bug? Please report it on our GitHub Issues page.
When reporting bugs, please include:
- Python version (
python --version) - spafw37 version
- Minimal code to reproduce the issue
- Expected vs actual behavior
- Full error traceback (if applicable)
For feature requests and questions, also use GitHub Issues with the appropriate label.
License
This project is licensed under the MIT License - see the LICENSE.md file for details.
Copyright (c) 2025 Ciara Norrish (@minouris)
CI/CD
The project uses GitHub Actions for continuous integration:
- Test Workflow - Runs on every push/PR to main/develop
- Publish to TestPyPI - Automatic deployment on push to main with version auto-increment
See .github/workflows/README.md for details.
Author
Minouris (Ciara Norrish) (@minouris)
Support Me: Support me on Patreon at Minouris Does Stuff! I'll post news and previews of all my projects, software and otherwise, including this one!
Links
- Repository: https://github.com/minouris/spafw37
- Issues: https://github.com/minouris/spafw37/issues
- TestPyPI: https://test.pypi.org/project/spafw37/
- Documentation: https://github.com/minouris/spafw37/tree/main/doc
- Examples: https://github.com/minouris/spafw37/tree/main/examples
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 spafw37-1.0.0.tar.gz.
File metadata
- Download URL: spafw37-1.0.0.tar.gz
- Upload date:
- Size: 79.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b95e8b0bf41e4d89f4fa3ba05828340f69b142bb7f0dcb3c767a3960eff02fef
|
|
| MD5 |
c8e0bc7574a20ba02e450bb03c0e474f
|
|
| BLAKE2b-256 |
67ea9f09b619071af0a0c0aaab68e93ffb576a0a2c8670f541447d0c097254e9
|
Provenance
The following attestation bundles were made for spafw37-1.0.0.tar.gz:
Publisher:
release.yml on minouris/spafw37
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spafw37-1.0.0.tar.gz -
Subject digest:
b95e8b0bf41e4d89f4fa3ba05828340f69b142bb7f0dcb3c767a3960eff02fef - Sigstore transparency entry: 701729645
- Sigstore integration time:
-
Permalink:
minouris/spafw37@deef0a883f4f0520252982393ad4ea0e7178c6a1 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/minouris
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@deef0a883f4f0520252982393ad4ea0e7178c6a1 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file spafw37-1.0.0-py3-none-any.whl.
File metadata
- Download URL: spafw37-1.0.0-py3-none-any.whl
- Upload date:
- Size: 42.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01cdf8432e8d514cfd168dbfaf48b7d3aeb6fb94d2bd27e15c610eb77607e108
|
|
| MD5 |
0b9424d65b2cf8d3d9afaf711ba3d982
|
|
| BLAKE2b-256 |
e61dd66dac18a6b96b2f32723d1b06cf3e965ce6ac6789417dcafddbb7e4978f
|
Provenance
The following attestation bundles were made for spafw37-1.0.0-py3-none-any.whl:
Publisher:
release.yml on minouris/spafw37
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spafw37-1.0.0-py3-none-any.whl -
Subject digest:
01cdf8432e8d514cfd168dbfaf48b7d3aeb6fb94d2bd27e15c610eb77607e108 - Sigstore transparency entry: 701729647
- Sigstore integration time:
-
Permalink:
minouris/spafw37@deef0a883f4f0520252982393ad4ea0e7178c6a1 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/minouris
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@deef0a883f4f0520252982393ad4ea0e7178c6a1 -
Trigger Event:
workflow_dispatch
-
Statement type: