Quickstart and Tutorial

Installation

Use easy_install

easy-install PyroMP

or pip

pip PyroMP

Or get the code directly from the repo on bitbucket.

Warehouse example

Hint

All code of this part of the tutorial can be found in the examples/warehouse directory.

You’ll build a simple warehouse that stores items, and that everyone can visit. Visitors can store items and retrieve other items from the warehouse (if they’ve been stored there).

In this tutorial you’ll first write a normal Python program that more or less implements the complete warehouse system, but in vanilla Python code. After that you’ll add PyroMP support to it, to make it a distributed warehouse system, where you can visit the central warehouse from many different processes.

Note

This example is adapted from Pyro4 tutorial.

Phase 1: the prototype

To start with, write the vanilla Python code for the warehouse and its visitors. This prototype is fully working but everything is running in a single process. It contains no PyroMP code at all, but shows what the system is going to look like later on.

The Warehouse object simply stores an array of items which we can query, and allows for a person to take an item or to store an item (warehouse.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''Adapted from Pyro4 warehouse example
http://pythonhosted.org/Pyro4/tutorials.html
'''

from __future__ import print_function, unicode_literals, division

# Built-in
import logging


class Warehouse(object):

    def __init__(self):
        self.contents = ["chair", "bike", "flashlight", "laptop", "couch"]
        self.logger = logging.getLogger("Warehouse")

    def list_contents(self):
        return self.contents

    def take(self, name, item):
        self.contents.remove(item)
        self.logger.info("{0} took the {1}.".format(name, item))

    def store(self, name, item):
        self.contents.append(item)
        self.logger.info("{0} stored the {1}.".format(name, item))

Then there is a Person that can visit the warehouse. The person has a name and deposit and retrieve actions on a particular warehouse (person.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''Adapted from Pyro4 warehouse example
http://pythonhosted.org/Pyro4/tutorials.html
'''

from __future__ import print_function, unicode_literals, division

# Extern
from six.moves import input


class Person(object):

    def __init__(self, name):
        self.name = name

    def visit(self, warehouse):
        print("This is {0}.".format(self.name))
        self.deposit(warehouse)
        self.retrieve(warehouse)
        print("Thank you, come again!")

    def deposit(self, warehouse):
        print("The warehouse contains: {}".format(
                                                warehouse.list_contents()))
        item = input("Type a thing you want to store (or empty): ").strip()
        if item:
            warehouse.store(self.name, item)

    def retrieve(self, warehouse):
        print("The warehouse contains: {}".format(
                                                warehouse.list_contents()))
        item = input("Type something you want to take (or empty): ").strip()
        if item:
            warehouse.take(self.name, item)

Finally you need a the main function that actually runs the code. It creates the warehouse and two visitors, and makes the visitors perform their actions in the warehouse (visit.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
This code runs this example.

Adapted from Pyro4 warehouse example
http://pythonhosted.org/Pyro4/tutorials.html
'''

from __future__ import print_function, unicode_literals, division

# Built-in
import logging

# Intern
from warehouse import Warehouse
from person import Person


def main():
    root_logger = logging.getLogger()
    root_logger.addHandler(logging.StreamHandler())
    root_logger.setLevel("INFO")
    warehouse = Warehouse()
    janet = Person("Janet")
    henry = Person("Henry")
    janet.visit(warehouse)
    henry.visit(warehouse)

if __name__ == "__main__":
    main()

Run visit.py. The output should look like:

This is Janet.
The warehouse contains: [u'chair', u'bike', u'flashlight', u'laptop', u'couch']
Type a thing you want to store (or empty): car              <-- typed in
Janet stored the car.
The warehouse contains: [u'chair', u'bike', u'flashlight', u'laptop', u'couch', 'car']
Type something you want to take (or empty): bike            <-- typed in
Janet took the bike.
Thank you, come again!
This is Henry.
The warehouse contains: [u'chair', u'flashlight', u'laptop', u'couch', 'car']
Type a thing you want to store (or empty): computer         <-- typed in
Henry stored the computer.
The warehouse contains: [u'chair', u'flashlight', u'laptop', u'couch', 'car', 'computer']
Type something you want to take (or empty): laptop          <-- typed in
Henry took the laptop.
Thank you, come again!

Phase 2: first PyroMP version

The first step is importing PyroMP. Futhermore the built-in logging module is not capable of multiprocess logging in an appropriate way, but Pyro4.log_server (see Logging), so we need to replace it.

To start the Warehouse in a separate process, we have to derive it from Service. There must be two lines changed and one added to perform this. Furthermore we use the get_logger() function to use a PyroMP logger. The modified lines are highlighted in the code (warehouse.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''Adapted from Pyro4 warehouse example
http://pythonhosted.org/Pyro4/tutorials.html
'''

from __future__ import print_function, unicode_literals, division

# Intern
import PyroMP


class Warehouse(PyroMP.Service):

    def __init__(self, async=False, multiplex=False):
        super(Warehouse, self).__init__(async, multiplex)
        self.contents = ["chair", "bike", "flashlight", "laptop", "couch"]
        self.logger = self.get_logger()

    def list_contents(self):
        return self.contents

    def take(self, name, item):
        self.contents.remove(item)
        self.logger.info("{0} took the {1}.".format(name, item))

    def store(self, name, item):
        self.contents.append(item)
        self.logger.info("{0} stored the {1}.".format(name, item))

The implementation of Person is not changed. In the main function we need to start the NameServer and LogServer. Afterwards we set the logging level to INFO. The warehouse service is started using the with statement and stopped after the with-block (visit.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
This code runs this example.

Adapted from Pyro4 warehouse example
http://pythonhosted.org/Pyro4/tutorials.html
'''

from __future__ import print_function, unicode_literals, division

# Intern
import PyroMP
import PyroMP.log_server as log
from warehouse import Warehouse
from person import Person


def main():
    with PyroMP.NameServer(), log.LogServer():
        log.set_loglevel("INFO")
        with Warehouse() as warehouse:
            janet = Person("Janet")
            henry = Person("Henry")
            janet.visit(warehouse)
            henry.visit(warehouse)

if __name__ == "__main__":
        main()

Run visit.py. It will take a short while until all processes have been started. The output should look like:

This is Janet.
The warehouse contains: [u'chair', u'bike', u'flashlight', u'laptop', u'couch']
Type a thing you want to store (or empty): car           <-- typed in
2014-02-03 17:07:45,368 - LogServer.WAREHOUSE            - INFO     - Janet stored the car.
The warehouse contains: [u'chair', u'bike', u'flashlight', u'laptop', u'couch', 'car']
Type something you want to take (or empty): bike         <-- typed in
2014-02-03 17:07:49,144 - LogServer.WAREHOUSE            - INFO     - Janet took the bike.
Thank you, come again!
This is Henry.
The warehouse contains: [u'chair', u'flashlight', u'laptop', u'couch', 'car']
Type a thing you want to store (or empty): computer      <-- typed in
2014-02-03 17:07:53,871 - LogServer.WAREHOUSE            - INFO     - Henry stored the computer.
The warehouse contains: [u'chair', u'flashlight', u'laptop', u'couch', 'car', 'computer']
Type something you want to take (or empty): laptop       <-- typed in
2014-02-03 17:08:04,869 - LogServer.WAREHOUSE            - INFO     - Henry took the laptop.
Thank you, come again!

Phase 3: access from another process

To have access to the Warehouse object the service must be running. We add a line where the main process will wait until enter is pressed and the service is not stopped (visit.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
This code runs this example.

Adapted from Pyro4 warehouse example
http://pythonhosted.org/Pyro4/tutorials.html
'''

from __future__ import print_function, unicode_literals, division

# Extern
from six.moves import input

# Intern
import PyroMP
import PyroMP.log_server as log
from warehouse import Warehouse
from person import Person


def main():
    with PyroMP.NameServer(), log.LogServer():
        log.set_loglevel("INFO")
        with Warehouse() as warehouse:
            janet = Person("Janet")
            henry = Person("Henry")
            janet.visit(warehouse)
            henry.visit(warehouse)
            # wait here so the warehouse service
            # is still available for the other visit
            input("Press enter to exit:")

if __name__ == "__main__":
    main()

We get access to the Warehouse Service by importing it and using the same syntax as before. The service is neither started again before nor stopped after the with-block, because it is already running and was started from another process (another_visit.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
This code runs a second process for this example.

Adapted from Pyro4 warehouse example
http://pythonhosted.org/Pyro4/tutorials.html
'''

from __future__ import print_function, unicode_literals, division

# Intern
from person import Person
from warehouse import Warehouse


def main():
    # no need to start NameServer or LogServer
    # because we assume they are still running
    with Warehouse(async=False) as warehouse:
        josephine = Person("Josephine")
        peter = Person("Peter")
        josephine.visit(warehouse)
        peter.visit(warehouse)

if __name__ == "__main__":
    main()

First run visit.py, then run another_visit.py, while the former waits for enter. The output of visit.py should look like:

This is Janet.
The warehouse contains: [u'chair', u'bike', u'flashlight', u'laptop', u'couch']
Type a thing you want to store (or empty): car           <-- typed in
2014-02-03 17:33:00,352 - LogServer.WAREHOUSE            - INFO     - Janet stored the car.
The warehouse contains: [u'chair', u'bike', u'flashlight', u'laptop', u'couch', 'car']
Type something you want to take (or empty): bike         <-- typed in
2014-02-03 17:33:01,805 - LogServer.WAREHOUSE            - INFO     - Janet took the bike.
Thank you, come again!
This is Henry.
The warehouse contains: [u'chair', u'flashlight', u'laptop', u'couch', 'car']
Type a thing you want to store (or empty): computer      <-- typed in
2014-02-03 17:33:03,506 - LogServer.WAREHOUSE            - INFO     - Henry stored the computer.
The warehouse contains: [u'chair', u'flashlight', u'laptop', u'couch', 'car', 'computer']
Type something you want to take (or empty): laptop       <-- typed in
2014-02-03 17:33:08,217 - LogServer.WAREHOUSE            - INFO     - Henry took the laptop.
Thank you, come again!
Press enter to exit:2014-02-03 17:33:31,789 - LogServer.WAREHOUSE            - INFO     - Josephine stored the football.
2014-02-03 17:33:34,191 - LogServer.WAREHOUSE            - INFO     - Josephine took the car.
2014-02-03 17:33:42,367 - LogServer.WAREHOUSE            - INFO     - Peter stored the baseball.
2014-02-03 17:33:45,019 - LogServer.WAREHOUSE            - INFO     - Peter took the couch.

The output of another_visit.py should look like:

This is Josephine.
The warehouse contains: [u'chair', u'flashlight', u'couch', 'car', 'computer']
Type a thing you want to store (or empty): football       <-- typed in
The warehouse contains: [u'chair', u'flashlight', u'couch', 'car', 'computer', 'football']
Type something you want to take (or empty): car           <-- typed in
Thank you, come again!
This is Peter.
The warehouse contains: [u'chair', u'flashlight', u'couch', 'computer', 'football']
Type a thing you want to store (or empty): baseball       <-- typed in
The warehouse contains: [u'chair', u'flashlight', u'couch', 'computer', 'football', 'baseball']
Type something you want to take (or empty): couch         <-- typed in
Thank you, come again!