From b60fa3214b20e549839f3385d55aa4660ea9117a Mon Sep 17 00:00:00 2001 From: brad stein Date: Sun, 21 Oct 2018 23:32:02 -0500 Subject: [PATCH] Added gunicorn to requirements.txt --- requirements.txt | 1 + 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 -> 379 bytes .../__pycache__/_compat.cpython-36.pyc | Bin 0 -> 7676 bytes .../__pycache__/arbiter.cpython-36.pyc | Bin 0 -> 16649 bytes .../argparse_compat.cpython-36.pyc | Bin 0 -> 58935 bytes .../__pycache__/config.cpython-36.pyc | Bin 0 -> 60005 bytes .../gunicorn/__pycache__/debug.cpython-36.pyc | Bin 0 -> 1963 bytes .../__pycache__/errors.cpython-36.pyc | Bin 0 -> 1017 bytes .../__pycache__/glogging.cpython-36.pyc | Bin 0 -> 11427 bytes .../__pycache__/pidfile.cpython-36.pyc | Bin 0 -> 2236 bytes .../__pycache__/reloader.cpython-36.pyc | Bin 0 -> 3958 bytes .../__pycache__/selectors.cpython-36.pyc | Bin 0 -> 17392 bytes .../gunicorn/__pycache__/six.cpython-36.pyc | Bin 0 -> 22117 bytes .../gunicorn/__pycache__/sock.cpython-36.pyc | Bin 0 -> 5888 bytes .../__pycache__/systemd.cpython-36.pyc | Bin 0 -> 1353 bytes .../gunicorn/__pycache__/util.cpython-36.pyc | Bin 0 -> 11849 bytes .../site-packages/gunicorn/_compat.py | 298 +++ .../site-packages/gunicorn/app/__init__.py | 4 + .../app/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 142 bytes .../app/__pycache__/base.cpython-36.pyc | Bin 0 -> 6023 bytes .../app/__pycache__/pasterapp.cpython-36.pyc | Bin 0 -> 5616 bytes .../app/__pycache__/wsgiapp.cpython-36.pyc | Bin 0 -> 1999 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 -> 308 bytes .../http/__pycache__/_sendfile.cpython-36.pyc | Bin 0 -> 1340 bytes .../http/__pycache__/body.cpython-36.pyc | Bin 0 -> 6419 bytes .../http/__pycache__/errors.cpython-36.pyc | Bin 0 -> 5838 bytes .../http/__pycache__/message.cpython-36.pyc | Bin 0 -> 8705 bytes .../http/__pycache__/parser.cpython-36.pyc | Bin 0 -> 1303 bytes .../http/__pycache__/unreader.cpython-36.pyc | Bin 0 -> 2425 bytes .../http/__pycache__/wsgi.cpython-36.pyc | Bin 0 -> 9234 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 -> 149 bytes .../__pycache__/statsd.cpython-36.pyc | Bin 0 -> 4471 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 -> 636 bytes .../__pycache__/_gaiohttp.cpython-36.pyc | Bin 0 -> 4883 bytes .../workers/__pycache__/base.cpython-36.pyc | Bin 0 -> 7497 bytes .../__pycache__/base_async.cpython-36.pyc | Bin 0 -> 3846 bytes .../__pycache__/gaiohttp.cpython-36.pyc | Bin 0 -> 728 bytes .../__pycache__/geventlet.cpython-36.pyc | Bin 0 -> 4628 bytes .../__pycache__/ggevent.cpython-36.pyc | Bin 0 -> 6664 bytes .../__pycache__/gthread.cpython-36.pyc | Bin 0 -> 8755 bytes .../__pycache__/gtornado.cpython-36.pyc | Bin 0 -> 4061 bytes .../workers/__pycache__/sync.cpython-36.pyc | Bin 0 -> 5079 bytes .../__pycache__/workertmp.cpython-36.pyc | Bin 0 -> 1726 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, 12268 insertions(+) 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/requirements.txt b/requirements.txt index 8c8a88f..58abcd2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,7 @@ channels-redis==2.3.1 constantly==15.1.0 daphne==2.2.2 Django==2.1.2 +gunicorn==19.9.0 hiredis==0.2.0 hyperlink==18.0.0 idna==2.7 diff --git a/venv/bin/gunicorn b/venv/bin/gunicorn new file mode 100755 index 0000000..873bb31 --- /dev/null +++ b/venv/bin/gunicorn @@ -0,0 +1,11 @@ +#!/home/brad/Development/ChannelsDrawShare/venv/bin/python + +# -*- 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..84e8236 --- /dev/null +++ b/venv/bin/gunicorn_paster @@ -0,0 +1,11 @@ +#!/home/brad/Development/ChannelsDrawShare/venv/bin/python + +# -*- 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..dad058f --- /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=JXkdPLeZv45f4DbL1j-1sPTWg2MraQohFJjId0L-E-8,261 +../../../bin/gunicorn_paster,sha256=4eoTdzH3KoCeWGFp5FQx8TYtK2JiQjFwaKyuQHXcMIg,263 +gunicorn-19.9.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +gunicorn/instrument/__pycache__/statsd.cpython-36.pyc,, +gunicorn/instrument/__pycache__/__init__.cpython-36.pyc,, +gunicorn/workers/__pycache__/base_async.cpython-36.pyc,, +gunicorn/workers/__pycache__/geventlet.cpython-36.pyc,, +gunicorn/workers/__pycache__/gthread.cpython-36.pyc,, +gunicorn/workers/__pycache__/gtornado.cpython-36.pyc,, +gunicorn/workers/__pycache__/workertmp.cpython-36.pyc,, +gunicorn/workers/__pycache__/_gaiohttp.cpython-36.pyc,, +gunicorn/workers/__pycache__/base.cpython-36.pyc,, +gunicorn/workers/__pycache__/sync.cpython-36.pyc,, +gunicorn/workers/__pycache__/ggevent.cpython-36.pyc,, +gunicorn/workers/__pycache__/__init__.cpython-36.pyc,, +gunicorn/workers/__pycache__/gaiohttp.cpython-36.pyc,, +gunicorn/http/__pycache__/wsgi.cpython-36.pyc,, +gunicorn/http/__pycache__/errors.cpython-36.pyc,, +gunicorn/http/__pycache__/message.cpython-36.pyc,, +gunicorn/http/__pycache__/body.cpython-36.pyc,, +gunicorn/http/__pycache__/unreader.cpython-36.pyc,, +gunicorn/http/__pycache__/parser.cpython-36.pyc,, +gunicorn/http/__pycache__/__init__.cpython-36.pyc,, +gunicorn/http/__pycache__/_sendfile.cpython-36.pyc,, +gunicorn/__pycache__/util.cpython-36.pyc,, +gunicorn/__pycache__/sock.cpython-36.pyc,, +gunicorn/__pycache__/_compat.cpython-36.pyc,, +gunicorn/__pycache__/glogging.cpython-36.pyc,, +gunicorn/__pycache__/config.cpython-36.pyc,, +gunicorn/__pycache__/errors.cpython-36.pyc,, +gunicorn/__pycache__/six.cpython-36.pyc,, +gunicorn/__pycache__/pidfile.cpython-36.pyc,, +gunicorn/__pycache__/arbiter.cpython-36.pyc,, +gunicorn/__pycache__/reloader.cpython-36.pyc,, +gunicorn/__pycache__/argparse_compat.cpython-36.pyc,, +gunicorn/__pycache__/__init__.cpython-36.pyc,, +gunicorn/__pycache__/systemd.cpython-36.pyc,, +gunicorn/__pycache__/selectors.cpython-36.pyc,, +gunicorn/__pycache__/debug.cpython-36.pyc,, +gunicorn/app/__pycache__/wsgiapp.cpython-36.pyc,, +gunicorn/app/__pycache__/base.cpython-36.pyc,, +gunicorn/app/__pycache__/__init__.cpython-36.pyc,, +gunicorn/app/__pycache__/pasterapp.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..9c32403e154c86e72a47fa54d57d49ce3da49f08 GIT binary patch literal 379 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 NP#yROr6#J){lECNG`#=- literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/argparse_compat.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/argparse_compat.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4142fe748076e8a1855c257331b5c7c9ca73b9e3 GIT binary patch literal 58935 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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..175b4c384d4cf61f7cf9db2850df7fb373486ca2 GIT binary patch literal 1963 zcmZuy&5ztP6u0x8%3?YpY7lK%tsp=(QgNT-uovY4>LT44Y>v5OBiw!NK0Fikk$eUH^CXY zKeTjDi8SL$l^$`advaDECy&x{!R62fe7u|p7tRU~$nQje7s&IEFRP>Ga+5c^U;&ku z#VJnUtMSZ$~)OsZsE0bO;lZm}e)|8^`K>pQ&2c~2ds@ICm)`wT4 z#|Le6gFN_p)oXj5Zg9tD8y2s#s9R}!mk7RUZb6wX(EsTzM?Tn#;~&0>F(|~sBT5J96pn$j^7g(uSqyG=BCkwBbb^OcI1q z)jFIc)3P3?rPKkhm^{waOozCcrP+*Yf6A3X?874s&y*g(imVqRp`OGIj<7gKuU9=T{F>M+B5*hz^pUG z<`i{a4g11J3%L^k2GA~l#Nk3a6;thKK zwSPEt1?HHJlBBC^Nuv8n@^F!sozq1G+hP|9X8t*X00k!N1sEoq11oaO%FPVR4mRW{ zW0LqHOcW`of&v6-W?>MU(q%21+g%-r)7Vs_cvjc*_;{9Qv-ntmFC+w2COFH-MOT|EH&m7o?;%3Veh%yN^8VjI7 zKD1sIyZ`Hox_kyh8s(e}rY{;U86DA@s3U_i@Y;fewVPE@6!s;wZy2EH}Z3!;6aRPfLjxZUH8nB(f rFN*f+aRE7GR2Qg&z^h;NrVg%l0rCZgyxaofSOGQN0>|5%??wK9W{&X* literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/errors.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/errors.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..206cfd58ac03bf5ed3f92b0e686cbe70974b3092 GIT binary patch literal 1017 zcmb7B!EVz)5Z$$%#7UYG6@)k-WFht7LlY$6R#ki_YzZ_!w>k%iC^Hv%v#uy+6ybq?7rQZw{K>iP9~%8Z-2h~<}&t+?JN(^r)cIGx>rmw z#lNCy_QhRr#ExcDuGqp+1Ixn~Zj--g zGW~k=T4vRShL0M4b4-XSofhp5+8LUeqTBK<6Kw0CM@sybN1;>qK}%EQ^;lo0l`xg8 z)+Tgo&**Fk!~s;_4+eeQ{yd*ot8!kZpYDM$h(6ED8S6h z&$TZ^nj^g+9fk-Q@AJ*wPW$X%L`+4{qMiP4^()kW$12l-dPrflH*SEi9tbQHgt&*mP>&ej9CSpe#Xd)vPOo8)3drS33sH}RSQToW(RojZ ztF_FU7w|C@hUBPKJC6PYD(4QGt={J&&)&p%(=G}*MYGdt2Tvn+uv)hspB4EsP0p#U zo5#WVb*#%OE%E?lYP1UQ6x)cvc0Pzg4@SrbV{&`wsB%@rLhPusVy(mpMm#{XE%7-J z{x5<1FUs=Gsw|*tG|p~n{PbSuK~_kW=1Cy)K$c~e#?qE?Q!nhJgj=;zDtTnfB%s*aNNfMN8SP0ssOfO({RL(g)gb@? 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..b2704739e20c26181970e51d93a6a27ac72c5573 GIT binary patch literal 11427 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 AumAu6 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..5a547b0390481abed73b2d1b6f3958acddc60483 GIT binary patch literal 3958 zcmaJ^-)|d99iN%~>9ylHZZCHSM=@|5GP+C49dP9+rL;*z2{t5mAUU=MBp87A`3w*w_Yqv?ct~E2?otd4P@2}5i{L#{q z_qXkTefHlbWB*|DvV1Q#l6umJ>=~DTK7I#ZIqA}{={I?~!?NFsT;IFTvWq5*R}MV+ zN6zT?kY(Zli#_>wtXqpbtm{?lwzC%c>Q&zb?9|4*f5z=h#5Mn7d@-)Y?Wplr!+#^< z_gHu7IrL(^6K8zY_G7k?~M8f#mdX$K}uaUtd+;|Nti>qT^ z!c=j(Thw1(&t1%P7ZW+Y3i@fNR1o}wefja_d^o;59*jR2WGcsMAMB|shkJVm$>qIC zHt3IJ_Ck=A3D?HQ(t=_uD2if}@h10lvH8i$+|p}vwfkfRdZ?|T+(J>`Mg{Y-FWTD7 zJP}iE==+Gf->_}=04>bPdtF1;F;}{%3L}i7u8_zO*ppz{7` zIKKW(Iu04f=7OKRw=fR2D8q8?8saWY)pZE-kGf7_<&$w5W5Z)rIOQ&?uo0G%SZRM1 zHexZ(BJH=i%j{k9mpbpT+G4kF&~ZFeOxV0_x_nifv|l~c!m1R96oNXW{s3XXPWjRA zjxq|Z8Cz$M9-oTi7TI+Q5KN3y15mbVBA)?epOjGcv{ix`>~7JlQ*#RVJaDCtyyjDJ zjUBcQTae8IQ?|&}WleKA#_qG+NnG^T^t(+#P=>B2$~m`E`BD8jrWtr!nE{}(UXLQy{=0!N1g&^c|lOR7jh#A4kuw+j%W;!Z{h>3 zq`VBNSgO#aAE#*$yf|8y%QnrV{M4w}f}kj+UEXwE({1S9&B96t+2p9p3+Hw=$_Km0 zJAF)AJagPu8M@|d017h`OV{>Irdy|K-Kwostyi`6o6K)So@Nmd8P96|Qe;LJ*rOTQ zk%M+Qa-$mBmS&L^__JOtS7(}At=A`e(Q}4Jeua*&;t|akW^QTru=9#Vj#j4tl}F7} z@JIqS(Q)*{DvpRzJV`r9;=t1G1SL9Zqywq2f@mO>{1M4+@HEW(`=B3kPS?~iB$w#} zf^1O!f^_C?SzxDL{0?R*g4hz+ZHgJ;@sqdCssAd%lR4<1w@}nIR5?p3WIsg$LOJ8W zmS3avfDEVUK0D%n5+&ItPMK5{l_CzzVWYyX!QEZLz|zeN=hy!;MT6rmLZ$nR3e zSqBvEe9ZuZiXyv2|5pQUK5>En6l}x++V;PNfUpAY<2J^RI*=)=Ry9?Fx`E{gx-!@f z+xOYg8s0^MT*Ryn1*k^-&BLpZ#M?|~qg`d=)hZi}9+KuK*y%a>yz9uHqmhe9loa*y zXQ=#I8HjsvrW19Zh!lc_rN_#cJZlY&qx+bn2qGExL<1z)LJkVyijxcH{XxJjdcAI2 zUcq$v1FAkmRXBqZK#N*`M3=iPSAOkwFSxb6@r#`eEv|3e*?`zYZz%@%HtuZdp}Sk_ zw>Ka94Gi7g`gEflSgPLEgU#)&yMBG~y0fKKeu)yaRfrPgM^r6zoFk4V0)n$m6rFjl zc~vw$x8~L~%=OW~c*xW@P!uuX9QFa&1l=5e04S*}{aNqKZIkYDIy8t)bGl^l-{Z1{ z*`jx{`EWGp3@0k@?1xX{PQD*^DwB0e#>1<=6vt5~A9W_`>>OELhk;S`+r#ll=9_0~hVE-}+c3q&4_kJ_5a|6M DS88yQ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/__pycache__/selectors.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/__pycache__/selectors.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..661b3212d6b9719daec8486cab29afbfd090f4cc GIT binary patch literal 17392 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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..24571097c4e3c4a1ae595c3a22104006d27aa222 GIT binary patch literal 22117 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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..b0e97dd85e1f710eeb37d54bfabd385aa963aeef GIT binary patch literal 5888 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 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..6dc20f65f2eaa84b2328329324774c8f9338ed71 GIT binary patch literal 1353 zcmah}&2AGh5Z<4ptwLKtxgbtHaEa7rkw6@xssi-~RjOLl1W|=U)>+RcZg%a(b{Ykh zQ+W^0JPa?eS5AEePKaM)E1=Qd7#g2p(;J@rm~<}&ZX zyKuj`bB|wnymJrF)GnN{uU<2+~+^g*0) zB}EFtv`LfzRrC8dw#XS(oDLGucnz&4do2KL=GcV6tN^=;y|>e;3jb*Y;ufWpnrMVI zy+dZbtY+zdunR?isuT8d7~bn<7?L=lLz{LO7TpTNibtzuD*^3x0b{lGQ6zi}1rN>o z2}c2me#|u4Ch$yV4eCDdzFI>BBk`^e`G6wYCDA@@;jxhHkLShXYf zhhXvrG$@|j&t%s0dr$5)A@{L}y+&0~Jo?j74aEQRCNOChWIWs8LK~VS8$k@aGs`8H z98zb7+Fh3GT-Hew7fFL4Knt`$pV~AR6etQ5MNyz=(7yDNqCpX)^N=Df`k*F2uTMgY z_WS-Jsnt3~x)RShb8hpWbNQ%IU~7Sb|uf2vyyvdPx6WKgyeqNmwd83DS57(lYFW?CHZuD zTJl}xU6Rj~XC&WU-YxlTc~Z+WkjGp&8?{pI~qc3TJ9 zkCY!Vjo@J5s;u(1?DC_*V``%L_?EF{mh&6?Fq7Xp)IL-`gb|Y}_pVWX;+7FU<`}_~ ztNvF_J!=~Lr>4@jH;mAF-Pj;y)2*l4^W}NdK>s7-{#Ssz*`tOfcDew)vcTY|F&^zpien$5kHfN7W(q z1lo(C8@SDda#200p2G8jnpaOFKc-$+M^phlkE>_Yv#2?to>NDWKd*{v0r>}n?qe;t z{X+Q#q5J+i?+xibu1=um3qxw3S0BLBN%e+$L7l|RA5>-aLG>a^FRBlzQ)v5;`mj2U z{FM5LI)nVf>P>Z4okNe)>Lqm^H6KxLsSD~NYR;%dbqO_R)ywJ?Z9saw7jIQ zsE?uMyeg@y$Sf^{Ss_W_o@OWp3}cDJMBMXfs;H4|~L9#`UxZh(i~H(tAT?dtWLix(@mu3mq2@%oJuRGF=!k_dS`PHlAgpy%Bdq6p-MX%-Kzc@jz7y!Y!ff&kQgcxd<6U@v+zM(Y zSC2pDHhvfIi)N6-5Yd4#2bLz=W23B6KUeRCjao;Cr}Fbr!PFl5VaAM1^-V+jc)s`i zLfq~ybQ|5HjWCL+E4ZmOL#f?_6yOB&)wo`S2L_ke|ES*F80F|Uv zsB};e%FL3y83awBO3G<6$wyA`0)E2@LGM&U4zkkjs2;X}%jTbpA3MwB>nOy=z*Odz z70t$`vbN06nXxr6x6Dm@{gv1rSjtutTlPic?H4zljoE?Obd;kq1AEKfv~L-=A-}P? z>g!JqrNG_H#O5HwdEP+l>-gO=R2J{>2KMKz{l>spw&u9ie*fh4Anxff4`#?$m!eLq zhcgeW?I7P-mdcd&eB8+|1^IM6fhs00cvuDN{YMXXtF_f?Jvf{XJ8>SH%dXXY6r)4^ zF844oi<)`=L^Y0sb~g@GUI#638JIDh5p!>JLWQB-qpjdh(8>>I?Punr;(S!P`#<*^#T!*#BK#)k^y_x13irw72L$(5|Zq2mLwySCbsItiCIbPc2wVS zdSDD4VEK1~3WuJ?G|?U;hUJ?c{>_>ee$%FdR3AirP1ptnatD8Mnx8^pU^KzfTc$1z z%xG?OqRQU1)*l-fIK{b5dtfVb)z-yp22R)cps{J)&fhX{>U_?~8JPnM=WF2nMKofk znHd=Sd1`L|d#Q%NKBvc1^!=MS!24%W)%9K*;+=0q*z4Q9hQ_9^LE!V@p>Q)^JV-ZjxanQtl;5C_OQ&H zxd*xQxtAYlpW<3auR?Veqm8guyn*DFRNR}{*<-94s7Pk<6J}~cYGR`a?FeR?9^mM~ zT9&ZZ*H8irZ9!e^H#Y5!Jp&s`!G`j%-m*5G%?!-?L8yfc)WRoHwJ@+Zoq@TT8G?Z6 z*llJ9&cK4Aux|)VFpb!2PHY*Qu5vcBu|LRy9bKx5RFx1sgnuhm$W3RW$c z`nmO{P-lbI?+yU9ltDJ$1kG`7)zwLyBm07C8GFsx5DJtF3XK0R1-HKgW%iw0#=vV% zsVr!i*qDu{Ri?R%l;GJXoJrUF^Vbs34E({w7L2oKcdyiUf_nXwu+%;C$$p{Mszy=1 zryKcsoo|G9U_VtJd{0fc-06ktRNnXBoR8iX22Q-uvFhD8!ouz(3&R9yicyGUF8wGP zl1$WXHDahx$@N35o9eFCMFaJ8Er^ntR;Q{e5MaGg5h()-09yu2P_~Ir0j|cCV7->) zuJ>TX+e2=r#?mw|iQVmVg&h;82`Npo^&qC=EKDXg7DUjnTov432kgiTMt<5J3D-uGxdit-vf4*)m{2mQZpD6->;kyLf`k3X+0M7o2`UEV3-N3j12uonDugeW;m;1;K6+ zl>Ma!EbOOC1y5Fzc&`Qc zWXT)t)y|FFjgghkS{Ufckch9Ne{=weVLMr_>O1)xT8=o#gF$k@!>J_hsvg$`6` z?u8mx@EE^r57+ILL_gL4>zn`f{f{kOJ6i{7`1{whcPTpkLL5YKly6rz^2@;b&*fFn z3V;G5KxP23AQZ4qvAN5|LojG-Ypa5(3KO!AnqsaCNRn&|%0o4vMW~mc>{@U=5^t@l zLx7Gbaac;GhkEN)T3yH{4jrPz<1tW9%h|PRgQy;=sv(k_Dx@rD?eu`-uIxCY#H(-! z2>o5q5fP*rmItnJtr^qHLIJ^O&zcU@4YCY2)WCTdP@p;&X7{-vD_EJ>D^4onfIz#AY<#m1M8pNaZ*KNE3Dvcd>MJv2-)Z zBENa@^7VqHsizY*cisnj-bT|8@rx)9hREcM1q96c2lkFaBFi|30*o^F96y?N>|YvV zAXfv949&ALZ$Nls2Q{Zg>hFdD@~OD((xsa~`)<^!tp;&2(Fma|x^X8=yq7LjE|(T> zp419265EL*aH>k}q@6~$(&*kf`D1j9=ogUwS#t)ug{DV0P;>X(<5&pKfq!K0F%$d) z9=Hs8r(2dj$mOJEHAxsQIEZNH10&ocSjwyMK&|nhsEw6`p`o z#y$h-13be%!?rnKC~wu$@2Ls&^w2}bjAzRcTMftmARSNim*PR7LD+}rFJo8v13SfU zaaL4{9TEk?sT|nL-0G=P--olke(K1=LgB1npS~N{x~CQv?wN1iGv5}Nea}3V82#+A zqWtSWMm!V=4yxu;Sj31$KJ2xZ0$mjRlY$^Gu{4wb;7V;p^urJWwK{8o<`Jnzy%FmL zY<6P9n1UIBWCyua73a5;ii6)yYDRBh#E8%;@Ku`gtp^3|s`k7w5eDuY>GynRO z{~wq)HC$6ZJ?us#fwr&~Xu_Ptl}KQq#p^iE8aNZ7g3SgF{0yHR!~?6?^Oat>T8SEc zXaXtkII?7NO*i78B5jCCkIaovpTGp>{}Lt}!z>;be{x(0Q2 zAuJ#bxL*|);hPnMVQAa4Z#^`#QP1=$KZV*czXnVCLwYoX%ni3yDf{-?{QR_ z$%A~AOs9JcS(U-hb9t#%WPB@izosqy6%4I$dHmz@$B32?kBX4YH4q>|)=}24BS5g} zNDH20Y8kd?1`cv(*+FY2b|Ft0;8WC%5f#|&8+rVlUWwbSGZHrDLr_(t2Rus1_sske z9aN)EScpz9q&1`N3)}mjUh1fgZG%^ipE=!qU~u=$=>^mdN9XhNN7_MDFGS<%?$3aZ zW4fTQ-IRjF;edXNguin0=Cy_6#p4K4%|Iv-_Z>QtT)eVP5i z#w$Tsk5>@$R%v~j&coeev@|?k@Dh(h*L1a;xGGR|*(nO;Ns?L#qFS{ZAjB2xR493v z`duUiSNOh6R-Y5v4W=q z#At>{W*)sM6^c>-+li1g7=zIw{S>f`Z2`Xk*}ySU4nops*>9wEK*0o#rkI7&P}zPp zRkoNBp>LqB_u@>G5iIlrMshc8=sM~%4_9TxL7dYX#jZl@bHK?&$MHndXB-L)JK4-N zr;tvs!ubMZ-36wdA=83d7Y2#=dpFelzYpJ>(Lai3n-5`zJzK_CV6@_g5l{!Eqqn4Q z7~mpt)k8$gNBhKJ7(I*U1}?9}qJyaQ8BZIyFl>`0-NuHAn3pH3T?B@3A%p7|k^a7o zD~tZ|OT2+$a6G?^pnHB>FZgPRNNsd_`F4QF>f!n5)O>XKY#L8}=Kmg-?<_SD_DRE9 zNc;0Yw=cL-_n*S$)QG~-9PM5d^KPI9y21KXarq{wD+b3 zmfn-H@~CFWuyg~gQrW(C>mu|7-wj{|J&|OC@J>Tx0~zkAAx!2*G4>HoBy!v}!Z4ZD z#9T^jH160P{cX&%&TU~l+rW0rU_16<**Q30z%y(gYYyV>v7vby56}?is;6f_DgYvu z4OD}m2t6oUIbXJk#X)wae{S0aR;{NsRHEEM;-gIEC~wTzbX)`>9;T$#X0z=c(w zh6BzDT}>;Q0DP^sBx2zrWL0m&6VcZ}2pr=!5Kh6xS~r2xdU0&qW?xM^|65fHIe(6QTyR=z?K_!8W7{t?m#cAjj|JAWev ze^j*suLSK*IK1>`XtX*CiWbG5^AO<+udS^WqdU+9MTEf@RHr7_Yoe+b7#J_ohb|(L zi7W9oLP19_z4Y;gbwnx4LoPZvlSqbx+`v1Y8_6mT)sHN)}QN`3D+mmnY!J1na!w5LX7A zNY{p6Af&8F!V?d7kGOxV#XlqurjXNu7|Z}wQz!Hv8J$5;JS}1vohgpSlnT32dcMV} z97x}4lEFK9e*zpFAqJi4wX4yp_(zG;LA)}VyuOIY*5ZYmm77-+_iE+(#aq|)pI`#< z0>!{^35sDv=0G(C`5~AHFcj z_J(y2l{zs@bt~Z2aiCI@DoT(Bp!>jW2)9G_L4O~mf8q{^hJz=;D;}vGD;YBJIM7M< zhDR+bp1=@x=>%5BFBr}t)CrdD&&3F2!DAQ$xN(@%dWV;g_`%Y_1`ZupyQF^yv-Tes zl1~u`9Y%mEJRIlg_3&8pZ_ewtq0|2y{gUkEtLfqxcS=J8wuG<~b2%~J7s#Ehtpv5z z3bd*CP@L|kTrl^50fuSt@e`*0J5KGufqro;z0|l|yc}#11uk3sbh*!!CrD}ZRE*o) z^eY%3Low=Gv~O1bSvVBhPnA{P=R8_v!hi4T;tBbBq4$_P87^MOZIn^cLyL zuwNvOyXIX<>K0f@1~{r003r>)>e*>Srs7UThRSUJIbJr<3*F+JrD-(ACyjQ4H|4Tm z#!^P-98EK_D-j5VP=AZPgiFV)n)vv9q>^g8|K>_kWrUU3L~C;h59%Ld z*myOBi(^>Q!~;K8A{0^9m%*nvDDdE>iFlECy@(b)gL@s^?C_f%sOA?D3`Y1B&It@0 zOZUgh_a26F@7D;#fh-SsI$P@7hYlTLl#3Ln4#DKDm#&vCmtN*~QGFN9%pK$m&}0Pn zrug}LAQtyDylBQX1#Xmay$h3t-9Zdyxz&qSAPaJ@n@QWvM&huvW6Shwn8*hk%ePvH zK|-6&LdSW)u#j@Lj(2Os>^3SReoYa>;BT2DTSEa@oY;BhwkdTqz809eto}j64 ztrcUlIDE@ow(4TZzG&1b6K<)$|Ak}6@|U5ba52=%-$=hb6N$c;gS?DA?R&m>+TB*Q z5%#A?EvcK0u+Tr_b^0erQ0DEUIL#=33+3`OaGj2MCy!h22;bhNe!-ZT^f%D!0Cy(U zd%UsSWAzWTtE)kU7iksv1@{da9xw%E1ux*n%Xr!C(bQs9rXCo-m-90UW5A~5vihee zJ+R;8P?5D4Q6Mmo&qInSR}GB4sh0pMDIzh}!h*PsX~fAC zZ3x}L4;CcEfSX+1N6qsHVl&*gd zH4@#{f6o#(NI%Pj8cF{RlYmK{$=8@%XF~5$A7)Zza+ArgG3hX&Tardwc8xytCySa*}*UsDG8M6RCsQYAng8 z9P%-feE*=&vExUXyvmLqj)NDU?ex6fVnc-q@9E^CORuvuVDb)=Pc!)}6UJ60j3}W$ z3FwGZDh`@BD&jB*U=gb>c1}21l%?nZVN5y7Z7gt~54_W1$ 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..59ef2b0a0e2fdb755ce83221618a024d76516249 GIT binary patch literal 142 zcmXr!<>i_mbT+!4fq~&M5W@i@kmUfx#VkM~g&~+hlhJP_LlH+d2P3=y literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/app/__pycache__/base.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/app/__pycache__/base.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d37b75acebe93cb76502bf5ba16d018343611e0 GIT binary patch literal 6023 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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..94e3311ac01af6306ab2ae3edaf68fa375100819 GIT binary patch literal 308 zcmY+7F>b;z7=>*oBnUMU0~{iiNRYZlRWDG4=w=2hsx9NzPK@mgJq@zpafME` zMG@tf{Pe8<{oi{kipgQ~y`4hniqqf|n$i(Z z@Q7w_pv=!94@%_oS7vR?na?+DKQQY`=!-wQZjEKeppZ1N$fQA5WrpW`=)`+(x@zZ~ zuG9r_3*y;*Kwm)d+~q$(ODPv3vQQt&ObluE9qupXHAIc Ygd?QVUw+)Eo(_^dN0yB(Q}}>>0Yc4DSpWb4 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..1f0f4b82a021b77452e714738d1096238d125778 GIT binary patch literal 1340 zcmZuxPjB2r6rUN}<3GEbwh*C+3kw|39@-$NdZMUN6HufyiLya}i)3wf#;(0y+npJw z-OAyR%?SyKZ@{@9hA%KzPWcL4;Elb>7QtBWH*enjdB6AGjK3TVg5OX6c>dNy=uh-c z>x2FTL|y?f#Bh$nuE!y6$0Q_+%~rEQtL^R3`W_WqHsa1K5W5(ekJC&1BPz%bi1kC4 zlg#7p%oRUz%IFebVs2$VNRaiIyTr_6{;wqL^L{o6H(0=WS124XJVv9zH84SknzD&_ zl@{8K1?OjyX`hKnQWkN(&@@kr`t=uYMv@mSPIEp?W$~FB%DSpbp*S0s#ZbLS<*>>p zDlWxbJ?bQ8?!c*7!?wK^v+dez2&<&iV|^{7xGqHGsK| z>`nu@T9Qj_=D}*1xHI}27DMB4C5DR;!(z?JO57Vh;0yE>8>m_10w1^K-qe~^M4}t$8hhU z{=uOjPznK+K-UI@Wx$l{`fq3>vNj0cOc@y6)0SLF?cElfPSv8~QajTqEtK}AQ4Rg0 zhuS?p+CMzqJJIemIxkaJX+OHTN7|c+q}%5wTyFLHYP(9S`)MK7B+u_B^7EHTGRwA;x=5#`DAvjgrK;`Q z$R1P+Ah~)jAHiYr2>`+Y?&A%>0KZ2(?BPvJ@m-KUd}d@M0Nm>A7K};I?(krp_!Q0< z*m(dwy(2cE6I%w%BNOIdqkCOh9@Hw$b?+IUmtxVSv9~`rkGCC!?~lJbK0Z1* z-P?_h4|Yz!IXZbZ7Cm!-{dD)>K-=y9ew-F8%F{D35sT0<+O(x4?iq(BXYar#X1n** iykvFGzosyfLjV)CM+hKf!V0YRcZO|<(jy;WYW)M-!&#mH 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..f4a4aaeb624d6dc0e9d11f41242476f9cb96704f GIT binary patch literal 6419 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/errors.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/errors.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c039717f8b890099d6dfd0a00bcda8c4a51bb15 GIT binary patch literal 5838 zcmbW5-EP!I6vyqgvtP+3n}mFk1PqAMrGixDq7p@Pr9h!7ED?z+no_yTjK zN5oxufJ%LUUiV@80={jZqE|it@r>gQX0>ZeWA7Pz&-`b8GiN?-)oRs0pZ@*x?ubl+a$ZlN@SOWJw>=M~CtVVWC*k!V3*&Nw(!hePAI-4hZUf5N#7uX`%i{krfvd^(4 zvX_KCL-sOTA$vvGHL_RP8rf^Yo+bM{TPJ&6*mG=SS8Hr`sHVxK)2}9_V1{mvwy!g7 zzrb|*t`N=cw0Yp%f7@{mL)Z6O4I?Vsw$~mwwjEV%d*HL>AHVra<5T2I z-2S`06%G!!4&B2Wt`~&ue*Z={`0}XRef4syd+fO#pL?f2dSQ6D<#6uv;O5~w4rw(= zE6}a13wubrLHSL5aDi@#_M%{>?;A0zeN)t?6jfXPW1l`i`H6H;nxN{Vl9iKUk zLfj(gDrcHSRP8*JLA<6H^^^6F%b%Fgru1Pw+}qo+NC;|T^{FZn6r^N>677ucsvw=u z)FWU`j0!_|4ZdSkWs@T(H>z{P(jGfu&u7`NvPMpEApW56fM*t-D-etxr0FNBVP)rt zOPm_`OpcrmrQ|}9O-kcTJM%-j zTC{{2d%SQ_eL2Yp9?9}XtN_F$a|b$P8zdbx?gLcT#4uFwiD#IeaAaY5H6D>~Q)<>e zs(J1phQ-Au+eLnUe ziA-9i4iphALb8ZFN;SpB>=GyDa$WW!$ptcE?Uox@o*!D*p7ZD4HOUIfMzDI45_sX0 z0`n1DCEq{5e?km7xrx;uxdS&$FQW}|hYL@1`)x`i<{A@SI4Td?Z*BZ)$Qfsy{>8D7 z&jPpym(VZP(rafWWW$4=L`%Haq5Y12P*6LbE3y_Ab_2i``o7g~^R8okel@V}T33Sx zb*)6%2!PKZP>%W440-v`VGFSf&k~)6idH+1#++{rE(O;({5ej3I{L!V^h!Wpt4EOtE#c zvF&>yZ!@<;>x*|*ApUK|OWZ&p8qq3m$dPIlwMHH)pIFI8G0z@zee#6s3ORkP9Md@? z>{g>5Rg#lWar79KzJGEbhm+y}OdPFSn!MuTWC=_*tn}{s)c=4qE^K literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/message.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/http/__pycache__/message.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee5aea2cc01c25cb3d9c1487b9e972bc5303c6b9 GIT binary patch literal 8705 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 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..b96fe052a968301a4323b409027bc38695c27346 GIT binary patch literal 149 zcmXr!<>i_mbT*m+2p)q77+?f49Dul(1xTbY1T$zd`mJOr0tq9CUrzcZxdr+KnFYF; zdBr7(IXSxN#Ri4x>DgKO>7{v@$@xWj2nI+=QE6^!UWtBud}dx|NqoFsLFFwDo80`A O(wtN~klDpR%m4sRpC()Y 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..e76b5e91893c25f8a5da3d86116b309574fa6edf GIT binary patch literal 4471 zcmbtX&6Cr}71y_Ak3Apk@HOl%T9&PiVaC9cN~Kt8mthzP6);=FK!TU7N~mQck1d&Q znSjxp%waFdpO93ga@qeRm*fx7HHU;-&N=1xTCzQ!aG*eTzivIXTEBk%`u$#CuGK1k z-ue6YJ1dIvPvyX6pnVNRFbJl!6s9t*r?ymCYb_16-qZU=%TVc=(KGv2%aU!gSL)j> z8*QsqW~Ekz*{uauZdF;ORbvaS#ajxivf68f)dFj*x0aZ8U#TySzP|49VB<*~1;PpY zgI>@NqQpzWICA2)BhVB#z7)=Y$4TtRy>-X$h3K&%LKeIMOr=>@GhGayptA17old}| zB7Biq{UG6?pPA3S-Z03_e)4jFs^bkiL00nPVUz?sD?bfI5_h=Q_i4DvrK52TMZAUx z6xcwa`L}eYG5xjDGMK?k)F!i73AM#+Rz_W76}Eud&Swi-EV3osSJ*OJLA}6^u#Zqz z*(y7Vy2c!KoSk^BwielE>?B)5Yl)p=ALH3F`-FXpdWD^atFkbUj7_y^Rc$C?dyrvwNOeZ_E^Kya;kL8-Abw@3#mR<+uB%FmES1QnxQ1s zy$bKD3jG+3RDPsH+HcXue2meVr?E)Zok7f#E5%YfBH+&hF0m5c(c%w3Yn=c7hsN*D z*B>{|ogG<^J6A7#(`eRfoR%oFT{n!v#C18%tW}eyVeWtjZE}IUZY>mQ`e0^>*xw71 zth9O4z1`g2%_{frZT;H4acgJuZf4L!@{TA9{3O%ED9Os(FZ^IYK3O-=A?UR;-EVg? zLk?#U{+|?n65s#twF^mqaA6P*HfUMAUT>o#EO|7U$)D!BWI$F9fU7#_U_1t=rLGID3XCWr*+166+Pg<(~5vzig zk}FGV%oMDF-W(mB;A+548a@w`(D!<8R18tq3mZ?gSvib+F1Kji;78FV)B6I3@OVdL z*52=_mWId$&4Ygo@g<5Nms8Z$*(4^ze)uGg%uOQTJm&lqzBZYGnX5h_RSukgiZ(w@ z!XZJccL4NfxIK?D16?gFFGu=t#H(|NVYn!aI&+vy6Xm<3J_E!T=1?ydsEe^a9QUy~ z+`(jH%pot&)bEb{bKt%_hkbcs@ySQtj-H$hj;$nW7a{AQyGs{Wn{_?Y;oDh>;vlS? z8Mm7^@4b^BWJVaZ<6jai893F^LIH~O?~RAk2IXHsWaf?S>kn?d8yJ%XPllbV2hdEM zQap!#MsmLV;I{^t+Yf@BwB~of|f&Dnre?hYeV}ZwB@N*Nj3hLq|#j=o=LT~p6ZB9 z21O?D#o2-{{+9Cv`qfe}1@Y`3bX6&sgR%x|bmLCkaXhCJJ`W=+xK_x5#!{_3Y0R~=B}|yx3rtA{SYy^nWvs&Ulx~H==g6DTUO*l-Yr}Vr zDfvA!u<{F|=H0*(L#!qbo(%($IBdvex;S0d;(?ol{lEz+|6yJd&|QNB3wnt+?Hv9a zo8}E!{|y>SQ^qk)WM;b;dr4O9hdz%dN-KXktAs-45s@Dt$xS%_2*@&140>V0rO(I!TgtiEfP)V* z9dNT!5Iqlh9JQ(k&4C=Vuohg}?F)Vd!yWdSUqJT=B~L&MN<1tiAgj`AMzz92@@Y&% z4jGReW{X~&L}aE?N=Z>U)y7B-i=;zqj;gON?!UUUf92Iz`)LR9x#CjVQBo;o&F)s&V0)!C}a>WP2zR$P=EM z=2q{I_DF$tgADtwaQQX?pHG0YG7E}KFM12q>BIMRfZ74G_Qn63G+%ciGp==3(_pE$`x3@!jh62j{QbF{)Kw?u6J8Wks>%*qMK$dxBl zD~SA<1sun7g^n8$O*G$k+EJ7$aI#jWDEgT{c-B*geJKJnH zJo6jqH>r-$d5ZR$>tav3{g@4V)ULYjv!T~3o|Igd#XgQ)bXJl1J*RwGV&k7vi}HP5 zCvlbpg%nAJoP3|(Bq5`h+(@$-$QDHpPAhgCMNpb.", 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..ad67eb1d7950461868ef1045d31e0d66fbbc6cce GIT binary patch literal 636 zcmaix-AcnS6vxw#tz}yUeFASpiYyhq9e#jgh~P#$8N`cHmfCfzZB4R`?pXw{eHdS$ zw_fcPyfSS%CQPxR|8o+4=Oa1ihS7MlKOXA}0H5H;r*r>`|MZLt23U#!0$9chR&kB5 zI@YlMsv*e-QyCkx8iBa!OSskHtOLxwHID=LtHWR(hvS6AaG8*qPbj2WJcbUJQF*X3 zcZk4W_%Vxo2D_D0p+~!@U}3Kesj!oOut7F*dbbsPybCklu7?vi6!!1pY(jhw!(nLz zW@@=w+z{!TOQ5>w5jBN%mox~GQEk$!+-g-#qy-}Bnhyl5RW7SvI`qP1!dMEgg%zcd z9#rS1Op0()gqupP(2Sye=V37D+r#dSJL=naUE9f93qFr9iQO=MO5Op{ikL#aE-m() zT4|WJ!(?dS}xBMGS`J^mg}A81kWS?qR!LN11`EMOWUQZ G4fz|AvCj|y 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..f3c84ea8a71e0791843c45f575652655594765e0 GIT binary patch literal 4883 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 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..dfa7d663bb32d1775589a818e1d93a3fc48ce31b GIT binary patch literal 728 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 F?#fCCN& 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/gthread.cpython-36.pyc b/venv/lib/python3.6/site-packages/gunicorn/workers/__pycache__/gthread.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f7a583aca4bc2758e8f5c240ed4701b64ec0274 GIT binary patch literal 8755 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 literal 0 HcmV?d00001 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..fe378c3 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/_gaiohttp.py @@ -0,0 +1,168 @@ +# -*- coding: utf-8 - +# +# This file is part of gunicorn released under the MIT license. +# See the NOTICE for more information. + +import asyncio +import datetime +import functools +import logging +import os + +try: + import ssl +except ImportError: + ssl = None + +import gunicorn.workers.base as base + +from aiohttp.wsgi import WSGIServerHttpProtocol as OldWSGIServerHttpProtocol + + +class WSGIServerHttpProtocol(OldWSGIServerHttpProtocol): + def log_access(self, request, environ, response, time): + self.logger.access(response, request, environ, datetime.timedelta(0, 0, time)) + + +class AiohttpWorker(base.Worker): + + def __init__(self, *args, **kw): # pragma: no cover + super().__init__(*args, **kw) + cfg = self.cfg + if cfg.is_ssl: + self.ssl_context = self._create_ssl_context(cfg) + else: + self.ssl_context = None + self.servers = [] + self.connections = {} + + def init_process(self): + # create new event_loop after fork + asyncio.get_event_loop().close() + + self.loop = asyncio.new_event_loop() + asyncio.set_event_loop(self.loop) + + super().init_process() + + def run(self): + self._runner = asyncio.ensure_future(self._run(), loop=self.loop) + + try: + self.loop.run_until_complete(self._runner) + finally: + self.loop.close() + + def wrap_protocol(self, proto): + proto.connection_made = _wrp( + proto, proto.connection_made, self.connections) + proto.connection_lost = _wrp( + proto, proto.connection_lost, self.connections, False) + return proto + + def factory(self, wsgi, addr): + # are we in debug level + is_debug = self.log.loglevel == logging.DEBUG + + proto = WSGIServerHttpProtocol( + wsgi, readpayload=True, + loop=self.loop, + log=self.log, + debug=is_debug, + keep_alive=self.cfg.keepalive, + access_log=self.log.access_log, + access_log_format=self.cfg.access_log_format) + return self.wrap_protocol(proto) + + def get_factory(self, sock, addr): + return functools.partial(self.factory, self.wsgi, addr) + + @asyncio.coroutine + def close(self): + try: + if hasattr(self.wsgi, 'close'): + yield from self.wsgi.close() + except: + self.log.exception('Process shutdown exception') + + @asyncio.coroutine + def _run(self): + for sock in self.sockets: + factory = self.get_factory(sock.sock, sock.cfg_addr) + self.servers.append( + (yield from self._create_server(factory, sock))) + + # If our parent changed then we shut down. + pid = os.getpid() + try: + while self.alive or self.connections: + self.notify() + + if (self.alive and + pid == os.getpid() and self.ppid != os.getppid()): + self.log.info("Parent changed, shutting down: %s", self) + self.alive = False + + # stop accepting requests + if not self.alive: + if self.servers: + self.log.info( + "Stopping server: %s, connections: %s", + pid, len(self.connections)) + for server in self.servers: + server.close() + self.servers.clear() + + # prepare connections for closing + for conn in self.connections.values(): + if hasattr(conn, 'closing'): + conn.closing() + + yield from asyncio.sleep(1.0, loop=self.loop) + except KeyboardInterrupt: + pass + + if self.servers: + for server in self.servers: + server.close() + + yield from self.close() + + @asyncio.coroutine + def _create_server(self, factory, sock): + return self.loop.create_server(factory, sock=sock.sock, + ssl=self.ssl_context) + + @staticmethod + def _create_ssl_context(cfg): + """ Creates SSLContext instance for usage in asyncio.create_server. + + See ssl.SSLSocket.__init__ for more details. + """ + ctx = ssl.SSLContext(cfg.ssl_version) + ctx.load_cert_chain(cfg.certfile, cfg.keyfile) + ctx.verify_mode = cfg.cert_reqs + if cfg.ca_certs: + ctx.load_verify_locations(cfg.ca_certs) + if cfg.ciphers: + ctx.set_ciphers(cfg.ciphers) + return ctx + + +class _wrp: + + def __init__(self, proto, meth, tracking, add=True): + self._proto = proto + self._id = id(proto) + self._meth = meth + self._tracking = tracking + self._add = add + + def __call__(self, *args): + if self._add: + self._tracking[self._id] = self._proto + elif self._id in self._tracking: + del self._tracking[self._id] + + conn = self._meth(*args) + return conn diff --git a/venv/lib/python3.6/site-packages/gunicorn/workers/base.py b/venv/lib/python3.6/site-packages/gunicorn/workers/base.py new file mode 100644 index 0000000..881efa0 --- /dev/null +++ b/venv/lib/python3.6/site-packages/gunicorn/workers/base.py @@ -0,0 +1,264 @@ +# -*- 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 os +from random import randint +import signal +from ssl import SSLError +import sys +import time +import traceback + +from gunicorn import six +from gunicorn import util +from gunicorn.workers.workertmp import WorkerTmp +from gunicorn.reloader import reloader_engines +from gunicorn.http.errors import ( + InvalidHeader, InvalidHeaderName, InvalidRequestLine, InvalidRequestMethod, + InvalidHTTPVersion, LimitRequestLine, LimitRequestHeaders, +) +from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest +from gunicorn.http.errors import InvalidSchemeHeaders +from gunicorn.http.wsgi import default_environ, Response +from gunicorn.six import MAXSIZE + + +class Worker(object): + + SIGNALS = [getattr(signal, "SIG%s" % x) + for x in "ABRT HUP QUIT INT TERM USR1 USR2 WINCH CHLD".split()] + + PIPE = [] + + def __init__(self, age, ppid, sockets, app, timeout, cfg, log): + """\ + This is called pre-fork so it shouldn't do anything to the + current process. If there's a need to make process wide + changes you'll want to do that in ``self.init_process()``. + """ + self.age = age + self.pid = "[booting]" + self.ppid = ppid + self.sockets = sockets + self.app = app + self.timeout = timeout + self.cfg = cfg + self.booted = False + self.aborted = False + self.reloader = None + + self.nr = 0 + jitter = randint(0, cfg.max_requests_jitter) + self.max_requests = cfg.max_requests + jitter or MAXSIZE + self.alive = True + self.log = log + self.tmp = WorkerTmp(cfg) + + def __str__(self): + return "" % 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()