Nuclear is a binding glue for CLI applications. It consists of tools for building CLI applications in Python, including:
- CLI parser for building nested CLI commands
- Sublog - contextual logger
- Shell utilities
from nuclear import CliBuilder
cli = CliBuilder()
@cli.add_command('hello')
def say_hello(name: str, decode: bool = False, repeat: int = 1):
"""
Say hello
:param decode: Decode name as base64
"""
message = f"I'm a {b64decode(name).decode() if decode else name}!"
print(' '.join([message] * repeat))
@cli.add_command('calculate', 'factorial')
def calculate_factorial(n: int):
"""Calculate factorial"""
print(reduce(lambda x, y: x * y, range(1, n + 1)))
@cli.add_command('calculate', 'primes')
def calculate_primes(n: int):
"""List prime numbers using Sieve of Eratosthenes"""
print(sorted(reduce((lambda r, x: r - set(range(x**2, n, x)) if (x in r) else r), range(2, n), set(range(2, n)))))
cli.run()
See demo.py for a complete example.
python3 -m pip install --upgrade nuclear
You need Python 3.8 or newer.
Sublog is a nuclear's contextual logging system that allows you to:
- display variables besides log messages:
logger.debug('message', airspeed=20)
, - wrap errors with context:
with add_context('ignition')
, - catch errors and show traceback in a concise, pretty format:
with error_handler()
.
from nuclear.sublog import logger, error_handler, add_context
with error_handler():
logger.debug('checking engine', temperature=85.0, pressure='12kPa')
with add_context('ignition', request=42):
logger.info('ignition ready', speed='zero')
with add_context('liftoff'):
raise RuntimeError('explosion')
Use nuclear.sublog.logger
to log message with a pretty format out of the box.
Pass additional context variables as keyword arguments to display them in the log message.
from nuclear.sublog import logger
logger.info('info log')
logger.debug('debug log', var1=1, var2='two')
logger.info('not great not terrible', radioactivity=3.6)
logger.error('this is bad')
logger.exception(RuntimeError('this is worse'))
Use nuclear.sublog.error_handler
to catch errors and show traceback in a concise, pretty format.
from nuclear.sublog import error_handler
with error_handler():
raise RuntimeError('explosion')
Use nuclear.sublog.add_context
to wrap code with additional context information.
This will be included in in the log message, if an error occurs.
from nuclear.sublog import add_context
with add_context('reloading plugins'):
with add_context('loading config'):
raise RuntimeError('file is missing')
This will produce an error with the following message:
reloading plugins: loading config: file is missing
Note that while each individual part of the message may not provide a comprehensive explanation of the error, when combined, the whole message becomes highly informative. This is the core principle behind enriching errors with context.
Nuclear provides utilities for running system shell commands.
Basic usage:
from nuclear import shell
window_id: str = shell('xdotool getactivewindow')
shell
function captures the stdout & stderr output of the shell command and returns it as a string.
It may also print live stdout in real time (line by line) and capture output in case of errors.
It has a lot of possibilities thanks to its parameters:
cmd: str
- shell command to runworkdir: Optional[Path] = None
- working directory for the commandprint_stdout: bool = False
- whether to print live stdout in real time (line by line) from a subprocessprint_log: bool = False
- whether to print a log message about running the commandraw_output: bool = False
- whether to let subprocess manage stdout/stderr on its own instead of capturing itindependent: bool = False
- whether to start an independent process that can outlive the caller processoutput_file: Optional[Path] = None
- optional file to write the output in real time
It returns the stdout of the command combined with stderr.
In case of non-zero command exit code, shell
raises CommandError
exception.