Module tenlib.flow
Main input/output functions.
Here's a base ten python file layout:
#!/usr/bin/env python
from ten import *
@entry
def main():
...
main()
The main coroutine's parameters are automatically mapped to a argparse
argument (see entry()).
To display messages, use msg_* functions (msg_info() or msg_success() for
instance).
If you script fails, you can stop the execution at any time using leave(),
failure() or error(). Additionally, use assume() to ensure a condition is valid.
If it is not, the script will exit.
Execution can be paused using pause(), or delayed using sleep().
To inform the user of progress in real time, use msg_status(), progress(), or
track(), or use the inform() decorator.
Most of the heavy lifting for display is done by the
rich library.
Sub-modules
tenlib.flow.console-
Stores the console object used by ten.
tenlib.flow.messageformatter-
This module provides different styles to display
msg_*messages …
Functions
def entry(entrypoint: Callable[..., ~FRetType]) ‑> Callable[[], ~FRetType]-
Converts the given coroutine or class into the program's entry point. Command line arguments are mapped to the parameters of the function or the object's
__init__. Also,TenExitandKeyboardInterruptexceptions will be caught and displayed nicely.Default values, as well as type hinting, are supported.
The generic look of the program with a coroutine is:
#!/usr/bin/env python3 from ten import * @entry def main(): ... main()And with a class:
#!/usr/bin/env python3 from ten import * @entry class Program: def __init__(self, ...): ... def run(self): ... Program()Examples
Creating a script with 1 required parameter, and 2 optional parameters:
#!/usr/bin/env python3 from ten import * @entry def exploit(url, username='admin', password='password'): """Exploits a post-authentication vulnerability on given URL. """ msg_info(f'URL: {url}') msg_info(f'Username: {username}') msg_info(f'Password: {password}') ... exploit()Running the script:
$ /tmp/exploit.py -h usage: boo.py [-h] [-u USERNAME] [-p PASSWORD] url Exploits a post-authentication vulnerability on given URL. positional arguments: url optional arguments: -h, --help show this help message and exit -u USERNAME, --username USERNAME -p PASSWORD, --password PASSWORD $ /tmp/exploit.py 'http://target.com' ⊙ URL: <http://target.com> ⊙ Username: admin ⊙ Password: password $ /tmp/exploit.py 'http://target.com' -u user1000 ⊙ URL: <http://target.com> ⊙ Username: user1000 ⊙ Password: password $Alternatively, with a class:
#!/usr/bin/env python3 from ten import * @entry class Exploit: def __init__(self, url, username='admin', password='password'): self.url = url self.username = username self.password = password def run(self): msg_info(f'URL: {self.url}') msg_info(f'Username: {self.username}') msg_info(f'Password: {self.password}') ... Exploit()Other supported functions declaration:
@entry def exploit(url, table=None, fields=['user()', 'version()']): """Gets a URL, an SQL table (str) and a list of SQL fields. """ ... @entry def sum_all(numeric_values: list[int]): """Takes a list of at least one int value as input. """ msg_info(f'Sum: {sum(numeric_values)}') @entry def sum_all(numeric_values: [123.4, 567.8]): """Takes a list of at least one float value as input. """ msg_info(f'Sum: {sum(numeric_values)}') @entry def add(a=3.12, b=4.12): """Adds two float numbers. """ msg_success(f'Sum: {a + b}') @entry def force_opts(a, *, b, c): """Forces b and c to be optional arguments. """ msg_success(f'Options: b={b!r} c={c!r}') def arg(name: str, description: str) ‑> Callable[[~FCallable], ~FCallable]-
Provides documentation for the parameter
nameof theentry()function. The documentation is visible when the program is run with the--helpflag.Example:
@entry @arg("name", "The name of the person to greet") def hello(name: str): msg_info(f"Hello, {name}!") def msg_info(*objects, **kwargs)-
Displays an info message.
def msg_failure(*objects, **kwargs)-
Displays a failure message.
def msg_error(*objects, **kwargs)-
Displays an error message.
def msg_success(*objects, **kwargs)-
Displays a success message.
def msg_warning(*objects, **kwargs)-
Displays a warning message.
def msg_print(*objects, **kwargs)-
Displays a message.
def msg_debug(*objects, **kwargs)-
Displays a debug message.
def msg_clear()-
Clears the current line.
def msg_status(status: Union[rich.console.ConsoleRenderable, rich.console.RichCast, str], *, spinner: str = 'dots', spinner_style: str = 'status.spinner', speed: float = 1.0, refresh_per_second: float = 12.5) ‑> rich.status.Status-
Display a status and spinner. See rich's
Console.status.Example
with msg_status("Doing something..."): # Do something slow ...Args
status:RenderableType- A status renderable (str or Text typically).
spinner:str, optional- Name of spinner animation (see python -m rich.spinner). Defaults to "dots".
spinner_style:StyleType, optional- Style of spinner. Defaults to "status.spinner".
speed:float, optional- Speed factor for spinner animation. Defaults to 1.0.
refresh_per_second:float, optional- Number of refreshes per second. Defaults to 12.5.
Returns
Status- A Status object that may be used as a context manager.
def bin_print(data: bytes)-
Prints bytes to stdout.
def leave(message=None, *args, **kwargs) ‑> NoReturn-
Displays a message if given, and exits.
Raises
TenExit- to exit
def failure(message: str = None) ‑> NoReturn-
Displays a failure message if given, and exits.
Raises
TenFailure- to exit
def error(message: str = None) ‑> NoReturn-
Displays an error message if given, displays the stack trace, and exits.
Raises
TenError- to exit
def assume(assumption: bool, message: str = 'Assumption failed') ‑> None-
If given assumption is
False, raisesTenFailure. This function is the equivalent ofassert, but it'll display a standard failure message and exit, without displaying a stack trace.In other words, if the assumption being false means the program has failed, but is not an error per-se, use
assume(). If you want an exception to be raised, use the built-inassert.This allows you to simplify code such as:
match = response.re.search(r'token="(.*?)"') if not match: failure('Response does not contain a token')to:
match = response.re.search(f'token="(.*?)"') assume(match, 'Response does not contain a token')Args
assumption:bool- Assumption to evaluate
message:str- Message for the
TenFailureexception
def inform(go: str = None, ok: str = None, ko: str = None, ko_exit: bool = False) ‑> Callable[[~FCallable], ~FCallable]-
This decorator will display a spinner and a message while a coroutine is running, and optionally a message after it is done.
Args
go:str- Message to display while the coro executes
ok:str- Message to display if the coro returns a result that evaluates
to
True. ko:str- Message to display if the coro returns a result that evaluates
to
False. ko_exit:bool- If set, the execution will stop if the coro does not
find a result. Defaults to
False.
okandkocan use the{result}format to display the result.Examples
Decorate a function that bruteforces a seed:
@inform( go='Bruteforcing seed...', ok='Seed found: {result}', ko='Unable to find seed' ) def find_seed(token): for i in range(1000000): if hashing.md5(i) == token: return i return NoneDecorate a login procedure: if it fails, the script will exit.
@inform( go='Trying to log in...', ok='Login successful', ko='Login failed, exiting', ko_exit=True ) def login(session, user, password): r = await session.post('/login', {'u': user, 'p': password}) return r.contains('login successful') def pause(message: str = 'Paused. Press ENTER to resume execution') ‑> None-
Pauses the execution of the program until
ENTERis pressed.Args
message:str- A message to display before pausing
def sleep(seconds) ‑> None-
Alias for
time.sleep. def progress(transient: bool = True, **kwargs) ‑> rich.progress.Progress-
A
Progressbar.Args
transient:bool- Clear the progress on exit. Defaults to False.
**kwargs- see the documentation for
Progress.
def track(sequence: Iterable[~FIterableItem], description: str = 'Working...', total: Optional[float] = None, transient: bool = True) ‑> Generator[~FIterableItem, None, None]-
Track progress when iterating over a sequence. This is a wrapper for rich's
track.Example
for i in track(range(100), "Iterating over 100 numbers"): # do something ...Args
sequence:Iterable- A sequence (must support
len()) you wish to iterate over. description:str, optional- Description of task show next to progress bar. Defaults to "Working".
total:float, optional- Total number of steps. Default is len(sequence).
transient:bool, optional- Clear the progress on exit. Defaults to True.
Returns
Iterable- The original iterable.
def ask(prompt: Union[str, ForwardRef('Text')] = '', *, password: bool = False, choices: Optional[list[str]] = None, show_default: bool = True, show_choices: bool = True, default: Any = Ellipsis) ‑> Any-
Shortcut to construct and run a prompt loop and return the result. Wrapper for
rich.prompt.Prompt.Example
>>> filename = ask("Enter a filename")Args
prompt:TextType, optional- Prompt text. Defaults to "".
password:bool, optional- Enable password input. Defaults to False.
choices:list[str], optional- A list of valid choices. Defaults to None.
show_default:bool, optional- Show default in prompt. Defaults to True.
show_choices:bool, optional- Show choices in prompt. Defaults to True.
def trace(function: ~FCallable) ‑> ~FCallable-
Decorator that displays a debug log line containing the function name, its parameters and the value returned.
def set_message_formatter(message_formatter: Union[MessageFormatter, Type[MessageFormatter], str]) ‑> None-
Sets given
MessageFormatteras the message formatter. def set_random_message_formatter() ‑> None-
Sets a random
MessageFormatteras the message formatter.