Skip to main content

WSGI server implemented in Rust.

Project description

Pyruvate WSGI server

https://gitlab.com/tschorr/pyruvate/badges/main/pipeline.svg https://codecov.io/gl/tschorr/pyruvate/branch/main/graph/badge.svg http://img.shields.io/pypi/v/pyruvate.svg

Pyruvate is a fast, multithreaded WSGI server implemented in Rust.

Features

Installation

If you are on Linux and use a recent Python version,

$ pip install pyruvate

is probably all you need to do.

Binary Packages

manylinux_2_28 and musllinux_1_2 wheels are available for the x86_64 architecture and active Python 3 versions (currently 3.9-3.13).

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.9) 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 using a TCP port

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)

From Python using a Unix socket

A hello world WSGI application using Pyruvate listening on unix:/tmp/pyruvate.socket 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, "/tmp/pyruvate.socket", 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.

async_logging

Optional: Log asynchronously using a dedicated thread. Defaults to True.

chunked_transfer

Optional: Whether to use chunked transfer encoding if no Content-Length header is present. Defaults to False.

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.

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 32 headers

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.

qmon_warn_threshold

Optional: Warning threshold for the number of requests in the request queue. A warning will be logged if the number of queued requests reaches this value. The value must be a positive integer. The default is None which disables the queue monitor.

send_timeout

Optional: Time to wait for a client connection to become available for writing after EAGAIN, in seconds. Connections that do not receive data within this time are closed. The value must be a positive integer. The default is 60 seconds.

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

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 4.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):

import os.path
import pyruvate

from mapproxy.wsgiapp import make_wsgi_app
application = make_wsgi_app(r'/path/to/mapproxy/mapproxy.yaml')

pyruvate.serve(application, "127.0.0.1:8005", 2)

Start from your virtualenv:

$ python config.py

Tested with Mapproxy 1.15.x, 1.13.x, 1.12.x.

Plone

Using pip

After installing Pyruvate in your Plone virtualenv, change the server section in your zope.ini file (located in instance/etc if you are using mkwsgiinstance to create the instance):

[server:main]
use = egg:pyruvate#main
socket = localhost:7878
workers = 2
Using zc.buildout

Using zc.buildout and plone.recipe.zope2instance you can define an instance part using Pyruvate’s PasteDeploy 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 6.0.x, 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 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 2.0, 1.10.x.

Radicale

You can find an example configuration for Radicale in the examples directory of the package. Tested with Radicale 3.5.0.

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.4.1 (2025-04-04)

  • Properly deregister connections with mio 1.0.3 (#33)

1.4.0 (2025-04-03)

  • Fix default arguments in PasteDeploy entry point (#32)

1.4.0rc2 (2025-03-25)

  • Simplify response timeout (#25)

  • Python API simplifications

1.4.0rc1 (2024-11-11)

  • Switch to pyo3-ffi and a stripped-down version of rust-cpython

  • Passing ‘blocksize’ as keyword argument to FileWrapper is no longer possible

1.3.0 (2024-07-04)

  • Switch back to rust-cpython (#29)

1.3.0-rc1 (2023-12-28)

  • Replace rust-cpython with PyO3 (#28)

  • Add support for Python 3.12

  • Drop support for Python 3.7

1.2.2 (2023-07-02)

  • Document Unix Domain Socket usage (#27)

  • Provide legacy manylinux wheel names (#26)

1.2.1 (2022-12-22)

  • Track and remove unfinished responses that did not otherwise error (#23)

  • Build musllinux_1_1 wheels (#24)

1.2.0 (2022-10-26)

  • Support Python 3.11 and discontinue Python 3.6, switch to manylinux2014 for building wheels (#19)

  • Add a request queue monitor (#17)

  • Remove blocking worker (#18)

  • Improve parsing of Content-Length header (#20)

1.1.4 (2022-04-19)

  • Fix handling of empty list responses (#14)

  • Support hostnames in socket addresses (#15)

1.1.3 (2022-04-11)

  • Simplify response writing and improve performance (#12)

  • Improve signal handling (#13)

1.1.2 (2022-01-10)

  • Migrate to Rust 2021

  • Use codecov binary uploader

  • Add CONTRIBUTING.rst

  • Fixed: The wrk benchmarking tool could make pyruvate hang when there is no Content-Length header (#11)

1.1.1 (2021-10-12)

  • Support Python 3.10

1.1.0 (2021-09-14)

  • Refactor FileWrapper and improve its performance

  • Increase the default maximum number of headers

  • Add Radicale example configuration

  • Update development status

1.0.3 (2021-06-05)

  • HEAD request: Do not complain about content length mismatch (#4)

  • More appropriate log level for client side connection termination (#5)

  • Simplify request parsing

1.0.2 (2021-05-02)

  • Close connection and log an error in the case where the actual content length is less than the Content-Length header provided by the application

  • Fix readme

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

This version

1.4.1

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyruvate-1.4.1.tar.gz (95.8 kB view details)

Uploaded Source

Built Distributions

pyruvate-1.4.1-cp313-cp313-musllinux_1_2_x86_64.whl (4.6 MB view details)

Uploaded CPython 3.13 musllinux: musl 1.2+ x86-64

pyruvate-1.4.1-cp313-cp313-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.13 manylinux: glibc 2.28+ x86-64

pyruvate-1.4.1-cp312-cp312-musllinux_1_2_x86_64.whl (4.6 MB view details)

Uploaded CPython 3.12 musllinux: musl 1.2+ x86-64

pyruvate-1.4.1-cp312-cp312-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.12 manylinux: glibc 2.28+ x86-64

pyruvate-1.4.1-cp311-cp311-musllinux_1_2_x86_64.whl (4.6 MB view details)

Uploaded CPython 3.11 musllinux: musl 1.2+ x86-64

pyruvate-1.4.1-cp311-cp311-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.11 manylinux: glibc 2.28+ x86-64

pyruvate-1.4.1-cp310-cp310-musllinux_1_2_x86_64.whl (4.6 MB view details)

Uploaded CPython 3.10 musllinux: musl 1.2+ x86-64

pyruvate-1.4.1-cp310-cp310-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.10 manylinux: glibc 2.28+ x86-64

pyruvate-1.4.1-cp39-cp39-musllinux_1_2_x86_64.whl (4.6 MB view details)

Uploaded CPython 3.9 musllinux: musl 1.2+ x86-64

pyruvate-1.4.1-cp39-cp39-manylinux_2_28_x86_64.whl (5.1 MB view details)

Uploaded CPython 3.9 manylinux: glibc 2.28+ x86-64

File details

Details for the file pyruvate-1.4.1.tar.gz.

File metadata

  • Download URL: pyruvate-1.4.1.tar.gz
  • Upload date:
  • Size: 95.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for pyruvate-1.4.1.tar.gz
Algorithm Hash digest
SHA256 f4747d0cf8ca39a06976fa9b7a09be52d634e4ff170a8389b25bc70cc74a22f8
MD5 d3d356d3ad6e0bbd1806408482690db5
BLAKE2b-256 b1d1dbc16f2236f492c8dc4cd4f27354315947644fd93280bdcf0343940aa352

See more details on using hashes here.

File details

Details for the file pyruvate-1.4.1-cp313-cp313-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for pyruvate-1.4.1-cp313-cp313-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 b570474c45de6c64212fa024d6283669ad69019cb35751386a1a713aa95035c3
MD5 640735971bbac468f246a07e4aa7b5db
BLAKE2b-256 39bb13f6cb04ac81a93b2d02a9a5adb3a7d38763a56416b1a4a81e0ccd2d08de

See more details on using hashes here.

File details

Details for the file pyruvate-1.4.1-cp313-cp313-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyruvate-1.4.1-cp313-cp313-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 cb19416f9001b974f76be211324d213c850ccbc75b81e844ca6ed1f6726fd6e0
MD5 733866e6806c3f9f005fbb2ddbb6d87c
BLAKE2b-256 b9d5c6ddb9b8fb5b88cd6eded0701f48b0af5cee41f725a4d0348003c3096262

See more details on using hashes here.

File details

Details for the file pyruvate-1.4.1-cp312-cp312-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for pyruvate-1.4.1-cp312-cp312-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 ef057a6134973d01b8518092b4dc6b794a18dcd8de95455159cd7f23565720e8
MD5 f8eea59cd691b76371a9a0cc45f26fd8
BLAKE2b-256 8bdde454899082c858f1fa37b765a61c4c08a164ac014e1d86a1030accaaa601

See more details on using hashes here.

File details

Details for the file pyruvate-1.4.1-cp312-cp312-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyruvate-1.4.1-cp312-cp312-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 54b225cea2ff50ed7bf9ad2dfd5a56e65e0840fbd5278a0e438ae613a699f07f
MD5 9a69e64c16c4c57b9bd2faca0306bb82
BLAKE2b-256 c83493908989c19efde9a9637e01719e0fd13aaa323279ab4587d3bed6e9f1cf

See more details on using hashes here.

File details

Details for the file pyruvate-1.4.1-cp311-cp311-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for pyruvate-1.4.1-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 c6512f2f94a743180299f9848887bbab41dd1a1d918b6e92270fd98c522213c8
MD5 c0b8e51e5a50d3b2a391548e1aec84ae
BLAKE2b-256 2030d058db0179340c528f89e6ec45f912c1382efceb2a1f3589bb2f0e11d5e9

See more details on using hashes here.

File details

Details for the file pyruvate-1.4.1-cp311-cp311-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyruvate-1.4.1-cp311-cp311-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8106fec424bfbae4882794e7b8e61a10c480cd47119561235e2d2dce2c9e3dce
MD5 551047a50ccf4993963768fa217a45a7
BLAKE2b-256 e1f2608089ea5a602fb7826911be09b214c6eb9f0288d4a6365492cec2923f6b

See more details on using hashes here.

File details

Details for the file pyruvate-1.4.1-cp310-cp310-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for pyruvate-1.4.1-cp310-cp310-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 cdd76adb3c1e3c8c7b1627ced5ef1354fb8564b13ba2df53d5f755a82f6f4089
MD5 916a2751eb26fe157d93f98090aa6341
BLAKE2b-256 d12a86270dfdc7f0884e3a8646357ca018a1d8e0522ee0c0bcecb1083fb221ae

See more details on using hashes here.

File details

Details for the file pyruvate-1.4.1-cp310-cp310-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyruvate-1.4.1-cp310-cp310-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e3e53676bcc681fa494a7422cea82748a6f363ac99c5ceabd4dcb6d35a365389
MD5 831dda0ee132597b3d0bbfb28833f9c1
BLAKE2b-256 13785793d1835ac026120b5f2a7a298313f448e892b88968b27ea970f6a17189

See more details on using hashes here.

File details

Details for the file pyruvate-1.4.1-cp39-cp39-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for pyruvate-1.4.1-cp39-cp39-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 c1407b84e970f85ea5d7eacb69dc8709c47889dd4fb26eadee9b15607e610d9e
MD5 ba0fdbe5b4cefa6c8952779215b7d127
BLAKE2b-256 cbd4d27a05fbcc8e9877a5c224984961f4cc8f17ec7cf5e39fa075c7e59bca5b

See more details on using hashes here.

File details

Details for the file pyruvate-1.4.1-cp39-cp39-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyruvate-1.4.1-cp39-cp39-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 2c0cfcfaeea91bdfc426cd80a7b6f848565ee559f131ce29dce0fec3c10172fe
MD5 fc5c9a50bf54206b1206aaa6a6fd17a8
BLAKE2b-256 d541fd2d58ae6cf6c915ba35116030a8a4d4f0524c65751e3e6060e8206ae4c0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page