├── .gitignore
├── LICENSE
├── README.rst
├── demos
├── appengine
│ ├── README
│ ├── app.yaml
│ ├── blog.py
│ ├── static
│ │ └── blog.css
│ └── templates
│ │ ├── archive.html
│ │ ├── base.html
│ │ ├── compose.html
│ │ ├── entry.html
│ │ ├── feed.xml
│ │ ├── home.html
│ │ └── modules
│ │ └── entry.html
├── benchmark
│ ├── benchmark.py
│ ├── chunk_benchmark.py
│ ├── gen_benchmark.py
│ ├── stack_context_benchmark.py
│ └── template_benchmark.py
├── blog
│ ├── Dockerfile
│ ├── README
│ ├── blog.py
│ ├── docker-compose.yml
│ ├── requirements.txt
│ ├── schema.sql
│ ├── static
│ │ └── blog.css
│ └── templates
│ │ ├── archive.html
│ │ ├── base.html
│ │ ├── compose.html
│ │ ├── create_author.html
│ │ ├── entry.html
│ │ ├── feed.xml
│ │ ├── home.html
│ │ ├── login.html
│ │ └── modules
│ │ └── entry.html
├── chat
│ ├── chatdemo.py
│ ├── static
│ │ ├── chat.css
│ │ └── chat.js
│ └── templates
│ │ ├── index.html
│ │ └── message.html
├── facebook
│ ├── README
│ ├── facebook.py
│ ├── static
│ │ ├── facebook.css
│ │ └── facebook.js
│ └── templates
│ │ ├── modules
│ │ └── post.html
│ │ └── stream.html
├── helloworld
│ └── helloworld.py
├── s3server
│ └── s3server.py
├── twitter
│ ├── home.html
│ └── twitterdemo.py
├── websocket
│ ├── chatdemo.py
│ ├── static
│ │ ├── chat.css
│ │ └── chat.js
│ └── templates
│ │ ├── index.html
│ │ └── message.html
└── webspider
│ └── webspider.py
├── docs
├── Makefile
├── asyncio.rst
├── auth.rst
├── autoreload.rst
├── caresresolver.rst
├── concurrent.rst
├── conf.py
├── coroutine.rst
├── escape.rst
├── faq.rst
├── favicon.ico
├── gen.rst
├── guide.rst
├── guide
│ ├── async.rst
│ ├── coroutines.rst
│ ├── intro.rst
│ ├── queues.rst
│ ├── running.rst
│ ├── security.rst
│ ├── structure.rst
│ └── templates.rst
├── http.rst
├── http1connection.rst
├── httpclient.rst
├── httpserver.rst
├── httputil.rst
├── index.rst
├── integration.rst
├── ioloop.rst
├── iostream.rst
├── locale.rst
├── locks.rst
├── log.rst
├── netutil.rst
├── networking.rst
├── options.rst
├── process.rst
├── queues.rst
├── releases.rst
├── releases
│ ├── v1.0.0.rst
│ ├── v1.0.1.rst
│ ├── v1.1.0.rst
│ ├── v1.1.1.rst
│ ├── v1.2.0.rst
│ ├── v1.2.1.rst
│ ├── v2.0.0.rst
│ ├── v2.1.0.rst
│ ├── v2.1.1.rst
│ ├── v2.2.0.rst
│ ├── v2.2.1.rst
│ ├── v2.3.0.rst
│ ├── v2.4.0.rst
│ ├── v2.4.1.rst
│ ├── v3.0.0.rst
│ ├── v3.0.1.rst
│ ├── v3.0.2.rst
│ ├── v3.1.0.rst
│ ├── v3.1.1.rst
│ ├── v3.2.0.rst
│ ├── v3.2.1.rst
│ ├── v3.2.2.rst
│ ├── v4.0.0.rst
│ ├── v4.0.1.rst
│ ├── v4.0.2.rst
│ ├── v4.1.0.rst
│ ├── v4.2.0.rst
│ ├── v4.2.1.rst
│ └── v4.3.0.rst
├── requirements.txt
├── stack_context.rst
├── tcpclient.rst
├── tcpserver.rst
├── template.rst
├── testing.rst
├── tornado.png
├── twisted.rst
├── util.rst
├── utilities.rst
├── web.rst
├── webframework.rst
├── websocket.rst
└── wsgi.rst
├── maint
├── README
├── requirements.txt
├── scripts
│ ├── custom_fixers
│ │ ├── __init__.py
│ │ ├── fix_future_imports.py
│ │ └── fix_unicode_literal.py
│ ├── run_autopep8.sh
│ ├── run_fixers.py
│ └── test_resolvers.py
├── test
│ ├── README
│ ├── appengine
│ │ ├── README
│ │ ├── common
│ │ │ ├── cgi_runtests.py
│ │ │ └── runtests.py
│ │ ├── py27
│ │ │ ├── app.yaml
│ │ │ ├── cgi_runtests.py
│ │ │ ├── runtests.py
│ │ │ └── tornado
│ │ ├── setup.py
│ │ └── tox.ini
│ ├── cython
│ │ ├── .gitignore
│ │ ├── MANIFEST.in
│ │ ├── cythonapp.pyx
│ │ ├── cythonapp_test.py
│ │ ├── pythonmodule.py
│ │ ├── setup.py
│ │ └── tox.ini
│ ├── pyuv
│ │ └── tox.ini
│ ├── redbot
│ │ ├── README
│ │ ├── red_test.py
│ │ └── tox.ini
│ └── websocket
│ │ ├── .gitignore
│ │ ├── client.py
│ │ ├── fuzzingclient.json
│ │ ├── fuzzingserver.json
│ │ ├── run-client.sh
│ │ ├── run-server.sh
│ │ ├── server.py
│ │ └── tox.ini
└── vm
│ ├── README
│ ├── freebsd
│ ├── Vagrantfile
│ ├── setup.sh
│ └── tox.ini
│ ├── shared-setup.sh
│ ├── ubuntu10.04
│ ├── Vagrantfile
│ ├── setup.sh
│ └── tox.ini
│ ├── ubuntu12.04
│ ├── Vagrantfile
│ ├── setup.sh
│ └── tox.ini
│ ├── ubuntu14.04
│ ├── Vagrantfile
│ ├── setup.sh
│ └── tox.ini
│ └── windows
│ ├── bootstrap.py
│ └── tox.ini
└── tornado
├── __init__.py
├── _locale_data.py
├── auth.py
├── autoreload.py
├── concurrent.py
├── curl_httpclient.py
├── escape.py
├── gen.py
├── http1connection.py
├── httpclient.py
├── httpserver.py
├── httputil.py
├── ioloop.py
├── iostream.py
├── locale.py
├── locks.py
├── log.py
├── netutil.py
├── options.py
├── platform
├── __init__.py
├── asyncio.py
├── auto.py
├── caresresolver.py
├── common.py
├── epoll.py
├── interface.py
├── kqueue.py
├── posix.py
├── select.py
├── twisted.py
└── windows.py
├── process.py
├── queues.py
├── simple_httpclient.py
├── speedups.c
├── stack_context.py
├── tcpclient.py
├── tcpserver.py
├── template.py
├── test
├── __init__.py
├── __main__.py
├── asyncio_test.py
├── auth_test.py
├── concurrent_test.py
├── csv_translations
│ └── fr_FR.csv
├── curl_httpclient_test.py
├── escape_test.py
├── gen_test.py
├── gettext_translations
│ ├── extract_me.py
│ └── fr_FR
│ │ └── LC_MESSAGES
│ │ └── tornado_test.po
├── httpclient_test.py
├── httpserver_test.py
├── httputil_test.py
├── import_test.py
├── ioloop_test.py
├── iostream_test.py
├── locale_test.py
├── locks_test.py
├── log_test.py
├── netutil_test.py
├── options_test.cfg
├── options_test.py
├── process_test.py
├── queues_test.py
├── resolve_test_helper.py
├── runtests.py
├── simple_httpclient_test.py
├── stack_context_test.py
├── static
│ ├── dir
│ │ └── index.html
│ ├── robots.txt
│ ├── sample.xml
│ ├── sample.xml.bz2
│ └── sample.xml.gz
├── static_foo.txt
├── tcpclient_test.py
├── tcpserver_test.py
├── template_test.py
├── templates
│ └── utf8.html
├── test.crt
├── test.key
├── testing_test.py
├── twisted_test.py
├── util.py
├── util_test.py
├── web_test.py
├── websocket_test.py
└── wsgi_test.py
├── testing.py
├── util.py
├── web.py
├── websocket.py
└── wsgi.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env/
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | *.egg-info/
23 | .installed.cfg
24 | *.egg
25 |
26 | # PyInstaller
27 | # Usually these files are written by a python script from a template
28 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
29 | *.manifest
30 | *.spec
31 |
32 | # Installer logs
33 | pip-log.txt
34 | pip-delete-this-directory.txt
35 |
36 | # Unit test / coverage reports
37 | htmlcov/
38 | .tox/
39 | .coverage
40 | .coverage.*
41 | .cache
42 | nosetests.xml
43 | coverage.xml
44 | *,cover
45 |
46 | # Translations
47 | *.mo
48 | *.pot
49 |
50 | # Django stuff:
51 | *.log
52 |
53 | # Sphinx documentation
54 | docs/_build/
55 |
56 | # PyBuilder
57 | target/
58 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 TaoBeier
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | Tornado-zh
2 | ===========
3 |
4 | Tornado 4.3 docs 中文翻译
5 |
6 |
--------------------------------------------------------------------------------
/demos/appengine/README:
--------------------------------------------------------------------------------
1 | Running the Tornado AppEngine example
2 | =====================================
3 | This example is designed to run in Google AppEngine, so there are a couple
4 | of steps to get it running. You can download the Google AppEngine Python
5 | development environment at http://code.google.com/appengine/downloads.html.
6 |
7 | 1. Link or copy the tornado code directory into this directory:
8 |
9 | ln -s ../../tornado tornado
10 |
11 | AppEngine doesn't use the Python modules installed on this machine.
12 | You need to have the 'tornado' module copied or linked for AppEngine
13 | to find it.
14 |
15 | 3. Install and run dev_appserver
16 |
17 | If you don't already have the App Engine SDK, download it from
18 | http://code.google.com/appengine/downloads.html
19 |
20 | To start the tornado demo, run the dev server on this directory:
21 |
22 | dev_appserver.py .
23 |
24 | 4. Visit http://localhost:8080/ in your browser
25 |
26 | If you sign in as an administrator, you will be able to create and
27 | edit blog posts. If you sign in as anybody else, you will only see
28 | the existing blog posts.
29 |
30 |
31 | If you want to deploy the blog in production:
32 |
33 | 1. Register a new appengine application and put its id in app.yaml
34 |
35 | First register a new application at http://appengine.google.com/.
36 | Then edit app.yaml in this directory and change the "application"
37 | setting from "tornado-appenginge" to your new application id.
38 |
39 | 2. Deploy to App Engine
40 |
41 | If you registered an application id, you can now upload your new
42 | Tornado blog by running this command:
43 |
44 | appcfg update .
45 |
46 | After that, visit application_id.appspot.com, where application_id
47 | is the application you registered.
48 |
49 |
--------------------------------------------------------------------------------
/demos/appengine/app.yaml:
--------------------------------------------------------------------------------
1 | application: tornado-appengine
2 | version: 2
3 | runtime: python27
4 | api_version: 1
5 | threadsafe: yes
6 |
7 | handlers:
8 | - url: /static/
9 | static_dir: static
10 |
11 | - url: /.*
12 | script: blog.application
13 |
--------------------------------------------------------------------------------
/demos/appengine/static/blog.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2009 Facebook
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | * not use this file except in compliance with the License. You may obtain
6 | * a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | body {
18 | background: white;
19 | color: black;
20 | margin: 15px;
21 | margin-top: 0;
22 | }
23 |
24 | body,
25 | input,
26 | textarea {
27 | font-family: Georgia, serif;
28 | font-size: 12pt;
29 | }
30 |
31 | table {
32 | border-collapse: collapse;
33 | border: 0;
34 | }
35 |
36 | td {
37 | border: 0;
38 | padding: 0;
39 | }
40 |
41 | h1,
42 | h2,
43 | h3,
44 | h4 {
45 | font-family: "Helvetica Nue", Helvetica, Arial, sans-serif;
46 | margin: 0;
47 | }
48 |
49 | h1 {
50 | font-size: 20pt;
51 | }
52 |
53 | pre,
54 | code {
55 | font-family: monospace;
56 | color: #060;
57 | }
58 |
59 | pre {
60 | margin-left: 1em;
61 | padding-left: 1em;
62 | border-left: 1px solid silver;
63 | line-height: 14pt;
64 | }
65 |
66 | a,
67 | a code {
68 | color: #00c;
69 | }
70 |
71 | #body {
72 | max-width: 800px;
73 | margin: auto;
74 | }
75 |
76 | #header {
77 | background-color: #3b5998;
78 | padding: 5px;
79 | padding-left: 10px;
80 | padding-right: 10px;
81 | margin-bottom: 1em;
82 | }
83 |
84 | #header,
85 | #header a {
86 | color: white;
87 | }
88 |
89 | #header h1 a {
90 | text-decoration: none;
91 | }
92 |
93 | #footer,
94 | #content {
95 | margin-left: 10px;
96 | margin-right: 10px;
97 | }
98 |
99 | #footer {
100 | margin-top: 3em;
101 | }
102 |
103 | .entry h1 a {
104 | color: black;
105 | text-decoration: none;
106 | }
107 |
108 | .entry {
109 | margin-bottom: 2em;
110 | }
111 |
112 | .entry .date {
113 | margin-top: 3px;
114 | }
115 |
116 | .entry p {
117 | margin: 0;
118 | margin-bottom: 1em;
119 | }
120 |
121 | .entry .body {
122 | margin-top: 1em;
123 | line-height: 16pt;
124 | }
125 |
126 | .compose td {
127 | vertical-align: middle;
128 | padding-bottom: 5px;
129 | }
130 |
131 | .compose td.field {
132 | padding-right: 10px;
133 | }
134 |
135 | .compose .title,
136 | .compose .submit {
137 | font-family: "Helvetica Nue", Helvetica, Arial, sans-serif;
138 | font-weight: bold;
139 | }
140 |
141 | .compose .title {
142 | font-size: 20pt;
143 | }
144 |
145 | .compose .title,
146 | .compose .body_source {
147 | width: 100%;
148 | }
149 |
150 | .compose .body_source {
151 | height: 500px;
152 | line-height: 16pt;
153 | }
154 |
--------------------------------------------------------------------------------
/demos/appengine/templates/archive.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block head %}
4 |
20 | {% end %}
21 |
22 | {% block body %}
23 |
31 | {% end %}
32 |
--------------------------------------------------------------------------------
/demos/appengine/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ handler.settings["blog_title"] }}
6 |
7 |
8 | {% block head %}{% end %}
9 |
10 |
11 |
12 |
25 |
{% block body %}{% end %}
26 |
27 | {% block bottom %}{% end %}
28 |
29 |
30 |
--------------------------------------------------------------------------------
/demos/appengine/templates/compose.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 |
16 | {% end %}
17 |
18 | {% block bottom %}
19 |
20 |
40 | {% end %}
41 |
--------------------------------------------------------------------------------
/demos/appengine/templates/entry.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | {% module Entry(entry) %}
5 | {% end %}
6 |
--------------------------------------------------------------------------------
/demos/appengine/templates/feed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% set date_format = "%Y-%m-%dT%H:%M:%SZ" %}
4 | {{ handler.settings["blog_title"] }}
5 | {% if len(entries) > 0 %}
6 | {{ max(e.updated for e in entries).strftime(date_format) }}
7 | {% else %}
8 | {{ datetime.datetime.utcnow().strftime(date_format) }}
9 | {% end %}
10 | http://{{ request.host }}/
11 |
12 |
13 | {{ handler.settings["blog_title"] }}
14 | {% for entry in entries %}
15 |
16 | http://{{ request.host }}/entry/{{ entry.slug }}
17 | {{ entry.title }}
18 |
19 | {{ entry.updated.strftime(date_format) }}
20 | {{ entry.published.strftime(date_format) }}
21 |
22 | {% raw entry.html %}
23 |
24 |
25 | {% end %}
26 |
27 |
--------------------------------------------------------------------------------
/demos/appengine/templates/home.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | {% for entry in entries %}
5 | {% module Entry(entry) %}
6 | {% end %}
7 |
8 | {% end %}
9 |
--------------------------------------------------------------------------------
/demos/appengine/templates/modules/entry.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ locale.format_date(entry.published, full_format=True, shorter=True) }}
4 |
{% raw entry.html %}
5 | {% if current_user and current_user.administrator %}
6 |
7 | {% end %}
8 |
9 |
--------------------------------------------------------------------------------
/demos/benchmark/benchmark.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # A simple benchmark of tornado's HTTP stack.
4 | # Requires 'ab' to be installed.
5 | #
6 | # Running without profiling:
7 | # demos/benchmark/benchmark.py
8 | # demos/benchmark/benchmark.py --quiet --num_runs=5|grep "Requests per second"
9 | #
10 | # Running with profiling:
11 | #
12 | # python -m cProfile -o /tmp/prof demos/benchmark/benchmark.py
13 | # python -m pstats /tmp/prof
14 | # % sort time
15 | # % stats 20
16 |
17 | from tornado.ioloop import IOLoop
18 | from tornado.options import define, options, parse_command_line
19 | from tornado.web import RequestHandler, Application
20 |
21 | import random
22 | import signal
23 | import subprocess
24 |
25 | try:
26 | xrange
27 | except NameError:
28 | xrange = range
29 |
30 | # choose a random port to avoid colliding with TIME_WAIT sockets left over
31 | # from previous runs.
32 | define("min_port", type=int, default=8000)
33 | define("max_port", type=int, default=9000)
34 |
35 | # Increasing --n without --keepalive will eventually run into problems
36 | # due to TIME_WAIT sockets
37 | define("n", type=int, default=15000)
38 | define("c", type=int, default=25)
39 | define("keepalive", type=bool, default=False)
40 | define("quiet", type=bool, default=False)
41 |
42 | # Repeat the entire benchmark this many times (on different ports)
43 | # This gives JITs time to warm up, etc. Pypy needs 3-5 runs at
44 | # --n=15000 for its JIT to reach full effectiveness
45 | define("num_runs", type=int, default=1)
46 |
47 | define("ioloop", type=str, default=None)
48 |
49 | class RootHandler(RequestHandler):
50 | def get(self):
51 | self.write("Hello, world")
52 |
53 | def _log(self):
54 | pass
55 |
56 | def handle_sigchld(sig, frame):
57 | IOLoop.instance().add_callback_from_signal(IOLoop.instance().stop)
58 |
59 | def main():
60 | parse_command_line()
61 | if options.ioloop:
62 | IOLoop.configure(options.ioloop)
63 | for i in xrange(options.num_runs):
64 | run()
65 |
66 | def run():
67 | app = Application([("/", RootHandler)])
68 | port = random.randrange(options.min_port, options.max_port)
69 | app.listen(port, address='127.0.0.1')
70 | signal.signal(signal.SIGCHLD, handle_sigchld)
71 | args = ["ab"]
72 | args.extend(["-n", str(options.n)])
73 | args.extend(["-c", str(options.c)])
74 | if options.keepalive:
75 | args.append("-k")
76 | if options.quiet:
77 | # just stops the progress messages printed to stderr
78 | args.append("-q")
79 | args.append("http://127.0.0.1:%d/" % port)
80 | subprocess.Popen(args)
81 | IOLoop.instance().start()
82 | IOLoop.instance().close()
83 | del IOLoop._instance
84 | assert not IOLoop.initialized()
85 |
86 | if __name__ == '__main__':
87 | main()
88 |
--------------------------------------------------------------------------------
/demos/benchmark/chunk_benchmark.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Downloads a large file in chunked encoding with both curl and simple clients
4 |
5 | import logging
6 | from tornado.curl_httpclient import CurlAsyncHTTPClient
7 | from tornado.simple_httpclient import SimpleAsyncHTTPClient
8 | from tornado.ioloop import IOLoop
9 | from tornado.options import define, options, parse_command_line
10 | from tornado.web import RequestHandler, Application
11 |
12 | define('port', default=8888)
13 | define('num_chunks', default=1000)
14 | define('chunk_size', default=2048)
15 |
16 |
17 | class ChunkHandler(RequestHandler):
18 | def get(self):
19 | for i in xrange(options.num_chunks):
20 | self.write('A' * options.chunk_size)
21 | self.flush()
22 | self.finish()
23 |
24 |
25 | def main():
26 | parse_command_line()
27 | app = Application([('/', ChunkHandler)])
28 | app.listen(options.port, address='127.0.0.1')
29 |
30 | def callback(response):
31 | response.rethrow()
32 | assert len(response.body) == (options.num_chunks * options.chunk_size)
33 | logging.warning("fetch completed in %s seconds", response.request_time)
34 | IOLoop.current().stop()
35 |
36 | logging.warning("Starting fetch with curl client")
37 | curl_client = CurlAsyncHTTPClient()
38 | curl_client.fetch('http://localhost:%d/' % options.port,
39 | callback=callback)
40 | IOLoop.current().start()
41 |
42 | logging.warning("Starting fetch with simple client")
43 | simple_client = SimpleAsyncHTTPClient()
44 | simple_client.fetch('http://localhost:%d/' % options.port,
45 | callback=callback)
46 | IOLoop.current().start()
47 |
48 |
49 | if __name__ == '__main__':
50 | main()
51 |
--------------------------------------------------------------------------------
/demos/benchmark/gen_benchmark.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # A simple benchmark of the tornado.gen module.
4 | # Runs in two modes, testing new-style (@coroutine and Futures)
5 | # and old-style (@engine and Tasks) coroutines.
6 |
7 | from timeit import Timer
8 |
9 | from tornado import gen
10 | from tornado.options import options, define, parse_command_line
11 |
12 | define('num', default=10000, help='number of iterations')
13 |
14 | # These benchmarks are delicate. They hit various fast-paths in the gen
15 | # machinery in order to stay synchronous so we don't need an IOLoop.
16 | # This removes noise from the results, but it's easy to change things
17 | # in a way that completely invalidates the results.
18 |
19 | @gen.engine
20 | def e2(callback):
21 | callback()
22 |
23 | @gen.engine
24 | def e1():
25 | for i in range(10):
26 | yield gen.Task(e2)
27 |
28 | @gen.coroutine
29 | def c2():
30 | pass
31 |
32 | @gen.coroutine
33 | def c1():
34 | for i in range(10):
35 | yield c2()
36 |
37 | def main():
38 | parse_command_line()
39 | t = Timer(e1)
40 | results = t.timeit(options.num) / options.num
41 | print('engine: %0.3f ms per iteration' % (results * 1000))
42 | t = Timer(c1)
43 | results = t.timeit(options.num) / options.num
44 | print('coroutine: %0.3f ms per iteration' % (results * 1000))
45 |
46 | if __name__ == '__main__':
47 | main()
48 |
--------------------------------------------------------------------------------
/demos/benchmark/stack_context_benchmark.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Benchmark for stack_context functionality."""
3 | import collections
4 | import contextlib
5 | import functools
6 | import subprocess
7 | import sys
8 |
9 | from tornado import stack_context
10 |
11 | class Benchmark(object):
12 | def enter_exit(self, count):
13 | """Measures the overhead of the nested "with" statements
14 | when using many contexts.
15 | """
16 | if count < 0:
17 | return
18 | with self.make_context():
19 | self.enter_exit(count - 1)
20 |
21 | def call_wrapped(self, count):
22 | """Wraps and calls a function at each level of stack depth
23 | to measure the overhead of the wrapped function.
24 | """
25 | # This queue is analogous to IOLoop.add_callback, but lets us
26 | # benchmark the stack_context in isolation without system call
27 | # overhead.
28 | queue = collections.deque()
29 | self.call_wrapped_inner(queue, count)
30 | while queue:
31 | queue.popleft()()
32 |
33 | def call_wrapped_inner(self, queue, count):
34 | if count < 0:
35 | return
36 | with self.make_context():
37 | queue.append(stack_context.wrap(
38 | functools.partial(self.call_wrapped_inner, queue, count - 1)))
39 |
40 | class StackBenchmark(Benchmark):
41 | def make_context(self):
42 | return stack_context.StackContext(self.__context)
43 |
44 | @contextlib.contextmanager
45 | def __context(self):
46 | yield
47 |
48 | class ExceptionBenchmark(Benchmark):
49 | def make_context(self):
50 | return stack_context.ExceptionStackContext(self.__handle_exception)
51 |
52 | def __handle_exception(self, typ, value, tb):
53 | pass
54 |
55 | def main():
56 | base_cmd = [
57 | sys.executable, '-m', 'timeit', '-s',
58 | 'from stack_context_benchmark import StackBenchmark, ExceptionBenchmark']
59 | cmds = [
60 | 'StackBenchmark().enter_exit(50)',
61 | 'StackBenchmark().call_wrapped(50)',
62 | 'StackBenchmark().enter_exit(500)',
63 | 'StackBenchmark().call_wrapped(500)',
64 |
65 | 'ExceptionBenchmark().enter_exit(50)',
66 | 'ExceptionBenchmark().call_wrapped(50)',
67 | 'ExceptionBenchmark().enter_exit(500)',
68 | 'ExceptionBenchmark().call_wrapped(500)',
69 | ]
70 | for cmd in cmds:
71 | print(cmd)
72 | subprocess.check_call(base_cmd + [cmd])
73 |
74 | if __name__ == '__main__':
75 | main()
76 |
--------------------------------------------------------------------------------
/demos/benchmark/template_benchmark.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # A simple benchmark of tornado template rendering, based on
4 | # https://github.com/mitsuhiko/jinja2/blob/master/examples/bench.py
5 |
6 | import sys
7 | from timeit import Timer
8 |
9 | from tornado.options import options, define, parse_command_line
10 | from tornado.template import Template
11 |
12 | define('num', default=100, help='number of iterations')
13 | define('dump', default=False, help='print template generated code and exit')
14 |
15 | context = {
16 | 'page_title': 'mitsuhiko\'s benchmark',
17 | 'table': [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) for x in range(1000)]
18 | }
19 |
20 | tmpl = Template("""\
21 |
22 |
23 |
24 | {{ page_title }}
25 |
26 |
27 |
30 |
31 | {% for href, caption in [ \
32 | ('index.html', 'Index'), \
33 | ('downloads.html', 'Downloads'), \
34 | ('products.html', 'Products') \
35 | ] %}
36 | {{ caption }}
37 | {% end %}
38 |
39 |
40 |
41 | {% for row in table %}
42 |
43 | {% for cell in row %}
44 | {{ cell }}
45 | {% end %}
46 |
47 | {% end %}
48 |
49 |
50 |
51 | \
52 | """)
53 |
54 | def render():
55 | tmpl.generate(**context)
56 |
57 | def main():
58 | parse_command_line()
59 | if options.dump:
60 | print(tmpl.code)
61 | sys.exit(0)
62 | t = Timer(render)
63 | results = t.timeit(options.num) / options.num
64 | print('%0.3f ms per iteration' % (results*1000))
65 |
66 | if __name__ == '__main__':
67 | main()
68 |
--------------------------------------------------------------------------------
/demos/blog/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:2.7
2 |
3 | EXPOSE 8888
4 |
5 | RUN apt-get update && apt-get install -y mysql-client
6 |
7 | # based on python:2.7-onbuild, but if we use that image directly
8 | # the above apt-get line runs too late.
9 | RUN mkdir -p /usr/src/app
10 | WORKDIR /usr/src/app
11 |
12 | COPY requirements.txt /usr/src/app/
13 | RUN pip install -r requirements.txt
14 |
15 | COPY . /usr/src/app
16 |
17 | CMD python blog.py --mysql_host=mysql
18 |
--------------------------------------------------------------------------------
/demos/blog/README:
--------------------------------------------------------------------------------
1 | Running the Tornado Blog example app
2 | ====================================
3 | This demo is a simple blogging engine that uses MySQL to store posts and
4 | Google Accounts for author authentication. Since it depends on MySQL, you
5 | need to set up MySQL and the database schema for the demo to run.
6 |
7 | If you have `docker` and `docker-compose` installed, the demo and all
8 | its prerequisites can be installed with `docker-compose up`.
9 |
10 | 1. Install prerequisites and build tornado
11 |
12 | See http://www.tornadoweb.org/ for installation instructions. If you can
13 | run the "helloworld" example application, your environment is set up
14 | correctly.
15 |
16 | 2. Install MySQL if needed
17 |
18 | Consult the documentation for your platform. Under Ubuntu Linux you
19 | can run "apt-get install mysql". Under OS X you can download the
20 | MySQL PKG file from http://dev.mysql.com/downloads/mysql/
21 |
22 | 3. Install Python prerequisites
23 |
24 | Install the packages MySQL-python, torndb, and markdown (e.g. using pip or
25 | easy_install). Note that these packages currently only work on
26 | Python 2. Tornado supports Python 3, but this blog demo does not.
27 |
28 | 3. Connect to MySQL and create a database and user for the blog.
29 |
30 | Connect to MySQL as a user that can create databases and users:
31 | mysql -u root
32 |
33 | Create a database named "blog":
34 | mysql> CREATE DATABASE blog;
35 |
36 | Allow the "blog" user to connect with the password "blog":
37 | mysql> GRANT ALL PRIVILEGES ON blog.* TO 'blog'@'localhost' IDENTIFIED BY 'blog';
38 |
39 | 4. Create the tables in your new database.
40 |
41 | You can use the provided schema.sql file by running this command:
42 | mysql --user=blog --password=blog --database=blog < schema.sql
43 |
44 | You can run the above command again later if you want to delete the
45 | contents of the blog and start over after testing.
46 |
47 | 5. Run the blog example
48 |
49 | With the default user, password, and database you can just run:
50 | ./blog.py
51 |
52 | If you've changed anything, you can alter the default MySQL settings
53 | with arguments on the command line, e.g.:
54 | ./blog.py --mysql_user=casey --mysql_password=happiness --mysql_database=foodblog
55 |
56 | 6. Visit your new blog
57 |
58 | Open http://localhost:8888/ in your web browser. You will be redirected to
59 | a Google account sign-in page because the blog uses Google accounts for
60 | authentication.
61 |
62 | Currently the first user to connect will automatically be given the
63 | ability to create and edit posts.
64 |
65 | Once you've created one blog post, subsequent users will not be
66 | prompted to sign in.
67 |
--------------------------------------------------------------------------------
/demos/blog/docker-compose.yml:
--------------------------------------------------------------------------------
1 | mysql:
2 | image: mysql:5.6
3 | environment:
4 | MYSQL_ROOT_PASSWORD: its_a_secret_to_everybody
5 | MYSQL_USER: blog
6 | MYSQL_PASSWORD: blog
7 | MYSQL_DATABASE: blog
8 | ports:
9 | - "3306"
10 | blog:
11 | build: .
12 | links:
13 | - mysql
14 | ports:
15 | - "8888:8888"
16 |
--------------------------------------------------------------------------------
/demos/blog/requirements.txt:
--------------------------------------------------------------------------------
1 | bcrypt
2 | futures
3 | MySQL-python
4 | markdown
5 | tornado
6 | torndb
7 |
--------------------------------------------------------------------------------
/demos/blog/schema.sql:
--------------------------------------------------------------------------------
1 | -- Copyright 2009 FriendFeed
2 | --
3 | -- Licensed under the Apache License, Version 2.0 (the "License"); you may
4 | -- not use this file except in compliance with the License. You may obtain
5 | -- a copy of the License at
6 | --
7 | -- http://www.apache.org/licenses/LICENSE-2.0
8 | --
9 | -- Unless required by applicable law or agreed to in writing, software
10 | -- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 | -- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 | -- License for the specific language governing permissions and limitations
13 | -- under the License.
14 |
15 | -- To create the database:
16 | -- CREATE DATABASE blog;
17 | -- GRANT ALL PRIVILEGES ON blog.* TO 'blog'@'localhost' IDENTIFIED BY 'blog';
18 | --
19 | -- To reload the tables:
20 | -- mysql --user=blog --password=blog --database=blog < schema.sql
21 |
22 | SET SESSION storage_engine = "InnoDB";
23 | SET SESSION time_zone = "+0:00";
24 | ALTER DATABASE CHARACTER SET "utf8";
25 |
26 | DROP TABLE IF EXISTS entries;
27 | CREATE TABLE entries (
28 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
29 | author_id INT NOT NULL REFERENCES authors(id),
30 | slug VARCHAR(100) NOT NULL UNIQUE,
31 | title VARCHAR(512) NOT NULL,
32 | markdown MEDIUMTEXT NOT NULL,
33 | html MEDIUMTEXT NOT NULL,
34 | published DATETIME NOT NULL,
35 | updated TIMESTAMP NOT NULL,
36 | KEY (published)
37 | );
38 |
39 | DROP TABLE IF EXISTS authors;
40 | CREATE TABLE authors (
41 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
42 | email VARCHAR(100) NOT NULL UNIQUE,
43 | name VARCHAR(100) NOT NULL,
44 | hashed_password VARCHAR(100) NOT NULL
45 | );
46 |
--------------------------------------------------------------------------------
/demos/blog/static/blog.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2009 Facebook
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | * not use this file except in compliance with the License. You may obtain
6 | * a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | body {
18 | background: white;
19 | color: black;
20 | margin: 15px;
21 | margin-top: 0;
22 | }
23 |
24 | body,
25 | input,
26 | textarea {
27 | font-family: Georgia, serif;
28 | font-size: 12pt;
29 | }
30 |
31 | table {
32 | border-collapse: collapse;
33 | border: 0;
34 | }
35 |
36 | td {
37 | border: 0;
38 | padding: 0;
39 | }
40 |
41 | h1,
42 | h2,
43 | h3,
44 | h4 {
45 | font-family: "Helvetica Nue", Helvetica, Arial, sans-serif;
46 | margin: 0;
47 | }
48 |
49 | h1 {
50 | font-size: 20pt;
51 | }
52 |
53 | pre,
54 | code {
55 | font-family: monospace;
56 | color: #060;
57 | }
58 |
59 | pre {
60 | margin-left: 1em;
61 | padding-left: 1em;
62 | border-left: 1px solid silver;
63 | line-height: 14pt;
64 | }
65 |
66 | a,
67 | a code {
68 | color: #00c;
69 | }
70 |
71 | #body {
72 | max-width: 800px;
73 | margin: auto;
74 | }
75 |
76 | #header {
77 | background-color: #3b5998;
78 | padding: 5px;
79 | padding-left: 10px;
80 | padding-right: 10px;
81 | margin-bottom: 1em;
82 | }
83 |
84 | #header,
85 | #header a {
86 | color: white;
87 | }
88 |
89 | #header h1 a {
90 | text-decoration: none;
91 | }
92 |
93 | #footer,
94 | #content {
95 | margin-left: 10px;
96 | margin-right: 10px;
97 | }
98 |
99 | #footer {
100 | margin-top: 3em;
101 | }
102 |
103 | .entry h1 a {
104 | color: black;
105 | text-decoration: none;
106 | }
107 |
108 | .entry {
109 | margin-bottom: 2em;
110 | }
111 |
112 | .entry .date {
113 | margin-top: 3px;
114 | }
115 |
116 | .entry p {
117 | margin: 0;
118 | margin-bottom: 1em;
119 | }
120 |
121 | .entry .body {
122 | margin-top: 1em;
123 | line-height: 16pt;
124 | }
125 |
126 | .compose td {
127 | vertical-align: middle;
128 | padding-bottom: 5px;
129 | }
130 |
131 | .compose td.field {
132 | padding-right: 10px;
133 | }
134 |
135 | .compose .title,
136 | .compose .submit {
137 | font-family: "Helvetica Nue", Helvetica, Arial, sans-serif;
138 | font-weight: bold;
139 | }
140 |
141 | .compose .title {
142 | font-size: 20pt;
143 | }
144 |
145 | .compose .title,
146 | .compose .markdown {
147 | width: 100%;
148 | }
149 |
150 | .compose .markdown {
151 | height: 500px;
152 | line-height: 16pt;
153 | }
154 |
--------------------------------------------------------------------------------
/demos/blog/templates/archive.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block head %}
4 |
20 | {% end %}
21 |
22 | {% block body %}
23 |
31 | {% end %}
32 |
--------------------------------------------------------------------------------
/demos/blog/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ escape(handler.settings["blog_title"]) }}
6 |
7 |
8 | {% block head %}{% end %}
9 |
10 |
11 |
12 |
23 |
{% block body %}{% end %}
24 |
25 | {% block bottom %}{% end %}
26 |
27 |
28 |
--------------------------------------------------------------------------------
/demos/blog/templates/compose.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 |
5 |
6 | {{ entry.markdown if entry else "" }}
7 |
12 | {% if entry %}
13 |
14 | {% end %}
15 | {% module xsrf_form_html() %}
16 |
17 | {% end %}
18 |
19 | {% block bottom %}
20 |
21 |
41 | {% end %}
42 |
--------------------------------------------------------------------------------
/demos/blog/templates/create_author.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 |
5 | Email:
6 | Name:
7 | Password:
8 | {% module xsrf_form_html() %}
9 |
10 |
11 | {% end %}
12 |
--------------------------------------------------------------------------------
/demos/blog/templates/entry.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | {% module Entry(entry) %}
5 | {% end %}
6 |
--------------------------------------------------------------------------------
/demos/blog/templates/feed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% set date_format = "%Y-%m-%dT%H:%M:%SZ" %}
4 | {{ handler.settings["blog_title"] }}
5 | {% if len(entries) > 0 %}
6 | {{ max(e.updated for e in entries).strftime(date_format) }}
7 | {% else %}
8 | {{ datetime.datetime.utcnow().strftime(date_format) }}
9 | {% end %}
10 | http://{{ request.host }}/
11 |
12 |
13 | {{ handler.settings["blog_title"] }}
14 | {% for entry in entries %}
15 |
16 | http://{{ request.host }}/entry/{{ entry.slug }}
17 | {{ entry.title }}
18 |
19 | {{ entry.updated.strftime(date_format) }}
20 | {{ entry.published.strftime(date_format) }}
21 |
22 | {% raw entry.html %}
23 |
24 |
25 | {% end %}
26 |
27 |
--------------------------------------------------------------------------------
/demos/blog/templates/home.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | {% for entry in entries %}
5 | {% module Entry(entry) %}
6 | {% end %}
7 |
8 | {% end %}
9 |
--------------------------------------------------------------------------------
/demos/blog/templates/login.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body %}
4 | {% if error %}
5 | Error: {{ error }}
6 | {% end %}
7 |
8 |
9 | Email:
10 | Password:
11 | {% module xsrf_form_html() %}
12 |
13 |
14 | {% end %}
15 |
--------------------------------------------------------------------------------
/demos/blog/templates/modules/entry.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ locale.format_date(entry.published, full_format=True, shorter=True) }}
4 |
{% raw entry.html %}
5 | {% if current_user %}
6 |
7 | {% end %}
8 |
9 |
--------------------------------------------------------------------------------
/demos/chat/static/chat.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2009 FriendFeed
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | * not use this file except in compliance with the License. You may obtain
6 | * a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | body {
18 | background: white;
19 | margin: 10px;
20 | }
21 |
22 | body,
23 | input {
24 | font-family: sans-serif;
25 | font-size: 10pt;
26 | color: black;
27 | }
28 |
29 | table {
30 | border-collapse: collapse;
31 | border: 0;
32 | }
33 |
34 | td {
35 | border: 0;
36 | padding: 0;
37 | }
38 |
39 | #body {
40 | position: absolute;
41 | bottom: 10px;
42 | left: 10px;
43 | }
44 |
45 | #input {
46 | margin-top: 0.5em;
47 | }
48 |
49 | #inbox .message {
50 | padding-top: 0.25em;
51 | }
52 |
53 | #nav {
54 | float: right;
55 | z-index: 99;
56 | }
57 |
--------------------------------------------------------------------------------
/demos/chat/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tornado Chat Demo
6 |
7 |
8 |
9 |
10 |
11 | {% for message in messages %}
12 | {% module Template("message.html", message=message) %}
13 | {% end %}
14 |
15 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/demos/chat/templates/message.html:
--------------------------------------------------------------------------------
1 | {% module linkify(message["body"]) %}
2 |
--------------------------------------------------------------------------------
/demos/facebook/README:
--------------------------------------------------------------------------------
1 | Running the Tornado Facebook example
2 | ====================================
3 |
4 | To run this example, you must register a Facebook application with a
5 | Connect URL set to the domain the this demo will be running on
6 | (i.e. http://localhost:8888/ by default). The API key and secret
7 | for this application must be passed on the command line:
8 |
9 | python facebook.py --facebook_api_key=ABC --facebook_secret=XYZ
10 |
--------------------------------------------------------------------------------
/demos/facebook/static/facebook.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2009 Facebook
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | * not use this file except in compliance with the License. You may obtain
6 | * a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | body {
18 | background: white;
19 | color: black;
20 | margin: 15px;
21 | }
22 |
23 | body,
24 | input,
25 | textarea {
26 | font-family: "Lucida Grande", Tahoma, Verdana, sans-serif;
27 | font-size: 10pt;
28 | }
29 |
30 | table {
31 | border-collapse: collapse;
32 | border: 0;
33 | }
34 |
35 | td {
36 | border: 0;
37 | padding: 0;
38 | }
39 |
40 | img {
41 | border: 0;
42 | }
43 |
44 | a {
45 | text-decoration: none;
46 | color: #3b5998;
47 | }
48 |
49 | a:hover {
50 | text-decoration: underline;
51 | }
52 |
53 | .post {
54 | border-bottom: 1px solid #eeeeee;
55 | min-height: 50px;
56 | padding-bottom: 10px;
57 | margin-top: 10px;
58 | }
59 |
60 | .post .picture {
61 | float: left;
62 | }
63 |
64 | .post .picture img {
65 | height: 50px;
66 | width: 50px;
67 | }
68 |
69 | .post .body {
70 | margin-left: 60px;
71 | }
72 |
73 | .post .media img {
74 | border: 1px solid #cccccc;
75 | padding: 3px;
76 | }
77 |
78 | .post .media:hover img {
79 | border: 1px solid #3b5998;
80 | }
81 |
82 | .post a.actor {
83 | font-weight: bold;
84 | }
85 |
86 | .post .meta {
87 | font-size: 11px;
88 | }
89 |
90 | .post a.permalink {
91 | color: #777777;
92 | }
93 |
94 | #body {
95 | max-width: 700px;
96 | margin: auto;
97 | }
98 |
--------------------------------------------------------------------------------
/demos/facebook/static/facebook.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tao12345666333/tornado-zh/e9e8519beb147d9e1290f6a4fa7d61123d1ecb1c/demos/facebook/static/facebook.js
--------------------------------------------------------------------------------
/demos/facebook/templates/modules/post.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% set author_url="http://www.facebook.com/profile.php?id=" + escape(post["from"]["id"]) %}
4 |
5 |
6 |
7 |
{{ escape(post["from"]["name"]) }}
8 | {% if "message" in post %}
9 |
{{ escape(post["message"]) }}
10 | {% end %}
11 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/facebook/templates/stream.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tornado Facebook Stream Demo
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 | {% for post in stream["data"] %}
17 | {{ modules.Post(post) }}
18 | {% end %}
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/demos/helloworld/helloworld.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright 2009 Facebook
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 | # not use this file except in compliance with the License. You may obtain
7 | # a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | # License for the specific language governing permissions and limitations
15 | # under the License.
16 |
17 | import tornado.httpserver
18 | import tornado.ioloop
19 | import tornado.options
20 | import tornado.web
21 |
22 | from tornado.options import define, options
23 |
24 | define("port", default=8888, help="run on the given port", type=int)
25 |
26 |
27 | class MainHandler(tornado.web.RequestHandler):
28 | def get(self):
29 | self.write("Hello, world")
30 |
31 |
32 | def main():
33 | tornado.options.parse_command_line()
34 | application = tornado.web.Application([
35 | (r"/", MainHandler),
36 | ])
37 | http_server = tornado.httpserver.HTTPServer(application)
38 | http_server.listen(options.port)
39 | tornado.ioloop.IOLoop.current().start()
40 |
41 |
42 | if __name__ == "__main__":
43 | main()
44 |
--------------------------------------------------------------------------------
/demos/twitter/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tornado Twitter Demo
4 |
5 |
6 |
7 | {% for tweet in timeline %}
8 | {{ tweet['user']['screen_name'] }} : {{ tweet['text'] }}
9 | {% end %}
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/demos/twitter/twitterdemo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """A simplistic Twitter viewer to demonstrate the use of TwitterMixin.
3 |
4 | To run this app, you must first register an application with Twitter:
5 | 1) Go to https://dev.twitter.com/apps and create an application.
6 | Your application must have a callback URL registered with Twitter.
7 | It doesn't matter what it is, but it has to be there (Twitter won't
8 | let you use localhost in a registered callback URL, but that won't stop
9 | you from running this demo on localhost).
10 | 2) Create a file called "secrets.cfg" and put your consumer key and
11 | secret (which Twitter gives you when you register an app) in it:
12 | twitter_consumer_key = 'asdf1234'
13 | twitter_consumer_secret = 'qwer5678'
14 | (you could also generate a random value for "cookie_secret" and put it
15 | in the same file, although it's not necessary to run this demo)
16 | 3) Run this program and go to http://localhost:8888 (by default) in your
17 | browser.
18 | """
19 |
20 | import logging
21 |
22 | from tornado.auth import TwitterMixin
23 | from tornado.escape import json_decode, json_encode
24 | from tornado.ioloop import IOLoop
25 | from tornado import gen
26 | from tornado.options import define, options, parse_command_line, parse_config_file
27 | from tornado.web import Application, RequestHandler, authenticated
28 |
29 | define('port', default=8888, help="port to listen on")
30 | define('config_file', default='secrets.cfg',
31 | help='filename for additional configuration')
32 |
33 | define('debug', default=False, group='application',
34 | help="run in debug mode (with automatic reloading)")
35 | # The following settings should probably be defined in secrets.cfg
36 | define('twitter_consumer_key', type=str, group='application')
37 | define('twitter_consumer_secret', type=str, group='application')
38 | define('cookie_secret', type=str, group='application',
39 | default='__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE__',
40 | help="signing key for secure cookies")
41 |
42 | class BaseHandler(RequestHandler):
43 | COOKIE_NAME = 'twitterdemo_user'
44 |
45 | def get_current_user(self):
46 | user_json = self.get_secure_cookie(self.COOKIE_NAME)
47 | if not user_json:
48 | return None
49 | return json_decode(user_json)
50 |
51 | class MainHandler(BaseHandler, TwitterMixin):
52 | @authenticated
53 | @gen.coroutine
54 | def get(self):
55 | timeline = yield self.twitter_request(
56 | '/statuses/home_timeline',
57 | access_token=self.current_user['access_token'])
58 | self.render('home.html', timeline=timeline)
59 |
60 | class LoginHandler(BaseHandler, TwitterMixin):
61 | @gen.coroutine
62 | def get(self):
63 | if self.get_argument('oauth_token', None):
64 | user = yield self.get_authenticated_user()
65 | del user["description"]
66 | self.set_secure_cookie(self.COOKIE_NAME, json_encode(user))
67 | self.redirect(self.get_argument('next', '/'))
68 | else:
69 | yield self.authorize_redirect(callback_uri=self.request.full_url())
70 |
71 | class LogoutHandler(BaseHandler):
72 | def get(self):
73 | self.clear_cookie(self.COOKIE_NAME)
74 |
75 | def main():
76 | parse_command_line(final=False)
77 | parse_config_file(options.config_file)
78 |
79 | app = Application(
80 | [
81 | ('/', MainHandler),
82 | ('/login', LoginHandler),
83 | ('/logout', LogoutHandler),
84 | ],
85 | login_url='/login',
86 | **options.group_dict('application'))
87 | app.listen(options.port)
88 |
89 | logging.info('Listening on http://localhost:%d' % options.port)
90 | IOLoop.current().start()
91 |
92 | if __name__ == '__main__':
93 | main()
94 |
--------------------------------------------------------------------------------
/demos/websocket/chatdemo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright 2009 Facebook
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 | # not use this file except in compliance with the License. You may obtain
7 | # a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | # License for the specific language governing permissions and limitations
15 | # under the License.
16 | """Simplified chat demo for websockets.
17 |
18 | Authentication, error handling, etc are left as an exercise for the reader :)
19 | """
20 |
21 | import logging
22 | import tornado.escape
23 | import tornado.ioloop
24 | import tornado.options
25 | import tornado.web
26 | import tornado.websocket
27 | import os.path
28 | import uuid
29 |
30 | from tornado.options import define, options
31 |
32 | define("port", default=8888, help="run on the given port", type=int)
33 |
34 |
35 | class Application(tornado.web.Application):
36 | def __init__(self):
37 | handlers = [
38 | (r"/", MainHandler),
39 | (r"/chatsocket", ChatSocketHandler),
40 | ]
41 | settings = dict(
42 | cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
43 | template_path=os.path.join(os.path.dirname(__file__), "templates"),
44 | static_path=os.path.join(os.path.dirname(__file__), "static"),
45 | xsrf_cookies=True,
46 | )
47 | tornado.web.Application.__init__(self, handlers, **settings)
48 |
49 |
50 | class MainHandler(tornado.web.RequestHandler):
51 | def get(self):
52 | self.render("index.html", messages=ChatSocketHandler.cache)
53 |
54 | class ChatSocketHandler(tornado.websocket.WebSocketHandler):
55 | waiters = set()
56 | cache = []
57 | cache_size = 200
58 |
59 | def get_compression_options(self):
60 | # Non-None enables compression with default options.
61 | return {}
62 |
63 | def open(self):
64 | ChatSocketHandler.waiters.add(self)
65 |
66 | def on_close(self):
67 | ChatSocketHandler.waiters.remove(self)
68 |
69 | @classmethod
70 | def update_cache(cls, chat):
71 | cls.cache.append(chat)
72 | if len(cls.cache) > cls.cache_size:
73 | cls.cache = cls.cache[-cls.cache_size:]
74 |
75 | @classmethod
76 | def send_updates(cls, chat):
77 | logging.info("sending message to %d waiters", len(cls.waiters))
78 | for waiter in cls.waiters:
79 | try:
80 | waiter.write_message(chat)
81 | except:
82 | logging.error("Error sending message", exc_info=True)
83 |
84 | def on_message(self, message):
85 | logging.info("got message %r", message)
86 | parsed = tornado.escape.json_decode(message)
87 | chat = {
88 | "id": str(uuid.uuid4()),
89 | "body": parsed["body"],
90 | }
91 | chat["html"] = tornado.escape.to_basestring(
92 | self.render_string("message.html", message=chat))
93 |
94 | ChatSocketHandler.update_cache(chat)
95 | ChatSocketHandler.send_updates(chat)
96 |
97 |
98 | def main():
99 | tornado.options.parse_command_line()
100 | app = Application()
101 | app.listen(options.port)
102 | tornado.ioloop.IOLoop.current().start()
103 |
104 |
105 | if __name__ == "__main__":
106 | main()
107 |
--------------------------------------------------------------------------------
/demos/websocket/static/chat.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2009 FriendFeed
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | * not use this file except in compliance with the License. You may obtain
6 | * a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | body {
18 | background: white;
19 | margin: 10px;
20 | }
21 |
22 | body,
23 | input {
24 | font-family: sans-serif;
25 | font-size: 10pt;
26 | color: black;
27 | }
28 |
29 | table {
30 | border-collapse: collapse;
31 | border: 0;
32 | }
33 |
34 | td {
35 | border: 0;
36 | padding: 0;
37 | }
38 |
39 | #body {
40 | position: absolute;
41 | bottom: 10px;
42 | left: 10px;
43 | }
44 |
45 | #input {
46 | margin-top: 0.5em;
47 | }
48 |
49 | #inbox .message {
50 | padding-top: 0.25em;
51 | }
52 |
53 | #nav {
54 | float: right;
55 | z-index: 99;
56 | }
57 |
--------------------------------------------------------------------------------
/demos/websocket/static/chat.js:
--------------------------------------------------------------------------------
1 | // Copyright 2009 FriendFeed
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
4 | // not use this file except in compliance with the License. You may obtain
5 | // a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 | // License for the specific language governing permissions and limitations
13 | // under the License.
14 |
15 | $(document).ready(function() {
16 | if (!window.console) window.console = {};
17 | if (!window.console.log) window.console.log = function() {};
18 |
19 | $("#messageform").live("submit", function() {
20 | newMessage($(this));
21 | return false;
22 | });
23 | $("#messageform").live("keypress", function(e) {
24 | if (e.keyCode == 13) {
25 | newMessage($(this));
26 | return false;
27 | }
28 | });
29 | $("#message").select();
30 | updater.start();
31 | });
32 |
33 | function newMessage(form) {
34 | var message = form.formToDict();
35 | updater.socket.send(JSON.stringify(message));
36 | form.find("input[type=text]").val("").select();
37 | }
38 |
39 | jQuery.fn.formToDict = function() {
40 | var fields = this.serializeArray();
41 | var json = {}
42 | for (var i = 0; i < fields.length; i++) {
43 | json[fields[i].name] = fields[i].value;
44 | }
45 | if (json.next) delete json.next;
46 | return json;
47 | };
48 |
49 | var updater = {
50 | socket: null,
51 |
52 | start: function() {
53 | var url = "ws://" + location.host + "/chatsocket";
54 | updater.socket = new WebSocket(url);
55 | updater.socket.onmessage = function(event) {
56 | updater.showMessage(JSON.parse(event.data));
57 | }
58 | },
59 |
60 | showMessage: function(message) {
61 | var existing = $("#m" + message.id);
62 | if (existing.length > 0) return;
63 | var node = $(message.html);
64 | node.hide();
65 | $("#inbox").append(node);
66 | node.slideDown();
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/demos/websocket/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tornado Chat Demo
6 |
7 |
8 |
9 |
10 |
11 | {% for message in messages %}
12 | {% include "message.html" %}
13 | {% end %}
14 |
15 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/demos/websocket/templates/message.html:
--------------------------------------------------------------------------------
1 | {% module linkify(message["body"]) %}
2 |
--------------------------------------------------------------------------------
/demos/webspider/webspider.py:
--------------------------------------------------------------------------------
1 | import time
2 | from datetime import timedelta
3 |
4 | try:
5 | from HTMLParser import HTMLParser
6 | from urlparse import urljoin, urldefrag
7 | except ImportError:
8 | from html.parser import HTMLParser
9 | from urllib.parse import urljoin, urldefrag
10 |
11 | from tornado import httpclient, gen, ioloop, queues
12 |
13 | base_url = 'http://www.tornadoweb.org/en/stable/'
14 | concurrency = 10
15 |
16 |
17 | @gen.coroutine
18 | def get_links_from_url(url):
19 | """Download the page at `url` and parse it for links.
20 |
21 | Returned links have had the fragment after `#` removed, and have been made
22 | absolute so, e.g. the URL 'gen.html#tornado.gen.coroutine' becomes
23 | 'http://www.tornadoweb.org/en/stable/gen.html'.
24 | """
25 | try:
26 | response = yield httpclient.AsyncHTTPClient().fetch(url)
27 | print('fetched %s' % url)
28 |
29 | html = response.body if isinstance(response.body, str) \
30 | else response.body.decode()
31 | urls = [urljoin(url, remove_fragment(new_url))
32 | for new_url in get_links(html)]
33 | except Exception as e:
34 | print('Exception: %s %s' % (e, url))
35 | raise gen.Return([])
36 |
37 | raise gen.Return(urls)
38 |
39 |
40 | def remove_fragment(url):
41 | pure_url, frag = urldefrag(url)
42 | return pure_url
43 |
44 |
45 | def get_links(html):
46 | class URLSeeker(HTMLParser):
47 | def __init__(self):
48 | HTMLParser.__init__(self)
49 | self.urls = []
50 |
51 | def handle_starttag(self, tag, attrs):
52 | href = dict(attrs).get('href')
53 | if href and tag == 'a':
54 | self.urls.append(href)
55 |
56 | url_seeker = URLSeeker()
57 | url_seeker.feed(html)
58 | return url_seeker.urls
59 |
60 |
61 | @gen.coroutine
62 | def main():
63 | q = queues.Queue()
64 | start = time.time()
65 | fetching, fetched = set(), set()
66 |
67 | @gen.coroutine
68 | def fetch_url():
69 | current_url = yield q.get()
70 | try:
71 | if current_url in fetching:
72 | return
73 |
74 | print('fetching %s' % current_url)
75 | fetching.add(current_url)
76 | urls = yield get_links_from_url(current_url)
77 | fetched.add(current_url)
78 |
79 | for new_url in urls:
80 | # Only follow links beneath the base URL
81 | if new_url.startswith(base_url):
82 | yield q.put(new_url)
83 |
84 | finally:
85 | q.task_done()
86 |
87 | @gen.coroutine
88 | def worker():
89 | while True:
90 | yield fetch_url()
91 |
92 | q.put(base_url)
93 |
94 | # Start workers, then wait for the work queue to be empty.
95 | for _ in range(concurrency):
96 | worker()
97 | yield q.join(timeout=timedelta(seconds=300))
98 | assert fetching == fetched
99 | print('Done in %d seconds, fetched %s URLs.' % (
100 | time.time() - start, len(fetched)))
101 |
102 |
103 | if __name__ == '__main__':
104 | import logging
105 | logging.basicConfig()
106 | io_loop = ioloop.IOLoop.current()
107 | io_loop.run_sync(main)
108 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all
2 | all: sphinx
3 |
4 | # No -W for doctests because that disallows tests with empty output.
5 | SPHINX_DOCTEST_OPTS=-n -d build/doctress .
6 | SPHINXOPTS=-n -W -d build/doctrees .
7 |
8 | .PHONY: sphinx
9 | sphinx:
10 | sphinx-build -b html $(SPHINXOPTS) build/html
11 |
12 | .PHONY: coverage
13 | coverage:
14 | sphinx-build -b coverage ${SPHINXOPTS} build/coverage
15 | cat build/coverage/python.txt
16 |
17 | .PHONY: latex
18 | latex:
19 | sphinx-build -b latex $(SPHINXOPTS) build/latex
20 |
21 | # Building a pdf requires a latex installation. For macports, the needed
22 | # packages are texlive-latex-extra and texlive-fonts-recommended.
23 | # The output is in build/latex/tornado.pdf
24 | .PHONY: pdf
25 | pdf: latex
26 | cd build/latex && pdflatex -interaction=nonstopmode tornado.tex
27 |
28 | .PHONY: doctest
29 | doctest:
30 | sphinx-build -b doctest $(SPHINX_DOCTEST_OPTS) build/doctest
31 |
32 | clean:
33 | rm -rf build
34 |
--------------------------------------------------------------------------------
/docs/asyncio.rst:
--------------------------------------------------------------------------------
1 | ``tornado.platform.asyncio`` --- Bridge between ``asyncio`` and Tornado
2 | =======================================================================
3 |
4 | .. automodule:: tornado.platform.asyncio
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/auth.rst:
--------------------------------------------------------------------------------
1 | ``tornado.auth`` --- Third-party login with OpenID and OAuth
2 | ============================================================
3 |
4 | .. testsetup::
5 |
6 | import tornado.auth, tornado.gen, tornado.web
7 |
8 | .. automodule:: tornado.auth
9 |
10 | Common protocols
11 | ----------------
12 |
13 | These classes implement the OpenID and OAuth standards. They will
14 | generally need to be subclassed to use them with any particular site.
15 | The degree of customization required will vary, but in most cases
16 | overridding the class attributes (which are named beginning with
17 | underscores for historical reasons) should be sufficient.
18 |
19 | .. autoclass:: OpenIdMixin
20 | :members:
21 |
22 | .. autoclass:: OAuthMixin
23 |
24 | .. automethod:: authorize_redirect
25 | .. automethod:: get_authenticated_user
26 | .. automethod:: _oauth_consumer_token
27 | .. automethod:: _oauth_get_user_future
28 | .. automethod:: get_auth_http_client
29 |
30 | .. autoclass:: OAuth2Mixin
31 | :members:
32 |
33 | Google
34 | ------
35 |
36 | .. autoclass:: GoogleOAuth2Mixin
37 | :members:
38 |
39 | Facebook
40 | --------
41 |
42 | .. autoclass:: FacebookGraphMixin
43 | :members:
44 |
45 | Twitter
46 | -------
47 |
48 | .. autoclass:: TwitterMixin
49 | :members:
50 |
--------------------------------------------------------------------------------
/docs/autoreload.rst:
--------------------------------------------------------------------------------
1 | ``tornado.autoreload`` --- Automatically detect code changes in development
2 | ===========================================================================
3 |
4 | .. automodule:: tornado.autoreload
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/caresresolver.rst:
--------------------------------------------------------------------------------
1 | ``tornado.platform.caresresolver`` --- Asynchronous DNS Resolver using C-Ares
2 | =============================================================================
3 |
4 | .. module:: tornado.platform.caresresolver
5 |
6 | This module contains a DNS resolver using the c-ares library (and its
7 | wrapper ``pycares``).
8 |
9 | .. py:class:: CaresResolver
10 |
11 | Name resolver based on the c-ares library.
12 |
13 | This is a non-blocking and non-threaded resolver. It may not produce
14 | the same results as the system resolver, but can be used for non-blocking
15 | resolution when threads cannot be used.
16 |
17 | c-ares fails to resolve some names when ``family`` is ``AF_UNSPEC``,
18 | so it is only recommended for use in ``AF_INET`` (i.e. IPv4). This is
19 | the default for ``tornado.simple_httpclient``, but other libraries
20 | may default to ``AF_UNSPEC``.
21 |
--------------------------------------------------------------------------------
/docs/concurrent.rst:
--------------------------------------------------------------------------------
1 | ``tornado.concurrent`` --- Work with threads and futures
2 | ========================================================
3 |
4 | .. testsetup::
5 |
6 | from tornado.concurrent import *
7 | from tornado import gen
8 |
9 | .. automodule:: tornado.concurrent
10 | :members:
11 | :exclude-members: Future, TracebackFuture
12 |
13 | .. autoclass:: Future
14 |
15 | Consumer methods
16 | ^^^^^^^^^^^^^^^^
17 |
18 | .. automethod:: Future.result
19 | .. automethod:: Future.exception
20 | .. automethod:: Future.exc_info
21 | .. automethod:: Future.add_done_callback
22 | .. automethod:: Future.done
23 | .. automethod:: Future.running
24 | .. automethod:: Future.cancel
25 | .. automethod:: Future.cancelled
26 |
27 | Producer methods
28 | ^^^^^^^^^^^^^^^^
29 |
30 | .. automethod:: Future.set_result
31 | .. automethod:: Future.set_exception
32 | .. automethod:: Future.set_exc_info
33 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # Ensure we get the local copy of tornado instead of what's on the standard path
2 | import os
3 | import sys
4 | import time
5 | sys.path.insert(0, os.path.abspath(".."))
6 | import tornado
7 |
8 | master_doc = "index"
9 |
10 | project = "Tornado"
11 | copyright = "2009-%s, The Tornado Authors" % time.strftime("%Y")
12 |
13 | version = release = tornado.version
14 |
15 | language = 'zh_CN'
16 |
17 | extensions = [
18 | "sphinx.ext.autodoc",
19 | "sphinx.ext.coverage",
20 | "sphinx.ext.doctest",
21 | "sphinx.ext.extlinks",
22 | "sphinx.ext.intersphinx",
23 | "sphinx.ext.viewcode",
24 | ]
25 |
26 | primary_domain = 'py'
27 | default_role = 'py:obj'
28 |
29 | autodoc_member_order = "bysource"
30 | autoclass_content = "both"
31 |
32 | # Without this line sphinx includes a copy of object.__init__'s docstring
33 | # on any class that doesn't define __init__.
34 | # https://bitbucket.org/birkenfeld/sphinx/issue/1337/autoclass_content-both-uses-object__init__
35 | autodoc_docstring_signature = False
36 |
37 | coverage_skip_undoc_in_source = True
38 | coverage_ignore_modules = [
39 | "tornado.platform.asyncio",
40 | "tornado.platform.caresresolver",
41 | "tornado.platform.twisted",
42 | ]
43 | # I wish this could go in a per-module file...
44 | coverage_ignore_classes = [
45 | # tornado.concurrent
46 | "TracebackFuture",
47 |
48 | # tornado.gen
49 | "Runner",
50 |
51 | # tornado.ioloop
52 | "PollIOLoop",
53 |
54 | # tornado.web
55 | "ChunkedTransferEncoding",
56 | "GZipContentEncoding",
57 | "OutputTransform",
58 | "TemplateModule",
59 | "url",
60 |
61 | # tornado.websocket
62 | "WebSocketProtocol",
63 | "WebSocketProtocol13",
64 | "WebSocketProtocol76",
65 | ]
66 |
67 | coverage_ignore_functions = [
68 | # various modules
69 | "doctests",
70 | "main",
71 |
72 | # tornado.escape
73 | # parse_qs_bytes should probably be documented but it's complicated by
74 | # having different implementations between py2 and py3.
75 | "parse_qs_bytes",
76 | ]
77 |
78 | html_favicon = 'favicon.ico'
79 |
80 | latex_documents = [
81 | ('index', 'tornado.tex', 'Tornado Documentation', 'The Tornado Authors', 'manual', False),
82 | ]
83 |
84 | # HACK: sphinx has limited support for substitutions with the |version|
85 | # variable, but there doesn't appear to be any way to use this in a link
86 | # target.
87 | # http://stackoverflow.com/questions/1227037/substitutions-inside-links-in-rest-sphinx
88 | # The extlink extension can be used to do link substitutions, but it requires a
89 | # portion of the url to be literally contained in the document. Therefore,
90 | # this link must be referenced as :current_tarball:`z`
91 | extlinks = {
92 | 'current_tarball': (
93 | 'https://pypi.org/packages/source/t/tornado/tornado-%s.tar.g%%s' % version,
94 | 'tornado-%s.tar.g' % version),
95 | }
96 |
97 | intersphinx_mapping = {
98 | 'python': ('https://docs.python.org/3.4/', None),
99 | }
100 |
101 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
102 |
103 | # On RTD we can't import sphinx_rtd_theme, but it will be applied by
104 | # default anyway. This block will use the same theme when building locally
105 | # as on RTD.
106 | if not on_rtd:
107 | import sphinx_rtd_theme
108 | html_theme = 'sphinx_rtd_theme'
109 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
110 |
--------------------------------------------------------------------------------
/docs/coroutine.rst:
--------------------------------------------------------------------------------
1 | 协程和并发
2 | ==========================
3 |
4 | .. toctree::
5 |
6 | gen
7 | concurrent
8 | locks
9 | queues
10 | process
11 |
--------------------------------------------------------------------------------
/docs/escape.rst:
--------------------------------------------------------------------------------
1 | ``tornado.escape`` --- 转义和字符串操作
2 | =======================================================
3 |
4 | .. automodule:: tornado.escape
5 |
6 | 转义函数
7 | ------------------
8 |
9 | .. autofunction:: xhtml_escape
10 | .. autofunction:: xhtml_unescape
11 |
12 | .. autofunction:: url_escape
13 | .. autofunction:: url_unescape
14 |
15 | .. autofunction:: json_encode
16 | .. autofunction:: json_decode
17 |
18 | Byte/unicode 转换
19 | ------------------------
20 | 这些函数在Tornado自身中被广泛使用, 但不应该被大多数应用程序直接
21 | 需要. 值得注意的是,许多这些功能的复杂性来源于这样一个事实:
22 | Tornado 同时支持Python 2 和Python 3.
23 |
24 | .. autofunction:: utf8
25 | .. autofunction:: to_unicode
26 | .. function:: native_str
27 |
28 | 转换一个byte 或unicode 字符串到 `str` 类型. 等价于
29 | Python 2的 `utf8` 和Python 3的 `to_unicode` .
30 |
31 | .. autofunction:: to_basestring
32 |
33 | .. autofunction:: recursive_unicode
34 |
35 | 其他函数
36 | -----------------------
37 | .. autofunction:: linkify
38 | .. autofunction:: squeeze
39 |
--------------------------------------------------------------------------------
/docs/faq.rst:
--------------------------------------------------------------------------------
1 | 常见问题
2 | ==========================
3 |
4 | .. contents::
5 | :local:
6 |
7 | Why isn't this example with ``time.sleep()`` running in parallel?
8 | -----------------------------------------------------------------
9 |
10 | Many people's first foray into Tornado's concurrency looks something like
11 | this::
12 |
13 | class BadExampleHandler(RequestHandler):
14 | def get(self):
15 | for i in range(5):
16 | print(i)
17 | time.sleep(1)
18 |
19 | Fetch this handler twice at the same time and you'll see that the second
20 | five-second countdown doesn't start until the first one has completely
21 | finished. The reason for this is that `time.sleep` is a **blocking**
22 | function: it doesn't allow control to return to the `.IOLoop` so that other
23 | handlers can be run.
24 |
25 | Of course, `time.sleep` is really just a placeholder in these examples,
26 | the point is to show what happens when something in a handler gets slow.
27 | No matter what the real code is doing, to achieve concurrency blocking
28 | code must be replaced with non-blocking equivalents. This means one of three things:
29 |
30 | 1. *Find a coroutine-friendly equivalent.* For `time.sleep`, use
31 | `tornado.gen.sleep` instead::
32 |
33 | class CoroutineSleepHandler(RequestHandler):
34 | @gen.coroutine
35 | def get(self):
36 | for i in range(5):
37 | print(i)
38 | yield gen.sleep(1)
39 |
40 | When this option is available, it is usually the best approach.
41 | See the `Tornado wiki `_
42 | for links to asynchronous libraries that may be useful.
43 |
44 | 2. *Find a callback-based equivalent.* Similar to the first option,
45 | callback-based libraries are available for many tasks, although they
46 | are slightly more complicated to use than a library designed for
47 | coroutines. These are typically used with `tornado.gen.Task` as an
48 | adapter::
49 |
50 | class CoroutineTimeoutHandler(RequestHandler):
51 | @gen.coroutine
52 | def get(self):
53 | io_loop = IOLoop.current()
54 | for i in range(5):
55 | print(i)
56 | yield gen.Task(io_loop.add_timeout, io_loop.time() + 1)
57 |
58 | Again, the
59 | `Tornado wiki `_
60 | can be useful to find suitable libraries.
61 |
62 | 3. *Run the blocking code on another thread.* When asynchronous libraries
63 | are not available, `concurrent.futures.ThreadPoolExecutor` can be used
64 | to run any blocking code on another thread. This is a universal solution
65 | that can be used for any blocking function whether an asynchronous
66 | counterpart exists or not::
67 |
68 | executor = concurrent.futures.ThreadPoolExecutor(8)
69 |
70 | class ThreadPoolHandler(RequestHandler):
71 | @gen.coroutine
72 | def get(self):
73 | for i in range(5):
74 | print(i)
75 | yield executor.submit(time.sleep, 1)
76 |
77 | See the :doc:`Asynchronous I/O ` chapter of the Tornado
78 | user's guide for more on blocking and asynchronous functions.
79 |
80 |
81 | My code is asynchronous, but it's not running in parallel in two browser tabs.
82 | ------------------------------------------------------------------------------
83 |
84 | Even when a handler is asynchronous and non-blocking, it can be surprisingly
85 | tricky to verify this. Browsers will recognize that you are trying to
86 | load the same page in two different tabs and delay the second request
87 | until the first has finished. To work around this and see that the server
88 | is in fact working in parallel, do one of two things:
89 |
90 | * Add something to your urls to make them unique. Instead of
91 | ``http://localhost:8888`` in both tabs, load
92 | ``http://localhost:8888/?x=1`` in one and
93 | ``http://localhost:8888/?x=2`` in the other.
94 |
95 | * Use two different browsers. For example, Firefox will be able to load
96 | a url even while that same url is being loaded in a Chrome tab.
97 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tao12345666333/tornado-zh/e9e8519beb147d9e1290f6a4fa7d61123d1ecb1c/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/gen.rst:
--------------------------------------------------------------------------------
1 | ``tornado.gen`` --- Simplify asynchronous code
2 | ==============================================
3 |
4 | .. testsetup::
5 |
6 | from tornado.web import *
7 | from tornado import gen
8 |
9 | .. automodule:: tornado.gen
10 |
11 | Decorators
12 | ----------
13 |
14 | .. autofunction:: coroutine
15 |
16 | .. autofunction:: engine
17 |
18 | Utility functions
19 | -----------------
20 |
21 | .. autoexception:: Return
22 |
23 | .. autofunction:: with_timeout
24 | .. autoexception:: TimeoutError
25 |
26 | .. autofunction:: sleep
27 |
28 | .. autodata:: moment
29 | :annotation:
30 |
31 | .. autoclass:: WaitIterator
32 | :members:
33 |
34 | .. autofunction:: multi
35 |
36 | .. autofunction:: multi_future
37 |
38 | .. autofunction:: Task
39 |
40 | .. class:: Arguments
41 |
42 | The result of a `Task` or `Wait` whose callback had more than one
43 | argument (or keyword arguments).
44 |
45 | The `Arguments` object is a `collections.namedtuple` and can be
46 | used either as a tuple ``(args, kwargs)`` or an object with attributes
47 | ``args`` and ``kwargs``.
48 |
49 | .. autofunction:: convert_yielded
50 |
51 | .. autofunction:: maybe_future
52 |
53 | Legacy interface
54 | ----------------
55 |
56 | Before support for `Futures <.Future>` was introduced in Tornado 3.0,
57 | coroutines used subclasses of `YieldPoint` in their ``yield`` expressions.
58 | These classes are still supported but should generally not be used
59 | except for compatibility with older interfaces. None of these classes
60 | are compatible with native (``await``-based) coroutines.
61 |
62 | .. autoclass:: YieldPoint
63 | :members:
64 |
65 | .. autoclass:: Callback
66 |
67 | .. autoclass:: Wait
68 |
69 | .. autoclass:: WaitAll
70 |
71 | .. autoclass:: MultiYieldPoint
72 |
--------------------------------------------------------------------------------
/docs/guide.rst:
--------------------------------------------------------------------------------
1 | 用户指南
2 | ============
3 |
4 | .. toctree::
5 |
6 | guide/intro
7 | guide/async
8 | guide/coroutines
9 | guide/queues
10 | guide/structure
11 | guide/templates
12 | guide/security
13 | guide/running
14 |
--------------------------------------------------------------------------------
/docs/guide/async.rst:
--------------------------------------------------------------------------------
1 | 异步和非阻塞I/O
2 | ---------------------------------
3 |
4 | 实时web功能需要为每个用户提供一个多数时间被闲置的长连接,
5 | 在传统的同步web服务器中,这意味着要为每个用户提供一个线程,
6 | 当然每个线程的开销都是很昂贵的.
7 |
8 | 为了尽量减少并发连接造成的开销,Tornado使用了一种单线程事件循环的方式.
9 | 这就意味着所有的应用代码都应该是异步非阻塞的,
10 | 因为在同一时间只有一个操作是有效的.
11 |
12 | 异步和非阻塞是非常相关的并且这两个术语经常交换使用,但它们不是完全相同的事情.
13 |
14 | 阻塞
15 | ~~~~~~~~
16 |
17 | 一个函数在等待某些事情的返回值的时候会被 **阻塞**. 函数被阻塞的原因有很多:
18 | 网络I/O,磁盘I/O,互斥锁等.事实上 *每个* 函数在运行和使用CPU的时候都或多或少
19 | 会被阻塞(举个极端的例子来说明为什么对待CPU阻塞要和对待一般阻塞一样的严肃:
20 | 比如密码哈希函数
21 | `bcrypt `_, 需要消耗几百毫秒的CPU时间,这已
22 | 经远远超过了一般的网络或者磁盘请求时间了).
23 |
24 | 一个函数可以在某些方面阻塞在另外一些方面不阻塞.例如,
25 | `tornado.httpclient` 在默认的配置下,会在DNS解析上面阻塞,但是在其他网络请
26 | 求的时候不阻塞
27 | (为了减轻这种影响,可以用 `.ThreadedResolver` 或者是
28 | 通过正确配置 ``libcurl`` 用 ``tornado.curl_httpclient`` 来做).
29 | 在Tornado的上下文中,我们一般讨论网络I/O上下文的阻塞,尽管各种阻塞已经被最小
30 | 化.
31 |
32 | 异步
33 | ~~~~~~~~~~~~
34 |
35 | **异步** 函数在会在完成之前返回,在应用中触发下一个动作之前通常会在后
36 | 台执行一些工作(和正常的 **同步** 函数在返回前就执行完所有的事情不同).这里列
37 | 举了几种风格的异步接口:
38 |
39 | * 回调参数
40 | * 返回一个占位符 (`.Future`, ``Promise``, ``Deferred``)
41 | * 传送给一个队列
42 | * 回调注册表 (POSIX信号)
43 |
44 | 不论使用哪种类型的接口, *按照定义* 异步函数与它们的调用者都有着不同的交互方
45 | 式;也没有什么对调用者透明的方式使得同步函数异步(类似 `gevent
46 | `_ 使用轻量级线程的系统性能虽然堪比异步系统,但它们并
47 | 没有真正的让事情异步).
48 |
49 | 例子
50 | ~~~~~~~~
51 |
52 | 一个简单的同步函数:
53 |
54 | .. testcode::
55 |
56 | from tornado.httpclient import HTTPClient
57 |
58 | def synchronous_fetch(url):
59 | http_client = HTTPClient()
60 | response = http_client.fetch(url)
61 | return response.body
62 |
63 | .. testoutput::
64 | :hide:
65 |
66 | 把上面的例子用回调参数重写的异步函数:
67 |
68 | .. testcode::
69 |
70 | from tornado.httpclient import AsyncHTTPClient
71 |
72 | def asynchronous_fetch(url, callback):
73 | http_client = AsyncHTTPClient()
74 | def handle_response(response):
75 | callback(response.body)
76 | http_client.fetch(url, callback=handle_response)
77 |
78 | .. testoutput::
79 | :hide:
80 |
81 | 使用 `.Future` 代替回调:
82 |
83 | .. testcode::
84 |
85 | from tornado.concurrent import Future
86 |
87 | def async_fetch_future(url):
88 | http_client = AsyncHTTPClient()
89 | my_future = Future()
90 | fetch_future = http_client.fetch(url)
91 | fetch_future.add_done_callback(
92 | lambda f: my_future.set_result(f.result()))
93 | return my_future
94 |
95 | .. testoutput::
96 | :hide:
97 |
98 | `.Future` 版本明显更加复杂,但是 ``Futures`` 却是Tornado中推荐的写法
99 | 因为它有两个主要的优势.首先是错误处理更加一致,因为 `.Future.result`
100 | 方法可以简单的抛出异常(相较于常见的回调函数接口特别指定错误处理),
101 | 而且 ``Futures`` 很适合和协程一起使用.协程会在后面深入讨论.这里是上
102 | 面例子的协程版本,和最初的同步版本很像:
103 |
104 | .. testcode::
105 |
106 | from tornado import gen
107 |
108 | @gen.coroutine
109 | def fetch_coroutine(url):
110 | http_client = AsyncHTTPClient()
111 | response = yield http_client.fetch(url)
112 | raise gen.Return(response.body)
113 |
114 | .. testoutput::
115 | :hide:
116 |
117 | ``raise gen.Return(response.body)`` 声明是在Python 2 (and 3.2)下人为
118 | 执行的, 因为在其中生成器不允许返回值.为了克服这个问题,Tornado的协程
119 | 抛出一种特殊的叫 `.Return` 的异常. 协程捕获这个异常并把它作为返回值.
120 | 在Python 3.3和更高版本,使用 ``return
121 | response.body`` 有相同的结果.
122 |
--------------------------------------------------------------------------------
/docs/guide/intro.rst:
--------------------------------------------------------------------------------
1 | 介绍
2 | ------------
3 |
4 | `Tornado `_ 是一个Python web框架和异步网络库
5 | 起初由 `FriendFeed
6 | `_ 开发. 通过使用非阻塞网络I/O, Tornado
7 | 可以支持上万级的连接,处理
8 | `长连接 `_,
9 | `WebSockets `_, 和其他
10 | 需要与每个用户保持长久连接的应用.
11 |
12 | Tornado 大体上可以被分为4个主要的部分:
13 |
14 | * web框架 (包括创建web应用的 `.RequestHandler` 类,还有很多其他支持的类).
15 | * HTTP的客户端和服务端实现 (`.HTTPServer` and
16 | `.AsyncHTTPClient`).
17 | * 异步网络库 (`.IOLoop` and `.IOStream`),
18 | 为HTTP组件提供构建模块,也可以用来实现其他协议.
19 | * 协程库 (`tornado.gen`) 允许异步代码写的更直接而不用链式回调的方式.
20 |
21 | Tornado web 框架和HTTP server 一起为
22 | `WSGI `_ 提供了一个全栈式的选择.
23 | 在WSGI容器 (`.WSGIAdapter`) 中使用Tornado web框架或者使用Tornado HTTP server
24 | 作为一个其他WSGI框架(`.WSGIContainer`)的容器,这样的组合方式都是有局限性的.
25 | 为了充分利用Tornado的特性,你需要一起使用Tornado的web框架和HTTP server.
26 |
--------------------------------------------------------------------------------
/docs/guide/queues.rst:
--------------------------------------------------------------------------------
1 | :class:`~tornado.queues.Queue` 示例 - 一个并发网络爬虫
2 | ================================================================
3 |
4 | .. currentmodule:: tornado.queues
5 |
6 | Tornado的 `tornado.queues` 模块实现了异步生产者/消费者模式的协程, 类似于
7 | 通过Python 标准库的 `queue` 实现线程模式.
8 |
9 | 一个yield `Queue.get` 的协程直到队列中有值的时候才会暂停. 如果队列设置了最大长度
10 | yield `Queue.put` 的协程直到队列中有空间才会暂停.
11 |
12 | 一个 `~Queue` 从0开始对完成的任务进行计数. `~Queue.put` 加计数;
13 | `~Queue.task_done` 减少计数.
14 |
15 | 这里的网络爬虫的例子, 队列开始的时候只包含 base_url. 当一个worker抓取到一个页面
16 | 它会解析链接并把它添加到队列中, 然后调用 `~Queue.task_done` 减少计数一次.
17 | 最后, 当一个worker抓取到的页面URL都是之前抓取到过的并且队列中没有任务了.
18 | 于是worker调用 `~Queue.task_done` 把计数减到0.
19 | 等待 `~Queue.join` 的主协程取消暂停并且完成.
20 |
21 | .. literalinclude:: ../../demos/webspider/webspider.py
22 |
--------------------------------------------------------------------------------
/docs/http.rst:
--------------------------------------------------------------------------------
1 | HTTP servers and clients
2 | ========================
3 |
4 | .. toctree::
5 |
6 | httpserver
7 | httpclient
8 | httputil
9 | http1connection
10 |
--------------------------------------------------------------------------------
/docs/http1connection.rst:
--------------------------------------------------------------------------------
1 | ``tornado.http1connection`` -- HTTP/1.x client/server implementation
2 | ====================================================================
3 |
4 | .. automodule:: tornado.http1connection
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/httpclient.rst:
--------------------------------------------------------------------------------
1 | ``tornado.httpclient`` --- 异步 HTTP 客户端
2 | ===================================================
3 |
4 | .. automodule:: tornado.httpclient
5 |
6 | HTTP 客户端接口
7 | ----------------------
8 |
9 | .. autoclass:: HTTPClient
10 | :members:
11 |
12 | .. autoclass:: AsyncHTTPClient
13 | :members:
14 |
15 | Request 对象
16 | ---------------
17 | .. autoclass:: HTTPRequest
18 | :members:
19 |
20 | Response 对象
21 | ----------------
22 | .. autoclass:: HTTPResponse
23 | :members:
24 |
25 | 异常
26 | ----------
27 | .. autoexception:: HTTPError
28 | :members:
29 |
30 | Command-line 接口
31 | ----------------------
32 |
33 | This module provides a simple command-line interface to fetch a url
34 | using Tornado's HTTP client. Example usage::
35 |
36 | # Fetch the url and print its body
37 | python -m tornado.httpclient http://www.google.com
38 |
39 | # Just print the headers
40 | python -m tornado.httpclient --print_headers --print_body=false http://www.google.com
41 |
42 | Implementations
43 | ~~~~~~~~~~~~~~~
44 |
45 | .. automodule:: tornado.simple_httpclient
46 | :members:
47 |
48 | .. module:: tornado.curl_httpclient
49 |
50 | .. class:: CurlAsyncHTTPClient(io_loop, max_clients=10, defaults=None)
51 |
52 | ``libcurl``-based HTTP client.
53 |
--------------------------------------------------------------------------------
/docs/httpserver.rst:
--------------------------------------------------------------------------------
1 | ``tornado.httpserver`` --- 非阻塞 HTTP server
2 | ===================================================
3 |
4 | .. automodule:: tornado.httpserver
5 |
6 | HTTP Server
7 | -----------
8 | .. autoclass:: HTTPServer
9 | :members:
10 |
--------------------------------------------------------------------------------
/docs/httputil.rst:
--------------------------------------------------------------------------------
1 | ``tornado.httputil`` --- Manipulate HTTP headers and URLs
2 | =========================================================
3 |
4 | .. testsetup::
5 |
6 | from tornado.httputil import *
7 |
8 | .. automodule:: tornado.httputil
9 | :members:
10 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. title:: Tornado Web Server
2 |
3 | |Tornado Web Server|
4 | ======================
5 |
6 | .. |Tornado Web Server| image:: tornado.png
7 | :alt: Tornado Web Server
8 |
9 | `Tornado `_ 是一个Python web框架和异步网络库,起初由 `FriendFeed
10 | `_ 开发. 通过使用非阻塞网络I/O, Tornado可以支撑上万级的连接,处理 `长连接 `_,
11 | `WebSockets `_ ,和其他需要与每个用户保持长久连接的应用.
12 |
13 | 相关链接
14 | -----------
15 |
16 | * `下载当前4.3版本 `_
17 | * `源码 (github) `_
18 | * 邮件列表: `discussion `_ and `announcements `_
19 | * `Stack Overflow `_
20 | * `Wiki `_
21 |
22 | Hello, world
23 | ------------
24 |
25 | 这是一个简单的Tornado的web应用::
26 |
27 | import tornado.ioloop
28 | import tornado.web
29 |
30 | class MainHandler(tornado.web.RequestHandler):
31 | def get(self):
32 | self.write("Hello, world")
33 |
34 | def make_app():
35 | return tornado.web.Application([
36 | (r"/", MainHandler),
37 | ])
38 |
39 | if __name__ == "__main__":
40 | app = make_app()
41 | app.listen(8888)
42 | tornado.ioloop.IOLoop.current().start()
43 |
44 | 这个例子没有使用Tornado的任何异步特性;了解详情请看 `simple chat room
45 | `_.
46 |
47 | 安装
48 | -----
49 |
50 | **自动安装**::
51 |
52 | pip install tornado
53 |
54 | Tornado在 `PyPI `_ 列表中,可以使用 ``pip`` 或 ``easy_install`` 安装. 注意源码发布中包含的示例应用可能不会出现在这种方式安装的代码中,所以你也可能希望通过下载一份源码包的拷贝来进行安装.
55 |
56 | **手动安装**: 下载当前4.3版本:
57 |
58 | .. parsed-literal::
59 |
60 | tar xvzf tornado-4.3.tar.gz
61 | cd tornado-4.3
62 | python setup.py build
63 | sudo python setup.py install
64 |
65 | Tornado的源码托管在 `hosted on GitHub
66 | `_.
67 |
68 | **Prerequisites**: Tornado 4.3 运行在Python 2.6, 2.7, 和 3.2+
69 | (对Python 2.6 和 3.2的支持是不推荐的并将在下个版本中移除). 对Python 2的2.7.9或更新版 *强烈*
70 | 推荐提高对SSL支持. 另外Tornado的依赖包可能通过 ``pip`` or ``setup.py install`` 被自动安装,
71 | 下面这些可选包可能是有用的:
72 |
73 | * `unittest2 `_ 是用来在Python 2.6上运行Tornado的测试用例的(更高版本的Python是不需要的)
74 | * `concurrent.futures `_ 是推荐配合Tornado使用的线程池并且可以支持 `tornado.netutil.ThreadedResolver` 的用法. 它只在Python 2中被需要,Python 3已经包括了这个标准库.
75 | * `pycurl `_ 是在
76 | ``tornado.curl_httpclient`` 中可选使用的.需要Libcurl 7.19.3.1 或更高版本;推荐使用7.21.1或更高版本.
77 | * `Twisted `_ 会在
78 | `tornado.platform.twisted` 中使用.
79 | * `pycares `_ 是一个当线程不适用情况下的非阻塞DNS解决方案.
80 | * `Monotime `_ 添加对monotonic clock的支持,当环境中的时钟被频繁调整的时候,改善其可靠性. 在Python 3.3中不再需要.
81 |
82 | **平台**: Tornado可以运行在任何类Unix平台上,虽然为了最好的性能和可扩展性
83 | 只有Linux(使用 ``epoll``)和BSD(使用 ``kqueue``)是推荐的产品部署环境(尽管Mac OS X通过BSD发展来并且支持kqueue,但它的网络质量很差,所以它只适合开发使用)
84 | Tornado也可以运行在Windows上,虽然它的配置不是官方支持的,同时也仅仅推荐开发使用.
85 |
86 | 文档
87 | -------------
88 |
89 | 这个文档同时也提供 `PDF 和 Epub 格式
90 | `_.
91 |
92 | .. toctree::
93 | :titlesonly:
94 |
95 | guide
96 | webframework
97 | http
98 | networking
99 | coroutine
100 | integration
101 | utilities
102 | faq
103 | releases
104 |
105 | * :ref:`genindex`
106 | * :ref:`modindex`
107 | * :ref:`search`
108 |
109 | 讨论和支持
110 | ----------------------
111 |
112 | 你可以讨论Tornado在 `Tornado 开发者邮件列表
113 | `_, 报告bug在 `GitHub issue tracker
114 | `_.
115 |
116 | 其他资源可以在 `Tornado wiki
117 | `_ 上找到. 新版本会宣布在 `announcements mailing list
118 | `_.
119 |
120 | Tornado is available under
121 | the `Apache License, Version 2.0
122 | `_.
123 |
124 | This web site and all documentation is licensed under `Creative
125 | Commons 3.0 `_.
126 |
--------------------------------------------------------------------------------
/docs/integration.rst:
--------------------------------------------------------------------------------
1 | 与其他服务集成
2 | ===============================
3 |
4 | .. toctree::
5 |
6 | auth
7 | wsgi
8 | asyncio
9 | caresresolver
10 | twisted
11 |
--------------------------------------------------------------------------------
/docs/ioloop.rst:
--------------------------------------------------------------------------------
1 | ``tornado.ioloop`` --- Main event loop
2 | ======================================
3 |
4 | .. automodule:: tornado.ioloop
5 |
6 | IOLoop objects
7 | --------------
8 |
9 | .. autoclass:: IOLoop
10 |
11 | Running an IOLoop
12 | ^^^^^^^^^^^^^^^^^
13 |
14 | .. automethod:: IOLoop.current
15 | .. automethod:: IOLoop.make_current
16 | .. automethod:: IOLoop.instance
17 | .. automethod:: IOLoop.initialized
18 | .. automethod:: IOLoop.install
19 | .. automethod:: IOLoop.clear_instance
20 | .. automethod:: IOLoop.start
21 | .. automethod:: IOLoop.stop
22 | .. automethod:: IOLoop.run_sync
23 | .. automethod:: IOLoop.close
24 |
25 | I/O events
26 | ^^^^^^^^^^
27 |
28 | .. automethod:: IOLoop.add_handler
29 | .. automethod:: IOLoop.update_handler
30 | .. automethod:: IOLoop.remove_handler
31 |
32 | Callbacks and timeouts
33 | ^^^^^^^^^^^^^^^^^^^^^^
34 |
35 | .. automethod:: IOLoop.add_callback
36 | .. automethod:: IOLoop.add_callback_from_signal
37 | .. automethod:: IOLoop.add_future
38 | .. automethod:: IOLoop.add_timeout
39 | .. automethod:: IOLoop.call_at
40 | .. automethod:: IOLoop.call_later
41 | .. automethod:: IOLoop.remove_timeout
42 | .. automethod:: IOLoop.spawn_callback
43 | .. automethod:: IOLoop.time
44 | .. autoclass:: PeriodicCallback
45 | :members:
46 |
47 | Debugging and error handling
48 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
49 |
50 | .. automethod:: IOLoop.handle_callback_exception
51 | .. automethod:: IOLoop.set_blocking_signal_threshold
52 | .. automethod:: IOLoop.set_blocking_log_threshold
53 | .. automethod:: IOLoop.log_stack
54 |
55 | Methods for subclasses
56 | ^^^^^^^^^^^^^^^^^^^^^^
57 |
58 | .. automethod:: IOLoop.initialize
59 | .. automethod:: IOLoop.close_fd
60 | .. automethod:: IOLoop.split_fd
61 |
--------------------------------------------------------------------------------
/docs/iostream.rst:
--------------------------------------------------------------------------------
1 | ``tornado.iostream`` --- Convenient wrappers for non-blocking sockets
2 | =====================================================================
3 |
4 | .. automodule:: tornado.iostream
5 |
6 | Base class
7 | ----------
8 |
9 | .. autoclass:: BaseIOStream
10 |
11 | Main interface
12 | ^^^^^^^^^^^^^^
13 |
14 | .. automethod:: BaseIOStream.write
15 | .. automethod:: BaseIOStream.read_bytes
16 | .. automethod:: BaseIOStream.read_until
17 | .. automethod:: BaseIOStream.read_until_regex
18 | .. automethod:: BaseIOStream.read_until_close
19 | .. automethod:: BaseIOStream.close
20 | .. automethod:: BaseIOStream.set_close_callback
21 | .. automethod:: BaseIOStream.closed
22 | .. automethod:: BaseIOStream.reading
23 | .. automethod:: BaseIOStream.writing
24 | .. automethod:: BaseIOStream.set_nodelay
25 |
26 | Methods for subclasses
27 | ^^^^^^^^^^^^^^^^^^^^^^
28 |
29 | .. automethod:: BaseIOStream.fileno
30 | .. automethod:: BaseIOStream.close_fd
31 | .. automethod:: BaseIOStream.write_to_fd
32 | .. automethod:: BaseIOStream.read_from_fd
33 | .. automethod:: BaseIOStream.get_fd_error
34 |
35 | Implementations
36 | ---------------
37 |
38 | .. autoclass:: IOStream
39 | :members:
40 |
41 | .. autoclass:: SSLIOStream
42 | :members:
43 |
44 | .. autoclass:: PipeIOStream
45 | :members:
46 |
47 | Exceptions
48 | ----------
49 |
50 | .. autoexception:: StreamBufferFullError
51 | .. autoexception:: StreamClosedError
52 | .. autoexception:: UnsatisfiableReadError
53 |
--------------------------------------------------------------------------------
/docs/locale.rst:
--------------------------------------------------------------------------------
1 | ``tornado.locale`` --- 国际化支持
2 | ===================================================
3 |
4 | .. automodule:: tornado.locale
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/locks.rst:
--------------------------------------------------------------------------------
1 | ``tornado.locks`` -- 同步原语
2 | ===============================================
3 |
4 | .. versionadded:: 4.2
5 |
6 | 使用和标准库提供给线程相似的同步原语协调协程.
7 |
8 | *(请注意, 这些原语不是线程安全的, 不能被用来代替标准库中的--它
9 | 们是为了协调在单线程app中的Tornado协程, 而不是为了在一个多线程
10 | app中保护共享对象.)*
11 |
12 | .. automodule:: tornado.locks
13 |
14 | Condition
15 | ---------
16 | .. autoclass:: Condition
17 | :members:
18 |
19 | Event
20 | -----
21 | .. autoclass:: Event
22 | :members:
23 |
24 | Semaphore
25 | ---------
26 | .. autoclass:: Semaphore
27 | :members:
28 |
29 | BoundedSemaphore
30 | ----------------
31 | .. autoclass:: BoundedSemaphore
32 | :members:
33 | :inherited-members:
34 |
35 | Lock
36 | ----
37 | .. autoclass:: Lock
38 | :members:
39 | :inherited-members:
40 |
--------------------------------------------------------------------------------
/docs/log.rst:
--------------------------------------------------------------------------------
1 | ``tornado.log`` --- Logging support
2 | ===================================
3 |
4 | .. automodule:: tornado.log
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/netutil.rst:
--------------------------------------------------------------------------------
1 | ``tornado.netutil`` --- Miscellaneous network utilities
2 | =======================================================
3 |
4 | .. automodule:: tornado.netutil
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/networking.rst:
--------------------------------------------------------------------------------
1 | 异步网络
2 | =======================
3 |
4 | .. toctree::
5 |
6 | ioloop
7 | iostream
8 | netutil
9 | tcpclient
10 | tcpserver
11 |
--------------------------------------------------------------------------------
/docs/options.rst:
--------------------------------------------------------------------------------
1 | ``tornado.options`` --- Command-line parsing
2 | ============================================
3 |
4 | .. automodule:: tornado.options
5 |
6 |
7 |
8 | Global functions
9 | ----------------
10 |
11 | .. autofunction:: define
12 |
13 | .. py:data:: options
14 |
15 | Global options object. All defined options are available as attributes
16 | on this object.
17 |
18 | .. autofunction:: parse_command_line
19 | .. autofunction:: parse_config_file
20 | .. autofunction:: print_help(file=sys.stderr)
21 | .. autofunction:: add_parse_callback
22 | .. autoexception:: Error
23 |
24 | OptionParser class
25 | ------------------
26 |
27 | .. autoclass:: OptionParser
28 | :members:
29 |
--------------------------------------------------------------------------------
/docs/process.rst:
--------------------------------------------------------------------------------
1 | ``tornado.process`` --- Utilities for multiple processes
2 | ========================================================
3 |
4 | .. automodule:: tornado.process
5 | :members:
6 |
7 | .. exception:: CalledProcessError
8 |
9 | An alias for `subprocess.CalledProcessError`.
10 |
--------------------------------------------------------------------------------
/docs/queues.rst:
--------------------------------------------------------------------------------
1 | ``tornado.queues`` -- 协程的队列
2 | ===========================================
3 |
4 | .. versionadded:: 4.2
5 |
6 | .. automodule:: tornado.queues
7 |
8 | Classes
9 | -------
10 |
11 | Queue
12 | ^^^^^
13 | .. autoclass:: Queue
14 | :members:
15 |
16 | PriorityQueue
17 | ^^^^^^^^^^^^^
18 | .. autoclass:: PriorityQueue
19 | :members:
20 |
21 | LifoQueue
22 | ^^^^^^^^^
23 | .. autoclass:: LifoQueue
24 | :members:
25 |
26 | Exceptions
27 | ----------
28 |
29 | QueueEmpty
30 | ^^^^^^^^^^
31 | .. autoexception:: QueueEmpty
32 |
33 | QueueFull
34 | ^^^^^^^^^
35 | .. autoexception:: QueueFull
36 |
--------------------------------------------------------------------------------
/docs/releases.rst:
--------------------------------------------------------------------------------
1 | 版本记录
2 | =============
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 | releases/v4.3.0
8 | releases/v4.2.1
9 | releases/v4.2.0
10 | releases/v4.1.0
11 | releases/v4.0.2
12 | releases/v4.0.1
13 | releases/v4.0.0
14 | releases/v3.2.2
15 | releases/v3.2.1
16 | releases/v3.2.0
17 | releases/v3.1.1
18 | releases/v3.1.0
19 | releases/v3.0.2
20 | releases/v3.0.1
21 | releases/v3.0.0
22 | releases/v2.4.1
23 | releases/v2.4.0
24 | releases/v2.3.0
25 | releases/v2.2.1
26 | releases/v2.2.0
27 | releases/v2.1.1
28 | releases/v2.1.0
29 | releases/v2.0.0
30 | releases/v1.2.1
31 | releases/v1.2.0
32 | releases/v1.1.1
33 | releases/v1.1.0
34 | releases/v1.0.1
35 | releases/v1.0.0
36 |
--------------------------------------------------------------------------------
/docs/releases/v1.0.0.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 1.0
2 | =========================
3 |
4 | July 22, 2010
5 | -------------
6 |
7 | ::
8 |
9 | We are pleased to announce the release of Tornado 1.0, available
10 | from
11 | https://github.com/downloads/facebook/tornado/tornado-1.0.tar.gz.
12 | There have been many changes since version 0.2; here are some of
13 | the highlights:
14 |
15 | New features:
16 | * Improved support for running other WSGI applications in a
17 | Tornado server (tested with Django and CherryPy)
18 | * Improved performance on Mac OS X and BSD (kqueue-based IOLoop),
19 | and experimental support for win32
20 | * Rewritten AsyncHTTPClient available as
21 | tornado.httpclient.AsyncHTTPClient2 (this will become the
22 | default in a future release)
23 | * Support for standard .mo files in addition to .csv in the locale
24 | module
25 | * Pre-forking support for running multiple Tornado processes at
26 | once (see HTTPServer.start())
27 | * SSL and gzip support in HTTPServer
28 | * reverse_url() function refers to urls from the Application
29 | config by name from templates and RequestHandlers
30 | * RequestHandler.on_connection_close() callback is called when the
31 | client has closed the connection (subject to limitations of the
32 | underlying network stack, any proxies, etc)
33 | * Static files can now be served somewhere other than /static/ via
34 | the static_url_prefix application setting
35 | * URL regexes can now use named groups ("(?P)") to pass
36 | arguments to get()/post() via keyword instead of position
37 | * HTTP header dictionary-like objects now support multiple values
38 | for the same header via the get_all() and add() methods.
39 | * Several new options in the httpclient module, including
40 | prepare_curl_callback and header_callback
41 | * Improved logging configuration in tornado.options.
42 | * UIModule.html_body() can be used to return html to be inserted
43 | at the end of the document body.
44 |
45 | Backwards-incompatible changes:
46 | * RequestHandler.get_error_html() now receives the exception
47 | object as a keyword argument if the error was caused by an
48 | uncaught exception.
49 | * Secure cookies are now more secure, but incompatible with
50 | cookies set by Tornado 0.2. To read cookies set by older
51 | versions of Tornado, pass include_name=False to
52 | RequestHandler.get_secure_cookie()
53 | * Parameters passed to RequestHandler.get/post() by extraction
54 | from the path now have %-escapes decoded, for consistency with
55 | the processing that was already done with other query
56 | parameters.
57 |
58 | Many thanks to everyone who contributed patches, bug reports, and
59 | feedback that went into this release!
60 |
61 | -Ben
62 |
--------------------------------------------------------------------------------
/docs/releases/v1.0.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 1.0.1
2 | ===========================
3 |
4 | Aug 13, 2010
5 | ------------
6 |
7 | ::
8 |
9 | This release fixes a bug with RequestHandler.get_secure_cookie, which would
10 | in some circumstances allow an attacker to tamper with data stored in the
11 | cookie.
12 |
--------------------------------------------------------------------------------
/docs/releases/v1.1.0.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 1.1
2 | =========================
3 |
4 | Sep 7, 2010
5 | -----------
6 |
7 | ::
8 |
9 | We are pleased to announce the release of Tornado 1.1, available from
10 | https://github.com/downloads/facebook/tornado/tornado-1.1.tar.gz
11 |
12 | Changes in this release:
13 | * RequestHandler.async_callback and related functions in other classes
14 | are no longer needed in most cases (although it's harmless to continue
15 | using them). Uncaught exceptions will now cause the request to be closed
16 | even in a callback. If you're curious how this works, see the new
17 | tornado.stack_context module.
18 | * The new tornado.testing module contains support for unit testing
19 | asynchronous IOLoop-based code.
20 | * AsyncHTTPClient has been rewritten (the new implementation was
21 | available as AsyncHTTPClient2 in Tornado 1.0; both names are
22 | supported for backwards compatibility).
23 | * The tornado.auth module has had a number of updates, including support
24 | for OAuth 2.0 and the Facebook Graph API, and upgrading Twitter and
25 | Google support to OAuth 1.0a.
26 | * The websocket module is back and supports the latest version (76) of the
27 | websocket protocol. Note that this module's interface is different
28 | from the websocket module that appeared in pre-1.0 versions of Tornado.
29 | * New method RequestHandler.initialize() can be overridden in subclasses
30 | to simplify handling arguments from URLSpecs. The sequence of methods
31 | called during initialization is documented at
32 | http://tornadoweb.org/documentation#overriding-requesthandler-methods
33 | * get_argument() and related methods now work on PUT requests in addition
34 | to POST.
35 | * The httpclient module now supports HTTP proxies.
36 | * When HTTPServer is run in SSL mode, the SSL handshake is now non-blocking.
37 | * Many smaller bug fixes and documentation updates
38 |
39 | Backwards-compatibility notes:
40 | * While most users of Tornado should not have to deal with the stack_context
41 | module directly, users of worker thread pools and similar constructs may
42 | need to use stack_context.wrap and/or NullContext to avoid memory leaks.
43 | * The new AsyncHTTPClient still works with libcurl version 7.16.x, but it
44 | performs better when both libcurl and pycurl are at least version 7.18.2.
45 | * OAuth transactions started under previous versions of the auth module
46 | cannot be completed under the new module. This applies only to the
47 | initial authorization process; once an authorized token is issued that
48 | token works with either version.
49 |
50 | Many thanks to everyone who contributed patches, bug reports, and feedback
51 | that went into this release!
52 |
53 | -Ben
54 |
--------------------------------------------------------------------------------
/docs/releases/v1.1.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 1.1.1
2 | ===========================
3 |
4 | Feb 8, 2011
5 | -----------
6 |
7 | ::
8 |
9 | Tornado 1.1.1 is a BACKWARDS-INCOMPATIBLE security update that fixes an
10 | XSRF vulnerability. It is available at
11 | https://github.com/downloads/facebook/tornado/tornado-1.1.1.tar.gz
12 |
13 | This is a backwards-incompatible change. Applications that previously
14 | relied on a blanket exception for XMLHTTPRequest may need to be modified
15 | to explicitly include the XSRF token when making ajax requests.
16 |
17 | The tornado chat demo application demonstrates one way of adding this
18 | token (specifically the function postJSON in demos/chat/static/chat.js).
19 |
20 | More information about this change and its justification can be found at
21 | http://www.djangoproject.com/weblog/2011/feb/08/security/
22 | http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails
23 |
--------------------------------------------------------------------------------
/docs/releases/v1.2.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 1.2.1
2 | ===========================
3 |
4 | Mar 3, 2011
5 | -----------
6 |
7 | ::
8 |
9 | We are pleased to announce the release of Tornado 1.2.1, available from
10 | https://github.com/downloads/facebook/tornado/tornado-1.2.1.tar.gz
11 |
12 | This release contains only two small changes relative to version 1.2:
13 | * FacebookGraphMixin has been updated to work with a recent change to the
14 | Facebook API.
15 | * Running "setup.py install" will no longer attempt to automatically
16 | install pycurl. This wasn't working well on platforms where the best way
17 | to install pycurl is via something like apt-get instead of easy_install.
18 |
19 | This is an important upgrade if you are using FacebookGraphMixin, but
20 | otherwise it can be safely ignored.
21 |
--------------------------------------------------------------------------------
/docs/releases/v2.0.0.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 2.0
2 | =========================
3 |
4 | Jun 21, 2011
5 | ------------
6 |
7 | ::
8 |
9 | Major changes:
10 | * Template output is automatically escaped by default; see backwards
11 | compatibility note below.
12 | * The default AsyncHTTPClient implementation is now simple_httpclient.
13 | * Python 3.2 is now supported.
14 |
15 | Backwards compatibility:
16 | * Template autoescaping is enabled by default. Applications upgrading from
17 | a previous release of Tornado must either disable autoescaping or adapt
18 | their templates to work with it. For most applications, the simplest
19 | way to do this is to pass autoescape=None to the Application constructor.
20 | Note that this affects certain built-in methods, e.g. xsrf_form_html
21 | and linkify, which must now be called with {% raw %} instead of {}
22 | * Applications that wish to continue using curl_httpclient instead of
23 | simple_httpclient may do so by calling
24 | AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
25 | at the beginning of the process. Users of Python 2.5 will probably want
26 | to use curl_httpclient as simple_httpclient only supports ssl on Python 2.6+.
27 | * Python 3 compatibility involved many changes throughout the codebase,
28 | so users are encouraged to test their applications more thoroughly than
29 | usual when upgrading to this release.
30 |
31 | Other changes in this release:
32 | * Templates support several new directives:
33 | - {% autoescape ...%} to control escaping behavior
34 | - {% raw ... %} for unescaped output
35 | - {% module ... %} for calling UIModules
36 | * {% module Template(path, **kwargs) %} may now be used to call another
37 | template with an independent namespace
38 | * All IOStream callbacks are now run directly on the IOLoop via add_callback.
39 | * HTTPServer now supports IPv6 where available. To disable, pass
40 | family=socket.AF_INET to HTTPServer.bind().
41 | * HTTPClient now supports IPv6, configurable via allow_ipv6=bool on the
42 | HTTPRequest. allow_ipv6 defaults to false on simple_httpclient and true
43 | on curl_httpclient.
44 | * RequestHandlers can use an encoding other than utf-8 for query parameters
45 | by overriding decode_argument()
46 | * Performance improvements, especially for applications that use a lot of
47 | IOLoop timeouts
48 | * HTTP OPTIONS method no longer requires an XSRF token.
49 | * JSON output (RequestHandler.write(dict)) now sets Content-Type to
50 | application/json
51 | * Etag computation can now be customized or disabled by overriding
52 | RequestHandler.compute_etag
53 | * USE_SIMPLE_HTTPCLIENT environment variable is no longer supported.
54 | Use AsyncHTTPClient.configure instead.
55 |
--------------------------------------------------------------------------------
/docs/releases/v2.1.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 2.1.1
2 | ===========================
3 |
4 | Oct 4, 2011
5 | -----------
6 |
7 | Bug fixes
8 | ~~~~~~~~~
9 |
10 | * Fixed handling of closed connections with the ``epoll`` (i.e. Linux)
11 | ``IOLoop``. Previously, closed connections could be shut down too early,
12 | which most often manifested as "Stream is closed" exceptions in
13 | ``SimpleAsyncHTTPClient``.
14 | * Fixed a case in which chunked responses could be closed prematurely,
15 | leading to truncated output.
16 | * ``IOStream.connect`` now reports errors more consistently via logging
17 | and the close callback (this affects e.g. connections to localhost
18 | on FreeBSD).
19 | * ``IOStream.read_bytes`` again accepts both ``int`` and ``long`` arguments.
20 | * ``PeriodicCallback`` no longer runs repeatedly when ``IOLoop`` iterations
21 | complete faster than the resolution of ``time.time()`` (mainly a problem
22 | on Windows).
23 |
24 | Backwards-compatibility note
25 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26 |
27 | * Listening for ``IOLoop.ERROR`` alone is no longer sufficient for detecting
28 | closed connections on an otherwise unused socket. ``IOLoop.ERROR`` must
29 | always be used in combination with ``READ`` or ``WRITE``.
30 |
--------------------------------------------------------------------------------
/docs/releases/v2.2.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 2.2.1
2 | ===========================
3 |
4 | Apr 23, 2012
5 | ------------
6 |
7 | Security fixes
8 | ~~~~~~~~~~~~~~
9 |
10 | * `tornado.web.RequestHandler.set_header` now properly sanitizes input
11 | values to protect against header injection, response splitting, etc.
12 | (it has always attempted to do this, but the check was incorrect).
13 | Note that redirects, the most likely source of such bugs, are protected
14 | by a separate check in `.RequestHandler.redirect`.
15 |
16 | Bug fixes
17 | ~~~~~~~~~
18 |
19 | * Colored logging configuration in `tornado.options` is compatible with
20 | Python 3.2.3 (and 3.3).
21 |
--------------------------------------------------------------------------------
/docs/releases/v2.4.0.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 2.4
2 | =========================
3 |
4 | Sep 4, 2012
5 | -----------
6 |
7 | General
8 | ~~~~~~~
9 |
10 | * Fixed Python 3 bugs in `tornado.auth`, `tornado.locale`, and `tornado.wsgi`.
11 |
12 | HTTP clients
13 | ~~~~~~~~~~~~
14 |
15 | * Removed ``max_simultaneous_connections`` argument from `tornado.httpclient`
16 | (both implementations). This argument hasn't been useful for some time
17 | (if you were using it you probably want ``max_clients`` instead)
18 | * ``tornado.simple_httpclient`` now accepts and ignores HTTP 1xx status
19 | responses.
20 |
21 | `tornado.ioloop` and `tornado.iostream`
22 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 |
24 | * Fixed a bug introduced in 2.3 that would cause `.IOStream` close callbacks
25 | to not run if there were pending reads.
26 | * Improved error handling in `.SSLIOStream` and SSL-enabled `.TCPServer`.
27 | * ``SSLIOStream.get_ssl_certificate`` now has a ``binary_form`` argument
28 | which is passed to ``SSLSocket.getpeercert``.
29 | * ``SSLIOStream.write`` can now be called while the connection is in progress,
30 | same as non-SSL `.IOStream` (but be careful not to send sensitive data until
31 | the connection has completed and the certificate has been verified).
32 | * `.IOLoop.add_handler` cannot be called more than once with the same file
33 | descriptor. This was always true for ``epoll``, but now the other
34 | implementations enforce it too.
35 | * On Windows, `.TCPServer` uses ``SO_EXCLUSIVEADDRUSER`` instead of ``SO_REUSEADDR``.
36 |
37 | `tornado.template`
38 | ~~~~~~~~~~~~~~~~~~
39 |
40 | * ``{% break %}`` and ``{% continue %}`` can now be used looping constructs
41 | in templates.
42 | * It is no longer an error for an if/else/for/etc block in a template to
43 | have an empty body.
44 |
45 | `tornado.testing`
46 | ~~~~~~~~~~~~~~~~~
47 |
48 | * New class `tornado.testing.AsyncHTTPSTestCase` is like `.AsyncHTTPTestCase`.
49 | but enables SSL for the testing server (by default using a self-signed
50 | testing certificate).
51 | * `tornado.testing.main` now accepts additional keyword arguments and forwards
52 | them to `unittest.main`.
53 |
54 | `tornado.web`
55 | ~~~~~~~~~~~~~
56 |
57 | * New method `.RequestHandler.get_template_namespace` can be overridden to
58 | add additional variables without modifying keyword arguments to
59 | ``render_string``.
60 | * `.RequestHandler.add_header` now works with `.WSGIApplication`.
61 | * `.RequestHandler.get_secure_cookie` now handles a potential error case.
62 | * ``RequestHandler.__init__`` now calls ``super().__init__`` to ensure that
63 | all constructors are called when multiple inheritance is used.
64 | * Docs have been updated with a description of all available
65 | :py:attr:`Application settings `
66 |
67 | Other modules
68 | ~~~~~~~~~~~~~
69 |
70 | * `.OAuthMixin` now accepts ``"oob"`` as a ``callback_uri``.
71 | * `.OpenIdMixin` now also returns the ``claimed_id`` field for the user.
72 | * `tornado.platform.twisted` shutdown sequence is now more compatible.
73 | * The logging configuration used in `tornado.options` is now more tolerant
74 | of non-ascii byte strings.
75 |
--------------------------------------------------------------------------------
/docs/releases/v2.4.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 2.4.1
2 | ===========================
3 |
4 | Nov 24, 2012
5 | ------------
6 |
7 | Bug fixes
8 | ~~~~~~~~~
9 |
10 | * Fixed a memory leak in `tornado.stack_context` that was especially likely
11 | with long-running ``@gen.engine`` functions.
12 | * `tornado.auth.TwitterMixin` now works on Python 3.
13 | * Fixed a bug in which ``IOStream.read_until_close`` with a streaming callback
14 | would sometimes pass the last chunk of data to the final callback instead
15 | of the streaming callback.
16 |
--------------------------------------------------------------------------------
/docs/releases/v3.0.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 3.0.1
2 | ===========================
3 |
4 | Apr 8, 2013
5 | -----------
6 |
7 | * The interface of `tornado.auth.FacebookGraphMixin` is now consistent
8 | with its documentation and the rest of the module. The
9 | ``get_authenticated_user`` and ``facebook_request`` methods return a
10 | ``Future`` and the ``callback`` argument is optional.
11 | * The `tornado.testing.gen_test` decorator will no longer be recognized
12 | as a (broken) test by ``nose``.
13 | * Work around a bug in Ubuntu 13.04 betas involving an incomplete backport
14 | of the `ssl.match_hostname` function.
15 | * `tornado.websocket.websocket_connect` now fails cleanly when it attempts
16 | to connect to a non-websocket url.
17 | * `tornado.testing.LogTrapTestCase` once again works with byte strings
18 | on Python 2.
19 | * The ``request`` attribute of `tornado.httpclient.HTTPResponse` is
20 | now always an `~tornado.httpclient.HTTPRequest`, never a ``_RequestProxy``.
21 | * Exceptions raised by the `tornado.gen` module now have better messages
22 | when tuples are used as callback keys.
23 |
--------------------------------------------------------------------------------
/docs/releases/v3.0.2.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 3.0.2
2 | ===========================
3 |
4 | Jun 2, 2013
5 | -----------
6 |
7 | * `tornado.auth.TwitterMixin` now defaults to version 1.1 of the
8 | Twitter API, instead of version 1.0 which is being `discontinued on
9 | June 11 `_. It also now uses HTTPS
10 | when talking to Twitter.
11 | * Fixed a potential memory leak with a long chain of `.gen.coroutine`
12 | or `.gen.engine` functions.
13 |
--------------------------------------------------------------------------------
/docs/releases/v3.1.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 3.1.1
2 | ===========================
3 |
4 | Sep 1, 2013
5 | -----------
6 |
7 | * `.StaticFileHandler` no longer fails if the client requests a ``Range`` that
8 | is larger than the entire file (Facebook has a crawler that does this).
9 | * `.RequestHandler.on_connection_close` now works correctly on subsequent
10 | requests of a keep-alive connection.
11 |
--------------------------------------------------------------------------------
/docs/releases/v3.2.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 3.2.1
2 | ===========================
3 |
4 | May 5, 2014
5 | -----------
6 |
7 | Security fixes
8 | ~~~~~~~~~~~~~~
9 |
10 | * The signed-value format used by `.RequestHandler.set_secure_cookie`
11 | and `.RequestHandler.get_secure_cookie` has changed to be more secure.
12 | **This is a disruptive change**. The ``secure_cookie`` functions
13 | take new ``version`` parameters to support transitions between cookie
14 | formats.
15 | * The new cookie format fixes a vulnerability that may be present in
16 | applications that use multiple cookies where the name of one cookie
17 | is a prefix of the name of another.
18 | * To minimize disruption, cookies in the older format will be accepted
19 | by default until they expire. Applications that may be vulnerable
20 | can reject all cookies in the older format by passing ``min_version=2``
21 | to `.RequestHandler.get_secure_cookie`.
22 | * Thanks to Joost Pol of `Certified Secure `_
23 | for reporting this issue.
24 |
25 | Backwards-compatibility notes
26 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27 |
28 | * Signed cookies issued by `.RequestHandler.set_secure_cookie` in Tornado
29 | 3.2.1 cannot be read by older releases. If you need to run 3.2.1
30 | in parallel with older releases, you can pass ``version=1`` to
31 | `.RequestHandler.set_secure_cookie` to issue cookies that are
32 | backwards-compatible (but have a known weakness, so this option
33 | should only be used for a transitional period).
34 |
35 | Other changes
36 | ~~~~~~~~~~~~~
37 |
38 | * The C extension used to speed up the websocket module now compiles
39 | correctly on Windows with MSVC and 64-bit mode. The fallback to
40 | the pure-Python alternative now works correctly on Mac OS X machines
41 | with no C compiler installed.
42 |
--------------------------------------------------------------------------------
/docs/releases/v3.2.2.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 3.2.2
2 | ===========================
3 |
4 | June 3, 2014
5 | ------------
6 |
7 | Security fixes
8 | ~~~~~~~~~~~~~~
9 |
10 | * The XSRF token is now encoded with a random mask on each request.
11 | This makes it safe to include in compressed pages without being
12 | vulnerable to the `BREACH attack `_.
13 | This applies to most applications that use both the ``xsrf_cookies``
14 | and ``gzip`` options (or have gzip applied by a proxy).
15 |
16 | Backwards-compatibility notes
17 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18 |
19 | * If Tornado 3.2.2 is run at the same time as older versions on the same
20 | domain, there is some potential for issues with the differing cookie
21 | versions. The `.Application` setting ``xsrf_cookie_version=1`` can
22 | be used for a transitional period to generate the older cookie format
23 | on newer servers.
24 |
25 | Other changes
26 | ~~~~~~~~~~~~~
27 |
28 | * ``tornado.platform.asyncio`` is now compatible with ``trollius`` version 0.3.
29 |
--------------------------------------------------------------------------------
/docs/releases/v4.0.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 4.0.1
2 | ===========================
3 |
4 | Aug 12, 2014
5 | ------------
6 |
7 | * The build will now fall back to pure-python mode if the C extension
8 | fails to build for any reason (previously it would fall back for some
9 | errors but not others).
10 | * `.IOLoop.call_at` and `.IOLoop.call_later` now always return
11 | a timeout handle for use with `.IOLoop.remove_timeout`.
12 | * If any callback of a `.PeriodicCallback` or `.IOStream` returns a
13 | `.Future`, any error raised in that future will now be logged
14 | (similar to the behavior of `.IOLoop.add_callback`).
15 | * Fixed an exception in client-side websocket connections when the
16 | connection is closed.
17 | * ``simple_httpclient`` once again correctly handles 204 status
18 | codes with no content-length header.
19 | * Fixed a regression in ``simple_httpclient`` that would result in
20 | timeouts for certain kinds of errors.
21 |
--------------------------------------------------------------------------------
/docs/releases/v4.0.2.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 4.0.2
2 | ===========================
3 |
4 | Sept 10, 2014
5 | -------------
6 |
7 | Bug fixes
8 | ~~~~~~~~~
9 |
10 | * Fixed a bug that could sometimes cause a timeout to fire after being
11 | cancelled.
12 | * `.AsyncTestCase` once again passes along arguments to test methods,
13 | making it compatible with extensions such as Nose's test generators.
14 | * `.StaticFileHandler` can again compress its responses when gzip is enabled.
15 | * ``simple_httpclient`` passes its ``max_buffer_size`` argument to the
16 | underlying stream.
17 | * Fixed a reference cycle that can lead to increased memory consumption.
18 | * `.add_accept_handler` will now limit the number of times it will call
19 | `~socket.socket.accept` per `.IOLoop` iteration, addressing a potential
20 | starvation issue.
21 | * Improved error handling in `.IOStream.connect` (primarily for FreeBSD
22 | systems)
23 |
--------------------------------------------------------------------------------
/docs/releases/v4.2.1.rst:
--------------------------------------------------------------------------------
1 | What's new in Tornado 4.2.1
2 | ===========================
3 |
4 | Jul 17, 2015
5 | ------------
6 |
7 | Security fix
8 | ~~~~~~~~~~~~
9 |
10 | * This release fixes a path traversal vulnerability in `.StaticFileHandler`,
11 | in which files whose names *started with* the ``static_path`` directory
12 | but were not actually *in* that directory could be accessed.
13 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | Twisted
2 |
--------------------------------------------------------------------------------
/docs/stack_context.rst:
--------------------------------------------------------------------------------
1 | ``tornado.stack_context`` --- Exception handling across asynchronous callbacks
2 | ==============================================================================
3 |
4 | .. automodule:: tornado.stack_context
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/tcpclient.rst:
--------------------------------------------------------------------------------
1 | ``tornado.tcpclient`` --- `.IOStream` connection factory
2 | ========================================================
3 |
4 | .. automodule:: tornado.tcpclient
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/tcpserver.rst:
--------------------------------------------------------------------------------
1 | ``tornado.tcpserver`` --- 基于 `.IOStream` 的基础 TCP 服务
2 | ================================================================
3 |
4 | .. automodule:: tornado.tcpserver
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/template.rst:
--------------------------------------------------------------------------------
1 | ``tornado.template`` --- 产生灵活的输出
2 | ===================================================
3 |
4 | .. automodule:: tornado.template
5 |
6 | Class reference
7 | ---------------
8 |
9 | .. autoclass:: Template(template_string, name="", loader=None, compress_whitespace=None, autoescape="xhtml_escape", whitespace=None)
10 | :members:
11 |
12 | .. autoclass:: BaseLoader
13 | :members:
14 |
15 | .. autoclass:: Loader
16 | :members:
17 |
18 | .. autoclass:: DictLoader
19 | :members:
20 |
21 | .. autoexception:: ParseError
22 |
23 | .. autofunction:: filter_whitespace
24 |
--------------------------------------------------------------------------------
/docs/testing.rst:
--------------------------------------------------------------------------------
1 | ``tornado.testing`` --- Unit testing support for asynchronous code
2 | ==================================================================
3 |
4 | .. automodule:: tornado.testing
5 |
6 | Asynchronous test cases
7 | -----------------------
8 |
9 | .. autoclass:: AsyncTestCase
10 | :members:
11 |
12 | .. autoclass:: AsyncHTTPTestCase
13 | :members:
14 |
15 | .. autoclass:: AsyncHTTPSTestCase
16 | :members:
17 |
18 | .. autofunction:: gen_test
19 |
20 | Controlling log output
21 | ----------------------
22 |
23 | .. autoclass:: ExpectLog
24 | :members:
25 |
26 | .. autoclass:: LogTrapTestCase
27 | :members:
28 |
29 | Test runner
30 | -----------
31 |
32 | .. autofunction:: main
33 |
34 | Helper functions
35 | ----------------
36 |
37 | .. autofunction:: bind_unused_port
38 |
39 | .. autofunction:: get_unused_port
40 |
41 | .. autofunction:: get_async_test_timeout
42 |
--------------------------------------------------------------------------------
/docs/tornado.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tao12345666333/tornado-zh/e9e8519beb147d9e1290f6a4fa7d61123d1ecb1c/docs/tornado.png
--------------------------------------------------------------------------------
/docs/twisted.rst:
--------------------------------------------------------------------------------
1 | ``tornado.platform.twisted`` --- Bridges between Twisted and Tornado
2 | ========================================================================
3 |
4 | .. automodule:: tornado.platform.twisted
5 |
6 | Twisted on Tornado
7 | ------------------
8 |
9 | .. autoclass:: TornadoReactor
10 | :members:
11 |
12 | .. autofunction:: install
13 |
14 | Tornado on Twisted
15 | ------------------
16 |
17 | .. autoclass:: TwistedIOLoop
18 | :members:
19 |
20 | Twisted DNS resolver
21 | --------------------
22 |
23 | .. autoclass:: TwistedResolver
24 | :members:
25 |
--------------------------------------------------------------------------------
/docs/util.rst:
--------------------------------------------------------------------------------
1 | ``tornado.util`` --- General-purpose utilities
2 | ==============================================
3 |
4 | .. testsetup::
5 |
6 | from tornado.util import *
7 |
8 | .. automodule:: tornado.util
9 | :members:
10 |
--------------------------------------------------------------------------------
/docs/utilities.rst:
--------------------------------------------------------------------------------
1 | 通用工具
2 | =========
3 |
4 | .. toctree::
5 |
6 | autoreload
7 | log
8 | options
9 | stack_context
10 | testing
11 | util
12 |
--------------------------------------------------------------------------------
/docs/webframework.rst:
--------------------------------------------------------------------------------
1 | web框架
2 | =============
3 |
4 | .. toctree::
5 |
6 | web
7 | template
8 | escape
9 | locale
10 | websocket
11 |
--------------------------------------------------------------------------------
/docs/websocket.rst:
--------------------------------------------------------------------------------
1 | ``tornado.websocket`` --- 浏览器与服务器双向通信
2 | ====================================================================
3 |
4 | .. testsetup::
5 |
6 | import tornado.websocket
7 |
8 | .. automodule:: tornado.websocket
9 |
10 | .. autoclass:: WebSocketHandler
11 |
12 | Event handlers
13 | --------------
14 |
15 | .. automethod:: WebSocketHandler.open
16 | .. automethod:: WebSocketHandler.on_message
17 | .. automethod:: WebSocketHandler.on_close
18 | .. automethod:: WebSocketHandler.select_subprotocol
19 |
20 | Output
21 | ------
22 |
23 | .. automethod:: WebSocketHandler.write_message
24 | .. automethod:: WebSocketHandler.close
25 |
26 | Configuration
27 | -------------
28 |
29 | .. automethod:: WebSocketHandler.check_origin
30 | .. automethod:: WebSocketHandler.get_compression_options
31 | .. automethod:: WebSocketHandler.set_nodelay
32 |
33 | Other
34 | -----
35 |
36 | .. automethod:: WebSocketHandler.ping
37 | .. automethod:: WebSocketHandler.on_pong
38 | .. autoexception:: WebSocketClosedError
39 |
40 |
41 | Client-side support
42 | -------------------
43 |
44 | .. autofunction:: websocket_connect
45 | .. autoclass:: WebSocketClientConnection
46 | :members:
47 |
--------------------------------------------------------------------------------
/docs/wsgi.rst:
--------------------------------------------------------------------------------
1 | ``tornado.wsgi`` --- Interoperability with other Python frameworks and servers
2 | ==============================================================================
3 |
4 | .. automodule:: tornado.wsgi
5 |
6 | Running Tornado apps on WSGI servers
7 | ------------------------------------
8 |
9 | .. autoclass:: WSGIAdapter
10 | :members:
11 |
12 | .. autoclass:: WSGIApplication
13 | :members:
14 |
15 | Running WSGI apps on Tornado servers
16 | ------------------------------------
17 |
18 | .. autoclass:: WSGIContainer
19 | :members:
20 |
--------------------------------------------------------------------------------
/maint/README:
--------------------------------------------------------------------------------
1 | This directory contains tools and scripts that are used in the development
2 | and maintainance of Tornado itself, but are probably not of interest to
3 | Tornado users.
4 |
--------------------------------------------------------------------------------
/maint/requirements.txt:
--------------------------------------------------------------------------------
1 | # Frozen pip requirements for tools used in the development of tornado.
2 | # This list is for python 3.5; for 2.7 add:
3 | # - backports.ssl-match-hostname
4 | # - futures
5 | # - mock
6 | #
7 | # Use virtualenv instead of venv; tox seems to get confused otherwise.
8 |
9 | # Tornado's required dependencies
10 | certifi==2015.9.6.2
11 |
12 | # Tornado's optional dependencies
13 | Twisted==15.4.0
14 | pycares==1.0.0
15 | pycurl==7.19.5.1
16 |
17 | # Other useful tools
18 | Sphinx==1.3.1
19 | autopep8==1.2.1
20 | coverage==4.0
21 | flake8==2.4.1
22 | pep8==1.6.2
23 | pyflakes==1.0.0
24 | sphinx-rtd-theme==0.1.9
25 | tox==2.1.1
26 | twine==1.6.2
27 | virtualenv==13.1.2
28 |
29 | # Indirect dependencies
30 | alabaster==0.7.6
31 | Babel==2.1.1
32 | docutils==0.12
33 | Jinja2==2.8
34 | MarkupSafe==0.23
35 | mccabe==0.3.1
36 | pkginfo==1.2.1
37 | pluggy==0.3.1
38 | py==1.4.30
39 | Pygments==2.0.2
40 | pytz==2015.6
41 | requests==2.7.0
42 | requests-toolbelt==0.4.0
43 | six==1.9.0
44 | snowballstemmer==1.2.0
45 | wheel==0.24.0
46 | zope.interface==4.1.2
47 |
--------------------------------------------------------------------------------
/maint/scripts/custom_fixers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tao12345666333/tornado-zh/e9e8519beb147d9e1290f6a4fa7d61123d1ecb1c/maint/scripts/custom_fixers/__init__.py
--------------------------------------------------------------------------------
/maint/scripts/custom_fixers/fix_future_imports.py:
--------------------------------------------------------------------------------
1 | """Updates all source files to import the same set of __future__ directives.
2 | """
3 | from lib2to3 import fixer_base
4 | from lib2to3 import pytree
5 | from lib2to3.pgen2 import token
6 | from lib2to3.fixer_util import FromImport, Name, Comma, Newline
7 |
8 | # copied from fix_tuple_params.py
9 | def is_docstring(stmt):
10 | return isinstance(stmt, pytree.Node) and \
11 | stmt.children[0].type == token.STRING
12 |
13 | class FixFutureImports(fixer_base.BaseFix):
14 | BM_compatible = True
15 |
16 | PATTERN = """import_from< 'from' module_name="__future__" 'import' any >"""
17 |
18 | def start_tree(self, tree, filename):
19 | self.found_future_import = False
20 |
21 | def new_future_import(self, old):
22 | new = FromImport("__future__",
23 | [Name("absolute_import", prefix=" "), Comma(),
24 | Name("division", prefix=" "), Comma(),
25 | Name("print_function", prefix=" "), Comma(),
26 | Name("with_statement", prefix=" ")])
27 | if old is not None:
28 | new.prefix = old.prefix
29 | return new
30 |
31 | def transform(self, node, results):
32 | self.found_future_import = True
33 | return self.new_future_import(node)
34 |
35 | def finish_tree(self, tree, filename):
36 | if self.found_future_import:
37 | return
38 | if not isinstance(tree, pytree.Node):
39 | # Empty files (usually __init__.py) show up as a single Leaf
40 | # instead of a Node, so leave them alone
41 | return
42 | first_stmt = tree.children[0]
43 | if is_docstring(first_stmt):
44 | # Skip a line and add the import after the docstring
45 | tree.insert_child(1, Newline())
46 | pos = 2
47 | elif first_stmt.prefix:
48 | # No docstring, but an initial comment (perhaps a #! line).
49 | # Transfer the initial comment to a new blank line.
50 | newline = Newline()
51 | newline.prefix = first_stmt.prefix
52 | first_stmt.prefix = ""
53 | tree.insert_child(0, newline)
54 | pos = 1
55 | else:
56 | # No comments or docstring, just insert at the start
57 | pos = 0
58 | tree.insert_child(pos, self.new_future_import(None))
59 | tree.insert_child(pos+1, Newline()) # terminates the import stmt
60 |
--------------------------------------------------------------------------------
/maint/scripts/custom_fixers/fix_unicode_literal.py:
--------------------------------------------------------------------------------
1 | import re
2 | from lib2to3.pgen2 import token
3 | from lib2to3 import fixer_base
4 | from lib2to3.fixer_util import Name, Call
5 |
6 | _literal_re = re.compile(r"[uU][rR]?[\'\"]")
7 |
8 |
9 | class FixUnicodeLiteral(fixer_base.BaseFix):
10 | BM_compatible = True
11 | PATTERN = """STRING"""
12 |
13 | def transform(self, node, results):
14 | if node.type == token.STRING and _literal_re.match(node.value):
15 | new = node.clone()
16 | new.value = new.value[1:]
17 | new.prefix = ''
18 | node.replace(Call(Name(u'u', prefix=node.prefix), [new]))
19 |
--------------------------------------------------------------------------------
/maint/scripts/run_autopep8.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Runs autopep8 in the configuration used for tornado.
4 | #
5 | # W602 is "deprecated form of raising exception", but the fix is incorrect
6 | # (and I'm not sure if the three-argument form of raise is really deprecated
7 | # in the first place)
8 | # E501 is "line longer than 80 chars" but the automated fix is ugly.
9 | # E301 adds a blank line between docstring and first method
10 | # E309 adds a blank line between class declaration and docstring (?)
11 | autopep8 --ignore=W602,E501,E301,E309 -i tornado/*.py tornado/platform/*.py tornado/test/*.py
12 |
--------------------------------------------------------------------------------
/maint/scripts/run_fixers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Usage is like 2to3:
3 | # $ maint/scripts/run_fixers.py -wn --no-diffs tornado
4 |
5 | import sys
6 | from lib2to3.main import main
7 |
8 | sys.exit(main("custom_fixers"))
9 |
--------------------------------------------------------------------------------
/maint/scripts/test_resolvers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from __future__ import print_function
3 |
4 | import pprint
5 | import socket
6 |
7 | from tornado import gen
8 | from tornado.ioloop import IOLoop
9 | from tornado.netutil import Resolver, ThreadedResolver
10 | from tornado.options import parse_command_line, define, options
11 |
12 | try:
13 | import twisted
14 | except ImportError:
15 | twisted = None
16 |
17 | try:
18 | import pycares
19 | except ImportError:
20 | pycares = None
21 |
22 | define('family', default='unspec',
23 | help='Address family to query: unspec, inet, or inet6')
24 |
25 | @gen.coroutine
26 | def main():
27 | args = parse_command_line()
28 |
29 | if not args:
30 | args = ['localhost', 'www.google.com',
31 | 'www.facebook.com', 'www.dropbox.com']
32 |
33 | resolvers = [Resolver(), ThreadedResolver()]
34 |
35 | if twisted is not None:
36 | from tornado.platform.twisted import TwistedResolver
37 | resolvers.append(TwistedResolver())
38 |
39 | if pycares is not None:
40 | from tornado.platform.caresresolver import CaresResolver
41 | resolvers.append(CaresResolver())
42 |
43 | family = {
44 | 'unspec': socket.AF_UNSPEC,
45 | 'inet': socket.AF_INET,
46 | 'inet6': socket.AF_INET6,
47 | }[options.family]
48 |
49 | for host in args:
50 | print('Resolving %s' % host)
51 | for resolver in resolvers:
52 | addrinfo = yield resolver.resolve(host, 80, family)
53 | print('%s: %s' % (resolver.__class__.__name__,
54 | pprint.pformat(addrinfo)))
55 | print()
56 |
57 | if __name__ == '__main__':
58 | IOLoop.instance().run_sync(main)
59 |
--------------------------------------------------------------------------------
/maint/test/README:
--------------------------------------------------------------------------------
1 | This directory contains additional tests that are not included in the main
2 | suite (because e.g. they have extra dependencies, run slowly, or produce
3 | more output than a simple pass/fail)
4 |
--------------------------------------------------------------------------------
/maint/test/appengine/README:
--------------------------------------------------------------------------------
1 | Unit test support for app engine. Currently very limited as most of
2 | our tests depend on direct network access, but these tests ensure that the
3 | modules that are supposed to work on app engine don't depend on any
4 | forbidden modules.
5 |
6 | The code lives in maint/appengine/common, but should be run from the py25
7 | or py27 subdirectories (which contain an app.yaml and a bunch of symlinks).
8 | runtests.py is the entry point; cgi_runtests.py is used internally.
9 |
--------------------------------------------------------------------------------
/maint/test/appengine/common/cgi_runtests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import sys
3 | import unittest
4 |
5 | # Most of our tests depend on IOLoop, which is not usable on app engine.
6 | # Run the tests that work, and check that everything else is at least
7 | # importable (via tornado.test.import_test)
8 | TEST_MODULES = [
9 | 'tornado.httputil.doctests',
10 | 'tornado.iostream.doctests',
11 | 'tornado.util.doctests',
12 | #'tornado.test.auth_test',
13 | #'tornado.test.concurrent_test',
14 | #'tornado.test.curl_httpclient_test',
15 | 'tornado.test.escape_test',
16 | #'tornado.test.gen_test',
17 | #'tornado.test.httpclient_test',
18 | #'tornado.test.httpserver_test',
19 | 'tornado.test.httputil_test',
20 | 'tornado.test.import_test',
21 | #'tornado.test.ioloop_test',
22 | #'tornado.test.iostream_test',
23 | 'tornado.test.locale_test',
24 | #'tornado.test.netutil_test',
25 | #'tornado.test.log_test',
26 | 'tornado.test.options_test',
27 | #'tornado.test.process_test',
28 | #'tornado.test.simple_httpclient_test',
29 | #'tornado.test.stack_context_test',
30 | 'tornado.test.template_test',
31 | #'tornado.test.testing_test',
32 | #'tornado.test.twisted_test',
33 | 'tornado.test.util_test',
34 | #'tornado.test.web_test',
35 | #'tornado.test.websocket_test',
36 | #'tornado.test.wsgi_test',
37 | ]
38 |
39 | def all():
40 | return unittest.defaultTestLoader.loadTestsFromNames(TEST_MODULES)
41 |
42 | def main():
43 | print "Content-Type: text/plain\r\n\r\n",
44 |
45 | try:
46 | unittest.main(defaultTest='all', argv=sys.argv[:1])
47 | except SystemExit, e:
48 | if e.code == 0:
49 | print "PASS"
50 | else:
51 | raise
52 |
53 | if __name__ == '__main__':
54 | main()
55 |
--------------------------------------------------------------------------------
/maint/test/appengine/common/runtests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from __future__ import with_statement
3 |
4 | import contextlib
5 | import errno
6 | import os
7 | import random
8 | import signal
9 | import socket
10 | import subprocess
11 | import sys
12 | import time
13 | import urllib2
14 |
15 | if __name__ == "__main__":
16 | tornado_root = os.path.abspath(os.path.join(os.path.dirname(__file__),
17 | '../../..'))
18 | # dev_appserver doesn't seem to set SO_REUSEADDR
19 | port = random.randrange(10000, 11000)
20 | # does dev_appserver.py ever live anywhere but /usr/local/bin?
21 | proc = subprocess.Popen([sys.executable,
22 | "/usr/local/bin/dev_appserver.py",
23 | os.path.dirname(os.path.abspath(__file__)),
24 | "--port=%d" % port,
25 | "--skip_sdk_update_check",
26 | ],
27 | cwd=tornado_root)
28 |
29 | try:
30 | for i in xrange(50):
31 | with contextlib.closing(socket.socket()) as sock:
32 | err = sock.connect_ex(('localhost', port))
33 | if err == 0:
34 | break
35 | elif err != errno.ECONNREFUSED:
36 | raise Exception("Got unexpected socket error %d" % err)
37 | time.sleep(0.1)
38 | else:
39 | raise Exception("Server didn't start listening")
40 |
41 | resp = urllib2.urlopen("http://localhost:%d/" % port)
42 | print resp.read()
43 | finally:
44 | # dev_appserver sometimes ignores SIGTERM (especially on 2.5),
45 | # so try a few times to kill it.
46 | for sig in [signal.SIGTERM, signal.SIGTERM, signal.SIGKILL]:
47 | os.kill(proc.pid, sig)
48 | res = os.waitpid(proc.pid, os.WNOHANG)
49 | if res != (0,0):
50 | break
51 | time.sleep(0.1)
52 | else:
53 | os.waitpid(proc.pid, 0)
54 |
--------------------------------------------------------------------------------
/maint/test/appengine/py27/app.yaml:
--------------------------------------------------------------------------------
1 | application: tornado-tests-appengine27
2 | version: 1
3 | runtime: python27
4 | threadsafe: false
5 | api_version: 1
6 |
7 | handlers:
8 | - url: /
9 | script: cgi_runtests.py
--------------------------------------------------------------------------------
/maint/test/appengine/py27/cgi_runtests.py:
--------------------------------------------------------------------------------
1 | ../common/cgi_runtests.py
--------------------------------------------------------------------------------
/maint/test/appengine/py27/runtests.py:
--------------------------------------------------------------------------------
1 | ../common/runtests.py
--------------------------------------------------------------------------------
/maint/test/appengine/py27/tornado:
--------------------------------------------------------------------------------
1 | ../../../../tornado
--------------------------------------------------------------------------------
/maint/test/appengine/setup.py:
--------------------------------------------------------------------------------
1 | # Dummy setup file to make tox happy. In the appengine world things aren't
2 | # installed through setup.py
3 | import distutils.core
4 | distutils.core.setup()
5 |
--------------------------------------------------------------------------------
/maint/test/appengine/tox.ini:
--------------------------------------------------------------------------------
1 | # App Engine tests require the SDK to be installed separately.
2 | # Version 1.6.1 or newer is required (older versions don't work when
3 | # python is run from a virtualenv)
4 | #
5 | # These are currently excluded from the main tox.ini because their
6 | # logs are spammy and they're a little flaky.
7 | [tox]
8 | envlist = py27-appengine
9 |
10 | [testenv]
11 | changedir = {toxworkdir}
12 |
13 | [testenv:py27-appengine]
14 | basepython = python2.7
15 | commands = python {toxinidir}/py27/runtests.py {posargs:}
16 |
--------------------------------------------------------------------------------
/maint/test/cython/.gitignore:
--------------------------------------------------------------------------------
1 | .eggs
2 | cythonapp.egg-info
3 | dist
4 |
--------------------------------------------------------------------------------
/maint/test/cython/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include cythonapp.pyx
2 |
--------------------------------------------------------------------------------
/maint/test/cython/cythonapp.pyx:
--------------------------------------------------------------------------------
1 | import cython
2 | from tornado import gen
3 | import pythonmodule
4 |
5 | async def native_coroutine():
6 | x = await pythonmodule.hello()
7 | if x != "hello":
8 | raise ValueError("expected hello, got %r" % x)
9 | return "goodbye"
10 |
11 | @gen.coroutine
12 | def decorated_coroutine():
13 | x = yield pythonmodule.hello()
14 | if x != "hello":
15 | raise ValueError("expected hello, got %r" % x)
16 | return "goodbye"
17 |
18 | # The binding directive is necessary for compatibility with
19 | # ArgReplacer (and therefore return_future), but only because
20 | # this is a static function.
21 | @cython.binding(True)
22 | def function_with_args(one, two, three):
23 | return (one, two, three)
24 |
25 |
26 | class AClass:
27 | # methods don't need the binding directive.
28 | def method_with_args(one, two, three):
29 | return (one, two, three)
30 |
--------------------------------------------------------------------------------
/maint/test/cython/cythonapp_test.py:
--------------------------------------------------------------------------------
1 | from tornado.testing import AsyncTestCase, gen_test
2 | from tornado.util import ArgReplacer
3 | import unittest
4 |
5 | import cythonapp
6 |
7 |
8 | class CythonCoroutineTest(AsyncTestCase):
9 | @gen_test
10 | def test_native_coroutine(self):
11 | x = yield cythonapp.native_coroutine()
12 | self.assertEqual(x, "goodbye")
13 |
14 | @gen_test
15 | def test_decorated_coroutine(self):
16 | x = yield cythonapp.decorated_coroutine()
17 | self.assertEqual(x, "goodbye")
18 |
19 |
20 | class CythonArgReplacerTest(unittest.TestCase):
21 | def test_arg_replacer_function(self):
22 | replacer = ArgReplacer(cythonapp.function_with_args, 'two')
23 | args = (1, 'old', 3)
24 | kwargs = {}
25 | self.assertEqual(replacer.get_old_value(args, kwargs), 'old')
26 | self.assertEqual(replacer.replace('new', args, kwargs),
27 | ('old', [1, 'new', 3], {}))
28 |
29 | def test_arg_replacer_method(self):
30 | replacer = ArgReplacer(cythonapp.AClass().method_with_args, 'two')
31 | args = (1, 'old', 3)
32 | kwargs = {}
33 | self.assertEqual(replacer.get_old_value(args, kwargs), 'old')
34 | self.assertEqual(replacer.replace('new', args, kwargs),
35 | ('old', [1, 'new', 3], {}))
36 |
--------------------------------------------------------------------------------
/maint/test/cython/pythonmodule.py:
--------------------------------------------------------------------------------
1 | from tornado import gen
2 |
3 | @gen.coroutine
4 | def hello():
5 | yield gen.sleep(0.001)
6 | raise gen.Return("hello")
7 |
--------------------------------------------------------------------------------
/maint/test/cython/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | try:
4 | import Cython.Build
5 | except:
6 | Cython = None
7 |
8 | if Cython is None:
9 | ext_modules = None
10 | else:
11 | ext_modules=Cython.Build.cythonize('cythonapp.pyx')
12 |
13 | setup(
14 | name='cythonapp',
15 | py_modules=['cythonapp_test', 'pythonmodule'],
16 | ext_modules=ext_modules,
17 | setup_requires='Cython>=0.23.1',
18 | )
19 |
--------------------------------------------------------------------------------
/maint/test/cython/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | # This currently segfaults on pypy.
3 | envlist = py27,py32,py33,py34,py35
4 |
5 | [testenv]
6 | deps =
7 | ../../..
8 | Cython>=0.23.3
9 | backports_abc>=0.4
10 | singledispatch
11 | commands = python -m unittest cythonapp_test
12 | # Most of these are defaults, but if you specify any you can't fall back
13 | # defaults for the others.
14 | basepython =
15 | py27: python2.7
16 | py32: python3.2
17 | py33: python3.3
18 | py34: python3.4
19 | py35: python3.5
20 |
--------------------------------------------------------------------------------
/maint/test/pyuv/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py27
3 | setupdir = ../../..
4 |
5 | [testenv]
6 | commands =
7 | python -m tornado.test.runtests --ioloop=tornaduv.UVLoop {posargs:}
8 | # twisted tests don't work on pyuv IOLoop currently.
9 | deps =
10 | pyuv
11 | tornaduv
12 | futures
13 | mock
14 |
--------------------------------------------------------------------------------
/maint/test/redbot/README:
--------------------------------------------------------------------------------
1 | Redbot is an HTTP validator that checks for common problems, especially
2 | related to cacheability. These tests ensure that Tornado's default behavior
3 | is correct (but note that this guarantee does not automatically extend
4 | to applications built on Tornado since application behavior can impact
5 | cacheability.
6 |
7 | http://redbot.org/about
--------------------------------------------------------------------------------
/maint/test/redbot/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py27
3 | setupdir=../../..
4 |
5 | [testenv]
6 | commands = python red_test.py
7 | deps =
8 | # Newer versions of thor have a bug with redbot (5/18/13)
9 | thor==0.2.0
10 | git+https://github.com/mnot/redbot.git
11 |
--------------------------------------------------------------------------------
/maint/test/websocket/.gitignore:
--------------------------------------------------------------------------------
1 | reports/
2 |
--------------------------------------------------------------------------------
/maint/test/websocket/client.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import logging
4 |
5 | from tornado import gen
6 | from tornado.ioloop import IOLoop
7 | from tornado.options import define, options, parse_command_line
8 | from tornado.websocket import websocket_connect
9 |
10 | define('url', default='ws://localhost:9001')
11 | define('name', default='Tornado')
12 |
13 | @gen.engine
14 | def run_tests():
15 | url = options.url + '/getCaseCount'
16 | control_ws = yield websocket_connect(url, None)
17 | num_tests = int((yield control_ws.read_message()))
18 | logging.info('running %d cases', num_tests)
19 | msg = yield control_ws.read_message()
20 | assert msg is None
21 |
22 | for i in range(1, num_tests + 1):
23 | logging.info('running test case %d', i)
24 | url = options.url + '/runCase?case=%d&agent=%s' % (i, options.name)
25 | test_ws = yield websocket_connect(url, None, compression_options={})
26 | while True:
27 | message = yield test_ws.read_message()
28 | if message is None:
29 | break
30 | test_ws.write_message(message, binary=isinstance(message, bytes))
31 |
32 | url = options.url + '/updateReports?agent=%s' % options.name
33 | update_ws = yield websocket_connect(url, None)
34 | msg = yield update_ws.read_message()
35 | assert msg is None
36 | IOLoop.instance().stop()
37 |
38 | def main():
39 | parse_command_line()
40 |
41 | IOLoop.instance().add_callback(run_tests)
42 |
43 | IOLoop.instance().start()
44 |
45 | if __name__ == '__main__':
46 | main()
47 |
--------------------------------------------------------------------------------
/maint/test/websocket/fuzzingclient.json:
--------------------------------------------------------------------------------
1 | {
2 | "options": {"failByDrop": false},
3 | "outdir": "./reports/servers",
4 |
5 | "servers": [
6 | {"agent": "Tornado/py26", "url": "ws://localhost:9001",
7 | "options": {"version": 18}},
8 | {"agent": "Tornado/py27", "url": "ws://localhost:9002",
9 | "options": {"version": 18}},
10 | {"agent": "Tornado/py32", "url": "ws://localhost:9003",
11 | "options": {"version": 18}},
12 | {"agent": "Tornado/pypy", "url": "ws://localhost:9004",
13 | "options": {"version": 18}}
14 | ],
15 |
16 | "cases": ["*"],
17 | "exclude-cases": ["9.*", "12.*.1","12.2.*", "12.3.*", "12.4.*", "12.5.*", "13.*.1"],
18 | "exclude-agent-cases": {}
19 | }
20 |
--------------------------------------------------------------------------------
/maint/test/websocket/fuzzingserver.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "url": "ws://localhost:9001",
4 |
5 | "options": {"failByDrop": false},
6 | "outdir": "./reports/clients",
7 | "webport": 8080,
8 |
9 | "cases": ["*"],
10 | "exclude-cases": ["9.*", "12.*.1","12.2.*", "12.3.*", "12.4.*", "12.5.*", "13.*.1"],
11 | "exclude-agent-cases": {}
12 | }
13 |
--------------------------------------------------------------------------------
/maint/test/websocket/run-client.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | tox
6 |
7 | .tox/py27/bin/wstest -m fuzzingserver &
8 | FUZZING_SERVER_PID=$!
9 |
10 | sleep 1
11 |
12 | .tox/py26/bin/python client.py --name='Tornado/py26'
13 | .tox/py27/bin/python client.py --name='Tornado/py27'
14 | .tox/py32/bin/python client.py --name='Tornado/py32'
15 | .tox/pypy/bin/python client.py --name='Tornado/pypy'
16 |
17 | kill $FUZZING_SERVER_PID
18 | wait
19 |
20 | echo "Tests complete. Output is in ./reports/clients/index.html"
21 |
--------------------------------------------------------------------------------
/maint/test/websocket/run-server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # Runs the autobahn websocket conformance test against tornado in both
4 | # python2 and python3. Output goes in ./reports/servers/index.html.
5 | #
6 | # The --cases and --exclude arguments can be used to run only part of
7 | # the suite. The default is --exclude="9.*" to skip the relatively slow
8 | # performance tests; pass --exclude="" to override and include them.
9 |
10 | set -e
11 |
12 | # build/update the virtualenvs
13 | tox
14 |
15 | .tox/py26/bin/python server.py --port=9001 &
16 | PY26_SERVER_PID=$!
17 |
18 | .tox/py27/bin/python server.py --port=9002 &
19 | PY27_SERVER_PID=$!
20 |
21 | .tox/py32/bin/python server.py --port=9003 &
22 | PY32_SERVER_PID=$!
23 |
24 | .tox/pypy/bin/python server.py --port=9004 &
25 | PYPY_SERVER_PID=$!
26 |
27 | sleep 1
28 |
29 | .tox/py27/bin/wstest -m fuzzingclient
30 |
31 | kill $PY26_SERVER_PID
32 | kill $PY27_SERVER_PID
33 | kill $PY32_SERVER_PID
34 | kill $PYPY_SERVER_PID
35 | wait
36 |
37 | echo "Tests complete. Output is in ./reports/servers/index.html"
38 |
--------------------------------------------------------------------------------
/maint/test/websocket/server.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from tornado.ioloop import IOLoop
4 | from tornado.options import define, options, parse_command_line
5 | from tornado.websocket import WebSocketHandler
6 | from tornado.web import Application
7 |
8 | define('port', default=9000)
9 |
10 | class EchoHandler(WebSocketHandler):
11 | def on_message(self, message):
12 | self.write_message(message, binary=isinstance(message, bytes))
13 |
14 | def get_compression_options(self):
15 | return {}
16 |
17 | if __name__ == '__main__':
18 | parse_command_line()
19 | app = Application([
20 | ('/', EchoHandler),
21 | ])
22 | app.listen(options.port, address='127.0.0.1')
23 | IOLoop.instance().start()
24 |
--------------------------------------------------------------------------------
/maint/test/websocket/tox.ini:
--------------------------------------------------------------------------------
1 | # We don't actually use tox to run this test, but it's the easiest way
2 | # to install autobahn and build the speedups module.
3 | # See run.sh for the real test runner.
4 | [tox]
5 | envlist = py27, py32, py26, pypy
6 | setupdir=../../..
7 |
8 | [testenv]
9 | commands = python -c pass
10 |
11 | [testenv:py27]
12 | deps = autobahntestsuite
13 |
--------------------------------------------------------------------------------
/maint/vm/README:
--------------------------------------------------------------------------------
1 | This directory contains virtual machine setup scripts for testing Tornado.
2 |
3 | Requirements:
4 |
5 | Vagrant (http://vagrantup.com) and VirtualBox (http://virtualbox.org).
6 | Vagrant provides an easy download for Ubuntu 10.04 (aka lucid64); base
7 | images for other platforms are harder to find and can be built with
8 | VeeWee (https://github.com/jedi4ever/veewee).
9 |
10 | Usage:
11 |
12 | cd to the appropriate directory and run `vagrant up`, then `vagrant ssh`.
13 | From there, simply run `tox` to run the full test suite, or cd to /tornado
14 | and test manually. Afterwards, use `vagrant suspend` or `vagrant destroy`
15 | to clean up.
16 |
17 | Notes:
18 |
19 | Python distutils (and therefore tox) assume that if the platform
20 | supports hard links, they can be used in the Tornado source directory.
21 | VirtualBox's shared folder filesystem does not support hard links (or
22 | symlinks), so we have to use NFS shared folders instead. (which has
23 | the unfortunate side effect of requiring sudo on the host machine)
24 |
--------------------------------------------------------------------------------
/maint/vm/freebsd/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
5 | VAGRANTFILE_API_VERSION = "2"
6 |
7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
8 | config.vm.box = "chef/freebsd-10.0"
9 |
10 | config.vm.network "private_network", type: "dhcp"
11 |
12 | # Share an additional folder to the guest VM. The first argument is
13 | # the path on the host to the actual folder. The second argument is
14 | # the path on the guest to mount the folder. And the optional third
15 | # argument is a set of non-required options.
16 | config.vm.synced_folder "../../..", "/tornado", type: "nfs"
17 |
18 | # Override the default /vagrant mapping to use nfs, since freebsd doesn't
19 | # support other folder types.
20 | config.vm.synced_folder ".", "/vagrant", type: "nfs"
21 |
22 | config.ssh.shell = "/bin/sh"
23 |
24 | config.vm.provision :shell, :path => "setup.sh"
25 | end
26 |
--------------------------------------------------------------------------------
/maint/vm/freebsd/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | chsh -s bash vagrant
4 |
5 | PACKAGES="
6 | curl
7 | python
8 | python34
9 | py27-pip
10 | py27-virtualenv
11 | "
12 |
13 | PIP_PACKAGES="
14 | futures
15 | pycurl
16 | tox
17 | "
18 |
19 | ASSUME_ALWAYS_YES=true pkg install $PACKAGES
20 |
21 | pip install $PIP_PACKAGES
22 |
23 | /tornado/maint/vm/shared-setup.sh
24 |
--------------------------------------------------------------------------------
/maint/vm/freebsd/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist=py27-full, py27, py34
3 | setupdir=/tornado
4 | # /home is a symlink to /usr/home, but tox doesn't like symlinks here
5 | toxworkdir=/usr/home/vagrant/tox-tornado
6 |
7 | [testenv]
8 | commands = python -m tornado.test.runtests {posargs:}
9 |
10 | [testenv:py27-full]
11 | # twisted's tests fail on freebsd
12 | deps =
13 | futures
14 | pycurl
15 |
--------------------------------------------------------------------------------
/maint/vm/shared-setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # Run at the end of each vm's provisioning script
3 |
4 | set -e
5 |
6 | # Link tox.ini into the home directory so you can run tox immediately
7 | # after ssh'ing in without cd'ing to /vagrant (since cd'ing to /tornado
8 | # gets the wrong config)
9 | ln -sf /vagrant/tox.ini ~vagrant/tox.ini
10 |
--------------------------------------------------------------------------------
/maint/vm/ubuntu10.04/Vagrantfile:
--------------------------------------------------------------------------------
1 | Vagrant::Config.run do |config|
2 | config.vm.box = "lucid64"
3 | config.vm.box_url = "http://files.vagrantup.com/lucid64.box"
4 |
5 | config.vm.network :hostonly, "172.19.1.2"
6 | config.vm.share_folder("tornado", "/tornado", "../../..", :nfs=> true)
7 |
8 | config.vm.provision :shell, :path => "setup.sh"
9 | end
--------------------------------------------------------------------------------
/maint/vm/ubuntu10.04/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | apt-get update
6 |
7 | # libcurl4-gnutls-dev is the default if you ask for libcurl4-dev, but it
8 | # has bugs that make our tests deadlock (the relevant tests detect this and
9 | # disable themselves, but it means that to get full coverage we have to use
10 | # the openssl version).
11 | # The oddly-named python-software-properties includes add-apt-repository.
12 | APT_PACKAGES="
13 | python-pip
14 | python-dev
15 | libcurl4-openssl-dev
16 | python-software-properties
17 | "
18 |
19 | apt-get -y install $APT_PACKAGES
20 |
21 |
22 | # Ubuntu 10.04 has python 2.6 as default; install more from here.
23 | add-apt-repository ppa:fkrull/deadsnakes
24 | apt-get update
25 |
26 | DEADSNAKES_PACKAGES="
27 | python2.7
28 | python2.7-dev
29 | python3.2
30 | python3.2-dev
31 | "
32 | apt-get -y install $DEADSNAKES_PACKAGES
33 |
34 |
35 | PIP_PACKAGES="
36 | futures
37 | pycurl
38 | tox
39 | twisted
40 | virtualenv
41 | "
42 |
43 | pip install $PIP_PACKAGES
44 |
45 | /tornado/maint/vm/shared-setup.sh
46 |
--------------------------------------------------------------------------------
/maint/vm/ubuntu10.04/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py27-full, py32, py26, py26-full, py27
3 | setupdir=/tornado
4 | toxworkdir=/home/vagrant/tox-tornado
5 |
6 | [testenv]
7 | commands = python -m tornado.test.runtests {posargs:}
8 |
9 | [testenv:py26]
10 | deps = unittest2
11 |
12 | [testenv:py26-full]
13 | deps =
14 | futures
15 | pycurl
16 | twisted==11.0.0
17 | unittest2
18 |
19 | [testenv:py27-full]
20 | basepython = python2.7
21 | deps =
22 | futures
23 | pycurl
24 | twisted==11.0.0
25 |
--------------------------------------------------------------------------------
/maint/vm/ubuntu12.04/Vagrantfile:
--------------------------------------------------------------------------------
1 | Vagrant::Config.run do |config|
2 | config.vm.box = "precise64"
3 | config.vm.box_url = "http://files.vagrantup.com/precise64.box"
4 |
5 | config.vm.network :hostonly, "172.19.1.5"
6 | config.vm.share_folder("tornado", "/tornado", "../../..", :nfs=> true)
7 |
8 | config.vm.provision :shell, :path => "setup.sh"
9 | end
--------------------------------------------------------------------------------
/maint/vm/ubuntu12.04/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | apt-get update
6 |
7 | # libcurl4-gnutls-dev is the default if you ask for libcurl4-dev, but it
8 | # has bugs that make our tests deadlock (the relevant tests detect this and
9 | # disable themselves, but it means that to get full coverage we have to use
10 | # the openssl version).
11 | # The oddly-named python-software-properties includes add-apt-repository.
12 | APT_PACKAGES="
13 | python-pip
14 | python-dev
15 | libcurl4-openssl-dev
16 | python-software-properties
17 | "
18 |
19 | apt-get -y install $APT_PACKAGES
20 |
21 |
22 | # Ubuntu 12.04 has python 2.7 as default; install more from here.
23 | add-apt-repository ppa:fkrull/deadsnakes
24 | apt-get update
25 |
26 | DEADSNAKES_PACKAGES="
27 | python3.2
28 | python3.2-dev
29 | "
30 | apt-get -y install $DEADSNAKES_PACKAGES
31 |
32 |
33 | PIP_PACKAGES="
34 | futures
35 | pycurl
36 | tox
37 | twisted
38 | virtualenv
39 | "
40 |
41 | pip install $PIP_PACKAGES
42 |
43 | /tornado/maint/vm/shared-setup.sh
44 |
--------------------------------------------------------------------------------
/maint/vm/ubuntu12.04/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py27-full, py32, py27, py27-select, py27-twisted
3 | setupdir=/tornado
4 | toxworkdir=/home/vagrant/tox-tornado
5 |
6 | [testenv]
7 | commands = python -m tornado.test.runtests {posargs:}
8 |
9 | [testenv:py27-full]
10 | basepython = python2.7
11 | deps =
12 | futures
13 | pycurl
14 | twisted==12.2.0
15 |
16 | [testenv:py27-select]
17 | basepython = python2.7
18 | deps =
19 | futures
20 | pycurl
21 | twisted==12.2.0
22 | commands = python -m tornado.test.runtests --ioloop=tornado.platform.select.SelectIOLoop {posargs:}
23 |
24 | [testenv:py27-twisted]
25 | basepython = python2.7
26 | deps =
27 | futures
28 | pycurl
29 | twisted==12.2.0
30 | commands = python -m tornado.test.runtests --ioloop=tornado.platform.twisted.TwistedIOLoop {posargs:}
31 |
32 | [testenv:py32]
33 | basepython = python3.2
34 | commands = python -bb -m tornado.test.runtests {posargs:}
--------------------------------------------------------------------------------
/maint/vm/ubuntu14.04/Vagrantfile:
--------------------------------------------------------------------------------
1 | Vagrant::Config.run do |config|
2 | config.vm.box = "ubuntu/trusty64"
3 |
4 | config.vm.network :hostonly, "172.19.1.8"
5 | config.vm.share_folder("tornado", "/tornado", "../../..", :nfs=> true)
6 |
7 | config.vm.provision :shell, :path => "setup.sh"
8 | end
--------------------------------------------------------------------------------
/maint/vm/ubuntu14.04/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | apt-get update
6 |
7 | # libcurl4-gnutls-dev is the default if you ask for libcurl4-dev, but it
8 | # has bugs that make our tests deadlock (the relevant tests detect this and
9 | # disable themselves, but it means that to get full coverage we have to use
10 | # the openssl version).
11 | APT_PACKAGES="
12 | python-pip
13 | python-dev
14 | python3-pycurl
15 | libcurl4-openssl-dev
16 | "
17 |
18 | apt-get -y install $APT_PACKAGES
19 |
20 | # Ubuntu 14.04 includes python 2.7 and 3.4.
21 |
22 | PIP_PACKAGES="
23 | futures
24 | pycurl
25 | tox
26 | twisted
27 | virtualenv
28 | "
29 |
30 | pip install $PIP_PACKAGES
31 |
32 | /tornado/maint/vm/shared-setup.sh
33 |
--------------------------------------------------------------------------------
/maint/vm/ubuntu14.04/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py27-full, py34, py27, py27-select, py27-twisted
3 | setupdir=/tornado
4 | toxworkdir=/home/vagrant/tox-tornado
5 |
6 | [testenv]
7 | commands = python -m tornado.test.runtests {posargs:}
8 |
9 | [testenv:py27-full]
10 | basepython = python2.7
11 | deps =
12 | futures
13 | mock
14 | pycurl
15 | twisted==14.0.0
16 |
17 | [testenv:py27-select]
18 | basepython = python2.7
19 | deps =
20 | futures
21 | mock
22 | pycurl
23 | twisted==14.0.0
24 | commands = python -m tornado.test.runtests --ioloop=tornado.platform.select.SelectIOLoop {posargs:}
25 |
26 | [testenv:py27-twisted]
27 | basepython = python2.7
28 | deps =
29 | futures
30 | mock
31 | pycurl
32 | twisted==14.0.0
33 | commands = python -m tornado.test.runtests --ioloop=tornado.platform.twisted.TwistedIOLoop {posargs:}
34 |
--------------------------------------------------------------------------------
/maint/vm/windows/bootstrap.py:
--------------------------------------------------------------------------------
1 | r"""Installs files needed for tornado testing on windows.
2 |
3 | These instructions are compatible with the VMs provided by http://modern.ie.
4 | The bootstrapping script works on the WinXP/IE6 and Win8/IE10 configurations,
5 | although tornado's tests do not pass on XP.
6 |
7 | 1) Install virtualbox guest additions (from the device menu in virtualbox)
8 | 2) Set up a shared folder to the root of your tornado repo. It must be a
9 | read-write mount to use tox, although the tests can be run directly
10 | in a read-only mount. This will probably assign drive letter E:.
11 | 3) Install Python 2.7 from python.org.
12 | 4) Run this script by double-clicking it, or running
13 | "c:\python27\python.exe bootstrap.py" in a shell.
14 |
15 | To run the tests by hand, cd to e:\ and run
16 | c:\python27\python.exe -m tornado.test.runtests
17 | To run the tests with tox, cd to e:\maint\vm\windows and run
18 | c:\python27\scripts\tox
19 | To run under cygwin (which must be installed separately), run
20 | cd /cygdrive/e; python -m tornado.test.runtests
21 | """
22 |
23 | import os
24 | import subprocess
25 | import sys
26 | import urllib
27 |
28 | TMPDIR = r'c:\tornado_bootstrap'
29 |
30 | PYTHON_VERSIONS = [
31 | (r'c:\python26\python.exe', 'http://www.python.org/ftp/python/2.6.6/python-2.6.6.msi'),
32 | (r'c:\python27\python.exe', 'http://www.python.org/ftp/python/2.7.3/python-2.7.3.msi'),
33 | (r'c:\python32\python.exe', 'http://www.python.org/ftp/python/3.2.3/python-3.2.3.msi'),
34 | (r'c:\python33\python.exe', 'http://www.python.org/ftp/python/3.3.0/python-3.3.0.msi'),
35 | ]
36 |
37 | SCRIPTS_DIR = r'c:\python27\scripts'
38 | EASY_INSTALL = os.path.join(SCRIPTS_DIR, 'easy_install.exe')
39 |
40 | PY_PACKAGES = ['tox', 'virtualenv', 'pip']
41 |
42 | def download_to_cache(url, local_name=None):
43 | if local_name is None:
44 | local_name = url.split('/')[-1]
45 | filename = os.path.join(TMPDIR, local_name)
46 | if not os.path.exists(filename):
47 | data = urllib.urlopen(url).read()
48 | with open(filename, 'wb') as f:
49 | f.write(data)
50 | return filename
51 |
52 | def main():
53 | if not os.path.exists(TMPDIR):
54 | os.mkdir(TMPDIR)
55 | os.chdir(TMPDIR)
56 | for exe, url in PYTHON_VERSIONS:
57 | if os.path.exists(exe):
58 | print "%s already exists, skipping" % exe
59 | continue
60 | print "Installing %s" % url
61 | filename = download_to_cache(url)
62 | # http://blog.jaraco.com/2012/01/how-i-install-python-on-windows.html
63 | subprocess.check_call(['msiexec', '/i', filename,
64 | 'ALLUSERS=1', '/passive'])
65 |
66 | if not os.path.exists(EASY_INSTALL):
67 | filename = download_to_cache('http://python-distribute.org/distribute_setup.py')
68 | subprocess.check_call([sys.executable, filename])
69 |
70 | subprocess.check_call([EASY_INSTALL] + PY_PACKAGES)
71 |
72 | # cygwin's setup.exe doesn't like being run from a script (looks
73 | # UAC-related). If it did, something like this might install it.
74 | # (install python, python-setuptools, python3, and easy_install
75 | # unittest2 (cygwin's python 2 is 2.6))
76 | #filename = download_to_cache('http://cygwin.com/setup.exe')
77 | #CYGTMPDIR = os.path.join(TMPDIR, 'cygwin')
78 | #if not os.path.exists(CYGTMPDIR):
79 | # os.mkdir(CYGTMPDIR)
80 | ## http://www.jbmurphy.com/2011/06/16/powershell-script-to-install-cygwin/
81 | #CYGWIN_ARGS = [filename, '-q', '-l', CYGTMPDIR,
82 | # '-s', 'http://mirror.nyi.net/cygwin/', '-R', r'c:\cygwin']
83 | #subprocess.check_call(CYGWIN_ARGS)
84 |
85 |
86 | if __name__ == '__main__':
87 | main()
88 |
--------------------------------------------------------------------------------
/maint/vm/windows/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py27-full, py32-full, py26, py26-full, py27, py32, py33, py27-opt, py33-monotonic
3 | setupdir = e:\
4 | toxworkdir = c:\tox-tornado
5 |
6 | [testenv]
7 | commands = python -m tornado.test.runtests {posargs:}
8 |
9 | [testenv:py26]
10 | deps = unittest2
11 |
12 | [testenv:py26-full]
13 | basepython = python2.6
14 | deps =
15 | futures
16 | mock
17 | unittest2
18 |
19 | [testenv:py27-full]
20 | basepython = python2.7
21 | deps =
22 | futures
23 | mock
24 |
25 | [testenv:py32-full]
26 | basepython = python3.2
27 | deps =
28 | mock
29 |
30 | [testenv:py33]
31 | # tox's path mappings haven't been updated for py33 yet.
32 | basepython = c:\python33\python.exe
33 |
34 | [testenv:py33-monotonic]
35 | basepython = c:\python33\python.exe
36 | commands = python -m tornado.test.runtests --ioloop_time_monotonic {posargs:}
37 |
38 | [testenv:py27-opt]
39 | basepython = python2.7
40 | deps =
41 | futures
42 | mock
43 | commands = python -O -m tornado.test.runtests {posargs:}
44 |
--------------------------------------------------------------------------------
/tornado/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright 2009 Facebook
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 | # not use this file except in compliance with the License. You may obtain
7 | # a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | # License for the specific language governing permissions and limitations
15 | # under the License.
16 |
17 | """The Tornado web server and tools."""
18 |
19 | from __future__ import absolute_import, division, print_function, with_statement
20 |
21 | # version is a human-readable version number.
22 |
23 | # version_info is a four-tuple for programmatic comparison. The first
24 | # three numbers are the components of the version number. The fourth
25 | # is zero for an official release, positive for a development branch,
26 | # or negative for a release candidate or beta (after the base version
27 | # number has been incremented)
28 | version = "4.3"
29 | version_info = (4, 3, 0, 0)
30 |
--------------------------------------------------------------------------------
/tornado/platform/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tao12345666333/tornado-zh/e9e8519beb147d9e1290f6a4fa7d61123d1ecb1c/tornado/platform/__init__.py
--------------------------------------------------------------------------------
/tornado/platform/auto.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright 2011 Facebook
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 | # not use this file except in compliance with the License. You may obtain
7 | # a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | # License for the specific language governing permissions and limitations
15 | # under the License.
16 |
17 | """Implementation of platform-specific functionality.
18 |
19 | For each function or class described in `tornado.platform.interface`,
20 | the appropriate platform-specific implementation exists in this module.
21 | Most code that needs access to this functionality should do e.g.::
22 |
23 | from tornado.platform.auto import set_close_exec
24 | """
25 |
26 | from __future__ import absolute_import, division, print_function, with_statement
27 |
28 | import os
29 |
30 | if 'APPENGINE_RUNTIME' in os.environ:
31 | from tornado.platform.common import Waker
32 |
33 | def set_close_exec(fd):
34 | pass
35 | elif os.name == 'nt':
36 | from tornado.platform.common import Waker
37 | from tornado.platform.windows import set_close_exec
38 | else:
39 | from tornado.platform.posix import set_close_exec, Waker
40 |
41 | try:
42 | # monotime monkey-patches the time module to have a monotonic function
43 | # in versions of python before 3.3.
44 | import monotime
45 | # Silence pyflakes warning about this unused import
46 | monotime
47 | except ImportError:
48 | pass
49 | try:
50 | from time import monotonic as monotonic_time
51 | except ImportError:
52 | monotonic_time = None
53 |
54 | __all__ = ['Waker', 'set_close_exec', 'monotonic_time']
55 |
--------------------------------------------------------------------------------
/tornado/platform/caresresolver.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import, division, print_function, with_statement
2 | import pycares
3 | import socket
4 |
5 | from tornado import gen
6 | from tornado.ioloop import IOLoop
7 | from tornado.netutil import Resolver, is_valid_ip
8 |
9 |
10 | class CaresResolver(Resolver):
11 | """Name resolver based on the c-ares library.
12 |
13 | This is a non-blocking and non-threaded resolver. It may not produce
14 | the same results as the system resolver, but can be used for non-blocking
15 | resolution when threads cannot be used.
16 |
17 | c-ares fails to resolve some names when ``family`` is ``AF_UNSPEC``,
18 | so it is only recommended for use in ``AF_INET`` (i.e. IPv4). This is
19 | the default for ``tornado.simple_httpclient``, but other libraries
20 | may default to ``AF_UNSPEC``.
21 |
22 | .. versionchanged:: 4.1
23 | The ``io_loop`` argument is deprecated.
24 | """
25 | def initialize(self, io_loop=None):
26 | self.io_loop = io_loop or IOLoop.current()
27 | self.channel = pycares.Channel(sock_state_cb=self._sock_state_cb)
28 | self.fds = {}
29 |
30 | def _sock_state_cb(self, fd, readable, writable):
31 | state = ((IOLoop.READ if readable else 0) |
32 | (IOLoop.WRITE if writable else 0))
33 | if not state:
34 | self.io_loop.remove_handler(fd)
35 | del self.fds[fd]
36 | elif fd in self.fds:
37 | self.io_loop.update_handler(fd, state)
38 | self.fds[fd] = state
39 | else:
40 | self.io_loop.add_handler(fd, self._handle_events, state)
41 | self.fds[fd] = state
42 |
43 | def _handle_events(self, fd, events):
44 | read_fd = pycares.ARES_SOCKET_BAD
45 | write_fd = pycares.ARES_SOCKET_BAD
46 | if events & IOLoop.READ:
47 | read_fd = fd
48 | if events & IOLoop.WRITE:
49 | write_fd = fd
50 | self.channel.process_fd(read_fd, write_fd)
51 |
52 | @gen.coroutine
53 | def resolve(self, host, port, family=0):
54 | if is_valid_ip(host):
55 | addresses = [host]
56 | else:
57 | # gethostbyname doesn't take callback as a kwarg
58 | self.channel.gethostbyname(host, family, (yield gen.Callback(1)))
59 | callback_args = yield gen.Wait(1)
60 | assert isinstance(callback_args, gen.Arguments)
61 | assert not callback_args.kwargs
62 | result, error = callback_args.args
63 | if error:
64 | raise Exception('C-Ares returned error %s: %s while resolving %s' %
65 | (error, pycares.errno.strerror(error), host))
66 | addresses = result.addresses
67 | addrinfo = []
68 | for address in addresses:
69 | if '.' in address:
70 | address_family = socket.AF_INET
71 | elif ':' in address:
72 | address_family = socket.AF_INET6
73 | else:
74 | address_family = socket.AF_UNSPEC
75 | if family != socket.AF_UNSPEC and family != address_family:
76 | raise Exception('Requested socket family %d but got %d' %
77 | (family, address_family))
78 | addrinfo.append((address_family, (address, port)))
79 | raise gen.Return(addrinfo)
80 |
--------------------------------------------------------------------------------
/tornado/platform/common.py:
--------------------------------------------------------------------------------
1 | """Lowest-common-denominator implementations of platform functionality."""
2 | from __future__ import absolute_import, division, print_function, with_statement
3 |
4 | import errno
5 | import socket
6 |
7 | from tornado.platform import interface
8 |
9 |
10 | class Waker(interface.Waker):
11 | """Create an OS independent asynchronous pipe.
12 |
13 | For use on platforms that don't have os.pipe() (or where pipes cannot
14 | be passed to select()), but do have sockets. This includes Windows
15 | and Jython.
16 | """
17 | def __init__(self):
18 | # Based on Zope select_trigger.py:
19 | # https://github.com/zopefoundation/Zope/blob/master/src/ZServer/medusa/thread/select_trigger.py
20 |
21 | self.writer = socket.socket()
22 | # Disable buffering -- pulling the trigger sends 1 byte,
23 | # and we want that sent immediately, to wake up ASAP.
24 | self.writer.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
25 |
26 | count = 0
27 | while 1:
28 | count += 1
29 | # Bind to a local port; for efficiency, let the OS pick
30 | # a free port for us.
31 | # Unfortunately, stress tests showed that we may not
32 | # be able to connect to that port ("Address already in
33 | # use") despite that the OS picked it. This appears
34 | # to be a race bug in the Windows socket implementation.
35 | # So we loop until a connect() succeeds (almost always
36 | # on the first try). See the long thread at
37 | # http://mail.zope.org/pipermail/zope/2005-July/160433.html
38 | # for hideous details.
39 | a = socket.socket()
40 | a.bind(("127.0.0.1", 0))
41 | a.listen(1)
42 | connect_address = a.getsockname() # assigned (host, port) pair
43 | try:
44 | self.writer.connect(connect_address)
45 | break # success
46 | except socket.error as detail:
47 | if (not hasattr(errno, 'WSAEADDRINUSE') or
48 | detail[0] != errno.WSAEADDRINUSE):
49 | # "Address already in use" is the only error
50 | # I've seen on two WinXP Pro SP2 boxes, under
51 | # Pythons 2.3.5 and 2.4.1.
52 | raise
53 | # (10048, 'Address already in use')
54 | # assert count <= 2 # never triggered in Tim's tests
55 | if count >= 10: # I've never seen it go above 2
56 | a.close()
57 | self.writer.close()
58 | raise socket.error("Cannot bind trigger!")
59 | # Close `a` and try again. Note: I originally put a short
60 | # sleep() here, but it didn't appear to help or hurt.
61 | a.close()
62 |
63 | self.reader, addr = a.accept()
64 | self.reader.setblocking(0)
65 | self.writer.setblocking(0)
66 | a.close()
67 | self.reader_fd = self.reader.fileno()
68 |
69 | def fileno(self):
70 | return self.reader.fileno()
71 |
72 | def write_fileno(self):
73 | return self.writer.fileno()
74 |
75 | def wake(self):
76 | try:
77 | self.writer.send(b"x")
78 | except (IOError, socket.error):
79 | pass
80 |
81 | def consume(self):
82 | try:
83 | while True:
84 | result = self.reader.recv(1024)
85 | if not result:
86 | break
87 | except (IOError, socket.error):
88 | pass
89 |
90 | def close(self):
91 | self.reader.close()
92 | self.writer.close()
93 |
--------------------------------------------------------------------------------
/tornado/platform/epoll.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright 2012 Facebook
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 | # not use this file except in compliance with the License. You may obtain
7 | # a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | # License for the specific language governing permissions and limitations
15 | # under the License.
16 | """EPoll-based IOLoop implementation for Linux systems."""
17 | from __future__ import absolute_import, division, print_function, with_statement
18 |
19 | import select
20 |
21 | from tornado.ioloop import PollIOLoop
22 |
23 |
24 | class EPollIOLoop(PollIOLoop):
25 | def initialize(self, **kwargs):
26 | super(EPollIOLoop, self).initialize(impl=select.epoll(), **kwargs)
27 |
--------------------------------------------------------------------------------
/tornado/platform/interface.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright 2011 Facebook
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 | # not use this file except in compliance with the License. You may obtain
7 | # a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | # License for the specific language governing permissions and limitations
15 | # under the License.
16 |
17 | """Interfaces for platform-specific functionality.
18 |
19 | This module exists primarily for documentation purposes and as base classes
20 | for other tornado.platform modules. Most code should import the appropriate
21 | implementation from `tornado.platform.auto`.
22 | """
23 |
24 | from __future__ import absolute_import, division, print_function, with_statement
25 |
26 |
27 | def set_close_exec(fd):
28 | """Sets the close-on-exec bit (``FD_CLOEXEC``)for a file descriptor."""
29 | raise NotImplementedError()
30 |
31 |
32 | class Waker(object):
33 | """A socket-like object that can wake another thread from ``select()``.
34 |
35 | The `~tornado.ioloop.IOLoop` will add the Waker's `fileno()` to
36 | its ``select`` (or ``epoll`` or ``kqueue``) calls. When another
37 | thread wants to wake up the loop, it calls `wake`. Once it has woken
38 | up, it will call `consume` to do any necessary per-wake cleanup. When
39 | the ``IOLoop`` is closed, it closes its waker too.
40 | """
41 | def fileno(self):
42 | """Returns the read file descriptor for this waker.
43 |
44 | Must be suitable for use with ``select()`` or equivalent on the
45 | local platform.
46 | """
47 | raise NotImplementedError()
48 |
49 | def write_fileno(self):
50 | """Returns the write file descriptor for this waker."""
51 | raise NotImplementedError()
52 |
53 | def wake(self):
54 | """Triggers activity on the waker's file descriptor."""
55 | raise NotImplementedError()
56 |
57 | def consume(self):
58 | """Called after the listen has woken up to do any necessary cleanup."""
59 | raise NotImplementedError()
60 |
61 | def close(self):
62 | """Closes the waker's file descriptor(s)."""
63 | raise NotImplementedError()
64 |
--------------------------------------------------------------------------------
/tornado/platform/kqueue.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright 2012 Facebook
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 | # not use this file except in compliance with the License. You may obtain
7 | # a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | # License for the specific language governing permissions and limitations
15 | # under the License.
16 | """KQueue-based IOLoop implementation for BSD/Mac systems."""
17 | from __future__ import absolute_import, division, print_function, with_statement
18 |
19 | import select
20 |
21 | from tornado.ioloop import IOLoop, PollIOLoop
22 |
23 | assert hasattr(select, 'kqueue'), 'kqueue not supported'
24 |
25 |
26 | class _KQueue(object):
27 | """A kqueue-based event loop for BSD/Mac systems."""
28 | def __init__(self):
29 | self._kqueue = select.kqueue()
30 | self._active = {}
31 |
32 | def fileno(self):
33 | return self._kqueue.fileno()
34 |
35 | def close(self):
36 | self._kqueue.close()
37 |
38 | def register(self, fd, events):
39 | if fd in self._active:
40 | raise IOError("fd %s already registered" % fd)
41 | self._control(fd, events, select.KQ_EV_ADD)
42 | self._active[fd] = events
43 |
44 | def modify(self, fd, events):
45 | self.unregister(fd)
46 | self.register(fd, events)
47 |
48 | def unregister(self, fd):
49 | events = self._active.pop(fd)
50 | self._control(fd, events, select.KQ_EV_DELETE)
51 |
52 | def _control(self, fd, events, flags):
53 | kevents = []
54 | if events & IOLoop.WRITE:
55 | kevents.append(select.kevent(
56 | fd, filter=select.KQ_FILTER_WRITE, flags=flags))
57 | if events & IOLoop.READ:
58 | kevents.append(select.kevent(
59 | fd, filter=select.KQ_FILTER_READ, flags=flags))
60 | # Even though control() takes a list, it seems to return EINVAL
61 | # on Mac OS X (10.6) when there is more than one event in the list.
62 | for kevent in kevents:
63 | self._kqueue.control([kevent], 0)
64 |
65 | def poll(self, timeout):
66 | kevents = self._kqueue.control(None, 1000, timeout)
67 | events = {}
68 | for kevent in kevents:
69 | fd = kevent.ident
70 | if kevent.filter == select.KQ_FILTER_READ:
71 | events[fd] = events.get(fd, 0) | IOLoop.READ
72 | if kevent.filter == select.KQ_FILTER_WRITE:
73 | if kevent.flags & select.KQ_EV_EOF:
74 | # If an asynchronous connection is refused, kqueue
75 | # returns a write event with the EOF flag set.
76 | # Turn this into an error for consistency with the
77 | # other IOLoop implementations.
78 | # Note that for read events, EOF may be returned before
79 | # all data has been consumed from the socket buffer,
80 | # so we only check for EOF on write events.
81 | events[fd] = IOLoop.ERROR
82 | else:
83 | events[fd] = events.get(fd, 0) | IOLoop.WRITE
84 | if kevent.flags & select.KQ_EV_ERROR:
85 | events[fd] = events.get(fd, 0) | IOLoop.ERROR
86 | return events.items()
87 |
88 |
89 | class KQueueIOLoop(PollIOLoop):
90 | def initialize(self, **kwargs):
91 | super(KQueueIOLoop, self).initialize(impl=_KQueue(), **kwargs)
92 |
--------------------------------------------------------------------------------
/tornado/platform/posix.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright 2011 Facebook
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 | # not use this file except in compliance with the License. You may obtain
7 | # a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | # License for the specific language governing permissions and limitations
15 | # under the License.
16 |
17 | """Posix implementations of platform-specific functionality."""
18 |
19 | from __future__ import absolute_import, division, print_function, with_statement
20 |
21 | import fcntl
22 | import os
23 |
24 | from tornado.platform import interface
25 |
26 |
27 | def set_close_exec(fd):
28 | flags = fcntl.fcntl(fd, fcntl.F_GETFD)
29 | fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
30 |
31 |
32 | def _set_nonblocking(fd):
33 | flags = fcntl.fcntl(fd, fcntl.F_GETFL)
34 | fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
35 |
36 |
37 | class Waker(interface.Waker):
38 | def __init__(self):
39 | r, w = os.pipe()
40 | _set_nonblocking(r)
41 | _set_nonblocking(w)
42 | set_close_exec(r)
43 | set_close_exec(w)
44 | self.reader = os.fdopen(r, "rb", 0)
45 | self.writer = os.fdopen(w, "wb", 0)
46 |
47 | def fileno(self):
48 | return self.reader.fileno()
49 |
50 | def write_fileno(self):
51 | return self.writer.fileno()
52 |
53 | def wake(self):
54 | try:
55 | self.writer.write(b"x")
56 | except IOError:
57 | pass
58 |
59 | def consume(self):
60 | try:
61 | while True:
62 | result = self.reader.read()
63 | if not result:
64 | break
65 | except IOError:
66 | pass
67 |
68 | def close(self):
69 | self.reader.close()
70 | self.writer.close()
71 |
--------------------------------------------------------------------------------
/tornado/platform/select.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright 2012 Facebook
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 | # not use this file except in compliance with the License. You may obtain
7 | # a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | # License for the specific language governing permissions and limitations
15 | # under the License.
16 | """Select-based IOLoop implementation.
17 |
18 | Used as a fallback for systems that don't support epoll or kqueue.
19 | """
20 | from __future__ import absolute_import, division, print_function, with_statement
21 |
22 | import select
23 |
24 | from tornado.ioloop import IOLoop, PollIOLoop
25 |
26 |
27 | class _Select(object):
28 | """A simple, select()-based IOLoop implementation for non-Linux systems"""
29 | def __init__(self):
30 | self.read_fds = set()
31 | self.write_fds = set()
32 | self.error_fds = set()
33 | self.fd_sets = (self.read_fds, self.write_fds, self.error_fds)
34 |
35 | def close(self):
36 | pass
37 |
38 | def register(self, fd, events):
39 | if fd in self.read_fds or fd in self.write_fds or fd in self.error_fds:
40 | raise IOError("fd %s already registered" % fd)
41 | if events & IOLoop.READ:
42 | self.read_fds.add(fd)
43 | if events & IOLoop.WRITE:
44 | self.write_fds.add(fd)
45 | if events & IOLoop.ERROR:
46 | self.error_fds.add(fd)
47 | # Closed connections are reported as errors by epoll and kqueue,
48 | # but as zero-byte reads by select, so when errors are requested
49 | # we need to listen for both read and error.
50 | # self.read_fds.add(fd)
51 |
52 | def modify(self, fd, events):
53 | self.unregister(fd)
54 | self.register(fd, events)
55 |
56 | def unregister(self, fd):
57 | self.read_fds.discard(fd)
58 | self.write_fds.discard(fd)
59 | self.error_fds.discard(fd)
60 |
61 | def poll(self, timeout):
62 | readable, writeable, errors = select.select(
63 | self.read_fds, self.write_fds, self.error_fds, timeout)
64 | events = {}
65 | for fd in readable:
66 | events[fd] = events.get(fd, 0) | IOLoop.READ
67 | for fd in writeable:
68 | events[fd] = events.get(fd, 0) | IOLoop.WRITE
69 | for fd in errors:
70 | events[fd] = events.get(fd, 0) | IOLoop.ERROR
71 | return events.items()
72 |
73 |
74 | class SelectIOLoop(PollIOLoop):
75 | def initialize(self, **kwargs):
76 | super(SelectIOLoop, self).initialize(impl=_Select(), **kwargs)
77 |
--------------------------------------------------------------------------------
/tornado/platform/windows.py:
--------------------------------------------------------------------------------
1 | # NOTE: win32 support is currently experimental, and not recommended
2 | # for production use.
3 |
4 |
5 | from __future__ import absolute_import, division, print_function, with_statement
6 | import ctypes
7 | import ctypes.wintypes
8 |
9 | # See: http://msdn.microsoft.com/en-us/library/ms724935(VS.85).aspx
10 | SetHandleInformation = ctypes.windll.kernel32.SetHandleInformation
11 | SetHandleInformation.argtypes = (ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD)
12 | SetHandleInformation.restype = ctypes.wintypes.BOOL
13 |
14 | HANDLE_FLAG_INHERIT = 0x00000001
15 |
16 |
17 | def set_close_exec(fd):
18 | success = SetHandleInformation(fd, HANDLE_FLAG_INHERIT, 0)
19 | if not success:
20 | raise ctypes.GetLastError()
21 |
--------------------------------------------------------------------------------
/tornado/speedups.c:
--------------------------------------------------------------------------------
1 | #define PY_SSIZE_T_CLEAN
2 | #include
3 |
4 | static PyObject* websocket_mask(PyObject* self, PyObject* args) {
5 | const char* mask;
6 | Py_ssize_t mask_len;
7 | const char* data;
8 | Py_ssize_t data_len;
9 | Py_ssize_t i;
10 | PyObject* result;
11 | char* buf;
12 |
13 | if (!PyArg_ParseTuple(args, "s#s#", &mask, &mask_len, &data, &data_len)) {
14 | return NULL;
15 | }
16 |
17 | result = PyBytes_FromStringAndSize(NULL, data_len);
18 | if (!result) {
19 | return NULL;
20 | }
21 | buf = PyBytes_AsString(result);
22 | for (i = 0; i < data_len; i++) {
23 | buf[i] = data[i] ^ mask[i % 4];
24 | }
25 |
26 | return result;
27 | }
28 |
29 | static PyMethodDef methods[] = {
30 | {"websocket_mask", websocket_mask, METH_VARARGS, ""},
31 | {NULL, NULL, 0, NULL}
32 | };
33 |
34 | #if PY_MAJOR_VERSION >= 3
35 | static struct PyModuleDef speedupsmodule = {
36 | PyModuleDef_HEAD_INIT,
37 | "speedups",
38 | NULL,
39 | -1,
40 | methods
41 | };
42 |
43 | PyMODINIT_FUNC
44 | PyInit_speedups(void) {
45 | return PyModule_Create(&speedupsmodule);
46 | }
47 | #else // Python 2.x
48 | PyMODINIT_FUNC
49 | initspeedups(void) {
50 | Py_InitModule("tornado.speedups", methods);
51 | }
52 | #endif
53 |
--------------------------------------------------------------------------------
/tornado/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tao12345666333/tornado-zh/e9e8519beb147d9e1290f6a4fa7d61123d1ecb1c/tornado/test/__init__.py
--------------------------------------------------------------------------------
/tornado/test/__main__.py:
--------------------------------------------------------------------------------
1 | """Shim to allow python -m tornado.test.
2 |
3 | This only works in python 2.7+.
4 | """
5 | from __future__ import absolute_import, division, print_function, with_statement
6 |
7 | from tornado.test.runtests import all, main
8 |
9 | # tornado.testing.main autodiscovery relies on 'all' being present in
10 | # the main module, so import it here even though it is not used directly.
11 | # The following line prevents a pyflakes warning.
12 | all = all
13 |
14 | main()
15 |
--------------------------------------------------------------------------------
/tornado/test/csv_translations/fr_FR.csv:
--------------------------------------------------------------------------------
1 | "school","école"
2 |
--------------------------------------------------------------------------------
/tornado/test/gettext_translations/extract_me.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa
2 | # Dummy source file to allow creation of the initial .po file in the
3 | # same way as a real project. I'm not entirely sure about the real
4 | # workflow here, but this seems to work.
5 | #
6 | # 1) xgettext --language=Python --keyword=_:1,2 --keyword=pgettext:1c,2 --keyword=pgettext:1c,2,3 extract_me.py -o tornado_test.po
7 | # 2) Edit tornado_test.po, setting CHARSET, Plural-Forms and setting msgstr
8 | # 3) msgfmt tornado_test.po -o tornado_test.mo
9 | # 4) Put the file in the proper location: $LANG/LC_MESSAGES
10 |
11 | from __future__ import absolute_import, division, print_function, with_statement
12 | _("school")
13 | pgettext("law", "right")
14 | pgettext("good", "right")
15 | pgettext("organization", "club", "clubs", 1)
16 | pgettext("stick", "club", "clubs", 1)
17 |
--------------------------------------------------------------------------------
/tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2015-01-27 11:05+0300\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=utf-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 | "Plural-Forms: nplurals=2; plural=(n > 1);\n"
20 |
21 | #: extract_me.py:11
22 | msgid "school"
23 | msgstr "école"
24 |
25 | #: extract_me.py:12
26 | msgctxt "law"
27 | msgid "right"
28 | msgstr "le droit"
29 |
30 | #: extract_me.py:13
31 | msgctxt "good"
32 | msgid "right"
33 | msgstr "le bien"
34 |
35 | #: extract_me.py:14
36 | msgctxt "organization"
37 | msgid "club"
38 | msgid_plural "clubs"
39 | msgstr[0] "le club"
40 | msgstr[1] "les clubs"
41 |
42 | #: extract_me.py:15
43 | msgctxt "stick"
44 | msgid "club"
45 | msgid_plural "clubs"
46 | msgstr[0] "le bâton"
47 | msgstr[1] "les bâtons"
48 |
--------------------------------------------------------------------------------
/tornado/test/import_test.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa
2 | from __future__ import absolute_import, division, print_function, with_statement
3 | from tornado.test.util import unittest
4 |
5 |
6 | class ImportTest(unittest.TestCase):
7 | def test_import_everything(self):
8 | # Some of our modules are not otherwise tested. Import them
9 | # all (unless they have external dependencies) here to at
10 | # least ensure that there are no syntax errors.
11 | import tornado.auth
12 | import tornado.autoreload
13 | import tornado.concurrent
14 | # import tornado.curl_httpclient # depends on pycurl
15 | import tornado.escape
16 | import tornado.gen
17 | import tornado.http1connection
18 | import tornado.httpclient
19 | import tornado.httpserver
20 | import tornado.httputil
21 | import tornado.ioloop
22 | import tornado.iostream
23 | import tornado.locale
24 | import tornado.log
25 | import tornado.netutil
26 | import tornado.options
27 | import tornado.process
28 | import tornado.simple_httpclient
29 | import tornado.stack_context
30 | import tornado.tcpserver
31 | import tornado.template
32 | import tornado.testing
33 | import tornado.util
34 | import tornado.web
35 | import tornado.websocket
36 | import tornado.wsgi
37 |
38 | # for modules with dependencies, if those dependencies can be loaded,
39 | # load them too.
40 |
41 | def test_import_pycurl(self):
42 | try:
43 | import pycurl
44 | except ImportError:
45 | pass
46 | else:
47 | import tornado.curl_httpclient
48 |
--------------------------------------------------------------------------------
/tornado/test/options_test.cfg:
--------------------------------------------------------------------------------
1 | port=443
2 | port=443
3 | username='李康'
4 |
5 | foo_bar='a'
6 |
--------------------------------------------------------------------------------
/tornado/test/resolve_test_helper.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import, division, print_function, with_statement
2 | from tornado.ioloop import IOLoop
3 | from tornado.netutil import ThreadedResolver
4 | from tornado.util import u
5 |
6 | # When this module is imported, it runs getaddrinfo on a thread. Since
7 | # the hostname is unicode, getaddrinfo attempts to import encodings.idna
8 | # but blocks on the import lock. Verify that ThreadedResolver avoids
9 | # this deadlock.
10 |
11 | resolver = ThreadedResolver()
12 | IOLoop.current().run_sync(lambda: resolver.resolve(u('localhost'), 80))
13 |
--------------------------------------------------------------------------------
/tornado/test/static/dir/index.html:
--------------------------------------------------------------------------------
1 | this is the index
2 |
--------------------------------------------------------------------------------
/tornado/test/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
3 |
--------------------------------------------------------------------------------
/tornado/test/static/sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 2008
6 | 141100
7 |
8 |
9 |
10 |
11 | 4
12 | 2011
13 | 59900
14 |
15 |
16 |
17 | 68
18 | 2011
19 | 13600
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tornado/test/static/sample.xml.bz2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tao12345666333/tornado-zh/e9e8519beb147d9e1290f6a4fa7d61123d1ecb1c/tornado/test/static/sample.xml.bz2
--------------------------------------------------------------------------------
/tornado/test/static/sample.xml.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tao12345666333/tornado-zh/e9e8519beb147d9e1290f6a4fa7d61123d1ecb1c/tornado/test/static/sample.xml.gz
--------------------------------------------------------------------------------
/tornado/test/static_foo.txt:
--------------------------------------------------------------------------------
1 | This file should not be served by StaticFileHandler even though
2 | its name starts with "static".
3 |
--------------------------------------------------------------------------------
/tornado/test/tcpserver_test.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import, division, print_function, with_statement
2 | import socket
3 |
4 | from tornado import gen
5 | from tornado.iostream import IOStream
6 | from tornado.log import app_log
7 | from tornado.stack_context import NullContext
8 | from tornado.tcpserver import TCPServer
9 | from tornado.testing import AsyncTestCase, ExpectLog, bind_unused_port, gen_test
10 |
11 |
12 | class TCPServerTest(AsyncTestCase):
13 | @gen_test
14 | def test_handle_stream_coroutine_logging(self):
15 | # handle_stream may be a coroutine and any exception in its
16 | # Future will be logged.
17 | class TestServer(TCPServer):
18 | @gen.coroutine
19 | def handle_stream(self, stream, address):
20 | yield gen.moment
21 | stream.close()
22 | 1 / 0
23 |
24 | server = client = None
25 | try:
26 | sock, port = bind_unused_port()
27 | with NullContext():
28 | server = TestServer()
29 | server.add_socket(sock)
30 | client = IOStream(socket.socket())
31 | with ExpectLog(app_log, "Exception in callback"):
32 | yield client.connect(('localhost', port))
33 | yield client.read_until_close()
34 | yield gen.moment
35 | finally:
36 | if server is not None:
37 | server.stop()
38 | if client is not None:
39 | client.close()
40 |
--------------------------------------------------------------------------------
/tornado/test/templates/utf8.html:
--------------------------------------------------------------------------------
1 | Héllo
2 |
--------------------------------------------------------------------------------
/tornado/test/test.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICSDCCAbGgAwIBAgIJAN1oTowzMbkzMA0GCSqGSIb3DQEBBQUAMD0xCzAJBgNV
3 | BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRkwFwYDVQQKDBBUb3JuYWRvIFdl
4 | YiBUZXN0MB4XDTEwMDgyNTE4MjQ0NFoXDTIwMDgyMjE4MjQ0NFowPTELMAkGA1UE
5 | BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExGTAXBgNVBAoMEFRvcm5hZG8gV2Vi
6 | IFRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALirW3mX4jbdFse2aZwW
7 | zszCJ1IsRDrzALpbvMYLLbIZqo+Z8v5aERKTRQpXFqGaZyY+tdwYy7X7YXcLtKqv
8 | jnw/MSeIaqkw5pROKz5aR0nkPLvcTmhJVLVPCLc8dFnIlu8aC9TrDhr90P+PzU39
9 | UG7zLweA9zXKBuW3Tjo5dMP3AgMBAAGjUDBOMB0GA1UdDgQWBBRhJjMBYrzddCFr
10 | /0vvPyHMeqgo0TAfBgNVHSMEGDAWgBRhJjMBYrzddCFr/0vvPyHMeqgo0TAMBgNV
11 | HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGP6GaxSfb21bikcqaK3ZKCC1sRJ
12 | tiCuvJZbBUFUCAzl05dYUfJZim/oWK+GqyUkUB8ciYivUNnn9OtS7DnlTgT2ws2e
13 | lNgn5cuFXoAGcHXzVlHG3yoywYBf3y0Dn20uzrlLXUWJAzoSLOt2LTaXvwlgm7hF
14 | W1q8SQ6UBshRw2X0
15 | -----END CERTIFICATE-----
16 |
--------------------------------------------------------------------------------
/tornado/test/test.key:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALirW3mX4jbdFse2
3 | aZwWzszCJ1IsRDrzALpbvMYLLbIZqo+Z8v5aERKTRQpXFqGaZyY+tdwYy7X7YXcL
4 | tKqvjnw/MSeIaqkw5pROKz5aR0nkPLvcTmhJVLVPCLc8dFnIlu8aC9TrDhr90P+P
5 | zU39UG7zLweA9zXKBuW3Tjo5dMP3AgMBAAECgYEAiygNaWYrf95AcUQi9w00zpUr
6 | nj9fNvCwxr2kVbRMvd2balS/CC4EmXPCXdVcZ3B7dBVjYzSIJV0Fh/iZLtnVysD9
7 | fcNMZ+Cz71b/T0ItsNYOsJk0qUVyP52uqsqkNppIPJsD19C+ZeMLZj6iEiylZyl8
8 | 2U16c/kVIjER63mUEGkCQQDayQOTGPJrKHqPAkUqzeJkfvHH2yCf+cySU+w6ezyr
9 | j9yxcq8aZoLusCebDVT+kz7RqnD5JePFvB38cMuepYBLAkEA2BTFdZx30f4moPNv
10 | JlXlPNJMUTUzsXG7n4vNc+18O5ous0NGQII8jZWrIcTrP8wiP9fF3JwUsKrJhcBn
11 | xRs3hQJBAIDUgz1YIE+HW3vgi1gkOh6RPdBAsVpiXtr/fggFz3j60qrO7FswaAMj
12 | SX8c/6KUlBYkNjgP3qruFf4zcUNvEzcCQQCaioCPFVE9ByBpjLG6IUTKsz2R9xL5
13 | nfYqrbpLZ1aq6iLsYvkjugHE4X57sHLwNfdo4dHJbnf9wqhO2MVe25BhAkBdKYpY
14 | 7OKc/2mmMbJDhVBgoixz/muN/5VjdfbvVY48naZkJF1p1tmogqPC5F1jPCS4rM+S
15 | FfPJIHRNEn2oktw5
16 | -----END PRIVATE KEY-----
17 |
--------------------------------------------------------------------------------
/tornado/test/util.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import, division, print_function, with_statement
2 |
3 | import os
4 | import platform
5 | import socket
6 | import sys
7 | import textwrap
8 |
9 | from tornado.testing import bind_unused_port
10 |
11 | # Encapsulate the choice of unittest or unittest2 here.
12 | # To be used as 'from tornado.test.util import unittest'.
13 | if sys.version_info < (2, 7):
14 | # In py26, we must always use unittest2.
15 | import unittest2 as unittest
16 | else:
17 | # Otherwise, use whichever version of unittest was imported in
18 | # tornado.testing.
19 | from tornado.testing import unittest
20 |
21 | skipIfNonUnix = unittest.skipIf(os.name != 'posix' or sys.platform == 'cygwin',
22 | "non-unix platform")
23 |
24 | # travis-ci.org runs our tests in an overworked virtual machine, which makes
25 | # timing-related tests unreliable.
26 | skipOnTravis = unittest.skipIf('TRAVIS' in os.environ,
27 | 'timing tests unreliable on travis')
28 |
29 | skipOnAppEngine = unittest.skipIf('APPENGINE_RUNTIME' in os.environ,
30 | 'not available on Google App Engine')
31 |
32 | # Set the environment variable NO_NETWORK=1 to disable any tests that
33 | # depend on an external network.
34 | skipIfNoNetwork = unittest.skipIf('NO_NETWORK' in os.environ,
35 | 'network access disabled')
36 |
37 | skipIfNoIPv6 = unittest.skipIf(not socket.has_ipv6, 'ipv6 support not present')
38 |
39 |
40 | skipBefore33 = unittest.skipIf(sys.version_info < (3, 3), 'PEP 380 (yield from) not available')
41 | skipBefore35 = unittest.skipIf(sys.version_info < (3, 5), 'PEP 492 (async/await) not available')
42 | skipNotCPython = unittest.skipIf(platform.python_implementation() != 'CPython',
43 | 'Not CPython implementation')
44 |
45 |
46 | def refusing_port():
47 | """Returns a local port number that will refuse all connections.
48 |
49 | Return value is (cleanup_func, port); the cleanup function
50 | must be called to free the port to be reused.
51 | """
52 | # On travis-ci, port numbers are reassigned frequently. To avoid
53 | # collisions with other tests, we use an open client-side socket's
54 | # ephemeral port number to ensure that nothing can listen on that
55 | # port.
56 | server_socket, port = bind_unused_port()
57 | server_socket.setblocking(1)
58 | client_socket = socket.socket()
59 | client_socket.connect(("127.0.0.1", port))
60 | conn, client_addr = server_socket.accept()
61 | conn.close()
62 | server_socket.close()
63 | return (client_socket.close, client_addr[1])
64 |
65 |
66 | def exec_test(caller_globals, caller_locals, s):
67 | """Execute ``s`` in a given context and return the result namespace.
68 |
69 | Used to define functions for tests in particular python
70 | versions that would be syntax errors in older versions.
71 | """
72 | # Flatten the real global and local namespace into our fake
73 | # globals: it's all global from the perspective of code defined
74 | # in s.
75 | global_namespace = dict(caller_globals, **caller_locals)
76 | local_namespace = {}
77 | exec(textwrap.dedent(s), global_namespace, local_namespace)
78 | return local_namespace
79 |
--------------------------------------------------------------------------------
/tornado/test/wsgi_test.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import, division, print_function, with_statement
2 | from wsgiref.validate import validator
3 |
4 | from tornado.escape import json_decode
5 | from tornado.test.httpserver_test import TypeCheckHandler
6 | from tornado.testing import AsyncHTTPTestCase
7 | from tornado.util import u
8 | from tornado.web import RequestHandler, Application
9 | from tornado.wsgi import WSGIApplication, WSGIContainer, WSGIAdapter
10 |
11 |
12 | class WSGIContainerTest(AsyncHTTPTestCase):
13 | def wsgi_app(self, environ, start_response):
14 | status = "200 OK"
15 | response_headers = [("Content-Type", "text/plain")]
16 | start_response(status, response_headers)
17 | return [b"Hello world!"]
18 |
19 | def get_app(self):
20 | return WSGIContainer(validator(self.wsgi_app))
21 |
22 | def test_simple(self):
23 | response = self.fetch("/")
24 | self.assertEqual(response.body, b"Hello world!")
25 |
26 |
27 | class WSGIApplicationTest(AsyncHTTPTestCase):
28 | def get_app(self):
29 | class HelloHandler(RequestHandler):
30 | def get(self):
31 | self.write("Hello world!")
32 |
33 | class PathQuotingHandler(RequestHandler):
34 | def get(self, path):
35 | self.write(path)
36 |
37 | # It would be better to run the wsgiref server implementation in
38 | # another thread instead of using our own WSGIContainer, but this
39 | # fits better in our async testing framework and the wsgiref
40 | # validator should keep us honest
41 | return WSGIContainer(validator(WSGIApplication([
42 | ("/", HelloHandler),
43 | ("/path/(.*)", PathQuotingHandler),
44 | ("/typecheck", TypeCheckHandler),
45 | ])))
46 |
47 | def test_simple(self):
48 | response = self.fetch("/")
49 | self.assertEqual(response.body, b"Hello world!")
50 |
51 | def test_path_quoting(self):
52 | response = self.fetch("/path/foo%20bar%C3%A9")
53 | self.assertEqual(response.body, u("foo bar\u00e9").encode("utf-8"))
54 |
55 | def test_types(self):
56 | headers = {"Cookie": "foo=bar"}
57 | response = self.fetch("/typecheck?foo=bar", headers=headers)
58 | data = json_decode(response.body)
59 | self.assertEqual(data, {})
60 |
61 | response = self.fetch("/typecheck", method="POST", body="foo=bar", headers=headers)
62 | data = json_decode(response.body)
63 | self.assertEqual(data, {})
64 |
65 | # This is kind of hacky, but run some of the HTTPServer tests through
66 | # WSGIContainer and WSGIApplication to make sure everything survives
67 | # repeated disassembly and reassembly.
68 | from tornado.test import httpserver_test
69 | from tornado.test import web_test
70 |
71 |
72 | class WSGIConnectionTest(httpserver_test.HTTPConnectionTest):
73 | def get_app(self):
74 | return WSGIContainer(validator(WSGIApplication(self.get_handlers())))
75 |
76 |
77 | def wrap_web_tests_application():
78 | result = {}
79 | for cls in web_test.wsgi_safe_tests:
80 | class WSGIApplicationWrappedTest(cls):
81 | def get_app(self):
82 | self.app = WSGIApplication(self.get_handlers(),
83 | **self.get_app_kwargs())
84 | return WSGIContainer(validator(self.app))
85 | result["WSGIApplication_" + cls.__name__] = WSGIApplicationWrappedTest
86 | return result
87 | globals().update(wrap_web_tests_application())
88 |
89 |
90 | def wrap_web_tests_adapter():
91 | result = {}
92 | for cls in web_test.wsgi_safe_tests:
93 | class WSGIAdapterWrappedTest(cls):
94 | def get_app(self):
95 | self.app = Application(self.get_handlers(),
96 | **self.get_app_kwargs())
97 | return WSGIContainer(validator(WSGIAdapter(self.app)))
98 | result["WSGIAdapter_" + cls.__name__] = WSGIAdapterWrappedTest
99 | return result
100 | globals().update(wrap_web_tests_adapter())
101 |
--------------------------------------------------------------------------------