Skip to main content

Stakk is designed to stack functions into a register, this registered stack is designed to be consumed by a stack of modular utilities.

Project description

Documentation Status codecov DeepSource

Description

Stakk is designed to stack functions into a register, the registered stack is designed to be consumed by a stack of modular utilities. This pattern is intended to facilitate a lower barrier for entry to configure command line interfaces, benchmark tasks, and manage threaded agents.


Quick Start:

register functions with stakk

Using the register decorator @stakk.register('stack_id') on your functions will register it with a stack in meta_handler.Stack. Functions in a stack are available for utilities to register and create a link to the stack.

import stakk

@stakk.register('stack_id')
def add(x : int, y : int):
    '''add two integers'''
    return x + y

You can also register async functions, these will be executed using asyncio.run() given a valid coroutine function.

import stakk
import asyncio

@stakk.register('stack_id')
async def delay_add(x : int, y : int):
    '''add two integers after 1 sec'''
    await asyncio.sleep(1)
    return x + y

cli - initialization standard

It is suggested to define the command line interface after if __name__ == '__main__'. Any code before the cli will run even if a cli command is used; code after the cli definition will not run when passing a cli command. The cli initialization will require 1 argument a stack_id, optionally you can provide a description it is suggested to use doc strings for this and init as shown.

import stakk

# registered functions...

# module level function calls...

if __name__ == '__main__':
    # main code (will run even when using cli commands)...
    stakk.cli(stack_id = 'stack_id', desc = __doc__) # desc is optional
    # main code (will NOT run when using cli commands)...

cli - args and optional args

Adding a default value will create an optional arg for the command. The keyword and default value will be displayed in the help docs along with the type if one is given.

"""This module does random stuff."""
import stakk

@stakk.register('cli')
def meet(name : str, greeting : str = 'hello', farewell : str = 'goodbye') -> str:
    '''meet a person'''
    return f'\n{greeting} {name}\n{farewell} {name}'

# module level function calls...

if __name__ == '__main__':
    # main code (will run even when using cli commands)...
    stakk.cli(stack_id = 'cli', desc = __doc__)
    # main code (will NOT run when using cli commands)...

NOTE: Adding type hinting to your functions enforces types in the cli and adds positional arg class identifiers in the help docs for that command.

Command usage:

python module.py meet foo

Output:

hello foo
goodbye foo

Module help output:

usage: module [-h] {meet} ...

This module does random stuff.

options:
-h, --help  show this help message and exit

commands:
{meet}
    meet      meet a person

Command help output:

usage: module meet [-gr GREETING] [-fa FAREWELL] [-h] name

meet(name: str, greeting: str = 'hello', farewell: str = 'goodbye') -> str

positional arguments:
name                  type: str

options:
-gr GREETING, --greeting GREETING
                        type: str, default: hello
-fa FAREWELL, --farewell FAREWELL
                        type: str, default: goodbye
-h, --help            Show this help message and exit.

cli - choice arguments

Adding an iterable as the type annotation will define a choices argument. A custom type checker is defined based on the option types in the iterable provided. This will allow you to define mixed types in your choices lists.

"""This module does random stuff."""
import stakk

foo_choices = ['foo', 'fooo','foooo']
bar_choices = ('bar', 1, 'baar', 2)

@stakk.register('cli')
def foo_choices(foo: foo_choices, bar: bar_choices = 2) -> tuple:
    '''foo bar'''
    return foo, bar

# module level function calls...

if __name__ == '__main__':
    # main code (will run even when using cli commands)...
    stakk.cli(stack_id = 'cli', desc = __doc__)
    # main code (will NOT run when using cli commands)...

Command usage:

python module.py foo_choices fooo --bar 1

Output:

('fooo', 1)

Command help output:

usage: module foo_choices [-ba BAR] [-h] foo

foo_choices(foo: choice_type, bar: choice_type = 2) -> tuple

positional arguments:
foo                 choices: (foo, fooo, foooo)

options:
-ba BAR, --bar BAR  choices: (bar, 1, baar, 2), default: 2
-h, --help          Show this help message and exit.

cli - list annotation

Using list as a type annotation has special context. This will prompt the cli to define a custom list type which returns re.split(r'[;,| ]', value). This allows you to specify string with delimiters which are converted to lists before being passed to the function. You are welcome to create and pass your own type functions but lists are built in for ease of use.

"""This module does random stuff."""
import stakk

@stakk.register('cli')
def foo_lists(foo : list, bar : list = ['foo','bar']) -> tuple:
        return foo, bar

# module level function calls...

if __name__ == '__main__':
    # main code (will run even when using cli commands)...
    stakk.cli(stack_id = 'cli', desc = __doc__)
    # main code (will NOT run when using cli commands)...

Command usage:

python module.py foo_lists 'foo,bar;foo|bar foo'

Output:

(['foo', 'bar', 'foo', 'bar', 'foo'], ['foo', 'bar'])

Command help output:

usage: module foo_lists [-ba BAR] [-h] foo

foo_lists(foo: type_list, bar: type_list = ['foo', 'bar']) -> tuple

positional arguments:
foo                 type: list

options:
-ba BAR, --bar BAR  type: list, default: ['foo', 'bar']
-h, --help          Show this help message and exit.

cli - variadic functions

Variadic functions are compatible with stakk cli utility. When calling kwargs from the cli; key=value should be used instead of -- and -, these are reserved for default arguments.

NOTE: providing type annotations will enforce type, however this will apply to all *args or **kwargs, if custom logic is needed you can create and pass a custom type function.

"""This module does random stuff."""
import stakk

@stakk.register('cli')
def variadic(*args: str, **kwargs: int):
    
    print("Positional arguments:")


    for arg in args:
        print(arg)

    print("Keyword arguments:")
    for key, value in kwargs.items():
        print(f"{key} = {value}")

# module level function calls...

if __name__ == '__main__':
    # main code (will run even when using cli commands)...
    stakk.cli(stack_id = 'cli', desc = __doc__)
    # main code (will NOT run when using cli commands)...

Command usage:

python module.py variadic foo bar foo foo=1 bar=2

Output:

Positional arguments:
foo
bar
foo

Keyword arguments:
foo = 1
bar = 2

Command help output:

usage: dev variadic [-h] [*args ...] [**kwargs ...]

variadic(args, kwargs)

positional arguments:
*args       ex: command arg1 arg2
**kwargs    ex: command key=value

options:
-h, --help  Show this help message and exit.

benchy - usage example

The benchy decorator is designed to collect performance timing and call info for selected functions. This can be used in combination with @stakk.register, the decorators are order independent.

import stakk
import asyncio

@stakk.benchy
@stakk.register('test_stack')
def add(x : int, y : int):
    '''add two integers'''
    return x + y

@stakk.register('test_stack')
@stakk.benchy
def subtract(x : int, y : int):
    '''subtract two integers'''
    return x - y

@stakk.benchy
@stakk.register('test_stack')
def calc(x : int, y : int, atype : str = '+') -> int:
    '''calculates a thing'''
    if atype == '+':
        res = add(x, y)
    elif atype == '-':
        res = subtract(x, y)
    return res

@stakk.register('test_stack')
@stakk.benchy
async def async_example():
    '''An example async function'''
    await asyncio.sleep(1)
    print("Async function completed.")

add(1,2)
add(2,2)
subtract(1,2)
calc(2,3, atype='-')
asyncio.get_event_loop().run_until_complete(async_example())

After the functions have been executed, the benchmark report can be accessed with stakk.benchy.report.

# print the benchmark report
print(stakk.benchy.report)

Example output:

{
    'add': [
        {
            'args': [{'type': 'int', 'value': 1}, {'type': 'int', 'value': 2}],
            'benchmark': 0.00015466799959540367,
            'kwargs': None,
            'result': {'type': 'int', 'value': 3}
        },
        {
            'args': [{'type': 'int', 'value': 2}, {'type': 'int', 'value': 2}],
            'benchmark': 6.068096263334155e-05,
            'kwargs': None,
            'result': {'type': 'int', 'value': 4}
        }
    ],
    'calc': [
        {
            'args': [{'type': 'int', 'value': 2}, {'type': 'int', 'value': 3}],
            'benchmark': 4.855601582676172e-05,
            'kwargs': {'atype': {'length': 1, 'type': 'str'}},
            'result': {'type': 'int', 'value': 5}
        }
    ],
    'subtract': [
        {
            'args': [{'type': 'int', 'value': 1}, {'type': 'int', 'value': 2}],
            'benchmark': 5.205394700169563e-05,
            'kwargs': None,
            'result': {'type': 'int', 'value': -1}
        }
    ],
    'async_example': [
        {
            'args': None,
            'benchmark': 1.001522845996078,
            'kwargs': None,
            'result': {'type': 'NoneType', 'value': None}
        }
    ]
}

The output of the benchmark report will adhere to the following format. function : call report. Call reports consist of {args, kwargs, result, benchmark} there will be a record for each call of a given function.

NOTE: given an iterable for arg, kwarg, or result the object will be summarized in terms of vector length.

{
    'function_name': [
        {
            'args': [{'type': 'arg_type', 'value': int}],
            'benchmark': float,
            'kwargs': {'kwarg_name': {'type': 'arg_type', 'length': int}},
            'result': {'type': 'arg_type', 'value': float}
        }
    ]
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stakk-0.1.0.tar.gz (15.4 kB view details)

Uploaded Source

Built Distribution

stakk-0.1.0-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

File details

Details for the file stakk-0.1.0.tar.gz.

File metadata

  • Download URL: stakk-0.1.0.tar.gz
  • Upload date:
  • Size: 15.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/4.0.2 CPython/3.11.4

File hashes

Hashes for stakk-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c6e64eb529be45b1da5a88e1b16dfb82849663b69e7b9b6684731a3435c47a56
MD5 a7741a9b20ae64206c6ecc99ca18dfd2
BLAKE2b-256 fdcadff499ec372720d3feedab58396c72510794b26135bbd24bd80eb7c9b0c2

See more details on using hashes here.

File details

Details for the file stakk-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: stakk-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/4.0.2 CPython/3.11.4

File hashes

Hashes for stakk-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 82702e4663260d2929b59512d5cc4db94c8e41390c7393a3469b28d3cd381107
MD5 525a9526e60a73e82487914078eb3d8e
BLAKE2b-256 8487c2101c01048c75a1ea720d3389f443b9a8f5b68b2cb1104e2f06843328b5

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page