Skip to main content

service-flow

A small, simple, and elegant component/function orchestration framework for Python, service-flow enables separation of a functionality into single-responsibility and bite-size functions/service objects, which are testable and reuseable. It further helps with readability and maintainability of the embedding application. It supports both sync and async services.

Implementation

service-flow has two parts -- a flow and multiple services (Note: the word service here refers to a function, not RESTful services):

flow

a flow is the definition of processing procedure. It is defined as the following:

basic example (>>)

flow = Service1() >> \
        Service2(*args) >> \
        ...
        ServiceN(**kwargs)

output = flow(input) ## input is a dictionary with all the input parameters as attributes

Python >> operator is overloaded to define the sequence of the processing. Service1(), ..., ServiceN() are instances of the services, or functions.

for each processing of the input, service-flow creates a context, a dictionary-like object that serves as input and gather outputs from all the services.

fork example (<)

flow = Service1() >> \
        Service2(*args) < \
        ('context_var_name', {
            'var_value1': (Service3() >> Service4(**kwargs)),
            'var_value2': (Service5() >> Service4(**kwargs)),
          }
        )

output = flow(input) ## input is a dictionary with all the input parameters as attributes

Python < operator is overloaded to define a fork in processing. In this example, context_var_name is the name of context key, and var_value1, var_value2 are potential values for forking.

A fork can not be the first service in a flow definition.

services

A service is the equivalent of a Python function.

example

basic service

A element in a list of services that composes the flow. it inherits from service_flow.middleware.Middleware.

class InplaceModification(Middleware):
    def __init__(self, increment): # initialization
        self.increment = 1

    def __call__(self, bar: list): # service call
        return {'bar': [i + self.increment for i in bar]}
decorator service

As the name implies, decorator service works similiarly to a python decorator that is nested on top of the subsequent flow.

A decorator service inherits from service_flow.middleware.DecoratorMiddleware and has one additional instance variable app, which is the reference to the subsequent flow.

class ExceptionHandler(DecoratorMiddleware):
    def __call__(self, context):
        try:
            self.app(context)
        except ZeroDivisionError:
            return {'error': 'decided by zero'}

async service

As asyncio was introduced in python version 3.4, service-flow now supports async middlewares. here is example:

import aiohttp

class GetPythonSiteHTML(Middleware):
    def __init__(self, increment):
        self.increment = increment

    async def __call__(self, bar: list):
      async with aiohttp.ClientSession() as session:
          async with session.get('http://python.org') as response:
              html = await response.text()
      return await {'response': html}

lambda service

service-flow supports simple functionality as a service in the form a lambda type. Note the following:

  1. a lambda service is always sync
  2. a lambda service can not be the first service on stack.

conventions

It needs to follow the convention below:

initialization parameters

parameters that initializes the service and is shared for all the calls to the service.

function parameters

These parameters are used as inputs to a service. They normally have to be existing attributes in the context object.

The only exception is if there is only one parameter and its name is "context", in which case the entire context dictionary will be passed in as the value of the context parameter.

return value

the return value of a service is optional. If a service does return values:

  1. if it is a dictionary, it will add/update the aforementioned processing context.
  2. otherwise, the return value is ignored and a warning message is logged.

exceptions

service-flow raises a few types of exceptions:

  1. StopFlowException: raised inside a service to signal stop processing. typical use cases include when incoming request is invalid.
  2. RetryException: raised inside a service to signal re-processing of the same request. typical use cases include when a http request issued from the service times out or database transaction level violation.
  3. FatalException: raised inside a service to restart the processor. typical use cases include when database connection is broken or other infrastructure related errors.

inspiration

service-flow draws inspiration from the following Ruby projects:

  1. Rack
  2. Light Service
  3. Ruby Middleware

TODOs

install

pip install service-flow

Metadata

Release files for service-flow 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for service-flow 0.3.1
File Size Uploaded Provenance
service_flow-0.3.1.tar.gz 9.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for service-flow 0.3.1
File Interpreter ABI Platform
service_flow-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 17.0 kB

Release files / service_flow-0.3.1.tar.gz

Download URL service_flow-0.3.1.tar.gz
Size 9.5 kB
Tags Source
SHA-256 checksum
How to use checksums
ea560b47f1cb059e643521ef475eda288c12ff03e5e96c0be29ad36d4d0f04c0
BLAKE2b-256 checksum
How to use checksums
0650a993ce8017e0560fa3a63d45b161c78352e92c484a7d350822318e994637
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.8.0

Release files / service_flow-0.3.1-py3-none-any.whl

Download URL service_flow-0.3.1-py3-none-any.whl
Size 7.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d97edc2fc5afd3af2a56f70f3077f360818807be326d050436a97c207d6370af
BLAKE2b-256 checksum
How to use checksums
e56ae620b0bccb8f4a6c8a2be14c2b6bb45fede84338f06959703c2ecbbd643c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.8.0

Security for service-flow 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page