Skip to main content
marx
=====

Framework/tools for python that get work done, make code easy to test,
and turn spaghetti logic back into something you can love.

The modeling here provides clear contracts, state encapsulation,
and strict typing.


Example
-------

You'll find the [full example](./tests/workflow/example_1.py) in the tests/ directory.

"""
This defines a workflow around the imperative: "Throw a Pie".

In a simple system this is about 5 lines of code. But in others,
where business logic requires the interaction of many systems,
logic is smeared around and duplicated, and not well encapsulated.

The approach here splits execution into discrete units which can be
composed into other workflows encouraging reuse, testability, etc.
"""


from marx.workflow.step import LogicUnit, ArgSpec, Step, ResultSpec
from marx.workflow.flow import Workflow
from marx.workflow.context import DefaultContext, Field
from tests.workflow.example_objects import User, PermissionDeniedError


class IsUserAuthorized(LogicUnit):
"""Checks permission for a user+action, and notifies authorities
if it fails."""

user = ArgSpec(User, docs="The user performing the action")

def __init__(self, action):
"""
@param action: The action that will be checked.
"""
self.action = action

def __call__(self, user):
"""
@param user: The user performing the action.
"""
if self.is_authorized(user):
return
self.notify_authorities(user, self.action)
raise PermissionDeniedError(self.action)

def is_authorized(self, user):
return user.name in ("bob", "mary")

def notify_authorities(self, user, action):
print "AUTHORITIES!!!", user, " attempted illegal action", self.action


class MakePie(LogicUnit):
"""Makes the pie."""

maker = ArgSpec(User, docs="The person making pie.")
pie = ResultSpec(basestring, docs="Kind of pie")

def __call__(self, maker):
maker.increment("pies_made", 1)
self.result.pie = 'lemon'


class ThrowThing(LogicUnit):
"""Subject Object (Verb) Indirect-object"""

actor = ArgSpec(User)
hit = ResultSpec(bool, default=False, docs="Did we get 'em?")

# we omit target and thing here, because we don't
# need to enumerate/type constrain the values in this example

def __call__(self, actor, thing, target):
actor.increment("things_throw")
print "Throwing", thing
self.result.hit = actor.can_throw()
# we don't need to return, but we can.
return self.result


class ThrowPieContext(DefaultContext):
"""The execution context for the ThrowPieWorkflow."""
thrower = Field(User, docs="Somebody has to throw it")
target = Field(User, docs="At somebody")
pie = Field(str, docs="A pie, which we make along the way")
was_hit = Field(bool, docs="Success of the throwing event")

""" A workflow is a series of steps."""
IsUserAuthorizedStep = Step(
IsUserAuthorized("throw_pie"),
# we bind from the context to the arguments of the method.
arg_map={IsUserAuthorized.USER: ThrowPieContext.THROWER}
)
MakePieStep = Step(
MakePie(),
arg_map={MakePie.MAKER: ThrowPieContext.THROWER},
# we bind from the returned result back to the context
result_map=MakePie.ResultMap(ThrowPieContext)
)
ThrowThingStep = Step(
ThrowThing(),
arg_map=ThrowThing.AutoMap({ThrowThing.ACTOR: ThrowPieContext.THROWER,
ThrowThing.THING: ThrowPieContext.PIE}),
result_map={ThrowPieContext.WAS_HIT: 'hit'}
)


""" There are a few ways to build up a workflow. By constructor..."""
ThrowPieWorkflowA = Workflow(steps=[
IsUserAuthorizedStep,
MakePieStep,
ThrowThingStep,
])

""" Or using add_step..."""
ThrowPieWorkflowB = Workflow().add_step(
IsUserAuthorizedStep
).add_step(
MakePieStep
).add_step(
ThrowThingStep
)

""" Or using the overloaded addition operator."""
EmptyWorkflow = Workflow()
ThrowPieWorkflowC_A = EmptyWorkflow + IsUserAuthorizedStep + MakePieStep
ThrowPieWorkflowC_B = EmptyWorkflow + ThrowThingStep
ThrowPieWorkflowC = ThrowPieWorkflowC_A + ThrowPieWorkflowC_B


def run():
"""To execute a workflow, prepare a context, and pass it through."""
ctx = ThrowPieContext()
ctx.thrower = User("bob")
ctx.target = User("frank")
for WorkflowType in (ThrowPieWorkflowA,
ThrowPieWorkflowB,
ThrowPieWorkflowC):
try:
WorkflowType(ctx)
assert ctx.was_hit is not None
assert ctx.pie == 'lemon'
return ctx
except PermissionDeniedError:
assert False

# Ensure that our ThrowPieWorkflowC did not modify its components
assert EmptyWorkflow.steps == []
assert ThrowThingStep not in ThrowPieWorkflowC_A.steps

Metadata

Release files for marx-workflows 0.0.8

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

Source distribution (sdist)

Source distribution for marx-workflows 0.0.8
File Size Uploaded Provenance Actions
marx-workflows-0.0.8.tar.gz 19.6 kB Without provenance Details for marx-workflows-0.0.8.tar.gz

Release files / marx-workflows-0.0.8.tar.gz

Download URL marx-workflows-0.0.8.tar.gz
Size 19.6 kB
Tags Source
SHA-256 checksum
How to use checksums
69cbd506276c613d7f1d766eff3182597d7d8c1d989571a435e3cb5c77833039
BLAKE2b-256 checksum
How to use checksums
e1e39a5a9a1c67edd2359f5a2782a533b92d256faa0835aee34baff242f6625b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No

Security for marx-workflows 0.0.8

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.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.0.8 This release

1 release file No provenance

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