Core API

This chapter describes the core module of MorphoCut.

Pipelines

The Pipeline is the main entry point for a MorphoCut application.

class morphocut.core.Pipeline(parent=None)[source]

A Pipeline manages the execution of nodes.

Nodes defined inside the pipeline context will be added to the pipeline. When the pipeline is executed, stream objects are passed from one node to the next in the same order.

Parameters

parent (Pipeline, optional) – A parent pipeline to attach to. If None and nested in an existing Pipeline, attach to this one.

Example

with Pipeline() as pipeline:
    ...

pipeline.run()
locals()[source]

Variables created in the scope of this Pipeline, including child Pipelines.

run()[source]

Run the complete pipeline.

This is a convenience method to be used in place of:

for _ in pipeline.transform_stream():
    pass
transform_stream(stream=None)[source]

Run the stream through all nodes and return it.

Parameters

stream – A stream to transform. This argument is solely to be used internally.

Returns

Stream – An iterable of stream objects.

Nodes

A Node applies creates, updates or deletes stream objects.

Call

In simple cases, a call to a regular function can be recorded in a pipeline using Call.

class morphocut.core.Call(clbl: Callable, *args, **kwargs)[source]

Call a function with the supplied parameters.

For every object in the stream, apply clbl to the corresponding stream variables.

Parameters
  • clbl – A callable.

  • *args – Positional arguments to clbl.

  • **kwargs – Keyword-arguments to clbl.

Returns

Variable – The result of the function invocation.

Example

def foo(bar):
    return bar

baz = ... # baz is a stream variable.
result = Call(foo, baz)
transform(clbl, args, kwargs)[source]

Apply clbl to the supplied arguments.

Subclassing Node

If a Node has multiple outputs or needs to change the stream, Node has to be subclassed.

The subclass has no or any number of @Output decorators. @ReturnOutputs is used to turn Node subclasses into a functions returning stream variables.

Overriding transform(...)

If the Node handles one object at a time, it is enough to implement a custom transform(...).

The parameter names have to correspond to attributes of the Node. Node.transform_stream() will then use introspection to call transform with the right parameter values.

@ReturnOutputs
@Output("bar")
@Output("baz")
class Foo(Node):
    """This class has two outputs."""
    def __init__(self, ham, spam):
        super().__init__()
        self.ham = ham
        self.spam = spam

    # This is automatically called by Node.transform_stream,
    # reading ham and spam from the stream
    # and introducing the result back into the stream.
    def transform(self, ham, spam):
        # ham and spam are raw values here
        return ham + spam, ham - spam

with Pipeline() as pipeline:
    # ham and spam are stream variables here
    ham = ...
    spam = ...
    bar, baz = Foo(ham, spam)

Overriding transform_stream

If the Node has to change the stream in some way, Node.transform_stream() needs to be overridden.

@ReturnOutputs
@Output("bar")
@Output("baz")
class Foo(Node):
    """This class has two outputs."""
    def __init__(self, ham, spam):
        super().__init__()
        self.ham = ham
        self.spam = spam

    def transform_stream(self, stream):
        with closing_if_closable(stream):
            for obj in stream:
                # Retrieve raw values
                ham, spam = self.prepare_input(obj, ("ham", "spam"))

                # Remove objects from stream based on some condition
                if not ham:
                    continue

                # Append new values to the stream object:
                # bar = ham + spam
                # baz = ham - spam
                yield self.prepare_output(obj, ham + spam, ham - spam)

with Pipeline() as pipeline:
    # ham and spam are stream variables here
    ham = ...
    spam = ...
    bar, baz = Foo(ham, spam)
class morphocut.core.Node[source]

Base class for all stream processing nodes.

after_stream()[source]

Do something after the stream was processed.

Called by transform_stream after stream processing is done. Override this in your own subclass.

prepare_input(obj, names)[source]

Return a tuple corresponding to the input ports.

prepare_output(obj, *values, n_remaining_hint=None)[source]

Update obj using the values corresponding to the output ports.

transform_stream(stream)[source]

Transform a stream. By default, this calls self.transform with appropriate parameters. transform has to be implemented by a subclass if transform_stream is not overridden. Override if the stream has to be altered in some way, i.e. objects are created, deleted or re-arranged.

@morphocut.core.Output(name, type=None, doc=None)[source]

Define an Output of a Node.

Parameters
  • name (str) – Name of the output.

  • type (type, optional) – Type of the output.

  • doc (str, optional) – Description of the output.

@morphocut.core.ReturnOutputs[source]

Turn Node into a function returning Output variables.

Variables

Upon instanciation, Nodes return Variables that are used to identify values in stream objects.

class morphocut.core.Variable(name, parent)[source]

A Variable identifies a value in a stream object.

Variables are (almost) never instanciated manually, they are created when calling a Node.

name

The name of the Variable.

parent

The parent that created the Variable. This is just used in the string representation.

Operations:

Variables support the following operations. Each operation is realized as a new Node in the Pipeline, so use them sparingly. Operator and method can be used interchangeably (if both present).

Operation

Operator

Method

Addition

a + b

a.add(b)

Containment Test

a.contains(b), b.in_(a)

True Division

a / b

a.truediv(b)

Integer Division

a // b

a.floordiv(b)

Bitwise And

a & b

a.and_(b)

Bitwise Exclusive Or

a ^ b

a.xor(b)

Bitwise Inversion

~ a

a.invert(b)

Bitwise Or

a | b

a.or_(b)

Exponentiation

a ** b

a.pow(b)

Identity

a.is_(b)

Identity

a.is_not(b)

Indexed Assignment

obj[k] = v

Indexed Deletion

del obj[k]

Indexing

obj[k]

Left Shift

a << b

a.lshift(b)

Modulo

a % b

a.mod(b)

Multiplication

a * b

a.mul(b)

Matrix Multiplication

a @ b

a.matmul(b)

Negation (Arithmetic)

- a

a.neg(b)

Negation (Logical)

a.not_()

Positive

+ a

a.pos(b)

Right Shift

a >> b

a.rshift(b)

Slice Assignment

seq[i:j] = values

Slice Deletion

del seq[i:j]

Slicing

seq[i:j]

Subtraction

a - b

a.sub(b)

Ordering

a < b

a.lt(b)

Ordering

a <= b

a.leq(b)

Equality

a == b

a.eq(b)

Difference

a != b

a.ne(b)

Ordering

a >= b

a.ge(b)

Ordering

a > b

a.gt(b)

a, b, i, j and k can be either Variable instances or raw values.

Stream

morphocut.core.Stream

A stream is an Iterator of StreamObjects.

class morphocut.core.StreamObject(data=None, n_remaining_hint=None)[source]

An object in the Stream that wraps all values.

data

The data associated with this stream object.

Type

dict

n_remaining_hint

Approximate number of remaining objects in the stream including the current object.

Type

int, optional

A value can be retrieved by indexing: obj[var]

copy()[source]

Create a shallow copy.

to_dict(**kwargs)[source]

Turn the StreamObject into a regular dictionary.