├── .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 |
5 |
6 |
7 |
8 | 9 |  {{ _("Cancel") }} 10 |
11 | {% if entry %} 12 | 13 | {% end %} 14 | {% module xsrf_form_html() %} 15 |
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 |
{{ _("Archive") }}
8 | {% end %} 9 | -------------------------------------------------------------------------------- /demos/appengine/templates/modules/entry.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ entry.title }}

3 |
{{ locale.format_date(entry.published, full_format=True, shorter=True) }}
4 |
{% raw entry.html %}
5 | {% if current_user and current_user.administrator %} 6 |
{{ _("Edit this post") }}
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 |
28 |

{{ page_title }}

29 |
30 | 39 |
40 | 41 | {% for row in table %} 42 | 43 | {% for cell in row %} 44 | 45 | {% end %} 46 | 47 | {% end %} 48 |
{{ cell }}
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 |
7 |
8 |
{{ _("Syntax documentation") }}
9 | 10 |  {{ _("Cancel") }} 11 |
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 |
{{ _("Archive") }}
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 |

{{ entry.title }}

3 |
{{ locale.format_date(entry.published, full_format=True, shorter=True) }}
4 |
{% raw entry.html %}
5 | {% if current_user %} 6 |
{{ _("Edit this post") }}
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 |
16 |
17 | 18 | 19 | 20 | 25 | 26 |
21 | 22 | 23 | {% module xsrf_form_html() %} 24 |
27 |
28 |
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 |
11 | {{ escape(current_user["name"]) }} - 12 | {{ _("Sign out") }} 13 |
14 |
{{ _("Refresh stream") }}
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 | 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 |
16 |
17 | 18 | 19 | 20 | 25 | 26 |
21 | 22 | 23 | {% module xsrf_form_html() %} 24 |
27 |
28 |
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 | --------------------------------------------------------------------------------