Robot Chef all in one package with lml

Now let us bring in lml and see see how the lml package can be used to rewrite Robot Chef but in a single package. This chapter introduces two classes: lml.plugin.PluginManager and lml.plugin.PluginInfo. And show how those classes can be used to make factory pattern. Meanwhile, it demonstrates that the lml based plugins can be made to load immediately and in a single package. And this sections helps you to understand the next section where we will make the plugins to be loaded later.

Demo

Please navigate to robotchef_allinone_lml and its packages. Do the following:

$ git clone https://github.com/python-lml/robotchef_allinone_lml
$ cd robotchef_allinone_lml
$ python setup.py install

And then you could try:

$ robotchef_allinone_lml "Fish and Chips"
I can fry Fish and Chips

Lml plugins and plugin manager

_images/robotchef_allinone_lml.svg

plugin.py

CuisineManager inherits from PluginManager class and replaces the static registry PLUGINS and the modular function get_a_plugin. Please note that CuisineManager declares that it is a manager for plugin_type named cuisine.

class CuisineManager(PluginManager):
    def __init__(self):
        PluginManager.__init__(self, "cuisine")

    def get_a_plugin(self, food_name=None, **keywords):
        return PluginManager.get_a_plugin(self, key=food_name, **keywords)

    def raise_exception(self, key):
        raise NoChefException("Cannot find a chef")

Next, the PluginInfo decorates all Chef’s subclasses as cuisine plugins and register the decorated classes with the manager class for cuisine, CuisineManager. The food names become the tags which will be used to look up the classes.

from lml.plugin import PluginInfo, PluginManager

class Chef(object):
    def make(self, **params):
        print("I am a chef")


@PluginInfo("cuisine", tags=["Portable Battery"])
class Boost(Chef):
    def make(self, food=None, **keywords):
        print("I can cook %s for robots" % food)


@PluginInfo("cuisine", tags=["Fish and Chips"])
class Fry(Chef):
    def make(self, food=None):
        print("I can fry " + food)


@PluginInfo("cuisine", tags=["Cornish Scone", "Jacket Potato"])
class Bake(Chef):
    def make(self, food=None):
        print("I can bake " + food)

Here is the code difference with Robot Chef All In One solution: plugin.py.

main.py

The main code has been updated to reflect the changes in plugin.py. CuisineManager has to be instantiated to be the a factory manager.

--- /home/docs/checkouts/readthedocs.org/user_builds/lml/checkouts/latest/examples/robotchef_allinone/robotchef_allinone/main.py
+++ /home/docs/checkouts/readthedocs.org/user_builds/lml/checkouts/latest/examples/robotchef_allinone_lml/robotchef_allinone_lml/main.py
@@ -1,15 +1,17 @@
 import sys
 
-import robotchef_allinone.plugin as cuisine_manager
+from robotchef_allinone_lml.plugin import CuisineManager, NoChefException
 
 
 def main():
     if len(sys.argv) < 2:
         sys.exit(-1)
 
+    cuisine_manager = CuisineManager()
+
     food_name = sys.argv[1]
     try:
         knowledged_chef = cuisine_manager.get_a_plugin(food_name)
         knowledged_chef.make(food=food_name)
-    except cuisine_manager.NoChefException:
+    except NoChefException:
         print("I do not know how to cook " + food_name)

Remember this interaction:

$ robotchef "Portable Battery"
I can cook Portable Battery for robots

The response comes from class Boost. It is obtained via CuisineManager when user types ‘Portable Battery’. And the food parameter was passed to the instance of Boost. make method was called and it prints ‘I can cook Portable Battery for robots’.

See also

  1. pyexcel-chart: use lml to refactor existing plugins