Hyppää sisältöön

HTTP-frameworkia Pythonilla

Luin taannoin CherryPy:n tutoriaalia ja muuta dokumentaatiota. CherryPy on Pythonilla toteuttu HTTP-framework. Se jäsentää HTTP-pyynnön URL:in ja kutsuu sen perusteella Python-ohjelman luokassa olevaa metodia. Kutsuttu metodi palauttaa kysytyn WWW-sivun.

Alla olevassa esimerkissä on määritelty "sivut" seuraaville URL:eille:

/
Root-luokan index-metodi
/page1
Root-luokan page1-metodi
/node/
Node-luokan index-metodi
/node/page2
Node-luokan page2-metodi

default-metodeja kutsutaan muille URL:eille.

class Node(object):

    def index(self, *args):
        return 'Node index: %s' % ','.join(args)

    def page2(self):
        return 'Node page2'

    def default(self, param1, param2):
        return 'Node default: %s,%s' % (param1, param2)

class Root(object):

    node = Node()

    def index(self):
        return 'Root index'

    def page1(self, param):
        return 'Root page1: %s' % param

    def default(self, *args):
        return 'Root default: %s' % ','.join(args)

En tutkinut CherryPyn koodia, mutta kokeilin, miten tuon kutsun voisi URL:in perusteella tehdä. Hämmästyin, kuinka yksinkertainen ohjelmasta tuli. Alla on koko ohjelma testikoodeineen.

def get_value(root, name_list):
    """Calls method and returns value

       >>> get_value(Root(), [])
       'Root index'
       >>> get_value(Root(), ['index'])
       'Root index'
       >>> get_value(Root(), ['page1', 'a'])
       'Root page1: a'
       >>> get_value(Root(), ['a', 'b', 'c'])
       'Root default: a,b,c'
       >>> get_value(Root(), ['node'])
       'Node index: '
       >>> get_value(Root(), ['node', 'index', 'a', 'b'])
       'Node index: a,b'
       >>> get_value(Root(), ['node', 'page2'])
       'Node page2'
       >>> get_value(Root(), ['node', 'a', 'b'])
       'Node default: a,b'
    """
    if len(name_list) > 0:
        if name_list[0] == '':
            return get_value(root, name_list[1:])
        try:
            a = getattr(root, name_list[0])
            if callable(a):
                return a(*name_list[1:])
            else:
                return get_value(a, name_list[1:])
        except AttributeError:
            return root.default(*name_list)
    else:
        return root.index()

def get(root, path):
    """Get value from path.

       >>> get(Root(), '/node/index/a/b/c/d')
       'Node index: a,b,c,d'
    """
    return get_value(root, path.split('/'))


def _test():
    import doctest
    doctest.testmod()

if __name__ == '__main__':
    _test()

Kun nämä koodit tallentaa tiedostoon (esimerkiksi calltest.py) voi ohjelman testit ajaa komennolla python calltest.py -v. Ohjelman testit on tehty Pythonin doctest-työkaluilla.

CherryPyn vastaava koodi on varmaankin täydellisempi ja siinä on otettu huomioon erikoistapauksia, joihin en ole tässä kiinnittänyt huomiota. En muutenkaan ole varma, kuinka tarkkaan tämä koodi vastaa CherryPyn toimintaa, mutta esimerkistä kuitenkin idea selvinnee ja samalla saa mielikuvan Pythonin ilmaisuvoimasta.

Millainenkohan vastaava toteutus olisi esimerkiksi Javalla tehtynä?

Ystävällisin terveisin

Timo Kankare

Kommentit