Pyruvate WSGI server
====================
.. image:: https://gitlab.com/tschorr/pyruvate/badges/main/pipeline.svg
:target: https://gitlab.com/tschorr/pyruvate
.. image:: https://codecov.io/gl/tschorr/pyruvate/branch/main/graph/badge.svg
:target: https://codecov.io/gl/tschorr/pyruvate
.. image:: http://img.shields.io/pypi/v/pyruvate.svg
:target: https://pypi.org/project/pyruvate
Pyruvate is a fast, multithreaded `WSGI <https://www.python.org/dev/peps/pep-3333>`_ server implemented in `Rust <https://www.rust-lang.org/>`_.
Features
--------
* Non-blocking read/write using `mio <https://github.com/tokio-rs/mio>`_
* Request parsing using `httparse <https://github.com/seanmonstar/httparse>`_
* `rust-cpython <https://github.com/dgrunwald/rust-cpython>`_ based Python interface
* Worker pool based on `threadpool <https://github.com/rust-threadpool/rust-threadpool>`_
* `PasteDeploy <https://pastedeploy.readthedocs.io/en/latest/>`_ entry point
Installation
------------
If you are on Linux and use a recent Python version,
.. code-block::
$ pip install pyruvate
is probably all you need to do.
Binary Packages
+++++++++++++++
`manylinux_2_17 <https://peps.python.org/pep-0600/>`_ and `musllinux_1_1 <https://peps.python.org/pep-0656/>`_ wheels are available for the `x86_64` architecture and active Python 3 versions (currently 3.8-3.12).
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 <https://doc.rust-lang.org/book/ch01-01-installation.html>`_ first.
Development Installation
++++++++++++++++++++++++
* Install `Rust <https://doc.rust-lang.org/book/ch01-01-installation.html>`__
* Install and activate a Python 3 (>= 3.8) `virtualenv <https://docs.python.org/3/tutorial/venv.html>`_
* Install `setuptools_rust <https://github.com/PyO3/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:
.. code-block:: python
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:
.. code-block:: python
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 <https://www.freedesktop.org/software/systemd/man/systemd.socket.html>`_
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 <https://docs.python.org/3/library/logging.html>`_.
The logger name is `pyruvate`.
See the Python documentation (`logging <https://docs.python.org/3/library/logging.html>`_, `logging.config <https://docs.python.org/3/library/logging.config.html>`_) 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):
.. code-block:: python
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.1.x, 3.2.x, 2.2.x <https://www.djangoproject.com/>`_.
MapProxy
++++++++
First create a basic WSGI configuration following the `MapProxy deployment documentation <https://mapproxy.org/docs/latest/deployment.html#server-script>`_.
Then modify `config.py` so it is using Pyruvate (2 workers listening on 127.0.0.1:8005):
.. code-block:: python
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 <https://mapproxy.org/>`_.
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 <https://pypi.org/project/zc.buildout/>`_ and `plone.recipe.zope2instance <https://pypi.org/project/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 <https://pypi.org/project/plone.recipe.zope2instance/#advanced-options>`_ 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 <https://gitlab.com/tschorr/pyruvate/-/tree/main/examples/plone52>`_ of the package.
Tested with `Plone 6.0.x, 5.2.x <https://plone.org/>`_.
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 2.0, 1.10.x <https://trypyramid.com/>`_.
Radicale
++++++++
You can find an example configuration for `Radicale <https://radicale.org>`_ in the `examples directory <https://gitlab.com/tschorr/pyruvate/-/tree/main/examples/plone52>`_ of the package.
Tested with `Radicale 3.1.8 <https://radicale.org>`_.
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 <https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive>`_ if you want persistent connections.
Changelog
=========
1.3.0 (2024-07-04)
------------------
* Switch back to rust-cpython (`#29 <https://gitlab.com/tschorr/pyruvate/-/issues/29>`_)
1.3.0-rc1 (2023-12-28)
----------------------
* Replace rust-cpython with PyO3 (`#28 <https://gitlab.com/tschorr/pyruvate/-/issues/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 <https://gitlab.com/tschorr/pyruvate/-/issues/27>`_)
* Provide legacy manylinux wheel names (`#26 <https://gitlab.com/tschorr/pyruvate/-/issues/26>`_)
1.2.1 (2022-12-22)
------------------
* Track and remove unfinished responses that did not otherwise error (`#23 <https://gitlab.com/tschorr/pyruvate/-/issues/23>`_)
* Build musllinux_1_1 wheels (`#24 <https://gitlab.com/tschorr/pyruvate/-/issues/24>`_)
1.2.0 (2022-10-26)
------------------
* Support Python 3.11 and discontinue Python 3.6, switch to manylinux2014 for building wheels (`#19 <https://gitlab.com/tschorr/pyruvate/-/issues/19>`_)
* Add a request queue monitor (`#17 <https://gitlab.com/tschorr/pyruvate/-/issues/17>`_)
* Remove blocking worker (`#18 <https://gitlab.com/tschorr/pyruvate/-/issues/18>`_)
* Improve parsing of Content-Length header (`#20 <https://gitlab.com/tschorr/pyruvate/-/issues/20>`_)
1.1.4 (2022-04-19)
------------------
* Fix handling of empty list responses (`#14 <https://gitlab.com/tschorr/pyruvate/-/issues/14>`_)
* Support hostnames in socket addresses (`#15 <https://gitlab.com/tschorr/pyruvate/-/issues/15>`_)
1.1.3 (2022-04-11)
------------------
* Simplify response writing and improve performance (`#12 <https://gitlab.com/tschorr/pyruvate/-/issues/12>`_)
* Improve signal handling (`#13 <https://gitlab.com/tschorr/pyruvate/-/issues/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 <https://gitlab.com/tschorr/pyruvate/-/issues/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 <https://radicale.org>`_ example configuration
* Update development status
1.0.3 (2021-06-05)
------------------
* HEAD request: Do not complain about content length mismatch (`#4 <https://gitlab.com/tschorr/pyruvate/-/issues/4>`_)
* More appropriate log level for client side connection termination (`#5 <https://gitlab.com/tschorr/pyruvate/-/issues/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
Raw data
{
"_id": null,
"home_page": "https://gitlab.com/tschorr/pyruvate",
"name": "pyruvate",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "WSGI",
"author": "tschorr",
"author_email": "t_schorr@gmx.de",
"download_url": "https://files.pythonhosted.org/packages/72/e2/9050c8f85a55305b716967b75d69e377d355940cd2a05c9722135140232a/pyruvate-1.3.0.tar.gz",
"platform": null,
"description": "Pyruvate WSGI server\n====================\n\n.. image:: https://gitlab.com/tschorr/pyruvate/badges/main/pipeline.svg\n :target: https://gitlab.com/tschorr/pyruvate\n\n.. image:: https://codecov.io/gl/tschorr/pyruvate/branch/main/graph/badge.svg\n :target: https://codecov.io/gl/tschorr/pyruvate\n\n.. image:: http://img.shields.io/pypi/v/pyruvate.svg\n :target: https://pypi.org/project/pyruvate\n\nPyruvate is a fast, multithreaded `WSGI <https://www.python.org/dev/peps/pep-3333>`_ server implemented in `Rust <https://www.rust-lang.org/>`_.\n\nFeatures\n--------\n\n* Non-blocking read/write using `mio <https://github.com/tokio-rs/mio>`_\n* Request parsing using `httparse <https://github.com/seanmonstar/httparse>`_\n* `rust-cpython <https://github.com/dgrunwald/rust-cpython>`_ based Python interface\n* Worker pool based on `threadpool <https://github.com/rust-threadpool/rust-threadpool>`_\n* `PasteDeploy <https://pastedeploy.readthedocs.io/en/latest/>`_ entry point\n\nInstallation\n------------\n\nIf you are on Linux and use a recent Python version,\n\n.. code-block::\n\n $ pip install pyruvate\n\nis probably all you need to do.\n\nBinary Packages\n+++++++++++++++\n\n`manylinux_2_17 <https://peps.python.org/pep-0600/>`_ and `musllinux_1_1 <https://peps.python.org/pep-0656/>`_ wheels are available for the `x86_64` architecture and active Python 3 versions (currently 3.8-3.12).\n\nSource Installation\n+++++++++++++++++++\n\nOn 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 <https://doc.rust-lang.org/book/ch01-01-installation.html>`_ first.\n\nDevelopment Installation\n++++++++++++++++++++++++\n\n* Install `Rust <https://doc.rust-lang.org/book/ch01-01-installation.html>`__\n* Install and activate a Python 3 (>= 3.8) `virtualenv <https://docs.python.org/3/tutorial/venv.html>`_\n* Install `setuptools_rust <https://github.com/PyO3/setuptools-rust>`_ using pip::\n\n $ pip install setuptools_rust\n\n* Install Pyruvate, e.g. using pip::\n\n $ pip install -e git+https://gitlab.com/tschorr/pyruvate.git#egg=pyruvate[test]\n\nUsing Pyruvate in your WSGI application\n---------------------------------------\n\nFrom Python using a TCP port\n++++++++++++++++++++++++++++\n\nA hello world WSGI application using Pyruvate listening on 127.0.0.1:7878 and using 2 worker threads looks like this:\n\n.. code-block:: python\n\n import pyruvate\n\n def application(environ, start_response):\n \"\"\"Simplest possible application object\"\"\"\n status = '200 OK'\n response_headers = [('Content-type', 'text/plain')]\n start_response(status, response_headers, None)\n return [b\"Hello world!\\n\"]\n\n pyruvate.serve(application, \"127.0.0.1:7878\", 2)\n\nFrom Python using a Unix socket\n+++++++++++++++++++++++++++++++\n\nA hello world WSGI application using Pyruvate listening on unix:/tmp/pyruvate.socket and using 2 worker threads looks like this:\n\n.. code-block:: python\n\n import pyruvate\n\n def application(environ, start_response):\n \"\"\"Simplest possible application object\"\"\"\n status = '200 OK'\n response_headers = [('Content-type', 'text/plain')]\n start_response(status, response_headers, None)\n return [b\"Hello world!\\n\"]\n\n pyruvate.serve(application, \"/tmp/pyruvate.socket\", 2)\n\nUsing PasteDeploy\n+++++++++++++++++\n\nAgain listening on 127.0.0.1:7878 and using 2 worker threads::\n\n [server:main]\n use = egg:pyruvate#main\n socket = 127.0.0.1:7878\n workers = 2\n\nConfiguration Options\n+++++++++++++++++++++\n\nsocket\n Required: The TCP socket Pyruvate should bind to.\n `Pyruvate` also supports `systemd socket activation <https://www.freedesktop.org/software/systemd/man/systemd.socket.html>`_\n If you specify `None` as the socket value, `Pyruvate` will try to acquire a socket bound by `systemd`.\n\nworkers\n Required: Number of worker threads to use.\n\nasync_logging\n Optional: Log asynchronously using a dedicated thread.\n Defaults to `True`.\n\nchunked_transfer\n Optional: Whether to use chunked transfer encoding if no Content-Length header is present.\n Defaults to `False`.\n\nkeepalive_timeout\n Optional: Specify a timeout in integer seconds for keepalive connection.\n The persistent connection will be closed after the timeout expires.\n Defaults to 60 seconds.\n\nmax_number_headers\n Optional: Maximum number of request headers that will be parsed.\n If a request contains more headers than configured, request processing will stop with an error indicating an incomplete request.\n The default is 32 headers\n\nmax_reuse_count\n Optional: Specify how often to reuse an existing connection.\n Setting this parameter to 0 will effectively disable keep-alive connections.\n This is the default.\n\nqmon_warn_threshold\n Optional: Warning threshold for the number of requests in the request queue.\n A warning will be logged if the number of queued requests reaches this value.\n The value must be a positive integer.\n The default is `None` which disables the queue monitor.\n\nsend_timeout\n Optional: Time to wait for a client connection to become available for\n writing after EAGAIN, in seconds. Connections that do not receive data\n within this time are closed.\n The value must be a positive integer.\n The default is 60 seconds.\n\nLogging\n+++++++\n\nPyruvate uses the standard `Python logging facility <https://docs.python.org/3/library/logging.html>`_.\nThe logger name is `pyruvate`.\nSee the Python documentation (`logging <https://docs.python.org/3/library/logging.html>`_, `logging.config <https://docs.python.org/3/library/logging.config.html>`_) for configuration options.\n\nExample Configurations\n----------------------\n\nDjango\n++++++\n\nAfter installing Pyruvate in your Django virtualenv, create or modify your `wsgi.py` file (one worker listening on 127.0.0.1:8000):\n\n.. code-block:: python\n\n import os\n import pyruvate\n\n from django.core.wsgi import get_wsgi_application\n\n os.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"your_django_application.settings\")\n\n application = get_wsgi_application()\n\n pyruvate.serve(application, \"127.0.0.1:8000\", 1)\n\nYou can now start Django + Pyruvate with::\n\n $ python wsgi.py\n\nOverride settings by using the `DJANGO_SETTINGS_MODULE` environment variable when appropriate.\nTested with `Django 4.1.x, 3.2.x, 2.2.x <https://www.djangoproject.com/>`_.\n\nMapProxy\n++++++++\n\nFirst create a basic WSGI configuration following the `MapProxy deployment documentation <https://mapproxy.org/docs/latest/deployment.html#server-script>`_.\nThen modify `config.py` so it is using Pyruvate (2 workers listening on 127.0.0.1:8005):\n\n.. code-block:: python\n\n import os.path\n import pyruvate\n\n from mapproxy.wsgiapp import make_wsgi_app\n application = make_wsgi_app(r'/path/to/mapproxy/mapproxy.yaml')\n\n pyruvate.serve(application, \"127.0.0.1:8005\", 2)\n\nStart from your virtualenv::\n\n $ python config.py\n\nTested with `Mapproxy 1.15.x, 1.13.x, 1.12.x <https://mapproxy.org/>`_.\n\nPlone\n+++++\n\nUsing `pip`\n~~~~~~~~~~~\n\nAfter 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)::\n\n [server:main]\n use = egg:pyruvate#main\n socket = localhost:7878\n workers = 2\n\nUsing `zc.buildout`\n~~~~~~~~~~~~~~~~~~~\n\nUsing `zc.buildout <https://pypi.org/project/zc.buildout/>`_ and `plone.recipe.zope2instance <https://pypi.org/project/plone.recipe.zope2instance>`_ you can define an instance part using Pyruvate's `PasteDeploy <https://pastedeploy.readthedocs.io/en/latest/>`_ entry point::\n\n [instance]\n recipe = plone.recipe.zope2instance\n http-address = 127.0.0.1:8080\n eggs =\n Plone\n pyruvate\n wsgi-ini-template = ${buildout:directory}/templates/pyruvate.ini.in\n\nThe `server` section of the template provided with the `wsgi-ini-template <https://pypi.org/project/plone.recipe.zope2instance/#advanced-options>`_ option should look like this (3 workers listening on `http-address` as specified in the buildout `[instance]` part)::\n\n [server:main]\n use = egg:pyruvate#main\n socket = %(http_address)s\n workers = 3\n\nThere is a minimal buildout example configuration for Plone 5.2 in the `examples directory <https://gitlab.com/tschorr/pyruvate/-/tree/main/examples/plone52>`_ of the package.\n\nTested with `Plone 6.0.x, 5.2.x <https://plone.org/>`_.\n\nPyramid\n+++++++\n\nInstall Pyruvate in your Pyramid virtualenv using pip::\n\n $ pip install pyruvate\n\nModify 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)::\n\n [server:main]\n use = egg:pyruvate#main\n socket = 127.0.0.1:7878\n workers = 5\n\nStart your application as usual using `pserve`::\n\n $ pserve path/to/your/configfile.ini\n\nTested with `Pyramid 2.0, 1.10.x <https://trypyramid.com/>`_.\n\nRadicale\n++++++++\n\nYou can find an example configuration for `Radicale <https://radicale.org>`_ in the `examples directory <https://gitlab.com/tschorr/pyruvate/-/tree/main/examples/plone52>`_ of the package.\nTested with `Radicale 3.1.8 <https://radicale.org>`_.\n\nNginx settings\n++++++++++++++\n\nLike other WSGI servers Pyruvate should be used behind a reverse proxy, e.g. Nginx::\n\n ....\n location / {\n proxy_pass http://localhost:7878;\n ...\n }\n ...\n\nNginx doesn't use keepalive connections by default so you will need to `modify your configuration <https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive>`_ if you want persistent connections.\n\n\nChangelog\n=========\n\n1.3.0 (2024-07-04)\n------------------\n\n* Switch back to rust-cpython (`#29 <https://gitlab.com/tschorr/pyruvate/-/issues/29>`_)\n\n1.3.0-rc1 (2023-12-28)\n----------------------\n\n* Replace rust-cpython with PyO3 (`#28 <https://gitlab.com/tschorr/pyruvate/-/issues/28>`_)\n* Add support for Python 3.12\n* Drop support for Python 3.7\n\n1.2.2 (2023-07-02)\n------------------\n\n* Document Unix Domain Socket usage (`#27 <https://gitlab.com/tschorr/pyruvate/-/issues/27>`_)\n* Provide legacy manylinux wheel names (`#26 <https://gitlab.com/tschorr/pyruvate/-/issues/26>`_) \n\n1.2.1 (2022-12-22)\n------------------\n\n* Track and remove unfinished responses that did not otherwise error (`#23 <https://gitlab.com/tschorr/pyruvate/-/issues/23>`_)\n* Build musllinux_1_1 wheels (`#24 <https://gitlab.com/tschorr/pyruvate/-/issues/24>`_)\n\n1.2.0 (2022-10-26)\n------------------\n\n* Support Python 3.11 and discontinue Python 3.6, switch to manylinux2014 for building wheels (`#19 <https://gitlab.com/tschorr/pyruvate/-/issues/19>`_)\n* Add a request queue monitor (`#17 <https://gitlab.com/tschorr/pyruvate/-/issues/17>`_)\n* Remove blocking worker (`#18 <https://gitlab.com/tschorr/pyruvate/-/issues/18>`_)\n* Improve parsing of Content-Length header (`#20 <https://gitlab.com/tschorr/pyruvate/-/issues/20>`_)\n\n1.1.4 (2022-04-19)\n------------------\n\n* Fix handling of empty list responses (`#14 <https://gitlab.com/tschorr/pyruvate/-/issues/14>`_)\n* Support hostnames in socket addresses (`#15 <https://gitlab.com/tschorr/pyruvate/-/issues/15>`_)\n\n1.1.3 (2022-04-11)\n------------------\n\n* Simplify response writing and improve performance (`#12 <https://gitlab.com/tschorr/pyruvate/-/issues/12>`_)\n* Improve signal handling (`#13 <https://gitlab.com/tschorr/pyruvate/-/issues/13>`_)\n\n1.1.2 (2022-01-10)\n------------------\n\n* Migrate to Rust 2021\n* Use codecov binary uploader\n* Add CONTRIBUTING.rst\n* Fixed: The wrk benchmarking tool could make pyruvate hang when there is no Content-Length header (`#11 <https://gitlab.com/tschorr/pyruvate/-/issues/11>`_)\n\n1.1.1 (2021-10-12)\n------------------\n\n* Support Python 3.10\n\n1.1.0 (2021-09-14)\n------------------\n\n* Refactor FileWrapper and improve its performance\n* Increase the default maximum number of headers\n* Add `Radicale <https://radicale.org>`_ example configuration\n* Update development status \n\n1.0.3 (2021-06-05)\n------------------\n\n* HEAD request: Do not complain about content length mismatch (`#4 <https://gitlab.com/tschorr/pyruvate/-/issues/4>`_) \n* More appropriate log level for client side connection termination (`#5 <https://gitlab.com/tschorr/pyruvate/-/issues/5>`_)\n* Simplify request parsing\n\n1.0.2 (2021-05-02)\n------------------\n\n* Close connection and log an error in the case where the actual content length is\n less than the Content-Length header provided by the application\n* Fix readme\n\n1.0.1 (2021-04-28)\n------------------\n\n* Fix decoding of URLs that contain non-ascii characters\n* Raise Python exception when response contains objects other than bytestrings\n instead of simply logging the error.\n\n1.0.0 (2021-03-24)\n------------------\n\n* Improve query string handling\n\n0.9.2 (2021-01-30)\n------------------\n\n* Better support for HTTP 1.1 Expect/Continue\n* Improve documentation\n\n0.9.1 (2021-01-13)\n------------------\n\n* Improve GIL handling\n* Propagate worker thread name to Python logging\n* Do not report broken pipe as error\n* PasteDeploy entry point: fix option handling\n\n0.9.0 (2021-01-06)\n------------------\n\n* Reusable connections\n* Chunked transfer-encoding\n* Support macOS\n\n0.8.4 (2020-12-12)\n------------------\n\n* Lower CPU usage\n\n0.8.3 (2020-11-26)\n------------------\n\n* Clean wheel build directories\n* Fix some test isolation problems\n* Remove a println\n\n0.8.2 (2020-11-17)\n------------------\n\n* Fix blocksize handling for sendfile case\n* Format unix stream peer address\n* Use latest mio\n\n0.8.1 (2020-11-10)\n------------------\n\n* Receiver in non-blocking worker must not block when channel is empty\n\n0.8.0 (2020-11-07)\n------------------\n\n* Logging overhaul\n* New async_logging option\n* Some performance improvements\n* Support Python 3.9\n* Switch to manylinux2010 platform tag\n\n0.7.1 (2020-09-16)\n------------------\n\n* Raise Python exception when socket is unavailable\n* Add Pyramid configuration example in readme\n\n0.7.0 (2020-08-30)\n------------------\n\n* Use Python logging\n* Display server info on startup\n* Fix socket activation for unix domain sockets\n\n0.6.2 (2020-08-12)\n------------------\n\n* Improved logging\n* PasteDeploy entry point now also uses at most 24 headers by default\n\n0.6.1 (2020-08-10)\n------------------\n\n* Improve request parsing\n* Increase default maximum number of headers to 24\n\n0.6.0 (2020-07-29)\n------------------\n\n* Support unix domain sockets\n* Improve sendfile usage\n\n0.5.3 (2020-07-15)\n------------------\n\n* Fix testing for completed sendfile call in case of EAGAIN\n\n0.5.2 (2020-07-15)\n------------------\n\n* Fix testing for completed response in case of EAGAIN\n* Cargo update\n\n0.5.1 (2020-07-07)\n------------------\n\n* Fix handling of read events\n* Fix changelog\n* Cargo update\n* 'Interrupted' error is not a todo\n* Remove unused code\n\n0.5.0 (2020-06-07)\n------------------\n\n* Add support for systemd socket activation\n\n0.4.0 (2020-06-29)\n------------------\n\n* Add a new worker that does nonblocking write\n* Add default arguments\n* Add option to configure maximum number of request headers\n* Add Via header\n\n0.3.0 (2020-06-16)\n------------------\n\n* Switch to rust-cpython\n* Fix passing of tcp connections to worker threads\n\n0.2.0 (2020-03-10)\n------------------\n\n* Added some Python tests (using py.test and tox)\n* Improve handling of HTTP headers\n* Respect content length header when using sendfile\n\n0.1.0 (2020-02-10)\n------------------\n\n* Initial release\n",
"bugtrack_url": null,
"license": null,
"summary": "WSGI server implemented in Rust.",
"version": "1.3.0",
"project_urls": {
"Homepage": "https://gitlab.com/tschorr/pyruvate"
},
"split_keywords": [
"wsgi"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "aa565fab2c5acef02a06c61dd2c3ec967ed0989f9ce49ee36e5b31bd0ff11a89",
"md5": "a61bedc890c98c772ce961e133d7c638",
"sha256": "6bb50688e4a71efff634efde67a0852b8a389e27ad5fe160d76f6202ffe2667b"
},
"downloads": -1,
"filename": "pyruvate-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "a61bedc890c98c772ce961e133d7c638",
"packagetype": "bdist_wheel",
"python_version": "cp310",
"requires_python": null,
"size": 474872,
"upload_time": "2024-07-04T08:48:17",
"upload_time_iso_8601": "2024-07-04T08:48:17.359280Z",
"url": "https://files.pythonhosted.org/packages/aa/56/5fab2c5acef02a06c61dd2c3ec967ed0989f9ce49ee36e5b31bd0ff11a89/pyruvate-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "449bd9f6dd29f3515eddde7aee779319b12ba000ef5c031eebcd2de23ed69b88",
"md5": "8864310c5f1622e5ba178b92482b39d6",
"sha256": "8dd809a6311e3f0255cb43853c5822176fc3bc9efe31876e9c562cbd4cae2dcc"
},
"downloads": -1,
"filename": "pyruvate-1.3.0-cp310-cp310-musllinux_1_1_x86_64.whl",
"has_sig": false,
"md5_digest": "8864310c5f1622e5ba178b92482b39d6",
"packagetype": "bdist_wheel",
"python_version": "cp310",
"requires_python": null,
"size": 472027,
"upload_time": "2024-07-04T08:48:22",
"upload_time_iso_8601": "2024-07-04T08:48:22.256367Z",
"url": "https://files.pythonhosted.org/packages/44/9b/d9f6dd29f3515eddde7aee779319b12ba000ef5c031eebcd2de23ed69b88/pyruvate-1.3.0-cp310-cp310-musllinux_1_1_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "2f175076a641591d161e52b39dc5dcb60f5abf8e4467695f95c158559088c3b4",
"md5": "d9c5dbce0f6fbd4ded1210872cbad3af",
"sha256": "0e87f507fad4d6eb16e8f9a07bf82bf107b0b09fbe582c8c579c6053cc0fdadd"
},
"downloads": -1,
"filename": "pyruvate-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "d9c5dbce0f6fbd4ded1210872cbad3af",
"packagetype": "bdist_wheel",
"python_version": "cp311",
"requires_python": null,
"size": 474872,
"upload_time": "2024-07-04T08:48:24",
"upload_time_iso_8601": "2024-07-04T08:48:24.846526Z",
"url": "https://files.pythonhosted.org/packages/2f/17/5076a641591d161e52b39dc5dcb60f5abf8e4467695f95c158559088c3b4/pyruvate-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "04cd2e67c6340412a9885c29f64d01fa1c95005702f3d7b5ba0bccb5ffbca9dc",
"md5": "5fa5ac82a416c80d81548374a787d8c7",
"sha256": "6eb4b60215d3bdf75cbcfc536a6d33cafb70f1d8136d284dfa0a0238cffb2de7"
},
"downloads": -1,
"filename": "pyruvate-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl",
"has_sig": false,
"md5_digest": "5fa5ac82a416c80d81548374a787d8c7",
"packagetype": "bdist_wheel",
"python_version": "cp311",
"requires_python": null,
"size": 472031,
"upload_time": "2024-07-04T08:48:27",
"upload_time_iso_8601": "2024-07-04T08:48:27.076191Z",
"url": "https://files.pythonhosted.org/packages/04/cd/2e67c6340412a9885c29f64d01fa1c95005702f3d7b5ba0bccb5ffbca9dc/pyruvate-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "374a4ce29946b3633519f1f634f8988dd89bfeb14a7f41e8a3f7d54f24149177",
"md5": "fcd89339d9ccdc2ec2ff63308eda9a8a",
"sha256": "9904ff88213e1725a4fe8ae906fd602520e7e438ac13d4a9f18ceacbcbbd9bfb"
},
"downloads": -1,
"filename": "pyruvate-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "fcd89339d9ccdc2ec2ff63308eda9a8a",
"packagetype": "bdist_wheel",
"python_version": "cp312",
"requires_python": null,
"size": 474707,
"upload_time": "2024-07-04T08:48:30",
"upload_time_iso_8601": "2024-07-04T08:48:30.020894Z",
"url": "https://files.pythonhosted.org/packages/37/4a/4ce29946b3633519f1f634f8988dd89bfeb14a7f41e8a3f7d54f24149177/pyruvate-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "aa3831d84229c957a1ff0cf662f17ba496f4c1f24aa66a1bc8fd3021331b3aa1",
"md5": "d490a5481b93ab1206b5ca92e6bcc436",
"sha256": "515cfa85863f793a9be72d95d2dce4c907a94af1fdfbfc24bfcf572907df8e18"
},
"downloads": -1,
"filename": "pyruvate-1.3.0-cp312-cp312-musllinux_1_1_x86_64.whl",
"has_sig": false,
"md5_digest": "d490a5481b93ab1206b5ca92e6bcc436",
"packagetype": "bdist_wheel",
"python_version": "cp312",
"requires_python": null,
"size": 472113,
"upload_time": "2024-07-04T08:48:32",
"upload_time_iso_8601": "2024-07-04T08:48:32.427829Z",
"url": "https://files.pythonhosted.org/packages/aa/38/31d84229c957a1ff0cf662f17ba496f4c1f24aa66a1bc8fd3021331b3aa1/pyruvate-1.3.0-cp312-cp312-musllinux_1_1_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "df0a1c05eaf3328554fecd42513501ff36f0cce8661c6e3205645fce5a3bf764",
"md5": "2d7dea2ba3d145102c1bedff58d4378f",
"sha256": "a99ba7e6c9a02eb92ac383193bfff11e30b6015d6ed132510f826cdfb6fa8741"
},
"downloads": -1,
"filename": "pyruvate-1.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "2d7dea2ba3d145102c1bedff58d4378f",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": null,
"size": 474737,
"upload_time": "2024-07-04T08:48:35",
"upload_time_iso_8601": "2024-07-04T08:48:35.009495Z",
"url": "https://files.pythonhosted.org/packages/df/0a/1c05eaf3328554fecd42513501ff36f0cce8661c6e3205645fce5a3bf764/pyruvate-1.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "2f02426249090c8a21a1506d46cc079c10a0a74f1b734cd5cd3aa14a76570e08",
"md5": "13ed9907648968608a44ee81949bae86",
"sha256": "ebf7f880c71058d48c96634296412dd71e347372a4087b2ad73084d43c853253"
},
"downloads": -1,
"filename": "pyruvate-1.3.0-cp38-cp38-musllinux_1_1_x86_64.whl",
"has_sig": false,
"md5_digest": "13ed9907648968608a44ee81949bae86",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": null,
"size": 472027,
"upload_time": "2024-07-04T08:48:37",
"upload_time_iso_8601": "2024-07-04T08:48:37.285432Z",
"url": "https://files.pythonhosted.org/packages/2f/02/426249090c8a21a1506d46cc079c10a0a74f1b734cd5cd3aa14a76570e08/pyruvate-1.3.0-cp38-cp38-musllinux_1_1_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "fd53c4f4ad06ee666c813a9c663bd413643d20620ca6765416c22c5c18347da9",
"md5": "ef73dde535cb028c3a230c2d606f1b3e",
"sha256": "8dcbad094d1ca6bec8bc7083b8a9ab28fa879f9b6355433484b2f18cc53c4503"
},
"downloads": -1,
"filename": "pyruvate-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "ef73dde535cb028c3a230c2d606f1b3e",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": null,
"size": 474755,
"upload_time": "2024-07-04T08:48:40",
"upload_time_iso_8601": "2024-07-04T08:48:40.006911Z",
"url": "https://files.pythonhosted.org/packages/fd/53/c4f4ad06ee666c813a9c663bd413643d20620ca6765416c22c5c18347da9/pyruvate-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "7ba32cadf846511ac5b87b353b9473f488e5e9806c4962856bee7b979915c570",
"md5": "761e3482dbcd182a63d8e5cd8cf57293",
"sha256": "023203dc5d5260c527804aa59259dacbda0a794d8d367ed14a09a4f3b14f2910"
},
"downloads": -1,
"filename": "pyruvate-1.3.0-cp39-cp39-musllinux_1_1_x86_64.whl",
"has_sig": false,
"md5_digest": "761e3482dbcd182a63d8e5cd8cf57293",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": null,
"size": 471993,
"upload_time": "2024-07-04T08:48:41",
"upload_time_iso_8601": "2024-07-04T08:48:41.794898Z",
"url": "https://files.pythonhosted.org/packages/7b/a3/2cadf846511ac5b87b353b9473f488e5e9806c4962856bee7b979915c570/pyruvate-1.3.0-cp39-cp39-musllinux_1_1_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "72e29050c8f85a55305b716967b75d69e377d355940cd2a05c9722135140232a",
"md5": "b0334b28d7074cef1299343c4dfa8556",
"sha256": "7debedaa98cb65858ddfff4193a115ca3b207aef803bdb09ef93f854d5ecfdba"
},
"downloads": -1,
"filename": "pyruvate-1.3.0.tar.gz",
"has_sig": false,
"md5_digest": "b0334b28d7074cef1299343c4dfa8556",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 61015,
"upload_time": "2024-07-04T08:48:44",
"upload_time_iso_8601": "2024-07-04T08:48:44.393839Z",
"url": "https://files.pythonhosted.org/packages/72/e2/9050c8f85a55305b716967b75d69e377d355940cd2a05c9722135140232a/pyruvate-1.3.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-07-04 08:48:44",
"github": false,
"gitlab": true,
"bitbucket": false,
"codeberg": false,
"gitlab_user": "tschorr",
"gitlab_project": "pyruvate",
"lcname": "pyruvate"
}