Skip to content

Commit

Permalink
Merge branch 'master' of github.com:benoitc/gunicorn into 2066-statsd…
Browse files Browse the repository at this point in the history
…-socket
  • Loading branch information
Wojciech Malinowski committed Oct 26, 2019
2 parents d1f6a77 + 235f06c commit 4b91ca1
Show file tree
Hide file tree
Showing 16 changed files with 72 additions and 28 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ matrix:
env: TOXENV=py37
dist: xenial
sudo: true
- python: pypy3
env: TOXENV=pypy3
dist: xenial
- python: 3.8-dev
env: TOXENV=py38-dev
dist: xenial
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ The relevant maintainer for a pull request is assigned in 3 steps:

* Step 2: Find the MAINTAINERS file which affects this directory. If the directory itself does not have a MAINTAINERS file, work your way up the the repo hierarchy until you find one.

* Step 3: The first maintainer listed is the primary maintainer. The pull request is assigned to him. He may assign it to other listed maintainers, at his discretion.
* Step 3: The first maintainer listed is the primary maintainer who is assigned the Pull Request. The primary maintainer can reassign a Pull Request to other listed maintainers.


### I'm a maintainer, should I make pull requests too?
Expand Down
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ Example with test app::
$ gunicorn --workers=2 test:app


Contributing
------------

See `our complete contributor's guide <CONTRIBUTING.md>`_ for more details.


License
-------

Expand Down
3 changes: 2 additions & 1 deletion docs/source/custom.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ Here is a small example where we create a very small WSGI app and load it with
a custom Application:

.. literalinclude:: ../../examples/standalone_app.py
:lines: 11-60
:start-after: # See the NOTICE for more information
:lines: 2-

Direct Usage of Existing WSGI Apps
----------------------------------
Expand Down
22 changes: 22 additions & 0 deletions docs/source/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ want to consider one of the alternate worker types.
installed, this is the most likely reason.


Extra Packages
==============
Some Gunicorn options require additional packages. You can use the ``[extra]``
syntax to install these at the same time as Gunicorn.

Most extra packages are needed for alternate worker types. See the
`design docs`_ for more information on when you'll want to consider an
alternate worker type.

* ``gunicorn[eventlet]`` - Eventlet-based greenlets workers
* ``gunicorn[gevent]`` - Gevent-based greenlets workers
* ``gunicorn[gthread]`` - Threaded workers
* ``gunicorn[tornado]`` - Tornado-based workers, not recommended

If you are running more than one instance of Gunicorn, the :ref:`proc-name`
setting will help distinguish between them in tools like ``ps`` and ``top``.

* ``gunicorn[setproctitle]`` - Enables setting the process name

Multiple extras can be combined, like
``pip install gunicorn[gevent,setproctitle]``.

Debian GNU/Linux
================

Expand Down
3 changes: 0 additions & 3 deletions examples/echo.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
#
# Example code from Eventlet sources

from wsgiref.validate import validator

from gunicorn import __version__


@validator
def app(environ, start_response):
"""Simplest possible application object"""

Expand Down
2 changes: 1 addition & 1 deletion gunicorn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.

version_info = (19, 9, 0)
version_info = (20, 0, 0)
__version__ = ".".join([str(v) for v in version_info])
SERVER_SOFTWARE = "gunicorn/%s" % __version__
4 changes: 1 addition & 3 deletions gunicorn/arbiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,7 @@ def run(self):
self.log.info("Handling signal: %s", signame)
handler()
self.wakeup()
except StopIteration:
self.halt()
except KeyboardInterrupt:
except (StopIteration, KeyboardInterrupt):
self.halt()
except HaltServer as inst:
self.halt(reason=inst.reason, exit_status=inst.exit_status)
Expand Down
2 changes: 1 addition & 1 deletion gunicorn/glogging.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ def _set_syslog_handler(self, log, cfg, fmt, name):
def _get_user(self, environ):
user = None
http_auth = environ.get("HTTP_AUTHORIZATION")
if http_auth and http_auth.startswith('Basic'):
if http_auth and http_auth.lower().startswith('basic'):
auth = http_auth.split(" ", 1)
if len(auth) == 2:
try:
Expand Down
3 changes: 2 additions & 1 deletion gunicorn/http/body.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys

from gunicorn.http.errors import (NoMoreData, ChunkMissingTerminator,
InvalidChunkSize)
InvalidChunkSize)


class ChunkedReader(object):
Expand Down Expand Up @@ -187,6 +187,7 @@ def __next__(self):
if not ret:
raise StopIteration()
return ret

next = __next__

def getsize(self, size):
Expand Down
5 changes: 4 additions & 1 deletion gunicorn/http/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,13 @@ def process_headers(self, headers):
if HEADER_RE.search(name):
raise InvalidHeaderName('%r' % name)

if not isinstance(value, str):
raise TypeError('%r is not a string' % value)

if HEADER_VALUE_RE.search(value):
raise InvalidHeader('%r' % value)

value = str(value).strip()
value = value.strip()
lname = name.lower().strip()
if lname == "content-length":
self.response_length = int(value)
Expand Down
8 changes: 5 additions & 3 deletions gunicorn/workers/workertmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ def __init__(self, cfg):
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)

# change the owner and group of the file if the worker will run as
# a different user or group, so that the worker can modify the file
if cfg.uid != os.geteuid() or cfg.gid != os.getegid():
util.chown(name, cfg.uid, cfg.gid)

# unlink the file so we don't leak tempory files
try:
if not IS_CYGWIN:
Expand Down
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Internet',
'Topic :: Utilities',
'Topic :: Software Development :: Libraries :: Python Modules',
Expand Down Expand Up @@ -78,6 +80,7 @@ def run_tests(self):
'eventlet': ['eventlet>=0.9.7'],
'tornado': ['tornado>=0.2'],
'gthread': [],
'setproctitle': ['setproctitle'],
}

setup(
Expand Down
12 changes: 10 additions & 2 deletions tests/test_logger.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import datetime
from types import SimpleNamespace

import pytest

from gunicorn.config import Config
from gunicorn.glogging import Logger

Expand Down Expand Up @@ -47,7 +49,13 @@ def test_atoms_zero_bytes():
assert atoms['B'] == 0


def test_get_username_from_basic_auth_header():
@pytest.mark.parametrize('auth', [
# auth type is case in-sensitive
'Basic YnJrMHY6',
'basic YnJrMHY6',
'BASIC YnJrMHY6',
])
def test_get_username_from_basic_auth_header(auth):
request = SimpleNamespace(headers=())
response = SimpleNamespace(
status='200', response_length=1024, sent=1024,
Expand All @@ -57,7 +65,7 @@ def test_get_username_from_basic_auth_header():
'REQUEST_METHOD': 'GET', 'RAW_URI': '/my/path?foo=bar',
'PATH_INFO': '/my/path', 'QUERY_STRING': 'foo=bar',
'SERVER_PROTOCOL': 'HTTP/1.1',
'HTTP_AUTHORIZATION': 'Basic YnJrMHY6',
'HTTP_AUTHORIZATION': auth,
}
logger = Logger(Config())
atoms = logger.atoms(response, request, environ, datetime.timedelta(seconds=1))
Expand Down
20 changes: 10 additions & 10 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ def test_parse_address(test_input, expected):


def test_parse_address_invalid():
with pytest.raises(RuntimeError) as err:
with pytest.raises(RuntimeError) as exc_info:
util.parse_address('127.0.0.1:test')
assert "'test' is not a valid port number." in str(err)
assert "'test' is not a valid port number." in str(exc_info.value)


def test_parse_fd_invalid():
with pytest.raises(RuntimeError) as err:
with pytest.raises(RuntimeError) as exc_info:
util.parse_address('fd://asd')
assert "'asd' is not a valid file descriptor." in str(err)
assert "'asd' is not a valid file descriptor." in str(exc_info.value)


def test_http_date():
Expand All @@ -63,24 +63,24 @@ def test_warn(capsys):
def test_import_app():
assert util.import_app('support:app')

with pytest.raises(ImportError) as err:
with pytest.raises(ImportError) as exc_info:
util.import_app('a:app')
assert 'No module' in str(err)
assert 'No module' in str(exc_info.value)

with pytest.raises(AppImportError) as err:
with pytest.raises(AppImportError) as exc_info:
util.import_app('support:wrong_app')
msg = "Failed to find application object 'wrong_app' in 'support'"
assert msg in str(err)
assert msg in str(exc_info.value)


def test_to_bytestring():
assert util.to_bytestring('test_str', 'ascii') == b'test_str'
assert util.to_bytestring('test_str®') == b'test_str\xc2\xae'
assert util.to_bytestring(b'byte_test_str') == b'byte_test_str'
with pytest.raises(TypeError) as err:
with pytest.raises(TypeError) as exc_info:
util.to_bytestring(100)
msg = '100 is not a string'
assert msg in str(err)
assert msg in str(exc_info.value)


@pytest.mark.parametrize('test_input, expected', [
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py34, py35, py36, py37, py38-dev, pypy, lint
envlist = py34, py35, py36, py37, py38-dev, pypy3, lint
skipsdist = True

[testenv]
Expand Down

0 comments on commit 4b91ca1

Please sign in to comment.