Robot Chef all in one package without lml

In this chapter, we are going to see how Robot Chef could be implemented without lml. In later on chapters, we will bring in lml step by step.

Demo

Please checkout the robot chef example:

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

And then you could try:

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

Conventional plugin and plugin factory

plugin.py

_images/robot_chef.svg

Chef is the plugin interface that makes food. Boost, Bake and Fry are the actual implementations. Boost are for “robots”. Bake and Fry are for human.

Note

The plugin interface is your responsibility. lml gives the freedom to you.

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


class Boost(Chef):
    def make(self, food=None, **keywords):
        print("I can cook %s for robots" % food)


class Fry(Chef):
    def make(self, food=None):
        print("I can fry " + food)


class Bake(Chef):
    def make(self, food=None):
        print("I can bake " + food)


PLUGINS = {
    "Portable Battery": Boost,
    "Fish and Chips": Fry,
    "Cornish Scone": Bake,
    "Jacket Potato": Bake,
}

Line 13, class Chef defines the plugin class interface. For robotchef, make is defined to illustrate the functionality. Naturally you will be deciding the interface for your plugins.

Some of you might suggest that class Chef is unnecessary because Python uses duck-typing, meaning as long as the plugin has make method, it should work. Yes, it would work but it is a short term solution. Look at the long term, you could pass on additional functionalities through class Chef without touching the plugins. What’s more, for plugin developers, a clear defined interface is better than no class at all. And I believe the functions of a real plugin are more than just one here.

Next in the plugin.py file, PLUGINS is the dictionary that has food name as key and Chef descendants as values. get_a_plugin method returns a Chef or raises NoChefException.

def get_a_plugin(food_name=None, **keywords):
    plugin = PLUGINS.get(food_name)
    if plugin is None:
        raise NoChefException("Cannot find a chef")
    plugin_cls = plugin()
    return plugin_cls

main.py

Let us glimpse through the main code:

import sys

import robotchef_allinone.plugin as cuisine_manager


def main():
    if len(sys.argv) < 2:
        sys.exit(-1)

    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:
        print("I do not know how to cook " + food_name)

The code takes the first command option as food name and feeds it to the factory method get_a_plugin, which returns a Chef to “make” the food. If no chef was found, it prints the default string: I do not know.

That is all about the all in one Robot Chef.