WSGI学习系列Pecan

Pecan Introduce

  Pecan是一个轻量级的基于Python的Web框架,

  Pecan的目标并不是要成为一个“full stack”的框架,

  因此Pecan本身不支持类似Session和Databases.

Pecan Features

  (1) Object-dispatch for easy routing

  (2) Full support for REST-style controllers

  除了上述功能外,还包括以下扩展功能:

  Extensible security framework

  Extensible template language support

  Extensible JSON support

  Easy Python-based configuration

Pecan Web Application Files Structure

├── MANIFEST.in
├── config.py
├── public
│   ├── css
│   │   └── style.css
│   └── images
├── setup.cfg
├── setup.py
└── test_project
    ├── __init__.py
    ├── app.py
    ├── controllers
    │   ├── __init__.py
    │   └── root.py
    ├── model
    │   └── __init__.py
    ├── templates
    │   ├── error.html
    │   ├── index.html
    │   └── layout.html
    └── tests
        ├── __init__.py
        ├── config.py
        ├── test_functional.py
        └── test_units.py

  以上是一个默认的Pecan Web应用文件包结构。

  (1) config.py是整个应用的配置入口.

  (2) public文件夹存放Web应用所需的Image,CSS或者Javascript.

  (3) setup.py和setup.cfg用于Web应用的安装部署.

  (4) controllers存放路由控制文件.

  (5) templates存储Html或者Json的模板文件.

  (6) tests存放测试用例.

  PS:

    在测试环境可以使用pecan serve config.py启动Web应用.

    在商用环境下可以部署Pecan的Web应用到Apache等Web服务器.

Object-dispatch for easy routing

  以下是一个简单是Web应用示例,示例中Pecan通过修饰器公开接口。

  这个示例是使用Pecan开发一个具体的Web应用。

from pecan import expose,request, redirect, response
from webob.exc import status_map

class BooksController(object):
    @expose(content_type='text/plain')
    def index(self):
        return "Welcome to book section."

    @expose(content_type='text/plain')
    def bestsellers(self):
        return "We have 5 books in the top 10."

class CatalogController(object):
    @expose(content_type='text/plain')
    def index(self):
        return "Welcome to the catalog."

    #books
    books = BooksController()

class RootController(object):

   # 默认页面以index.html为模板 @expose(generic
=True, template='index.html') def index(self): return dict() # for POST @index.when(method='POST') def index_post_test(self, q): redirect('http://pecan.readthedocs.org/en/latest/search.html?q=%s' % q)
   # show error page @expose(
'error.html') def error(self, status): try: status = int(status) except ValueError: # pragma: no cover status = 500 message = getattr(status_map.get(status), 'explanation', '') return dict(status=status, message=message) # /hello @expose(generic=True, template='index.html') def hello(self): return dict() # /testtext.txt @expose(content_type='text/plain') def testtext(self): return 'text test' # json @expose('json') def jsontest(self): response.status = 200 return {'foo': 'bar'} # catalog catalog = CatalogController()

Full support for REST-style controllers

以下是一个使用Pecan实现的Rest服务.

包括GET,POST,PUT,DELETE.

from pecan import abort, expose

# Note: this is *not* thread-safe.  In real life, use a persistent data store.
BOOKS = {
    '0': 'The Last of the Mohicans',
    '1': 'Catch-22'
}


class BookController(object):

    def __init__(self, id_):
        self.id_ = id_
        assert self.book

    @property
    def book(self):
        if self.id_ in BOOKS:
            return dict(id=self.id_, name=BOOKS[self.id_])
        abort(404)

    # HTTP GET /<id>/
    @expose(generic=True, template='json')
    def index(self):
        return self.book

    # HTTP PUT /<id>/
    @index.when(method='PUT', template='json')
    def index_PUT(self, **kw):
        BOOKS[self.id_] = kw['name']
        return self.book

    # HTTP DELETE /<id>/
    @index.when(method='DELETE', template='json')
    def index_DELETE(self):
        del BOOKS[self.id_]
        return dict()


class RootRestController(object):

    @expose()
    def _lookup(self, id_, *remainder):
        return BookController(id_), remainder

    # HTTP GET /
    @expose(generic=True, template='json')
    def index(self):
        return [dict(id=k, name=v) for k, v in BOOKS.items()]

    # HTTP POST /
    @index.when(method='POST', template='json')
    def index_POST(self, **kw):
        id_ = len(BOOKS)
        BOOKS[id_] = kw['name']
        return dict(id=id_, name=kw['name'])