Skip to main content

a lib for describing Actions and how they should be performed

Project description

actionpack

a lib for describing Actions and how they should be performed

tests codecov publish

Overview

Side effects are annoying. Verification of intended outcome is often difficult and can depend on the system's state at runtime. Questions like "Is the file going to be present when data is written?" or "Will that service be available?" come to mind. Keeping track of external system state is just impractical, but declaring intent and encapsulating its disposition is doable.

Usage

What are Actions for?

Action objects are used to declare intent:

>>> action = Read('path/to/some/file')

Can Actions be connected?

A Result can be produced by performing an Action and that value can be percolated through a collection of ActionTypes using the Pipeline abstraction:

>>> pipeline = Pipeline(ReadInput('Which file? '), Read)

The above, is not the most helpful incantation, but toss the following in a while loop and witness some REPL-like behavior (bonus points for feeding it actual filenames/filepaths).

result = Pipeline(ReadInput('Which file? '), Read).perform()
print(result.value)

Sometimes ActionTypes in a Pipeline don't "fit" together. That's where the Pipeline.Fitting comes in:

>>> listen = ReadInput('What should I record? ')
>>> record = Pipeline.Fitting(
...     action=Write,
...     **{
...         'prefix': f'[{datetime.now()}] ',
...         'append': True,
...         'filename': filename,
...         'to_write': Pipeline.Receiver
...     },
... )
>>> Pipeline(listen, record).perform()

⚠️ NOTE: Writing to stdout is also possible using the Write.STDOUT object as a filename. How that works is an exercise left for the user.

Handling multiple Actions at a time

An Action collection can be used to describe a procedure:

>>> actions = [action,
...            Read('path/to/some/other/file'),
...            ReadInput('>>> how goes? <<<\n  > '),
...            MakeRequest('GET', 'http://google.com'),
...            RetryPolicy(MakeRequest('GET', 'http://bad-connectivity.com'),
...                        max_retries=2,
...                        delay_between_attempts=2)
...            Write('path/to/yet/another/file', 'sup')]
...
>>> procedure = Procedure(*actions)

And a Procedure can be executed synchronously or otherwise:

>>> results = procedure.execute()  # synchronously by default
>>> _results = procedure.execute(synchronously=False)  # async; not thread safe
>>> result = next(results)
>>> print(result.value)

A KeyedProcedure is just a Procedure comprised of named Actions. The Action names are used as keys for convenient result lookup.

>>> prompt = '>>> sure, I'll save it for ya.. <<<\n  > '
>>> saveme = ReadInput(prompt).set(name='saveme')
>>> writeme = Write('path/to/yet/another/file', 'sup').set(name='writeme')
>>> actions = [saveme, writeme]
>>> keyed_procedure = KeyedProcedure(*actions)
>>> results = keyed_procedure.execute()
>>> keyed_results = dict(results)
>>> first, second = keyed_results.get('saveme'), keyed_results.get('writeme')

⚠️ NOTE: Procedure elements are evaluated independently unlike with a Pipeline in which the result of performing an Action is passed to the next ActionType.

For the honeybadgers

One can also create an Action from some arbitrary function

>>> Call(closure=Closure(some_function, arg, kwarg=kwarg))

Development

Build scripting is managed via Makefile. Execute make commands to see the available commands. To get started, simply run make. Doing so will create a virtualenv loaded with the relevant dependencies. All tests can be run with make tests and a single test can be run with something like the following:

make test TESTCASE=<tests-subdir>.<test-module>.<class-name>.<method-name>

Making new actionpack.actions is straightforward. After defining a class that inherits Action, ensure it has an .instruction method. If any attribute validation is desired, a .validate method can be added.

There is no need to add Action dependencies to setup.py. Dependencies required for developing an Action go in :::drum roll::: requirements.txt. When declaring your Action class, a requires parameter can be passed a tuple.

class MakeRequest(Action, requires=('requests',)):
    ...

This will check if the dependencies are installed and, if so, will register each of them as class attributes.

mr = MakeRequest('GET', 'http://localhost')
mr.requests  #=> <module 'requests' from '~/actionpack/actionpack-venv/lib/python3/site-packages/requests/__init__.py'>

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

actionpack-1.6.0a2.tar.gz (21.6 kB view details)

Uploaded Source

File details

Details for the file actionpack-1.6.0a2.tar.gz.

File metadata

  • Download URL: actionpack-1.6.0a2.tar.gz
  • Upload date:
  • Size: 21.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.10.1 pkginfo/1.8.2 requests/2.27.1 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.10

File hashes

Hashes for actionpack-1.6.0a2.tar.gz
Algorithm Hash digest
SHA256 2361318bdca44b98a7b6421da2568f97d114df259b1fd199e088090785f358d7
MD5 f76bcaf5fe642e4ae73ddf2f350fe032
BLAKE2b-256 270e9c33ef4c5b7dae9d2f6e5a722edc72c15486d645dca174c098e01ba3a65e

See more details on using hashes here.

Supported by

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