首页 > 编程语言 > 详细

python bottle学习

时间:2020-05-06 00:18:59      阅读:72      评论:0      收藏:0      [点我收藏+]
  1. 安装
pip install bottle
  1. 示例程序
from bottle import Bottle, run, template
app = Bottle()

@app.route("/")
def index():
    return "bottle index"

@app.route("/hello/<name>")
def hello(name="Stranger"):
    return template("hellp {{name}}", name=name)

run(app, host="0.0.0.0", port=8888, debug=True)

请求路由

动态路由

  • :int 只匹配(有符号)数字,并将值转换为整数。
  • 浮标 类似于:int,但用于十进制数。
  • 路径 以非贪婪的方式匹配所有字符,包括斜线字符,并且可以用于匹配多个路径段。
  • :re 允许您在配置字段中指定自定义正则表达式。不修改匹配的值。
@route(‘/object/<id:int>‘)
def callback(id):
    assert isinstance(id, int)

@route(‘/show/<name:re:[a-z]+>‘)
def callback(name):
    assert name.isalpha()

@route(‘/static/<path:path>‘)
def callback(path):
    return static_file(path, ...)

实现自己的过滤器

app = Bottle()

def list_filter(config):
    ‘‘‘ Matches a comma separated list of numbers. ‘‘‘
    delimiter = config or ‘,‘
    regexp = r‘\d+(%s\d)*‘ % re.escape(delimiter)

    def to_python(match):
        return map(int, match.split(delimiter))

    def to_url(numbers):
        return delimiter.join(map(str, numbers))
    return regexp, to_python, to_url

app.router.add_filter(‘list‘, list_filter)

@app.route(‘/follow/<ids:list>‘)
def follow_users(ids):
    for id in ids:

显式路由配置

def setup_routing(app):
    app.route(‘/new‘, [‘GET‘, ‘POST‘], form_new)
    app.route(‘/edit‘, [‘GET‘, ‘POST‘], form_edit)

app = Bottle()
setup_routing(app)

路由静态文件

不会主动添加路由和回调,必须自己添加。

这个 static_file() 函数是以安全和方便的方式提供文件的帮助程序 ,此示例仅限于 /path/to/your/static/files 目录,因为 `` 通配符不匹配包含斜杠的路径。若要在子目录中提供文件,请更改通配符以使用 path 过滤器:

from bottle import static_file

@route(‘/static/<filename>‘)
def server_static(filename):
    return static_file(filename, root=‘/path/to/your/static/files‘)

错误页面

如果出了什么问题, Bottle 会显示一个信息丰富但相当简单的错误页面。您可以使用 error() 装饰:

from bottle import error

@error(404)
def error404(error):
    return ‘Nothing here, sorry‘

从今以后, 404 File not Found 错误将向用户显示自定义错误页。传递给错误处理程序的唯一参数是 HTTPError, 除此之外,错误处理程序与常规请求回调非常相似。你可以阅读requestresponse并返回HTTPError实例

配置文件

基础知识

import bottle
app = bottle.default_app()             # or bottle.Bottle() if you prefer

app.config[‘autojson‘]    = False      # Turns off the "autojson" feature
app.config[‘sqlite.db‘]   = ‘:memory:‘ # Tells the sqlite plugin which db to use
app.config[‘myapp.param‘] = ‘value‘    # Example for a custom config value.

# Change many values at once
app.config.update({
    ‘autojson‘: False,
    ‘sqlite.db‘: ‘:memory:‘,
    ‘myapp.param‘: ‘value‘
})

# Add default values
app.config.setdefault(‘myapp.param2‘, ‘some default‘)

# Receive values
param  = app.config[‘myapp.param‘]
param2 = app.config.get(‘myapp.param2‘, ‘fallback value‘)

# An example route using configuration values
@app.route(‘/about‘, view=‘about.rst‘)
def about():
    email = app.config.get(‘my.email‘, ‘nomail@example.com‘)
    return {‘email‘: email}

app对象不一定总是可用的,但只要你在处理一个请求,你可以使用 request 对象来获得当前的应用对象和它的配置:

from bottle import request
def is_admin(user):
    return user == request.app.config[‘myapp.admin_user‘]

命名约定

方便起见,插件和应用应该遵循一些简单的规则,特别是在给配置参数命名的时候:

  • 所有的key都应该是小写的字符串,并符合Python的变量命名规则(除了下划线外,没有特殊字符)。
  • 命名空间通过点来区分(例如: namespace.fieldnamespace.subnamespacew.field )。
  • Bottle框架,使用根命名空间来存储它的配置。插件应该在它们自己的命名空间中存储它们的变量(例如: sqlite.dbwerkzeug.use_debugger` )。
  • 你的应用应该使用一个独立的命名空间(例如: myapp.* )。

从文件中加载配置

在你不想通过修改代码来修改配置的时候,配置文件是非常有用的。常见的配置文件语法如下:

[sqlite]
db = /tmp/test.db
commit = auto

[myapp]
admin_user = defnull

通过 ConfigDict.load_config() 方法,你可以从一些ini文件中导入配置:

app.config.load_config(‘/etc/myapp.conf‘)

从字典中加载配置

另外一个有用的方法,是 ConfigDict.load_dict() 。将字典中的配置,放到各自的命名空间下面:

# Load an entire dict structure
app.config.load_dict({
    ‘autojson‘: False,
    ‘sqlite‘: { ‘db‘: ‘:memory:‘ },
    ‘myapp‘: {
        ‘param‘: ‘value‘,
        ‘param2‘: ‘value2‘
    }
})

assert app.config[‘myapp.param‘] == ‘value‘

# Load configuration from a json file
with open(‘/etc/myapp.json‘) as fp:
    app.config.load_dict(json.load(fp))

同步WSGI的限制

简单来说, WSGI标准 (pep 3333) 定义了下面这一个request/response的循环:每次请求到达的时候,应用中的callable会被调用一次,返回一个主体iterator。接着服务器会遍历该主体,分块写入socket。遍历完整个主体,就关闭客户端的连接。

足够简单,但是存在一个小问题:所有这些都是同步的。如果你的应用需要等待数据(IO, socket, 数据库, ...),除了返回一个空字符串(忙等),就只能阻塞当前线程。两种办法都会占用当前线程,导致线程不能处理新的请求,只能处理当前的一个请求。

大部分服务器都限制了线程的数量,避免伴随它们而来的资源消耗。常见的是一个线程池,内有20个或更少数量的线程。一旦所有的线程都被占用了,任何新的请求都会阻塞。事实上,对于其他人来说,服务器已经宕机了。如果你想实现一个聊天程序,使用ajax轮询来获取实时消息,很快你就会受到线程数量的限制。这样能同时服务的用户就太少了。

Greenlet

大多数服务器的线程池都限制了线程池中线程的数量,避免创建和切换线程的代价。尽管和创建进程(fork)的代价比起来,线程还是挺便宜的。但是也没便宜到可以接受为每一个请求创建一个线程。

gevent 模块添加了 greenlet 的支持。greenlet和传统的线程类似,但其创建只需消耗很少的资源。基于gevent的服务器可以生成成千上万的greenlet,为每个连接分配一个greenlet也毫无压力。阻塞greenlet,也不会影响到服务器接受新的请求。同时处理的连接数理论上是没有限制的。

这令创建异步应用难以置信的简单,因为它们看起来很想同步程序。基于gevent服务器实际上不是异步的,是大规模多线程。下面是一个例子。

from gevent import monkey; monkey.patch_all()

from time import sleep
from bottle import route, run

@route(‘/stream‘)
def stream():
    yield ‘START‘
    sleep(3)
    yield ‘MIDDLE‘
    sleep(5)
    yield ‘END‘

run(host=‘0.0.0.0‘, port=8080, server=‘gevent‘)

第一行很重要。它让gevent monkey-patch了大部分Python的阻塞API,让它们不阻塞当前线程,将CPU让给下一个greenlet。它实际上用基于gevent的伪线程替换了Python的线程。这就是你依然可以使用 time.sleep() 这个照常来说会阻塞线程的函数。如果这种monkey-patch的方式感令你感到不舒服,你依然可以使用gevent中相应的函数 gevent.sleep()

如果你运行了上面的代码,接着访问 http://localhost:8080/stream ,你可看到 START, MIDDLE, 和 END 这几个字样依次出现(用时大约8秒)。它像普通的线程一样工作,但是现在你的服务器能同时处理成千上万的连接了。

WebSockets

让我们暂时忘记底层的细节,来谈谈WebSocket。既然你正在阅读这篇文章,你有可能已经知道什么是WebSocket了,一个在浏览器(客户端)和Web应用(服务端)的双向的交流通道。

感谢 gevent-websocket 包帮我们做的工作。下面是一个WebSocket的简单例子,接受消息然后将其发回客户端。

from bottle import request, Bottle, abort
app = Bottle()

@app.route(‘/websocket‘)
def handle_websocket():
    wsock = request.environ.get(‘wsgi.websocket‘)
    if not wsock:
        abort(400, ‘Expected WebSocket request.‘)

    while True:
        try:
            message = wsock.receive()
            wsock.send("Your message was: %r" % message)
        except WebSocketError:
            break

from gevent.pywsgi import WSGIServer
from geventwebsocket import WebSocketError
from geventwebsocket.handler import WebSocketHandler
server = WSGIServer(("0.0.0.0", 8080), app,
                    handler_class=WebSocketHandler)
server.serve_forever()

while循环直到客户端关闭连接的时候才会终止。You get the idea ??

客户端的JavaScript API也十分简洁明了:

<!DOCTYPE html>
<html>
<head>
  <script type="text/javascript">
    var ws = new WebSocket("ws://example.com:8080/websocket");
    ws.onopen = function() {
        ws.send("Hello, world");
    };
    ws.onmessage = function (evt) {
        alert(evt.data);
    };
  </script>
</head>
</html>

使用钩子

例如,你想提供跨域资源共享,可参考下面的例子。

from bottle import hook, response, route

@hook(‘after_request‘)
def enable_cors():
    response.headers[‘Access-Control-Allow-Origin‘] = ‘*‘

@route(‘/foo‘)
def say_foo():
    return ‘foo!‘

@route(‘/bar‘)
def say_bar():
    return {‘type‘: ‘friendly‘, ‘content‘: ‘Hi!‘}

你也可以使用 before_request ,这样在route的回调函数被调用之前,都会调用你的钩子函数。

python bottle学习

原文:https://www.cnblogs.com/iFanLiwei/p/12833832.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!