.. % NB: Please don't break any of the long lines - Verbatim is picky about line breaks

.. index::
   pair: packages; ``_modules``
   pair: modules; ``_input_ports``
   pair: modules; ``_output_ports``
   pair: modules; ``ModuleError``

.. role:: red

.. code-block:: python
    :linenos:

    ###############################################################################
    # PythonCalc
    #
    # A VisTrails package is simply a Python class that subclasses from
    # Module.  For this class to be executable, it must define a method
    # compute(self) that will perform the appropriate computations and set
    # the results.
    #
    # Extra helper methods can be defined, as usual. In this case, we're
    # using a helper method op(self, v1, v2) that performs the right
    # operations.

    from vistrails.core.modules.vistrails_module import Module, ModuleError
    from vistrails.core.modules.config import IPort, OPort

    class PythonCalc(Module):
        """PythonCalc is a module that performs simple arithmetic operations
    on its inputs."""

        # You need to report the ports the module wants to make
        # available. This is done by creating _input_ports and
        # _output_ports lists composed of InputPort (IPort) and OutputPort
        # (OPort) objects. These are simple ports that take only one
        # value. We'll see in later tutorials how to create compound ports
        # which can take a tuple of values.  Each port must specify its
        # name and signature.  The signature specifies the package
        # (e.g. "basic" which is shorthand for
        # "org.vistrails.vistrails.basic") and module (e.g. "Float").
        # Note that the third input port (op) has two other arguments.
        # The "enum" entry_type specifies that there are a set of options
        # the user should choose from, and the values then specifies those
        # options.
        _input_ports = [IPort(name="value1", signature="basic:Float"),
                        IPort(name="value2", signature="basic:Float"),
                        IPort(name="op", signature="basic:String",
                              entry_type="enum", values=["+", "-", "*", "/"])]
        _output_ports = [OPort(name="value", signature="basic:Float")]

        # This constructor is strictly unnecessary. However, some modules
        # might want to initialize per-object data. When implementing your
        # own constructor, remember that it must not take any extra
        # parameters.
        def __init__(self):
            Module.__init__(self)

        # This is the method you should implement in every module that
        # will be executed directly. VisTrails does not use the return
        # value of this method.
        def compute(self):
            # get_input is a method defined in Module that returns
            # the value stored at an input port. If there's no value
            # stored on the port, the method will return None.
            v1 = self.get_input("value1")
            v2 = self.get_input("value2")

            # You should call set_output to store the appropriate results
            # on the ports.  In this case, we are only storing a
            # floating-point result, so we can use the number types
            # directly. For more complicated data, you should
            # return an instance of a VisTrails Module. This will be made
            # clear in further examples that use these more complicated data.
            self.set_output("value", self.op(v1, v2))

        def op(self, v1, v2):
            op = self.get_input("op")
            if op == '+':
                return v1 + v2
            elif op == '-':
                return v1 - v2
            elif op == '*':
                return v1 * v2
            elif op == '/':
                return v1 / v2
            # If a module wants to report an error to VisTrails, it should raise
            # ModuleError with a descriptive error. This allows the interpreter
            # to capture the error and report it to the caller of the evaluation
            # function.
            raise ModuleError(self, "unrecognized operation: '%s'" % op)

    # VisTrails will only load the modules specified in the _modules list.
    # This list contains all of the modules a package defines.
    _modules = [PythonCalc,]