Merge pull request #229 from kdelwat/sphinx-docs

Use Sphinx for documentation
This commit is contained in:
Eli Uriegas 2017-01-19 16:54:02 -06:00 committed by GitHub
commit f77bb81def
14 changed files with 381 additions and 130 deletions

2
.gitignore vendored
View File

@ -11,3 +11,5 @@ settings.py
.idea/* .idea/*
.cache/* .cache/*
.python-version .python-version
docs/_build/
docs/_api/

110
README.md
View File

@ -1,110 +0,0 @@
# Sanic
[![Join the chat at https://gitter.im/sanic-python/Lobby](https://badges.gitter.im/sanic-python/Lobby.svg)](https://gitter.im/sanic-python/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://travis-ci.org/channelcat/sanic.svg?branch=master)](https://travis-ci.org/channelcat/sanic)
[![PyPI](https://img.shields.io/pypi/v/sanic.svg)](https://pypi.python.org/pypi/sanic/)
[![PyPI](https://img.shields.io/pypi/pyversions/sanic.svg)](https://pypi.python.org/pypi/sanic/)
Sanic is a Flask-like Python 3.5+ web server that's written to go fast. It's based on the work done by the amazing folks at magicstack, and was inspired by this article: https://magic.io/blog/uvloop-blazing-fast-python-networking/.
On top of being Flask-like, Sanic supports async request handlers. This means you can use the new shiny async/await syntax from Python 3.5, making your code non-blocking and speedy.
## Benchmarks
All tests were run on an AWS medium instance running ubuntu, using 1 process. Each script delivered a small JSON response and was tested with wrk using 100 connections. Pypy was tested for Falcon and Flask but did not speed up requests.
| Server | Implementation | Requests/sec | Avg Latency |
| ------- | ------------------- | ------------:| -----------:|
| Sanic | Python 3.5 + uvloop | 33,342 | 2.96ms |
| Wheezy | gunicorn + meinheld | 20,244 | 4.94ms |
| Falcon | gunicorn + meinheld | 18,972 | 5.27ms |
| Bottle | gunicorn + meinheld | 13,596 | 7.36ms |
| Flask | gunicorn + meinheld | 4,988 | 20.08ms |
| Kyoukai | Python 3.5 + uvloop | 3,889 | 27.44ms |
| Aiohttp | Python 3.5 + uvloop | 2,979 | 33.42ms |
| Tornado | Python 3.5 | 2,138 | 46.66ms |
## Hello World
```python
from sanic import Sanic
from sanic.response import json
app = Sanic()
@app.route("/")
async def test(request):
return json({"hello": "world"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
```
## Installation
* `python -m pip install sanic`
## Use SSL
* Optionally pass in an SSLContext:
```
import ssl
certificate = "/path/to/certificate"
keyfile = "/path/to/keyfile"
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certificate, keyfile=keyfile)
app.run(host="0.0.0.0", port=8443, ssl=context)
```
## Documentation
* [Getting started](docs/getting_started.md)
* [Request Data](docs/request_data.md)
* [Routing](docs/routing.md)
* [Middleware](docs/middleware.md)
* [Exceptions](docs/exceptions.md)
* [Blueprints](docs/blueprints.md)
* [Class Based Views](docs/class_based_views.md)
* [Cookies](docs/cookies.md)
* [Static Files](docs/static_files.md)
* [Custom Protocol](docs/custom_protocol.md)
* [Testing](docs/testing.md)
* [Deploying](docs/deploying.md)
* [Extensions](docs/extensions.md)
* [Contributing](docs/contributing.md)
* [License](LICENSE)
## TODO:
* Streamed file processing
* File output
* Examples of integrations with 3rd-party modules
* RESTful router
## Limitations:
* No wheels for uvloop and httptools on Windows :(
## Final Thoughts:
▄▄▄▄▄
▀▀▀██████▄▄▄ _______________
▄▄▄▄▄ █████████▄ / \
▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! |
▀▀█████▄▄ ▀██████▄██ | _________________/
▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
▀▀▀▄ ▀▀███ ▀ ▄▄
▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌
██▀▄▄▄██▀▄███▀ ▀▀████ ▄██
▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀
▌ ▐▀████▐███▒▒▒▒▒▐██▌
▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
▀▀█████████▀
▄▄██▀██████▀█
▄██▀ ▀▀▀ █
▄█ ▐▌
▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
▌ ▐ ▀▀▄▄▄▀
▀▀▄▄▀

127
README.rst Normal file
View File

@ -0,0 +1,127 @@
Sanic
=================================
|Join the chat at https://gitter.im/sanic-python/Lobby| |Build Status| |PyPI| |PyPI version|
Sanic is a Flask-like Python 3.5+ web server that's written to go fast. It's based on the work done by the amazing folks at magicstack, and was inspired by `this article <https://magic.io/blog/uvloop-blazing-fast-python-networking/>`_.
On top of being Flask-like, Sanic supports async request handlers. This means you can use the new shiny async/await syntax from Python 3.5, making your code non-blocking and speedy.
Sanic is developed `on GitHub <https://github.com/channelcat/sanic/>`_. Contributions are welcome!
Benchmarks
----------
All tests were run on an AWS medium instance running ubuntu, using 1
process. Each script delivered a small JSON response and was tested with
wrk using 100 connections. Pypy was tested for Falcon and Flask but did
not speed up requests.
+-----------+-----------------------+----------------+---------------+
| Server | Implementation | Requests/sec | Avg Latency |
+===========+=======================+================+===============+
| Sanic | Python 3.5 + uvloop | 33,342 | 2.96ms |
+-----------+-----------------------+----------------+---------------+
| Wheezy | gunicorn + meinheld | 20,244 | 4.94ms |
+-----------+-----------------------+----------------+---------------+
| Falcon | gunicorn + meinheld | 18,972 | 5.27ms |
+-----------+-----------------------+----------------+---------------+
| Bottle | gunicorn + meinheld | 13,596 | 7.36ms |
+-----------+-----------------------+----------------+---------------+
| Flask | gunicorn + meinheld | 4,988 | 20.08ms |
+-----------+-----------------------+----------------+---------------+
| Kyoukai | Python 3.5 + uvloop | 3,889 | 27.44ms |
+-----------+-----------------------+----------------+---------------+
| Aiohttp | Python 3.5 + uvloop | 2,979 | 33.42ms |
+-----------+-----------------------+----------------+---------------+
| Tornado | Python 3.5 | 2,138 | 46.66ms |
+-----------+-----------------------+----------------+---------------+
Hello World Example
-------------------
.. code:: python
from sanic import Sanic
from sanic.response import json
app = Sanic()
@app.route("/")
async def test(request):
return json({"hello": "world"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
SSL Example
-----------
Optionally pass in an SSLContext:
.. code:: python
import ssl
certificate = "/path/to/certificate"
keyfile = "/path/to/keyfile"
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certificate, keyfile=keyfile)
app.run(host="0.0.0.0", port=8443, ssl=context)
Installation
------------
- ``python -m pip install sanic``
Documentation
-------------
Documentation can be found in the ``docs`` directory.
.. |Join the chat at https://gitter.im/sanic-python/Lobby| image:: https://badges.gitter.im/sanic-python/Lobby.svg
:target: https://gitter.im/sanic-python/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. |Build Status| image:: https://travis-ci.org/channelcat/sanic.svg?branch=master
:target: https://travis-ci.org/channelcat/sanic
.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
:target: https://pypi.python.org/pypi/sanic/
.. |PyPI version| image:: https://img.shields.io/pypi/pyversions/sanic.svg
:target: https://pypi.python.org/pypi/sanic/
TODO
----
* Streamed file processing
* File output
* Examples of integrations with 3rd-party modules
* RESTful router
Limitations
-----------
* No wheels for uvloop and httptools on Windows :(
Final Thoughts
--------------
::
▄▄▄▄▄
▀▀▀██████▄▄▄ _______________
▄▄▄▄▄ █████████▄ / \
▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! |
▀▀█████▄▄ ▀██████▄██ | _________________/
▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
▀▀▀▄ ▀▀███ ▀ ▄▄
▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌
██▀▄▄▄██▀▄███▀ ▀▀████ ▄██
▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀
▌ ▐▀████▐███▒▒▒▒▒▐██▌
▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
▀▀█████████▀
▄▄██▀██████▀█
▄██▀ ▀▀▀ █
▄█ ▐▌
▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
▌ ▐ ▀▀▄▄▄▀
▀▀▄▄▀

155
docs/conf.py Normal file
View File

@ -0,0 +1,155 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Sanic documentation build configuration file, created by
# sphinx-quickstart on Sun Dec 25 18:07:21 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
import os
import sys
# Add support for Markdown documentation using Recommonmark
from recommonmark.parser import CommonMarkParser
# Ensure that sanic is present in the path, to allow sphinx-apidoc to
# autogenerate documentation from docstrings
root_directory = os.path.dirname(os.getcwd())
sys.path.insert(0, root_directory)
import sanic
# -- General configuration ------------------------------------------------
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinx.ext.githubpages']
templates_path = ['_templates']
# Enable support for both Restructured Text and Markdown
source_parsers = {'.md': CommonMarkParser}
source_suffix = ['.rst', '.md']
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'Sanic'
copyright = '2016, Sanic contributors'
author = 'Sanic contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = sanic.__version__
# The full version, including alpha/beta/rc tags.
release = sanic.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
#
# modules.rst is generated by sphinx-apidoc but is unused. This suppresses
# a warning about it.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'modules.rst']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'Sanicdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'Sanic.tex', 'Sanic Documentation',
'Sanic contributors', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'sanic', 'Sanic Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'Sanic', 'Sanic Documentation',
author, 'Sanic', 'One line description of project.',
'Miscellaneous'),
]
# -- Options for Epub output ----------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
epub_author = author
epub_publisher = author
epub_copyright = copyright
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']

View File

@ -6,5 +6,18 @@ Thank you for your interest!
* `python -m pip install pytest` * `python -m pip install pytest`
* `python -m pytest tests` * `python -m pytest tests`
## Documentation
Sanic's documentation is built using [sphinx](http://www.sphinx-doc.org/en/1.5.1/). Guides are written in Markdown and can be found in the `docs` folder, while the module reference is automatically generated using `sphinx-apidoc`.
To generate the documentation from scratch:
```bash
sphinx-apidoc -fo docs/_api/ sanic
sphinx-build -b html docs docs/_build
```
The HTML documentation will be created in the `docs/_build` folder.
## Warning ## Warning
One of the main goals of Sanic is speed. Code that lowers the performance of Sanic without significant gains in usability, security, or features may not be merged. One of the main goals of Sanic is speed. Code that lowers the performance of Sanic without significant gains in usability, security, or features may not be merged.

33
docs/index.rst Normal file
View File

@ -0,0 +1,33 @@
.. include:: ../README.rst
Guides
======
.. toctree::
:maxdepth: 2
getting_started
request_data
routing
middleware
exceptions
blueprints
class_based_views
cookies
static_files
custom_protocol
testing
deploying
extensions
contributing
Module Documentation
====================
.. toctree::
Module Reference <_api/sanic>
* :ref:`genindex`
* :ref:`search`

View File

@ -12,4 +12,6 @@ kyoukai
falcon falcon
tornado tornado
aiofiles aiofiles
sphinx
recommonmark
beautifulsoup4 beautifulsoup4

View File

@ -165,6 +165,7 @@ class Handler:
def response(self, request, exception): def response(self, request, exception):
""" """
Fetches and executes an exception handler and returns a response object Fetches and executes an exception handler and returns a response object
:param request: Request :param request: Request
:param exception: Exception to handle :param exception: Exception to handle
:return: Response object :return: Response object

View File

@ -154,6 +154,7 @@ File = namedtuple('File', ['type', 'body', 'name'])
def parse_multipart_form(body, boundary): def parse_multipart_form(body, boundary):
""" """
Parses a request body and returns fields and files Parses a request body and returns fields and files
:param body: Bytes request body :param body: Bytes request body
:param boundary: Bytes multipart boundary :param boundary: Bytes multipart boundary
:return: fields (RequestParameters), files (RequestParameters) :return: fields (RequestParameters), files (RequestParameters)

View File

@ -31,12 +31,20 @@ class RouteDoesNotExist(Exception):
class Router: class Router:
""" """
Router supports basic routing with parameters and method checks Router supports basic routing with parameters and method checks
Usage: Usage:
@app.route('/my_url/<my_param>', methods=['GET', 'POST', ...])
.. code-block:: python
@sanic.route('/my/url/<my_param>', methods=['GET', 'POST', ...])
def my_route(request, my_param): def my_route(request, my_param):
do stuff... do stuff...
or or
@app.route('/my_url/<my_param:my_type>', methods=['GET', 'POST', ...])
.. code-block:: python
@sanic.route('/my/url/<my_param:my_type>', methods['GET', 'POST', ...])
def my_route_with_type(request, my_param: my_type): def my_route_with_type(request, my_param: my_type):
do stuff... do stuff...
@ -61,6 +69,7 @@ class Router:
def add(self, uri, methods, handler, host=None): def add(self, uri, methods, handler, host=None):
""" """
Adds a handler to the route list Adds a handler to the route list
:param uri: Path to match :param uri: Path to match
:param methods: Array of accepted method names. :param methods: Array of accepted method names.
If none are provided, any method is allowed If none are provided, any method is allowed
@ -179,6 +188,7 @@ class Router:
""" """
Gets a request handler based on the URL of the request, or raises an Gets a request handler based on the URL of the request, or raises an
error error
:param request: Request object :param request: Request object
:return: handler, arguments, keyword arguments :return: handler, arguments, keyword arguments
""" """

View File

@ -49,6 +49,7 @@ class Sanic:
def route(self, uri, methods=None, host=None): def route(self, uri, methods=None, host=None):
""" """
Decorates a function to be registered as a route Decorates a function to be registered as a route
:param uri: path of the URL :param uri: path of the URL
:param methods: list or tuple of methods allowed :param methods: list or tuple of methods allowed
:return: decorated function :return: decorated function
@ -71,6 +72,7 @@ class Sanic:
A helper method to register class instance or A helper method to register class instance or
functions as a handler to the application url functions as a handler to the application url
routes. routes.
:param handler: function or class instance :param handler: function or class instance
:param uri: path of the URL :param uri: path of the URL
:param methods: list or tuple of methods allowed :param methods: list or tuple of methods allowed
@ -86,7 +88,8 @@ class Sanic:
def exception(self, *exceptions): def exception(self, *exceptions):
""" """
Decorates a function to be registered as a handler for exceptions Decorates a function to be registered as a handler for exceptions
:param *exceptions: exceptions
:param \*exceptions: exceptions
:return: decorated function :return: decorated function
""" """
@ -132,6 +135,7 @@ class Sanic:
def blueprint(self, blueprint, **options): def blueprint(self, blueprint, **options):
""" """
Registers a blueprint on the application. Registers a blueprint on the application.
:param blueprint: Blueprint object :param blueprint: Blueprint object
:param options: option dictionary with blueprint defaults :param options: option dictionary with blueprint defaults
:return: Nothing :return: Nothing
@ -165,6 +169,7 @@ class Sanic:
Takes a request from the HTTP Server and returns a response object to Takes a request from the HTTP Server and returns a response object to
be sent back The HTTP Server only expects a response object, so be sent back The HTTP Server only expects a response object, so
exception handling must be done here exception handling must be done here
:param request: HTTP Request object :param request: HTTP Request object
:param response_callback: Response function to be called with the :param response_callback: Response function to be called with the
response as the only argument response as the only argument
@ -248,6 +253,7 @@ class Sanic:
""" """
Runs the HTTP Server and listens until keyboard interrupt or term Runs the HTTP Server and listens until keyboard interrupt or term
signal. On termination, drains connections before closing. signal. On termination, drains connections before closing.
:param host: Address to host on :param host: Address to host on
:param port: Port to host on :param port: Port to host on
:param debug: Enables debug output (slows server) :param debug: Enables debug output (slows server)
@ -352,6 +358,7 @@ class Sanic:
""" """
Starts multiple server processes simultaneously. Stops on interrupt Starts multiple server processes simultaneously. Stops on interrupt
and terminate signals, and drains connections when complete. and terminate signals, and drains connections when complete.
:param server_settings: kw arguments to be passed to the serve function :param server_settings: kw arguments to be passed to the serve function
:param workers: number of workers to launch :param workers: number of workers to launch
:param stop_event: if provided, is used as a stop signal :param stop_event: if provided, is used as a stop signal

View File

@ -223,6 +223,7 @@ def update_current_time(loop):
""" """
Caches the current time, since it is needed Caches the current time, since it is needed
at the end of every keep-alive request to update the request timeout time at the end of every keep-alive request to update the request timeout time
:param loop: :param loop:
:return: :return:
""" """
@ -251,6 +252,7 @@ def serve(host, port, request_handler, error_handler, before_start=None,
reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100): reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100):
""" """
Starts asynchronous HTTP Server on an individual process. Starts asynchronous HTTP Server on an individual process.
:param host: Address to host on :param host: Address to host on
:param port: Port to host on :param port: Port to host on
:param request_handler: Sanic request handler with middleware :param request_handler: Sanic request handler with middleware
@ -260,9 +262,11 @@ def serve(host, port, request_handler, error_handler, before_start=None,
:param after_start: Function to be executed after the server starts :param after_start: Function to be executed after the server starts
listening. Takes single argument `loop` listening. Takes single argument `loop`
:param before_stop: Function to be executed when a stop signal is :param before_stop: Function to be executed when a stop signal is
received before it is respected. Takes single argumenet `loop` received before it is respected. Takes single
argument `loop`
:param after_stop: Function to be executed when a stop signal is :param after_stop: Function to be executed when a stop signal is
received after it is respected. Takes single argumenet `loop` received after it is respected. Takes single
argument `loop`
:param debug: Enables debug output (slows server) :param debug: Enables debug output (slows server)
:param request_timeout: time in seconds :param request_timeout: time in seconds
:param ssl: SSLContext :param ssl: SSLContext

View File

@ -15,12 +15,14 @@ def register(app, uri, file_or_directory, pattern, use_modified_since):
""" """
Registers a static directory handler with Sanic by adding a route to the Registers a static directory handler with Sanic by adding a route to the
router and registering a handler. router and registering a handler.
:param app: Sanic :param app: Sanic
:param file_or_directory: File or directory path to serve from :param file_or_directory: File or directory path to serve from
:param uri: URL to serve from :param uri: URL to serve from
:param pattern: regular expression used to match files in the URL :param pattern: regular expression used to match files in the URL
:param use_modified_since: If true, send file modified time, and return :param use_modified_since: If true, send file modified time, and return
not modified if the browser's matches the server's not modified if the browser's matches the
server's
""" """
# If we're not trying to match a file directly, # If we're not trying to match a file directly,

View File

@ -7,21 +7,25 @@ class HTTPMethodView:
to every HTTP method you want to support. to every HTTP method you want to support.
For example: For example:
class DummyView(HTTPMethodView):
.. code-block:: python
class DummyView(HTTPMethodView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
return text('I am get method') return text('I am get method')
def put(self, request, *args, **kwargs): def put(self, request, *args, **kwargs):
return text('I am put method') return text('I am put method')
etc. etc.
If someone tries to use a non-implemented method, there will be a If someone tries to use a non-implemented method, there will be a
405 response. 405 response.
If you need any url params just mention them in method definition: If you need any url params just mention them in method definition:
class DummyView(HTTPMethodView):
.. code-block:: python
class DummyView(HTTPMethodView):
def get(self, request, my_param_here, *args, **kwargs): def get(self, request, my_param_here, *args, **kwargs):
return text('I am get method with %s' % my_param_here) return text('I am get method with %s' % my_param_here)