Python Development [Tornado]: Introduction and Use

Tornado Framework

Introduction:

Tornado is our open source version of the Web server in FriendFeed and its commonly used tools.Tornado differs significantly from the current mainstream Web server framework, including most Python frameworks: it is a non-blocking server and is fairly fast.Thanks to its non-blocking approach and use of epoll, Tornado can handle thousands of connections per second, so Tornado is an ideal framework for real-time Web services.Tornado is a Python web framework with an asynchronous network library that was originally developed on FriendFeed, a social service.By using non-blocking network I/O, Tornado can extend to thousands of open connections, making it an ideal choice for long polling, WebSockets, and other applications that require long connections to each user.

 

Installation:

C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.9\sbin>pip3 install tornado
Collecting tornado
  Downloading tornado-4.5.1-cp36-cp36m-win_amd64.whl (422kB)
    100% |████████████████████████████████| 430kB 16kB/s
Installing collected packages: tornado
Successfully installed tornado-4.5.1

Installation succeeds after executing pip3 install tornado on Python 3.6

 

Simple examples:

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

After running, the browser enters http://127.0.0.1:8888 to access and display Hello world.This example does not use any of Tornado's asynchronous features and looks like a simple chat room.

 

Simple Web Services

1. Examples of applications:

Tornado is a framework for writing responses to HTTP requests.As a programmer, your job is to write a handler that responds to HTTP requests for specific conditions.Here is a basic example of a fully functional Tornado application:

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)     # python hello.py --port=8000 Specifies the default run port 8000 incoming type int

class IndexHandler(tornado.web.RequestHandler):                    # Processing Class
    def get(self):                                                  # get request
        greeting = self.get_argument('greeting', 'Hello')         # Receives the greeting parameter, which defaults to Hello
        self.write(greeting + ', friendly user!')                 # Data returned

if __name__ == "__main__":
    # This is the statement that really makes Tornado work.First, we use Tornado's options module to parse the command line
    # Then we created an instance of Tornado's Application class.The most important parameter passed to the Application class_u init_u method is handlers.
    # It tells Tornado which class to use to respond to requests.
    tornado.options.parse_command_line()
    app = tornado.web.Application(handlers=[(r"/", IndexHandler)])

    # Code that starts here will be reused: once the Application object is created, we can pass it to the HTTPServer object in Tornado.
    # Then listen on the port we specified on the command line (taken out through the options object.)
    # Finally, once the program is ready to receive HTTP requests, we create an instance of Tornado's IOLoop.
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

Run the program directly or specify a port:

$ python hello.py --port=8000

Access the corresponding port service through the browser and pass in the greeting parameter to get different results:

$ curl http://localhost:8000/
Hello, friendly user!
$ curl http://localhost:8000/?greeting=Salutations
Salutations, friendly user!

(1) Parameter handlers:

Let's take another look at this line in the hello.py example:

app = tornado.web.Application(handlers=[(r"/", IndexHandler)])

The handlers parameter here is very important and deserves further study.It should be a list of tuples, where the first element of each tuple is a regular expression for matching and the second element is a RequestHanlder class.In hello.py, we only specify one regular expression-RequestHanlder pair, but you can specify as many as you want.

(2) Use regular expressions to specify paths:

Tornado uses regular expressions in tuples to match the path of an HTTP request.(This path is the part of the URL that follows the host name, excluding query strings and fragments.)Tornado treats these regular expressions as having line start and end anchors (that is, the string'/'is treated as'^/$').

If a regular expression contains a capture grouping (that is, parts of the regular expression are enclosed in parentheses), the matching content is passed to the RequestHandler object as an argument to the corresponding HTTP request.We'll see its use in the next example.

 

2. More complex examples:

import textwrap

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class ReverseHandler(tornado.web.RequestHandler):
    def get(self, input):
        self.write(input[::-1])

class WrapHandler(tornado.web.RequestHandler):
    def post(self):
        text = self.get_argument('text')
        width = self.get_argument('width', 40)
        self.write(textwrap.fill(text, int(width)))
        
if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[
            (r"/reverse/(\w+)", ReverseHandler),          # Regular matching accesses url s, calling different function classes
            (r"/wrap", WrapHandler)
        ]
    )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

Start a program, which is a general Web service-side framework for string manipulation.So far, you can do two things with it.First, GET requests to/reverse/string will return the reverse form of the string specified in the URL path.

$ curl http://localhost:8000/reverse/stressed
desserts

$ curl http://localhost:8000/reverse/slipup
pupils

Second, POST requests to/wrap will take the specified text from the parameter text and return the text decorated with the specified width as specified by the parameter width.The following request specifies a string with no width, so its output width is specified as the default get_argument value of 40 characters in the program.

$ http://localhost:8000/wrap -d text=Lorem+ipsum+dolor+sit+amet,+consectetuer+adipiscing+elit.
Lorem ipsum dolor sit amet, consectetuer
adipiscing elit.

  

RequestHandler More

So far, we've learned the basics of the RequestHandler object: how to get information from an incoming HTTP request (using get_argument and parameters passed to get and post) and write HTTP responses (using the write method).In addition, there is a lot to learn, which we will explain in the next chapters.There are also other things to remember about how RequestHandler and Tornado use it.

1. HTTP method:

As of the examples discussed so far, each RequestHandler class defines the behavior of only one HTTP method.However, it is possible and useful to define multiple methods in the same processing function.It is a good way to bind conceptually related functions to the same class.For example, you might write a processing function that handles objects with a specific ID in your database, using both GET and POST methods.Imagine the GET method returning information for this part, while the POST method changes the part of this ID in the database:

# matched with (r"/widget/(\d+)", WidgetHandler)
class WidgetHandler(tornado.web.RequestHandler):
    def get(self, widget_id):
        widget = retrieve_from_db(widget_id)
        self.write(widget.serialize())

    def post(self, widget_id):
        widget = retrieve_from_db(widget_id)
        widget['foo'] = self.get_argument('foo')
        save_to_db(widget)

We have only used GET and POST methods so far, but Tornado supports any legitimate HTTP requests (GET, POST, PUT, DELETE, HEAD, OPTIONS).You can easily define the behavior of any of these methods by using a method with the same name in the RequestHandler class.Here is another example of an imagination where a HEAD request for a specific frob ID only gives information based on the presence of the frob, and the GET method returns the entire object:

# matched with (r"/frob/(\d+)", FrobHandler)
class FrobHandler(tornado.web.RequestHandler):
    def head(self, frob_id):
        frob = retrieve_from_db(frob_id)
        if frob is not None:
            self.set_status(200)
        else:
            self.set_status(404)
    def get(self, frob_id):
        frob = retrieve_from_db(frob_id)
        self.write(frob.serialize())

 

2. HTTP status code:

From the code above, you can explicitly set the HTTP status code using the set_status() method of the RequestHandler class.However, you need to remember that in some cases Tornado automatically sets the HTTP status code.Here is an outline of a common situation:

404 Not Found
 Tornado returns a 404 (Not Found) response code when the path of the HTTP request does not match the pattern corresponding to any RequestHandler class.

400 Bad Request
 If you call a get_argument function without a default value and no parameter with a given name is found, Tornado will automatically return a 400 (Bad Request) response code.

405 Method Not Allowed
 If the incoming request uses an HTTP method not defined in RequestHandler (for example, a POST request, but only a get method is defined in the processing function), Tornado will return a 405 (Methos Not Allowed) response code.

500 Internal Server Error
 Tornado returns a 500 (Internal Server Error) response code when the program encounters any errors that prevent it from exiting.Any exception not caught in your code will also result in a 500 response code.

200 OK
 If the response is successful and no other return code is set, Tornado will return a 200 (OK) response code by default. 

When any of these errors occurs, Tornado will send a short snippet containing a status code and error information to the client by default.If you want to use your own method instead of the default error response, you can override the write_error method in your RequestHandler class.For example, code listings 1-3 are versions of the hello.py example that add regular error messages.

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        greeting = self.get_argument('greeting', 'Hello')
        self.write(greeting + ', friendly user!')
    def write_error(self, status_code, **kwargs):
        self.write("Gosh darnit, user! You caused a %d error." % status_code)

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

When we try a POST request, we get the following response.In general, we should get the default error response from Tornado, but because we override write_error, we get something different:

$ curl -d foo=bar http://localhost:8000/
Gosh darnit, user! You caused a 405 error.

Keywords: Python curl Web Server network

Added by BluePhoenixNC on Thu, 20 Jun 2019 22:57:00 +0300