WSGI server implemented in Rust.
Project description
Pyruvate WSGI server
Pyruvate is a reasonably fast, multithreaded, non-blocking WSGI server implemented in Rust.
Features
Non-blocking read/write using mio
Request parsing using httparse
rust-cpython based Python interface
Worker pool based on threadpool
PasteDeploy entry point
Installation
If you are on Linux and use a recent Python version,
$ pip install pyruvate
is probably all you need to do.
Manylinux2010 binary wheels
Manylinux2010 wheels are available for active Python 3 versions (currently 3.6-3.9). Pip supports manylinux2010 wheels since version 19.0. Setuptools (used by e.g. zc.buildout) supports manylinux2010 wheels since version 42.0.0 <https://setuptools.readthedocs.io/en/latest/history.html#id216 _. So if you are on Linux and the Pyruvate source distribution is preferred over the binary package try upgrading pip and/or setuptools first.
Source installation
On macOS or if for any other reason you want to install the source tarball (e.g. using pip install –no-binary) you will need to install Rust first.
Development Installation
Install Rust
Install and activate a Python 3 (>= 3.6) virtualenv
Install setuptools_rust using pip:
$ pip install setuptools_rust
Install pyruvate, e.g. using pip:
$ pip install -e git+https://gitlab.com/tschorr/pyruvate.git#egg=pyruvate[test]
Using Pyruvate in your WSGI application
From Python
A hello world WSGI application using pyruvate listening on 127.0.0.1:7878 and using 2 worker threads looks like this:
import pyruvate
def application(environ, start_response):
"""Simplest possible application object"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers, None)
return [b"Hello world!\n"]
pyruvate.serve(application, "127.0.0.1:7878", 2)
Using PasteDeploy
Again listening on 127.0.0.1:7878 and using 2 worker threads:
[server:main] use = egg:pyruvate#main socket = 127.0.0.1:7878 workers = 2
Configuration Options
- socket
Required: The TCP socket Pyruvate should bind to. pyruvate also supports systemd socket activation If you specify None as the socket value, pyruvate will try to acquire a socket bound by systemd.
- workers
Required: Number of worker threads to use.
- write_blocking
Optional: Use a blocking connection for writing. Pyruvate currently supports two types of workers: The default worker will write in a non-blocking manner, registering WSGI responses for later processing if the socket isn’t available for writing immediately. By setting this option to True you can enable a worker that will instead set the connection into blocking mode for writing. Defaults to False.
- max_number_headers
Optional: Maximum number of request headers that will be parsed. If a request contains more headers than configured, request processing will stop with an error indicating an incomplete request. The default is 24 headers
- async_logging
Optional: Log asynchronously using a dedicated thread. Defaults to True.
- max_reuse_count
Optional: Specify how often to reuse an existing connection. Setting this parameter to 0 will effectively disable keep-alive connections. This is the default.
- keepalive_timeout
Optional: Specify a timeout in integer seconds for keepalive connection. The persistent connection will be closed after the timeout expires. Defaults to 60 seconds.
- chunked_transfer
Optional: Whether to use chunked transfer encoding if no Content-Length header is present. Defaults to False.
Logging
Pyruvate uses the standard Python logging facility. The logger name is pyruvate. See the Python documentation (logging, logging.config) for configuration options.
Example Configurations
Django 2
After installing Pyruvate in your Django virtualenv, create or modify your wsgi.py file (one worker listening on 127.0.0.1:8000):
import os
import pyruvate
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "your_django_application.settings")
application = get_wsgi_application()
pyruvate.serve(application, "127.0.0.1:8000", 1)
You can now start Django + Pyruvate with:
$ python wsgi.py
Override settings by using the DJANGO_SETTINGS_MODULE environment variable when appropriate. Tested with Django 2.2.x.
MapProxy
First create a basic WSGI configuration following the MapProxy deployment documentation. Then modify config.py so it is using Pyruvate (2 workers listening on 127.0.0.1:8005):
from logging.config import fileConfig
import os.path
import pyruvate
fileConfig(r'/path/to/mapproxy/log.ini', {'here': os.path.dirname(__file__)})
from mapproxy.wsgiapp import make_wsgi_app
application = make_wsgi_app(r'/path/to/mapproxy/mapproxy.yml')
pyruvate.serve(application, "127.0.0.1:8005", 2)
Start from your virtualenv:
$ python config.py
Tested with Mapproxy 1.12.x.
Plone 5.2
Using zc.buildout and plone.recipe.zope2instance you can define an instance part using Pyruvate’s PasteDeploy <https://pastedeploy.readthedocs.io/en/latest/> _entry point:
[instance] recipe = plone.recipe.zope2instance http-address = 127.0.0.1:8080 eggs = Plone pyruvate wsgi-ini-template = ${buildout:directory}/templates/pyruvate.ini.in
The server section of the template provided with the wsgi-ini-template option should look like this (3 workers listening on http-address as specified in the buildout [instance] part):
[server:main] use = egg:pyruvate#main socket = %(http_address)s workers = 3
There is a minimal buildout example configuration for Plone 5.2 in the examples directory of the package.
Tested with Plone 5.2.x.
Pyramid
Install Pyruvate in your Pyramid virtualenv using pip:
$ pip install pyruvate
Modify the server section in your .ini file to use Pyruvate’s PasteDeploy <https://pastedeploy.readthedocs.io/en/latest/> _entry point (listening on 127.0.0.1:7878 and using 5 workers):
[server:main] use = egg:pyruvate#main socket = 127.0.0.1:7878 workers = 5
Start your application as usual using pserve:
$ pserve path/to/your/configfile.ini
Tested with Pyramid 1.10.x.
Nginx settings
Like other WSGI servers pyruvate should be used behind a reverse proxy, e.g. Nginx:
.... location / { proxy_pass http://localhost:7878; ... } ...
Nginx doesn’t use keepalive connections by default so you will need to modify your configuration if you want persistent connections.
Changelog
1.0.1 (2021-04-28)
Fix decoding of URLs that contain non-ascii characters
Raise Python exception when response contains objects other than bytestrings instead of simply logging the error.
1.0.0 (2021-03-24)
Improve query string handling
0.9.2 (2021-01-30)
Better support for HTTP 1.1 Expect/Continue
Improve documentation
0.9.1 (2021-01-13)
Improve GIL handling
Propagate worker thread name to Python logging
Do not report broken pipe as error
PasteDeploy entry point: fix option handling
0.9.0 (2021-01-06)
Reusable connections
Chunked transfer-encoding
Support macOS
0.8.4 (2020-12-12)
Lower CPU usage
0.8.3 (2020-11-26)
Clean wheel build directories
Fix some test isolation problems
Remove a println
0.8.2 (2020-11-17)
Fix blocksize handling for sendfile case
Format unix stream peer address
Use latest mio
0.8.1 (2020-11-10)
Receiver in non-blocking worker must not block when channel is empty
0.8.0 (2020-11-07)
Logging overhaul
New async_logging option
Some performance improvements
Support Python 3.9
Switch to manylinux2010 platform tag
0.7.1 (2020-09-16)
Raise Python exception when socket is unavailable
Add Pyramid configuration example in readme
0.7.0 (2020-08-30)
Use Python logging
Display server info on startup
Fix socket activation for unix domain sockets
0.6.2 (2020-08-12)
Improved logging
PasteDeploy entry point now also uses at most 24 headers by default
0.6.1 (2020-08-10)
Improve request parsing
Increase default maximum number of headers to 24
0.6.0 (2020-07-29)
Support unix domain sockets
Improve sendfile usage
0.5.3 (2020-07-15)
Fix testing for completed sendfile call in case of EAGAIN
0.5.2 (2020-07-15)
Fix testing for completed response in case of EAGAIN
Cargo update
0.5.1 (2020-07-07)
Fix handling of read events
Fix changelog
Cargo update
‘Interrupted’ error is not a todo
Remove unused code
0.5.0 (2020-06-07)
Add support for systemd socket activation
0.4.0 (2020-06-29)
Add a new worker that does nonblocking write
Add default arguments
Add option to configure maximum number of request headers
Add Via header
0.3.0 (2020-06-16)
Switch to rust-cpython
Fix passing of tcp connections to worker threads
0.2.0 (2020-03-10)
Added some Python tests (using py.test and tox)
Improve handling of HTTP headers
Respect content length header when using sendfile
0.1.0 (2020-02-10)
Initial release
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
File details
Details for the file pyruvate-1.0.1.tar.gz
.
File metadata
- Download URL: pyruvate-1.0.1.tar.gz
- Upload date:
- Size: 67.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.24.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.51.0 CPython/3.9.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 85df46fc194df5dbf5bec816cf53c45977612031e07ac51f9d64c10fb62e107a |
|
MD5 | 5df2cd8fb23cdd29bc3f4b0419669ed5 |
|
BLAKE2b-256 | 100aa9454aacae2ea4e52ca4b05b55ad016e4a502be1ed78c9e956821c8695f6 |
File details
Details for the file pyruvate-1.0.1-cp39-cp39-manylinux2010_x86_64.whl
.
File metadata
- Download URL: pyruvate-1.0.1-cp39-cp39-manylinux2010_x86_64.whl
- Upload date:
- Size: 1.1 MB
- Tags: CPython 3.9, manylinux: glibc 2.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.24.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.51.0 CPython/3.9.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | a797062bc1a98f594d539b19b6a162c03295905b97500a9a50828bf849e6939c |
|
MD5 | a73ed94b6646119fa630c5b39ea6cffd |
|
BLAKE2b-256 | 2973ebf45b36fbb48881bb221fdcfaa10a58462dcda8d597b21702e399a36743 |
File details
Details for the file pyruvate-1.0.1-cp38-cp38-manylinux2010_x86_64.whl
.
File metadata
- Download URL: pyruvate-1.0.1-cp38-cp38-manylinux2010_x86_64.whl
- Upload date:
- Size: 1.1 MB
- Tags: CPython 3.8, manylinux: glibc 2.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.24.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.51.0 CPython/3.9.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c229659e166bfcb8543af627233b5137b6dbd7fd2d9b756acabd9b1e7f33840f |
|
MD5 | 690c4a877b194bc6468e06c1f6c9a578 |
|
BLAKE2b-256 | 9f7c60d60bfea8d3142c97751d2c19941195a93b2e4c08abe2a4737048639773 |
File details
Details for the file pyruvate-1.0.1-cp37-cp37m-manylinux2010_x86_64.whl
.
File metadata
- Download URL: pyruvate-1.0.1-cp37-cp37m-manylinux2010_x86_64.whl
- Upload date:
- Size: 1.1 MB
- Tags: CPython 3.7m, manylinux: glibc 2.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.24.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.51.0 CPython/3.9.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c33f52383ae61be87a30ede0916de6d0f5c9e33c42b05f4ac60710375cda42c1 |
|
MD5 | 8d219d7d52d741ae7354e6f780270655 |
|
BLAKE2b-256 | dcaae0928a964a9600a6d7e8deb27126a7cbbdbf57c7d515ddb1c61cee5b0d1e |
File details
Details for the file pyruvate-1.0.1-cp36-cp36m-manylinux2010_x86_64.whl
.
File metadata
- Download URL: pyruvate-1.0.1-cp36-cp36m-manylinux2010_x86_64.whl
- Upload date:
- Size: 1.1 MB
- Tags: CPython 3.6m, manylinux: glibc 2.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.24.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.51.0 CPython/3.9.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d90e37617f499bdb56f3d6ec0f415def2e9418d9d9a9ee806f821ce888cc9cb2 |
|
MD5 | 3410178fca05b4953cf9fbe35ee2bd54 |
|
BLAKE2b-256 | 94fe5762906141157c2ef44e70be15bc8eba62417b1a7cdefa98c49163c79add |