Skip to main content

Create data orchestration pipelines over AWS Lambda functions from within python code using decorators

Project description

OrchWiPy

Create data orchestration pipelines over AWS Lambda functions from within python code using decorators.

  • No need for separate definition file, you define the orchestration + lambda functions in the code
  • Simpler data passing: only return changes to current state rather than the whole state
  • Visualize flow graph
  • Statically check state passing between steps
  • Generate AWS cloudformation template automatically
  • Run locally
  • Handle lambda invocation payload size limit transparently

We developed OrchWiPy at ask-next.com

Example

from orchwipy import MicroFunctions, ConditionalReturns, ReturnUpdates
from orchwipy import build_cfn_template

# Our pipeline starts at function 'start'
pipe = MicroFunctions(beginner="start")

@pipe.fn()
# pipe.fn() will convert this to a lambda function when deployed
def start(*, choice: str | None, **kwargs):
    # **kwargs is always needed

    beans = None
    if not choice:
        choice = input("Tea or coffee? (t/c)")
    if choice == 'c':
        beans = int(input("How much beans "))

    # if choice is 'c', then we go to 'coffee' else 'tea'
    return ConditionalReturns(
                choice,
                # default choice is 'tea'
                # "*" => passing no other arguments, required for first step
                ReturnUpdates("tea", "*"),
                # only include beans argument
                c=ReturnUpdates("coffee", "*", beans=beans))

@pipe.fn(template_props=dict(Layers=[{"Ref": 'MorePythonPackagesLayer'}]))
# MorePythonPackagesLayer will become a CFN template parameter
def tea(**kwargs):
    print("Have tea")
    # can also return a dictionary, with $next as the next function name
    return {"$next": "coffee", "beans": 5}

@pipe.fn()
def coffee(*, beans: int, **kwargs):
    print(f"Put {beans} beans into the coffee machine")
    # include all the arguments passed previously and add (update) the milk argument
    return ReturnUpdates("drink", milk=True)

@pipe.fn(terminates=True)
# terminates => specify that this may be the end of the pipe
def drink(*, milk: bool | None, **kwargs):
    if milk:
        print("Add some milk if its bitter")
    print("Drink!")
    return {}

# boilerplate: pass execution to pipe when running in AWS lambda
def lambda_handler(event, context):
    pipe.lambda_handler(event, context)

if __name__ == "__main__":
    pipe.check_graph("start")

    # local run
    pipe.run(choice=None)

    # check execution pipeline and generate a graphviz graph for visualization
    with open("example.dot", "w") as f:
        pipe.save_dot(f, top_node="start")

    # generate a aws cloudformation template for deployment
    with open("example.template.yml", 'w') as f:
        build_cfn_template(
            output_template=f,
            code_py_relative_to_template=__file__.split("/")[-1],
            plant=pipe,
            layers=[],
            policies=[],
        )

Visualization

It generates a graphviz dot diagram example.dot:

flowchart TD
    start["start"] --> cond1@{ label: "choice == 'c' ?" }
    tea["tea"] --> coffee["coffee"]
    coffee --> drink["drink"]
    cond1 -.-> coffee
    cond1 --> tea

    cond1@{ shape: rect}
    style cond1 stroke:#FFD600

Static checking

This involves checking all the execution paths to ensure that all the required function arguments are passed through all possible invocations.

In the above example, if we replace this line in function tea:

    return {"$next": "coffee", "beans": 5}

with

    return {"$next": "coffee"}

then the coffee step will not receive the beans argument when execution flows through start -> tea -> coffee . Invoking pipe.check_graph(start_at="start") will throw us this warning:

:0: MissingArgument: {'beans'} : caller history ['start', 'tea'] -> coffee

More Examples

tests/sample.py is another example:

flowchart TD
    welcome["welcome"] --> cond1@{ label: "choice = 't' ?" } & cond2@{ label: "choice = 'c' ?" } & cond3["choice = * ?"]
    making_tea["making_tea"] --> boil_water["boil_water"]
    making_coffee["making_coffee"] --> boil_water
    boil_water --> cond4@{ label: "choice = 'coffee' ?" }
    grind["grind"] --> pour_water["pour_water"]
    dip["dip"] --> pour_water
    pour_water --> drink["drink"]
    cond1 --> making_tea
    cond2 --> making_coffee
    cond3 --> nochoice["nochoice"]
    cond4 --> grind
    cond4 -.-> dip

    cond2@{ shape: rect}
    cond4@{ shape: rect}
    style cond1 stroke:#00C853
    style cond2 stroke:#00C853
    style cond3 stroke:#00C853
    style cond4 stroke:#2962FF

Why

Building complex backend pipelines AFAIK requires creating pipeline definitions separate from the code itself. In my experience, this creates a lot of technical baggage. Futher, serverless backends (with AWS Lambda) gives us a lot of scalability but its hard to run locally. Also, Step Functions are great but without static checking, its very easy to get missing data errors while in production.

Orchwipy tries to solve all these issues by having your code as the single source of truth about your data pipeline. Deployment templates, static checking, ... everything simply derives from it.

At ask-next.com Orchwipy orchestrates our data pipeline with ~50 services

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

orchwipy-0.1.0.tar.gz (20.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

orchwipy-0.1.0-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: orchwipy-0.1.0.tar.gz
  • Upload date:
  • Size: 20.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.7

File hashes

Hashes for orchwipy-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a4042152389c517b975a8025d7b4df9e001187d94f9407c4d836a828c0af4185
MD5 3e77cbd548807bffe820af8febc562c7
BLAKE2b-256 441fbcbdc1ab3d2f1f8b3fd080fb125850bd04e60307ea6da1921fa183b7fe3b

See more details on using hashes here.

File details

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

File metadata

  • Download URL: orchwipy-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.7

File hashes

Hashes for orchwipy-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0f98bfe6499d667bdf1ec9a387cd60fd2031d1d2ebb90b40891de03355d3febf
MD5 441783bdbdff0c0938b48512c7f47fb6
BLAKE2b-256 72ba8f8791773c12196a9bab9c717a0cf2284d774617d01ab46583b5d1da2e6a

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