pip install bottle
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)
@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
, 除此之外,错误处理程序与常规请求回调非常相似。你可以阅读request
给response
并返回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‘]
方便起见,插件和应用应该遵循一些简单的规则,特别是在给配置参数命名的时候:
namespace.field
或 namespace.subnamespacew.field
)。或
werkzeug.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标准 (pep 3333) 定义了下面这一个request/response的循环:每次请求到达的时候,应用中的callable会被调用一次,返回一个主体iterator。接着服务器会遍历该主体,分块写入socket。遍历完整个主体,就关闭客户端的连接。
足够简单,但是存在一个小问题:所有这些都是同步的。如果你的应用需要等待数据(IO, socket, 数据库, ...),除了返回一个空字符串(忙等),就只能阻塞当前线程。两种办法都会占用当前线程,导致线程不能处理新的请求,只能处理当前的一个请求。
大部分服务器都限制了线程的数量,避免伴随它们而来的资源消耗。常见的是一个线程池,内有20个或更少数量的线程。一旦所有的线程都被占用了,任何新的请求都会阻塞。事实上,对于其他人来说,服务器已经宕机了。如果你想实现一个聊天程序,使用ajax轮询来获取实时消息,很快你就会受到线程数量的限制。这样能同时服务的用户就太少了。
大多数服务器的线程池都限制了线程池中线程的数量,避免创建和切换线程的代价。尽管和创建进程(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秒)。它像普通的线程一样工作,但是现在你的服务器能同时处理成千上万的连接了。
让我们暂时忘记底层的细节,来谈谈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的回调函数被调用之前,都会调用你的钩子函数。
原文:https://www.cnblogs.com/iFanLiwei/p/12833832.html