Bottle - bernardopnunes/SoftwareEngineeringSDUW GitHub Wiki

Groups

  1. The beauties

Catalogue

Brief Introduction


The bottle is a fast, simple, and lightweight WSGI micro web-framework for Python. It is distributed as a single file module and has no dependencies other than the Python Standard Library.

The Bottle framework can be roughly divided into the following parts:

1、Routing:

  • handle different requests with some designated functions;
  • support for clean and dynamic URLs;
  • make requests to function-call mapping;

2、Templates:

  • render the special syntax in the template into a string;
  • have a fast and pythonic built-in template engine, and importantly the template engine of Bottle can be arbitrarily specified, including mako, jinja2, cheetah;

3、Utilities:

  • have some components to provide information related to processing requests, which could help to access data conveniently
  • files, cookies, headers, and other HTTP-related metadata

4、Server:

  • support multiple WSGI-based default services by a built-in HTTP development server

“Hello World” in a bottle

from bottle import route, run
@route('/hello/:name')
def index(name='World'):
    return '<b>Hello %s!</b>' % name
run(host='localhost', port=8080)

Run this script, visit http://localhost:8080/hello and you will see “Hello World!”

Install

Because Bottle is a micro Web framework, this framework consists of only one .py file, it does not depend on other libraries, so you can download the bottle.py file directly into the project folder.

If you use the Linux:

Bottle official recommended installation method:

 $ wget http://bottlepy.org/bottle.py

This command will get the latest code from the development version, which contains all the latest features. If you want a more stable environment, you can download the stable version of the distribution provided in PyPI, using PIP (recommended), easy install, or package manager.

$ sudo pip install bottle              # recommended
$ sudo easy_install bottle             # alternative without pip
$ sudo apt-get install python-bottle   # works for debian, ubuntu, ...

Also, if you need a python2.5 or newer include running the bottle application if you don't have the root user's permission to install the package, it is recommended that you use virtualenv. I prefer to use virtualenvwrapper, which can be viewed as an enhanced version of virtualenv.

windows:

Unfortunately, my computer system is Windows, so I just call my cmd and use the pip:

pip install bottle

Now you just add it to your .py file

from bottle import Bottle

then you can use it

Routing

1、Overview

When a program receives a request, it would look for the corresponding view function according to URL, and the function could generate a page that should be sent to the client. And besides, each differnet URL corresponds to different view functions, so that a mapping relationship exsits between them. Routing is the function that handles this kind of mapping relationship.

In the example "Hello World", we link the /hello path to the hello() function, and the funtion returns the value of text "Hello World!", so when we run the code on localhost port 8080 and serve requests, a page that writes "Hello World" would show up.

The implementations of routing include two parts:

  1. Generate URL mapping
  2. Match the correct view function according to the request

2、Static Routes

Static routing is as simple as the path part of the URL (minus the hostname and get parameter parts) matching the routing rule exactly.

from bottle import Bottle
app = Bottle()
@app.route("/route/test")
def index():
    return "hello world !"
app.run(host="127.0.0.1", port=8000, reloader=True, debug=True)

The code above is static routing. If you run it, then you can visit http://127.0.0.1:8000/route/test to see hello world! And if you want to quit it, just hit ctrl-c;

The route() decorator associates a URL address with a callback function(index()), each time you request a URL, the corresponding callback function will run a few, and the return value of the callback function will be sent to the browser, so you can add as many routers as you want in your application via the route() function.

3、Dynamic Routes

In the last kind of the routing, we present a very simple web application with a single route. However, it is very unconvenient that an application just links with one route, actually we can bind more than one route to a single callback, and we can access them through keyword argurment by adding wildcards to URLs.
Here is a simple example transformed from "Hello World":

from bottle import route, run
@route('/')
@route('/hello/<name>')
def greet(name='Stranger'):
    return template('Hello {{name}}, how are you?', name=name)

Routes that contain wildcards are called dynamic routes (as opposed to static routes) and match more than one URL at the same time. A simple wildcard consists of a name enclosed in angle brackets (e.g. ) and accepts one or more characters up to the next slash (/).

Each wildcard passes the covered part of the URL as a keyword argument to the request callback. And filters can be used to define more specific wildcards, and/or transform the covered part of the URL before it is passed to the callback. The syntax for the optional config part depends on the filter used.
For example:

@route('/<action>/<user>')            
def user_api(action, user):
    ...

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

4、Routing static files

To develop the WEB, you need to involve related static resources, such as CSS, JS, images, fonts, and so on. There's a corresponding function static_file() used to provide static file services.

static_file(filename,root=images_path)

Set the static resource access route, to set the route. Then call the bottle static file() method, the program will follow this route to find the relevant file and display it in the browser.

images_path = './images'
@app.route('/images/<filename:re:.*\.jpg>')
def server_static(filename):
    return static_file(filename, root=images_path)

But the wildcard for filename cannot contain '/', so if you want to access a subdirectory under that directory, we can use a formatted wildcard.

@app.route('/static/:path#.+#')
def server_static(path):
    return static_file(path, root='/path/to/your/static/files')

5、HTTP Request methods

The HTTP protocol defines several request methods for different tasks to indicate the action to be performed on the given resource.

  • GET

The GET method is the default for all routes with no other method specified. These routes will match GET requests only. And GET request can only be used to get data.

@get('/login') # or @route('/login')
def login():
    return '''
        <form action="/login" method="post">
            Username: <input name="username" type="text" />
            Password: <input name="password" type="password" />
            <input value="Login" type="submit" />
        </form>
    '''
  • HEAD

The HEAD method is used to ask for the response identical to the one that would correspond to a GET request, but without the response body. This is useful for retrieving meta-information about a resource without having to download the entire document.

  • POST

The POST method is commonly used for HTML form submission, committing an entity to a specified resource, usually resulting in a change of state or side effects on the server. The type of the request body is specified by the content type header.

@post('/login') # or @route('/login', method='POST')
def do_login():
    username = request.forms.get('username')
    password = request.forms.get('password')
    if check_login(username, password):
        return "<p>Your login information was correct.</p>"
    else:
        return "<p>Login failed.</p>"
  • PUT

The PUT method is used to add resources or replace the representation of the target resources with the payload in the request.

  • DELETE

The DELETE request method is used to delete the specified resource.

  • OPTIONS

The OPTIONS method is used to get the communication options supported by the destination resource.

Templates

1. What is Templates

Bottle comes with a fast, powerful and easy to learn built-in template engine called SimpleTemplate or stpl for short. The template system is used to render both HTML and custom values to get a string, which is then returned to the client.

2.SimpleTemplate Syntax

Python is a very powerful language but its whitespace-aware syntax makes it difficult to use as a template language. SimpleTemplate removes some of these restrictions and allows you to write clean, readable and maintainable templates while preserving full access to the features, libraries and speed of the Python language.

(1)Inline Expressions

As long as the python statement in {{...}} returns a string or a representation of a string, it will be a valid statement.

>>>from bottle import template
>>>template('hello {{name}}', name='ju')
u'helloju'
>>>template('hello {{name if name else "world!"}}', name=None)
u'helloworld!'
>>>template('hello {{name if name else "world!"}}',name="feige")
u'hellofeige'

The contained python expression is executed at render-time and has access to all keyword arguments passed to the SimpleTemplate.render() method.

  • ATTENTION:HTML special characters are escaped automatically to prevent XSS attacks. You can start the expression with an exclamation mark to disable escaping for that expression:
>>> template('Hello {{name}}!', name='<b>World</b>')
u'Hello &lt;b&gt;World&lt;/b&gt;!'
>>> template('Hello {{!name}}!', name='<b>World</b>')
u'Hello <b>World</b>!'

(2)Embedded Python Code

The template engine allows you to embed lines or blocks of python code within your template.
Code lines start with % and code blocks are surrounded by <% and %>. Lines that do not start with % are rendered as plain text.

% name = "Xiao"  # a line of python code
<p>Some plain text in between</p>
<%
  # A block of python code
  name = name.title().strip()
%>
<p>More plain text</p>

Only the % character at the beginning of the line makes sense. We can escape it with%%. We don’t have to escape them if they appear mid-text in out template markup. %%Represents a line beginning with%%, and%%% represents a line beginning with%%:

This line contains % and <% but no python code.
\% This text-line starts with the '%' token.
\<% Another line that starts with a token but is rendered as text.
{{'\\%'}} this line starts with an escaped token.

3.TEMPLATE FUNCTIONS

Each template is preloaded with a bunch of functions that help with the most common use cases. And we can also import anything you want within your templates.

(1)include(sub_template, **variables)

Render a sub-template with the specified variables and insert the resulting text into the current template.

% include('header.tpl', title='Page Title')
Page Content
% include('footer.tpl')

(2)rebase(name, **variables)

After the current template is rendered, its resulting text is stored in a variable named base and passed to the base-template, which is then rendered.

<html>
<head>
  <title>{{title or 'No title'}}</title>
</head>
<body>
  {{!base}}
</body>
</html>
% rebase('base.tpl', title='Page Title')
<p>Page Content ...</p>

(3)defined(name)

Check whether the current variable has been defined, true defined, false undefined.

(4)get(name, default=None)

Gets the value of a variable. If it does not exist, set it the default value.

4.THE USE of TEMPLATES:

Bottle comes with a fast and powerful built-in template engine called SimpleTemplate Engine. To render a template you can use the template() function or the view() decorator. All you have to do is to provide the name of the template and the variables you want to pass to the template as key word arguments.

For example:

@route('/hello')
@route('/hello/<name>')
def hello(name='World'):
    return template('hello_template', name=name)

This will load the template file hello_template.tpl and render it with the name variable set. Bottle will look for templates in the ./views/ folder or any folder specified in the bottle.TEMPLATE_PATH list.

The view() decorator allows you to return a dictionary with the template variables instead of calling template():

@route('/hello')
@route('/hello/<name>')
@view('hello_template')
def hello(name='World'):
return dict(name=name)

5.SYNTAX:

The template syntax is a very thin layer around the Python language. Its main purpose is to ensure correct indentation of blocks, so you can format your template without worrying about indentation. Follow the link for a full syntax description: SimpleTemplate Engine.

For example:

%if name == 'World':
    <h1>Hello {{name}}!</h1>
    <p>This is a test.</p>
%else:
    <h1>Hello {{name.title()}}!</h1>
    <p>How are you?</p>
%end

6.CACHING:

Templates are caching in memory after compilation. Modifications made to the template files will have no affect until you clear the template cache. Call bottle.TEMPLATES.clear() to do so. Caching is disabled in debug mode.

Accessing Request Data

Cookies, HTTP headers, HTML < form > Fields and other request data can be obtained through the global request object, which saves the current request data at any time. As long as it is accessed in a routing callback function, it can even work in a multi-threaded environment.

1、HTTP Header

All HTTP headers sent by clients (such as referer, agent or accept language) are saved in a WSGIHeaderDict, which can be accessed through BaseRequest.headers property to get. WSGIHeaderDict is dictionary based and its keys are not case sensitive:

from bottle import route,request
@route(‘js_ajax’)
def is_ajax():
   if request.header.get(‘X-Requested-With’)==’XMLHttpRequest’:
      return ‘This is an AJAX request’
   else:
      return ‘This is a normal request’

2、Cookies

Cookies are small pieces of text stored in the client browser, and each request will return them to the server. They can be used to store some state information of multiple requests (HTTP itself is stateless), but they cannot be used to store security related things. They are easily tampered with by the client. All cookies sent back by clients can be sent through BaseRequest.cookies to get. The following example shows a simple cookie based traffic counter:

from bottle import route,request,response
@route(‘/counter’)
def counter():
   count=int(request.COOKIES.get(‘counter’,’0’))
   count+=1
   response.set_cookie(‘counter’,str(count))
   return 'You visited this page %d times' % count

3、File uploads

To support file uploads, we have to change the <form> tag a bit. First, we tell the browser to encode the form data in a different way by adding an enctype="multipart/form-data" attribute to the

tag. Then, we add <input type="file" /> tags to allow the user to select a file. Here is an example:
<form action="/upload" method="post" enctype="multipart/form-data">
  Category:      <input type="text" name="category" />
  Select a file: <input type="file" name="upload" />
  <input type="submit" value="Start upload" />
</form>

Bottle stores file uploads in BaseRequest.files as FileUpload instances, along with some metadata about the upload. Let us assume you just want to save the file to disk:

@route('/upload', method='POST')
def do_upload():
    category   = request.forms.get('category')
    upload     = request.files.get('upload')
    name, ext = os.path.splitext(upload.filename)
    if ext not in ('.png','.jpg','.jpeg'):
        return 'File extension not allowed.'

    save_path = get_save_path_for_category(category)
    upload.save(save_path) # appends upload.filename automatically
    return 'OK'

Server

Bottle has a built-in HTTP development server, and supports paste, fapws3, bjoern, Google App Engine, Cherrypy or any other WSGI HTTP server.

1. Deployment

Bottle runs on the built-in wsgiref WSGIServer by default. This threadless server is no better for development, but it is not the best choice for growing applications or for actual deployment.

2. Multi-threaded server

The fastest way to improve efficiency is to deploy the application to a multi-threaded server or a server similar to Asynchronous WSGI, such as paste or cherrypy, and tell Bottle to start with these servers instead of its own built-in server.

bottle.run(server='paste')

Bottle supports many servers, such as the ones listed below:

Name Home Page Introduction
cgi Run as CGI script
flup http://trac.saddi.com/flup Run as FastCGI process
gae http://code.google.com/appengine/docs/python/overview.html Google App Engine deployment
wsgiref http://docs.python.org/library/wsgiref.html The default is a single-threaded server
cherrypy http://www.cherrypy.org/ Multi-threaded server
paste http://pythonpaste.org/ Multi-threaded, stable, tried and tested
gunicorn http://pypi.python.org/pypi/gunicorn Pre-forked, partly written in C
gevent http://www.gevent.org/ Asynchronous (greenlets)

It can be seen that the web server that bottle adapts is very rich. The working mode is also very comprehensive, there are multi-threaded (such as paste), multi-process mode (such as gunicorn), and coroutine-based (such as gevent). The specific choice of web server depends on the characteristics of the application, such as CPU bound or IO bound.

The full server name can be obtained using the server_names variable, and you can manually start the bottle using your server:

from paste import httpserver
httpserver.serve(bottle.default_app(), host='0.0.0.0', port = 80)

3. Multi-server process

A Python process can only use one CPU, even if the server hardware has multiple CPUs, you can start multiple applications on different ports, each application uses a CPU, and then use the offload server to offload access, such as Apache mod_wsgi Nginx can be used as a front-end distribution server.

Pros and Cons

Pros

Bottle can be considered as a mini flask because it is more compact and concise than other micro web-framework. Bottle is suitable for inclusion in other projects or for small projects such as REST API and so on.

Bottle is a fast, simple and lightweight WSGI micro web-framework for Python. It is distributed as a single file module and has no dependencies other than the Python Standard Library.

Bottle does not depend on any external libraries. We can just download bottle.py int our project directory and start coding. If you want a more stable environment, you can install via pip.

In pure WSGI, the range of types you may return form your application is very limited. Applications must return an iterable yielding byte strings. You may return a string but this causes most servers to transmit your content char by char. Unicode strings are not allowed at all. This is not very practical. Bottle is much more flexible and supports a wide range of types. It even adds a Content-Length header if possible and encodes Unicode automatically, so you don’t have to.

Cons

One consequence of bottle minimalism is that some functions don’t exist at all. Form validation, including CRSF protection, is not supported. If you want to build web applications that support a high level of user interaction, you need to add them yourself.

Conclusion

Bottle is a very delicate,simple and efficient WSGI framework, which provides the basic support needed in Python web development: URL routing, request / response object encapsulation, template support, and WSGI server integration support. The whole framework has about 2000 lines of code. The core part of the framework has no other dependencies. It can run as long as there is a python environment. We can compare bottle to a mini flask because it is more compact and concise than other "micro frames". Due to its minimal footprint, bottle is ideal for inclusion in other projects or for small projects such as rapid delivery of rest APIs.

References

https://blog.csdn.net/happyteafriends/article/details/42552093 https://www.cnblogs.com/wupeiqi/articles/5341480.html https://blog.csdn.net/zandaoguang/article/details/77387358?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2.nonecase http://www.bottlepy.org/docs/dev/tutorial.html#request-routing https://www.jianshu.com/p/8590665a5eb5 https://blog.csdn.net/JackLiu16/article/details/81611979 https://blog.csdn.net/tyfbhlxd/article/details/72123854

Division of work

  • Jiayu Wang : Brief Introduncion + Routing 1、3、5
  • XiaoXuan Wang : Install + Routing 2、4
  • Yiran Wang : Templates 1、2、3 + Conclusion
  • Wenzheng Yang : Templates 1、2、3 + Pros&Cons
  • Yuqi Wang : Server
  • Rongqing Shi : Accessing Request Data
⚠️ **GitHub.com Fallback** ⚠️