From 11b478aa97e11494a4ecdcb7c89d3500ad4803f1 Mon Sep 17 00:00:00 2001 From: c00lerxo Date: Tue, 11 Dec 2018 13:01:20 +0100 Subject: [PATCH] Add README --- README.MD | 2 + venv/bin/gunicorn | 11 + venv/bin/gunicorn_paster | 11 + .../gunicorn-19.9.0.dist-info/INSTALLER | 1 + .../gunicorn-19.9.0.dist-info/LICENSE.txt | 23 + .../gunicorn-19.9.0.dist-info/METADATA | 111 + .../gunicorn-19.9.0.dist-info/RECORD | 89 + .../gunicorn-19.9.0.dist-info/WHEEL | 6 + .../entry_points.txt | 8 + .../gunicorn-19.9.0.dist-info/top_level.txt | 1 + .../site-packages/gunicorn/__init__.py | 8 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 377 bytes .../__pycache__/_compat.cpython-36.pyc | Bin 0 -> 7674 bytes .../__pycache__/arbiter.cpython-36.pyc | Bin 0 -> 16647 bytes .../argparse_compat.cpython-36.pyc | Bin 0 -> 58933 bytes .../__pycache__/config.cpython-36.pyc | Bin 0 -> 60003 bytes .../gunicorn/__pycache__/debug.cpython-36.pyc | Bin 0 -> 1961 bytes .../__pycache__/errors.cpython-36.pyc | Bin 0 -> 1015 bytes .../__pycache__/glogging.cpython-36.pyc | Bin 0 -> 11425 bytes .../__pycache__/pidfile.cpython-36.pyc | Bin 0 -> 2234 bytes .../__pycache__/reloader.cpython-36.pyc | Bin 0 -> 3956 bytes .../__pycache__/selectors.cpython-36.pyc | Bin 0 -> 17390 bytes .../gunicorn/__pycache__/six.cpython-36.pyc | Bin 0 -> 22115 bytes .../gunicorn/__pycache__/sock.cpython-36.pyc | Bin 0 -> 5886 bytes .../__pycache__/systemd.cpython-36.pyc | Bin 0 -> 1351 bytes .../gunicorn/__pycache__/util.cpython-36.pyc | Bin 0 -> 11847 bytes .../site-packages/gunicorn/_compat.py | 298 +++ .../site-packages/gunicorn/app/__init__.py | 4 + .../app/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 140 bytes .../app/__pycache__/base.cpython-36.pyc | Bin 0 -> 6021 bytes .../app/__pycache__/pasterapp.cpython-36.pyc | Bin 0 -> 5614 bytes .../app/__pycache__/wsgiapp.cpython-36.pyc | Bin 0 -> 1997 bytes .../site-packages/gunicorn/app/base.py | 223 ++ .../site-packages/gunicorn/app/pasterapp.py | 209 ++ .../site-packages/gunicorn/app/wsgiapp.py | 65 + .../site-packages/gunicorn/arbiter.py | 646 +++++ .../site-packages/gunicorn/argparse_compat.py | 2362 +++++++++++++++++ .../site-packages/gunicorn/config.py | 1950 ++++++++++++++ .../python3.6/site-packages/gunicorn/debug.py | 69 + .../site-packages/gunicorn/errors.py | 29 + .../site-packages/gunicorn/glogging.py | 478 ++++ .../site-packages/gunicorn/http/__init__.py | 9 + .../http/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 306 bytes .../http/__pycache__/_sendfile.cpython-36.pyc | Bin 0 -> 1338 bytes .../http/__pycache__/body.cpython-36.pyc | Bin 0 -> 6417 bytes .../http/__pycache__/errors.cpython-36.pyc | Bin 0 -> 5836 bytes .../http/__pycache__/message.cpython-36.pyc | Bin 0 -> 8703 bytes .../http/__pycache__/parser.cpython-36.pyc | Bin 0 -> 1301 bytes .../http/__pycache__/unreader.cpython-36.pyc | Bin 0 -> 2423 bytes .../http/__pycache__/wsgi.cpython-36.pyc | Bin 0 -> 9232 bytes .../site-packages/gunicorn/http/_sendfile.py | 67 + .../site-packages/gunicorn/http/body.py | 259 ++ .../site-packages/gunicorn/http/errors.py | 120 + .../site-packages/gunicorn/http/message.py | 363 +++ .../site-packages/gunicorn/http/parser.py | 51 + .../site-packages/gunicorn/http/unreader.py | 80 + .../site-packages/gunicorn/http/wsgi.py | 411 +++ .../gunicorn/instrument/__init__.py | 0 .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 147 bytes .../__pycache__/statsd.cpython-36.pyc | Bin 0 -> 4469 bytes .../gunicorn/instrument/statsd.py | 123 + .../site-packages/gunicorn/pidfile.py | 86 + .../site-packages/gunicorn/reloader.py | 132 + .../site-packages/gunicorn/selectors.py | 592 +++++ .../python3.6/site-packages/gunicorn/six.py | 762 ++++++ .../python3.6/site-packages/gunicorn/sock.py | 209 ++ .../site-packages/gunicorn/systemd.py | 45 + .../python3.6/site-packages/gunicorn/util.py | 557 ++++ .../gunicorn/workers/__init__.py | 22 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 634 bytes .../__pycache__/_gaiohttp.cpython-36.pyc | Bin 0 -> 4881 bytes .../workers/__pycache__/base.cpython-36.pyc | Bin 0 -> 7495 bytes .../__pycache__/base_async.cpython-36.pyc | Bin 0 -> 3844 bytes .../__pycache__/gaiohttp.cpython-36.pyc | Bin 0 -> 726 bytes .../__pycache__/geventlet.cpython-36.pyc | Bin 0 -> 4626 bytes .../__pycache__/ggevent.cpython-36.pyc | Bin 0 -> 6662 bytes .../__pycache__/gthread.cpython-36.pyc | Bin 0 -> 8753 bytes .../__pycache__/gtornado.cpython-36.pyc | Bin 0 -> 4059 bytes .../workers/__pycache__/sync.cpython-36.pyc | Bin 0 -> 5077 bytes .../__pycache__/workertmp.cpython-36.pyc | Bin 0 -> 1724 bytes .../gunicorn/workers/_gaiohttp.py | 168 ++ .../site-packages/gunicorn/workers/base.py | 264 ++ .../gunicorn/workers/base_async.py | 147 + .../gunicorn/workers/gaiohttp.py | 27 + .../gunicorn/workers/geventlet.py | 148 ++ .../site-packages/gunicorn/workers/ggevent.py | 246 ++ .../site-packages/gunicorn/workers/gthread.py | 367 +++ .../gunicorn/workers/gtornado.py | 146 + .../site-packages/gunicorn/workers/sync.py | 208 ++ .../gunicorn/workers/workertmp.py | 56 + 90 files changed, 12269 insertions(+) create mode 100644 README.MD create mode 100755 venv/bin/gunicorn create mode 100755 venv/bin/gunicorn_paster create mode 100644 venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/LICENSE.txt create mode 100644 venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/METADATA create mode 100644 venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/RECORD create mode 100644 venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/WHEEL create mode 100644 venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/entry_points.txt create mode 100644 venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__init__.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/_compat.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/arbiter.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/argparse_compat.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/config.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/debug.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/errors.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/glogging.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/pidfile.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/reloader.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/selectors.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/six.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/sock.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/systemd.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/__pycache__/util.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/_compat.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/app/__init__.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/app/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/app/__pycache__/base.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/app/__pycache__/pasterapp.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/app/__pycache__/wsgiapp.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/app/base.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/app/pasterapp.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/arbiter.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/argparse_compat.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/config.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/debug.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/errors.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/glogging.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/__init__.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/_sendfile.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/body.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/errors.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/message.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/parser.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/unreader.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/wsgi.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/_sendfile.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/body.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/errors.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/message.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/parser.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/unreader.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/http/wsgi.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/instrument/__init__.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/instrument/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/instrument/__pycache__/statsd.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/instrument/statsd.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/pidfile.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/reloader.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/selectors.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/six.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/sock.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/systemd.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/util.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__init__.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/__init__.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/_gaiohttp.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/base.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/base_async.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/gaiohttp.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/geventlet.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/ggevent.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/gthread.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/gtornado.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/sync.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/workertmp.cpython-36.pyc create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/_gaiohttp.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/base.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/base_async.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/gaiohttp.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/geventlet.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/ggevent.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/gthread.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/gtornado.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/sync.py create mode 100644 venv/lib/python3.6/site-packages/gunicorn/workers/workertmp.py diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..4a59965 --- /dev/null +++ b/README.MD @@ -0,0 +1,2 @@ +# Cat or Not - first prototype +### Monday 15:30 group diff --git a/venv/bin/gunicorn b/venv/bin/gunicorn new file mode 100755 index 0000000..4f91070 --- /dev/null +++ b/venv/bin/gunicorn @@ -0,0 +1,11 @@ +#!/home/c00ler/cat_or_not/venv/bin/python3 + +# -*- coding: utf-8 -*- +import re +import sys + +from gunicorn.app.wsgiapp import run + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(run()) diff --git a/venv/bin/gunicorn_paster b/venv/bin/gunicorn_paster new file mode 100755 index 0000000..df4251e --- /dev/null +++ b/venv/bin/gunicorn_paster @@ -0,0 +1,11 @@ +#!/home/c00ler/cat_or_not/venv/bin/python3 + +# -*- coding: utf-8 -*- +import re +import sys + +from gunicorn.app.pasterapp import run + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(run()) diff --git a/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/INSTALLER b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/LICENSE.txt b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..65865a9 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/LICENSE.txt @@ -0,0 +1,23 @@ +2009-2018 (c) BenoƮt Chesneau +2009-2015 (c) Paul J. Davis + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/METADATA b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/METADATA new file mode 100644 index 0000000..148e500 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/METADATA @@ -0,0 +1,111 @@ +Metadata-Version: 2.1 +Name: gunicorn +Version: 19.9.0 +Summary: WSGI HTTP Server for UNIX +Home-page: http://gunicorn.org +Author: Benoit Chesneau +Author-email: benoitc@e-engura.com +License: MIT +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Other Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: POSIX +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Topic :: Internet +Classifier: Topic :: Utilities +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Server +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Requires-Python: >=2.6, !=3.0.*, !=3.1.* +Provides-Extra: tornado +Provides-Extra: gthread +Provides-Extra: eventlet +Provides-Extra: gevent +Provides-Extra: eventlet +Requires-Dist: eventlet (>=0.9.7); extra == 'eventlet' +Provides-Extra: gevent +Requires-Dist: gevent (>=0.13); extra == 'gevent' +Provides-Extra: gthread +Provides-Extra: tornado +Requires-Dist: tornado (>=0.2); extra == 'tornado' + +Gunicorn +-------- + +.. image:: https://img.shields.io/pypi/v/gunicorn.svg?style=flat + :alt: PyPI version + :target: https://pypi.python.org/pypi/gunicorn + +.. image:: https://img.shields.io/pypi/pyversions/gunicorn.svg + :alt: Supported Python versions + :target: https://pypi.python.org/pypi/gunicorn + +.. image:: https://travis-ci.org/benoitc/gunicorn.svg?branch=master + :alt: Build Status + :target: https://travis-ci.org/benoitc/gunicorn + +Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. It's a pre-fork +worker model ported from Ruby's Unicorn_ project. The Gunicorn server is broadly +compatible with various web frameworks, simply implemented, light on server +resource usage, and fairly speedy. + +Feel free to join us in `#gunicorn`_ on Freenode_. + +Documentation +------------- + +The documentation is hosted at http://docs.gunicorn.org. + +Installation +------------ + +Gunicorn requires **Python 2.x >= 2.6** or **Python 3.x >= 3.2**. + +Install from PyPI:: + + $ pip install gunicorn + + +Usage +----- + +Basic usage:: + + $ gunicorn [OPTIONS] APP_MODULE + +Where ``APP_MODULE`` is of the pattern ``$(MODULE_NAME):$(VARIABLE_NAME)``. The +module name can be a full dotted path. The variable name refers to a WSGI +callable that should be found in the specified module. + +Example with test app:: + + $ cd examples + $ gunicorn --workers=2 test:app + + +License +------- + +Gunicorn is released under the MIT License. See the LICENSE_ file for more +details. + +.. _Unicorn: https://bogomips.org/unicorn/ +.. _`#gunicorn`: https://webchat.freenode.net/?channels=gunicorn +.. _Freenode: https://freenode.net/ +.. _LICENSE: https://github.com/benoitc/gunicorn/blob/master/LICENSE + + diff --git a/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/RECORD b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/RECORD new file mode 100644 index 0000000..6166ec9 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/RECORD @@ -0,0 +1,89 @@ +gunicorn/__init__.py,sha256=kRm2HQJytwQi-xmyUfM0cOMyls23DfFDgGFbGI2Gj68,255 +gunicorn/_compat.py,sha256=5cXb6vMfVzInDq-AHNyZfsK-UG5NetDn62nPfqylHSU,9355 +gunicorn/arbiter.py,sha256=AbJNSFnTmx9Qd-vZAqEH3y5fz8ydPmyli_BERNIwdyE,20158 +gunicorn/argparse_compat.py,sha256=gsHDGwo4BSJWHdiaEXy0Emr96NKC0LDYmK5nB7PE8Qc,87791 +gunicorn/config.py,sha256=wYeAJFMweU3FXNF4BdfgZzPC94vUXUnuYgI6lNk-5_U,53420 +gunicorn/debug.py,sha256=UUw-eteLEm_OQ98D6K3XtDjx4Dya2H35zdiu8z7F7uc,2289 +gunicorn/errors.py,sha256=JlDBjag90gMiRwLHG3xzEJzDOntSl1iM32R277-U6j0,919 +gunicorn/glogging.py,sha256=bvnX-sky6HgqJor2JZ9VKZZzT4uh_yOgknkYegB7D7Y,15581 +gunicorn/pidfile.py,sha256=_69tsfF1aHklrMrJe2sHERovMduRByVTv99my7yQ874,2357 +gunicorn/reloader.py,sha256=CPNfYAAvJHazX3NAM7qysSRt0fpiHBGPqBlB0tYKhxs,3839 +gunicorn/selectors.py,sha256=14_UESrpE3AQKXWKeeAUG9vBTzJ0yTYDGtEo6xOtlDY,18997 +gunicorn/six.py,sha256=6N-6RCENPfBtMpN5UmgDfDKmJebbbuPu_Dk3Zf8ngww,27344 +gunicorn/sock.py,sha256=gX2FsdsOGMCtSHbDXn7lsiYYYRc3roQklIJLip1oZQo,6019 +gunicorn/systemd.py,sha256=ffhv17cdv-hDeFAJi1eAVtJskkVciV6cQU75Q2oplqg,1362 +gunicorn/util.py,sha256=Ns_a8Pf7MkaEi0KbV3GsP9aVQ2a_S45EjSE6Iyg2tYU,16229 +gunicorn/app/__init__.py,sha256=GuqstqdkizeV4HRbd8aGMBn0Q8IDOyRU1wMMNqNe5GY,127 +gunicorn/app/base.py,sha256=LKxyziLMPNlK3qm6dPMieELBqfLfmwBFnn9SB-KBogE,6652 +gunicorn/app/pasterapp.py,sha256=AGzZnUpcpw8O8KrizxTgdJBZ4lQdrHgsV0gdx7FVTs8,6046 +gunicorn/app/wsgiapp.py,sha256=ny71qjegQHl_bGMjNfq_aemPrmGEpH2bMRIdph6bj4Q,1870 +gunicorn/http/__init__.py,sha256=b4TF3x5F0VYOPTOeNYwRGR1EYHBaPMhZRMoNeuD5-n0,277 +gunicorn/http/_sendfile.py,sha256=Eqd-s3HlvLuyfGjqaH_Jk72cAtEV8hQv5tb1M1AqcBU,2217 +gunicorn/http/body.py,sha256=MmlZpj_6oRPj3oPVSMQZr0X3KH6ikntxDnVcLgfekZs,7345 +gunicorn/http/errors.py,sha256=sNjF2lm4m2qyZ9l95_U33FRxPXpxXzjnZyYqWS-hxd4,2850 +gunicorn/http/message.py,sha256=G5po0upwbrTyIggb_IEAItIjSi_aDoWYLPQ62o8pOI4,12257 +gunicorn/http/parser.py,sha256=IRMvp0veP4wL8Z4vgNV72CPydCNPdNNIy9u-DlDvvSo,1294 +gunicorn/http/unreader.py,sha256=s4kDW5euiJPsDuHzCqFXUtHCApqIxpShb9dtAyjJw9Y,2019 +gunicorn/http/wsgi.py,sha256=SETzcFoLggub2aMuGduTVELBwJGg9YvvDbkiFbugkwU,12856 +gunicorn/instrument/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +gunicorn/instrument/statsd.py,sha256=5xueDuTZMFtmS8ayGT4sU_OyB9qkEv4Agk-eJwAmhJM,4434 +gunicorn/workers/__init__.py,sha256=arPaAM8HxcK39L2dmDzmMhpK9bsyLOJymuCcBz_qqw0,774 +gunicorn/workers/_gaiohttp.py,sha256=llho90CjwpeAB9ehrYeGmD9VZZAPdcNpVwnrBA3GEZA,5079 +gunicorn/workers/base.py,sha256=nzo4KfCQkO3Y2HKuKVk-xInZUiYay_A5B9e_9NVXU28,9121 +gunicorn/workers/base_async.py,sha256=54VkS3S_wrFD7v3jInhFfkeBhaPnV5UN-cu-i5MoXkc,5575 +gunicorn/workers/gaiohttp.py,sha256=3rhXky6APkhI0D9nwXlogLo_Jd9v98CiEuCy9inzCU4,823 +gunicorn/workers/geventlet.py,sha256=mE-Zw3zh8lOZVaprXcfaoBMmwKeDj6sZzdjmgIsvHXw,4258 +gunicorn/workers/ggevent.py,sha256=OV5KCJ3qlJP5E46sjyWQKGbQ5xGR2SOrZlEtLhIB89s,7412 +gunicorn/workers/gthread.py,sha256=HIoWuylHZfH1wlSh4eZ8wxo1kQ5abvdUaFfKfIsgQvI,12009 +gunicorn/workers/gtornado.py,sha256=LtBWnEX7MNpeGX-YmlBoV1_OOhjkdytFmt1pzOlRPZk,5044 +gunicorn/workers/sync.py,sha256=_vd1JATNLG4MgJppNJG5KWBIzLGYqRzhEAQVz9H11LI,7153 +gunicorn/workers/workertmp.py,sha256=6QINPBrriLvezgkC_hclOOeXLi_owMt_SOA5KPEIN-A,1459 +gunicorn-19.9.0.dist-info/LICENSE.txt,sha256=eJ_hG5Lhyr-890S1_MOSyb1cZ5hgOk6J-SW2M3mE0d8,1136 +gunicorn-19.9.0.dist-info/METADATA,sha256=SBjzTcJcbKUR9ev_rvypyWJYU0qgHvm8KzgfG6FtniE,3388 +gunicorn-19.9.0.dist-info/RECORD,, +gunicorn-19.9.0.dist-info/WHEEL,sha256=gduuPyBvFJQSQ0zdyxF7k0zynDXbIbvg5ZBHoXum5uk,110 +gunicorn-19.9.0.dist-info/entry_points.txt,sha256=XeFINKRdSUKwJwaVSolO24PuV_YeO71IMF-rOra5JO8,184 +gunicorn-19.9.0.dist-info/top_level.txt,sha256=cdMaa2yhxb8do-WioY9qRHUCfwf55YztjwQCncaInoE,9 +../../../bin/gunicorn,sha256=7yu1I7Rtn3C9Bl_xSluXZLIB0F6-0oADZ_RfNlEwpLg,245 +../../../bin/gunicorn_paster,sha256=GZ9v1m5WHSh7Yup93ysPfhsFUDkSsv-YN-tXToR1k9k,247 +gunicorn-19.9.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +gunicorn/__pycache__/selectors.cpython-36.pyc,, +gunicorn/__pycache__/_compat.cpython-36.pyc,, +gunicorn/__pycache__/__init__.cpython-36.pyc,, +gunicorn/__pycache__/systemd.cpython-36.pyc,, +gunicorn/__pycache__/pidfile.cpython-36.pyc,, +gunicorn/__pycache__/six.cpython-36.pyc,, +gunicorn/__pycache__/util.cpython-36.pyc,, +gunicorn/__pycache__/glogging.cpython-36.pyc,, +gunicorn/__pycache__/errors.cpython-36.pyc,, +gunicorn/__pycache__/debug.cpython-36.pyc,, +gunicorn/__pycache__/config.cpython-36.pyc,, +gunicorn/__pycache__/sock.cpython-36.pyc,, +gunicorn/__pycache__/reloader.cpython-36.pyc,, +gunicorn/__pycache__/argparse_compat.cpython-36.pyc,, +gunicorn/__pycache__/arbiter.cpython-36.pyc,, +gunicorn/app/__pycache__/base.cpython-36.pyc,, +gunicorn/app/__pycache__/pasterapp.cpython-36.pyc,, +gunicorn/app/__pycache__/__init__.cpython-36.pyc,, +gunicorn/app/__pycache__/wsgiapp.cpython-36.pyc,, +gunicorn/http/__pycache__/parser.cpython-36.pyc,, +gunicorn/http/__pycache__/__init__.cpython-36.pyc,, +gunicorn/http/__pycache__/message.cpython-36.pyc,, +gunicorn/http/__pycache__/body.cpython-36.pyc,, +gunicorn/http/__pycache__/_sendfile.cpython-36.pyc,, +gunicorn/http/__pycache__/wsgi.cpython-36.pyc,, +gunicorn/http/__pycache__/errors.cpython-36.pyc,, +gunicorn/http/__pycache__/unreader.cpython-36.pyc,, +gunicorn/instrument/__pycache__/__init__.cpython-36.pyc,, +gunicorn/instrument/__pycache__/statsd.cpython-36.pyc,, +gunicorn/workers/__pycache__/base_async.cpython-36.pyc,, +gunicorn/workers/__pycache__/base.cpython-36.pyc,, +gunicorn/workers/__pycache__/gaiohttp.cpython-36.pyc,, +gunicorn/workers/__pycache__/workertmp.cpython-36.pyc,, +gunicorn/workers/__pycache__/geventlet.cpython-36.pyc,, +gunicorn/workers/__pycache__/gtornado.cpython-36.pyc,, +gunicorn/workers/__pycache__/sync.cpython-36.pyc,, +gunicorn/workers/__pycache__/__init__.cpython-36.pyc,, +gunicorn/workers/__pycache__/_gaiohttp.cpython-36.pyc,, +gunicorn/workers/__pycache__/gthread.cpython-36.pyc,, +gunicorn/workers/__pycache__/ggevent.cpython-36.pyc,, diff --git a/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/WHEEL b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/WHEEL new file mode 100644 index 0000000..1316c41 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.31.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/entry_points.txt b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/entry_points.txt new file mode 100644 index 0000000..d5b5aa1 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/entry_points.txt @@ -0,0 +1,8 @@ + + [console_scripts] + gunicorn=gunicorn.app.wsgiapp:run + gunicorn_paster=gunicorn.app.pasterapp:run + + [paste.server_runner] + main=gunicorn.app.pasterapp:paste_server + \ No newline at end of file diff --git a/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/top_level.txt b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/top_level.txt new file mode 100644 index 0000000..8f22dcc --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn-19.9.0.dist-info/top_level.txt @@ -0,0 +1 @@ +gunicorn diff --git a/venv/lib/python3.6/site-packages/gunicorn/__init__.py b/venv/lib/python3.6/site-packages/gunicorn/__init__.py new file mode 100644 index 0000000..7820479 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +version_info = (19, 9, 0) +__version__ = ".".join([str(v) for v in version_info]) +SERVER_SOFTWARE = "gunicorn/%s" % __version__ diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/__init__.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..642097d643c04addd93b8d487c208ac85f96759a GIT binary patch literal 377 zcmYLFOHRWu6nsw7U)rh*Doy~4ESe$;5{s$=L0E#6Adn)ECF)SQq)BQgl|W@xF2D&` za|cf1EvsIk8+ZoMK552dYy3QZy=K$?d~e-;1HczzT_&nygxo=5)ex9DCd^``SBT&Y z7Gyy}gOwv=8$y#+0&r@}EwnCcnh>CfJ0x0Q83+q;ijZxj5zOJP@j`N#W2ZM`Y6b8C z4pFAeg<~kg-BV;HN@)G1)nPZEWZjHso%@2vtP>v{h{thnzdI@tK1@Zj5tQ5Cd7|o+ r{-4sY6?p!&=hNWw{Oab!_cnR|l%9pE_9rPT;%HFDozzpXNSpis_MKM% literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/_compat.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32f8ff6adbf307669e9c2d034fc88f724fa056db GIT binary patch literal 7674 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/arbiter.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/arbiter.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ecd7abe60e6c96a0e586c3b41fdaf858056f18df GIT binary patch literal 16647 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
    ?8W6GY~N)($$x1;d(} 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 z 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/debug.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/debug.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f2946f524133b8697a0541e6e55d6aa469a187f5 GIT binary patch literal 1961 zcmZuyPj4JG6u0O9%x<-TLYKdg~!|_9r$+$^O*mPFrNoY zdl0e?3z_|l4k8vY=NTDvn024@yYufJOnA)ruo=gPHET*9XLY60ypnOF@-kOBmvLIL zc+^yx%4=93)gm5prMQU9#&KSa<2y~2XSJwyUI1pYryV)tN7`#D^;bE7dGS;j4xrxbs#NmNkUo+D4Ya) z=w9E_9VOC?CslgLrS8Z{eUv;&%ZAIo4fuFD5-#i&9+2OOfL9$a>7$6ECHr2GMkE%!W55Hf{APE?c zQO@FrTk_FP9RKi53`wCxW3nKAcZssZ_a%X6?>IUMcMfz?#oYaO+;8>M5@hIfI1eLc3<7y>vDMFa~Cw zAvUL|^Qzw!K6=O<32=aR`4bKY+Nl_8@5Gz{y^8M94pPxUeOro5`;0^NfTKKfrPMZX zlUpkip7Oe=WE`1B=+puTjEYntJ z_N~1KeOF+P=_pB7RV_($H%T5hX}NlI5rJ52Bf-2sM-ZUEWW5N(WOHCej#;^#VbQ^c z9Cb_*UxJAu1yxXfAk8ccVpF)R`Rp!)3|Jbcv8hGzq^_s&(In3%@sWVHkPuXv;4B}O z$54jRbr5o8J(f`nEgja40{v92A~Kgyd9mE`r#x#EsH!@aihTxUs~L)IQNKn`Y7 ze6D;Uk_^cOsW3#xc$aVPwaO>|B4R3m7VY$Vqh6u%Z0Cb0^k9H|FeJB!jw)AqEX0mFE7nRJVZ=i;+Y+Ax z;r|l2|GX$(uZkSXTI1xl#!v5b7NogUNwy4R7RaJVlUUj^ZtI18lyIk3YUQIHt%M^S h)aZpYy6KGVan)}zl>`)f7is06)}uXZ1~u-Fy}wsA(@OvV literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/glogging.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/glogging.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67a46ed4a8820ce232863d3f162c491fae46321c GIT binary patch literal 11425 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/pidfile.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/pidfile.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0da94b1f79e15a0fcf06744438d89ed8d758beb4 GIT binary patch literal 2234 zcmZ`)TW=dh6rP#Ac;h&UsUV^yz$#J{j7S2#NO?e|P$K9Ik#&G1$i$84xNaX0RSX}hn&I17h*@Hm!AR!J((!*(je_0_d(bCAek zELX!d%eo#O$4L@CRN*L7ax>h`A1QqtXUfVQtFWV0JEsrksz0<}1ZSKchJ$Rgf0Pk* z)ADx6VR~q#aT6n}V=8#TA_NjMf&iy@vmuPvB~y(9V{mdam2|ZO?w^_VvHX2?EAJ1t zhVgLY;V4ezMzVcXAN8(W-Z~tmac7{@=NqW7p%|W+Ad2EN&ZFo8eVsDOxX;J+X%IJO z*JsvNz(H)*+F!9LSdX2ui714=T!@_a#3?%xdW9^PeB$Iz;S>T~ZdWX`Q~rco|2%N| zy$aD!`NS<;EiEM|aIP_&kodwaCGxpmwQ!~LHGd!`-oyvzeBntK(%v%b)ueaIPX#Ai zT>T?%7Yg5M1}*hzAGx3|7U1IyyCu4nKFQb!SDNE%A0|khJPGwEO>M$b3!1eod~2XX z#H`Hb`dYS5fXVW9q8eiyLpe}cx}JyX%Q(w7!<%h7Oo1?qb%Ps6#(M0mW!14a%DWre z@lTwwM%@ID+es{e8Y2do@efrFAyeBMrFq;}J6aF4&9tVt8+X{wADIetSv+`ghOG$* zSd>&#nWD^i$2x{iU!|Ra#Hv*(&`AcFGG6CVe;^wb?Ln#4-11AP8>gQgw%jhM6_cU>gkdp$7n_rhax#lcpzNFF3C~5rVG z802L5J(kuuT;l9LOT_`WsR@pkLa7pkZZQRk;aZao%|^wzgP}^Tf5wT^+_(fz>!qFt zi+&AL!?FGv-fg*1@!r3ECZ4Mp83lw1!58@@QAJwL`(8%!%y+`r8iw`#Pi)`=%c|72 zPkzWL9cbu?!m|lKsn6n2#vpy!Wx0D))z?d3N#=loiz0!afUQumkM@yQ!1XO>6Nj_$ zyM@yWq;ru`l_PSG3-Fwy&4c!gbUi^F@1v(sSNY00u#rUMc^P@ccpI8)(ER&U^U66b z3()f0|7jsQ^05(qKCQ%AM(l5(-#)@GA}=dGjJsi~6#f)Vy@K%Agf$jRB)(1r!mxf5 zlc_@8Xj;IOG~*uO=V2t=*Ex-)Zjd zH19Ut6563vb4(3_tAFRaipvi$vbQlYl%2~d=K>jDXBM2AtT|_)tw(Aenkt^c zk<~B-o+|=3xEOfByuE37n($Ym{3Yy+KX}+vo!s93mqHV~U)pz_zF`YIK!FF&KSRI! A+5i9m literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/reloader.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/reloader.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..286ec3c3961313a30bded7dd859fd244428430cd GIT binary patch literal 3956 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/six.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/six.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7666d61d9c9e109daf86c8d0c3ced283dc28f8d9 GIT binary patch literal 22115 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/sock.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/sock.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c140040ef3a68c00be059fe86c23d6af534047a GIT binary patch literal 5886 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 YB*|v{r#Q@7Tw7dP%r0K>3jX{51ISbbYXATM literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/systemd.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/systemd.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21fd00099393deb71e46f64764376af38fe5b7c7 GIT binary patch literal 1351 zcmah}%}yIJ5Z<2zPztoAm#QZpdWi&Csc@(wgjx}(kPuWPRZ&zayIIc?lU;jddx=uf z6Mc`K`wo4Qz4pW_^wjb062ebaOCGJq_BY?meBRBKmBz0hPrsbhE$g>6H(dOFhf}>m zLoI5@R^Osd-=+(+M(f}0zC-P<)m*&Bee3AjobQFUS>Q0M8qQsu>J*K(w0-WJyVR!6 zm2+i(vu7T+@;H|co~d0~V^?lbr)ynWvRB-yQ8t0BWa!3=)!Y1$qGd zf=R&>;JQu17y>!FKet6L0?C453<|HIRb)Si6_jv#n!;mfLm}ewgan;Q-u?%DEp9US z5C00x#B1XfU8ZwVfGhG9BmyM~P!?#2r-Vi1_2EIcw|6x7c+eqC5iT^rqzOhel?*c} zF>jJG+A6S*(JLoR!8$1^tNWCz(%#l|)&!M8$RdVK7)}eYo7nrEZdLftBj9%^C4mk{ zSkrrC*2`*^{ztn|1gJV;F8lt2Zu&ln;^54r?fXTye81w+YMDwvyIsH-ZGGY~8$-cE z(Lu~mKztBUMRrM)aTJi?>$rjN zzVU8qbNf-{&`PKI4G@ApF~6Q?XsnlO_L{S7*PNQYYOgwS8GTK=*L0=%MfG2FJA>Qo h2HoEKlU{MAATnnRV!pJU2%5#Pvw$mQ0G6DZ`xkQem;(R+ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/util.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/util.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3510cb13c61832fe24eb1e94a5418c258b7d3f4 GIT binary patch literal 11847 zcma)CZH!#kS-xL$=kDFv^?Kv1KjJv!BpZ8@c-D3j*L7Xn@mJ!;opo)eP9{mGvvcn5 z&g{&c@ws=ryQA5Fx@k#+8ltucAr-+tR9XZG3GoAjKnMgvfJ#-Wi&O*!Bnu>3=r0Qd z&-31y@p@BK)}C|DJzw`d=e!@!`=0yU#DxEQzr5qk;H+VM-`Mi^2+HU1i{7&gLmA3! z8ZEPInk-vQt87c|lpV=4<&5NR*_Aw7&Pwi;J;}$)W0L!2U-I$txa7HVPV$NJgyfUu zNy&GVcSt@}o|1fLd8g#l>Ou}x#sEa%tvU?#tLuywF}5F^G_?j582_$?zm;26OZ zEB*(jo;D5sQxj?1>qcn3W~`I4$>x)-+48Jup#R}f|Eq)kJJb~BdP?0?JJs|%W_dOp zsa{nNs~J2WQoGe2^f;_Ot@f!$P*YI*)uX6+Iv8&}(|A@L*fjC}uc}+>F_lOA5p_^K zj`m{c25w`nTvSh}C-FR|X4O;3kE++yVO2oSW9n)33~G+6XVnqp uCL;k$beYEMe zUMRmHbl+d+y#d|F)N%BDVLUlh!P_L^O)CtV|5mi zeG)b2RY}bwzo4$EPa(gkuB#i!FDdJWQTWVVGHJm~COf(w#+AixSc~iJu!O2?rPgkB zsxez%>~yZQI&B?a(z>ltGZqJHaV1{w1bEna#(h-lxK0ZWtZu~Am3ms{$F^;%npr}DE=!PFl5VaAM1^>su0c)oXR zE^c+^I`z(xg>Jp6jx(sZ08KuLfookRKnjigg( zbWjkw%#yqp%?*3)<=F09%2s2W_C@2ZlN-+Zbl+?^%2AoVy=iaQw~X76-q>96 z^=Ae$;BI7Mv!CHSucP%f{B9X4i+6Z^`}5Xbqi-x)Gu&vucj9^wcXgNtE99#SQM=j2 ziHFrzkZ&(aWlDQKZs!+*e7c@M6%!X6tb(=PqlY@x+DdgPIFt|DaUPq?uGM@Lqr=i& z?qOmUHS^x_Y8(fxP8_Jb4w~RGuwptR=3Z}y3PU?bn!%l*nIFvB%gjc_*{F2)fACkx zB-!O^gjsarf4bW21|lGd-3-Dc1Lj-`^dw$Xa1)11NV0=jl8j86*s2{TW+ky((bBfl z17qj_%fAy;IP^58iFP3|EZ_9-Z`!o*n=~DydOzxG!Zs+7JNT2+{45H6qXCBAG#)fq}f6KtB^Eo4DWcDqbuYvOy!HAtk zrf=-!skyzsO!WiyIX#}D@884$-am_~Uh1|W-1&Nhy}sS8Yi#-|#68~$^kQ4LD81F~ z$56~)iT$V()ar}%0P>D02Rq0^?M7gcw6+@7mfJetX+w?W7j?UZx$CuM_8ROW2m9Mk znVb>cXS4L|LA$0F=*?~GKB8O*`S99=sq_vEE#budD6Z-_TCK;+$=FS(O5qJTs>H8U z(sxyG_R)HjWYcYn3XYz^c)gp+9wf^8D}r*A<~*f#_um`3b1#x{)&S2-Km*zae-jxJS2s!9kQ!f(r)pvE2>Tsl4yMF&n)l44inwW7RuxgoT|+wjNTNViY2oOFxQ+ zBolR-^%yEta{UnNCORuiqJg@)7DUNZvt3mc2(VtSh?D^XfGvXsDBHxR09WHmuvSZQ z*Sj#`tpT@FV`-X~#O|~^!j6g4fRrZLr68u_EQ}{M7DULfSnW1JT9o30@>u#dDDK2j zb!6o8#KucB0d?XNptM8uijvFov432kgiTlZP)J3V-sG4;nV-vf4HXwf2@ihw|l- z9Md3BmW8eug}$j5`i3s9=V0t1Pc+=&o`KL*)*)(847Ep2t6->;kJ9vW33XXzI7o1)}EV3-N3j4aG+uaT=`#>`h34+}s zDErHGSlFK`6+Br<;=K~A53DO-L1ML|#IJ|-SZ0V47u&Fe-Ev{@hdFi&`V`I>xCV9l zB1_(IueNXGPK>OySHnP821I-n{iA(I4BG*_P^nIuzLT>Y+pfwdMI)at5#)7TSqqFAOBpDd8)evmp0DLbC_4;MD5@4o@fV)@l z_znCbzR&@%p+#96 znYA`zX)@6gp6$ zxf^O+!C(B6Jy^F_68*IFUtj;>w?4UW?d%dr!`~0i-lgdD3ULs{QNC4O&o2V$Kbu!U zGXM&V0GR>Af>5A7#pW&-55b_Vt*r>ADvZfKYKploAW5=KC=XSK7NK5(vTMTeNW9gm z4gor%#9=9!9O$iEX>}o+ICO^+kHLRHm8a#Mwr<*e;4aNN~xXOwsq z4gsOR1v(;vG{f@1HLf*fdRZtS80~4(fw}=Vnfgt%KcGm+PjYstQ<8EJMQ#B8!8ja~ zGLG8~oAq!7yWuUuJtE25>B&~oKHtKip_ftF`ciH`-z6_4*A2CUIJYMT+TpwjEnphG zeE|MeY&C5BoJDITtB-#U&im^iPoc-#Cag0o^pMz$=DU*Y{EbwOGk`Q_S<}a0Q zCRyY+FJ8G`ur&2_!shmeK+juf`YwJE#laAnoU(v`S^vP^QAlJN=TLxA2A|_c(~kX1 zBMjtfz>$G@R^|-|Z|tDv)KLB1FhD*Pw_Un?6KLO!+O?G+PR8mXltm|Qhl%&%<;s=P zrJEHi^qwA=-d+sqTgy+CNviFDy z{t*vc27S{_OYi4$(y|&S3>O?kwDbHB_XrjN-nJsR^{#;p`wNZlx2m- z;gqpYU-|&gu+N}v1{lg)vGjXt3_U&ckTIj#a>Q1H@!v_u6aA%l5NHtgA^MBhRes-2 z@mrh~m12iPfp9AO_9C}>qSW)@YOkF-JU3T3E7+&!#9tE2Sb2k$1uL-@6}mR7OVNjxQ){-doOTF+ z7t}mB0TCgcL2GF0pF-)!lY4j@xl|SBw~~s3-%4smuVciJ(5tCh^XDt2`O^8T^A}zj zQhOCDVgo((+e{v`i^&wtcnDe-aAYe`@ROvMTWDaz^(OKWwP?8yib(A`T!MzN3FiYE zVutFjVL=}}p6Y`^438c!Hr9rz=Dz3v14Z~L_f!zuNYd}1!$YLPLt&t^OsS>mN+)5g zU?lbwd16O7^wTJ;oghfVZdvfw4{um-RcN(=ElBC@94MtGJ3gfIb`EYy4l$Mit&V;e zv!sTR)acJ5DcEIqQB}bBU^nH$WXkbDsZ zhLBxdAqL(43qcwqb zEE%ugLsyt^5~hC^Ns^WK1s}rr8KLF~=|}~SUcUYSLq;@k??)!@G9FS!9tCzz^q{|C zJA`ELIwg~e$r2#ktjd;Hy`WU?L18)z3&p1SGC~ZR3V7Fyanm&mx zGkK7&lF4+BA*(X@IW8}?ii~fi?$@NHzk;DPE{}g)K95?qnh=kQkj&K=AVStr*4Gdq z*l?r;&oQ+O+cSL!xwGh?H50p#rws5Z>PCnP?Dng9{GDEoTg@{PH0DE4Rl^57O2GHb z>|q^Lqjp$`PS2$^!|roi`=4HDtMx5|myew}-FaYe=gjFj)DA}H^RtIrL9|qeM$_G& z0UbwlL1DWo1&PA}y(S5N`R2`QbH|Fu5TeHa37O-FgK#NcM#NjC^=Uc_cZ<=|@L0i1JPuve)lTB7K+$EVD3r%ZY9)wj)lPsASFBT^ zl_f%LcZ&m)0t#b~C8)^FpQfn*9eJbPa<9e>7h+7SHruUWi5eQR_3F)^}mp#!I?|4HGdhk5@Yg4BfThV3KGLEJquG*96H8p2%h^b|-1 zK*X|vY7i8m2W2bgOE$4M$jUxf)dcaczKs@yQ*g1? zNyeAkol3d@+>R!0THJO7L~Ipw?9`*>_ela@f}74iMEbzalkND6zK2AS-AqIm66DsV zA)YU97NCz&120r^wg4t0oS{5%15Hpw7<^8(YjV9Ns(Oxr@gjZb zA|jc%5N{zAbma1jpPE~&-mV-k(zrfxio`y3V`R_4|Gc@O!s&93Ld$;-J2On_*G1kb1~J&`_&921h1X;}wUv zGT=nIHvIk|WknL6c({AS{bMcuE_rYmh@lVei%#f0GCYHzcv{3TI#V2uDHV34^!x^= zav*&xNe1ua{Rwbzgcx+D+p0z@;vXeW8}Z6y{Q4zCwk}<`S-Cl%xbv0k7jIqHe}W0b z3lsyxB`AgwnFHMvtnwZN@nF9yj9#{(=9U9ECF1S;IfoJ$;pYF(SeA8-jmZ8o7e?D< zWT?~egPi-8?>QOheUz#6Chf@#+j9GIV+bFA8@+2BGW;946ECA?eHYjqUjd|-d;=&{ z#4$2nc7CMA0h$1V5b8lscXI$l%mLI9hr%x1J%{-Q_vkcuxF=Wb7BqYS&*uW#l5BTS z_fV-F!&El|UL6N2HL0QmX#hG8+=g&FWFPe3qV&()fuX<6D;}vGDH$>`6&qg{k-gzj zi;5>Ogk3t0mGKLPa|m^UWqUI*0$K1FMgVRU=Ct1CB_w{Zbg+Q~$JH+B-^Q%H$Asil z1VV=ppb8Jgd3rrO*8Cf@`Yq`6ze2wxdu2Xd9OF)DXuy^bc496j=7$2g)3xQGwo-vM z6(5Sz{Tmm|Jz#)g5`6r)si{2^2M+Yfk@QmIa`Ce8A_`o#_~~+=D^HNp=*bwjx#>4B zK!#$}H)-Fj-ZOA0@Ldf8^wq&f75UNSKJU}>3+ocA=jRr^>8)`&T8eO~kkRjwt_=G{ z;<)Prs7T!cE6D&ybpt@8!8bkIZOBC2uENX zWq8ifBqO^L@qw}uXwM63geZ8MvB-GJiIlI|4{>0*z$)oK#vHKFuBMWdC;g`^{T-6T zs<)F}uZB^#!?<9e=J9od=wt}$H`q(Kbi}HOkIzRcskZxXt|V1PScy%vHiPh>{uc}z zt%h)M1WOut;HOH2BFg#__!I{P9^5byFA}d8(W0kuuY;Q%ezODBd=kN6gkRyDz`(I| zf2@4(VJP>0jX)g8@_?tarJjB8;6X;YNO9^AjNf|kdg)5(C4LpvbJ5J)LCye8MsRP6 zpZ^BL;+}>V&A6t(jWVuxVY0A0h`}s2yU{XaLGE=kX}eiZ9G13inSK=$`Cw!DQVTIi zXtQbPI1d;WQqI=VZVj2;MrFjWDPkD>P4kO}7-#6J)Hs8!?a~7hGV~jxJO;rNG!?G3 zVr&+NZ<>qNl322LjU~#2Tk7q7;pox)73e5j3^nsN(l5_MqVMG(FC$O;o-dwur&+Cs zy~$xq>SiM>^zV3`{s9t{f01RnEGW~~lqZ4fbj&+>+#ALmSejRc{Z0N-Tr@ei1gljJ)>{Yz{eOC8K+eL+6u zkdK+<`v-lF9Y4Y36?XJ+9K85!rRVh~8!AlRVp3d+!Lst?PI1^J38>WBM%lNxJu#$h!w_V#C^L+nN&-F5%oA#R=^E}`6 YbKY+6ac|Q1JjcJ`Wl=ZgP4M6U0?wV*KL7v# literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/_compat.py b/venv/lib/python3.6/site-packages/gunicorn/_compat.py new file mode 100644 index 0000000..39dbfdf --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/_compat.py @@ -0,0 +1,298 @@ +import sys + +from gunicorn import six + +PY26 = (sys.version_info[:2] == (2, 6)) +PY33 = (sys.version_info >= (3, 3)) + + +def _check_if_pyc(fname): + """Return True if the extension is .pyc, False if .py + and None if otherwise""" + from imp import find_module + from os.path import realpath, dirname, basename, splitext + + # Normalize the file-path for the find_module() + filepath = realpath(fname) + dirpath = dirname(filepath) + module_name = splitext(basename(filepath))[0] + + # Validate and fetch + try: + fileobj, fullpath, (_, _, pytype) = find_module(module_name, [dirpath]) + except ImportError: + raise IOError("Cannot find config file. " + "Path maybe incorrect! : {0}".format(filepath)) + return pytype, fileobj, fullpath + + +def _get_codeobj(pyfile): + """ Returns the code object, given a python file """ + from imp import PY_COMPILED, PY_SOURCE + + result, fileobj, fullpath = _check_if_pyc(pyfile) + + # WARNING: + # fp.read() can blowup if the module is extremely large file. + # Lookout for overflow errors. + try: + data = fileobj.read() + finally: + fileobj.close() + + # This is a .pyc file. Treat accordingly. + if result is PY_COMPILED: + # .pyc format is as follows: + # 0 - 4 bytes: Magic number, which changes with each create of .pyc file. + # First 2 bytes change with each marshal of .pyc file. Last 2 bytes is "\r\n". + # 4 - 8 bytes: Datetime value, when the .py was last changed. + # 8 - EOF: Marshalled code object data. + # So to get code object, just read the 8th byte onwards till EOF, and + # UN-marshal it. + import marshal + code_obj = marshal.loads(data[8:]) + + elif result is PY_SOURCE: + # This is a .py file. + code_obj = compile(data, fullpath, 'exec') + + else: + # Unsupported extension + raise Exception("Input file is unknown format: {0}".format(fullpath)) + + # Return code object + return code_obj + +if six.PY3: + def execfile_(fname, *args): + if fname.endswith(".pyc"): + code = _get_codeobj(fname) + else: + code = compile(open(fname, 'rb').read(), fname, 'exec') + return six.exec_(code, *args) + + def bytes_to_str(b): + if isinstance(b, six.text_type): + return b + return str(b, 'latin1') + + import urllib.parse + + def unquote_to_wsgi_str(string): + return _unquote_to_bytes(string).decode('latin-1') + + _unquote_to_bytes = urllib.parse.unquote_to_bytes + +else: + def execfile_(fname, *args): + """ Overriding PY2 execfile() implementation to support .pyc files """ + if fname.endswith(".pyc"): + return six.exec_(_get_codeobj(fname), *args) + return execfile(fname, *args) + + def bytes_to_str(s): + if isinstance(s, unicode): + return s.encode('utf-8') + return s + + import urllib + unquote_to_wsgi_str = urllib.unquote + + +# The following code adapted from trollius.py33_exceptions +def _wrap_error(exc, mapping, key): + if key not in mapping: + return + new_err_cls = mapping[key] + new_err = new_err_cls(*exc.args) + + # raise a new exception with the original traceback + six.reraise(new_err_cls, new_err, + exc.__traceback__ if hasattr(exc, '__traceback__') else sys.exc_info()[2]) + +if PY33: + import builtins + + BlockingIOError = builtins.BlockingIOError + BrokenPipeError = builtins.BrokenPipeError + ChildProcessError = builtins.ChildProcessError + ConnectionRefusedError = builtins.ConnectionRefusedError + ConnectionResetError = builtins.ConnectionResetError + InterruptedError = builtins.InterruptedError + ConnectionAbortedError = builtins.ConnectionAbortedError + PermissionError = builtins.PermissionError + FileNotFoundError = builtins.FileNotFoundError + ProcessLookupError = builtins.ProcessLookupError + + def wrap_error(func, *args, **kw): + return func(*args, **kw) +else: + import errno + import select + import socket + + class BlockingIOError(OSError): + pass + + class BrokenPipeError(OSError): + pass + + class ChildProcessError(OSError): + pass + + class ConnectionRefusedError(OSError): + pass + + class InterruptedError(OSError): + pass + + class ConnectionResetError(OSError): + pass + + class ConnectionAbortedError(OSError): + pass + + class PermissionError(OSError): + pass + + class FileNotFoundError(OSError): + pass + + class ProcessLookupError(OSError): + pass + + _MAP_ERRNO = { + errno.EACCES: PermissionError, + errno.EAGAIN: BlockingIOError, + errno.EALREADY: BlockingIOError, + errno.ECHILD: ChildProcessError, + errno.ECONNABORTED: ConnectionAbortedError, + errno.ECONNREFUSED: ConnectionRefusedError, + errno.ECONNRESET: ConnectionResetError, + errno.EINPROGRESS: BlockingIOError, + errno.EINTR: InterruptedError, + errno.ENOENT: FileNotFoundError, + errno.EPERM: PermissionError, + errno.EPIPE: BrokenPipeError, + errno.ESHUTDOWN: BrokenPipeError, + errno.EWOULDBLOCK: BlockingIOError, + errno.ESRCH: ProcessLookupError, + } + + def wrap_error(func, *args, **kw): + """ + Wrap socket.error, IOError, OSError, select.error to raise new specialized + exceptions of Python 3.3 like InterruptedError (PEP 3151). + """ + try: + return func(*args, **kw) + except (socket.error, IOError, OSError) as exc: + if hasattr(exc, 'winerror'): + _wrap_error(exc, _MAP_ERRNO, exc.winerror) + # _MAP_ERRNO does not contain all Windows errors. + # For some errors like "file not found", exc.errno should + # be used (ex: ENOENT). + _wrap_error(exc, _MAP_ERRNO, exc.errno) + raise + except select.error as exc: + if exc.args: + _wrap_error(exc, _MAP_ERRNO, exc.args[0]) + raise + +if PY26: + from urlparse import ( + _parse_cache, MAX_CACHE_SIZE, clear_cache, _splitnetloc, SplitResult, + scheme_chars, + ) + + def urlsplit(url, scheme='', allow_fragments=True): + """Parse a URL into 5 components: + :///?# + Return a 5-tuple: (scheme, netloc, path, query, fragment). + Note that we don't break the components up in smaller bits + (e.g. netloc is a single string) and we don't expand % escapes.""" + allow_fragments = bool(allow_fragments) + key = url, scheme, allow_fragments, type(url), type(scheme) + cached = _parse_cache.get(key, None) + if cached: + return cached + if len(_parse_cache) >= MAX_CACHE_SIZE: # avoid runaway growth + clear_cache() + netloc = query = fragment = '' + i = url.find(':') + if i > 0: + if url[:i] == 'http': # optimize the common case + scheme = url[:i].lower() + url = url[i+1:] + if url[:2] == '//': + netloc, url = _splitnetloc(url, 2) + if (('[' in netloc and ']' not in netloc) or + (']' in netloc and '[' not in netloc)): + raise ValueError("Invalid IPv6 URL") + if allow_fragments and '#' in url: + url, fragment = url.split('#', 1) + if '?' in url: + url, query = url.split('?', 1) + v = SplitResult(scheme, netloc, url, query, fragment) + _parse_cache[key] = v + return v + for c in url[:i]: + if c not in scheme_chars: + break + else: + # make sure "url" is not actually a port number (in which case + # "scheme" is really part of the path) + rest = url[i+1:] + if not rest or any(c not in '0123456789' for c in rest): + # not a port number + scheme, url = url[:i].lower(), rest + + if url[:2] == '//': + netloc, url = _splitnetloc(url, 2) + if (('[' in netloc and ']' not in netloc) or + (']' in netloc and '[' not in netloc)): + raise ValueError("Invalid IPv6 URL") + if allow_fragments and '#' in url: + url, fragment = url.split('#', 1) + if '?' in url: + url, query = url.split('?', 1) + v = SplitResult(scheme, netloc, url, query, fragment) + _parse_cache[key] = v + return v + +else: + from gunicorn.six.moves.urllib.parse import urlsplit + + +import inspect + +if hasattr(inspect, 'signature'): + positionals = ( + inspect.Parameter.POSITIONAL_ONLY, + inspect.Parameter.POSITIONAL_OR_KEYWORD, + ) + + def get_arity(f): + sig = inspect.signature(f) + arity = 0 + + for param in sig.parameters.values(): + if param.kind in positionals: + arity += 1 + + return arity +else: + def get_arity(f): + return len(inspect.getargspec(f)[0]) + + +try: + import html + + def html_escape(s): + return html.escape(s) +except ImportError: + import cgi + + def html_escape(s): + return cgi.escape(s, quote=True) diff --git a/venv/lib/python3.6/site-packages/gunicorn/app/__init__.py b/venv/lib/python3.6/site-packages/gunicorn/app/__init__.py new file mode 100644 index 0000000..87f0611 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/app/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. diff --git a/venv/lib/python3.6/site-packages/gunicorn/app/__pycache__/__init__.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/app/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..040397ef2a405bdfa3737c1c342d06e6023c4678 GIT binary patch literal 140 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 CJ#hg5 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/app/__pycache__/pasterapp.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/app/__pycache__/pasterapp.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b908f3917c7c69958828ef91be48f64b7390b105 GIT binary patch literal 5614 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/app/base.py b/venv/lib/python3.6/site-packages/gunicorn/app/base.py new file mode 100644 index 0000000..e468c95 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/app/base.py @@ -0,0 +1,223 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. +from __future__ import print_function + +import os +import sys +import traceback + +from gunicorn._compat import execfile_ +from gunicorn import util +from gunicorn.arbiter import Arbiter +from gunicorn.config import Config, get_default_config_file +from gunicorn import debug + +class BaseApplication(object): + """ + An application interface for configuring and loading + the various necessities for any given web framework. + """ + def __init__(self, usage=None, prog=None): + self.usage = usage + self.cfg = None + self.callable = None + self.prog = prog + self.logger = None + self.do_load_config() + + def do_load_config(self): + """ + Loads the configuration + """ + try: + self.load_default_config() + self.load_config() + except Exception as e: + print("\nError: %s" % str(e), file=sys.stderr) + sys.stderr.flush() + sys.exit(1) + + def load_default_config(self): + # init configuration + self.cfg = Config(self.usage, prog=self.prog) + + def init(self, parser, opts, args): + raise NotImplementedError + + def load(self): + raise NotImplementedError + + def load_config(self): + """ + This method is used to load the configuration from one or several input(s). + Custom Command line, configuration file. + You have to override this method in your class. + """ + raise NotImplementedError + + def reload(self): + self.do_load_config() + if self.cfg.spew: + debug.spew() + + def wsgi(self): + if self.callable is None: + self.callable = self.load() + return self.callable + + def run(self): + try: + Arbiter(self).run() + except RuntimeError as e: + print("\nError: %s\n" % e, file=sys.stderr) + sys.stderr.flush() + sys.exit(1) + + +class Application(BaseApplication): + + # 'init' and 'load' methods are implemented by WSGIApplication. + # pylint: disable=abstract-method + + def chdir(self): + # chdir to the configured path before loading, + # default is the current dir + os.chdir(self.cfg.chdir) + + # add the path to sys.path + if self.cfg.chdir not in sys.path: + sys.path.insert(0, self.cfg.chdir) + + def get_config_from_filename(self, filename): + + if not os.path.exists(filename): + raise RuntimeError("%r doesn't exist" % filename) + + cfg = { + "__builtins__": __builtins__, + "__name__": "__config__", + "__file__": filename, + "__doc__": None, + "__package__": None + } + try: + execfile_(filename, cfg, cfg) + except Exception: + print("Failed to read config file: %s" % filename, file=sys.stderr) + traceback.print_exc() + sys.stderr.flush() + sys.exit(1) + + return cfg + + def get_config_from_module_name(self, module_name): + return vars(util.import_module(module_name)) + + def load_config_from_module_name_or_filename(self, location): + """ + Loads the configuration file: the file is a python file, otherwise raise an RuntimeError + Exception or stop the process if the configuration file contains a syntax error. + """ + + if location.startswith("python:"): + module_name = location[len("python:"):] + cfg = self.get_config_from_module_name(module_name) + else: + if location.startswith("file:"): + filename = location[len("file:"):] + else: + filename = location + cfg = self.get_config_from_filename(filename) + + for k, v in cfg.items(): + # Ignore unknown names + if k not in self.cfg.settings: + continue + try: + self.cfg.set(k.lower(), v) + except: + print("Invalid value for %s: %s\n" % (k, v), file=sys.stderr) + sys.stderr.flush() + raise + + return cfg + + def load_config_from_file(self, filename): + return self.load_config_from_module_name_or_filename(location=filename) + + def load_config(self): + # parse console args + parser = self.cfg.parser() + args = parser.parse_args() + + # optional settings from apps + cfg = self.init(parser, args, args.args) + + # set up import paths and follow symlinks + self.chdir() + + # Load up the any app specific configuration + if cfg: + for k, v in cfg.items(): + self.cfg.set(k.lower(), v) + + env_args = parser.parse_args(self.cfg.get_cmd_args_from_env()) + + if args.config: + self.load_config_from_file(args.config) + elif env_args.config: + self.load_config_from_file(env_args.config) + else: + default_config = get_default_config_file() + if default_config is not None: + self.load_config_from_file(default_config) + + # Load up environment configuration + for k, v in vars(env_args).items(): + if v is None: + continue + if k == "args": + continue + self.cfg.set(k.lower(), v) + + # Lastly, update the configuration with any command line settings. + for k, v in vars(args).items(): + if v is None: + continue + if k == "args": + continue + self.cfg.set(k.lower(), v) + + # current directory might be changed by the config now + # set up import paths and follow symlinks + self.chdir() + + def run(self): + if self.cfg.check_config: + try: + self.load() + except: + msg = "\nError while loading the application:\n" + print(msg, file=sys.stderr) + traceback.print_exc() + sys.stderr.flush() + sys.exit(1) + sys.exit(0) + + if self.cfg.spew: + debug.spew() + + if self.cfg.daemon: + util.daemonize(self.cfg.enable_stdio_inheritance) + + # set python paths + if self.cfg.pythonpath: + paths = self.cfg.pythonpath.split(",") + for path in paths: + pythonpath = os.path.abspath(path) + if pythonpath not in sys.path: + sys.path.insert(0, pythonpath) + + super(Application, self).run() diff --git a/venv/lib/python3.6/site-packages/gunicorn/app/pasterapp.py b/venv/lib/python3.6/site-packages/gunicorn/app/pasterapp.py new file mode 100644 index 0000000..dbcd339 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/app/pasterapp.py @@ -0,0 +1,209 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. +from __future__ import print_function + +# pylint: skip-file + +import os +import pkg_resources +import sys + +try: + import configparser as ConfigParser +except ImportError: + import ConfigParser + +from paste.deploy import loadapp, loadwsgi +SERVER = loadwsgi.SERVER + +from gunicorn.app.base import Application +from gunicorn.config import Config, get_default_config_file +from gunicorn import util + + +def _has_logging_config(paste_file): + cfg_parser = ConfigParser.ConfigParser() + cfg_parser.read([paste_file]) + return cfg_parser.has_section('loggers') + + +def paste_config(gconfig, config_url, relative_to, global_conf=None): + # add entry to pkg_resources + sys.path.insert(0, relative_to) + pkg_resources.working_set.add_entry(relative_to) + + config_url = config_url.split('#')[0] + cx = loadwsgi.loadcontext(SERVER, config_url, relative_to=relative_to, + global_conf=global_conf) + gc, lc = cx.global_conf.copy(), cx.local_conf.copy() + cfg = {} + + host, port = lc.pop('host', ''), lc.pop('port', '') + if host and port: + cfg['bind'] = '%s:%s' % (host, port) + elif host: + cfg['bind'] = host.split(',') + + cfg['default_proc_name'] = gc.get('__file__') + + # init logging configuration + config_file = config_url.split(':')[1] + if _has_logging_config(config_file): + cfg.setdefault('logconfig', config_file) + + for k, v in gc.items(): + if k not in gconfig.settings: + continue + cfg[k] = v + + for k, v in lc.items(): + if k not in gconfig.settings: + continue + cfg[k] = v + + return cfg + + +def load_pasteapp(config_url, relative_to, global_conf=None): + return loadapp(config_url, relative_to=relative_to, + global_conf=global_conf) + +class PasterBaseApplication(Application): + gcfg = None + + def app_config(self): + return paste_config(self.cfg, self.cfgurl, self.relpath, + global_conf=self.gcfg) + + def load_config(self): + super(PasterBaseApplication, self).load_config() + + # reload logging conf + if hasattr(self, "cfgfname"): + parser = ConfigParser.ConfigParser() + parser.read([self.cfgfname]) + if parser.has_section('loggers'): + from logging.config import fileConfig + config_file = os.path.abspath(self.cfgfname) + fileConfig(config_file, dict(__file__=config_file, + here=os.path.dirname(config_file))) + + +class PasterApplication(PasterBaseApplication): + + def init(self, parser, opts, args): + if len(args) != 1: + parser.error("No application name specified.") + + cwd = util.getcwd() + cfgfname = os.path.normpath(os.path.join(cwd, args[0])) + cfgfname = os.path.abspath(cfgfname) + if not os.path.exists(cfgfname): + parser.error("Config file not found: %s" % cfgfname) + + self.cfgurl = 'config:%s' % cfgfname + self.relpath = os.path.dirname(cfgfname) + self.cfgfname = cfgfname + + sys.path.insert(0, self.relpath) + pkg_resources.working_set.add_entry(self.relpath) + + return self.app_config() + + def load(self): + # chdir to the configured path before loading, + # default is the current dir + os.chdir(self.cfg.chdir) + + return load_pasteapp(self.cfgurl, self.relpath, global_conf=self.gcfg) + + +class PasterServerApplication(PasterBaseApplication): + + def __init__(self, app, gcfg=None, host="127.0.0.1", port=None, **kwargs): + # pylint: disable=super-init-not-called + self.cfg = Config() + self.gcfg = gcfg # need to hold this for app_config + self.app = app + self.callable = None + + gcfg = gcfg or {} + cfgfname = gcfg.get("__file__") + if cfgfname is not None: + self.cfgurl = 'config:%s' % cfgfname + self.relpath = os.path.dirname(cfgfname) + self.cfgfname = cfgfname + + cfg = kwargs.copy() + + if port and not host.startswith("unix:"): + bind = "%s:%s" % (host, port) + else: + bind = host + cfg["bind"] = bind.split(',') + + if gcfg: + for k, v in gcfg.items(): + cfg[k] = v + cfg["default_proc_name"] = cfg['__file__'] + + try: + for k, v in cfg.items(): + if k.lower() in self.cfg.settings and v is not None: + self.cfg.set(k.lower(), v) + except Exception as e: + print("\nConfig error: %s" % str(e), file=sys.stderr) + sys.stderr.flush() + sys.exit(1) + + if cfg.get("config"): + self.load_config_from_file(cfg["config"]) + else: + default_config = get_default_config_file() + if default_config is not None: + self.load_config_from_file(default_config) + + def load(self): + return self.app + + +def run(): + """\ + The ``gunicorn_paster`` command for launching Paster compatible + applications like Pylons or Turbogears2 + """ + util.warn("""This command is deprecated. + + You should now use the `--paste` option. Ex.: + + gunicorn --paste development.ini + """) + + from gunicorn.app.pasterapp import PasterApplication + PasterApplication("%(prog)s [OPTIONS] pasteconfig.ini").run() + + +def paste_server(app, gcfg=None, host="127.0.0.1", port=None, **kwargs): + """\ + A paster server. + + Then entry point in your paster ini file should looks like this: + + [server:main] + use = egg:gunicorn#main + host = 127.0.0.1 + port = 5000 + + """ + + util.warn("""This command is deprecated. + + You should now use the `--paste` option. Ex.: + + gunicorn --paste development.ini + """) + + from gunicorn.app.pasterapp import PasterServerApplication + PasterServerApplication(app, gcfg=gcfg, host=host, port=port, **kwargs).run() diff --git a/venv/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py b/venv/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py new file mode 100644 index 0000000..2205944 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import os + +from gunicorn.errors import ConfigError +from gunicorn.app.base import Application +from gunicorn import util + + +class WSGIApplication(Application): + def init(self, parser, opts, args): + if opts.paste: + app_name = 'main' + path = opts.paste + if '#' in path: + path, app_name = path.split('#') + path = os.path.abspath(os.path.normpath( + os.path.join(util.getcwd(), path))) + + if not os.path.exists(path): + raise ConfigError("%r not found" % path) + + # paste application, load the config + self.cfgurl = 'config:%s#%s' % (path, app_name) + self.relpath = os.path.dirname(path) + + from .pasterapp import paste_config + return paste_config(self.cfg, self.cfgurl, self.relpath) + + if len(args) < 1: + parser.error("No application module specified.") + + self.cfg.set("default_proc_name", args[0]) + self.app_uri = args[0] + + def load_wsgiapp(self): + # load the app + return util.import_app(self.app_uri) + + def load_pasteapp(self): + # load the paste app + from .pasterapp import load_pasteapp + return load_pasteapp(self.cfgurl, self.relpath, global_conf=self.cfg.paste_global_conf) + + def load(self): + if self.cfg.paste is not None: + return self.load_pasteapp() + else: + return self.load_wsgiapp() + + +def run(): + """\ + The ``gunicorn`` command line runner for launching Gunicorn with + generic WSGI applications. + """ + from gunicorn.app.wsgiapp import WSGIApplication + WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run() + + +if __name__ == '__main__': + run() diff --git a/venv/lib/python3.6/site-packages/gunicorn/arbiter.py b/venv/lib/python3.6/site-packages/gunicorn/arbiter.py new file mode 100644 index 0000000..083ee6a --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/arbiter.py @@ -0,0 +1,646 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. +from __future__ import print_function + +import errno +import os +import random +import select +import signal +import sys +import time +import traceback + +from gunicorn.errors import HaltServer, AppImportError +from gunicorn.pidfile import Pidfile +from gunicorn import sock, systemd, util + +from gunicorn import __version__, SERVER_SOFTWARE + + +class Arbiter(object): + """ + Arbiter maintain the workers processes alive. It launches or + kills them if needed. It also manages application reloading + via SIGHUP/USR2. + """ + + # A flag indicating if a worker failed to + # to boot. If a worker process exist with + # this error code, the arbiter will terminate. + WORKER_BOOT_ERROR = 3 + + # A flag indicating if an application failed to be loaded + APP_LOAD_ERROR = 4 + + START_CTX = {} + + LISTENERS = [] + WORKERS = {} + PIPE = [] + + # I love dynamic languages + SIG_QUEUE = [] + SIGNALS = [getattr(signal, "SIG%s" % x) + for x in "HUP QUIT INT TERM TTIN TTOU USR1 USR2 WINCH".split()] + SIG_NAMES = dict( + (getattr(signal, name), name[3:].lower()) for name in dir(signal) + if name[:3] == "SIG" and name[3] != "_" + ) + + def __init__(self, app): + os.environ["SERVER_SOFTWARE"] = SERVER_SOFTWARE + + self._num_workers = None + self._last_logged_active_worker_count = None + self.log = None + + self.setup(app) + + self.pidfile = None + self.systemd = False + self.worker_age = 0 + self.reexec_pid = 0 + self.master_pid = 0 + self.master_name = "Master" + + cwd = util.getcwd() + + args = sys.argv[:] + args.insert(0, sys.executable) + + # init start context + self.START_CTX = { + "args": args, + "cwd": cwd, + 0: sys.executable + } + + def _get_num_workers(self): + return self._num_workers + + def _set_num_workers(self, value): + old_value = self._num_workers + self._num_workers = value + self.cfg.nworkers_changed(self, value, old_value) + num_workers = property(_get_num_workers, _set_num_workers) + + def setup(self, app): + self.app = app + self.cfg = app.cfg + + if self.log is None: + self.log = self.cfg.logger_class(app.cfg) + + # reopen files + if 'GUNICORN_FD' in os.environ: + self.log.reopen_files() + + self.worker_class = self.cfg.worker_class + self.address = self.cfg.address + self.num_workers = self.cfg.workers + self.timeout = self.cfg.timeout + self.proc_name = self.cfg.proc_name + + self.log.debug('Current configuration:\n{0}'.format( + '\n'.join( + ' {0}: {1}'.format(config, value.value) + for config, value + in sorted(self.cfg.settings.items(), + key=lambda setting: setting[1])))) + + # set enviroment' variables + if self.cfg.env: + for k, v in self.cfg.env.items(): + os.environ[k] = v + + if self.cfg.preload_app: + self.app.wsgi() + + def start(self): + """\ + Initialize the arbiter. Start listening and set pidfile if needed. + """ + self.log.info("Starting gunicorn %s", __version__) + + if 'GUNICORN_PID' in os.environ: + self.master_pid = int(os.environ.get('GUNICORN_PID')) + self.proc_name = self.proc_name + ".2" + self.master_name = "Master.2" + + self.pid = os.getpid() + if self.cfg.pidfile is not None: + pidname = self.cfg.pidfile + if self.master_pid != 0: + pidname += ".2" + self.pidfile = Pidfile(pidname) + self.pidfile.create(self.pid) + self.cfg.on_starting(self) + + self.init_signals() + + if not self.LISTENERS: + fds = None + listen_fds = systemd.listen_fds() + if listen_fds: + self.systemd = True + fds = range(systemd.SD_LISTEN_FDS_START, + systemd.SD_LISTEN_FDS_START + listen_fds) + + elif self.master_pid: + fds = [] + for fd in os.environ.pop('GUNICORN_FD').split(','): + fds.append(int(fd)) + + self.LISTENERS = sock.create_sockets(self.cfg, self.log, fds) + + listeners_str = ",".join([str(l) for l in self.LISTENERS]) + self.log.debug("Arbiter booted") + self.log.info("Listening at: %s (%s)", listeners_str, self.pid) + self.log.info("Using worker: %s", self.cfg.worker_class_str) + + # check worker class requirements + if hasattr(self.worker_class, "check_config"): + self.worker_class.check_config(self.cfg, self.log) + + self.cfg.when_ready(self) + + def init_signals(self): + """\ + Initialize master signal handling. Most of the signals + are queued. Child signals only wake up the master. + """ + # close old PIPE + for p in self.PIPE: + os.close(p) + + # initialize the pipe + self.PIPE = pair = os.pipe() + for p in pair: + util.set_non_blocking(p) + util.close_on_exec(p) + + self.log.close_on_exec() + + # initialize all signals + for s in self.SIGNALS: + signal.signal(s, self.signal) + signal.signal(signal.SIGCHLD, self.handle_chld) + + def signal(self, sig, frame): + if len(self.SIG_QUEUE) < 5: + self.SIG_QUEUE.append(sig) + self.wakeup() + + def run(self): + "Main master loop." + self.start() + util._setproctitle("master [%s]" % self.proc_name) + + try: + self.manage_workers() + + while True: + self.maybe_promote_master() + + sig = self.SIG_QUEUE.pop(0) if self.SIG_QUEUE else None + if sig is None: + self.sleep() + self.murder_workers() + self.manage_workers() + continue + + if sig not in self.SIG_NAMES: + self.log.info("Ignoring unknown signal: %s", sig) + continue + + signame = self.SIG_NAMES.get(sig) + handler = getattr(self, "handle_%s" % signame, None) + if not handler: + self.log.error("Unhandled signal: %s", signame) + continue + self.log.info("Handling signal: %s", signame) + handler() + self.wakeup() + except StopIteration: + self.halt() + except KeyboardInterrupt: + self.halt() + except HaltServer as inst: + self.halt(reason=inst.reason, exit_status=inst.exit_status) + except SystemExit: + raise + except Exception: + self.log.info("Unhandled exception in main loop", + exc_info=True) + self.stop(False) + if self.pidfile is not None: + self.pidfile.unlink() + sys.exit(-1) + + def handle_chld(self, sig, frame): + "SIGCHLD handling" + self.reap_workers() + self.wakeup() + + def handle_hup(self): + """\ + HUP handling. + - Reload configuration + - Start the new worker processes with a new configuration + - Gracefully shutdown the old worker processes + """ + self.log.info("Hang up: %s", self.master_name) + self.reload() + + def handle_term(self): + "SIGTERM handling" + raise StopIteration + + def handle_int(self): + "SIGINT handling" + self.stop(False) + raise StopIteration + + def handle_quit(self): + "SIGQUIT handling" + self.stop(False) + raise StopIteration + + def handle_ttin(self): + """\ + SIGTTIN handling. + Increases the number of workers by one. + """ + self.num_workers += 1 + self.manage_workers() + + def handle_ttou(self): + """\ + SIGTTOU handling. + Decreases the number of workers by one. + """ + if self.num_workers <= 1: + return + self.num_workers -= 1 + self.manage_workers() + + def handle_usr1(self): + """\ + SIGUSR1 handling. + Kill all workers by sending them a SIGUSR1 + """ + self.log.reopen_files() + self.kill_workers(signal.SIGUSR1) + + def handle_usr2(self): + """\ + SIGUSR2 handling. + Creates a new master/worker set as a slave of the current + master without affecting old workers. Use this to do live + deployment with the ability to backout a change. + """ + self.reexec() + + def handle_winch(self): + """SIGWINCH handling""" + if self.cfg.daemon: + self.log.info("graceful stop of workers") + self.num_workers = 0 + self.kill_workers(signal.SIGTERM) + else: + self.log.debug("SIGWINCH ignored. Not daemonized") + + def maybe_promote_master(self): + if self.master_pid == 0: + return + + if self.master_pid != os.getppid(): + self.log.info("Master has been promoted.") + # reset master infos + self.master_name = "Master" + self.master_pid = 0 + self.proc_name = self.cfg.proc_name + del os.environ['GUNICORN_PID'] + # rename the pidfile + if self.pidfile is not None: + self.pidfile.rename(self.cfg.pidfile) + # reset proctitle + util._setproctitle("master [%s]" % self.proc_name) + + def wakeup(self): + """\ + Wake up the arbiter by writing to the PIPE + """ + try: + os.write(self.PIPE[1], b'.') + except IOError as e: + if e.errno not in [errno.EAGAIN, errno.EINTR]: + raise + + def halt(self, reason=None, exit_status=0): + """ halt arbiter """ + self.stop() + self.log.info("Shutting down: %s", self.master_name) + if reason is not None: + self.log.info("Reason: %s", reason) + if self.pidfile is not None: + self.pidfile.unlink() + self.cfg.on_exit(self) + sys.exit(exit_status) + + def sleep(self): + """\ + Sleep until PIPE is readable or we timeout. + A readable PIPE means a signal occurred. + """ + try: + ready = select.select([self.PIPE[0]], [], [], 1.0) + if not ready[0]: + return + while os.read(self.PIPE[0], 1): + pass + except (select.error, OSError) as e: + # TODO: select.error is a subclass of OSError since Python 3.3. + error_number = getattr(e, 'errno', e.args[0]) + if error_number not in [errno.EAGAIN, errno.EINTR]: + raise + except KeyboardInterrupt: + sys.exit() + + def stop(self, graceful=True): + """\ + Stop workers + + :attr graceful: boolean, If True (the default) workers will be + killed gracefully (ie. trying to wait for the current connection) + """ + + unlink = self.reexec_pid == self.master_pid == 0 and not self.systemd + sock.close_sockets(self.LISTENERS, unlink) + + self.LISTENERS = [] + sig = signal.SIGTERM + if not graceful: + sig = signal.SIGQUIT + limit = time.time() + self.cfg.graceful_timeout + # instruct the workers to exit + self.kill_workers(sig) + # wait until the graceful timeout + while self.WORKERS and time.time() < limit: + time.sleep(0.1) + + self.kill_workers(signal.SIGKILL) + + def reexec(self): + """\ + Relaunch the master and workers. + """ + if self.reexec_pid != 0: + self.log.warning("USR2 signal ignored. Child exists.") + return + + if self.master_pid != 0: + self.log.warning("USR2 signal ignored. Parent exists.") + return + + master_pid = os.getpid() + self.reexec_pid = os.fork() + if self.reexec_pid != 0: + return + + self.cfg.pre_exec(self) + + environ = self.cfg.env_orig.copy() + environ['GUNICORN_PID'] = str(master_pid) + + if self.systemd: + environ['LISTEN_PID'] = str(os.getpid()) + environ['LISTEN_FDS'] = str(len(self.LISTENERS)) + else: + environ['GUNICORN_FD'] = ','.join( + str(l.fileno()) for l in self.LISTENERS) + + os.chdir(self.START_CTX['cwd']) + + # exec the process using the original environment + os.execvpe(self.START_CTX[0], self.START_CTX['args'], environ) + + def reload(self): + old_address = self.cfg.address + + # reset old environment + for k in self.cfg.env: + if k in self.cfg.env_orig: + # reset the key to the value it had before + # we launched gunicorn + os.environ[k] = self.cfg.env_orig[k] + else: + # delete the value set by gunicorn + try: + del os.environ[k] + except KeyError: + pass + + # reload conf + self.app.reload() + self.setup(self.app) + + # reopen log files + self.log.reopen_files() + + # do we need to change listener ? + if old_address != self.cfg.address: + # close all listeners + for l in self.LISTENERS: + l.close() + # init new listeners + self.LISTENERS = sock.create_sockets(self.cfg, self.log) + listeners_str = ",".join([str(l) for l in self.LISTENERS]) + self.log.info("Listening at: %s", listeners_str) + + # do some actions on reload + self.cfg.on_reload(self) + + # unlink pidfile + if self.pidfile is not None: + self.pidfile.unlink() + + # create new pidfile + if self.cfg.pidfile is not None: + self.pidfile = Pidfile(self.cfg.pidfile) + self.pidfile.create(self.pid) + + # set new proc_name + util._setproctitle("master [%s]" % self.proc_name) + + # spawn new workers + for _ in range(self.cfg.workers): + self.spawn_worker() + + # manage workers + self.manage_workers() + + def murder_workers(self): + """\ + Kill unused/idle workers + """ + if not self.timeout: + return + workers = list(self.WORKERS.items()) + for (pid, worker) in workers: + try: + if time.time() - worker.tmp.last_update() <= self.timeout: + continue + except (OSError, ValueError): + continue + + if not worker.aborted: + self.log.critical("WORKER TIMEOUT (pid:%s)", pid) + worker.aborted = True + self.kill_worker(pid, signal.SIGABRT) + else: + self.kill_worker(pid, signal.SIGKILL) + + def reap_workers(self): + """\ + Reap workers to avoid zombie processes + """ + try: + while True: + wpid, status = os.waitpid(-1, os.WNOHANG) + if not wpid: + break + if self.reexec_pid == wpid: + self.reexec_pid = 0 + else: + # A worker was terminated. If the termination reason was + # that it could not boot, we'll shut it down to avoid + # infinite start/stop cycles. + exitcode = status >> 8 + if exitcode == self.WORKER_BOOT_ERROR: + reason = "Worker failed to boot." + raise HaltServer(reason, self.WORKER_BOOT_ERROR) + if exitcode == self.APP_LOAD_ERROR: + reason = "App failed to load." + raise HaltServer(reason, self.APP_LOAD_ERROR) + + worker = self.WORKERS.pop(wpid, None) + if not worker: + continue + worker.tmp.close() + self.cfg.child_exit(self, worker) + except OSError as e: + if e.errno != errno.ECHILD: + raise + + def manage_workers(self): + """\ + Maintain the number of workers by spawning or killing + as required. + """ + if len(self.WORKERS.keys()) < self.num_workers: + self.spawn_workers() + + workers = self.WORKERS.items() + workers = sorted(workers, key=lambda w: w[1].age) + while len(workers) > self.num_workers: + (pid, _) = workers.pop(0) + self.kill_worker(pid, signal.SIGTERM) + + active_worker_count = len(workers) + if self._last_logged_active_worker_count != active_worker_count: + self._last_logged_active_worker_count = active_worker_count + self.log.debug("{0} workers".format(active_worker_count), + extra={"metric": "gunicorn.workers", + "value": active_worker_count, + "mtype": "gauge"}) + + def spawn_worker(self): + self.worker_age += 1 + worker = self.worker_class(self.worker_age, self.pid, self.LISTENERS, + self.app, self.timeout / 2.0, + self.cfg, self.log) + self.cfg.pre_fork(self, worker) + pid = os.fork() + if pid != 0: + worker.pid = pid + self.WORKERS[pid] = worker + return pid + + # Do not inherit the temporary files of other workers + for sibling in self.WORKERS.values(): + sibling.tmp.close() + + # Process Child + worker.pid = os.getpid() + try: + util._setproctitle("worker [%s]" % self.proc_name) + self.log.info("Booting worker with pid: %s", worker.pid) + self.cfg.post_fork(self, worker) + worker.init_process() + sys.exit(0) + except SystemExit: + raise + except AppImportError as e: + self.log.debug("Exception while loading the application", + exc_info=True) + print("%s" % e, file=sys.stderr) + sys.stderr.flush() + sys.exit(self.APP_LOAD_ERROR) + except: + self.log.exception("Exception in worker process") + if not worker.booted: + sys.exit(self.WORKER_BOOT_ERROR) + sys.exit(-1) + finally: + self.log.info("Worker exiting (pid: %s)", worker.pid) + try: + worker.tmp.close() + self.cfg.worker_exit(self, worker) + except: + self.log.warning("Exception during worker exit:\n%s", + traceback.format_exc()) + + def spawn_workers(self): + """\ + Spawn new workers as needed. + + This is where a worker process leaves the main loop + of the master process. + """ + + for _ in range(self.num_workers - len(self.WORKERS.keys())): + self.spawn_worker() + time.sleep(0.1 * random.random()) + + def kill_workers(self, sig): + """\ + Kill all workers with the signal `sig` + :attr sig: `signal.SIG*` value + """ + worker_pids = list(self.WORKERS.keys()) + for pid in worker_pids: + self.kill_worker(pid, sig) + + def kill_worker(self, pid, sig): + """\ + Kill a worker + + :attr pid: int, worker pid + :attr sig: `signal.SIG*` value + """ + try: + os.kill(pid, sig) + except OSError as e: + if e.errno == errno.ESRCH: + try: + worker = self.WORKERS.pop(pid) + worker.tmp.close() + self.cfg.worker_exit(self, worker) + return + except (KeyError, OSError): + return + raise diff --git a/venv/lib/python3.6/site-packages/gunicorn/argparse_compat.py b/venv/lib/python3.6/site-packages/gunicorn/argparse_compat.py new file mode 100644 index 0000000..32d948c --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/argparse_compat.py @@ -0,0 +1,2362 @@ +# Author: Steven J. Bethard . + +"""Command-line parsing library + +This module is an optparse-inspired command-line parsing library that: + + - handles both optional and positional arguments + - produces highly informative usage messages + - supports parsers that dispatch to sub-parsers + +The following is a simple usage example that sums integers from the +command-line and writes the result to a file:: + + parser = argparse.ArgumentParser( + description='sum the integers at the command line') + parser.add_argument( + 'integers', metavar='int', nargs='+', type=int, + help='an integer to be summed') + parser.add_argument( + '--log', default=sys.stdout, type=argparse.FileType('w'), + help='the file where the sum should be written') + args = parser.parse_args() + args.log.write('%s' % sum(args.integers)) + args.log.close() + +The module contains the following public classes: + + - ArgumentParser -- The main entry point for command-line parsing. As the + example above shows, the add_argument() method is used to populate + the parser with actions for optional and positional arguments. Then + the parse_args() method is invoked to convert the args at the + command-line into an object with attributes. + + - ArgumentError -- The exception raised by ArgumentParser objects when + there are errors with the parser's actions. Errors raised while + parsing the command-line are caught by ArgumentParser and emitted + as command-line messages. + + - FileType -- A factory for defining types of files to be created. As the + example above shows, instances of FileType are typically passed as + the type= argument of add_argument() calls. + + - Action -- The base class for parser actions. Typically actions are + selected by passing strings like 'store_true' or 'append_const' to + the action= argument of add_argument(). However, for greater + customization of ArgumentParser actions, subclasses of Action may + be defined and passed as the action= argument. + + - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, + ArgumentDefaultsHelpFormatter -- Formatter classes which + may be passed as the formatter_class= argument to the + ArgumentParser constructor. HelpFormatter is the default, + RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser + not to change the formatting for help text, and + ArgumentDefaultsHelpFormatter adds information about argument defaults + to the help. + +All other classes in this module are considered implementation details. +(Also note that HelpFormatter and RawDescriptionHelpFormatter are only +considered public as object names -- the API of the formatter objects is +still considered an implementation detail.) +""" + +__version__ = '1.2.1' +__all__ = [ + 'ArgumentParser', + 'ArgumentError', + 'ArgumentTypeError', + 'FileType', + 'HelpFormatter', + 'ArgumentDefaultsHelpFormatter', + 'RawDescriptionHelpFormatter', + 'RawTextHelpFormatter', + 'Namespace', + 'Action', + 'ONE_OR_MORE', + 'OPTIONAL', + 'PARSER', + 'REMAINDER', + 'SUPPRESS', + 'ZERO_OR_MORE', +] + + +import copy as _copy +import os as _os +import re as _re +import sys as _sys +import textwrap as _textwrap + +from gettext import gettext as _ + +try: + set +except NameError: + # for python < 2.4 compatibility (sets module is there since 2.3): + from sets import Set as set + +try: + basestring +except NameError: + basestring = str + +try: + sorted +except NameError: + # for python < 2.4 compatibility: + def sorted(iterable, reverse=False): + result = list(iterable) + result.sort() + if reverse: + result.reverse() + return result + + +def _callable(obj): + return hasattr(obj, '__call__') or hasattr(obj, '__bases__') + + +SUPPRESS = '==SUPPRESS==' + +OPTIONAL = '?' +ZERO_OR_MORE = '*' +ONE_OR_MORE = '+' +PARSER = 'A...' +REMAINDER = '...' +_UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args' + +# ============================= +# Utility functions and classes +# ============================= + +class _AttributeHolder(object): + """Abstract base class that provides __repr__. + + The __repr__ method returns a string in the format:: + ClassName(attr=name, attr=name, ...) + The attributes are determined either by a class-level attribute, + '_kwarg_names', or by inspecting the instance __dict__. + """ + + def __repr__(self): + type_name = type(self).__name__ + arg_strings = [] + for arg in self._get_args(): + arg_strings.append(repr(arg)) + for name, value in self._get_kwargs(): + arg_strings.append('%s=%r' % (name, value)) + return '%s(%s)' % (type_name, ', '.join(arg_strings)) + + def _get_kwargs(self): + return sorted(self.__dict__.items()) + + def _get_args(self): + return [] + + +def _ensure_value(namespace, name, value): + if getattr(namespace, name, None) is None: + setattr(namespace, name, value) + return getattr(namespace, name) + + +# =============== +# Formatting Help +# =============== + +class HelpFormatter(object): + """Formatter for generating usage messages and argument help strings. + + Only the name of this class is considered a public API. All the methods + provided by the class are considered an implementation detail. + """ + + def __init__(self, + prog, + indent_increment=2, + max_help_position=24, + width=None): + + # default setting for width + if width is None: + try: + width = int(_os.environ['COLUMNS']) + except (KeyError, ValueError): + width = 80 + width -= 2 + + self._prog = prog + self._indent_increment = indent_increment + self._max_help_position = max_help_position + self._width = width + + self._current_indent = 0 + self._level = 0 + self._action_max_length = 0 + + self._root_section = self._Section(self, None) + self._current_section = self._root_section + + self._whitespace_matcher = _re.compile(r'\s+') + self._long_break_matcher = _re.compile(r'\n\n\n+') + + # =============================== + # Section and indentation methods + # =============================== + def _indent(self): + self._current_indent += self._indent_increment + self._level += 1 + + def _dedent(self): + self._current_indent -= self._indent_increment + assert self._current_indent >= 0, 'Indent decreased below 0.' + self._level -= 1 + + class _Section(object): + + def __init__(self, formatter, parent, heading=None): + self.formatter = formatter + self.parent = parent + self.heading = heading + self.items = [] + + def format_help(self): + # format the indented section + if self.parent is not None: + self.formatter._indent() + join = self.formatter._join_parts + for func, args in self.items: + func(*args) + item_help = join([func(*args) for func, args in self.items]) + if self.parent is not None: + self.formatter._dedent() + + # return nothing if the section was empty + if not item_help: + return '' + + # add the heading if the section was non-empty + if self.heading is not SUPPRESS and self.heading is not None: + current_indent = self.formatter._current_indent + heading = '%*s%s:\n' % (current_indent, '', self.heading) + else: + heading = '' + + # join the section-initial newline, the heading and the help + return join(['\n', heading, item_help, '\n']) + + def _add_item(self, func, args): + self._current_section.items.append((func, args)) + + # ======================== + # Message building methods + # ======================== + def start_section(self, heading): + self._indent() + section = self._Section(self, self._current_section, heading) + self._add_item(section.format_help, []) + self._current_section = section + + def end_section(self): + self._current_section = self._current_section.parent + self._dedent() + + def add_text(self, text): + if text is not SUPPRESS and text is not None: + self._add_item(self._format_text, [text]) + + def add_usage(self, usage, actions, groups, prefix=None): + if usage is not SUPPRESS: + args = usage, actions, groups, prefix + self._add_item(self._format_usage, args) + + def add_argument(self, action): + if action.help is not SUPPRESS: + + # find all invocations + get_invocation = self._format_action_invocation + invocations = [get_invocation(action)] + for subaction in self._iter_indented_subactions(action): + invocations.append(get_invocation(subaction)) + + # update the maximum item length + invocation_length = max([len(s) for s in invocations]) + action_length = invocation_length + self._current_indent + self._action_max_length = max(self._action_max_length, + action_length) + + # add the item to the list + self._add_item(self._format_action, [action]) + + def add_arguments(self, actions): + for action in actions: + self.add_argument(action) + + # ======================= + # Help-formatting methods + # ======================= + def format_help(self): + help = self._root_section.format_help() + if help: + help = self._long_break_matcher.sub('\n\n', help) + help = help.strip('\n') + '\n' + return help + + def _join_parts(self, part_strings): + return ''.join([part + for part in part_strings + if part and part is not SUPPRESS]) + + def _format_usage(self, usage, actions, groups, prefix): + if prefix is None: + prefix = _('usage: ') + + # if usage is specified, use that + if usage is not None: + usage = usage % dict(prog=self._prog) + + # if no optionals or positionals are available, usage is just prog + elif usage is None and not actions: + usage = '%(prog)s' % dict(prog=self._prog) + + # if optionals and positionals are available, calculate usage + elif usage is None: + prog = '%(prog)s' % dict(prog=self._prog) + + # split optionals from positionals + optionals = [] + positionals = [] + for action in actions: + if action.option_strings: + optionals.append(action) + else: + positionals.append(action) + + # build full usage string + format = self._format_actions_usage + action_usage = format(optionals + positionals, groups) + usage = ' '.join([s for s in [prog, action_usage] if s]) + + # wrap the usage parts if it's too long + text_width = self._width - self._current_indent + if len(prefix) + len(usage) > text_width: + + # break usage into wrappable parts + part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' + opt_usage = format(optionals, groups) + pos_usage = format(positionals, groups) + opt_parts = _re.findall(part_regexp, opt_usage) + pos_parts = _re.findall(part_regexp, pos_usage) + assert ' '.join(opt_parts) == opt_usage + assert ' '.join(pos_parts) == pos_usage + + # helper for wrapping lines + def get_lines(parts, indent, prefix=None): + lines = [] + line = [] + if prefix is not None: + line_len = len(prefix) - 1 + else: + line_len = len(indent) - 1 + for part in parts: + if line_len + 1 + len(part) > text_width: + lines.append(indent + ' '.join(line)) + line = [] + line_len = len(indent) - 1 + line.append(part) + line_len += len(part) + 1 + if line: + lines.append(indent + ' '.join(line)) + if prefix is not None: + lines[0] = lines[0][len(indent):] + return lines + + # if prog is short, follow it with optionals or positionals + if len(prefix) + len(prog) <= 0.75 * text_width: + indent = ' ' * (len(prefix) + len(prog) + 1) + if opt_parts: + lines = get_lines([prog] + opt_parts, indent, prefix) + lines.extend(get_lines(pos_parts, indent)) + elif pos_parts: + lines = get_lines([prog] + pos_parts, indent, prefix) + else: + lines = [prog] + + # if prog is long, put it on its own line + else: + indent = ' ' * len(prefix) + parts = opt_parts + pos_parts + lines = get_lines(parts, indent) + if len(lines) > 1: + lines = [] + lines.extend(get_lines(opt_parts, indent)) + lines.extend(get_lines(pos_parts, indent)) + lines = [prog] + lines + + # join lines into usage + usage = '\n'.join(lines) + + # prefix with 'usage:' + return '%s%s\n\n' % (prefix, usage) + + def _format_actions_usage(self, actions, groups): + # find group indices and identify actions in groups + group_actions = set() + inserts = {} + for group in groups: + try: + start = actions.index(group._group_actions[0]) + except ValueError: + continue + else: + end = start + len(group._group_actions) + if actions[start:end] == group._group_actions: + for action in group._group_actions: + group_actions.add(action) + if not group.required: + if start in inserts: + inserts[start] += ' [' + else: + inserts[start] = '[' + inserts[end] = ']' + else: + if start in inserts: + inserts[start] += ' (' + else: + inserts[start] = '(' + inserts[end] = ')' + for i in range(start + 1, end): + inserts[i] = '|' + + # collect all actions format strings + parts = [] + for i, action in enumerate(actions): + + # suppressed arguments are marked with None + # remove | separators for suppressed arguments + if action.help is SUPPRESS: + parts.append(None) + if inserts.get(i) == '|': + inserts.pop(i) + elif inserts.get(i + 1) == '|': + inserts.pop(i + 1) + + # produce all arg strings + elif not action.option_strings: + part = self._format_args(action, action.dest) + + # if it's in a group, strip the outer [] + if action in group_actions: + if part[0] == '[' and part[-1] == ']': + part = part[1:-1] + + # add the action string to the list + parts.append(part) + + # produce the first way to invoke the option in brackets + else: + option_string = action.option_strings[0] + + # if the Optional doesn't take a value, format is: + # -s or --long + if action.nargs == 0: + part = '%s' % option_string + + # if the Optional takes a value, format is: + # -s ARGS or --long ARGS + else: + default = action.dest.upper() + args_string = self._format_args(action, default) + part = '%s %s' % (option_string, args_string) + + # make it look optional if it's not required or in a group + if not action.required and action not in group_actions: + part = '[%s]' % part + + # add the action string to the list + parts.append(part) + + # insert things at the necessary indices + for i in sorted(inserts, reverse=True): + parts[i:i] = [inserts[i]] + + # join all the action items with spaces + text = ' '.join([item for item in parts if item is not None]) + + # clean up separators for mutually exclusive groups + open = r'[\[(]' + close = r'[\])]' + text = _re.sub(r'(%s) ' % open, r'\1', text) + text = _re.sub(r' (%s)' % close, r'\1', text) + text = _re.sub(r'%s *%s' % (open, close), r'', text) + text = _re.sub(r'\(([^|]*)\)', r'\1', text) + text = text.strip() + + # return the text + return text + + def _format_text(self, text): + if '%(prog)' in text: + text = text % dict(prog=self._prog) + text_width = self._width - self._current_indent + indent = ' ' * self._current_indent + return self._fill_text(text, text_width, indent) + '\n\n' + + def _format_action(self, action): + # determine the required width and the entry label + help_position = min(self._action_max_length + 2, + self._max_help_position) + help_width = self._width - help_position + action_width = help_position - self._current_indent - 2 + action_header = self._format_action_invocation(action) + + # ho nelp; start on same line and add a final newline + if not action.help: + tup = self._current_indent, '', action_header + action_header = '%*s%s\n' % tup + + # short action name; start on the same line and pad two spaces + elif len(action_header) <= action_width: + tup = self._current_indent, '', action_width, action_header + action_header = '%*s%-*s ' % tup + indent_first = 0 + + # long action name; start on the next line + else: + tup = self._current_indent, '', action_header + action_header = '%*s%s\n' % tup + indent_first = help_position + + # collect the pieces of the action help + parts = [action_header] + + # if there was help for the action, add lines of help text + if action.help: + help_text = self._expand_help(action) + help_lines = self._split_lines(help_text, help_width) + parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) + for line in help_lines[1:]: + parts.append('%*s%s\n' % (help_position, '', line)) + + # or add a newline if the description doesn't end with one + elif not action_header.endswith('\n'): + parts.append('\n') + + # if there are any sub-actions, add their help as well + for subaction in self._iter_indented_subactions(action): + parts.append(self._format_action(subaction)) + + # return a single string + return self._join_parts(parts) + + def _format_action_invocation(self, action): + if not action.option_strings: + metavar, = self._metavar_formatter(action, action.dest)(1) + return metavar + + else: + parts = [] + + # if the Optional doesn't take a value, format is: + # -s, --long + if action.nargs == 0: + parts.extend(action.option_strings) + + # if the Optional takes a value, format is: + # -s ARGS, --long ARGS + else: + default = action.dest.upper() + args_string = self._format_args(action, default) + for option_string in action.option_strings: + parts.append('%s %s' % (option_string, args_string)) + + return ', '.join(parts) + + def _metavar_formatter(self, action, default_metavar): + if action.metavar is not None: + result = action.metavar + elif action.choices is not None: + choice_strs = [str(choice) for choice in action.choices] + result = '{%s}' % ','.join(choice_strs) + else: + result = default_metavar + + def format(tuple_size): + if isinstance(result, tuple): + return result + else: + return (result, ) * tuple_size + return format + + def _format_args(self, action, default_metavar): + get_metavar = self._metavar_formatter(action, default_metavar) + if action.nargs is None: + result = '%s' % get_metavar(1) + elif action.nargs == OPTIONAL: + result = '[%s]' % get_metavar(1) + elif action.nargs == ZERO_OR_MORE: + result = '[%s [%s ...]]' % get_metavar(2) + elif action.nargs == ONE_OR_MORE: + result = '%s [%s ...]' % get_metavar(2) + elif action.nargs == REMAINDER: + result = '...' + elif action.nargs == PARSER: + result = '%s ...' % get_metavar(1) + else: + formats = ['%s' for _ in range(action.nargs)] + result = ' '.join(formats) % get_metavar(action.nargs) + return result + + def _expand_help(self, action): + params = dict(vars(action), prog=self._prog) + for name in list(params): + if params[name] is SUPPRESS: + del params[name] + for name in list(params): + if hasattr(params[name], '__name__'): + params[name] = params[name].__name__ + if params.get('choices') is not None: + choices_str = ', '.join([str(c) for c in params['choices']]) + params['choices'] = choices_str + return self._get_help_string(action) % params + + def _iter_indented_subactions(self, action): + try: + get_subactions = action._get_subactions + except AttributeError: + pass + else: + self._indent() + for subaction in get_subactions(): + yield subaction + self._dedent() + + def _split_lines(self, text, width): + text = self._whitespace_matcher.sub(' ', text).strip() + return _textwrap.wrap(text, width) + + def _fill_text(self, text, width, indent): + text = self._whitespace_matcher.sub(' ', text).strip() + return _textwrap.fill(text, width, initial_indent=indent, + subsequent_indent=indent) + + def _get_help_string(self, action): + return action.help + + +class RawDescriptionHelpFormatter(HelpFormatter): + """Help message formatter which retains any formatting in descriptions. + + Only the name of this class is considered a public API. All the methods + provided by the class are considered an implementation detail. + """ + + def _fill_text(self, text, width, indent): + return ''.join([indent + line for line in text.splitlines(True)]) + + +class RawTextHelpFormatter(RawDescriptionHelpFormatter): + """Help message formatter which retains formatting of all help text. + + Only the name of this class is considered a public API. All the methods + provided by the class are considered an implementation detail. + """ + + def _split_lines(self, text, width): + return text.splitlines() + + +class ArgumentDefaultsHelpFormatter(HelpFormatter): + """Help message formatter which adds default values to argument help. + + Only the name of this class is considered a public API. All the methods + provided by the class are considered an implementation detail. + """ + + def _get_help_string(self, action): + help = action.help + if '%(default)' not in action.help: + if action.default is not SUPPRESS: + defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] + if action.option_strings or action.nargs in defaulting_nargs: + help += ' (default: %(default)s)' + return help + + +# ===================== +# Options and Arguments +# ===================== + +def _get_action_name(argument): + if argument is None: + return None + elif argument.option_strings: + return '/'.join(argument.option_strings) + elif argument.metavar not in (None, SUPPRESS): + return argument.metavar + elif argument.dest not in (None, SUPPRESS): + return argument.dest + else: + return None + + +class ArgumentError(Exception): + """An error from creating or using an argument (optional or positional). + + The string value of this exception is the message, augmented with + information about the argument that caused it. + """ + + def __init__(self, argument, message): + self.argument_name = _get_action_name(argument) + self.message = message + + def __str__(self): + if self.argument_name is None: + format = '%(message)s' + else: + format = 'argument %(argument_name)s: %(message)s' + return format % dict(message=self.message, + argument_name=self.argument_name) + + +class ArgumentTypeError(Exception): + """An error from trying to convert a command line string to a type.""" + pass + + +# ============== +# Action classes +# ============== + +class Action(_AttributeHolder): + """Information about how to convert command line strings to Python objects. + + Action objects are used by an ArgumentParser to represent the information + needed to parse a single argument from one or more strings from the + command line. The keyword arguments to the Action constructor are also + all attributes of Action instances. + + Keyword Arguments: + + - option_strings -- A list of command-line option strings which + should be associated with this action. + + - dest -- The name of the attribute to hold the created object(s) + + - nargs -- The number of command-line arguments that should be + consumed. By default, one argument will be consumed and a single + value will be produced. Other values include: + - N (an integer) consumes N arguments (and produces a list) + - '?' consumes zero or one arguments + - '*' consumes zero or more arguments (and produces a list) + - '+' consumes one or more arguments (and produces a list) + Note that the difference between the default and nargs=1 is that + with the default, a single value will be produced, while with + nargs=1, a list containing a single value will be produced. + + - const -- The value to be produced if the option is specified and the + option uses an action that takes no values. + + - default -- The value to be produced if the option is not specified. + + - type -- The type which the command-line arguments should be converted + to, should be one of 'string', 'int', 'float', 'complex' or a + callable object that accepts a single string argument. If None, + 'string' is assumed. + + - choices -- A container of values that should be allowed. If not None, + after a command-line argument has been converted to the appropriate + type, an exception will be raised if it is not a member of this + collection. + + - required -- True if the action must always be specified at the + command line. This is only meaningful for optional command-line + arguments. + + - help -- The help string describing the argument. + + - metavar -- The name to be used for the option's argument with the + help string. If None, the 'dest' value will be used as the name. + """ + + def __init__(self, + option_strings, + dest, + nargs=None, + const=None, + default=None, + type=None, + choices=None, + required=False, + help=None, + metavar=None): + self.option_strings = option_strings + self.dest = dest + self.nargs = nargs + self.const = const + self.default = default + self.type = type + self.choices = choices + self.required = required + self.help = help + self.metavar = metavar + + def _get_kwargs(self): + names = [ + 'option_strings', + 'dest', + 'nargs', + 'const', + 'default', + 'type', + 'choices', + 'help', + 'metavar', + ] + return [(name, getattr(self, name)) for name in names] + + def __call__(self, parser, namespace, values, option_string=None): + raise NotImplementedError(_('.__call__() not defined')) + + +class _StoreAction(Action): + + def __init__(self, + option_strings, + dest, + nargs=None, + const=None, + default=None, + type=None, + choices=None, + required=False, + help=None, + metavar=None): + if nargs == 0: + raise ValueError('nargs for store actions must be > 0; if you ' + 'have nothing to store, actions such as store ' + 'true or store const may be more appropriate') + if const is not None and nargs != OPTIONAL: + raise ValueError('nargs must be %r to supply const' % OPTIONAL) + super(_StoreAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=nargs, + const=const, + default=default, + type=type, + choices=choices, + required=required, + help=help, + metavar=metavar) + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, values) + + +class _StoreConstAction(Action): + + def __init__(self, + option_strings, + dest, + const, + default=None, + required=False, + help=None, + metavar=None): + super(_StoreConstAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + const=const, + default=default, + required=required, + help=help) + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, self.const) + + +class _StoreTrueAction(_StoreConstAction): + + def __init__(self, + option_strings, + dest, + default=False, + required=False, + help=None): + super(_StoreTrueAction, self).__init__( + option_strings=option_strings, + dest=dest, + const=True, + default=default, + required=required, + help=help) + + +class _StoreFalseAction(_StoreConstAction): + + def __init__(self, + option_strings, + dest, + default=True, + required=False, + help=None): + super(_StoreFalseAction, self).__init__( + option_strings=option_strings, + dest=dest, + const=False, + default=default, + required=required, + help=help) + + +class _AppendAction(Action): + + def __init__(self, + option_strings, + dest, + nargs=None, + const=None, + default=None, + type=None, + choices=None, + required=False, + help=None, + metavar=None): + if nargs == 0: + raise ValueError('nargs for append actions must be > 0; if arg ' + 'strings are not supplying the value to append, ' + 'the append const action may be more appropriate') + if const is not None and nargs != OPTIONAL: + raise ValueError('nargs must be %r to supply const' % OPTIONAL) + super(_AppendAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=nargs, + const=const, + default=default, + type=type, + choices=choices, + required=required, + help=help, + metavar=metavar) + + def __call__(self, parser, namespace, values, option_string=None): + items = _copy.copy(_ensure_value(namespace, self.dest, [])) + items.append(values) + setattr(namespace, self.dest, items) + + +class _AppendConstAction(Action): + + def __init__(self, + option_strings, + dest, + const, + default=None, + required=False, + help=None, + metavar=None): + super(_AppendConstAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + const=const, + default=default, + required=required, + help=help, + metavar=metavar) + + def __call__(self, parser, namespace, values, option_string=None): + items = _copy.copy(_ensure_value(namespace, self.dest, [])) + items.append(self.const) + setattr(namespace, self.dest, items) + + +class _CountAction(Action): + + def __init__(self, + option_strings, + dest, + default=None, + required=False, + help=None): + super(_CountAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + default=default, + required=required, + help=help) + + def __call__(self, parser, namespace, values, option_string=None): + new_count = _ensure_value(namespace, self.dest, 0) + 1 + setattr(namespace, self.dest, new_count) + + +class _HelpAction(Action): + + def __init__(self, + option_strings, + dest=SUPPRESS, + default=SUPPRESS, + help=None): + super(_HelpAction, self).__init__( + option_strings=option_strings, + dest=dest, + default=default, + nargs=0, + help=help) + + def __call__(self, parser, namespace, values, option_string=None): + parser.print_help() + parser.exit() + + +class _VersionAction(Action): + + def __init__(self, + option_strings, + version=None, + dest=SUPPRESS, + default=SUPPRESS, + help="show program's version number and exit"): + super(_VersionAction, self).__init__( + option_strings=option_strings, + dest=dest, + default=default, + nargs=0, + help=help) + self.version = version + + def __call__(self, parser, namespace, values, option_string=None): + version = self.version + if version is None: + version = parser.version + formatter = parser._get_formatter() + formatter.add_text(version) + parser.exit(message=formatter.format_help()) + + +class _SubParsersAction(Action): + + class _ChoicesPseudoAction(Action): + + def __init__(self, name, help): + sup = super(_SubParsersAction._ChoicesPseudoAction, self) + sup.__init__(option_strings=[], dest=name, help=help) + + def __init__(self, + option_strings, + prog, + parser_class, + dest=SUPPRESS, + help=None, + metavar=None): + + self._prog_prefix = prog + self._parser_class = parser_class + self._name_parser_map = {} + self._choices_actions = [] + + super(_SubParsersAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=PARSER, + choices=self._name_parser_map, + help=help, + metavar=metavar) + + def add_parser(self, name, **kwargs): + # set prog from the existing prefix + if kwargs.get('prog') is None: + kwargs['prog'] = '%s %s' % (self._prog_prefix, name) + + # create a pseudo-action to hold the choice help + if 'help' in kwargs: + help = kwargs.pop('help') + choice_action = self._ChoicesPseudoAction(name, help) + self._choices_actions.append(choice_action) + + # create the parser and add it to the map + parser = self._parser_class(**kwargs) + self._name_parser_map[name] = parser + return parser + + def _get_subactions(self): + return self._choices_actions + + def __call__(self, parser, namespace, values, option_string=None): + parser_name = values[0] + arg_strings = values[1:] + + # set the parser name if requested + if self.dest is not SUPPRESS: + setattr(namespace, self.dest, parser_name) + + # select the parser + try: + parser = self._name_parser_map[parser_name] + except KeyError: + tup = parser_name, ', '.join(self._name_parser_map) + msg = _('unknown parser %r (choices: %s)' % tup) + raise ArgumentError(self, msg) + + # parse all the remaining options into the namespace + # store any unrecognized options on the object, so that the top + # level parser can decide what to do with them + namespace, arg_strings = parser.parse_known_args(arg_strings, namespace) + if arg_strings: + vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, []) + getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings) + + +# ============== +# Type classes +# ============== + +class FileType(object): + """Factory for creating file object types + + Instances of FileType are typically passed as type= arguments to the + ArgumentParser add_argument() method. + + Keyword Arguments: + - mode -- A string indicating how the file is to be opened. Accepts the + same values as the builtin open() function. + - bufsize -- The file's desired buffer size. Accepts the same values as + the builtin open() function. + """ + + def __init__(self, mode='r', bufsize=None): + self._mode = mode + self._bufsize = bufsize + + def __call__(self, string): + # the special argument "-" means sys.std{in,out} + if string == '-': + if 'r' in self._mode: + return _sys.stdin + elif 'w' in self._mode: + return _sys.stdout + else: + msg = _('argument "-" with mode %r' % self._mode) + raise ValueError(msg) + + # all other arguments are used as file names + if self._bufsize: + return open(string, self._mode, self._bufsize) + else: + return open(string, self._mode) + + def __repr__(self): + args = [self._mode, self._bufsize] + args_str = ', '.join([repr(arg) for arg in args if arg is not None]) + return '%s(%s)' % (type(self).__name__, args_str) + +# =========================== +# Optional and Positional Parsing +# =========================== + +class Namespace(_AttributeHolder): + """Simple object for storing attributes. + + Implements equality by attribute names and values, and provides a simple + string representation. + """ + + def __init__(self, **kwargs): + for name in kwargs: + setattr(self, name, kwargs[name]) + + __hash__ = None + + def __eq__(self, other): + return vars(self) == vars(other) + + def __ne__(self, other): + return not (self == other) + + def __contains__(self, key): + return key in self.__dict__ + + +class _ActionsContainer(object): + + def __init__(self, + description, + prefix_chars, + argument_default, + conflict_handler): + super(_ActionsContainer, self).__init__() + + self.description = description + self.argument_default = argument_default + self.prefix_chars = prefix_chars + self.conflict_handler = conflict_handler + + # set up registries + self._registries = {} + + # register actions + self.register('action', None, _StoreAction) + self.register('action', 'store', _StoreAction) + self.register('action', 'store_const', _StoreConstAction) + self.register('action', 'store_true', _StoreTrueAction) + self.register('action', 'store_false', _StoreFalseAction) + self.register('action', 'append', _AppendAction) + self.register('action', 'append_const', _AppendConstAction) + self.register('action', 'count', _CountAction) + self.register('action', 'help', _HelpAction) + self.register('action', 'version', _VersionAction) + self.register('action', 'parsers', _SubParsersAction) + + # raise an exception if the conflict handler is invalid + self._get_handler() + + # action storage + self._actions = [] + self._option_string_actions = {} + + # groups + self._action_groups = [] + self._mutually_exclusive_groups = [] + + # defaults storage + self._defaults = {} + + # determines whether an "option" looks like a negative number + self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$') + + # whether or not there are any optionals that look like negative + # numbers -- uses a list so it can be shared and edited + self._has_negative_number_optionals = [] + + # ==================== + # Registration methods + # ==================== + def register(self, registry_name, value, object): + registry = self._registries.setdefault(registry_name, {}) + registry[value] = object + + def _registry_get(self, registry_name, value, default=None): + return self._registries[registry_name].get(value, default) + + # ================================== + # Namespace default accessor methods + # ================================== + def set_defaults(self, **kwargs): + self._defaults.update(kwargs) + + # if these defaults match any existing arguments, replace + # the previous default on the object with the new one + for action in self._actions: + if action.dest in kwargs: + action.default = kwargs[action.dest] + + def get_default(self, dest): + for action in self._actions: + if action.dest == dest and action.default is not None: + return action.default + return self._defaults.get(dest, None) + + + # ======================= + # Adding argument actions + # ======================= + def add_argument(self, *args, **kwargs): + """ + add_argument(dest, ..., name=value, ...) + add_argument(option_string, option_string, ..., name=value, ...) + """ + + # if no positional args are supplied or only one is supplied and + # it doesn't look like an option string, parse a positional + # argument + chars = self.prefix_chars + if not args or len(args) == 1 and args[0][0] not in chars: + if args and 'dest' in kwargs: + raise ValueError('dest supplied twice for positional argument') + kwargs = self._get_positional_kwargs(*args, **kwargs) + + # otherwise, we're adding an optional argument + else: + kwargs = self._get_optional_kwargs(*args, **kwargs) + + # if no default was supplied, use the parser-level default + if 'default' not in kwargs: + dest = kwargs['dest'] + if dest in self._defaults: + kwargs['default'] = self._defaults[dest] + elif self.argument_default is not None: + kwargs['default'] = self.argument_default + + # create the action object, and add it to the parser + action_class = self._pop_action_class(kwargs) + if not _callable(action_class): + raise ValueError('unknown action "%s"' % action_class) + action = action_class(**kwargs) + + # raise an error if the action type is not callable + type_func = self._registry_get('type', action.type, action.type) + if not _callable(type_func): + raise ValueError('%r is not callable' % type_func) + + return self._add_action(action) + + def add_argument_group(self, *args, **kwargs): + group = _ArgumentGroup(self, *args, **kwargs) + self._action_groups.append(group) + return group + + def add_mutually_exclusive_group(self, **kwargs): + group = _MutuallyExclusiveGroup(self, **kwargs) + self._mutually_exclusive_groups.append(group) + return group + + def _add_action(self, action): + # resolve any conflicts + self._check_conflict(action) + + # add to actions list + self._actions.append(action) + action.container = self + + # index the action by any option strings it has + for option_string in action.option_strings: + self._option_string_actions[option_string] = action + + # set the flag if any option strings look like negative numbers + for option_string in action.option_strings: + if self._negative_number_matcher.match(option_string): + if not self._has_negative_number_optionals: + self._has_negative_number_optionals.append(True) + + # return the created action + return action + + def _remove_action(self, action): + self._actions.remove(action) + + def _add_container_actions(self, container): + # collect groups by titles + title_group_map = {} + for group in self._action_groups: + if group.title in title_group_map: + msg = _('cannot merge actions - two groups are named %r') + raise ValueError(msg % (group.title)) + title_group_map[group.title] = group + + # map each action to its group + group_map = {} + for group in container._action_groups: + + # if a group with the title exists, use that, otherwise + # create a new group matching the container's group + if group.title not in title_group_map: + title_group_map[group.title] = self.add_argument_group( + title=group.title, + description=group.description, + conflict_handler=group.conflict_handler) + + # map the actions to their new group + for action in group._group_actions: + group_map[action] = title_group_map[group.title] + + # add container's mutually exclusive groups + # NOTE: if add_mutually_exclusive_group ever gains title= and + # description= then this code will need to be expanded as above + for group in container._mutually_exclusive_groups: + mutex_group = self.add_mutually_exclusive_group( + required=group.required) + + # map the actions to their new mutex group + for action in group._group_actions: + group_map[action] = mutex_group + + # add all actions to this container or their group + for action in container._actions: + group_map.get(action, self)._add_action(action) + + def _get_positional_kwargs(self, dest, **kwargs): + # make sure required is not specified + if 'required' in kwargs: + msg = _("'required' is an invalid argument for positionals") + raise TypeError(msg) + + # mark positional arguments as required if at least one is + # always required + if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: + kwargs['required'] = True + if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: + kwargs['required'] = True + + # return the keyword arguments with no option strings + return dict(kwargs, dest=dest, option_strings=[]) + + def _get_optional_kwargs(self, *args, **kwargs): + # determine short and long option strings + option_strings = [] + long_option_strings = [] + for option_string in args: + # error on strings that don't start with an appropriate prefix + if not option_string[0] in self.prefix_chars: + msg = _('invalid option string %r: ' + 'must start with a character %r') + tup = option_string, self.prefix_chars + raise ValueError(msg % tup) + + # strings starting with two prefix characters are long options + option_strings.append(option_string) + if option_string[0] in self.prefix_chars: + if len(option_string) > 1: + if option_string[1] in self.prefix_chars: + long_option_strings.append(option_string) + + # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' + dest = kwargs.pop('dest', None) + if dest is None: + if long_option_strings: + dest_option_string = long_option_strings[0] + else: + dest_option_string = option_strings[0] + dest = dest_option_string.lstrip(self.prefix_chars) + if not dest: + msg = _('dest= is required for options like %r') + raise ValueError(msg % option_string) + dest = dest.replace('-', '_') + + # return the updated keyword arguments + return dict(kwargs, dest=dest, option_strings=option_strings) + + def _pop_action_class(self, kwargs, default=None): + action = kwargs.pop('action', default) + return self._registry_get('action', action, action) + + def _get_handler(self): + # determine function from conflict handler string + handler_func_name = '_handle_conflict_%s' % self.conflict_handler + try: + return getattr(self, handler_func_name) + except AttributeError: + msg = _('invalid conflict_resolution value: %r') + raise ValueError(msg % self.conflict_handler) + + def _check_conflict(self, action): + + # find all options that conflict with this option + confl_optionals = [] + for option_string in action.option_strings: + if option_string in self._option_string_actions: + confl_optional = self._option_string_actions[option_string] + confl_optionals.append((option_string, confl_optional)) + + # resolve any conflicts + if confl_optionals: + conflict_handler = self._get_handler() + conflict_handler(action, confl_optionals) + + def _handle_conflict_error(self, action, conflicting_actions): + message = _('conflicting option string(s): %s') + conflict_string = ', '.join([option_string + for option_string, action + in conflicting_actions]) + raise ArgumentError(action, message % conflict_string) + + def _handle_conflict_resolve(self, action, conflicting_actions): + + # remove all conflicting options + for option_string, action in conflicting_actions: + + # remove the conflicting option + action.option_strings.remove(option_string) + self._option_string_actions.pop(option_string, None) + + # if the option now has no option string, remove it from the + # container holding it + if not action.option_strings: + action.container._remove_action(action) + + +class _ArgumentGroup(_ActionsContainer): + + def __init__(self, container, title=None, description=None, **kwargs): + # add any missing keyword arguments by checking the container + update = kwargs.setdefault + update('conflict_handler', container.conflict_handler) + update('prefix_chars', container.prefix_chars) + update('argument_default', container.argument_default) + super_init = super(_ArgumentGroup, self).__init__ + super_init(description=description, **kwargs) + + # group attributes + self.title = title + self._group_actions = [] + + # share most attributes with the container + self._registries = container._registries + self._actions = container._actions + self._option_string_actions = container._option_string_actions + self._defaults = container._defaults + self._has_negative_number_optionals = \ + container._has_negative_number_optionals + + def _add_action(self, action): + action = super(_ArgumentGroup, self)._add_action(action) + self._group_actions.append(action) + return action + + def _remove_action(self, action): + super(_ArgumentGroup, self)._remove_action(action) + self._group_actions.remove(action) + + +class _MutuallyExclusiveGroup(_ArgumentGroup): + + def __init__(self, container, required=False): + super(_MutuallyExclusiveGroup, self).__init__(container) + self.required = required + self._container = container + + def _add_action(self, action): + if action.required: + msg = _('mutually exclusive arguments must be optional') + raise ValueError(msg) + action = self._container._add_action(action) + self._group_actions.append(action) + return action + + def _remove_action(self, action): + self._container._remove_action(action) + self._group_actions.remove(action) + + +class ArgumentParser(_AttributeHolder, _ActionsContainer): + """Object for parsing command line strings into Python objects. + + Keyword Arguments: + - prog -- The name of the program (default: sys.argv[0]) + - usage -- A usage message (default: auto-generated from arguments) + - description -- A description of what the program does + - epilog -- Text following the argument descriptions + - parents -- Parsers whose arguments should be copied into this one + - formatter_class -- HelpFormatter class for printing help messages + - prefix_chars -- Characters that prefix optional arguments + - fromfile_prefix_chars -- Characters that prefix files containing + additional arguments + - argument_default -- The default value for all arguments + - conflict_handler -- String indicating how to handle conflicts + - add_help -- Add a -h/-help option + """ + + def __init__(self, + prog=None, + usage=None, + description=None, + epilog=None, + version=None, + parents=[], + formatter_class=HelpFormatter, + prefix_chars='-', + fromfile_prefix_chars=None, + argument_default=None, + conflict_handler='error', + add_help=True): + + if version is not None: + import warnings + warnings.warn( + """The "version" argument to ArgumentParser is deprecated. """ + """Please use """ + """"add_argument(..., action='version', version="N", ...)" """ + """instead""", DeprecationWarning) + + superinit = super(ArgumentParser, self).__init__ + superinit(description=description, + prefix_chars=prefix_chars, + argument_default=argument_default, + conflict_handler=conflict_handler) + + # default setting for prog + if prog is None: + prog = _os.path.basename(_sys.argv[0]) + + self.prog = prog + self.usage = usage + self.epilog = epilog + self.version = version + self.formatter_class = formatter_class + self.fromfile_prefix_chars = fromfile_prefix_chars + self.add_help = add_help + + add_group = self.add_argument_group + self._positionals = add_group(_('positional arguments')) + self._optionals = add_group(_('optional arguments')) + self._subparsers = None + + # register types + def identity(string): + return string + self.register('type', None, identity) + + # add help and version arguments if necessary + # (using explicit default to override global argument_default) + if '-' in prefix_chars: + default_prefix = '-' + else: + default_prefix = prefix_chars[0] + if self.add_help: + self.add_argument( + default_prefix+'h', default_prefix*2+'help', + action='help', default=SUPPRESS, + help=_('show this help message and exit')) + if self.version: + self.add_argument( + default_prefix+'v', default_prefix*2+'version', + action='version', default=SUPPRESS, + version=self.version, + help=_("show program's version number and exit")) + + # add parent arguments and defaults + for parent in parents: + self._add_container_actions(parent) + try: + defaults = parent._defaults + except AttributeError: + pass + else: + self._defaults.update(defaults) + + # ======================= + # Pretty __repr__ methods + # ======================= + def _get_kwargs(self): + names = [ + 'prog', + 'usage', + 'description', + 'version', + 'formatter_class', + 'conflict_handler', + 'add_help', + ] + return [(name, getattr(self, name)) for name in names] + + # ================================== + # Optional/Positional adding methods + # ================================== + def add_subparsers(self, **kwargs): + if self._subparsers is not None: + self.error(_('cannot have multiple subparser arguments')) + + # add the parser class to the arguments if it's not present + kwargs.setdefault('parser_class', type(self)) + + if 'title' in kwargs or 'description' in kwargs: + title = _(kwargs.pop('title', 'subcommands')) + description = _(kwargs.pop('description', None)) + self._subparsers = self.add_argument_group(title, description) + else: + self._subparsers = self._positionals + + # prog defaults to the usage message of this parser, skipping + # optional arguments and with no "usage:" prefix + if kwargs.get('prog') is None: + formatter = self._get_formatter() + positionals = self._get_positional_actions() + groups = self._mutually_exclusive_groups + formatter.add_usage(self.usage, positionals, groups, '') + kwargs['prog'] = formatter.format_help().strip() + + # create the parsers action and add it to the positionals list + parsers_class = self._pop_action_class(kwargs, 'parsers') + action = parsers_class(option_strings=[], **kwargs) + self._subparsers._add_action(action) + + # return the created parsers action + return action + + def _add_action(self, action): + if action.option_strings: + self._optionals._add_action(action) + else: + self._positionals._add_action(action) + return action + + def _get_optional_actions(self): + return [action + for action in self._actions + if action.option_strings] + + def _get_positional_actions(self): + return [action + for action in self._actions + if not action.option_strings] + + # ===================================== + # Command line argument parsing methods + # ===================================== + def parse_args(self, args=None, namespace=None): + args, argv = self.parse_known_args(args, namespace) + if argv: + msg = _('unrecognized arguments: %s') + self.error(msg % ' '.join(argv)) + return args + + def parse_known_args(self, args=None, namespace=None): + # args default to the system args + if args is None: + args = _sys.argv[1:] + + # default Namespace built from parser defaults + if namespace is None: + namespace = Namespace() + + # add any action defaults that aren't present + for action in self._actions: + if action.dest is not SUPPRESS: + if not hasattr(namespace, action.dest): + if action.default is not SUPPRESS: + default = action.default + if isinstance(action.default, basestring): + default = self._get_value(action, default) + setattr(namespace, action.dest, default) + + # add any parser defaults that aren't present + for dest in self._defaults: + if not hasattr(namespace, dest): + setattr(namespace, dest, self._defaults[dest]) + + # parse the arguments and exit if there are any errors + try: + namespace, args = self._parse_known_args(args, namespace) + if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): + args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) + delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) + return namespace, args + except ArgumentError: + err = _sys.exc_info()[1] + self.error(str(err)) + + def _parse_known_args(self, arg_strings, namespace): + # replace arg strings that are file references + if self.fromfile_prefix_chars is not None: + arg_strings = self._read_args_from_files(arg_strings) + + # map all mutually exclusive arguments to the other arguments + # they can't occur with + action_conflicts = {} + for mutex_group in self._mutually_exclusive_groups: + group_actions = mutex_group._group_actions + for i, mutex_action in enumerate(mutex_group._group_actions): + conflicts = action_conflicts.setdefault(mutex_action, []) + conflicts.extend(group_actions[:i]) + conflicts.extend(group_actions[i + 1:]) + + # find all option indices, and determine the arg_string_pattern + # which has an 'O' if there is an option at an index, + # an 'A' if there is an argument, or a '-' if there is a '--' + option_string_indices = {} + arg_string_pattern_parts = [] + arg_strings_iter = iter(arg_strings) + for i, arg_string in enumerate(arg_strings_iter): + + # all args after -- are non-options + if arg_string == '--': + arg_string_pattern_parts.append('-') + for arg_string in arg_strings_iter: + arg_string_pattern_parts.append('A') + + # otherwise, add the arg to the arg strings + # and note the index if it was an option + else: + option_tuple = self._parse_optional(arg_string) + if option_tuple is None: + pattern = 'A' + else: + option_string_indices[i] = option_tuple + pattern = 'O' + arg_string_pattern_parts.append(pattern) + + # join the pieces together to form the pattern + arg_strings_pattern = ''.join(arg_string_pattern_parts) + + # converts arg strings to the appropriate and then takes the action + seen_actions = set() + seen_non_default_actions = set() + + def take_action(action, argument_strings, option_string=None): + seen_actions.add(action) + argument_values = self._get_values(action, argument_strings) + + # error if this argument is not allowed with other previously + # seen arguments, assuming that actions that use the default + # value don't really count as "present" + if argument_values is not action.default: + seen_non_default_actions.add(action) + for conflict_action in action_conflicts.get(action, []): + if conflict_action in seen_non_default_actions: + msg = _('not allowed with argument %s') + action_name = _get_action_name(conflict_action) + raise ArgumentError(action, msg % action_name) + + # take the action if we didn't receive a SUPPRESS value + # (e.g. from a default) + if argument_values is not SUPPRESS: + action(self, namespace, argument_values, option_string) + + # function to convert arg_strings into an optional action + def consume_optional(start_index): + + # get the optional identified at this index + option_tuple = option_string_indices[start_index] + action, option_string, explicit_arg = option_tuple + + # identify additional optionals in the same arg string + # (e.g. -xyz is the same as -x -y -z if no args are required) + match_argument = self._match_argument + action_tuples = [] + while True: + + # if we found no optional action, skip it + if action is None: + extras.append(arg_strings[start_index]) + return start_index + 1 + + # if there is an explicit argument, try to match the + # optional's string arguments to only this + if explicit_arg is not None: + arg_count = match_argument(action, 'A') + + # if the action is a single-dash option and takes no + # arguments, try to parse more single-dash options out + # of the tail of the option string + chars = self.prefix_chars + if arg_count == 0 and option_string[1] not in chars: + action_tuples.append((action, [], option_string)) + char = option_string[0] + option_string = char + explicit_arg[0] + new_explicit_arg = explicit_arg[1:] or None + optionals_map = self._option_string_actions + if option_string in optionals_map: + action = optionals_map[option_string] + explicit_arg = new_explicit_arg + else: + msg = _('ignored explicit argument %r') + raise ArgumentError(action, msg % explicit_arg) + + # if the action expect exactly one argument, we've + # successfully matched the option; exit the loop + elif arg_count == 1: + stop = start_index + 1 + args = [explicit_arg] + action_tuples.append((action, args, option_string)) + break + + # error if a double-dash option did not use the + # explicit argument + else: + msg = _('ignored explicit argument %r') + raise ArgumentError(action, msg % explicit_arg) + + # if there is no explicit argument, try to match the + # optional's string arguments with the following strings + # if successful, exit the loop + else: + start = start_index + 1 + selected_patterns = arg_strings_pattern[start:] + arg_count = match_argument(action, selected_patterns) + stop = start + arg_count + args = arg_strings[start:stop] + action_tuples.append((action, args, option_string)) + break + + # add the Optional to the list and return the index at which + # the Optional's string args stopped + assert action_tuples + for action, args, option_string in action_tuples: + take_action(action, args, option_string) + return stop + + # the list of Positionals left to be parsed; this is modified + # by consume_positionals() + positionals = self._get_positional_actions() + + # function to convert arg_strings into positional actions + def consume_positionals(start_index): + # match as many Positionals as possible + match_partial = self._match_arguments_partial + selected_pattern = arg_strings_pattern[start_index:] + arg_counts = match_partial(positionals, selected_pattern) + + # slice off the appropriate arg strings for each Positional + # and add the Positional and its args to the list + for action, arg_count in zip(positionals, arg_counts): + args = arg_strings[start_index: start_index + arg_count] + start_index += arg_count + take_action(action, args) + + # slice off the Positionals that we just parsed and return the + # index at which the Positionals' string args stopped + positionals[:] = positionals[len(arg_counts):] + return start_index + + # consume Positionals and Optionals alternately, until we have + # passed the last option string + extras = [] + start_index = 0 + if option_string_indices: + max_option_string_index = max(option_string_indices) + else: + max_option_string_index = -1 + while start_index <= max_option_string_index: + + # consume any Positionals preceding the next option + next_option_string_index = min([ + index + for index in option_string_indices + if index >= start_index]) + if start_index != next_option_string_index: + positionals_end_index = consume_positionals(start_index) + + # only try to parse the next optional if we didn't consume + # the option string during the positionals parsing + if positionals_end_index > start_index: + start_index = positionals_end_index + continue + else: + start_index = positionals_end_index + + # if we consumed all the positionals we could and we're not + # at the index of an option string, there were extra arguments + if start_index not in option_string_indices: + strings = arg_strings[start_index:next_option_string_index] + extras.extend(strings) + start_index = next_option_string_index + + # consume the next optional and any arguments for it + start_index = consume_optional(start_index) + + # consume any positionals following the last Optional + stop_index = consume_positionals(start_index) + + # if we didn't consume all the argument strings, there were extras + extras.extend(arg_strings[stop_index:]) + + # if we didn't use all the Positional objects, there were too few + # arg strings supplied. + if positionals: + self.error(_('too few arguments')) + + # make sure all required actions were present + for action in self._actions: + if action.required: + if action not in seen_actions: + name = _get_action_name(action) + self.error(_('argument %s is required') % name) + + # make sure all required groups had one option present + for group in self._mutually_exclusive_groups: + if group.required: + for action in group._group_actions: + if action in seen_non_default_actions: + break + + # if no actions were used, report the error + else: + names = [_get_action_name(action) + for action in group._group_actions + if action.help is not SUPPRESS] + msg = _('one of the arguments %s is required') + self.error(msg % ' '.join(names)) + + # return the updated namespace and the extra arguments + return namespace, extras + + def _read_args_from_files(self, arg_strings): + # expand arguments referencing files + new_arg_strings = [] + for arg_string in arg_strings: + + # for regular arguments, just add them back into the list + if arg_string[0] not in self.fromfile_prefix_chars: + new_arg_strings.append(arg_string) + + # replace arguments referencing files with the file content + else: + try: + args_file = open(arg_string[1:]) + try: + arg_strings = [] + for arg_line in args_file.read().splitlines(): + for arg in self.convert_arg_line_to_args(arg_line): + arg_strings.append(arg) + arg_strings = self._read_args_from_files(arg_strings) + new_arg_strings.extend(arg_strings) + finally: + args_file.close() + except IOError: + err = _sys.exc_info()[1] + self.error(str(err)) + + # return the modified argument list + return new_arg_strings + + def convert_arg_line_to_args(self, arg_line): + return [arg_line] + + def _match_argument(self, action, arg_strings_pattern): + # match the pattern for this action to the arg strings + nargs_pattern = self._get_nargs_pattern(action) + match = _re.match(nargs_pattern, arg_strings_pattern) + + # raise an exception if we weren't able to find a match + if match is None: + nargs_errors = { + None: _('expected one argument'), + OPTIONAL: _('expected at most one argument'), + ONE_OR_MORE: _('expected at least one argument'), + } + default = _('expected %s argument(s)') % action.nargs + msg = nargs_errors.get(action.nargs, default) + raise ArgumentError(action, msg) + + # return the number of arguments matched + return len(match.group(1)) + + def _match_arguments_partial(self, actions, arg_strings_pattern): + # progressively shorten the actions list by slicing off the + # final actions until we find a match + result = [] + for i in range(len(actions), 0, -1): + actions_slice = actions[:i] + pattern = ''.join([self._get_nargs_pattern(action) + for action in actions_slice]) + match = _re.match(pattern, arg_strings_pattern) + if match is not None: + result.extend([len(string) for string in match.groups()]) + break + + # return the list of arg string counts + return result + + def _parse_optional(self, arg_string): + # if it's an empty string, it was meant to be a positional + if not arg_string: + return None + + # if it doesn't start with a prefix, it was meant to be positional + if not arg_string[0] in self.prefix_chars: + return None + + # if the option string is present in the parser, return the action + if arg_string in self._option_string_actions: + action = self._option_string_actions[arg_string] + return action, arg_string, None + + # if it's just a single character, it was meant to be positional + if len(arg_string) == 1: + return None + + # if the option string before the "=" is present, return the action + if '=' in arg_string: + option_string, explicit_arg = arg_string.split('=', 1) + if option_string in self._option_string_actions: + action = self._option_string_actions[option_string] + return action, option_string, explicit_arg + + # search through all possible prefixes of the option string + # and all actions in the parser for possible interpretations + option_tuples = self._get_option_tuples(arg_string) + + # if multiple actions match, the option string was ambiguous + if len(option_tuples) > 1: + options = ', '.join([option_string + for action, option_string, explicit_arg in option_tuples]) + tup = arg_string, options + self.error(_('ambiguous option: %s could match %s') % tup) + + # if exactly one action matched, this segmentation is good, + # so return the parsed action + elif len(option_tuples) == 1: + option_tuple, = option_tuples + return option_tuple + + # if it was not found as an option, but it looks like a negative + # number, it was meant to be positional + # unless there are negative-number-like options + if self._negative_number_matcher.match(arg_string): + if not self._has_negative_number_optionals: + return None + + # if it contains a space, it was meant to be a positional + if ' ' in arg_string: + return None + + # it was meant to be an optional but there is no such option + # in this parser (though it might be a valid option in a subparser) + return None, arg_string, None + + def _get_option_tuples(self, option_string): + result = [] + + # option strings starting with two prefix characters are only + # split at the '=' + chars = self.prefix_chars + if option_string[0] in chars and option_string[1] in chars: + if '=' in option_string: + option_prefix, explicit_arg = option_string.split('=', 1) + else: + option_prefix = option_string + explicit_arg = None + for option_string in self._option_string_actions: + if option_string.startswith(option_prefix): + action = self._option_string_actions[option_string] + tup = action, option_string, explicit_arg + result.append(tup) + + # single character options can be concatenated with their arguments + # but multiple character options always have to have their argument + # separate + elif option_string[0] in chars and option_string[1] not in chars: + option_prefix = option_string + explicit_arg = None + short_option_prefix = option_string[:2] + short_explicit_arg = option_string[2:] + + for option_string in self._option_string_actions: + if option_string == short_option_prefix: + action = self._option_string_actions[option_string] + tup = action, option_string, short_explicit_arg + result.append(tup) + elif option_string.startswith(option_prefix): + action = self._option_string_actions[option_string] + tup = action, option_string, explicit_arg + result.append(tup) + + # shouldn't ever get here + else: + self.error(_('unexpected option string: %s') % option_string) + + # return the collected option tuples + return result + + def _get_nargs_pattern(self, action): + # in all examples below, we have to allow for '--' args + # which are represented as '-' in the pattern + nargs = action.nargs + + # the default (None) is assumed to be a single argument + if nargs is None: + nargs_pattern = '(-*A-*)' + + # allow zero or one arguments + elif nargs == OPTIONAL: + nargs_pattern = '(-*A?-*)' + + # allow zero or more arguments + elif nargs == ZERO_OR_MORE: + nargs_pattern = '(-*[A-]*)' + + # allow one or more arguments + elif nargs == ONE_OR_MORE: + nargs_pattern = '(-*A[A-]*)' + + # allow any number of options or arguments + elif nargs == REMAINDER: + nargs_pattern = '([-AO]*)' + + # allow one argument followed by any number of options or arguments + elif nargs == PARSER: + nargs_pattern = '(-*A[-AO]*)' + + # all others should be integers + else: + nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) + + # if this is an optional action, -- is not allowed + if action.option_strings: + nargs_pattern = nargs_pattern.replace('-*', '') + nargs_pattern = nargs_pattern.replace('-', '') + + # return the pattern + return nargs_pattern + + # ======================== + # Value conversion methods + # ======================== + def _get_values(self, action, arg_strings): + # for everything but PARSER args, strip out '--' + if action.nargs not in [PARSER, REMAINDER]: + arg_strings = [s for s in arg_strings if s != '--'] + + # optional argument produces a default when not present + if not arg_strings and action.nargs == OPTIONAL: + if action.option_strings: + value = action.const + else: + value = action.default + if isinstance(value, basestring): + value = self._get_value(action, value) + self._check_value(action, value) + + # when nargs='*' on a positional, if there were no command-line + # args, use the default if it is anything other than None + elif (not arg_strings and action.nargs == ZERO_OR_MORE and + not action.option_strings): + if action.default is not None: + value = action.default + else: + value = arg_strings + self._check_value(action, value) + + # single argument or optional argument produces a single value + elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: + arg_string, = arg_strings + value = self._get_value(action, arg_string) + self._check_value(action, value) + + # REMAINDER arguments convert all values, checking none + elif action.nargs == REMAINDER: + value = [self._get_value(action, v) for v in arg_strings] + + # PARSER arguments convert all values, but check only the first + elif action.nargs == PARSER: + value = [self._get_value(action, v) for v in arg_strings] + self._check_value(action, value[0]) + + # all other types of nargs produce a list + else: + value = [self._get_value(action, v) for v in arg_strings] + for v in value: + self._check_value(action, v) + + # return the converted value + return value + + def _get_value(self, action, arg_string): + type_func = self._registry_get('type', action.type, action.type) + if not _callable(type_func): + msg = _('%r is not callable') + raise ArgumentError(action, msg % type_func) + + # convert the value to the appropriate type + try: + result = type_func(arg_string) + + # ArgumentTypeErrors indicate errors + except ArgumentTypeError: + name = getattr(action.type, '__name__', repr(action.type)) + msg = str(_sys.exc_info()[1]) + raise ArgumentError(action, msg) + + # TypeErrors or ValueErrors also indicate errors + except (TypeError, ValueError): + name = getattr(action.type, '__name__', repr(action.type)) + msg = _('invalid %s value: %r') + raise ArgumentError(action, msg % (name, arg_string)) + + # return the converted value + return result + + def _check_value(self, action, value): + # converted value must be one of the choices (if specified) + if action.choices is not None and value not in action.choices: + tup = value, ', '.join(map(repr, action.choices)) + msg = _('invalid choice: %r (choose from %s)') % tup + raise ArgumentError(action, msg) + + # ======================= + # Help-formatting methods + # ======================= + def format_usage(self): + formatter = self._get_formatter() + formatter.add_usage(self.usage, self._actions, + self._mutually_exclusive_groups) + return formatter.format_help() + + def format_help(self): + formatter = self._get_formatter() + + # usage + formatter.add_usage(self.usage, self._actions, + self._mutually_exclusive_groups) + + # description + formatter.add_text(self.description) + + # positionals, optionals and user-defined groups + for action_group in self._action_groups: + formatter.start_section(action_group.title) + formatter.add_text(action_group.description) + formatter.add_arguments(action_group._group_actions) + formatter.end_section() + + # epilog + formatter.add_text(self.epilog) + + # determine help from format above + return formatter.format_help() + + def format_version(self): + import warnings + warnings.warn( + 'The format_version method is deprecated -- the "version" ' + 'argument to ArgumentParser is no longer supported.', + DeprecationWarning) + formatter = self._get_formatter() + formatter.add_text(self.version) + return formatter.format_help() + + def _get_formatter(self): + return self.formatter_class(prog=self.prog) + + # ===================== + # Help-printing methods + # ===================== + def print_usage(self, file=None): + if file is None: + file = _sys.stdout + self._print_message(self.format_usage(), file) + + def print_help(self, file=None): + if file is None: + file = _sys.stdout + self._print_message(self.format_help(), file) + + def print_version(self, file=None): + import warnings + warnings.warn( + 'The print_version method is deprecated -- the "version" ' + 'argument to ArgumentParser is no longer supported.', + DeprecationWarning) + self._print_message(self.format_version(), file) + + def _print_message(self, message, file=None): + if message: + if file is None: + file = _sys.stderr + file.write(message) + + # =============== + # Exiting methods + # =============== + def exit(self, status=0, message=None): + if message: + self._print_message(message, _sys.stderr) + _sys.exit(status) + + def error(self, message): + """error(message: string) + + Prints a usage message incorporating the message to stderr and + exits. + + If you override this in a subclass, it should not return -- it + should either exit or raise an exception. + """ + self.print_usage(_sys.stderr) + self.exit(2, _('%s: error: %s\n') % (self.prog, message)) diff --git a/venv/lib/python3.6/site-packages/gunicorn/config.py b/venv/lib/python3.6/site-packages/gunicorn/config.py new file mode 100644 index 0000000..aa97894 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/config.py @@ -0,0 +1,1950 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +# Please remember to run "make -C docs html" after update "desc" attributes. + +import copy +import grp +import inspect +try: + import argparse +except ImportError: # python 2.6 + from . import argparse_compat as argparse +import os +import pwd +import re +import ssl +import sys +import textwrap +import shlex + +from gunicorn import __version__ +from gunicorn import _compat +from gunicorn.errors import ConfigError +from gunicorn.reloader import reloader_engines +from gunicorn import six +from gunicorn import util + +KNOWN_SETTINGS = [] +PLATFORM = sys.platform + + +def make_settings(ignore=None): + settings = {} + ignore = ignore or () + for s in KNOWN_SETTINGS: + setting = s() + if setting.name in ignore: + continue + settings[setting.name] = setting.copy() + return settings + + +def auto_int(_, x): + # for compatible with octal numbers in python3 + if re.match(r'0(\d)', x, re.IGNORECASE): + x = x.replace('0', '0o', 1) + return int(x, 0) + + +class Config(object): + + def __init__(self, usage=None, prog=None): + self.settings = make_settings() + self.usage = usage + self.prog = prog or os.path.basename(sys.argv[0]) + self.env_orig = os.environ.copy() + + def __getattr__(self, name): + if name not in self.settings: + raise AttributeError("No configuration setting for: %s" % name) + return self.settings[name].get() + + def __setattr__(self, name, value): + if name != "settings" and name in self.settings: + raise AttributeError("Invalid access!") + super(Config, self).__setattr__(name, value) + + def set(self, name, value): + if name not in self.settings: + raise AttributeError("No configuration setting for: %s" % name) + self.settings[name].set(value) + + def get_cmd_args_from_env(self): + if 'GUNICORN_CMD_ARGS' in self.env_orig: + return shlex.split(self.env_orig['GUNICORN_CMD_ARGS']) + return [] + + def parser(self): + kwargs = { + "usage": self.usage, + "prog": self.prog + } + parser = argparse.ArgumentParser(**kwargs) + parser.add_argument("-v", "--version", + action="version", default=argparse.SUPPRESS, + version="%(prog)s (version " + __version__ + ")\n", + help="show program's version number and exit") + parser.add_argument("args", nargs="*", help=argparse.SUPPRESS) + + keys = sorted(self.settings, key=self.settings.__getitem__) + for k in keys: + self.settings[k].add_option(parser) + + return parser + + @property + def worker_class_str(self): + uri = self.settings['worker_class'].get() + + ## are we using a threaded worker? + is_sync = uri.endswith('SyncWorker') or uri == 'sync' + if is_sync and self.threads > 1: + return "threads" + return uri + + @property + def worker_class(self): + uri = self.settings['worker_class'].get() + + ## are we using a threaded worker? + is_sync = uri.endswith('SyncWorker') or uri == 'sync' + if is_sync and self.threads > 1: + uri = "gunicorn.workers.gthread.ThreadWorker" + + worker_class = util.load_class(uri) + if hasattr(worker_class, "setup"): + worker_class.setup() + return worker_class + + @property + def address(self): + s = self.settings['bind'].get() + return [util.parse_address(_compat.bytes_to_str(bind)) for bind in s] + + @property + def uid(self): + return self.settings['user'].get() + + @property + def gid(self): + return self.settings['group'].get() + + @property + def proc_name(self): + pn = self.settings['proc_name'].get() + if pn is not None: + return pn + else: + return self.settings['default_proc_name'].get() + + @property + def logger_class(self): + uri = self.settings['logger_class'].get() + if uri == "simple": + # support the default + uri = LoggerClass.default + + # if default logger is in use, and statsd is on, automagically switch + # to the statsd logger + if uri == LoggerClass.default: + if 'statsd_host' in self.settings and self.settings['statsd_host'].value is not None: + uri = "gunicorn.instrument.statsd.Statsd" + + logger_class = util.load_class( + uri, + default="gunicorn.glogging.Logger", + section="gunicorn.loggers") + + if hasattr(logger_class, "install"): + logger_class.install() + return logger_class + + @property + def is_ssl(self): + return self.certfile or self.keyfile + + @property + def ssl_options(self): + opts = {} + for name, value in self.settings.items(): + if value.section == 'SSL': + opts[name] = value.get() + return opts + + @property + def env(self): + raw_env = self.settings['raw_env'].get() + env = {} + + if not raw_env: + return env + + for e in raw_env: + s = _compat.bytes_to_str(e) + try: + k, v = s.split('=', 1) + except ValueError: + raise RuntimeError("environment setting %r invalid" % s) + + env[k] = v + + return env + + @property + def sendfile(self): + if self.settings['sendfile'].get() is not None: + return False + + if 'SENDFILE' in os.environ: + sendfile = os.environ['SENDFILE'].lower() + return sendfile in ['y', '1', 'yes', 'true'] + + return True + + @property + def reuse_port(self): + return self.settings['reuse_port'].get() + + @property + def paste_global_conf(self): + raw_global_conf = self.settings['raw_paste_global_conf'].get() + if raw_global_conf is None: + return None + + global_conf = {} + for e in raw_global_conf: + s = _compat.bytes_to_str(e) + try: + k, v = re.split(r'(?= 0.9.7 (or install it via + ``pip install gunicorn[eventlet]``) + * ``gevent`` - Requires gevent >= 0.13 (or install it via + ``pip install gunicorn[gevent]``) + * ``tornado`` - Requires tornado >= 0.2 (or install it via + ``pip install gunicorn[tornado]``) + * ``gthread`` - Python 2 requires the futures package to be installed + (or install it via ``pip install gunicorn[gthread]``) + * ``gaiohttp`` - Deprecated. + + Optionally, you can provide your own worker by giving Gunicorn a + Python path to a subclass of ``gunicorn.workers.base.Worker``. + This alternative syntax will load the gevent class: + ``gunicorn.workers.ggevent.GeventWorker``. + + .. deprecated:: 19.8 + The ``gaiohttp`` worker is deprecated. Please use + ``aiohttp.worker.GunicornWebWorker`` instead. See + :ref:`asyncio-workers` for more information on how to use it. + """ + +class WorkerThreads(Setting): + name = "threads" + section = "Worker Processes" + cli = ["--threads"] + meta = "INT" + validator = validate_pos_int + type = int + default = 1 + desc = """\ + The number of worker threads for handling requests. + + Run each worker with the specified number of threads. + + A positive integer generally in the ``2-4 x $(NUM_CORES)`` range. + You'll want to vary this a bit to find the best for your particular + application's work load. + + If it is not defined, the default is ``1``. + + This setting only affects the Gthread worker type. + + .. note:: + If you try to use the ``sync`` worker type and set the ``threads`` + setting to more than 1, the ``gthread`` worker type will be used + instead. + """ + + +class WorkerConnections(Setting): + name = "worker_connections" + section = "Worker Processes" + cli = ["--worker-connections"] + meta = "INT" + validator = validate_pos_int + type = int + default = 1000 + desc = """\ + The maximum number of simultaneous clients. + + This setting only affects the Eventlet and Gevent worker types. + """ + + +class MaxRequests(Setting): + name = "max_requests" + section = "Worker Processes" + cli = ["--max-requests"] + meta = "INT" + validator = validate_pos_int + type = int + default = 0 + desc = """\ + The maximum number of requests a worker will process before restarting. + + Any value greater than zero will limit the number of requests a work + will process before automatically restarting. This is a simple method + to help limit the damage of memory leaks. + + If this is set to zero (the default) then the automatic worker + restarts are disabled. + """ + + +class MaxRequestsJitter(Setting): + name = "max_requests_jitter" + section = "Worker Processes" + cli = ["--max-requests-jitter"] + meta = "INT" + validator = validate_pos_int + type = int + default = 0 + desc = """\ + The maximum jitter to add to the *max_requests* setting. + + The jitter causes the restart per worker to be randomized by + ``randint(0, max_requests_jitter)``. This is intended to stagger worker + restarts to avoid all workers restarting at the same time. + + .. versionadded:: 19.2 + """ + + +class Timeout(Setting): + name = "timeout" + section = "Worker Processes" + cli = ["-t", "--timeout"] + meta = "INT" + validator = validate_pos_int + type = int + default = 30 + desc = """\ + Workers silent for more than this many seconds are killed and restarted. + + Generally set to thirty seconds. Only set this noticeably higher if + you're sure of the repercussions for sync workers. For the non sync + workers it just means that the worker process is still communicating and + is not tied to the length of time required to handle a single request. + """ + + +class GracefulTimeout(Setting): + name = "graceful_timeout" + section = "Worker Processes" + cli = ["--graceful-timeout"] + meta = "INT" + validator = validate_pos_int + type = int + default = 30 + desc = """\ + Timeout for graceful workers restart. + + After receiving a restart signal, workers have this much time to finish + serving requests. Workers still alive after the timeout (starting from + the receipt of the restart signal) are force killed. + """ + + +class Keepalive(Setting): + name = "keepalive" + section = "Worker Processes" + cli = ["--keep-alive"] + meta = "INT" + validator = validate_pos_int + type = int + default = 2 + desc = """\ + The number of seconds to wait for requests on a Keep-Alive connection. + + Generally set in the 1-5 seconds range for servers with direct connection + to the client (e.g. when you don't have separate load balancer). When + Gunicorn is deployed behind a load balancer, it often makes sense to + set this to a higher value. + + .. note:: + ``sync`` worker does not support persistent connections and will + ignore this option. + """ + + +class LimitRequestLine(Setting): + name = "limit_request_line" + section = "Security" + cli = ["--limit-request-line"] + meta = "INT" + validator = validate_pos_int + type = int + default = 4094 + desc = """\ + The maximum size of HTTP request line in bytes. + + This parameter is used to limit the allowed size of a client's + HTTP request-line. Since the request-line consists of the HTTP + method, URI, and protocol version, this directive places a + restriction on the length of a request-URI allowed for a request + on the server. A server needs this value to be large enough to + hold any of its resource names, including any information that + might be passed in the query part of a GET request. Value is a number + from 0 (unlimited) to 8190. + + This parameter can be used to prevent any DDOS attack. + """ + + +class LimitRequestFields(Setting): + name = "limit_request_fields" + section = "Security" + cli = ["--limit-request-fields"] + meta = "INT" + validator = validate_pos_int + type = int + default = 100 + desc = """\ + Limit the number of HTTP headers fields in a request. + + This parameter is used to limit the number of headers in a request to + prevent DDOS attack. Used with the *limit_request_field_size* it allows + more safety. By default this value is 100 and can't be larger than + 32768. + """ + + +class LimitRequestFieldSize(Setting): + name = "limit_request_field_size" + section = "Security" + cli = ["--limit-request-field_size"] + meta = "INT" + validator = validate_pos_int + type = int + default = 8190 + desc = """\ + Limit the allowed size of an HTTP request header field. + + Value is a positive number or 0. Setting it to 0 will allow unlimited + header field sizes. + + .. warning:: + Setting this parameter to a very high or unlimited value can open + up for DDOS attacks. + """ + + +class Reload(Setting): + name = "reload" + section = 'Debugging' + cli = ['--reload'] + validator = validate_bool + action = 'store_true' + default = False + + desc = '''\ + Restart workers when code changes. + + This setting is intended for development. It will cause workers to be + restarted whenever application code changes. + + The reloader is incompatible with application preloading. When using a + paste configuration be sure that the server block does not import any + application code or the reload will not work as designed. + + The default behavior is to attempt inotify with a fallback to file + system polling. Generally, inotify should be preferred if available + because it consumes less system resources. + + .. note:: + In order to use the inotify reloader, you must have the ``inotify`` + package installed. + ''' + + +class ReloadEngine(Setting): + name = "reload_engine" + section = "Debugging" + cli = ["--reload-engine"] + meta = "STRING" + validator = validate_reload_engine + default = "auto" + desc = """\ + The implementation that should be used to power :ref:`reload`. + + Valid engines are: + + * 'auto' + * 'poll' + * 'inotify' (requires inotify) + + .. versionadded:: 19.7 + """ + + +class ReloadExtraFiles(Setting): + name = "reload_extra_files" + action = "append" + section = "Debugging" + cli = ["--reload-extra-file"] + meta = "FILES" + validator = validate_list_of_existing_files + default = [] + desc = """\ + Extends :ref:`reload` option to also watch and reload on additional files + (e.g., templates, configurations, specifications, etc.). + + .. versionadded:: 19.8 + """ + + +class Spew(Setting): + name = "spew" + section = "Debugging" + cli = ["--spew"] + validator = validate_bool + action = "store_true" + default = False + desc = """\ + Install a trace function that spews every line executed by the server. + + This is the nuclear option. + """ + + +class ConfigCheck(Setting): + name = "check_config" + section = "Debugging" + cli = ["--check-config"] + validator = validate_bool + action = "store_true" + default = False + desc = """\ + Check the configuration. + """ + + +class PreloadApp(Setting): + name = "preload_app" + section = "Server Mechanics" + cli = ["--preload"] + validator = validate_bool + action = "store_true" + default = False + desc = """\ + Load application code before the worker processes are forked. + + By preloading an application you can save some RAM resources as well as + speed up server boot times. Although, if you defer application loading + to each worker process, you can reload your application code easily by + restarting workers. + """ + + +class Sendfile(Setting): + name = "sendfile" + section = "Server Mechanics" + cli = ["--no-sendfile"] + validator = validate_bool + action = "store_const" + const = False + + desc = """\ + Disables the use of ``sendfile()``. + + If not set, the value of the ``SENDFILE`` environment variable is used + to enable or disable its usage. + + .. versionadded:: 19.2 + .. versionchanged:: 19.4 + Swapped ``--sendfile`` with ``--no-sendfile`` to actually allow + disabling. + .. versionchanged:: 19.6 + added support for the ``SENDFILE`` environment variable + """ + + +class ReusePort(Setting): + name = "reuse_port" + section = "Server Mechanics" + cli = ["--reuse-port"] + validator = validate_bool + action = "store_true" + default = False + + desc = """\ + Set the ``SO_REUSEPORT`` flag on the listening socket. + + .. versionadded:: 19.8 + """ + + +class Chdir(Setting): + name = "chdir" + section = "Server Mechanics" + cli = ["--chdir"] + validator = validate_chdir + default = util.getcwd() + desc = """\ + Chdir to specified directory before apps loading. + """ + + +class Daemon(Setting): + name = "daemon" + section = "Server Mechanics" + cli = ["-D", "--daemon"] + validator = validate_bool + action = "store_true" + default = False + desc = """\ + Daemonize the Gunicorn process. + + Detaches the server from the controlling terminal and enters the + background. + """ + +class Env(Setting): + name = "raw_env" + action = "append" + section = "Server Mechanics" + cli = ["-e", "--env"] + meta = "ENV" + validator = validate_list_string + default = [] + + desc = """\ + Set environment variable (key=value). + + Pass variables to the execution environment. Ex.:: + + $ gunicorn -b 127.0.0.1:8000 --env FOO=1 test:app + + and test for the foo variable environment in your application. + """ + + +class Pidfile(Setting): + name = "pidfile" + section = "Server Mechanics" + cli = ["-p", "--pid"] + meta = "FILE" + validator = validate_string + default = None + desc = """\ + A filename to use for the PID file. + + If not set, no PID file will be written. + """ + +class WorkerTmpDir(Setting): + name = "worker_tmp_dir" + section = "Server Mechanics" + cli = ["--worker-tmp-dir"] + meta = "DIR" + validator = validate_string + default = None + desc = """\ + A directory to use for the worker heartbeat temporary file. + + If not set, the default temporary directory will be used. + + .. note:: + The current heartbeat system involves calling ``os.fchmod`` on + temporary file handlers and may block a worker for arbitrary time + if the directory is on a disk-backed filesystem. + + See :ref:`blocking-os-fchmod` for more detailed information + and a solution for avoiding this problem. + """ + + +class User(Setting): + name = "user" + section = "Server Mechanics" + cli = ["-u", "--user"] + meta = "USER" + validator = validate_user + default = os.geteuid() + desc = """\ + Switch worker processes to run as this user. + + A valid user id (as an integer) or the name of a user that can be + retrieved with a call to ``pwd.getpwnam(value)`` or ``None`` to not + change the worker process user. + """ + + +class Group(Setting): + name = "group" + section = "Server Mechanics" + cli = ["-g", "--group"] + meta = "GROUP" + validator = validate_group + default = os.getegid() + desc = """\ + Switch worker process to run as this group. + + A valid group id (as an integer) or the name of a user that can be + retrieved with a call to ``pwd.getgrnam(value)`` or ``None`` to not + change the worker processes group. + """ + +class Umask(Setting): + name = "umask" + section = "Server Mechanics" + cli = ["-m", "--umask"] + meta = "INT" + validator = validate_pos_int + type = auto_int + default = 0 + desc = """\ + A bit mask for the file mode on files written by Gunicorn. + + Note that this affects unix socket permissions. + + A valid value for the ``os.umask(mode)`` call or a string compatible + with ``int(value, 0)`` (``0`` means Python guesses the base, so values + like ``0``, ``0xFF``, ``0022`` are valid for decimal, hex, and octal + representations) + """ + + +class Initgroups(Setting): + name = "initgroups" + section = "Server Mechanics" + cli = ["--initgroups"] + validator = validate_bool + action = 'store_true' + default = False + + desc = """\ + If true, set the worker process's group access list with all of the + groups of which the specified username is a member, plus the specified + group id. + + .. versionadded:: 19.7 + """ + + +class TmpUploadDir(Setting): + name = "tmp_upload_dir" + section = "Server Mechanics" + meta = "DIR" + validator = validate_string + default = None + desc = """\ + Directory to store temporary request data as they are read. + + This may disappear in the near future. + + This path should be writable by the process permissions set for Gunicorn + workers. If not specified, Gunicorn will choose a system generated + temporary directory. + """ + + +class SecureSchemeHeader(Setting): + name = "secure_scheme_headers" + section = "Server Mechanics" + validator = validate_dict + default = { + "X-FORWARDED-PROTOCOL": "ssl", + "X-FORWARDED-PROTO": "https", + "X-FORWARDED-SSL": "on" + } + desc = """\ + + A dictionary containing headers and values that the front-end proxy + uses to indicate HTTPS requests. These tell Gunicorn to set + ``wsgi.url_scheme`` to ``https``, so your application can tell that the + request is secure. + + The dictionary should map upper-case header names to exact string + values. The value comparisons are case-sensitive, unlike the header + names, so make sure they're exactly what your front-end proxy sends + when handling HTTPS requests. + + It is important that your front-end proxy configuration ensures that + the headers defined here can not be passed directly from the client. + """ + + +class ForwardedAllowIPS(Setting): + name = "forwarded_allow_ips" + section = "Server Mechanics" + cli = ["--forwarded-allow-ips"] + meta = "STRING" + validator = validate_string_to_list + default = os.environ.get("FORWARDED_ALLOW_IPS", "127.0.0.1") + desc = """\ + Front-end's IPs from which allowed to handle set secure headers. + (comma separate). + + Set to ``*`` to disable checking of Front-end IPs (useful for setups + where you don't know in advance the IP address of Front-end, but + you still trust the environment). + + By default, the value of the ``FORWARDED_ALLOW_IPS`` environment + variable. If it is not defined, the default is ``"127.0.0.1"``. + """ + + +class AccessLog(Setting): + name = "accesslog" + section = "Logging" + cli = ["--access-logfile"] + meta = "FILE" + validator = validate_string + default = None + desc = """\ + The Access log file to write to. + + ``'-'`` means log to stdout. + """ + +class DisableRedirectAccessToSyslog(Setting): + name = "disable_redirect_access_to_syslog" + section = "Logging" + cli = ["--disable-redirect-access-to-syslog"] + validator = validate_bool + action = 'store_true' + default = False + desc = """\ + Disable redirect access logs to syslog. + + .. versionadded:: 19.8 + """ + + +class AccessLogFormat(Setting): + name = "access_log_format" + section = "Logging" + cli = ["--access-logformat"] + meta = "STRING" + validator = validate_string + default = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' + desc = """\ + The access log format. + + =========== =========== + Identifier Description + =========== =========== + h remote address + l ``'-'`` + u user name + t date of the request + r status line (e.g. ``GET / HTTP/1.1``) + m request method + U URL path without query string + q query string + H protocol + s status + B response length + b response length or ``'-'`` (CLF format) + f referer + a user agent + T request time in seconds + D request time in microseconds + L request time in decimal seconds + p process ID + {Header}i request header + {Header}o response header + {Variable}e environment variable + =========== =========== + """ + + +class ErrorLog(Setting): + name = "errorlog" + section = "Logging" + cli = ["--error-logfile", "--log-file"] + meta = "FILE" + validator = validate_string + default = '-' + desc = """\ + The Error log file to write to. + + Using ``'-'`` for FILE makes gunicorn log to stderr. + + .. versionchanged:: 19.2 + Log to stderr by default. + + """ + + +class Loglevel(Setting): + name = "loglevel" + section = "Logging" + cli = ["--log-level"] + meta = "LEVEL" + validator = validate_string + default = "info" + desc = """\ + The granularity of Error log outputs. + + Valid level names are: + + * debug + * info + * warning + * error + * critical + """ + + +class CaptureOutput(Setting): + name = "capture_output" + section = "Logging" + cli = ["--capture-output"] + validator = validate_bool + action = 'store_true' + default = False + desc = """\ + Redirect stdout/stderr to specified file in :ref:`errorlog`. + + .. versionadded:: 19.6 + """ + + +class LoggerClass(Setting): + name = "logger_class" + section = "Logging" + cli = ["--logger-class"] + meta = "STRING" + validator = validate_class + default = "gunicorn.glogging.Logger" + desc = """\ + The logger you want to use to log events in Gunicorn. + + The default class (``gunicorn.glogging.Logger``) handle most of + normal usages in logging. It provides error and access logging. + + You can provide your own logger by giving Gunicorn a + Python path to a subclass like ``gunicorn.glogging.Logger``. + """ + + +class LogConfig(Setting): + name = "logconfig" + section = "Logging" + cli = ["--log-config"] + meta = "FILE" + validator = validate_string + default = None + desc = """\ + The log config file to use. + Gunicorn uses the standard Python logging module's Configuration + file format. + """ + + +class LogConfigDict(Setting): + name = "logconfig_dict" + section = "Logging" + cli = ["--log-config-dict"] + validator = validate_dict + default = {} + desc = """\ + The log config dictionary to use, using the standard Python + logging module's dictionary configuration format. This option + takes precedence over the :ref:`logconfig` option, which uses the + older file configuration format. + + Format: https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig + + .. versionadded:: 19.8 + """ + + +class SyslogTo(Setting): + name = "syslog_addr" + section = "Logging" + cli = ["--log-syslog-to"] + meta = "SYSLOG_ADDR" + validator = validate_string + + if PLATFORM == "darwin": + default = "unix:///var/run/syslog" + elif PLATFORM in ('freebsd', 'dragonfly', ): + default = "unix:///var/run/log" + elif PLATFORM == "openbsd": + default = "unix:///dev/log" + else: + default = "udp://localhost:514" + + desc = """\ + Address to send syslog messages. + + Address is a string of the form: + + * ``unix://PATH#TYPE`` : for unix domain socket. ``TYPE`` can be ``stream`` + for the stream driver or ``dgram`` for the dgram driver. + ``stream`` is the default. + * ``udp://HOST:PORT`` : for UDP sockets + * ``tcp://HOST:PORT`` : for TCP sockets + + """ + + +class Syslog(Setting): + name = "syslog" + section = "Logging" + cli = ["--log-syslog"] + validator = validate_bool + action = 'store_true' + default = False + desc = """\ + Send *Gunicorn* logs to syslog. + + .. versionchanged:: 19.8 + You can now disable sending access logs by using the + :ref:`disable-redirect-access-to-syslog` setting. + """ + + +class SyslogPrefix(Setting): + name = "syslog_prefix" + section = "Logging" + cli = ["--log-syslog-prefix"] + meta = "SYSLOG_PREFIX" + validator = validate_string + default = None + desc = """\ + Makes Gunicorn use the parameter as program-name in the syslog entries. + + All entries will be prefixed by ``gunicorn.``. By default the + program name is the name of the process. + """ + + +class SyslogFacility(Setting): + name = "syslog_facility" + section = "Logging" + cli = ["--log-syslog-facility"] + meta = "SYSLOG_FACILITY" + validator = validate_string + default = "user" + desc = """\ + Syslog facility name + """ + + +class EnableStdioInheritance(Setting): + name = "enable_stdio_inheritance" + section = "Logging" + cli = ["-R", "--enable-stdio-inheritance"] + validator = validate_bool + default = False + action = "store_true" + desc = """\ + Enable stdio inheritance. + + Enable inheritance for stdio file descriptors in daemon mode. + + Note: To disable the Python stdout buffering, you can to set the user + environment variable ``PYTHONUNBUFFERED`` . + """ + + +# statsD monitoring +class StatsdHost(Setting): + name = "statsd_host" + section = "Logging" + cli = ["--statsd-host"] + meta = "STATSD_ADDR" + default = None + validator = validate_hostport + desc = """\ + ``host:port`` of the statsd server to log to. + + .. versionadded:: 19.1 + """ + +class StatsdPrefix(Setting): + name = "statsd_prefix" + section = "Logging" + cli = ["--statsd-prefix"] + meta = "STATSD_PREFIX" + default = "" + validator = validate_string + desc = """\ + Prefix to use when emitting statsd metrics (a trailing ``.`` is added, + if not provided). + + .. versionadded:: 19.2 + """ + + +class Procname(Setting): + name = "proc_name" + section = "Process Naming" + cli = ["-n", "--name"] + meta = "STRING" + validator = validate_string + default = None + desc = """\ + A base to use with setproctitle for process naming. + + This affects things like ``ps`` and ``top``. If you're going to be + running more than one instance of Gunicorn you'll probably want to set a + name to tell them apart. This requires that you install the setproctitle + module. + + If not set, the *default_proc_name* setting will be used. + """ + + +class DefaultProcName(Setting): + name = "default_proc_name" + section = "Process Naming" + validator = validate_string + default = "gunicorn" + desc = """\ + Internal setting that is adjusted for each type of application. + """ + + +class PythonPath(Setting): + name = "pythonpath" + section = "Server Mechanics" + cli = ["--pythonpath"] + meta = "STRING" + validator = validate_string + default = None + desc = """\ + A comma-separated list of directories to add to the Python path. + + e.g. + ``'/home/djangoprojects/myproject,/home/python/mylibrary'``. + """ + + +class Paste(Setting): + name = "paste" + section = "Server Mechanics" + cli = ["--paste", "--paster"] + meta = "STRING" + validator = validate_string + default = None + desc = """\ + Load a PasteDeploy config file. The argument may contain a ``#`` + symbol followed by the name of an app section from the config file, + e.g. ``production.ini#admin``. + + At this time, using alternate server blocks is not supported. Use the + command line arguments to control server configuration instead. + """ + + +class OnStarting(Setting): + name = "on_starting" + section = "Server Hooks" + validator = validate_callable(1) + type = six.callable + + def on_starting(server): + pass + default = staticmethod(on_starting) + desc = """\ + Called just before the master process is initialized. + + The callable needs to accept a single instance variable for the Arbiter. + """ + + +class OnReload(Setting): + name = "on_reload" + section = "Server Hooks" + validator = validate_callable(1) + type = six.callable + + def on_reload(server): + pass + default = staticmethod(on_reload) + desc = """\ + Called to recycle workers during a reload via SIGHUP. + + The callable needs to accept a single instance variable for the Arbiter. + """ + + +class WhenReady(Setting): + name = "when_ready" + section = "Server Hooks" + validator = validate_callable(1) + type = six.callable + + def when_ready(server): + pass + default = staticmethod(when_ready) + desc = """\ + Called just after the server is started. + + The callable needs to accept a single instance variable for the Arbiter. + """ + + +class Prefork(Setting): + name = "pre_fork" + section = "Server Hooks" + validator = validate_callable(2) + type = six.callable + + def pre_fork(server, worker): + pass + default = staticmethod(pre_fork) + desc = """\ + Called just before a worker is forked. + + The callable needs to accept two instance variables for the Arbiter and + new Worker. + """ + + +class Postfork(Setting): + name = "post_fork" + section = "Server Hooks" + validator = validate_callable(2) + type = six.callable + + def post_fork(server, worker): + pass + default = staticmethod(post_fork) + desc = """\ + Called just after a worker has been forked. + + The callable needs to accept two instance variables for the Arbiter and + new Worker. + """ + + +class PostWorkerInit(Setting): + name = "post_worker_init" + section = "Server Hooks" + validator = validate_callable(1) + type = six.callable + + def post_worker_init(worker): + pass + + default = staticmethod(post_worker_init) + desc = """\ + Called just after a worker has initialized the application. + + The callable needs to accept one instance variable for the initialized + Worker. + """ + +class WorkerInt(Setting): + name = "worker_int" + section = "Server Hooks" + validator = validate_callable(1) + type = six.callable + + def worker_int(worker): + pass + + default = staticmethod(worker_int) + desc = """\ + Called just after a worker exited on SIGINT or SIGQUIT. + + The callable needs to accept one instance variable for the initialized + Worker. + """ + + +class WorkerAbort(Setting): + name = "worker_abort" + section = "Server Hooks" + validator = validate_callable(1) + type = six.callable + + def worker_abort(worker): + pass + + default = staticmethod(worker_abort) + desc = """\ + Called when a worker received the SIGABRT signal. + + This call generally happens on timeout. + + The callable needs to accept one instance variable for the initialized + Worker. + """ + + +class PreExec(Setting): + name = "pre_exec" + section = "Server Hooks" + validator = validate_callable(1) + type = six.callable + + def pre_exec(server): + pass + default = staticmethod(pre_exec) + desc = """\ + Called just before a new master process is forked. + + The callable needs to accept a single instance variable for the Arbiter. + """ + + +class PreRequest(Setting): + name = "pre_request" + section = "Server Hooks" + validator = validate_callable(2) + type = six.callable + + def pre_request(worker, req): + worker.log.debug("%s %s" % (req.method, req.path)) + default = staticmethod(pre_request) + desc = """\ + Called just before a worker processes the request. + + The callable needs to accept two instance variables for the Worker and + the Request. + """ + + +class PostRequest(Setting): + name = "post_request" + section = "Server Hooks" + validator = validate_post_request + type = six.callable + + def post_request(worker, req, environ, resp): + pass + default = staticmethod(post_request) + desc = """\ + Called after a worker processes the request. + + The callable needs to accept two instance variables for the Worker and + the Request. + """ + + +class ChildExit(Setting): + name = "child_exit" + section = "Server Hooks" + validator = validate_callable(2) + type = six.callable + + def child_exit(server, worker): + pass + default = staticmethod(child_exit) + desc = """\ + Called just after a worker has been exited, in the master process. + + The callable needs to accept two instance variables for the Arbiter and + the just-exited Worker. + + .. versionadded:: 19.7 + """ + + +class WorkerExit(Setting): + name = "worker_exit" + section = "Server Hooks" + validator = validate_callable(2) + type = six.callable + + def worker_exit(server, worker): + pass + default = staticmethod(worker_exit) + desc = """\ + Called just after a worker has been exited, in the worker process. + + The callable needs to accept two instance variables for the Arbiter and + the just-exited Worker. + """ + + +class NumWorkersChanged(Setting): + name = "nworkers_changed" + section = "Server Hooks" + validator = validate_callable(3) + type = six.callable + + def nworkers_changed(server, new_value, old_value): + pass + default = staticmethod(nworkers_changed) + desc = """\ + Called just after *num_workers* has been changed. + + The callable needs to accept an instance variable of the Arbiter and + two integers of number of workers after and before change. + + If the number of workers is set for the first time, *old_value* would + be ``None``. + """ + +class OnExit(Setting): + name = "on_exit" + section = "Server Hooks" + validator = validate_callable(1) + + def on_exit(server): + pass + + default = staticmethod(on_exit) + desc = """\ + Called just before exiting Gunicorn. + + The callable needs to accept a single instance variable for the Arbiter. + """ + + +class ProxyProtocol(Setting): + name = "proxy_protocol" + section = "Server Mechanics" + cli = ["--proxy-protocol"] + validator = validate_bool + default = False + action = "store_true" + desc = """\ + Enable detect PROXY protocol (PROXY mode). + + Allow using HTTP and Proxy together. It may be useful for work with + stunnel as HTTPS frontend and Gunicorn as HTTP server. + + PROXY protocol: http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt + + Example for stunnel config:: + + [https] + protocol = proxy + accept = 443 + connect = 80 + cert = /etc/ssl/certs/stunnel.pem + key = /etc/ssl/certs/stunnel.key + """ + + +class ProxyAllowFrom(Setting): + name = "proxy_allow_ips" + section = "Server Mechanics" + cli = ["--proxy-allow-from"] + validator = validate_string_to_list + default = "127.0.0.1" + desc = """\ + Front-end's IPs from which allowed accept proxy requests (comma separate). + + Set to ``*`` to disable checking of Front-end IPs (useful for setups + where you don't know in advance the IP address of Front-end, but + you still trust the environment) + """ + + +class KeyFile(Setting): + name = "keyfile" + section = "SSL" + cli = ["--keyfile"] + meta = "FILE" + validator = validate_string + default = None + desc = """\ + SSL key file + """ + + +class CertFile(Setting): + name = "certfile" + section = "SSL" + cli = ["--certfile"] + meta = "FILE" + validator = validate_string + default = None + desc = """\ + SSL certificate file + """ + +class SSLVersion(Setting): + name = "ssl_version" + section = "SSL" + cli = ["--ssl-version"] + validator = validate_pos_int + default = ssl.PROTOCOL_SSLv23 + desc = """\ + SSL version to use (see stdlib ssl module's) + + .. versionchanged:: 19.7 + The default value has been changed from ``ssl.PROTOCOL_TLSv1`` to + ``ssl.PROTOCOL_SSLv23``. + """ + +class CertReqs(Setting): + name = "cert_reqs" + section = "SSL" + cli = ["--cert-reqs"] + validator = validate_pos_int + default = ssl.CERT_NONE + desc = """\ + Whether client certificate is required (see stdlib ssl module's) + """ + +class CACerts(Setting): + name = "ca_certs" + section = "SSL" + cli = ["--ca-certs"] + meta = "FILE" + validator = validate_string + default = None + desc = """\ + CA certificates file + """ + +class SuppressRaggedEOFs(Setting): + name = "suppress_ragged_eofs" + section = "SSL" + cli = ["--suppress-ragged-eofs"] + action = "store_true" + default = True + validator = validate_bool + desc = """\ + Suppress ragged EOFs (see stdlib ssl module's) + """ + +class DoHandshakeOnConnect(Setting): + name = "do_handshake_on_connect" + section = "SSL" + cli = ["--do-handshake-on-connect"] + validator = validate_bool + action = "store_true" + default = False + desc = """\ + Whether to perform SSL handshake on socket connect (see stdlib ssl module's) + """ + + +if sys.version_info >= (2, 7): + class Ciphers(Setting): + name = "ciphers" + section = "SSL" + cli = ["--ciphers"] + validator = validate_string + default = 'TLSv1' + desc = """\ + Ciphers to use (see stdlib ssl module's) + """ + + +class PasteGlobalConf(Setting): + name = "raw_paste_global_conf" + action = "append" + section = "Server Mechanics" + cli = ["--paste-global"] + meta = "CONF" + validator = validate_list_string + default = [] + + desc = """\ + Set a PasteDeploy global config variable in ``key=value`` form. + + The option can be specified multiple times. + + The variables are passed to the the PasteDeploy entrypoint. Example:: + + $ gunicorn -b 127.0.0.1:8000 --paste development.ini --paste-global FOO=1 --paste-global BAR=2 + + .. versionadded:: 19.7 + """ diff --git a/venv/lib/python3.6/site-packages/gunicorn/debug.py b/venv/lib/python3.6/site-packages/gunicorn/debug.py new file mode 100644 index 0000000..996fe1b --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/debug.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +"""The debug module contains utilities and functions for better +debugging Gunicorn.""" + +import sys +import linecache +import re +import inspect + +__all__ = ['spew', 'unspew'] + +_token_spliter = re.compile(r'\W+') + + +class Spew(object): + + def __init__(self, trace_names=None, show_values=True): + self.trace_names = trace_names + self.show_values = show_values + + def __call__(self, frame, event, arg): + if event == 'line': + lineno = frame.f_lineno + if '__file__' in frame.f_globals: + filename = frame.f_globals['__file__'] + if (filename.endswith('.pyc') or + filename.endswith('.pyo')): + filename = filename[:-1] + name = frame.f_globals['__name__'] + line = linecache.getline(filename, lineno) + else: + name = '[unknown]' + try: + src = inspect.getsourcelines(frame) + line = src[lineno] + except IOError: + line = 'Unknown code named [%s]. VM instruction #%d' % ( + frame.f_code.co_name, frame.f_lasti) + if self.trace_names is None or name in self.trace_names: + print('%s:%s: %s' % (name, lineno, line.rstrip())) + if not self.show_values: + return self + details = [] + tokens = _token_spliter.split(line) + for tok in tokens: + if tok in frame.f_globals: + details.append('%s=%r' % (tok, frame.f_globals[tok])) + if tok in frame.f_locals: + details.append('%s=%r' % (tok, frame.f_locals[tok])) + if details: + print("\t%s" % ' '.join(details)) + return self + + +def spew(trace_names=None, show_values=False): + """Install a trace hook which writes incredibly detailed logs + about what code is being executed to stdout. + """ + sys.settrace(Spew(trace_names, show_values)) + + +def unspew(): + """Remove the trace hook installed by spew. + """ + sys.settrace(None) diff --git a/venv/lib/python3.6/site-packages/gunicorn/errors.py b/venv/lib/python3.6/site-packages/gunicorn/errors.py new file mode 100644 index 0000000..727d336 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/errors.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +# We don't need to call super() in __init__ methods of our +# BaseException and Exception classes because we also define +# our own __str__ methods so there is no need to pass 'message' +# to the base class to get a meaningful output from 'str(exc)'. +# pylint: disable=super-init-not-called + + +# we inherit from BaseException here to make sure to not be caught +# at application level +class HaltServer(BaseException): + def __init__(self, reason, exit_status=1): + self.reason = reason + self.exit_status = exit_status + + def __str__(self): + return "" % (self.reason, self.exit_status) + + +class ConfigError(Exception): + """ Exception raised on config error """ + + +class AppImportError(Exception): + """ Exception raised when loading an application """ diff --git a/venv/lib/python3.6/site-packages/gunicorn/glogging.py b/venv/lib/python3.6/site-packages/gunicorn/glogging.py new file mode 100644 index 0000000..041a74d --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/glogging.py @@ -0,0 +1,478 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import base64 +import binascii +import time +import logging +logging.Logger.manager.emittedNoHandlerWarning = 1 +from logging.config import fileConfig +try: + from logging.config import dictConfig +except ImportError: + # python 2.6 + dictConfig = None +import os +import socket +import sys +import threading +import traceback + +from gunicorn import util +from gunicorn.six import PY3, string_types + + +# syslog facility codes +SYSLOG_FACILITIES = { + "auth": 4, + "authpriv": 10, + "cron": 9, + "daemon": 3, + "ftp": 11, + "kern": 0, + "lpr": 6, + "mail": 2, + "news": 7, + "security": 4, # DEPRECATED + "syslog": 5, + "user": 1, + "uucp": 8, + "local0": 16, + "local1": 17, + "local2": 18, + "local3": 19, + "local4": 20, + "local5": 21, + "local6": 22, + "local7": 23 + } + + +CONFIG_DEFAULTS = dict( + version=1, + disable_existing_loggers=False, + + loggers={ + "root": {"level": "INFO", "handlers": ["console"]}, + "gunicorn.error": { + "level": "INFO", + "handlers": ["error_console"], + "propagate": True, + "qualname": "gunicorn.error" + }, + + "gunicorn.access": { + "level": "INFO", + "handlers": ["console"], + "propagate": True, + "qualname": "gunicorn.access" + } + }, + handlers={ + "console": { + "class": "logging.StreamHandler", + "formatter": "generic", + "stream": "ext://sys.stdout" + }, + "error_console": { + "class": "logging.StreamHandler", + "formatter": "generic", + "stream": "ext://sys.stderr" + }, + }, + formatters={ + "generic": { + "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s", + "datefmt": "[%Y-%m-%d %H:%M:%S %z]", + "class": "logging.Formatter" + } + } +) + + +def loggers(): + """ get list of all loggers """ + root = logging.root + existing = root.manager.loggerDict.keys() + return [logging.getLogger(name) for name in existing] + + +class SafeAtoms(dict): + + def __init__(self, atoms): + dict.__init__(self) + for key, value in atoms.items(): + if isinstance(value, string_types): + self[key] = value.replace('"', '\\"') + else: + self[key] = value + + def __getitem__(self, k): + if k.startswith("{"): + kl = k.lower() + if kl in self: + return super(SafeAtoms, self).__getitem__(kl) + else: + return "-" + if k in self: + return super(SafeAtoms, self).__getitem__(k) + else: + return '-' + + +def parse_syslog_address(addr): + + # unix domain socket type depends on backend + # SysLogHandler will try both when given None + if addr.startswith("unix://"): + sock_type = None + + # set socket type only if explicitly requested + parts = addr.split("#", 1) + if len(parts) == 2: + addr = parts[0] + if parts[1] == "dgram": + sock_type = socket.SOCK_DGRAM + + return (sock_type, addr.split("unix://")[1]) + + if addr.startswith("udp://"): + addr = addr.split("udp://")[1] + socktype = socket.SOCK_DGRAM + elif addr.startswith("tcp://"): + addr = addr.split("tcp://")[1] + socktype = socket.SOCK_STREAM + else: + raise RuntimeError("invalid syslog address") + + if '[' in addr and ']' in addr: + host = addr.split(']')[0][1:].lower() + elif ':' in addr: + host = addr.split(':')[0].lower() + elif addr == "": + host = "localhost" + else: + host = addr.lower() + + addr = addr.split(']')[-1] + if ":" in addr: + port = addr.split(':', 1)[1] + if not port.isdigit(): + raise RuntimeError("%r is not a valid port number." % port) + port = int(port) + else: + port = 514 + + return (socktype, (host, port)) + + +class Logger(object): + + LOG_LEVELS = { + "critical": logging.CRITICAL, + "error": logging.ERROR, + "warning": logging.WARNING, + "info": logging.INFO, + "debug": logging.DEBUG + } + loglevel = logging.INFO + + error_fmt = r"%(asctime)s [%(process)d] [%(levelname)s] %(message)s" + datefmt = r"[%Y-%m-%d %H:%M:%S %z]" + + access_fmt = "%(message)s" + syslog_fmt = "[%(process)d] %(message)s" + + atoms_wrapper_class = SafeAtoms + + def __init__(self, cfg): + self.error_log = logging.getLogger("gunicorn.error") + self.error_log.propagate = False + self.access_log = logging.getLogger("gunicorn.access") + self.access_log.propagate = False + self.error_handlers = [] + self.access_handlers = [] + self.logfile = None + self.lock = threading.Lock() + self.cfg = cfg + self.setup(cfg) + + def setup(self, cfg): + self.loglevel = self.LOG_LEVELS.get(cfg.loglevel.lower(), logging.INFO) + self.error_log.setLevel(self.loglevel) + self.access_log.setLevel(logging.INFO) + + # set gunicorn.error handler + if self.cfg.capture_output and cfg.errorlog != "-": + for stream in sys.stdout, sys.stderr: + stream.flush() + + self.logfile = open(cfg.errorlog, 'a+') + os.dup2(self.logfile.fileno(), sys.stdout.fileno()) + os.dup2(self.logfile.fileno(), sys.stderr.fileno()) + + self._set_handler(self.error_log, cfg.errorlog, + logging.Formatter(self.error_fmt, self.datefmt)) + + # set gunicorn.access handler + if cfg.accesslog is not None: + self._set_handler(self.access_log, cfg.accesslog, + fmt=logging.Formatter(self.access_fmt), stream=sys.stdout) + + # set syslog handler + if cfg.syslog: + self._set_syslog_handler( + self.error_log, cfg, self.syslog_fmt, "error" + ) + if not cfg.disable_redirect_access_to_syslog: + self._set_syslog_handler( + self.access_log, cfg, self.syslog_fmt, "access" + ) + + if dictConfig is None and cfg.logconfig_dict: + util.warn("Dictionary-based log configuration requires " + "Python 2.7 or above.") + + if dictConfig and cfg.logconfig_dict: + config = CONFIG_DEFAULTS.copy() + config.update(cfg.logconfig_dict) + try: + dictConfig(config) + except ( + AttributeError, + ImportError, + ValueError, + TypeError + ) as exc: + raise RuntimeError(str(exc)) + elif cfg.logconfig: + if os.path.exists(cfg.logconfig): + defaults = CONFIG_DEFAULTS.copy() + defaults['__file__'] = cfg.logconfig + defaults['here'] = os.path.dirname(cfg.logconfig) + fileConfig(cfg.logconfig, defaults=defaults, + disable_existing_loggers=False) + else: + msg = "Error: log config '%s' not found" + raise RuntimeError(msg % cfg.logconfig) + + def critical(self, msg, *args, **kwargs): + self.error_log.critical(msg, *args, **kwargs) + + def error(self, msg, *args, **kwargs): + self.error_log.error(msg, *args, **kwargs) + + def warning(self, msg, *args, **kwargs): + self.error_log.warning(msg, *args, **kwargs) + + def info(self, msg, *args, **kwargs): + self.error_log.info(msg, *args, **kwargs) + + def debug(self, msg, *args, **kwargs): + self.error_log.debug(msg, *args, **kwargs) + + def exception(self, msg, *args, **kwargs): + self.error_log.exception(msg, *args, **kwargs) + + def log(self, lvl, msg, *args, **kwargs): + if isinstance(lvl, string_types): + lvl = self.LOG_LEVELS.get(lvl.lower(), logging.INFO) + self.error_log.log(lvl, msg, *args, **kwargs) + + def atoms(self, resp, req, environ, request_time): + """ Gets atoms for log formating. + """ + status = resp.status + if isinstance(status, str): + status = status.split(None, 1)[0] + atoms = { + 'h': environ.get('REMOTE_ADDR', '-'), + 'l': '-', + 'u': self._get_user(environ) or '-', + 't': self.now(), + 'r': "%s %s %s" % (environ['REQUEST_METHOD'], + environ['RAW_URI'], environ["SERVER_PROTOCOL"]), + 's': status, + 'm': environ.get('REQUEST_METHOD'), + 'U': environ.get('PATH_INFO'), + 'q': environ.get('QUERY_STRING'), + 'H': environ.get('SERVER_PROTOCOL'), + 'b': getattr(resp, 'sent', None) is not None and str(resp.sent) or '-', + 'B': getattr(resp, 'sent', None), + 'f': environ.get('HTTP_REFERER', '-'), + 'a': environ.get('HTTP_USER_AGENT', '-'), + 'T': request_time.seconds, + 'D': (request_time.seconds*1000000) + request_time.microseconds, + 'L': "%d.%06d" % (request_time.seconds, request_time.microseconds), + 'p': "<%s>" % os.getpid() + } + + # add request headers + if hasattr(req, 'headers'): + req_headers = req.headers + else: + req_headers = req + + if hasattr(req_headers, "items"): + req_headers = req_headers.items() + + atoms.update(dict([("{%s}i" % k.lower(), v) for k, v in req_headers])) + + resp_headers = resp.headers + if hasattr(resp_headers, "items"): + resp_headers = resp_headers.items() + + # add response headers + atoms.update(dict([("{%s}o" % k.lower(), v) for k, v in resp_headers])) + + # add environ variables + environ_variables = environ.items() + atoms.update(dict([("{%s}e" % k.lower(), v) for k, v in environ_variables])) + + return atoms + + def access(self, resp, req, environ, request_time): + """ See http://httpd.apache.org/docs/2.0/logs.html#combined + for format details + """ + + if not (self.cfg.accesslog or self.cfg.logconfig or + self.cfg.logconfig_dict or + (self.cfg.syslog and not self.cfg.disable_redirect_access_to_syslog)): + return + + # wrap atoms: + # - make sure atoms will be test case insensitively + # - if atom doesn't exist replace it by '-' + safe_atoms = self.atoms_wrapper_class(self.atoms(resp, req, environ, + request_time)) + + try: + self.access_log.info(self.cfg.access_log_format, safe_atoms) + except: + self.error(traceback.format_exc()) + + def now(self): + """ return date in Apache Common Log Format """ + return time.strftime('[%d/%b/%Y:%H:%M:%S %z]') + + def reopen_files(self): + if self.cfg.capture_output and self.cfg.errorlog != "-": + for stream in sys.stdout, sys.stderr: + stream.flush() + + with self.lock: + if self.logfile is not None: + self.logfile.close() + self.logfile = open(self.cfg.errorlog, 'a+') + os.dup2(self.logfile.fileno(), sys.stdout.fileno()) + os.dup2(self.logfile.fileno(), sys.stderr.fileno()) + + + for log in loggers(): + for handler in log.handlers: + if isinstance(handler, logging.FileHandler): + handler.acquire() + try: + if handler.stream: + handler.close() + handler.stream = handler._open() + finally: + handler.release() + + def close_on_exec(self): + for log in loggers(): + for handler in log.handlers: + if isinstance(handler, logging.FileHandler): + handler.acquire() + try: + if handler.stream: + util.close_on_exec(handler.stream.fileno()) + finally: + handler.release() + + def _get_gunicorn_handler(self, log): + for h in log.handlers: + if getattr(h, "_gunicorn", False): + return h + + def _set_handler(self, log, output, fmt, stream=None): + # remove previous gunicorn log handler + h = self._get_gunicorn_handler(log) + if h: + log.handlers.remove(h) + + if output is not None: + if output == "-": + h = logging.StreamHandler(stream) + else: + util.check_is_writeable(output) + h = logging.FileHandler(output) + # make sure the user can reopen the file + try: + os.chown(h.baseFilename, self.cfg.user, self.cfg.group) + except OSError: + # it's probably OK there, we assume the user has given + # /dev/null as a parameter. + pass + + h.setFormatter(fmt) + h._gunicorn = True + log.addHandler(h) + + def _set_syslog_handler(self, log, cfg, fmt, name): + # setup format + if not cfg.syslog_prefix: + prefix = cfg.proc_name.replace(":", ".") + else: + prefix = cfg.syslog_prefix + + prefix = "gunicorn.%s.%s" % (prefix, name) + + # set format + fmt = logging.Formatter(r"%s: %s" % (prefix, fmt)) + + # syslog facility + try: + facility = SYSLOG_FACILITIES[cfg.syslog_facility.lower()] + except KeyError: + raise RuntimeError("unknown facility name") + + # parse syslog address + socktype, addr = parse_syslog_address(cfg.syslog_addr) + + # finally setup the syslog handler + if sys.version_info >= (2, 7): + h = logging.handlers.SysLogHandler(address=addr, + facility=facility, socktype=socktype) + else: + # socktype is only supported in 2.7 and sup + # fix issue #541 + h = logging.handlers.SysLogHandler(address=addr, + facility=facility) + + h.setFormatter(fmt) + h._gunicorn = True + log.addHandler(h) + + def _get_user(self, environ): + user = None + http_auth = environ.get("HTTP_AUTHORIZATION") + if http_auth and http_auth.startswith('Basic'): + auth = http_auth.split(" ", 1) + if len(auth) == 2: + try: + # b64decode doesn't accept unicode in Python < 3.3 + # so we need to convert it to a byte string + auth = base64.b64decode(auth[1].strip().encode('utf-8')) + if PY3: # b64decode returns a byte string in Python 3 + auth = auth.decode('utf-8') + auth = auth.split(":", 1) + except (TypeError, binascii.Error, UnicodeDecodeError) as exc: + self.debug("Couldn't get username: %s", exc) + return user + if len(auth) == 2: + user = auth[0] + return user diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/__init__.py b/venv/lib/python3.6/site-packages/gunicorn/http/__init__.py new file mode 100644 index 0000000..1da6f3e --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/http/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +from gunicorn.http.message import Message, Request +from gunicorn.http.parser import RequestParser + +__all__ = ['Message', 'Request', 'RequestParser'] diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/__init__.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d3072574481ee125f6aee55b79276a7d7d0144d GIT binary patch literal 306 zcmXr!<>lHlpFc*Fk%8ec0}@~cvK@f9SPMv`Fr+Z%Fyt~uG3GKwF)@PpOgYTCEKw{# zHggI~3TrxZ6l)4wFoP!hOQ3oNO{QDyzNy8zfdTCx}a(+>sUPehtfnF}e2!z5Y5mW^Q5Q9M4Z?VV6C+6hD z$FF24Vgs28B7Rxvm*f`c7i1RbCY5I9r0C{Ym=tAa85`-t%t0_fHtNU6XXa&=#K-Fu ZRNmsS$<0qG%}KQbnN$q2Lx7Qo2>=LCPlEse literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/_sendfile.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/_sendfile.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02cf7b7316d6c9a167aa10303cff78825449e673 GIT binary patch literal 1338 zcmZux&2HQ_5GE;!`q^DONsYkhrQ7yU>>)9b1VMlVMU2=j3^=yobjj@atiSr`A@nEu ztaU+u4k9lB7-Bd>VcX*nH)9f#`9`Bzq1E(uX#I@xO&f7%8i*Z?%*W{k{uSlq7sR@u z%Sq~Scj}5)oHDw=7nobA4-%vu<}NVvnEyKoyS$tB!ZjAK&Ls+a3=h$ucLhw)fu?LE z-XytpW5M~UWZGw9G%4~pn`@dSdG+?eyMg36i<6A^6PbUf`m!p^LMYDqMc!Ah6WK4b zk%|j3(|CL`z}l}QkGK$dF}95xXaNXh1-$@~?=eCOrI_I<5x*&tT1&B^cR=g~2BgM1 zp~{UsW2>zNKDU)y;~LeZwjoPoLC>AqVHD1}gf;#Gozt2sh#xGfw$m;{Q@_=QNcCWD zEnTl6R||51%{*8Q6L(7gz@l$FF2!&$Vpyy=Srhk~5BLIo$p&gxyTHe7_;^zSr+o=K z|HTMwLG8AjuJ!+61uMY1VzTmnYXb2vvq2ym9{5MVz=Pb_=YH)ozwvx_?fE|u`$M>Q zQ2*dS5GaL!N}wwP!ZKjWb@ey20a+P@Z>9_kZfi@>g`(9Gw*jE43e8-y`jf#H8KlPq=EYigubCqv`JUkK20(+Bq9#71!?J za94;zXi|V*sRXQo5jc{`J07V)5IQj{Dizu>gNsayIF?*#nxD=Um!TWAil&0gDpMMB zu*qgpyU}vB4b;>}B)*0q|9ZNmX606yl=n`nBxCooCy&H5efVH&Qsv3G5cx`ZtyH;n z6W9H64iuM<TC0>I7IZo!xY%?=ONiErVI zf!)^Yrb@yw)^)J~o!B&Bo|rIy8r^A&a=%hZraLeBtPt}yiJiTfdAiLY{9^di(b3`Y z$?i^cw7-4w!{PDEq3D=!%x``T{ycjF{yQI?#Fk(h^$(Z&rWaoadF8T$;rG26Xo ivw~F_|DM7~_5n=P9wC5`2`jLgpBc6xN{`&d)cObXY*)wt literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/body.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/body.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be9975eb9339f4ed9090c2f90f1aca8cb437601e GIT binary patch literal 6417 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/parser.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/parser.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e8750bebfca8f00e2d690066aa51768b8342d8e GIT binary patch literal 1301 zcmZuw&2AGh5cXd-+cZf-B_e)`#0{;~rh+2q0ig;Gh)aa15Yh^1wOzYu(#>vSdr^zR zh4ei*^A0?Tublb{oS1Q%HkDXuKF@f(GxmHld#zUE_s@+l?HVC}$jT=`KL9ZZl1Q49 zF^wrjo#kxIV?GwKpyVBqT#5@Kg{sYY9DrSu0oZ{Gz^=>sA&J6Eh(aRj>L1m#QpQHi zwGYeeRM}63R;g6lHQ!sMmsW=7mmdS=0K{wqC=ydiVkT+KC6oMu#6k*LgB9}1I;@Zx z@>0y}f)A-SO7}Ak|Kf)$%3Bcg0$@gGBq5g0Amp6sEz4$Xj(falSh3V4ft{LUfPo|s zn)jX1D*LJ_#E9ySXU79qKS@n$t=5>qh1J#DG*;7zu`&akR0}z&^tnC^Ir5x{ElXbI>fClOIre*nQ)7`A7gM^gBK4&Zs`P zy*J(d3=sqw0WB{H1A4$8fYd=k5a>sw7Bph?O>9rzkUS>j65kuG=>{xu)0~u5E|V-T zjdHC+TTZYeQ#&jRCyq*aj)bRDufu8AKr-QMYK-SkuVED73vIl*@Ba% z3qV*07zt<__}S!b+J!Y(t>5B2bTM#Rq+^vNu8}0;QdT+Y%_NytX}<7ijHo*ZcM$FY zIDrvXE`lwgkvR?XPVm_VA!LGu>md&}{eA{p*B_SX0K{PE}oJ!Lp_ hi%|>8qmjz2e~l0MzZ$sk)uNQI1H1r2lXiIL$v?r~8tDK4 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/unreader.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/unreader.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75308f3dd36ff057fc7fbc4b1e1de9f4c2cd868d GIT binary patch literal 2423 zcmaJ@UymC#5VyVF%_f)R(xU~HKPCK8ArieqZ9%A@Q`PGUPL;w#K%fX|wYT*f#7 zON2ZpXBvn3UFiBNAc6>*k}i%cr9;+blzd5qC-^B5TzU(y%YpYr0DK@h@ILS*5dt4N zJ`l7+TICmT8fmwv^>lBVAX79CCU>FhZ6K0#sUTe@s9>k0>*ZHW=h6Fs)T|%HXIrBJ?Od)#E?E+kU5E%AfQ9rEl;69AkPr(tnxzxVW9+`vTIF&=087cOa z8mSgl0f;(la}ok;{p8^!8*IU}xVXN$*lo2FXAlUWI>PO)Ig(pV~T0WM+_zZ?r3Q{xKg>)SvG zeU*kRq#@^)O6Ij*g6+#wUL@Xg1Iq;Gf zdX=VIO<-ZwoJGLt>p)f|EOy>{N_i%S^>#~v!8>+-+=$`<$` zaMc$Lun?TdWAz?PFRo+`pS!SK)UnGi-MutkHSvf*y$OSQ3kkYw4Y92)AD2LK6%Ry+ z;oA36(jkpLy93=tS4iu=^I`%ktA=o3)2LOT>@dcURk;3GtuXd&^#JNef$+bw2T#;! z_jz!Qhk1hl7tnI`4oImFkYMau4%I*{6UK(f#D}RYUbGHi6yArvOqx2KT{&yf#rR=j z?4i=9KoCD&!H@V!6~76{Ba)mLZ%|PmQ;0@PaWEJDEOf~Ct8}k|c8jYIVaO{Y&*aqD zTE~pWXhdv!qwIgMKR^>Oga||)r;Jwf7~DGdf;eRjmM_UFj-3s+E9Kg(VYpd4HWj<;P+p0SommG1n%_twMGdKM zhPK2G+hUakiY;2CMbSlz0KK3nP@w-o_o?W^0^Pn8eVPJ&X#3(m6b1Sg6i9#P%#gZ7 zp)lWkxA`vTobR09IiKDsm7JgdS>?&Kw-x0-lq0`c=0ul8oUXWTRURg9DCX*+Z7oT@xhg7*!@KfCS7U#JTGq1{IN{Iq=`YybPSU8wHd zCrY3_R?<}D-E+R)T@cQJ>Yn%2?gjrMa+f|-L_rikQ$*2!=|Fd1M!6))D3^Vu`z_Qg zii)Uyrns+&88M6ZWpPH#;r*&OE9UXOf-laA1$?o7s;f=Jq^smYmPusiHW zz8Ceq=iz3@3nPi@{N4JkJM}ehtzOfT(!JnWtJe{C{g&`$GIOlhXzlnlJt^Y5PO#}k z`vX5r3Qs#hOYU>Ok1_1}>Pqv`Eex(EcIXFUqto+WfT2p~tFONL z{;2Y!Cwp(cx%6c3t(7Nx-&^^#O{<;!bYyPg313FyE5vnhJSQ}vf2M-xhA^>jLs;0S zofK9u^ds3C41D=QSyYrZeNR7n({me7co|8o#)_xJ8d5dVVpXUIntVA{yEzdy0taxf*);@^1M|dTdZ`IF1Fj%4%D$0YjQr;)|E*AF^Gw6s*Re# z%T5dlp7Y`p=UyQEy}FcrS<@w*!$jZq_Y*_3q84u~P3EYj;CY*V)QS8Z&s#!8w%W34 z;BR#9_-dy;A&|0!-Zhi<8bGoS<*Nm)g?m$(Qn5I&GqMcX9`regTzoUG5 zX>3GxYzPAqcr)!C8{Ith-o5z8|>V3vJi%a`hsEMWS?!eR7VJ2fZC_4=Ee zIG0HtM81za&ab`I3PjJBVUkPxg^A7IqiN=&SkviVh*ETt%}$ZB4Z~m2aYEs_yB4I(V2UA0PvwkoMDKJ=KJy{z=t$ z4LqiR7U){34nR3~2LwD&=M`aoVWo$b2{+2!Z8jggaWzcT>%!f`e7)GPYR8@ri_O>4-<+b_`AJ*5K-beN3-PJou5v8?H zy!GbVy~c-bZtd11@8Q}#cVe+0tgSXzZ?E33*-X~N8V;cM{ls|M7yF5|<40S4k(j$f zU+yRRP<9fd+wTNPE|VM>OkMiBFo~F+S(@lz--!(;l@mMr5$&3F2$)K?L*vv?7d1&t zJVx^zDww_3kpa_`F%(2t?tl?w&CgQ`EgPX$=;y&uyp61EQdwAEkh0G;+%jZ&Z{I^2 z_1pbkQfT)&eh_&rA*4Gg4Ei#1owO%6WO=HunQj?0d;EzvpiTj&=O{s3Cg zKtnyn43;WCL|vP#7yZcaQl2SThNO|!z@ab>^sxz=P4bpd#<@s?1vBW79B5;E@2}{{ zKr#R66K$LyJ8@pz5NI9i89Nk zMKC*JmSx8hXLux?M#5Kr3q?{9bEHzDYOF(=X1CM&99sV}&V55GRwd4I&)>udGh&`; zbeUEw&T;)8bNvF>JDi8&0v(U!fg#d0p2wQXC{5OQ;Rfw{c3g~Ytl~f8Vz(08V)lS{ zF)l`D;v&w0B`%_$jkk)o)t!sZlEt7|Vl%cj01j)H8g60z_S(G%O|Nn5qk5vaR{imV z`t9bZ_!hu=yB|cIVCaWr7+ER4wfxp1S{83-EhVSqxW?Vpb$oGqwb86MnoG@39@LXb z$)nP!JZZaMZ+zIin@q}HT6Rqu+>_tJG>Phs%&XyduZE)v>$;)rd0~6Y-|^)gw6jG^ ztxO%oFV*XbL5TVVp^3z#Y}olbm_6()bxdey(*mE({DWJ~yWYLV%4%&svDf@?&<{eN zl~1DefuXWfmgqoO@Br9H3KO%}e-5k#4**~a9?+oIYWo0Gytu^agq2&qJ@&` zN-W_M(BSi(nBkz;i4wi%2l6#)H3lu1T(SW28YOgiYbDkK@+R_$mFg%=;!9}VRLKIs zhXc7r?Z#F=jFLjwmYqSw=-RcngiLdJ%E8Nn{$4<5H&3&r>gI%UvD&cvk^daMLV*Mj zM=$DzTGp$otrY=yY@ogLA)RgGPt(w*R?*Y32>%t;GJ006BJyPzZAONu#h+FLT0|Rb zDSV;YMp>0){pgwgbmVU037;YPX6P;~KzLJJf;XOrcL?nv3aH7E=D^z)Wl_O9FREe& zZ%53EGk6!ooH&bjQOt{Tc$dV2IFEN(To4!Wt_Z?SFG-T4Hko?*(VN)A3iLiQCmc$4 zBH;)?Z6XE2Kx(mTkmI~9a8$j#AP3apnnFyjXOZE)-dJIAKFV@NNb-G5MNqvJ4Iz&0 ztzoe3i^SO(hLOk4s%%n^VkVj%L^4V$Bo`#f9t3PN+G0tC{0;}3lDvEZT|i}IdJjeH zG$_VU^`Fo!B)Lf)QZg{d|3)*DlXHmkkHXa{-3X%b-cu(>Yc$~#ZlDq`Ud#z;EWjSN z{dAxIif#;%6gb4w`Hpm$q6M0oB#2o>)PatKjYO)#C067dMM+SCU$&hOuOMg0*9Ej7 z_W+KDHM*9SE!a0DypI+Omdlb4sGg%NcB8qH7!*l)@e9oH;+K@XiMe6C1LS2NNiE0Q zORae^-0BZ|B4ZmFZs}7(fCeX*S?rOHIGq}DfPVjmCxc8?96x%Bc;@ho&Q7UvbfU&F z5hemNUA{mj*5!Mn*ReN6euwrhzk|pK+$z{Zov;*ALQ*<8VTg#qgMF;PUZX!zPTnOS z<3Rl}kQM}4*;Xqf`360GWzr{@07h65@QuY@^KYFEBl%%laCMDz`CFNTolw}0ZCUy1n zP7q1LoN||%osUT&rcD)KD)Mf;iH3&`_p-y5GKt)^$&ulC_}JP0Ui(x%S_V619+^-;lUfV4Kr-?{g!o#lXMJs|BVNFH(z?z8^JQ!xUP0#~ zj-a-fv&Ge z!IFU9r`Bd}z1<{pG&$0)%2AnM3iRJnru7_@1HVlYAiJPd)6GBk zpQbKSZB~AQDmKtbzKOslOM)cDK}NPE+gp+lb5hBor61qA|FDjjsU%Y{G2@7f4&+0s zh$x{$ofyNY({o9Jw)%rXC*0!6Sx>XE&*Chp;x;JUm6&Xtfh(wV!fldd1cB9*@O5Mq z2n$Ro0xjv2nq)GyHk)BG#B;i)juxh(bV9aI*ONCfG@V{P+2u?_LSG7dTa_$f3tQO($9JWn#Iu;jY1gtS)UnFCO@)QHAT*R9@niUq zXoo)};6r9Pi*v>XPK?N*j^1`;k~f50K1Lv6(?H-AfdzO$8ax@q{eiU!XBQf&Fi=2a z#PiUr z&vfT9afSl76tH2es2O@uodre&I%GU}L^vlQf$Zbc+4M06134Ax5L!YGOd|Im;>qz` zVnQ0j!8Hy<6pD@GIb)63LX5y0h6XMwTw3%YwFFTILvoF;O}UQQIB3j~ojGV$#CieW zpijx~Od~usb!1-+Q>rsXK&N-`VWMu!ckw8w`14E=s!Ff^s6dOqG z2KjXIHz;{R32U$pa*4jv3DVPZoDCUxNc5K6bS)(0`~41mdmQvK-G8%yxR}gQmkTbkl#UfMP7Qzxw@VrVy)9p<{k#&a6kc1U##Ma zwZ%w^&j6WVd6UM?k!ZAfJ-I|xzspr0-2cJ)J-04N1hN^|>&=>z-Y?k_s2DdfQvN=* zTm6j<0QmIEl^0u6WExLv{4dQ=vr?iRUZI3fi-vz|_%=#zb$Yb| z*L(+YGY-+EJ!$0d zk6W4)m&vI}Ejyp8!o3|jn0#2>{8T^7pJ5uriLg{C0y(BhFH>@n>UF*Z?mz9qDdJnW zB)`^resA>y+_&)^9bf-&_>&`$9GPPZokrrszLDPDzfS@hzK4X)6#sFk5S|EJT}Pt; ZVVO^1S*;p;2K8!X_M%<1OLoO>{y*1yDfj>Y literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/_sendfile.py b/venv/lib/python3.6/site-packages/gunicorn/http/_sendfile.py new file mode 100644 index 0000000..1764cb3 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/http/_sendfile.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import errno +import os +import sys + +try: + import ctypes + import ctypes.util +except MemoryError: + # selinux execmem denial + # https://bugzilla.redhat.com/show_bug.cgi?id=488396 + raise ImportError + +SUPPORTED_PLATFORMS = ( + 'darwin', + 'freebsd', + 'dragonfly', + 'linux2') + +if sys.platform not in SUPPORTED_PLATFORMS: + raise ImportError("sendfile isn't supported on this platform") + +_libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) +_sendfile = _libc.sendfile + + +def sendfile(fdout, fdin, offset, nbytes): + if sys.platform == 'darwin': + _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_uint64, + ctypes.POINTER(ctypes.c_uint64), ctypes.c_voidp, + ctypes.c_int] + _nbytes = ctypes.c_uint64(nbytes) + result = _sendfile(fdin, fdout, offset, _nbytes, None, 0) + + if result == -1: + e = ctypes.get_errno() + if e == errno.EAGAIN and _nbytes.value is not None: + return _nbytes.value + raise OSError(e, os.strerror(e)) + return _nbytes.value + elif sys.platform in ('freebsd', 'dragonfly',): + _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_uint64, + ctypes.c_uint64, ctypes.c_voidp, + ctypes.POINTER(ctypes.c_uint64), ctypes.c_int] + _sbytes = ctypes.c_uint64() + result = _sendfile(fdin, fdout, offset, nbytes, None, _sbytes, 0) + if result == -1: + e = ctypes.get_errno() + if e == errno.EAGAIN and _sbytes.value is not None: + return _sbytes.value + raise OSError(e, os.strerror(e)) + return _sbytes.value + + else: + _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, + ctypes.POINTER(ctypes.c_uint64), ctypes.c_size_t] + + _offset = ctypes.c_uint64(offset) + sent = _sendfile(fdout, fdin, _offset, nbytes) + if sent == -1: + e = ctypes.get_errno() + raise OSError(e, os.strerror(e)) + return sent diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/body.py b/venv/lib/python3.6/site-packages/gunicorn/http/body.py new file mode 100644 index 0000000..fb8633e --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/http/body.py @@ -0,0 +1,259 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +from gunicorn.http.errors import (NoMoreData, ChunkMissingTerminator, + InvalidChunkSize) +from gunicorn import six + + +class ChunkedReader(object): + def __init__(self, req, unreader): + self.req = req + self.parser = self.parse_chunked(unreader) + self.buf = six.BytesIO() + + def read(self, size): + if not isinstance(size, six.integer_types): + raise TypeError("size must be an integral type") + if size < 0: + raise ValueError("Size must be positive.") + if size == 0: + return b"" + + if self.parser: + while self.buf.tell() < size: + try: + self.buf.write(six.next(self.parser)) + except StopIteration: + self.parser = None + break + + data = self.buf.getvalue() + ret, rest = data[:size], data[size:] + self.buf = six.BytesIO() + self.buf.write(rest) + return ret + + def parse_trailers(self, unreader, data): + buf = six.BytesIO() + buf.write(data) + + idx = buf.getvalue().find(b"\r\n\r\n") + done = buf.getvalue()[:2] == b"\r\n" + while idx < 0 and not done: + self.get_data(unreader, buf) + idx = buf.getvalue().find(b"\r\n\r\n") + done = buf.getvalue()[:2] == b"\r\n" + if done: + unreader.unread(buf.getvalue()[2:]) + return b"" + self.req.trailers = self.req.parse_headers(buf.getvalue()[:idx]) + unreader.unread(buf.getvalue()[idx + 4:]) + + def parse_chunked(self, unreader): + (size, rest) = self.parse_chunk_size(unreader) + while size > 0: + while size > len(rest): + size -= len(rest) + yield rest + rest = unreader.read() + if not rest: + raise NoMoreData() + yield rest[:size] + # Remove \r\n after chunk + rest = rest[size:] + while len(rest) < 2: + rest += unreader.read() + if rest[:2] != b'\r\n': + raise ChunkMissingTerminator(rest[:2]) + (size, rest) = self.parse_chunk_size(unreader, data=rest[2:]) + + def parse_chunk_size(self, unreader, data=None): + buf = six.BytesIO() + if data is not None: + buf.write(data) + + idx = buf.getvalue().find(b"\r\n") + while idx < 0: + self.get_data(unreader, buf) + idx = buf.getvalue().find(b"\r\n") + + data = buf.getvalue() + line, rest_chunk = data[:idx], data[idx + 2:] + + chunk_size = line.split(b";", 1)[0].strip() + try: + chunk_size = int(chunk_size, 16) + except ValueError: + raise InvalidChunkSize(chunk_size) + + if chunk_size == 0: + try: + self.parse_trailers(unreader, rest_chunk) + except NoMoreData: + pass + return (0, None) + return (chunk_size, rest_chunk) + + def get_data(self, unreader, buf): + data = unreader.read() + if not data: + raise NoMoreData() + buf.write(data) + + +class LengthReader(object): + def __init__(self, unreader, length): + self.unreader = unreader + self.length = length + + def read(self, size): + if not isinstance(size, six.integer_types): + raise TypeError("size must be an integral type") + + size = min(self.length, size) + if size < 0: + raise ValueError("Size must be positive.") + if size == 0: + return b"" + + buf = six.BytesIO() + data = self.unreader.read() + while data: + buf.write(data) + if buf.tell() >= size: + break + data = self.unreader.read() + + buf = buf.getvalue() + ret, rest = buf[:size], buf[size:] + self.unreader.unread(rest) + self.length -= size + return ret + + +class EOFReader(object): + def __init__(self, unreader): + self.unreader = unreader + self.buf = six.BytesIO() + self.finished = False + + def read(self, size): + if not isinstance(size, six.integer_types): + raise TypeError("size must be an integral type") + if size < 0: + raise ValueError("Size must be positive.") + if size == 0: + return b"" + + if self.finished: + data = self.buf.getvalue() + ret, rest = data[:size], data[size:] + self.buf = six.BytesIO() + self.buf.write(rest) + return ret + + data = self.unreader.read() + while data: + self.buf.write(data) + if self.buf.tell() > size: + break + data = self.unreader.read() + + if not data: + self.finished = True + + data = self.buf.getvalue() + ret, rest = data[:size], data[size:] + self.buf = six.BytesIO() + self.buf.write(rest) + return ret + + +class Body(object): + def __init__(self, reader): + self.reader = reader + self.buf = six.BytesIO() + + def __iter__(self): + return self + + def __next__(self): + ret = self.readline() + if not ret: + raise StopIteration() + return ret + next = __next__ + + def getsize(self, size): + if size is None: + return six.MAXSIZE + elif not isinstance(size, six.integer_types): + raise TypeError("size must be an integral type") + elif size < 0: + return six.MAXSIZE + return size + + def read(self, size=None): + size = self.getsize(size) + if size == 0: + return b"" + + if size < self.buf.tell(): + data = self.buf.getvalue() + ret, rest = data[:size], data[size:] + self.buf = six.BytesIO() + self.buf.write(rest) + return ret + + while size > self.buf.tell(): + data = self.reader.read(1024) + if not data: + break + self.buf.write(data) + + data = self.buf.getvalue() + ret, rest = data[:size], data[size:] + self.buf = six.BytesIO() + self.buf.write(rest) + return ret + + def readline(self, size=None): + size = self.getsize(size) + if size == 0: + return b"" + + data = self.buf.getvalue() + self.buf = six.BytesIO() + + ret = [] + while 1: + idx = data.find(b"\n", 0, size) + idx = idx + 1 if idx >= 0 else size if len(data) >= size else 0 + if idx: + ret.append(data[:idx]) + self.buf.write(data[idx:]) + break + + ret.append(data) + size -= len(data) + data = self.reader.read(min(1024, size)) + if not data: + break + + return b"".join(ret) + + def readlines(self, size=None): + ret = [] + data = self.read() + while data: + pos = data.find(b"\n") + if pos < 0: + ret.append(data) + data = b"" + else: + line, data = data[:pos + 1], data[pos + 1:] + ret.append(line) + return ret diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/errors.py b/venv/lib/python3.6/site-packages/gunicorn/http/errors.py new file mode 100644 index 0000000..7839ef0 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/http/errors.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +# We don't need to call super() in __init__ methods of our +# BaseException and Exception classes because we also define +# our own __str__ methods so there is no need to pass 'message' +# to the base class to get a meaningful output from 'str(exc)'. +# pylint: disable=super-init-not-called + + +class ParseException(Exception): + pass + + +class NoMoreData(IOError): + def __init__(self, buf=None): + self.buf = buf + + def __str__(self): + return "No more data after: %r" % self.buf + + +class InvalidRequestLine(ParseException): + def __init__(self, req): + self.req = req + self.code = 400 + + def __str__(self): + return "Invalid HTTP request line: %r" % self.req + + +class InvalidRequestMethod(ParseException): + def __init__(self, method): + self.method = method + + def __str__(self): + return "Invalid HTTP method: %r" % self.method + + +class InvalidHTTPVersion(ParseException): + def __init__(self, version): + self.version = version + + def __str__(self): + return "Invalid HTTP Version: %r" % self.version + + +class InvalidHeader(ParseException): + def __init__(self, hdr, req=None): + self.hdr = hdr + self.req = req + + def __str__(self): + return "Invalid HTTP Header: %r" % self.hdr + + +class InvalidHeaderName(ParseException): + def __init__(self, hdr): + self.hdr = hdr + + def __str__(self): + return "Invalid HTTP header name: %r" % self.hdr + + +class InvalidChunkSize(IOError): + def __init__(self, data): + self.data = data + + def __str__(self): + return "Invalid chunk size: %r" % self.data + + +class ChunkMissingTerminator(IOError): + def __init__(self, term): + self.term = term + + def __str__(self): + return "Invalid chunk terminator is not '\\r\\n': %r" % self.term + + +class LimitRequestLine(ParseException): + def __init__(self, size, max_size): + self.size = size + self.max_size = max_size + + def __str__(self): + return "Request Line is too large (%s > %s)" % (self.size, self.max_size) + + +class LimitRequestHeaders(ParseException): + def __init__(self, msg): + self.msg = msg + + def __str__(self): + return self.msg + + +class InvalidProxyLine(ParseException): + def __init__(self, line): + self.line = line + self.code = 400 + + def __str__(self): + return "Invalid PROXY line: %r" % self.line + + +class ForbiddenProxyRequest(ParseException): + def __init__(self, host): + self.host = host + self.code = 403 + + def __str__(self): + return "Proxy request from %r not allowed" % self.host + + +class InvalidSchemeHeaders(ParseException): + def __str__(self): + return "Contradictory scheme headers" diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/message.py b/venv/lib/python3.6/site-packages/gunicorn/http/message.py new file mode 100644 index 0000000..2700b32 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/http/message.py @@ -0,0 +1,363 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import re +import socket +from errno import ENOTCONN + +from gunicorn._compat import bytes_to_str +from gunicorn.http.unreader import SocketUnreader +from gunicorn.http.body import ChunkedReader, LengthReader, EOFReader, Body +from gunicorn.http.errors import (InvalidHeader, InvalidHeaderName, NoMoreData, + InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, + LimitRequestLine, LimitRequestHeaders) +from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest +from gunicorn.http.errors import InvalidSchemeHeaders +from gunicorn.six import BytesIO, string_types +from gunicorn.util import split_request_uri + +MAX_REQUEST_LINE = 8190 +MAX_HEADERS = 32768 +DEFAULT_MAX_HEADERFIELD_SIZE = 8190 + +HEADER_RE = re.compile(r"[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\"]") +METH_RE = re.compile(r"[A-Z0-9$-_.]{3,20}") +VERSION_RE = re.compile(r"HTTP/(\d+)\.(\d+)") + + +class Message(object): + def __init__(self, cfg, unreader): + self.cfg = cfg + self.unreader = unreader + self.version = None + self.headers = [] + self.trailers = [] + self.body = None + self.scheme = "https" if cfg.is_ssl else "http" + + # set headers limits + self.limit_request_fields = cfg.limit_request_fields + if (self.limit_request_fields <= 0 + or self.limit_request_fields > MAX_HEADERS): + self.limit_request_fields = MAX_HEADERS + self.limit_request_field_size = cfg.limit_request_field_size + if self.limit_request_field_size < 0: + self.limit_request_field_size = DEFAULT_MAX_HEADERFIELD_SIZE + + # set max header buffer size + max_header_field_size = self.limit_request_field_size or DEFAULT_MAX_HEADERFIELD_SIZE + self.max_buffer_headers = self.limit_request_fields * \ + (max_header_field_size + 2) + 4 + + unused = self.parse(self.unreader) + self.unreader.unread(unused) + self.set_body_reader() + + def parse(self, unreader): + raise NotImplementedError() + + def parse_headers(self, data): + cfg = self.cfg + headers = [] + + # Split lines on \r\n keeping the \r\n on each line + lines = [bytes_to_str(line) + "\r\n" for line in data.split(b"\r\n")] + + # handle scheme headers + scheme_header = False + secure_scheme_headers = {} + if '*' in cfg.forwarded_allow_ips: + secure_scheme_headers = cfg.secure_scheme_headers + elif isinstance(self.unreader, SocketUnreader): + remote_addr = self.unreader.sock.getpeername() + if isinstance(remote_addr, tuple): + remote_host = remote_addr[0] + if remote_host in cfg.forwarded_allow_ips: + secure_scheme_headers = cfg.secure_scheme_headers + elif isinstance(remote_addr, string_types): + secure_scheme_headers = cfg.secure_scheme_headers + + # Parse headers into key/value pairs paying attention + # to continuation lines. + while lines: + if len(headers) >= self.limit_request_fields: + raise LimitRequestHeaders("limit request headers fields") + + # Parse initial header name : value pair. + curr = lines.pop(0) + header_length = len(curr) + if curr.find(":") < 0: + raise InvalidHeader(curr.strip()) + name, value = curr.split(":", 1) + name = name.rstrip(" \t").upper() + if HEADER_RE.search(name): + raise InvalidHeaderName(name) + + name, value = name.strip(), [value.lstrip()] + + # Consume value continuation lines + while lines and lines[0].startswith((" ", "\t")): + curr = lines.pop(0) + header_length += len(curr) + if header_length > self.limit_request_field_size > 0: + raise LimitRequestHeaders("limit request headers " + + "fields size") + value.append(curr) + value = ''.join(value).rstrip() + + if header_length > self.limit_request_field_size > 0: + raise LimitRequestHeaders("limit request headers fields size") + + if name in secure_scheme_headers: + secure = value == secure_scheme_headers[name] + scheme = "https" if secure else "http" + if scheme_header: + if scheme != self.scheme: + raise InvalidSchemeHeaders() + else: + scheme_header = True + self.scheme = scheme + + headers.append((name, value)) + + return headers + + def set_body_reader(self): + chunked = False + content_length = None + for (name, value) in self.headers: + if name == "CONTENT-LENGTH": + content_length = value + elif name == "TRANSFER-ENCODING": + chunked = value.lower() == "chunked" + elif name == "SEC-WEBSOCKET-KEY1": + content_length = 8 + + if chunked: + self.body = Body(ChunkedReader(self, self.unreader)) + elif content_length is not None: + try: + content_length = int(content_length) + except ValueError: + raise InvalidHeader("CONTENT-LENGTH", req=self) + + if content_length < 0: + raise InvalidHeader("CONTENT-LENGTH", req=self) + + self.body = Body(LengthReader(self.unreader, content_length)) + else: + self.body = Body(EOFReader(self.unreader)) + + def should_close(self): + for (h, v) in self.headers: + if h == "CONNECTION": + v = v.lower().strip() + if v == "close": + return True + elif v == "keep-alive": + return False + break + return self.version <= (1, 0) + + +class Request(Message): + def __init__(self, cfg, unreader, req_number=1): + self.method = None + self.uri = None + self.path = None + self.query = None + self.fragment = None + + # get max request line size + self.limit_request_line = cfg.limit_request_line + if (self.limit_request_line < 0 + or self.limit_request_line >= MAX_REQUEST_LINE): + self.limit_request_line = MAX_REQUEST_LINE + + self.req_number = req_number + self.proxy_protocol_info = None + super(Request, self).__init__(cfg, unreader) + + def get_data(self, unreader, buf, stop=False): + data = unreader.read() + if not data: + if stop: + raise StopIteration() + raise NoMoreData(buf.getvalue()) + buf.write(data) + + def parse(self, unreader): + buf = BytesIO() + self.get_data(unreader, buf, stop=True) + + # get request line + line, rbuf = self.read_line(unreader, buf, self.limit_request_line) + + # proxy protocol + if self.proxy_protocol(bytes_to_str(line)): + # get next request line + buf = BytesIO() + buf.write(rbuf) + line, rbuf = self.read_line(unreader, buf, self.limit_request_line) + + self.parse_request_line(line) + buf = BytesIO() + buf.write(rbuf) + + # Headers + data = buf.getvalue() + idx = data.find(b"\r\n\r\n") + + done = data[:2] == b"\r\n" + while True: + idx = data.find(b"\r\n\r\n") + done = data[:2] == b"\r\n" + + if idx < 0 and not done: + self.get_data(unreader, buf) + data = buf.getvalue() + if len(data) > self.max_buffer_headers: + raise LimitRequestHeaders("max buffer headers") + else: + break + + if done: + self.unreader.unread(data[2:]) + return b"" + + self.headers = self.parse_headers(data[:idx]) + + ret = data[idx + 4:] + buf = None + return ret + + def read_line(self, unreader, buf, limit=0): + data = buf.getvalue() + + while True: + idx = data.find(b"\r\n") + if idx >= 0: + # check if the request line is too large + if idx > limit > 0: + raise LimitRequestLine(idx, limit) + break + elif len(data) - 2 > limit > 0: + raise LimitRequestLine(len(data), limit) + self.get_data(unreader, buf) + data = buf.getvalue() + + return (data[:idx], # request line, + data[idx + 2:]) # residue in the buffer, skip \r\n + + def proxy_protocol(self, line): + """\ + Detect, check and parse proxy protocol. + + :raises: ForbiddenProxyRequest, InvalidProxyLine. + :return: True for proxy protocol line else False + """ + if not self.cfg.proxy_protocol: + return False + + if self.req_number != 1: + return False + + if not line.startswith("PROXY"): + return False + + self.proxy_protocol_access_check() + self.parse_proxy_protocol(line) + + return True + + def proxy_protocol_access_check(self): + # check in allow list + if isinstance(self.unreader, SocketUnreader): + try: + remote_host = self.unreader.sock.getpeername()[0] + except socket.error as e: + if e.args[0] == ENOTCONN: + raise ForbiddenProxyRequest("UNKNOW") + raise + if ("*" not in self.cfg.proxy_allow_ips and + remote_host not in self.cfg.proxy_allow_ips): + raise ForbiddenProxyRequest(remote_host) + + def parse_proxy_protocol(self, line): + bits = line.split() + + if len(bits) != 6: + raise InvalidProxyLine(line) + + # Extract data + proto = bits[1] + s_addr = bits[2] + d_addr = bits[3] + + # Validation + if proto not in ["TCP4", "TCP6"]: + raise InvalidProxyLine("protocol '%s' not supported" % proto) + if proto == "TCP4": + try: + socket.inet_pton(socket.AF_INET, s_addr) + socket.inet_pton(socket.AF_INET, d_addr) + except socket.error: + raise InvalidProxyLine(line) + elif proto == "TCP6": + try: + socket.inet_pton(socket.AF_INET6, s_addr) + socket.inet_pton(socket.AF_INET6, d_addr) + except socket.error: + raise InvalidProxyLine(line) + + try: + s_port = int(bits[4]) + d_port = int(bits[5]) + except ValueError: + raise InvalidProxyLine("invalid port %s" % line) + + if not ((0 <= s_port <= 65535) and (0 <= d_port <= 65535)): + raise InvalidProxyLine("invalid port %s" % line) + + # Set data + self.proxy_protocol_info = { + "proxy_protocol": proto, + "client_addr": s_addr, + "client_port": s_port, + "proxy_addr": d_addr, + "proxy_port": d_port + } + + def parse_request_line(self, line_bytes): + bits = [bytes_to_str(bit) for bit in line_bytes.split(None, 2)] + if len(bits) != 3: + raise InvalidRequestLine(bytes_to_str(line_bytes)) + + # Method + if not METH_RE.match(bits[0]): + raise InvalidRequestMethod(bits[0]) + self.method = bits[0].upper() + + # URI + self.uri = bits[1] + + try: + parts = split_request_uri(self.uri) + except ValueError: + raise InvalidRequestLine(bytes_to_str(line_bytes)) + self.path = parts.path or "" + self.query = parts.query or "" + self.fragment = parts.fragment or "" + + # Version + match = VERSION_RE.match(bits[2]) + if match is None: + raise InvalidHTTPVersion(bits[2]) + self.version = (int(match.group(1)), int(match.group(2))) + + def set_body_reader(self): + super(Request, self).set_body_reader() + if isinstance(self.body.reader, EOFReader): + self.body = Body(LengthReader(self.unreader, 0)) diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/parser.py b/venv/lib/python3.6/site-packages/gunicorn/http/parser.py new file mode 100644 index 0000000..a4a0f1e --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/http/parser.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +from gunicorn.http.message import Request +from gunicorn.http.unreader import SocketUnreader, IterUnreader + + +class Parser(object): + + mesg_class = None + + def __init__(self, cfg, source): + self.cfg = cfg + if hasattr(source, "recv"): + self.unreader = SocketUnreader(source) + else: + self.unreader = IterUnreader(source) + self.mesg = None + + # request counter (for keepalive connetions) + self.req_count = 0 + + def __iter__(self): + return self + + def __next__(self): + # Stop if HTTP dictates a stop. + if self.mesg and self.mesg.should_close(): + raise StopIteration() + + # Discard any unread body of the previous message + if self.mesg: + data = self.mesg.body.read(8192) + while data: + data = self.mesg.body.read(8192) + + # Parse the next request + self.req_count += 1 + self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count) + if not self.mesg: + raise StopIteration() + return self.mesg + + next = __next__ + + +class RequestParser(Parser): + + mesg_class = Request diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/unreader.py b/venv/lib/python3.6/site-packages/gunicorn/http/unreader.py new file mode 100644 index 0000000..9f312a8 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/http/unreader.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import os + +from gunicorn import six + +# Classes that can undo reading data from +# a given type of data source. + + +class Unreader(object): + def __init__(self): + self.buf = six.BytesIO() + + def chunk(self): + raise NotImplementedError() + + def read(self, size=None): + if size is not None and not isinstance(size, six.integer_types): + raise TypeError("size parameter must be an int or long.") + + if size is not None: + if size == 0: + return b"" + if size < 0: + size = None + + self.buf.seek(0, os.SEEK_END) + + if size is None and self.buf.tell(): + ret = self.buf.getvalue() + self.buf = six.BytesIO() + return ret + if size is None: + d = self.chunk() + return d + + while self.buf.tell() < size: + chunk = self.chunk() + if not chunk: + ret = self.buf.getvalue() + self.buf = six.BytesIO() + return ret + self.buf.write(chunk) + data = self.buf.getvalue() + self.buf = six.BytesIO() + self.buf.write(data[size:]) + return data[:size] + + def unread(self, data): + self.buf.seek(0, os.SEEK_END) + self.buf.write(data) + + +class SocketUnreader(Unreader): + def __init__(self, sock, max_chunk=8192): + super(SocketUnreader, self).__init__() + self.sock = sock + self.mxchunk = max_chunk + + def chunk(self): + return self.sock.recv(self.mxchunk) + + +class IterUnreader(Unreader): + def __init__(self, iterable): + super(IterUnreader, self).__init__() + self.iter = iter(iterable) + + def chunk(self): + if not self.iter: + return b"" + try: + return six.next(self.iter) + except StopIteration: + self.iter = None + return b"" diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/wsgi.py b/venv/lib/python3.6/site-packages/gunicorn/http/wsgi.py new file mode 100644 index 0000000..ff75974 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/http/wsgi.py @@ -0,0 +1,411 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import io +import logging +import os +import re +import sys + +from gunicorn._compat import unquote_to_wsgi_str +from gunicorn.http.message import HEADER_RE +from gunicorn.http.errors import InvalidHeader, InvalidHeaderName +from gunicorn.six import string_types, binary_type, reraise +from gunicorn import SERVER_SOFTWARE +import gunicorn.util as util + +try: + # Python 3.3 has os.sendfile(). + from os import sendfile +except ImportError: + try: + from ._sendfile import sendfile + except ImportError: + sendfile = None + +# Send files in at most 1GB blocks as some operating systems can have problems +# with sending files in blocks over 2GB. +BLKSIZE = 0x3FFFFFFF + +HEADER_VALUE_RE = re.compile(r'[\x00-\x1F\x7F]') + +log = logging.getLogger(__name__) + + +class FileWrapper(object): + + def __init__(self, filelike, blksize=8192): + self.filelike = filelike + self.blksize = blksize + if hasattr(filelike, 'close'): + self.close = filelike.close + + def __getitem__(self, key): + data = self.filelike.read(self.blksize) + if data: + return data + raise IndexError + + +class WSGIErrorsWrapper(io.RawIOBase): + + def __init__(self, cfg): + # There is no public __init__ method for RawIOBase so + # we don't need to call super() in the __init__ method. + # pylint: disable=super-init-not-called + errorlog = logging.getLogger("gunicorn.error") + handlers = errorlog.handlers + self.streams = [] + + if cfg.errorlog == "-": + self.streams.append(sys.stderr) + handlers = handlers[1:] + + for h in handlers: + if hasattr(h, "stream"): + self.streams.append(h.stream) + + def write(self, data): + for stream in self.streams: + try: + stream.write(data) + except UnicodeError: + stream.write(data.encode("UTF-8")) + stream.flush() + + +def base_environ(cfg): + return { + "wsgi.errors": WSGIErrorsWrapper(cfg), + "wsgi.version": (1, 0), + "wsgi.multithread": False, + "wsgi.multiprocess": (cfg.workers > 1), + "wsgi.run_once": False, + "wsgi.file_wrapper": FileWrapper, + "SERVER_SOFTWARE": SERVER_SOFTWARE, + } + + +def default_environ(req, sock, cfg): + env = base_environ(cfg) + env.update({ + "wsgi.input": req.body, + "gunicorn.socket": sock, + "REQUEST_METHOD": req.method, + "QUERY_STRING": req.query, + "RAW_URI": req.uri, + "SERVER_PROTOCOL": "HTTP/%s" % ".".join([str(v) for v in req.version]) + }) + return env + + +def proxy_environ(req): + info = req.proxy_protocol_info + + if not info: + return {} + + return { + "PROXY_PROTOCOL": info["proxy_protocol"], + "REMOTE_ADDR": info["client_addr"], + "REMOTE_PORT": str(info["client_port"]), + "PROXY_ADDR": info["proxy_addr"], + "PROXY_PORT": str(info["proxy_port"]), + } + + +def create(req, sock, client, server, cfg): + resp = Response(req, sock, cfg) + + # set initial environ + environ = default_environ(req, sock, cfg) + + # default variables + host = None + script_name = os.environ.get("SCRIPT_NAME", "") + + # add the headers to the environ + for hdr_name, hdr_value in req.headers: + if hdr_name == "EXPECT": + # handle expect + if hdr_value.lower() == "100-continue": + sock.send(b"HTTP/1.1 100 Continue\r\n\r\n") + elif hdr_name == 'HOST': + host = hdr_value + elif hdr_name == "SCRIPT_NAME": + script_name = hdr_value + elif hdr_name == "CONTENT-TYPE": + environ['CONTENT_TYPE'] = hdr_value + continue + elif hdr_name == "CONTENT-LENGTH": + environ['CONTENT_LENGTH'] = hdr_value + continue + + key = 'HTTP_' + hdr_name.replace('-', '_') + if key in environ: + hdr_value = "%s,%s" % (environ[key], hdr_value) + environ[key] = hdr_value + + # set the url scheme + environ['wsgi.url_scheme'] = req.scheme + + # set the REMOTE_* keys in environ + # authors should be aware that REMOTE_HOST and REMOTE_ADDR + # may not qualify the remote addr: + # http://www.ietf.org/rfc/rfc3875 + if isinstance(client, string_types): + environ['REMOTE_ADDR'] = client + elif isinstance(client, binary_type): + environ['REMOTE_ADDR'] = client.decode() + else: + environ['REMOTE_ADDR'] = client[0] + environ['REMOTE_PORT'] = str(client[1]) + + # handle the SERVER_* + # Normally only the application should use the Host header but since the + # WSGI spec doesn't support unix sockets, we are using it to create + # viable SERVER_* if possible. + if isinstance(server, string_types): + server = server.split(":") + if len(server) == 1: + # unix socket + if host: + server = host.split(':') + if len(server) == 1: + if req.scheme == "http": + server.append(80) + elif req.scheme == "https": + server.append(443) + else: + server.append('') + else: + # no host header given which means that we are not behind a + # proxy, so append an empty port. + server.append('') + environ['SERVER_NAME'] = server[0] + environ['SERVER_PORT'] = str(server[1]) + + # set the path and script name + path_info = req.path + if script_name: + path_info = path_info.split(script_name, 1)[1] + environ['PATH_INFO'] = unquote_to_wsgi_str(path_info) + environ['SCRIPT_NAME'] = script_name + + # override the environ with the correct remote and server address if + # we are behind a proxy using the proxy protocol. + environ.update(proxy_environ(req)) + return resp, environ + + +class Response(object): + + def __init__(self, req, sock, cfg): + self.req = req + self.sock = sock + self.version = SERVER_SOFTWARE + self.status = None + self.chunked = False + self.must_close = False + self.headers = [] + self.headers_sent = False + self.response_length = None + self.sent = 0 + self.upgrade = False + self.cfg = cfg + + def force_close(self): + self.must_close = True + + def should_close(self): + if self.must_close or self.req.should_close(): + return True + if self.response_length is not None or self.chunked: + return False + if self.req.method == 'HEAD': + return False + if self.status_code < 200 or self.status_code in (204, 304): + return False + return True + + def start_response(self, status, headers, exc_info=None): + if exc_info: + try: + if self.status and self.headers_sent: + reraise(exc_info[0], exc_info[1], exc_info[2]) + finally: + exc_info = None + elif self.status is not None: + raise AssertionError("Response headers already set!") + + self.status = status + + # get the status code from the response here so we can use it to check + # the need for the connection header later without parsing the string + # each time. + try: + self.status_code = int(self.status.split()[0]) + except ValueError: + self.status_code = None + + self.process_headers(headers) + self.chunked = self.is_chunked() + return self.write + + def process_headers(self, headers): + for name, value in headers: + if not isinstance(name, string_types): + raise TypeError('%r is not a string' % name) + + if HEADER_RE.search(name): + raise InvalidHeaderName('%r' % name) + + if HEADER_VALUE_RE.search(value): + raise InvalidHeader('%r' % value) + + value = str(value).strip() + lname = name.lower().strip() + if lname == "content-length": + self.response_length = int(value) + elif util.is_hoppish(name): + if lname == "connection": + # handle websocket + if value.lower().strip() == "upgrade": + self.upgrade = True + elif lname == "upgrade": + if value.lower().strip() == "websocket": + self.headers.append((name.strip(), value)) + + # ignore hopbyhop headers + continue + self.headers.append((name.strip(), value)) + + def is_chunked(self): + # Only use chunked responses when the client is + # speaking HTTP/1.1 or newer and there was + # no Content-Length header set. + if self.response_length is not None: + return False + elif self.req.version <= (1, 0): + return False + elif self.req.method == 'HEAD': + # Responses to a HEAD request MUST NOT contain a response body. + return False + elif self.status_code in (204, 304): + # Do not use chunked responses when the response is guaranteed to + # not have a response body. + return False + return True + + def default_headers(self): + # set the connection header + if self.upgrade: + connection = "upgrade" + elif self.should_close(): + connection = "close" + else: + connection = "keep-alive" + + headers = [ + "HTTP/%s.%s %s\r\n" % (self.req.version[0], + self.req.version[1], self.status), + "Server: %s\r\n" % self.version, + "Date: %s\r\n" % util.http_date(), + "Connection: %s\r\n" % connection + ] + if self.chunked: + headers.append("Transfer-Encoding: chunked\r\n") + return headers + + def send_headers(self): + if self.headers_sent: + return + tosend = self.default_headers() + tosend.extend(["%s: %s\r\n" % (k, v) for k, v in self.headers]) + + header_str = "%s\r\n" % "".join(tosend) + util.write(self.sock, util.to_bytestring(header_str, "ascii")) + self.headers_sent = True + + def write(self, arg): + self.send_headers() + if not isinstance(arg, binary_type): + raise TypeError('%r is not a byte' % arg) + arglen = len(arg) + tosend = arglen + if self.response_length is not None: + if self.sent >= self.response_length: + # Never write more than self.response_length bytes + return + + tosend = min(self.response_length - self.sent, tosend) + if tosend < arglen: + arg = arg[:tosend] + + # Sending an empty chunk signals the end of the + # response and prematurely closes the response + if self.chunked and tosend == 0: + return + + self.sent += tosend + util.write(self.sock, arg, self.chunked) + + def can_sendfile(self): + return self.cfg.sendfile is not False and sendfile is not None + + def sendfile(self, respiter): + if self.cfg.is_ssl or not self.can_sendfile(): + return False + + if not util.has_fileno(respiter.filelike): + return False + + fileno = respiter.filelike.fileno() + try: + offset = os.lseek(fileno, 0, os.SEEK_CUR) + if self.response_length is None: + filesize = os.fstat(fileno).st_size + + # The file may be special and sendfile will fail. + # It may also be zero-length, but that is okay. + if filesize == 0: + return False + + nbytes = filesize - offset + else: + nbytes = self.response_length + except (OSError, io.UnsupportedOperation): + return False + + self.send_headers() + + if self.is_chunked(): + chunk_size = "%X\r\n" % nbytes + self.sock.sendall(chunk_size.encode('utf-8')) + + sockno = self.sock.fileno() + sent = 0 + + while sent != nbytes: + count = min(nbytes - sent, BLKSIZE) + sent += sendfile(sockno, fileno, offset + sent, count) + + if self.is_chunked(): + self.sock.sendall(b"\r\n") + + os.lseek(fileno, offset, os.SEEK_SET) + + return True + + def write_file(self, respiter): + if not self.sendfile(respiter): + for item in respiter: + self.write(item) + + def close(self): + if not self.headers_sent: + self.send_headers() + if self.chunked: + util.write_chunk(self.sock, b"") diff --git a/venv/lib/python3.6/site-packages/gunicorn/instrument/__init__.py b/venv/lib/python3.6/site-packages/gunicorn/instrument/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.6/site-packages/gunicorn/instrument/__pycache__/__init__.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/instrument/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59a52f98b92a0b249a403a18b60d850043d4d2af GIT binary patch literal 147 zcmXr!<>lHlpFf5H2p)q77+?f49Dul(1xTbY1T$zd`mJOr0tq9CUk>^uxdr+KnFYE@ zrI|S?x;YjmMcG-#M*8Wcd6~)iMR^EDW?pegQE6^!UWtBud}dx|NqoFsLFFwDo80`A O(wtN~kjceB%m4tNg(QXm literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/instrument/__pycache__/statsd.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/instrument/__pycache__/statsd.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c76bd1a6a6bd1835ddafe18bfaa31a07b438c98 GIT binary patch literal 4469 zcmbtXPm|Ng71w{u9((?(}r1`eLnC z`NvHX+5>2%35n_sP&%SH(G{D&y1egw_28Lo4r!s zZrNyCtuiaMD$H&zuyU))Dy zE*0U6%<2aT5B;92=;D3Z9ty}nPwO)edct0>|% zM4-S13eCTzGmYu5m6pK_W}-Hk#Y(6xX0tNt605KU)OJ2w*kX|_;l9F_*$V0fc8Gn1 zy2@7BVbnF|u%qnQYqhn=K4Zt(8d^*21p64zmf0ulQ`9T$By4pmv+k2Q*yvJjmW?N# z2$&N_BH_cS+18!psh2ndPY9UQ!{6yivs4^hzJ}@0MQgo)vV|fB5Q)-NQzcc$nyMtU z(psu^^|78RuJQ6~w9Uubp0=kaR@Xu)q1a;$Ps^#&Eq5!amMohzq|Eo_xjCyn|CsU9+G!NQQ#+;9!5!4-hSZ+1MJ>Y_SYx-T7|F`4t+YLYqb(yON-Cac-jQti)Ls{aXV zf_0MXN-NA1tbpDe9iHH7z)2cD50lXMdT&$=QP&F#PqbM%jC?LPXx-q4(IwOS0%q`d zM`YISAE=gQ$OX-Ve+=;@iXewm)YaJ}Cc}RCBo56@BH%pc`~nSq(BJ|R^OoPUZo zKS{zNL2I`U^k=v|i!uXUEi5lb`f$XnbBJNMD2zIDm`fAoyQ4k@#OLNvFBYhau|6F4 zkvZJKWLwN3FVEEPj{S4szBq?{d1CR&N8XN}oD7bwBx)A{Yrnfo7gw8gJ=5XaS&5<` ztehFQnm6vglOJS87`5YX2$l?+>S&<=Mf&%~!)b%^FCa4W`u4R4H{T76$$}@t&gFe* zCQd0I__Y7&Dx*+jm$BeqXuSMeYIG%+kF`BBHJP?s=0Bz;8I5VsS+cFkc1gCua?snU zg?=Rzn@VErYWy*bZKhUQq9_urLu*0Hp)F0dN1(N#{Sn&oRI8*K|7%j|E)dJ4T3b(b zL?(kG6WHQ(!506_*#iA)DR_cd_K&)%6udz>gEhK-JMK81(+Qskk>m|9c<4J}+sPS! z-I0v%coB1m^BtrZ$RGlN#9_)TnHjuDxYryVz2SvD@MsbfQBoyjl-)rdVZ)cfFYUO# znCTsGM&-`-&b?b(?!(P{S^44S?FZX=^Ultr@AGEm=H`Q&+b-R68`jUNTX!Ecced~4 zD%uj+;;(Mq-?>YClPfDw%3zzj#wy*?ZmySQm~H93=e>g=>kM@=Nmvk>j)@)&W8`Pw zp$HmF(KWTI+9>(Y2B8$iG}JYsmrCK?FoRDc(^W#D4{V``5yXU9Qm{&Dj7{*0LAkz*~4Tfj@Q2w4!5;S;v*J3eQuz6$YOpZ$f(kS=6iz z-#Mb>_sqb`FN~UZ0#6LFnml+m3`F9vA(zSGbXkiBZW8tbC#39$c}YNb4H7KqCEm1i z_!BnG8?yczG?b={W1PrubbK1=iV+)=PmNX%J`$s~gQ^Dk&`>nKVDcOFYR;vW=lg*W zSxE%QFqp{9b}#mltlAHK9#523{&H3c1tkeyq8BF-nW>aeQdCa0F%rWf>Cl>^>Z`BzUY*~&^yZvnRW&(T& zj5C}_VBx?sq$~T7`svkI7x#c^6EE^>BqYcpqoBFxTd^iQ9ArQ>E_^dM2$_~_7i9u@ z!gJHy>iy9kD$s6_Vc!)l-zMPm2~bvML6PZ2Z-F{}_`VKM+h^9k_&~YpJpfPo z{*KtGf%AS4~5BMws$h{#h7eugCI4Voz_SvbOcUyxF% zYlD<<0A$8KQleDaY-Xk>{4gAy+@|~!=a`7W1;9u`c%5L5RygIBi0$8^;^dQAIRP2D z@_cFqksq^w<8ZFfVIyK(@&WC!oD?*2ejWWL)gd}f(Oz?1>`AvDvtf_gRo8ts^m@gUlIybA$AOE^Dl)(4lrKwc{Bvqi zzR&95r4SKUdf--3S6^ZjCE|4HR`+RT; cG_x<~M!G^#MI3`LG@D3P1Ig-!I{j(?10p@|TmS$7 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/instrument/statsd.py b/venv/lib/python3.6/site-packages/gunicorn/instrument/statsd.py new file mode 100644 index 0000000..4bbcb20 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/instrument/statsd.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +"Bare-bones implementation of statsD's protocol, client-side" + +import socket +import logging +from re import sub + +from gunicorn.glogging import Logger +from gunicorn import six + +# Instrumentation constants +METRIC_VAR = "metric" +VALUE_VAR = "value" +MTYPE_VAR = "mtype" +GAUGE_TYPE = "gauge" +COUNTER_TYPE = "counter" +HISTOGRAM_TYPE = "histogram" + +class Statsd(Logger): + """statsD-based instrumentation, that passes as a logger + """ + def __init__(self, cfg): + """host, port: statsD server + """ + Logger.__init__(self, cfg) + self.prefix = sub(r"^(.+[^.]+)\.*$", "\\g<1>.", cfg.statsd_prefix) + try: + host, port = cfg.statsd_host + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.connect((host, int(port))) + except Exception: + self.sock = None + + # Log errors and warnings + def critical(self, msg, *args, **kwargs): + Logger.critical(self, msg, *args, **kwargs) + self.increment("gunicorn.log.critical", 1) + + def error(self, msg, *args, **kwargs): + Logger.error(self, msg, *args, **kwargs) + self.increment("gunicorn.log.error", 1) + + def warning(self, msg, *args, **kwargs): + Logger.warning(self, msg, *args, **kwargs) + self.increment("gunicorn.log.warning", 1) + + def exception(self, msg, *args, **kwargs): + Logger.exception(self, msg, *args, **kwargs) + self.increment("gunicorn.log.exception", 1) + + # Special treatement for info, the most common log level + def info(self, msg, *args, **kwargs): + self.log(logging.INFO, msg, *args, **kwargs) + + # skip the run-of-the-mill logs + def debug(self, msg, *args, **kwargs): + self.log(logging.DEBUG, msg, *args, **kwargs) + + def log(self, lvl, msg, *args, **kwargs): + """Log a given statistic if metric, value and type are present + """ + try: + extra = kwargs.get("extra", None) + if extra is not None: + metric = extra.get(METRIC_VAR, None) + value = extra.get(VALUE_VAR, None) + typ = extra.get(MTYPE_VAR, None) + if metric and value and typ: + if typ == GAUGE_TYPE: + self.gauge(metric, value) + elif typ == COUNTER_TYPE: + self.increment(metric, value) + elif typ == HISTOGRAM_TYPE: + self.histogram(metric, value) + else: + pass + + # Log to parent logger only if there is something to say + if msg: + Logger.log(self, lvl, msg, *args, **kwargs) + except Exception: + Logger.warning(self, "Failed to log to statsd", exc_info=True) + + # access logging + def access(self, resp, req, environ, request_time): + """Measure request duration + request_time is a datetime.timedelta + """ + Logger.access(self, resp, req, environ, request_time) + duration_in_ms = request_time.seconds * 1000 + float(request_time.microseconds) / 10 ** 3 + status = resp.status + if isinstance(status, str): + status = int(status.split(None, 1)[0]) + self.histogram("gunicorn.request.duration", duration_in_ms) + self.increment("gunicorn.requests", 1) + self.increment("gunicorn.request.status.%d" % status, 1) + + # statsD methods + # you can use those directly if you want + def gauge(self, name, value): + self._sock_send("{0}{1}:{2}|g".format(self.prefix, name, value)) + + def increment(self, name, value, sampling_rate=1.0): + self._sock_send("{0}{1}:{2}|c|@{3}".format(self.prefix, name, value, sampling_rate)) + + def decrement(self, name, value, sampling_rate=1.0): + self._sock_send("{0}{1}:-{2}|c|@{3}".format(self.prefix, name, value, sampling_rate)) + + def histogram(self, name, value): + self._sock_send("{0}{1}:{2}|ms".format(self.prefix, name, value)) + + def _sock_send(self, msg): + try: + if isinstance(msg, six.text_type): + msg = msg.encode("ascii") + if self.sock: + self.sock.send(msg) + except Exception: + Logger.warning(self, "Error sending message to statsd", exc_info=True) diff --git a/venv/lib/python3.6/site-packages/gunicorn/pidfile.py b/venv/lib/python3.6/site-packages/gunicorn/pidfile.py new file mode 100644 index 0000000..a6e085f --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/pidfile.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import errno +import os +import tempfile + + +class Pidfile(object): + """\ + Manage a PID file. If a specific name is provided + it and '"%s.oldpid" % name' will be used. Otherwise + we create a temp file using os.mkstemp. + """ + + def __init__(self, fname): + self.fname = fname + self.pid = None + + def create(self, pid): + oldpid = self.validate() + if oldpid: + if oldpid == os.getpid(): + return + msg = "Already running on PID %s (or pid file '%s' is stale)" + raise RuntimeError(msg % (oldpid, self.fname)) + + self.pid = pid + + # Write pidfile + fdir = os.path.dirname(self.fname) + if fdir and not os.path.isdir(fdir): + raise RuntimeError("%s doesn't exist. Can't create pidfile." % fdir) + fd, fname = tempfile.mkstemp(dir=fdir) + os.write(fd, ("%s\n" % self.pid).encode('utf-8')) + if self.fname: + os.rename(fname, self.fname) + else: + self.fname = fname + os.close(fd) + + # set permissions to -rw-r--r-- + os.chmod(self.fname, 420) + + def rename(self, path): + self.unlink() + self.fname = path + self.create(self.pid) + + def unlink(self): + """ delete pidfile""" + try: + with open(self.fname, "r") as f: + pid1 = int(f.read() or 0) + + if pid1 == self.pid: + os.unlink(self.fname) + except: + pass + + def validate(self): + """ Validate pidfile and make it stale if needed""" + if not self.fname: + return + try: + with open(self.fname, "r") as f: + try: + wpid = int(f.read()) + except ValueError: + return + + try: + os.kill(wpid, 0) + return wpid + except OSError as e: + if e.args[0] == errno.EPERM: + return wpid + if e.args[0] == errno.ESRCH: + return + raise + except IOError as e: + if e.args[0] == errno.ENOENT: + return + raise diff --git a/venv/lib/python3.6/site-packages/gunicorn/reloader.py b/venv/lib/python3.6/site-packages/gunicorn/reloader.py new file mode 100644 index 0000000..c879885 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/reloader.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import os +import os.path +import re +import sys +import time +import threading + +COMPILED_EXT_RE = re.compile(r'py[co]$') + + +class Reloader(threading.Thread): + def __init__(self, extra_files=None, interval=1, callback=None): + super(Reloader, self).__init__() + self.setDaemon(True) + self._extra_files = set(extra_files or ()) + self._extra_files_lock = threading.RLock() + self._interval = interval + self._callback = callback + + def add_extra_file(self, filename): + with self._extra_files_lock: + self._extra_files.add(filename) + + def get_files(self): + fnames = [ + COMPILED_EXT_RE.sub('py', module.__file__) + for module in tuple(sys.modules.values()) + if getattr(module, '__file__', None) + ] + + with self._extra_files_lock: + fnames.extend(self._extra_files) + + return fnames + + def run(self): + mtimes = {} + while True: + for filename in self.get_files(): + try: + mtime = os.stat(filename).st_mtime + except OSError: + continue + old_time = mtimes.get(filename) + if old_time is None: + mtimes[filename] = mtime + continue + elif mtime > old_time: + if self._callback: + self._callback(filename) + time.sleep(self._interval) + +has_inotify = False +if sys.platform.startswith('linux'): + try: + from inotify.adapters import Inotify + import inotify.constants + has_inotify = True + except ImportError: + pass + + +if has_inotify: + + class InotifyReloader(threading.Thread): + event_mask = (inotify.constants.IN_CREATE | inotify.constants.IN_DELETE + | inotify.constants.IN_DELETE_SELF | inotify.constants.IN_MODIFY + | inotify.constants.IN_MOVE_SELF | inotify.constants.IN_MOVED_FROM + | inotify.constants.IN_MOVED_TO) + + def __init__(self, extra_files=None, callback=None): + super(InotifyReloader, self).__init__() + self.setDaemon(True) + self._callback = callback + self._dirs = set() + self._watcher = Inotify() + + for extra_file in extra_files: + self.add_extra_file(extra_file) + + def add_extra_file(self, filename): + dirname = os.path.dirname(filename) + + if dirname in self._dirs: + return + + self._watcher.add_watch(dirname, mask=self.event_mask) + self._dirs.add(dirname) + + def get_dirs(self): + fnames = [ + os.path.dirname(COMPILED_EXT_RE.sub('py', module.__file__)) + for module in tuple(sys.modules.values()) + if hasattr(module, '__file__') + ] + + return set(fnames) + + def run(self): + self._dirs = self.get_dirs() + + for dirname in self._dirs: + self._watcher.add_watch(dirname, mask=self.event_mask) + + for event in self._watcher.event_gen(): + if event is None: + continue + + filename = event[3] + + self._callback(filename) + +else: + + class InotifyReloader(object): + def __init__(self, callback=None): + raise ImportError('You must have the inotify module installed to ' + 'use the inotify reloader') + + +preferred_reloader = InotifyReloader if has_inotify else Reloader + +reloader_engines = { + 'auto': preferred_reloader, + 'poll': Reloader, + 'inotify': InotifyReloader, +} diff --git a/venv/lib/python3.6/site-packages/gunicorn/selectors.py b/venv/lib/python3.6/site-packages/gunicorn/selectors.py new file mode 100644 index 0000000..cdae569 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/selectors.py @@ -0,0 +1,592 @@ +"""Selectors module. + +This module allows high-level and efficient I/O multiplexing, built upon the +`select` module primitives. + +The following code adapted from trollius.selectors. +""" + + +from abc import ABCMeta, abstractmethod +from collections import namedtuple, Mapping +import math +import select +import sys + +from gunicorn._compat import wrap_error, InterruptedError +from gunicorn import six + + +# generic events, that must be mapped to implementation-specific ones +EVENT_READ = (1 << 0) +EVENT_WRITE = (1 << 1) + + +def _fileobj_to_fd(fileobj): + """Return a file descriptor from a file object. + + Parameters: + fileobj -- file object or file descriptor + + Returns: + corresponding file descriptor + + Raises: + ValueError if the object is invalid + """ + if isinstance(fileobj, six.integer_types): + fd = fileobj + else: + try: + fd = int(fileobj.fileno()) + except (AttributeError, TypeError, ValueError): + raise ValueError("Invalid file object: " + "{0!r}".format(fileobj)) + if fd < 0: + raise ValueError("Invalid file descriptor: {0}".format(fd)) + return fd + + +SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data']) +"""Object used to associate a file object to its backing file descriptor, +selected event mask and attached data.""" + + +class _SelectorMapping(Mapping): + """Mapping of file objects to selector keys.""" + + def __init__(self, selector): + self._selector = selector + + def __len__(self): + return len(self._selector._fd_to_key) + + def __getitem__(self, fileobj): + try: + fd = self._selector._fileobj_lookup(fileobj) + return self._selector._fd_to_key[fd] + except KeyError: + raise KeyError("{0!r} is not registered".format(fileobj)) + + def __iter__(self): + return iter(self._selector._fd_to_key) + + +class BaseSelector(six.with_metaclass(ABCMeta)): + """Selector abstract base class. + + A selector supports registering file objects to be monitored for specific + I/O events. + + A file object is a file descriptor or any object with a `fileno()` method. + An arbitrary object can be attached to the file object, which can be used + for example to store context information, a callback, etc. + + A selector can use various implementations (select(), poll(), epoll()...) + depending on the platform. The default `Selector` class uses the most + efficient implementation on the current platform. + """ + + @abstractmethod + def register(self, fileobj, events, data=None): + """Register a file object. + + Parameters: + fileobj -- file object or file descriptor + events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE) + data -- attached data + + Returns: + SelectorKey instance + + Raises: + ValueError if events is invalid + KeyError if fileobj is already registered + OSError if fileobj is closed or otherwise is unacceptable to + the underlying system call (if a system call is made) + + Note: + OSError may or may not be raised + """ + raise NotImplementedError + + @abstractmethod + def unregister(self, fileobj): + """Unregister a file object. + + Parameters: + fileobj -- file object or file descriptor + + Returns: + SelectorKey instance + + Raises: + KeyError if fileobj is not registered + + Note: + If fileobj is registered but has since been closed this does + *not* raise OSError (even if the wrapped syscall does) + """ + raise NotImplementedError + + def modify(self, fileobj, events, data=None): + """Change a registered file object monitored events or attached data. + + Parameters: + fileobj -- file object or file descriptor + events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE) + data -- attached data + + Returns: + SelectorKey instance + + Raises: + Anything that unregister() or register() raises + """ + self.unregister(fileobj) + return self.register(fileobj, events, data) + + @abstractmethod + def select(self, timeout=None): + """Perform the actual selection, until some monitored file objects are + ready or a timeout expires. + + Parameters: + timeout -- if timeout > 0, this specifies the maximum wait time, in + seconds + if timeout <= 0, the select() call won't block, and will + report the currently ready file objects + if timeout is None, select() will block until a monitored + file object becomes ready + + Returns: + list of (key, events) for ready file objects + `events` is a bitwise mask of EVENT_READ|EVENT_WRITE + """ + raise NotImplementedError + + def close(self): + """Close the selector. + + This must be called to make sure that any underlying resource is freed. + """ + pass + + def get_key(self, fileobj): + """Return the key associated to a registered file object. + + Returns: + SelectorKey for this file object + """ + mapping = self.get_map() + try: + return mapping[fileobj] + except KeyError: + raise KeyError("{0!r} is not registered".format(fileobj)) + + @abstractmethod + def get_map(self): + """Return a mapping of file objects to selector keys.""" + raise NotImplementedError + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + +class _BaseSelectorImpl(BaseSelector): + """Base selector implementation.""" + + def __init__(self): + # this maps file descriptors to keys + self._fd_to_key = {} + # read-only mapping returned by get_map() + self._map = _SelectorMapping(self) + + def _fileobj_lookup(self, fileobj): + """Return a file descriptor from a file object. + + This wraps _fileobj_to_fd() to do an exhaustive search in case + the object is invalid but we still have it in our map. This + is used by unregister() so we can unregister an object that + was previously registered even if it is closed. It is also + used by _SelectorMapping. + """ + try: + return _fileobj_to_fd(fileobj) + except ValueError: + # Do an exhaustive search. + for key in self._fd_to_key.values(): + if key.fileobj is fileobj: + return key.fd + # Raise ValueError after all. + raise + + def register(self, fileobj, events, data=None): + if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)): + raise ValueError("Invalid events: {0!r}".format(events)) + + key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data) + + if key.fd in self._fd_to_key: + raise KeyError("{0!r} (FD {1}) is already registered" + .format(fileobj, key.fd)) + + self._fd_to_key[key.fd] = key + return key + + def unregister(self, fileobj): + try: + key = self._fd_to_key.pop(self._fileobj_lookup(fileobj)) + except KeyError: + raise KeyError("{0!r} is not registered".format(fileobj)) + return key + + def modify(self, fileobj, events, data=None): + # TODO: Subclasses can probably optimize this even further. + try: + key = self._fd_to_key[self._fileobj_lookup(fileobj)] + except KeyError: + raise KeyError("{0!r} is not registered".format(fileobj)) + if events != key.events: + self.unregister(fileobj) + key = self.register(fileobj, events, data) + elif data != key.data: + # Use a shortcut to update the data. + key = key._replace(data=data) + self._fd_to_key[key.fd] = key + return key + + def close(self): + self._fd_to_key.clear() + + def get_map(self): + return self._map + + def _key_from_fd(self, fd): + """Return the key associated to a given file descriptor. + + Parameters: + fd -- file descriptor + + Returns: + corresponding key, or None if not found + """ + try: + return self._fd_to_key[fd] + except KeyError: + return None + + +class SelectSelector(_BaseSelectorImpl): + """Select-based selector.""" + + def __init__(self): + super(SelectSelector, self).__init__() + self._readers = set() + self._writers = set() + + def register(self, fileobj, events, data=None): + key = super(SelectSelector, self).register(fileobj, events, data) + if events & EVENT_READ: + self._readers.add(key.fd) + if events & EVENT_WRITE: + self._writers.add(key.fd) + return key + + def unregister(self, fileobj): + key = super(SelectSelector, self).unregister(fileobj) + self._readers.discard(key.fd) + self._writers.discard(key.fd) + return key + + if sys.platform == 'win32': + def _select(self, r, w, _, timeout=None): + r, w, x = select.select(r, w, w, timeout) + return r, w + x, [] + else: + _select = select.select + + def select(self, timeout=None): + timeout = None if timeout is None else max(timeout, 0) + ready = [] + try: + r, w, _ = wrap_error(self._select, + self._readers, self._writers, [], timeout) + except InterruptedError: + return ready + r = set(r) + w = set(w) + for fd in r | w: + events = 0 + if fd in r: + events |= EVENT_READ + if fd in w: + events |= EVENT_WRITE + + key = self._key_from_fd(fd) + if key: + ready.append((key, events & key.events)) + return ready + + +if hasattr(select, 'poll'): + + class PollSelector(_BaseSelectorImpl): + """Poll-based selector.""" + + def __init__(self): + super(PollSelector, self).__init__() + self._poll = select.poll() + + def register(self, fileobj, events, data=None): + key = super(PollSelector, self).register(fileobj, events, data) + poll_events = 0 + if events & EVENT_READ: + poll_events |= select.POLLIN + if events & EVENT_WRITE: + poll_events |= select.POLLOUT + self._poll.register(key.fd, poll_events) + return key + + def unregister(self, fileobj): + key = super(PollSelector, self).unregister(fileobj) + self._poll.unregister(key.fd) + return key + + def select(self, timeout=None): + if timeout is None: + timeout = None + elif timeout <= 0: + timeout = 0 + else: + # poll() has a resolution of 1 millisecond, round away from + # zero to wait *at least* timeout seconds. + timeout = int(math.ceil(timeout * 1e3)) + ready = [] + try: + fd_event_list = wrap_error(self._poll.poll, timeout) + except InterruptedError: + return ready + for fd, event in fd_event_list: + events = 0 + if event & ~select.POLLIN: + events |= EVENT_WRITE + if event & ~select.POLLOUT: + events |= EVENT_READ + + key = self._key_from_fd(fd) + if key: + ready.append((key, events & key.events)) + return ready + + +if hasattr(select, 'epoll'): + + class EpollSelector(_BaseSelectorImpl): + """Epoll-based selector.""" + + def __init__(self): + super(EpollSelector, self).__init__() + self._epoll = select.epoll() + + def fileno(self): + return self._epoll.fileno() + + def register(self, fileobj, events, data=None): + key = super(EpollSelector, self).register(fileobj, events, data) + epoll_events = 0 + if events & EVENT_READ: + epoll_events |= select.EPOLLIN + if events & EVENT_WRITE: + epoll_events |= select.EPOLLOUT + self._epoll.register(key.fd, epoll_events) + return key + + def unregister(self, fileobj): + key = super(EpollSelector, self).unregister(fileobj) + try: + self._epoll.unregister(key.fd) + except OSError: + # This can happen if the FD was closed since it + # was registered. + pass + return key + + def select(self, timeout=None): + if timeout is None: + timeout = -1 + elif timeout <= 0: + timeout = 0 + else: + # epoll_wait() has a resolution of 1 millisecond, round away + # from zero to wait *at least* timeout seconds. + timeout = math.ceil(timeout * 1e3) * 1e-3 + max_ev = len(self._fd_to_key) + ready = [] + try: + fd_event_list = wrap_error(self._epoll.poll, timeout, max_ev) + except InterruptedError: + return ready + for fd, event in fd_event_list: + events = 0 + if event & ~select.EPOLLIN: + events |= EVENT_WRITE + if event & ~select.EPOLLOUT: + events |= EVENT_READ + + key = self._key_from_fd(fd) + if key: + ready.append((key, events & key.events)) + return ready + + def close(self): + self._epoll.close() + super(EpollSelector, self).close() + + +if hasattr(select, 'devpoll'): + + class DevpollSelector(_BaseSelectorImpl): + """Solaris /dev/poll selector.""" + + def __init__(self): + super(DevpollSelector, self).__init__() + self._devpoll = select.devpoll() + + def fileno(self): + return self._devpoll.fileno() + + def register(self, fileobj, events, data=None): + key = super(DevpollSelector, self).register(fileobj, events, data) + poll_events = 0 + if events & EVENT_READ: + poll_events |= select.POLLIN + if events & EVENT_WRITE: + poll_events |= select.POLLOUT + self._devpoll.register(key.fd, poll_events) + return key + + def unregister(self, fileobj): + key = super(DevpollSelector, self).unregister(fileobj) + self._devpoll.unregister(key.fd) + return key + + def select(self, timeout=None): + if timeout is None: + timeout = None + elif timeout <= 0: + timeout = 0 + else: + # devpoll() has a resolution of 1 millisecond, round away from + # zero to wait *at least* timeout seconds. + timeout = math.ceil(timeout * 1e3) + ready = [] + try: + fd_event_list = self._devpoll.poll(timeout) + except InterruptedError: + return ready + for fd, event in fd_event_list: + events = 0 + if event & ~select.POLLIN: + events |= EVENT_WRITE + if event & ~select.POLLOUT: + events |= EVENT_READ + + key = self._key_from_fd(fd) + if key: + ready.append((key, events & key.events)) + return ready + + def close(self): + self._devpoll.close() + super(DevpollSelector, self).close() + + +if hasattr(select, 'kqueue'): + + class KqueueSelector(_BaseSelectorImpl): + """Kqueue-based selector.""" + + def __init__(self): + super(KqueueSelector, self).__init__() + self._kqueue = select.kqueue() + + def fileno(self): + return self._kqueue.fileno() + + def register(self, fileobj, events, data=None): + key = super(KqueueSelector, self).register(fileobj, events, data) + if events & EVENT_READ: + kev = select.kevent(key.fd, select.KQ_FILTER_READ, + select.KQ_EV_ADD) + self._kqueue.control([kev], 0, 0) + if events & EVENT_WRITE: + kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, + select.KQ_EV_ADD) + self._kqueue.control([kev], 0, 0) + return key + + def unregister(self, fileobj): + key = super(KqueueSelector, self).unregister(fileobj) + if key.events & EVENT_READ: + kev = select.kevent(key.fd, select.KQ_FILTER_READ, + select.KQ_EV_DELETE) + try: + self._kqueue.control([kev], 0, 0) + except OSError: + # This can happen if the FD was closed since it + # was registered. + pass + if key.events & EVENT_WRITE: + kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, + select.KQ_EV_DELETE) + try: + self._kqueue.control([kev], 0, 0) + except OSError: + # See comment above. + pass + return key + + def select(self, timeout=None): + timeout = None if timeout is None else max(timeout, 0) + max_ev = len(self._fd_to_key) + ready = [] + try: + kev_list = wrap_error(self._kqueue.control, + None, max_ev, timeout) + except InterruptedError: + return ready + for kev in kev_list: + fd = kev.ident + flag = kev.filter + events = 0 + if flag == select.KQ_FILTER_READ: + events |= EVENT_READ + if flag == select.KQ_FILTER_WRITE: + events |= EVENT_WRITE + + key = self._key_from_fd(fd) + if key: + ready.append((key, events & key.events)) + return ready + + def close(self): + self._kqueue.close() + super(KqueueSelector, self).close() + + +# Choose the best implementation: roughly, epoll|kqueue|devpoll > poll > select. +# select() also can't accept a FD > FD_SETSIZE (usually around 1024) +if 'KqueueSelector' in globals(): + DefaultSelector = KqueueSelector +elif 'EpollSelector' in globals(): + DefaultSelector = EpollSelector +elif 'DevpollSelector' in globals(): + DefaultSelector = DevpollSelector +elif 'PollSelector' in globals(): + DefaultSelector = PollSelector +else: + DefaultSelector = SelectSelector diff --git a/venv/lib/python3.6/site-packages/gunicorn/six.py b/venv/lib/python3.6/site-packages/gunicorn/six.py new file mode 100644 index 0000000..21b0e80 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/six.py @@ -0,0 +1,762 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2014 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import absolute_import + +import functools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.8.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + # This is a bit ugly, but it avoids running this again. + delattr(obj.__class__, self.name) + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), + MovedModule("winreg", "_winreg"), +] +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) +else: + def iterkeys(d, **kw): + return iter(d.iterkeys(**kw)) + + def itervalues(d, **kw): + return iter(d.itervalues(**kw)) + + def iteritems(d, **kw): + return iter(d.iteritems(**kw)) + + def iterlists(d, **kw): + return iter(d.iterlists(**kw)) + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + def u(s): + return s + unichr = chr + if sys.version_info[1] <= 1: + def int2byte(i): + return bytes((i,)) + else: + # This is about 2x faster than the implementation above on 3.2+ + int2byte = operator.methodcaller("to_bytes", 1, "big") + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO +else: + def b(s): + return s + # Workaround for standalone backslash + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + def byte2int(bs): + return ord(bs[0]) + def indexbytes(buf, i): + return ord(buf[i]) + def iterbytes(buf): + return (ord(byte) for byte in buf) + import StringIO + StringIO = BytesIO = StringIO.StringIO +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/lib/python3.6/site-packages/gunicorn/sock.py b/venv/lib/python3.6/site-packages/gunicorn/sock.py new file mode 100644 index 0000000..8870936 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/sock.py @@ -0,0 +1,209 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import errno +import os +import socket +import stat +import sys +import time + +from gunicorn import util +from gunicorn.six import string_types + + +class BaseSocket(object): + + def __init__(self, address, conf, log, fd=None): + self.log = log + self.conf = conf + + self.cfg_addr = address + if fd is None: + sock = socket.socket(self.FAMILY, socket.SOCK_STREAM) + bound = False + else: + sock = socket.fromfd(fd, self.FAMILY, socket.SOCK_STREAM) + os.close(fd) + bound = True + + self.sock = self.set_options(sock, bound=bound) + + def __str__(self): + return "" % self.sock.fileno() + + def __getattr__(self, name): + return getattr(self.sock, name) + + def set_options(self, sock, bound=False): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + if (self.conf.reuse_port + and hasattr(socket, 'SO_REUSEPORT')): # pragma: no cover + try: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + except socket.error as err: + if err.errno not in (errno.ENOPROTOOPT, errno.EINVAL): + raise + if not bound: + self.bind(sock) + sock.setblocking(0) + + # make sure that the socket can be inherited + if hasattr(sock, "set_inheritable"): + sock.set_inheritable(True) + + sock.listen(self.conf.backlog) + return sock + + def bind(self, sock): + sock.bind(self.cfg_addr) + + def close(self): + if self.sock is None: + return + + try: + self.sock.close() + except socket.error as e: + self.log.info("Error while closing socket %s", str(e)) + + self.sock = None + + +class TCPSocket(BaseSocket): + + FAMILY = socket.AF_INET + + def __str__(self): + if self.conf.is_ssl: + scheme = "https" + else: + scheme = "http" + + addr = self.sock.getsockname() + return "%s://%s:%d" % (scheme, addr[0], addr[1]) + + def set_options(self, sock, bound=False): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + return super(TCPSocket, self).set_options(sock, bound=bound) + + +class TCP6Socket(TCPSocket): + + FAMILY = socket.AF_INET6 + + def __str__(self): + (host, port, _, _) = self.sock.getsockname() + return "http://[%s]:%d" % (host, port) + + +class UnixSocket(BaseSocket): + + FAMILY = socket.AF_UNIX + + def __init__(self, addr, conf, log, fd=None): + if fd is None: + try: + st = os.stat(addr) + except OSError as e: + if e.args[0] != errno.ENOENT: + raise + else: + if stat.S_ISSOCK(st.st_mode): + os.remove(addr) + else: + raise ValueError("%r is not a socket" % addr) + super(UnixSocket, self).__init__(addr, conf, log, fd=fd) + + def __str__(self): + return "unix:%s" % self.cfg_addr + + def bind(self, sock): + old_umask = os.umask(self.conf.umask) + sock.bind(self.cfg_addr) + util.chown(self.cfg_addr, self.conf.uid, self.conf.gid) + os.umask(old_umask) + + +def _sock_type(addr): + if isinstance(addr, tuple): + if util.is_ipv6(addr[0]): + sock_type = TCP6Socket + else: + sock_type = TCPSocket + elif isinstance(addr, string_types): + sock_type = UnixSocket + else: + raise TypeError("Unable to create socket from: %r" % addr) + return sock_type + + +def create_sockets(conf, log, fds=None): + """ + Create a new socket for the configured addresses or file descriptors. + + If a configured address is a tuple then a TCP socket is created. + If it is a string, a Unix socket is created. Otherwise, a TypeError is + raised. + """ + listeners = [] + + # get it only once + laddr = conf.address + + # check ssl config early to raise the error on startup + # only the certfile is needed since it can contains the keyfile + if conf.certfile and not os.path.exists(conf.certfile): + raise ValueError('certfile "%s" does not exist' % conf.certfile) + + if conf.keyfile and not os.path.exists(conf.keyfile): + raise ValueError('keyfile "%s" does not exist' % conf.keyfile) + + # sockets are already bound + if fds is not None: + for fd in fds: + sock = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM) + sock_name = sock.getsockname() + sock_type = _sock_type(sock_name) + listener = sock_type(sock_name, conf, log, fd=fd) + listeners.append(listener) + + return listeners + + # no sockets is bound, first initialization of gunicorn in this env. + for addr in laddr: + sock_type = _sock_type(addr) + sock = None + for i in range(5): + try: + sock = sock_type(addr, conf, log) + except socket.error as e: + if e.args[0] == errno.EADDRINUSE: + log.error("Connection in use: %s", str(addr)) + if e.args[0] == errno.EADDRNOTAVAIL: + log.error("Invalid address: %s", str(addr)) + if i < 5: + msg = "connection to {addr} failed: {error}" + log.debug(msg.format(addr=str(addr), error=str(e))) + log.error("Retrying in 1 second.") + time.sleep(1) + else: + break + + if sock is None: + log.error("Can't connect to %s", str(addr)) + sys.exit(1) + + listeners.append(sock) + + return listeners + + +def close_sockets(listeners, unlink=True): + for sock in listeners: + sock_name = sock.getsockname() + sock.close() + if unlink and _sock_type(sock_name) is UnixSocket: + os.unlink(sock_name) diff --git a/venv/lib/python3.6/site-packages/gunicorn/systemd.py b/venv/lib/python3.6/site-packages/gunicorn/systemd.py new file mode 100644 index 0000000..10ffb8d --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/systemd.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import os + +SD_LISTEN_FDS_START = 3 + + +def listen_fds(unset_environment=True): + """ + Get the number of sockets inherited from systemd socket activation. + + :param unset_environment: clear systemd environment variables unless False + :type unset_environment: bool + :return: the number of sockets to inherit from systemd socket activation + :rtype: int + + Returns zero immediately if $LISTEN_PID is not set to the current pid. + Otherwise, returns the number of systemd activation sockets specified by + $LISTEN_FDS. + + When $LISTEN_PID matches the current pid, unsets the environment variables + unless the ``unset_environment`` flag is ``False``. + + .. note:: + Unlike the sd_listen_fds C function, this implementation does not set + the FD_CLOEXEC flag because the gunicorn arbiter never needs to do this. + + .. seealso:: + ``_ + + """ + fds = int(os.environ.get('LISTEN_FDS', 0)) + listen_pid = int(os.environ.get('LISTEN_PID', 0)) + + if listen_pid != os.getpid(): + return 0 + + if unset_environment: + os.environ.pop('LISTEN_PID', None) + os.environ.pop('LISTEN_FDS', None) + + return fds diff --git a/venv/lib/python3.6/site-packages/gunicorn/util.py b/venv/lib/python3.6/site-packages/gunicorn/util.py new file mode 100644 index 0000000..84f6937 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/util.py @@ -0,0 +1,557 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +from __future__ import print_function + +import email.utils +import fcntl +import io +import os +import pkg_resources +import pwd +import random +import socket +import sys +import textwrap +import time +import traceback +import inspect +import errno +import warnings +import logging +import re + +from gunicorn import _compat +from gunicorn.errors import AppImportError +from gunicorn.six import text_type +from gunicorn.workers import SUPPORTED_WORKERS + +REDIRECT_TO = getattr(os, 'devnull', '/dev/null') + +# Server and Date aren't technically hop-by-hop +# headers, but they are in the purview of the +# origin server which the WSGI spec says we should +# act like. So we drop them and add our own. +# +# In the future, concatenation server header values +# might be better, but nothing else does it and +# dropping them is easier. +hop_headers = set(""" + connection keep-alive proxy-authenticate proxy-authorization + te trailers transfer-encoding upgrade + server date + """.split()) + +try: + from setproctitle import setproctitle + + def _setproctitle(title): + setproctitle("gunicorn: %s" % title) +except ImportError: + def _setproctitle(title): + return + + +try: + from importlib import import_module +except ImportError: + def _resolve_name(name, package, level): + """Return the absolute name of the module to be imported.""" + if not hasattr(package, 'rindex'): + raise ValueError("'package' not set to a string") + dot = len(package) + for _ in range(level, 1, -1): + try: + dot = package.rindex('.', 0, dot) + except ValueError: + msg = "attempted relative import beyond top-level package" + raise ValueError(msg) + return "%s.%s" % (package[:dot], name) + + def import_module(name, package=None): + """Import a module. + +The 'package' argument is required when performing a relative import. It +specifies the package to use as the anchor point from which to resolve the +relative import to an absolute import. + +""" + if name.startswith('.'): + if not package: + raise TypeError("relative imports require the 'package' argument") + level = 0 + for character in name: + if character != '.': + break + level += 1 + name = _resolve_name(name[level:], package, level) + __import__(name) + return sys.modules[name] + + +def load_class(uri, default="gunicorn.workers.sync.SyncWorker", + section="gunicorn.workers"): + if inspect.isclass(uri): + return uri + if uri.startswith("egg:"): + # uses entry points + entry_str = uri.split("egg:")[1] + try: + dist, name = entry_str.rsplit("#", 1) + except ValueError: + dist = entry_str + name = default + + try: + return pkg_resources.load_entry_point(dist, section, name) + except: + exc = traceback.format_exc() + msg = "class uri %r invalid or not found: \n\n[%s]" + raise RuntimeError(msg % (uri, exc)) + else: + components = uri.split('.') + if len(components) == 1: + while True: + if uri.startswith("#"): + uri = uri[1:] + + if uri in SUPPORTED_WORKERS: + components = SUPPORTED_WORKERS[uri].split(".") + break + + try: + return pkg_resources.load_entry_point("gunicorn", + section, uri) + except: + exc = traceback.format_exc() + msg = "class uri %r invalid or not found: \n\n[%s]" + raise RuntimeError(msg % (uri, exc)) + + klass = components.pop(-1) + + try: + mod = import_module('.'.join(components)) + except: + exc = traceback.format_exc() + msg = "class uri %r invalid or not found: \n\n[%s]" + raise RuntimeError(msg % (uri, exc)) + return getattr(mod, klass) + + +def get_username(uid): + """ get the username for a user id""" + return pwd.getpwuid(uid).pw_name + + +def set_owner_process(uid, gid, initgroups=False): + """ set user and group of workers processes """ + + if gid: + if uid: + try: + username = get_username(uid) + except KeyError: + initgroups = False + + # versions of python < 2.6.2 don't manage unsigned int for + # groups like on osx or fedora + gid = abs(gid) & 0x7FFFFFFF + + if initgroups: + os.initgroups(username, gid) + elif gid != os.getgid(): + os.setgid(gid) + + if uid: + os.setuid(uid) + + +def chown(path, uid, gid): + gid = abs(gid) & 0x7FFFFFFF # see note above. + os.chown(path, uid, gid) + + +if sys.platform.startswith("win"): + def _waitfor(func, pathname, waitall=False): + # Peform the operation + func(pathname) + # Now setup the wait loop + if waitall: + dirname = pathname + else: + dirname, name = os.path.split(pathname) + dirname = dirname or '.' + # Check for `pathname` to be removed from the filesystem. + # The exponential backoff of the timeout amounts to a total + # of ~1 second after which the deletion is probably an error + # anyway. + # Testing on a i7@4.3GHz shows that usually only 1 iteration is + # required when contention occurs. + timeout = 0.001 + while timeout < 1.0: + # Note we are only testing for the existence of the file(s) in + # the contents of the directory regardless of any security or + # access rights. If we have made it this far, we have sufficient + # permissions to do that much using Python's equivalent of the + # Windows API FindFirstFile. + # Other Windows APIs can fail or give incorrect results when + # dealing with files that are pending deletion. + L = os.listdir(dirname) + if not L if waitall else name in L: + return + # Increase the timeout and try again + time.sleep(timeout) + timeout *= 2 + warnings.warn('tests may fail, delete still pending for ' + pathname, + RuntimeWarning, stacklevel=4) + + def _unlink(filename): + _waitfor(os.unlink, filename) +else: + _unlink = os.unlink + + +def unlink(filename): + try: + _unlink(filename) + except OSError as error: + # The filename need not exist. + if error.errno not in (errno.ENOENT, errno.ENOTDIR): + raise + + +def is_ipv6(addr): + try: + socket.inet_pton(socket.AF_INET6, addr) + except socket.error: # not a valid address + return False + except ValueError: # ipv6 not supported on this platform + return False + return True + + +def parse_address(netloc, default_port=8000): + if re.match(r'unix:(//)?', netloc): + return re.split(r'unix:(//)?', netloc)[-1] + + if netloc.startswith("tcp://"): + netloc = netloc.split("tcp://")[1] + + # get host + if '[' in netloc and ']' in netloc: + host = netloc.split(']')[0][1:].lower() + elif ':' in netloc: + host = netloc.split(':')[0].lower() + elif netloc == "": + host = "0.0.0.0" + else: + host = netloc.lower() + + #get port + netloc = netloc.split(']')[-1] + if ":" in netloc: + port = netloc.split(':', 1)[1] + if not port.isdigit(): + raise RuntimeError("%r is not a valid port number." % port) + port = int(port) + else: + port = default_port + return (host, port) + + +def close_on_exec(fd): + flags = fcntl.fcntl(fd, fcntl.F_GETFD) + flags |= fcntl.FD_CLOEXEC + fcntl.fcntl(fd, fcntl.F_SETFD, flags) + + +def set_non_blocking(fd): + flags = fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK + fcntl.fcntl(fd, fcntl.F_SETFL, flags) + + +def close(sock): + try: + sock.close() + except socket.error: + pass + +try: + from os import closerange +except ImportError: + def closerange(fd_low, fd_high): + # Iterate through and close all file descriptors. + for fd in range(fd_low, fd_high): + try: + os.close(fd) + except OSError: # ERROR, fd wasn't open to begin with (ignored) + pass + + +def write_chunk(sock, data): + if isinstance(data, text_type): + data = data.encode('utf-8') + chunk_size = "%X\r\n" % len(data) + chunk = b"".join([chunk_size.encode('utf-8'), data, b"\r\n"]) + sock.sendall(chunk) + + +def write(sock, data, chunked=False): + if chunked: + return write_chunk(sock, data) + sock.sendall(data) + + +def write_nonblock(sock, data, chunked=False): + timeout = sock.gettimeout() + if timeout != 0.0: + try: + sock.setblocking(0) + return write(sock, data, chunked) + finally: + sock.setblocking(1) + else: + return write(sock, data, chunked) + + +def write_error(sock, status_int, reason, mesg): + html = textwrap.dedent("""\ + + + %(reason)s + + +

    %(reason)s

    + %(mesg)s + + + """) % {"reason": reason, "mesg": _compat.html_escape(mesg)} + + http = textwrap.dedent("""\ + HTTP/1.1 %s %s\r + Connection: close\r + Content-Type: text/html\r + Content-Length: %d\r + \r + %s""") % (str(status_int), reason, len(html), html) + write_nonblock(sock, http.encode('latin1')) + + +def import_app(module): + parts = module.split(":", 1) + if len(parts) == 1: + module, obj = module, "application" + else: + module, obj = parts[0], parts[1] + + try: + __import__(module) + except ImportError: + if module.endswith(".py") and os.path.exists(module): + msg = "Failed to find application, did you mean '%s:%s'?" + raise ImportError(msg % (module.rsplit(".", 1)[0], obj)) + else: + raise + + mod = sys.modules[module] + + is_debug = logging.root.level == logging.DEBUG + try: + app = eval(obj, vars(mod)) + except NameError: + if is_debug: + traceback.print_exception(*sys.exc_info()) + raise AppImportError("Failed to find application object %r in %r" % (obj, module)) + + if app is None: + raise AppImportError("Failed to find application object: %r" % obj) + + if not callable(app): + raise AppImportError("Application object must be callable.") + return app + + +def getcwd(): + # get current path, try to use PWD env first + try: + a = os.stat(os.environ['PWD']) + b = os.stat(os.getcwd()) + if a.st_ino == b.st_ino and a.st_dev == b.st_dev: + cwd = os.environ['PWD'] + else: + cwd = os.getcwd() + except: + cwd = os.getcwd() + return cwd + + +def http_date(timestamp=None): + """Return the current date and time formatted for a message header.""" + if timestamp is None: + timestamp = time.time() + s = email.utils.formatdate(timestamp, localtime=False, usegmt=True) + return s + + +def is_hoppish(header): + return header.lower().strip() in hop_headers + + +def daemonize(enable_stdio_inheritance=False): + """\ + Standard daemonization of a process. + http://www.svbug.com/documentation/comp.unix.programmer-FAQ/faq_2.html#SEC16 + """ + if 'GUNICORN_FD' not in os.environ: + if os.fork(): + os._exit(0) + os.setsid() + + if os.fork(): + os._exit(0) + + os.umask(0o22) + + # In both the following any file descriptors above stdin + # stdout and stderr are left untouched. The inheritance + # option simply allows one to have output go to a file + # specified by way of shell redirection when not wanting + # to use --error-log option. + + if not enable_stdio_inheritance: + # Remap all of stdin, stdout and stderr on to + # /dev/null. The expectation is that users have + # specified the --error-log option. + + closerange(0, 3) + + fd_null = os.open(REDIRECT_TO, os.O_RDWR) + + if fd_null != 0: + os.dup2(fd_null, 0) + + os.dup2(fd_null, 1) + os.dup2(fd_null, 2) + + else: + fd_null = os.open(REDIRECT_TO, os.O_RDWR) + + # Always redirect stdin to /dev/null as we would + # never expect to need to read interactive input. + + if fd_null != 0: + os.close(0) + os.dup2(fd_null, 0) + + # If stdout and stderr are still connected to + # their original file descriptors we check to see + # if they are associated with terminal devices. + # When they are we map them to /dev/null so that + # are still detached from any controlling terminal + # properly. If not we preserve them as they are. + # + # If stdin and stdout were not hooked up to the + # original file descriptors, then all bets are + # off and all we can really do is leave them as + # they were. + # + # This will allow 'gunicorn ... > output.log 2>&1' + # to work with stdout/stderr going to the file + # as expected. + # + # Note that if using --error-log option, the log + # file specified through shell redirection will + # only be used up until the log file specified + # by the option takes over. As it replaces stdout + # and stderr at the file descriptor level, then + # anything using stdout or stderr, including having + # cached a reference to them, will still work. + + def redirect(stream, fd_expect): + try: + fd = stream.fileno() + if fd == fd_expect and stream.isatty(): + os.close(fd) + os.dup2(fd_null, fd) + except AttributeError: + pass + + redirect(sys.stdout, 1) + redirect(sys.stderr, 2) + + +def seed(): + try: + random.seed(os.urandom(64)) + except NotImplementedError: + random.seed('%s.%s' % (time.time(), os.getpid())) + + +def check_is_writeable(path): + try: + f = open(path, 'a') + except IOError as e: + raise RuntimeError("Error: '%s' isn't writable [%r]" % (path, e)) + f.close() + + +def to_bytestring(value, encoding="utf8"): + """Converts a string argument to a byte string""" + if isinstance(value, bytes): + return value + if not isinstance(value, text_type): + raise TypeError('%r is not a string' % value) + + return value.encode(encoding) + +def has_fileno(obj): + if not hasattr(obj, "fileno"): + return False + + # check BytesIO case and maybe others + try: + obj.fileno() + except (AttributeError, IOError, io.UnsupportedOperation): + return False + + return True + + +def warn(msg): + print("!!!", file=sys.stderr) + + lines = msg.splitlines() + for i, line in enumerate(lines): + if i == 0: + line = "WARNING: %s" % line + print("!!! %s" % line, file=sys.stderr) + + print("!!!\n", file=sys.stderr) + sys.stderr.flush() + + +def make_fail_app(msg): + msg = to_bytestring(msg) + + def app(environ, start_response): + start_response("500 Internal Server Error", [ + ("Content-Type", "text/plain"), + ("Content-Length", str(len(msg))) + ]) + return [msg] + + return app + + +def split_request_uri(uri): + if uri.startswith("//"): + # When the path starts with //, urlsplit considers it as a + # relative uri while the RFC says we should consider it as abs_path + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 + # We use temporary dot prefix to workaround this behaviour + parts = _compat.urlsplit("." + uri) + return parts._replace(path=parts.path[1:]) + + return _compat.urlsplit(uri) diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/__init__.py b/venv/lib/python3.6/site-packages/gunicorn/workers/__init__.py new file mode 100644 index 0000000..074e001 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import sys + +# supported gunicorn workers. +SUPPORTED_WORKERS = { + "sync": "gunicorn.workers.sync.SyncWorker", + "eventlet": "gunicorn.workers.geventlet.EventletWorker", + "gevent": "gunicorn.workers.ggevent.GeventWorker", + "gevent_wsgi": "gunicorn.workers.ggevent.GeventPyWSGIWorker", + "gevent_pywsgi": "gunicorn.workers.ggevent.GeventPyWSGIWorker", + "tornado": "gunicorn.workers.gtornado.TornadoWorker", + "gthread": "gunicorn.workers.gthread.ThreadWorker", +} + + +if sys.version_info >= (3, 4): + # gaiohttp worker can be used with Python 3.4+ only. + SUPPORTED_WORKERS["gaiohttp"] = "gunicorn.workers.gaiohttp.AiohttpWorker" diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/__init__.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5f76b42dcb552e98ca2b9060fcd52c9c3ad1a56 GIT binary patch literal 634 zcmaix-AcnS6vxw#tz}ykeFASpiY$fA3ooW9IEDyrtdl_y%3Nxft+pl2Ho9jKy!IV@ zlHPi?SMbWT>6kFZhW^h<_??gBG!3Kj@wWe1Rsi?}KR%uNSNx9~Trj|r4-mjIR?yY$oxL+N4^MH&)8o*^pXD*FloCITNa~b9b zD|3hN+=Ux3-(|2}I0bsViwYKY@{j^M{RitML%VZZz$d#fX!~jB%Cl7!E0ef zX{39_xhc~uoMz#sk}7c$qXYY)*X#BN?Hgy?N`y64jsP#K(32{EW_~&4#sktgW*YW3?Qfg)ql~QIhJdmk`f=_p;2B#fMyURhG6( HS1a;2+kwqG literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/_gaiohttp.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/_gaiohttp.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0808fb57d724d505b3ba946d4e65eadeb6883ad GIT binary patch literal 4881 zcmZ`-%a0sK8Lxg!ch7Slwl}sD!Wa@@h_eP9AVk42B(WV~7E5cip-I%VyKBci)7w3+ zs`2iQX5lcF!X;6ThzsJ#zkv7~I7LFLQzTB@IOoLgtDc$NS(zSHb-nw0{=To~X1m?^ z$6wYy`qu@<{>{!jEwpc=$Umb>S!(4htlm~=nZ6y`=Iw-zdAp%|+%m0NSleU8WtVYp z*bw(EM!(bwYrkQI{b6;AKL`f(u#WY1-Z*H6EsNb{sgt_TSnBfD%n6(5*HRCCkJkrn zjC3&KrvXNSnH{dB)-LPS{{#BiPS4U?d%Jf(*yZ9W7x$DJJrJcTlQK_ee(~67+(wZX zQE^6`fD0?NpD}RZ;5=8*nxuw`9(o5T@1n>qswo?=fi-oe*3?$^z`du^*3l$3+;Tu_90?uCMD{PwHTDop}Ez{dbH%+h2JL!e=qG_*v!dw=v zLyebo`)8HV_KXx0QYt+z3K6=7B5$EmY(^FVd-%+<$Uf{7XX*?b@g20?sWY>tEUFb= zin*xPr`pS?_Km$*JH0^H?Ds=2laZ9Uz7QpX!*-|>C1s)bksdw+c)f(V zGdy?E{Wgju7iV_MZdfgQa&?v4vnQ=^+`t}#3vZ!89Fy*eQ$mGLZQ^*cNxf69_rR?g z0rwUoC^U!x%D#>>dcGhHgl%5PvEb4ESmCAJNQ?`!AoQtOa1n0e9gPc>kwF}ca;~^| znU1JfJ?DDb!E(||#i+Gq+t#K%*;r*1YZ8M7SV(WeKEvO4(NGK)Zq0cF`bE}!#38Oz zYvy7MwFwEVTxOsq#GW?L!fdA~OX%e-j5t z5?xNPDh)QMGqSVsY$$w zs#lvk{-Nw=aQ`$FVE`;K6p2@88HJKlN6`L$oIvo$?_k)(MrL=%Q6{? z8#H{5*e0RF{NumG{33*1Stakxsuj1bV@pEY2)vKGFk@H&4A>SgQ;S^qeK_VlI4ET? zRrW$G4BXTv=vrhk#sZAjO|Bx_Af)Na$77Y2heekkC459Mj0o>Pj%BQrF#MSfM$@`} zy6CxXYnDDBQjNVsAua17;qir4P7OLPdGxTMiDGzs1ZX~b6V$W!*vH!^WZ&e*Hi|Q| zPXJN_*T9oE#n{ZQI43Tj1ba*&OPjG1=lF}Ge_~}_K1AL4+!6$9O@c5Q6`xO?YUR*I zn+;m%wP$STm|kaqOl`_$HlUtBu}*vBSEya_1!|f{z*OsKH)ht%vDncT&4S_<{LLL~ z(3hV4eUHB84mPQG^a`yWT$mAGTPHPjaav1V=MUmEb2pvc>ep&B0O9T zeR2w}q<8}p;wGvvK&B9pi;K(XBH0{`c#-PPGG{QEfwoJjJ-EauOLeUR4T56r6{X7d zk3$!9rP`+zXjJy;X2pI9G~ zQkvpWisE6c1NUfI4QJh=F#a;FwgZfG?8)Y;!%!4mfvshzqx=+lH(oNe;U`rvC+M-!;jm z?C$Q~f453DyICPsTqL}UP}LpFxX&@vou_NtD}-)08r}J`%Xt@~+Qvb<#%`CHqdDiG z6ug^q6=%8JUT*5O#657O8>gSIa$Vf(B=A(PQJ{?yE>r|x%WzSm_I{Rg?GO2}Y1GX$ zu2jf~u@44Lw-t`1vDqp0C3DJrE-#bVq|ETWZ1fl(!Y2`A<{jvrGG`>^>gc>0$zcA7 zBG*wdk8Cu6HMUHdT>C#NJepThNvC?@y^cB|2<2SMNm zoxte?o!2@wK`SVvEFTB0+bELq&c$sFw=p|)o>{m}rb0f*`@6+J&^=JE4*cq}{(_QW94Tl&=#S;z~1F&|j^=e}RV0eahnpW9j7p*27ors5rNti<+w=b=wjag#bHktav`gKv70>^w zpxo7Dyy2B!<>~YPod~_^a&C;%kn}w2b?|oeSH&$#jU-7ifRmb^$*DRJQ_F5S>$T03 F{{g}Hh6(@x literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/base.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/base.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3561f5b6343dfdf4b0c1aa603a74b3ef44283b6 GIT binary patch literal 7495 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/base_async.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/base_async.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1baa053716d406517308f4112d8a8f47f7ebbcc9 GIT binary patch literal 3844 zcmb7HPj4H?6`xrymrIJIWJ;DLN0u1}P0h5C+d61^FxmybM3EnYc+BG=8!(4);*F3yT-SzU=(^w(bZt7O zu8V=?Seh#ZR$x1}=F36Fsc3E{s0KBs2EGv12eZyBXa3xTIhTIKJku+_U|!L$1&v9; znfHtx*0R2Y7p%pV?R64A=?(l%d=zBws7oEwUm<7Y9WXv4CmrKsw#Pm0882)&S@6sk z3{Dn33w6;ec{XaxD|;2xrH`4(92*8_)Xcg(j=HsGUH5bLuOHk>2E$v!-tg_Gqh9E}9e(c}(GPBa z=hprx>UCogy<{>I^48Oi^xY1HwKhB!7QDHFBFQbr8+^QQF66bTn`g084ye3`A}b&X zo3M|N;w(z8<~QTy-e4H|13yZ9uPsC@T3px=71c9R1E*$a3gc1Yc9Wym&>#&E#>ZFA zdvH#stNS5BH5KgyX-;{<`m{ulXs%}0?{Bx)Hy*op*Ecurw>MgbHm_{q%V96U99(du z(UWc`6n@7$b`Ls{7y8~Rv}XPo6-&9{)1sL1Ds9ENTl(c{jmsNsAMHf>jfgQY$r6BP~o!&Z3|7*h%SVi?pH7 zp0HofarRPv7j}Naw%H?=n)3M;Xiyd}h@O-pD=np_TfV6C+2$_WWzpYF%wJFmX8INJ zH_lFM&c+Ln#lG}-YEKLfsmHY2(f14c5sQ9{5%qrMlnZ$FioEe3<;Rep4d(QSn;c6= zdwwZt1oIR2DZU;fKFBO+i4XpZ#5<6Ri>LR>c%yI;~@a2M^#v`KK!vpfbvJj$(X!OGR-YVaD$ z%AV0Lof@L4HD2>9(iq*$C05{rA@1bXO1iI_f7$ePA+~8Y4DreTlMRn0tHCnXA2dF~ zjAFZ#H^*avC@#T0HM8$061Jkwz*ptI?+-g+@3}7s$dwuS zM~SLud*97X3&M9b56_Q|UC<=%#vx#PFIJXzS2~r=_{T`zMknd0LKyF>qUS#y?W@{O z5)bbIX*z`EIHF{zY{ac|p-KWBJ?W$USSs5;>SkK!+R^i#h$BMIOtRcQ>iR>iqnbOX zEenDgSlPMnwzsz*Y`g6T@2jHDxGF*-id9jkL{-}FJ=ombZtt`osxmC)+RRbX3tQzJ z7sH-R{Kyx|%BCJ?j^_zgKq8bi)JasesgO$<6ieg)_b(!;m3essz48$V<0iLxog1Jw zukc2}1aE>{05!P9ZNuVA_*%u;gJLxJ97==VL|cPbdBv#mCb$J&H|i)W;NLXHjq{kC z8SNIyAbv9_O%#p8OTb~mUSMqzIEwqoN$Hf2zW^rF{x*U_iXBf#mzY73Fc+wzk$O^m zVw{vPf~S^e+`*Y{vcU2R@8axn3Aan)Lr~)Eeo^zd+jtppr!a$ zvPh>nP6ooW#q*f_5h3*_XuYCa!yKuXf@Q+EGb}%V#EO>qYbKE*+v3l#a81|b)vBlC zIbKQ&!S$pWyiRd|*WSSP;+d5%G99oCJkNa7erR;}*fE^~MsR~>kn)*D8QsWzFa*v? z`s-Y*dOBa3F5n*r{#7;~j^_^Les66s3X@)PKzD#IHJp8OeUxBp%cLU`0-x;q)a>kG zS2w#OAqXO;Aoc$ElGd`<3w`%cbcS@%dlO4=yr?&FQ?F1`laCm$p6%>rFKP~BnM`4) zc@W3_)*FH@II0lEhZ?_}8Q}9&M44m_yA|#xzA_@AiW<$F>Y#Hpm6GBrsW1t#oSD5? zbbZ_$0m=f`eCE`3Xb_e|TsM8uAmK_65RHzg>#NG4z-7s$m{d{oIwWOvx&-B_bkLEV zBoR(k9>k;2%ayM{k01ch&?>GI*Cg^fkvBk`>H*U13+YNEQM`pFWy^z6;>CxNvIwR9 zM3r~${`8@{y|KIPRL|$9HIrQ?zfJ0H(O8p4h&9q&%k%5%;8e-a5LKBJrnsb~5!Aq` zP6g?Hc>%=)U5Fo}$fqC-z-8tzRsoa|oDneD+%f=a0HunCvO2FCS$~x;0<}b3(^K(1i!$?SHB8o(@AWV>8(JJi?>3>bqBFG!Wwf`)pehZI$_qM zukrf4($gqz!)+afs^zbRt{;Z3`!Y~m$q6O}UBva1gw}+<(A!4Uv;8P202_u%R2gojNoX^duO#6AXY@5aN(}4MtR4z|P58@ZK{ zfsL8}z%R+l)W5*QbCYr;%Ja+j-uu4qdD8E9fBm@i{$T^5-{`;RF62+(Gv6UF#IQh- zJBh#V|4tbhD1D&X6SMh?7;1`PUdD)>+^vkUlVW9V5eMXc%n49eOpyrB~Qg zY+(zu^&nh@GgVN@IipFcme%GpmBuDQ0Adv_45xOP8hWYpn(Gi4ZoepGI#*gw`NedH z0@#0AO;^CxySGbDj}|rMQ9Vt8hw)tVd15&W=^5vAG^&_KbCDP*t9g-eX%m|&Nk1q} zGo?9rf;8KKp>8?f_y75Ry|GK^Jw2h5@c70vr+fKYeMq!hTf4P~{jy;;Ch`~cE}8&Z z2A{Y#ARIfrs9oDHdoQzGY5PoTrOV(=A#IxR-Cs1Qd%|i9-E<1hV03R!U6)K4?+umS zWuj%-j$^pzIR1%r-;KHO=VWZNe4MBG=%Ppk8;M89dcB%F81LzBl1kEy_q1WYDgDQp hVuj$R&VxCG;FExKF~I>I-~l1{0M0wa!>xu-{sIks&#C|b literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/geventlet.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/geventlet.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d60a23a815b6495bbefd1ba76e4c5473120ef2c GIT binary patch literal 4626 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 J_BOrBe*jONTLl0B literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/ggevent.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/ggevent.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..370749dab167d7e95d4670cca65295d3e3cb8fb4 GIT binary patch literal 6662 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 jqo{X}n`M#%NQlro>jW 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/gtornado.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/gtornado.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..265bd56876e3d5fb969c171918e473511a353406 GIT binary patch literal 4059 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" % self.pid + + def notify(self): + """\ + Your worker subclass must arrange to have this method called + once every ``self.timeout`` seconds. If you fail in accomplishing + this task, the master process will murder your workers. + """ + self.tmp.notify() + + def run(self): + """\ + This is the mainloop of a worker process. You should override + this method in a subclass to provide the intended behaviour + for your particular evil schemes. + """ + raise NotImplementedError() + + def init_process(self): + """\ + If you override this method in a subclass, the last statement + in the function should be to call this method with + super(MyWorkerClass, self).init_process() so that the ``run()`` + loop is initiated. + """ + + # set environment' variables + if self.cfg.env: + for k, v in self.cfg.env.items(): + os.environ[k] = v + + util.set_owner_process(self.cfg.uid, self.cfg.gid, + initgroups=self.cfg.initgroups) + + # Reseed the random number generator + util.seed() + + # For waking ourselves up + self.PIPE = os.pipe() + for p in self.PIPE: + util.set_non_blocking(p) + util.close_on_exec(p) + + # Prevent fd inheritance + for s in self.sockets: + util.close_on_exec(s) + util.close_on_exec(self.tmp.fileno()) + + self.wait_fds = self.sockets + [self.PIPE[0]] + + self.log.close_on_exec() + + self.init_signals() + + # start the reloader + if self.cfg.reload: + def changed(fname): + self.log.info("Worker reloading: %s modified", fname) + self.alive = False + self.cfg.worker_int(self) + time.sleep(0.1) + sys.exit(0) + + reloader_cls = reloader_engines[self.cfg.reload_engine] + self.reloader = reloader_cls(extra_files=self.cfg.reload_extra_files, + callback=changed) + self.reloader.start() + + self.load_wsgi() + self.cfg.post_worker_init(self) + + # Enter main run loop + self.booted = True + self.run() + + def load_wsgi(self): + try: + self.wsgi = self.app.wsgi() + except SyntaxError as e: + if not self.cfg.reload: + raise + + self.log.exception(e) + + # fix from PR #1228 + # storing the traceback into exc_tb will create a circular reference. + # per https://docs.python.org/2/library/sys.html#sys.exc_info warning, + # delete the traceback after use. + try: + _, exc_val, exc_tb = sys.exc_info() + self.reloader.add_extra_file(exc_val.filename) + + tb_string = six.StringIO() + traceback.print_tb(exc_tb, file=tb_string) + self.wsgi = util.make_fail_app(tb_string.getvalue()) + finally: + del exc_tb + + def init_signals(self): + # reset signaling + for s in self.SIGNALS: + signal.signal(s, signal.SIG_DFL) + # init new signaling + signal.signal(signal.SIGQUIT, self.handle_quit) + signal.signal(signal.SIGTERM, self.handle_exit) + signal.signal(signal.SIGINT, self.handle_quit) + signal.signal(signal.SIGWINCH, self.handle_winch) + signal.signal(signal.SIGUSR1, self.handle_usr1) + signal.signal(signal.SIGABRT, self.handle_abort) + + # Don't let SIGTERM and SIGUSR1 disturb active requests + # by interrupting system calls + if hasattr(signal, 'siginterrupt'): # python >= 2.6 + signal.siginterrupt(signal.SIGTERM, False) + signal.siginterrupt(signal.SIGUSR1, False) + + if hasattr(signal, 'set_wakeup_fd'): + signal.set_wakeup_fd(self.PIPE[1]) + + def handle_usr1(self, sig, frame): + self.log.reopen_files() + + def handle_exit(self, sig, frame): + self.alive = False + + def handle_quit(self, sig, frame): + self.alive = False + # worker_int callback + self.cfg.worker_int(self) + time.sleep(0.1) + sys.exit(0) + + def handle_abort(self, sig, frame): + self.alive = False + self.cfg.worker_abort(self) + sys.exit(1) + + def handle_error(self, req, client, addr, exc): + request_start = datetime.now() + addr = addr or ('', -1) # unix socket case + if isinstance(exc, (InvalidRequestLine, InvalidRequestMethod, + InvalidHTTPVersion, InvalidHeader, InvalidHeaderName, + LimitRequestLine, LimitRequestHeaders, + InvalidProxyLine, ForbiddenProxyRequest, + InvalidSchemeHeaders, + SSLError)): + + status_int = 400 + reason = "Bad Request" + + if isinstance(exc, InvalidRequestLine): + mesg = "Invalid Request Line '%s'" % str(exc) + elif isinstance(exc, InvalidRequestMethod): + mesg = "Invalid Method '%s'" % str(exc) + elif isinstance(exc, InvalidHTTPVersion): + mesg = "Invalid HTTP Version '%s'" % str(exc) + elif isinstance(exc, (InvalidHeaderName, InvalidHeader,)): + mesg = "%s" % str(exc) + if not req and hasattr(exc, "req"): + req = exc.req # for access log + elif isinstance(exc, LimitRequestLine): + mesg = "%s" % str(exc) + elif isinstance(exc, LimitRequestHeaders): + mesg = "Error parsing headers: '%s'" % str(exc) + elif isinstance(exc, InvalidProxyLine): + mesg = "'%s'" % str(exc) + elif isinstance(exc, ForbiddenProxyRequest): + reason = "Forbidden" + mesg = "Request forbidden" + status_int = 403 + elif isinstance(exc, InvalidSchemeHeaders): + mesg = "%s" % str(exc) + elif isinstance(exc, SSLError): + reason = "Forbidden" + mesg = "'%s'" % str(exc) + status_int = 403 + + msg = "Invalid request from ip={ip}: {error}" + self.log.debug(msg.format(ip=addr[0], error=str(exc))) + else: + if hasattr(req, "uri"): + self.log.exception("Error handling request %s", req.uri) + status_int = 500 + reason = "Internal Server Error" + mesg = "" + + if req is not None: + request_time = datetime.now() - request_start + environ = default_environ(req, client, self.cfg) + environ['REMOTE_ADDR'] = addr[0] + environ['REMOTE_PORT'] = str(addr[1]) + resp = Response(req, client, self.cfg) + resp.status = "%s %s" % (status_int, reason) + resp.response_length = len(mesg) + self.log.access(resp, req, environ, request_time) + + try: + util.write_error(client, status_int, reason, mesg) + except: + self.log.debug("Failed to send error message.") + + def handle_winch(self, sig, fname): + # Ignore SIGWINCH in worker. Fixes a crash on OpenBSD. + self.log.debug("worker: SIGWINCH ignored.") diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/base_async.py b/venv/lib/python3.6/site-packages/gunicorn/workers/base_async.py new file mode 100644 index 0000000..a3a0f91 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/base_async.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +from datetime import datetime +import errno +import socket +import ssl +import sys + +import gunicorn.http as http +import gunicorn.http.wsgi as wsgi +import gunicorn.util as util +import gunicorn.workers.base as base +from gunicorn import six + +ALREADY_HANDLED = object() + + +class AsyncWorker(base.Worker): + + def __init__(self, *args, **kwargs): + super(AsyncWorker, self).__init__(*args, **kwargs) + self.worker_connections = self.cfg.worker_connections + + def timeout_ctx(self): + raise NotImplementedError() + + def is_already_handled(self, respiter): + # some workers will need to overload this function to raise a StopIteration + return respiter == ALREADY_HANDLED + + def handle(self, listener, client, addr): + req = None + try: + parser = http.RequestParser(self.cfg, client) + try: + listener_name = listener.getsockname() + if not self.cfg.keepalive: + req = six.next(parser) + self.handle_request(listener_name, req, client, addr) + else: + # keepalive loop + proxy_protocol_info = {} + while True: + req = None + with self.timeout_ctx(): + req = six.next(parser) + if not req: + break + if req.proxy_protocol_info: + proxy_protocol_info = req.proxy_protocol_info + else: + req.proxy_protocol_info = proxy_protocol_info + self.handle_request(listener_name, req, client, addr) + except http.errors.NoMoreData as e: + self.log.debug("Ignored premature client disconnection. %s", e) + except StopIteration as e: + self.log.debug("Closing connection. %s", e) + except ssl.SSLError: + # pass to next try-except level + six.reraise(*sys.exc_info()) + except EnvironmentError: + # pass to next try-except level + six.reraise(*sys.exc_info()) + except Exception as e: + self.handle_error(req, client, addr, e) + except ssl.SSLError as e: + if e.args[0] == ssl.SSL_ERROR_EOF: + self.log.debug("ssl connection closed") + client.close() + else: + self.log.debug("Error processing SSL request.") + self.handle_error(req, client, addr, e) + except EnvironmentError as e: + if e.errno not in (errno.EPIPE, errno.ECONNRESET): + self.log.exception("Socket error processing request.") + else: + if e.errno == errno.ECONNRESET: + self.log.debug("Ignoring connection reset") + else: + self.log.debug("Ignoring EPIPE") + except Exception as e: + self.handle_error(req, client, addr, e) + finally: + util.close(client) + + def handle_request(self, listener_name, req, sock, addr): + request_start = datetime.now() + environ = {} + resp = None + try: + self.cfg.pre_request(self, req) + resp, environ = wsgi.create(req, sock, addr, + listener_name, self.cfg) + environ["wsgi.multithread"] = True + self.nr += 1 + if self.alive and self.nr >= self.max_requests: + self.log.info("Autorestarting worker after current request.") + resp.force_close() + self.alive = False + + if not self.cfg.keepalive: + resp.force_close() + + respiter = self.wsgi(environ, resp.start_response) + if self.is_already_handled(respiter): + return False + try: + if isinstance(respiter, environ['wsgi.file_wrapper']): + resp.write_file(respiter) + else: + for item in respiter: + resp.write(item) + resp.close() + request_time = datetime.now() - request_start + self.log.access(resp, req, environ, request_time) + finally: + if hasattr(respiter, "close"): + respiter.close() + if resp.should_close(): + raise StopIteration() + except StopIteration: + raise + except EnvironmentError: + # If the original exception was a socket.error we delegate + # handling it to the caller (where handle() might ignore it) + six.reraise(*sys.exc_info()) + except Exception: + if resp and resp.headers_sent: + # If the requests have already been sent, we should close the + # connection to indicate the error. + self.log.exception("Error handling request") + try: + sock.shutdown(socket.SHUT_RDWR) + sock.close() + except EnvironmentError: + pass + raise StopIteration() + raise + finally: + try: + self.cfg.post_request(self, req, environ, resp) + except Exception: + self.log.exception("Exception in post_request hook") + return True diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/gaiohttp.py b/venv/lib/python3.6/site-packages/gunicorn/workers/gaiohttp.py new file mode 100644 index 0000000..bef6b49 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/gaiohttp.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import sys + +from gunicorn import util + +if sys.version_info >= (3, 4): + try: + import aiohttp # pylint: disable=unused-import + except ImportError: + raise RuntimeError("You need aiohttp installed to use this worker.") + else: + try: + from aiohttp.worker import GunicornWebWorker as AiohttpWorker + except ImportError: + from gunicorn.workers._gaiohttp import AiohttpWorker + + util.warn( + "The 'gaiohttp' worker is deprecated. See --worker-class " + "documentation for more information." + ) + __all__ = ['AiohttpWorker'] +else: + raise RuntimeError("You need Python >= 3.4 to use the gaiohttp worker") diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/geventlet.py b/venv/lib/python3.6/site-packages/gunicorn/workers/geventlet.py new file mode 100644 index 0000000..189062c --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/geventlet.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +from functools import partial +import errno +import sys + +try: + import eventlet +except ImportError: + raise RuntimeError("You need eventlet installed to use this worker.") + +# validate the eventlet version +if eventlet.version_info < (0, 9, 7): + raise RuntimeError("You need eventlet >= 0.9.7") + + +from eventlet import hubs, greenthread +from eventlet.greenio import GreenSocket +from eventlet.hubs import trampoline +from eventlet.wsgi import ALREADY_HANDLED as EVENTLET_ALREADY_HANDLED +import greenlet + +from gunicorn.http.wsgi import sendfile as o_sendfile +from gunicorn.workers.base_async import AsyncWorker + +def _eventlet_sendfile(fdout, fdin, offset, nbytes): + while True: + try: + return o_sendfile(fdout, fdin, offset, nbytes) + except OSError as e: + if e.args[0] == errno.EAGAIN: + trampoline(fdout, write=True) + else: + raise + + +def _eventlet_serve(sock, handle, concurrency): + """ + Serve requests forever. + + This code is nearly identical to ``eventlet.convenience.serve`` except + that it attempts to join the pool at the end, which allows for gunicorn + graceful shutdowns. + """ + pool = eventlet.greenpool.GreenPool(concurrency) + server_gt = eventlet.greenthread.getcurrent() + + while True: + try: + conn, addr = sock.accept() + gt = pool.spawn(handle, conn, addr) + gt.link(_eventlet_stop, server_gt, conn) + conn, addr, gt = None, None, None + except eventlet.StopServe: + sock.close() + pool.waitall() + return + + +def _eventlet_stop(client, server, conn): + """ + Stop a greenlet handling a request and close its connection. + + This code is lifted from eventlet so as not to depend on undocumented + functions in the library. + """ + try: + try: + client.wait() + finally: + conn.close() + except greenlet.GreenletExit: + pass + except Exception: + greenthread.kill(server, *sys.exc_info()) + + +def patch_sendfile(): + from gunicorn.http import wsgi + + if o_sendfile is not None: + setattr(wsgi, "sendfile", _eventlet_sendfile) + + +class EventletWorker(AsyncWorker): + + def patch(self): + hubs.use_hub() + eventlet.monkey_patch(os=False) + patch_sendfile() + + def is_already_handled(self, respiter): + if respiter == EVENTLET_ALREADY_HANDLED: + raise StopIteration() + else: + return super(EventletWorker, self).is_already_handled(respiter) + + def init_process(self): + super(EventletWorker, self).init_process() + self.patch() + + def handle_quit(self, sig, frame): + eventlet.spawn(super(EventletWorker, self).handle_quit, sig, frame) + + def handle_usr1(self, sig, frame): + eventlet.spawn(super(EventletWorker, self).handle_usr1, sig, frame) + + def timeout_ctx(self): + return eventlet.Timeout(self.cfg.keepalive or None, False) + + def handle(self, listener, client, addr): + if self.cfg.is_ssl: + client = eventlet.wrap_ssl(client, server_side=True, + **self.cfg.ssl_options) + + super(EventletWorker, self).handle(listener, client, addr) + + def run(self): + acceptors = [] + for sock in self.sockets: + gsock = GreenSocket(sock) + gsock.setblocking(1) + hfun = partial(self.handle, gsock) + acceptor = eventlet.spawn(_eventlet_serve, gsock, hfun, + self.worker_connections) + + acceptors.append(acceptor) + eventlet.sleep(0.0) + + while self.alive: + self.notify() + eventlet.sleep(1.0) + + self.notify() + try: + with eventlet.Timeout(self.cfg.graceful_timeout) as t: + for a in acceptors: + a.kill(eventlet.StopServe()) + for a in acceptors: + a.wait() + except eventlet.Timeout as te: + if te != t: + raise + for a in acceptors: + a.kill() diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/ggevent.py b/venv/lib/python3.6/site-packages/gunicorn/workers/ggevent.py new file mode 100644 index 0000000..fb9d919 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/ggevent.py @@ -0,0 +1,246 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import errno +import os +import sys +from datetime import datetime +from functools import partial +import time + +_socket = __import__("socket") + +# workaround on osx, disable kqueue +if sys.platform == "darwin": + os.environ['EVENT_NOKQUEUE'] = "1" + +try: + import gevent +except ImportError: + raise RuntimeError("You need gevent installed to use this worker.") +from gevent.pool import Pool +from gevent.server import StreamServer +from gevent.socket import wait_write, socket +from gevent import pywsgi + +import gunicorn +from gunicorn.http.wsgi import base_environ +from gunicorn.workers.base_async import AsyncWorker +from gunicorn.http.wsgi import sendfile as o_sendfile + +VERSION = "gevent/%s gunicorn/%s" % (gevent.__version__, gunicorn.__version__) + +def _gevent_sendfile(fdout, fdin, offset, nbytes): + while True: + try: + return o_sendfile(fdout, fdin, offset, nbytes) + except OSError as e: + if e.args[0] == errno.EAGAIN: + wait_write(fdout) + else: + raise + +def patch_sendfile(): + from gunicorn.http import wsgi + + if o_sendfile is not None: + setattr(wsgi, "sendfile", _gevent_sendfile) + + +class GeventWorker(AsyncWorker): + + server_class = None + wsgi_handler = None + + def patch(self): + from gevent import monkey + monkey.noisy = False + + # if the new version is used make sure to patch subprocess + if gevent.version_info[0] == 0: + monkey.patch_all() + else: + monkey.patch_all(subprocess=True) + + # monkey patch sendfile to make it none blocking + patch_sendfile() + + # patch sockets + sockets = [] + for s in self.sockets: + if sys.version_info[0] == 3: + sockets.append(socket(s.FAMILY, _socket.SOCK_STREAM, + fileno=s.sock.fileno())) + else: + sockets.append(socket(s.FAMILY, _socket.SOCK_STREAM, + _sock=s)) + self.sockets = sockets + + def notify(self): + super(GeventWorker, self).notify() + if self.ppid != os.getppid(): + self.log.info("Parent changed, shutting down: %s", self) + sys.exit(0) + + def timeout_ctx(self): + return gevent.Timeout(self.cfg.keepalive, False) + + def run(self): + servers = [] + ssl_args = {} + + if self.cfg.is_ssl: + ssl_args = dict(server_side=True, **self.cfg.ssl_options) + + for s in self.sockets: + s.setblocking(1) + pool = Pool(self.worker_connections) + if self.server_class is not None: + environ = base_environ(self.cfg) + environ.update({ + "wsgi.multithread": True, + "SERVER_SOFTWARE": VERSION, + }) + server = self.server_class( + s, application=self.wsgi, spawn=pool, log=self.log, + handler_class=self.wsgi_handler, environ=environ, + **ssl_args) + else: + hfun = partial(self.handle, s) + server = StreamServer(s, handle=hfun, spawn=pool, **ssl_args) + + server.start() + servers.append(server) + + while self.alive: + self.notify() + gevent.sleep(1.0) + + try: + # Stop accepting requests + for server in servers: + if hasattr(server, 'close'): # gevent 1.0 + server.close() + if hasattr(server, 'kill'): # gevent < 1.0 + server.kill() + + # Handle current requests until graceful_timeout + ts = time.time() + while time.time() - ts <= self.cfg.graceful_timeout: + accepting = 0 + for server in servers: + if server.pool.free_count() != server.pool.size: + accepting += 1 + + # if no server is accepting a connection, we can exit + if not accepting: + return + + self.notify() + gevent.sleep(1.0) + + # Force kill all active the handlers + self.log.warning("Worker graceful timeout (pid:%s)" % self.pid) + for server in servers: + server.stop(timeout=1) + except: + pass + + def handle(self, listener, client, addr): + # Connected socket timeout defaults to socket.getdefaulttimeout(). + # This forces to blocking mode. + client.setblocking(1) + super(GeventWorker, self).handle(listener, client, addr) + + def handle_request(self, listener_name, req, sock, addr): + try: + super(GeventWorker, self).handle_request(listener_name, req, sock, + addr) + except gevent.GreenletExit: + pass + except SystemExit: + pass + + def handle_quit(self, sig, frame): + # Move this out of the signal handler so we can use + # blocking calls. See #1126 + gevent.spawn(super(GeventWorker, self).handle_quit, sig, frame) + + def handle_usr1(self, sig, frame): + # Make the gevent workers handle the usr1 signal + # by deferring to a new greenlet. See #1645 + gevent.spawn(super(GeventWorker, self).handle_usr1, sig, frame) + + if gevent.version_info[0] == 0: + + def init_process(self): + # monkey patch here + self.patch() + + # reinit the hub + import gevent.core + gevent.core.reinit() + + #gevent 0.13 and older doesn't reinitialize dns for us after forking + #here's the workaround + gevent.core.dns_shutdown(fail_requests=1) + gevent.core.dns_init() + super(GeventWorker, self).init_process() + + else: + + def init_process(self): + # monkey patch here + self.patch() + + # reinit the hub + from gevent import hub + hub.reinit() + + # then initialize the process + super(GeventWorker, self).init_process() + + +class GeventResponse(object): + + status = None + headers = None + sent = None + + def __init__(self, status, headers, clength): + self.status = status + self.headers = headers + self.sent = clength + + +class PyWSGIHandler(pywsgi.WSGIHandler): + + def log_request(self): + start = datetime.fromtimestamp(self.time_start) + finish = datetime.fromtimestamp(self.time_finish) + response_time = finish - start + resp_headers = getattr(self, 'response_headers', {}) + resp = GeventResponse(self.status, resp_headers, self.response_length) + if hasattr(self, 'headers'): + req_headers = self.headers.items() + else: + req_headers = [] + self.server.log.access(resp, req_headers, self.environ, response_time) + + def get_environ(self): + env = super(PyWSGIHandler, self).get_environ() + env['gunicorn.sock'] = self.socket + env['RAW_URI'] = self.path + return env + + +class PyWSGIServer(pywsgi.WSGIServer): + pass + + +class GeventPyWSGIWorker(GeventWorker): + "The Gevent StreamServer based workers." + server_class = PyWSGIServer + wsgi_handler = PyWSGIHandler diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/gthread.py b/venv/lib/python3.6/site-packages/gunicorn/workers/gthread.py new file mode 100644 index 0000000..862f873 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/gthread.py @@ -0,0 +1,367 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +# design: +# a threaded worker accepts connections in the main loop, accepted +# connections are are added to the thread pool as a connection job. On +# keepalive connections are put back in the loop waiting for an event. +# If no event happen after the keep alive timeout, the connectoin is +# closed. + +from collections import deque +from datetime import datetime +import errno +from functools import partial +import os +import socket +import ssl +import sys +from threading import RLock +import time + +from .. import http +from ..http import wsgi +from .. import util +from . import base +from .. import six + + +try: + import concurrent.futures as futures +except ImportError: + raise RuntimeError(""" + You need to install the 'futures' package to use this worker with this + Python version. + """) + +try: + from asyncio import selectors +except ImportError: + from gunicorn import selectors + + +class TConn(object): + + def __init__(self, cfg, sock, client, server): + self.cfg = cfg + self.sock = sock + self.client = client + self.server = server + + self.timeout = None + self.parser = None + + # set the socket to non blocking + self.sock.setblocking(False) + + def init(self): + self.sock.setblocking(True) + if self.parser is None: + # wrap the socket if needed + if self.cfg.is_ssl: + self.sock = ssl.wrap_socket(self.sock, server_side=True, + **self.cfg.ssl_options) + + # initialize the parser + self.parser = http.RequestParser(self.cfg, self.sock) + + def set_timeout(self): + # set the timeout + self.timeout = time.time() + self.cfg.keepalive + + def close(self): + util.close(self.sock) + + +class ThreadWorker(base.Worker): + + def __init__(self, *args, **kwargs): + super(ThreadWorker, self).__init__(*args, **kwargs) + self.worker_connections = self.cfg.worker_connections + self.max_keepalived = self.cfg.worker_connections - self.cfg.threads + # initialise the pool + self.tpool = None + self.poller = None + self._lock = None + self.futures = deque() + self._keep = deque() + self.nr_conns = 0 + + @classmethod + def check_config(cls, cfg, log): + max_keepalived = cfg.worker_connections - cfg.threads + + if max_keepalived <= 0 and cfg.keepalive: + log.warning("No keepalived connections can be handled. " + + "Check the number of worker connections and threads.") + + def init_process(self): + self.tpool = futures.ThreadPoolExecutor(max_workers=self.cfg.threads) + self.poller = selectors.DefaultSelector() + self._lock = RLock() + super(ThreadWorker, self).init_process() + + def handle_quit(self, sig, frame): + self.alive = False + # worker_int callback + self.cfg.worker_int(self) + self.tpool.shutdown(False) + time.sleep(0.1) + sys.exit(0) + + def _wrap_future(self, fs, conn): + fs.conn = conn + self.futures.append(fs) + fs.add_done_callback(self.finish_request) + + def enqueue_req(self, conn): + conn.init() + # submit the connection to a worker + fs = self.tpool.submit(self.handle, conn) + self._wrap_future(fs, conn) + + def accept(self, server, listener): + try: + sock, client = listener.accept() + # initialize the connection object + conn = TConn(self.cfg, sock, client, server) + self.nr_conns += 1 + # enqueue the job + self.enqueue_req(conn) + except EnvironmentError as e: + if e.errno not in (errno.EAGAIN, + errno.ECONNABORTED, errno.EWOULDBLOCK): + raise + + def reuse_connection(self, conn, client): + with self._lock: + # unregister the client from the poller + self.poller.unregister(client) + # remove the connection from keepalive + try: + self._keep.remove(conn) + except ValueError: + # race condition + return + + # submit the connection to a worker + self.enqueue_req(conn) + + def murder_keepalived(self): + now = time.time() + while True: + with self._lock: + try: + # remove the connection from the queue + conn = self._keep.popleft() + except IndexError: + break + + delta = conn.timeout - now + if delta > 0: + # add the connection back to the queue + with self._lock: + self._keep.appendleft(conn) + break + else: + self.nr_conns -= 1 + # remove the socket from the poller + with self._lock: + try: + self.poller.unregister(conn.sock) + except EnvironmentError as e: + if e.errno != errno.EBADF: + raise + except KeyError: + # already removed by the system, continue + pass + + # close the socket + conn.close() + + def is_parent_alive(self): + # If our parent changed then we shut down. + if self.ppid != os.getppid(): + self.log.info("Parent changed, shutting down: %s", self) + return False + return True + + def run(self): + # init listeners, add them to the event loop + for sock in self.sockets: + sock.setblocking(False) + # a race condition during graceful shutdown may make the listener + # name unavailable in the request handler so capture it once here + server = sock.getsockname() + acceptor = partial(self.accept, server) + self.poller.register(sock, selectors.EVENT_READ, acceptor) + + while self.alive: + # notify the arbiter we are alive + self.notify() + + # can we accept more connections? + if self.nr_conns < self.worker_connections: + # wait for an event + events = self.poller.select(1.0) + for key, _ in events: + callback = key.data + callback(key.fileobj) + + # check (but do not wait) for finished requests + result = futures.wait(self.futures, timeout=0, + return_when=futures.FIRST_COMPLETED) + else: + # wait for a request to finish + result = futures.wait(self.futures, timeout=1.0, + return_when=futures.FIRST_COMPLETED) + + # clean up finished requests + for fut in result.done: + self.futures.remove(fut) + + if not self.is_parent_alive(): + break + + # hanle keepalive timeouts + self.murder_keepalived() + + self.tpool.shutdown(False) + self.poller.close() + + for s in self.sockets: + s.close() + + futures.wait(self.futures, timeout=self.cfg.graceful_timeout) + + def finish_request(self, fs): + if fs.cancelled(): + self.nr_conns -= 1 + fs.conn.close() + return + + try: + (keepalive, conn) = fs.result() + # if the connection should be kept alived add it + # to the eventloop and record it + if keepalive: + # flag the socket as non blocked + conn.sock.setblocking(False) + + # register the connection + conn.set_timeout() + with self._lock: + self._keep.append(conn) + + # add the socket to the event loop + self.poller.register(conn.sock, selectors.EVENT_READ, + partial(self.reuse_connection, conn)) + else: + self.nr_conns -= 1 + conn.close() + except: + # an exception happened, make sure to close the + # socket. + self.nr_conns -= 1 + fs.conn.close() + + def handle(self, conn): + keepalive = False + req = None + try: + req = six.next(conn.parser) + if not req: + return (False, conn) + + # handle the request + keepalive = self.handle_request(req, conn) + if keepalive: + return (keepalive, conn) + except http.errors.NoMoreData as e: + self.log.debug("Ignored premature client disconnection. %s", e) + + except StopIteration as e: + self.log.debug("Closing connection. %s", e) + except ssl.SSLError as e: + if e.args[0] == ssl.SSL_ERROR_EOF: + self.log.debug("ssl connection closed") + conn.sock.close() + else: + self.log.debug("Error processing SSL request.") + self.handle_error(req, conn.sock, conn.client, e) + + except EnvironmentError as e: + if e.errno not in (errno.EPIPE, errno.ECONNRESET): + self.log.exception("Socket error processing request.") + else: + if e.errno == errno.ECONNRESET: + self.log.debug("Ignoring connection reset") + else: + self.log.debug("Ignoring connection epipe") + except Exception as e: + self.handle_error(req, conn.sock, conn.client, e) + + return (False, conn) + + def handle_request(self, req, conn): + environ = {} + resp = None + try: + self.cfg.pre_request(self, req) + request_start = datetime.now() + resp, environ = wsgi.create(req, conn.sock, conn.client, + conn.server, self.cfg) + environ["wsgi.multithread"] = True + self.nr += 1 + if self.alive and self.nr >= self.max_requests: + self.log.info("Autorestarting worker after current request.") + resp.force_close() + self.alive = False + + if not self.cfg.keepalive: + resp.force_close() + elif len(self._keep) >= self.max_keepalived: + resp.force_close() + + respiter = self.wsgi(environ, resp.start_response) + try: + if isinstance(respiter, environ['wsgi.file_wrapper']): + resp.write_file(respiter) + else: + for item in respiter: + resp.write(item) + + resp.close() + request_time = datetime.now() - request_start + self.log.access(resp, req, environ, request_time) + finally: + if hasattr(respiter, "close"): + respiter.close() + + if resp.should_close(): + self.log.debug("Closing connection.") + return False + except EnvironmentError: + # pass to next try-except level + six.reraise(*sys.exc_info()) + except Exception: + if resp and resp.headers_sent: + # If the requests have already been sent, we should close the + # connection to indicate the error. + self.log.exception("Error handling request") + try: + conn.sock.shutdown(socket.SHUT_RDWR) + conn.sock.close() + except EnvironmentError: + pass + raise StopIteration() + raise + finally: + try: + self.cfg.post_request(self, req, environ, resp) + except Exception: + self.log.exception("Exception in post_request hook") + + return True diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/gtornado.py b/venv/lib/python3.6/site-packages/gunicorn/workers/gtornado.py new file mode 100644 index 0000000..7c1b118 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/gtornado.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import copy +import os +import sys + +try: + import tornado +except ImportError: + raise RuntimeError("You need tornado installed to use this worker.") +import tornado.web +import tornado.httpserver +from tornado.ioloop import IOLoop, PeriodicCallback +from tornado.wsgi import WSGIContainer +from gunicorn.workers.base import Worker +from gunicorn import __version__ as gversion + + +# `io_loop` arguments to many Tornado functions have been removed in Tornado 5.0 +# +IOLOOP_PARAMETER_REMOVED = tornado.version_info >= (5, 0, 0) + + +class TornadoWorker(Worker): + + @classmethod + def setup(cls): + web = sys.modules.pop("tornado.web") + old_clear = web.RequestHandler.clear + + def clear(self): + old_clear(self) + if "Gunicorn" not in self._headers["Server"]: + self._headers["Server"] += " (Gunicorn/%s)" % gversion + web.RequestHandler.clear = clear + sys.modules["tornado.web"] = web + + def handle_exit(self, sig, frame): + if self.alive: + super(TornadoWorker, self).handle_exit(sig, frame) + + def handle_request(self): + self.nr += 1 + if self.alive and self.nr >= self.max_requests: + self.log.info("Autorestarting worker after current request.") + self.alive = False + + def watchdog(self): + if self.alive: + self.notify() + + if self.ppid != os.getppid(): + self.log.info("Parent changed, shutting down: %s", self) + self.alive = False + + def heartbeat(self): + if not self.alive: + if self.server_alive: + if hasattr(self, 'server'): + try: + self.server.stop() + except Exception: + pass + self.server_alive = False + else: + if not self.ioloop._callbacks: + self.ioloop.stop() + + def run(self): + self.ioloop = IOLoop.instance() + self.alive = True + self.server_alive = False + if IOLOOP_PARAMETER_REMOVED: + PeriodicCallback(self.watchdog, 1000).start() + PeriodicCallback(self.heartbeat, 1000).start() + else: + PeriodicCallback(self.watchdog, 1000, io_loop=self.ioloop).start() + PeriodicCallback(self.heartbeat, 1000, io_loop=self.ioloop).start() + + # Assume the app is a WSGI callable if its not an + # instance of tornado.web.Application or is an + # instance of tornado.wsgi.WSGIApplication + app = self.wsgi + if not isinstance(app, tornado.web.Application) or \ + isinstance(app, tornado.wsgi.WSGIApplication): + app = WSGIContainer(app) + + # Monkey-patching HTTPConnection.finish to count the + # number of requests being handled by Tornado. This + # will help gunicorn shutdown the worker if max_requests + # is exceeded. + httpserver = sys.modules["tornado.httpserver"] + if hasattr(httpserver, 'HTTPConnection'): + old_connection_finish = httpserver.HTTPConnection.finish + + def finish(other): + self.handle_request() + old_connection_finish(other) + httpserver.HTTPConnection.finish = finish + sys.modules["tornado.httpserver"] = httpserver + + server_class = tornado.httpserver.HTTPServer + else: + + class _HTTPServer(tornado.httpserver.HTTPServer): + + def on_close(instance, server_conn): + self.handle_request() + super(_HTTPServer, instance).on_close(server_conn) + + server_class = _HTTPServer + + if self.cfg.is_ssl: + _ssl_opt = copy.deepcopy(self.cfg.ssl_options) + # tornado refuses initialization if ssl_options contains following + # options + del _ssl_opt["do_handshake_on_connect"] + del _ssl_opt["suppress_ragged_eofs"] + if IOLOOP_PARAMETER_REMOVED: + server = server_class(app, ssl_options=_ssl_opt) + else: + server = server_class(app, io_loop=self.ioloop, + ssl_options=_ssl_opt) + else: + if IOLOOP_PARAMETER_REMOVED: + server = server_class(app) + else: + server = server_class(app, io_loop=self.ioloop) + + self.server = server + self.server_alive = True + + for s in self.sockets: + s.setblocking(0) + if hasattr(server, "add_socket"): # tornado > 2.0 + server.add_socket(s) + elif hasattr(server, "_sockets"): # tornado 2.0 + server._sockets[s.fileno()] = s + + server.no_keep_alive = self.cfg.keepalive <= 0 + server.start(num_processes=1) + + self.ioloop.start() diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/sync.py b/venv/lib/python3.6/site-packages/gunicorn/workers/sync.py new file mode 100644 index 0000000..1d2ce2f --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/sync.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. +# + +from datetime import datetime +import errno +import os +import select +import socket +import ssl +import sys + +import gunicorn.http as http +import gunicorn.http.wsgi as wsgi +import gunicorn.util as util +import gunicorn.workers.base as base +from gunicorn import six + +class StopWaiting(Exception): + """ exception raised to stop waiting for a connnection """ + +class SyncWorker(base.Worker): + + def accept(self, listener): + client, addr = listener.accept() + client.setblocking(1) + util.close_on_exec(client) + self.handle(listener, client, addr) + + def wait(self, timeout): + try: + self.notify() + ret = select.select(self.wait_fds, [], [], timeout) + if ret[0]: + if self.PIPE[0] in ret[0]: + os.read(self.PIPE[0], 1) + return ret[0] + + except select.error as e: + if e.args[0] == errno.EINTR: + return self.sockets + if e.args[0] == errno.EBADF: + if self.nr < 0: + return self.sockets + else: + raise StopWaiting + raise + + def is_parent_alive(self): + # If our parent changed then we shut down. + if self.ppid != os.getppid(): + self.log.info("Parent changed, shutting down: %s", self) + return False + return True + + def run_for_one(self, timeout): + listener = self.sockets[0] + while self.alive: + self.notify() + + # Accept a connection. If we get an error telling us + # that no connection is waiting we fall down to the + # select which is where we'll wait for a bit for new + # workers to come give us some love. + try: + self.accept(listener) + # Keep processing clients until no one is waiting. This + # prevents the need to select() for every client that we + # process. + continue + + except EnvironmentError as e: + if e.errno not in (errno.EAGAIN, errno.ECONNABORTED, + errno.EWOULDBLOCK): + raise + + if not self.is_parent_alive(): + return + + try: + self.wait(timeout) + except StopWaiting: + return + + def run_for_multiple(self, timeout): + while self.alive: + self.notify() + + try: + ready = self.wait(timeout) + except StopWaiting: + return + + if ready is not None: + for listener in ready: + if listener == self.PIPE[0]: + continue + + try: + self.accept(listener) + except EnvironmentError as e: + if e.errno not in (errno.EAGAIN, errno.ECONNABORTED, + errno.EWOULDBLOCK): + raise + + if not self.is_parent_alive(): + return + + def run(self): + # if no timeout is given the worker will never wait and will + # use the CPU for nothing. This minimal timeout prevent it. + timeout = self.timeout or 0.5 + + # self.socket appears to lose its blocking status after + # we fork in the arbiter. Reset it here. + for s in self.sockets: + s.setblocking(0) + + if len(self.sockets) > 1: + self.run_for_multiple(timeout) + else: + self.run_for_one(timeout) + + def handle(self, listener, client, addr): + req = None + try: + if self.cfg.is_ssl: + client = ssl.wrap_socket(client, server_side=True, + **self.cfg.ssl_options) + + parser = http.RequestParser(self.cfg, client) + req = six.next(parser) + self.handle_request(listener, req, client, addr) + except http.errors.NoMoreData as e: + self.log.debug("Ignored premature client disconnection. %s", e) + except StopIteration as e: + self.log.debug("Closing connection. %s", e) + except ssl.SSLError as e: + if e.args[0] == ssl.SSL_ERROR_EOF: + self.log.debug("ssl connection closed") + client.close() + else: + self.log.debug("Error processing SSL request.") + self.handle_error(req, client, addr, e) + except EnvironmentError as e: + if e.errno not in (errno.EPIPE, errno.ECONNRESET): + self.log.exception("Socket error processing request.") + else: + if e.errno == errno.ECONNRESET: + self.log.debug("Ignoring connection reset") + else: + self.log.debug("Ignoring EPIPE") + except Exception as e: + self.handle_error(req, client, addr, e) + finally: + util.close(client) + + def handle_request(self, listener, req, client, addr): + environ = {} + resp = None + try: + self.cfg.pre_request(self, req) + request_start = datetime.now() + resp, environ = wsgi.create(req, client, addr, + listener.getsockname(), self.cfg) + # Force the connection closed until someone shows + # a buffering proxy that supports Keep-Alive to + # the backend. + resp.force_close() + self.nr += 1 + if self.nr >= self.max_requests: + self.log.info("Autorestarting worker after current request.") + self.alive = False + respiter = self.wsgi(environ, resp.start_response) + try: + if isinstance(respiter, environ['wsgi.file_wrapper']): + resp.write_file(respiter) + else: + for item in respiter: + resp.write(item) + resp.close() + request_time = datetime.now() - request_start + self.log.access(resp, req, environ, request_time) + finally: + if hasattr(respiter, "close"): + respiter.close() + except EnvironmentError: + # pass to next try-except level + six.reraise(*sys.exc_info()) + except Exception: + if resp and resp.headers_sent: + # If the requests have already been sent, we should close the + # connection to indicate the error. + self.log.exception("Error handling request") + try: + client.shutdown(socket.SHUT_RDWR) + client.close() + except EnvironmentError: + pass + raise StopIteration() + raise + finally: + try: + self.cfg.post_request(self, req, environ, resp) + except Exception: + self.log.exception("Exception in post_request hook") diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/workertmp.py b/venv/lib/python3.6/site-packages/gunicorn/workers/workertmp.py new file mode 100644 index 0000000..36bc97a --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/workertmp.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import os +import platform +import tempfile + +from gunicorn import util + +PLATFORM = platform.system() +IS_CYGWIN = PLATFORM.startswith('CYGWIN') + + +class WorkerTmp(object): + + def __init__(self, cfg): + old_umask = os.umask(cfg.umask) + fdir = cfg.worker_tmp_dir + if fdir and not os.path.isdir(fdir): + raise RuntimeError("%s doesn't exist. Can't create workertmp." % fdir) + fd, name = tempfile.mkstemp(prefix="wgunicorn-", dir=fdir) + + # allows the process to write to the file + util.chown(name, cfg.uid, cfg.gid) + os.umask(old_umask) + + # unlink the file so we don't leak tempory files + try: + if not IS_CYGWIN: + util.unlink(name) + self._tmp = os.fdopen(fd, 'w+b', 1) + except: + os.close(fd) + raise + + self.spinner = 0 + + def notify(self): + try: + self.spinner = (self.spinner + 1) % 2 + os.fchmod(self._tmp.fileno(), self.spinner) + except AttributeError: + # python < 2.6 + self._tmp.truncate(0) + os.write(self._tmp.fileno(), b"X") + + def last_update(self): + return os.fstat(self._tmp.fileno()).st_ctime + + def fileno(self): + return self._tmp.fileno() + + def close(self): + return self._tmp.close()