Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config coverage increase #626

Merged
merged 29 commits into from
Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
87ad889
Added test for env_file config to increase coverage
euri10 Apr 11, 2020
f3ddf6e
Added test for reload_dirs
euri10 Apr 11, 2020
c89ba6c
Added test for workers set with env vars
euri10 Apr 11, 2020
f640b07
Added test for forwarded_allow_ips
euri10 Apr 11, 2020
36b6360
Added test for use_colors
euri10 Apr 11, 2020
9a12c79
Added test for log_config ini file
euri10 Apr 11, 2020
e3f9fa9
Commenting that ini file config for now it breaks mass http tests !
euri10 Apr 11, 2020
ac361d6
Seems like the fix in https://github.com/encode/uvicorn/pull/512/file…
euri10 Apr 11, 2020
ef86999
Added log_level tests, as str or int
euri10 Apr 11, 2020
d11ad7e
Added access_log set to False
euri10 Apr 11, 2020
31bf6fe
Added test for failing app import string
euri10 Apr 11, 2020
5920492
Added test for should_reload property
euri10 Apr 11, 2020
34f686b
Added test for uds
euri10 Apr 11, 2020
d3318a2
Added optional python-dotenv
euri10 Apr 11, 2020
3e0b500
Skipping uds tests on windows
euri10 Apr 11, 2020
c76bbd6
Added fd tests, not quite sure about those and uds though
euri10 Apr 11, 2020
f6983fd
Rebased against master
euri10 Apr 11, 2020
1341496
Added oserror test when tring to use sockets on windows
euri10 Apr 12, 2020
1ec6388
Test rebind socket oserror
euri10 Apr 12, 2020
08193f9
Removed python 3.6 specific (linked to #328) as logger kwarg is not i…
euri10 Apr 12, 2020
736d4ae
Skip uds test on pypy it makes travis timeout after 10min
euri10 Apr 12, 2020
647094f
Was not uds that make pypy fail
euri10 Apr 12, 2020
6b816e8
Seems like pypy doesnt like the socket test
euri10 Apr 12, 2020
07a754a
Lint again
euri10 Apr 12, 2020
c0f817e
Check if that pypy last config test is the timeout culprit
euri10 Apr 12, 2020
8f2d21c
Removing that fd test, see if this breaks pypy
euri10 Apr 12, 2020
1dca35e
Lint
euri10 Apr 12, 2020
1aa56d5
Skipping "socket" tests on pypy
euri10 Apr 12, 2020
b108142
Minor modif on tests names
euri10 Apr 18, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ install:
choco install python3;
export PATH=/c/Python37:/c/Python37/Scripts:/c/Python38:/c/Python38/Scripts:$PATH;
python -m pip install -U click h11 wsproto==0.13.* websockets==8.*;
python -m pip install -U autoflake black codecov flake8 isort pytest pytest-cov requests watchgod;
python -m pip install -U autoflake black codecov flake8 isort pytest pytest-cov requests watchgod python-dotenv;
elif [ "$TRAVIS_PYTHON_VERSION" = "pypy3" ]; then
pip install -U click h11 wsproto==0.13.*;
pip install -U autoflake codecov flake8 isort pytest pytest-cov requests watchgod;
pip install -U autoflake codecov flake8 isort pytest pytest-cov requests watchgod python-dotenv;
else
pip install -U -r requirements.txt;
fi;
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ httptools
uvloop>=0.14.0
websockets==8.*
wsproto==0.13.*
python-dotenv

# Testing
autoflake
Expand Down
41 changes: 41 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,44 @@ def certfile_and_keyfile(tmp_path):
fout.write(PRIVATE_KEY)

return certfile, keyfile


ENV_FILE = """KEY_TRUE="1"
KEY_FALSE=""
WEB_CONCURRENCY=2048
"""


@pytest.fixture(scope="function")
def env_file(tmp_path):
envfile = str(tmp_path / ".env")
with open(envfile, "w") as fout:
fout.write(ENV_FILE)
return envfile


INI_LOG_CONFIG = """[loggers]
keys=root
[handlers]
keys=h
[formatters]
keys=f
[logger_root]
level=INFO
handlers=h
[handler_h]
class=StreamHandler
level=INFO
formatter=f
args=(sys.stderr,)
[formatter_f]
format=%(asctime)s %(name)s %(levelname)-4s %(message)s
"""


@pytest.fixture(scope="function")
def ini_log_config(tmp_path):
inifile = str(tmp_path / "log_config.ini")
with open(inifile, "w") as fout:
fout.write(INI_LOG_CONFIG)
return inifile
117 changes: 116 additions & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import os
import platform
import socket
import sys

import pytest

from uvicorn import protocols
from uvicorn.config import Config
from uvicorn.config import LOG_LEVELS, Config
from uvicorn.middleware.debug import DebugMiddleware
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
from uvicorn.middleware.wsgi import WSGIMiddleware
Expand Down Expand Up @@ -66,3 +69,115 @@ def test_ssl_config(certfile_and_keyfile):
config.load()

assert config.is_ssl is True


def test_env_file(env_file):
config = Config(app=asgi_app, env_file=env_file)
config.load()
assert bool(os.environ.get("KEY_TRUE"))
assert not bool(os.environ.get("KEY_FALSE"))
assert os.environ.get("KEY_NOT_EXISTS") is None
# you'd love that a beefy desktop !
assert int(os.environ.get("WEB_CONCURRENCY")) == 2048
assert config.workers == 2048


def test_reload_dir(tmp_path):
config = Config(app=asgi_app, reload_dirs=tmp_path)
config.load()
assert config.reload_dirs == tmp_path


def test_forwarded_allow_ips():
config = Config(app=asgi_app, forwarded_allow_ips="192.168.0.1")
config.load()
assert config.forwarded_allow_ips == "192.168.0.1"


@pytest.mark.parametrize("use_colors", [(True), (False)])
def test_log_config_use_colors(use_colors):
log_config = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {"default": {}, "access": {}},
}
config = Config(app=asgi_app, log_config=log_config, use_colors=use_colors)
config.load()
assert config.use_colors == use_colors


def test_log_config_inifile(ini_log_config):
config = Config(app=asgi_app, log_config=ini_log_config)
config.load()
assert config


log_lvl_passed = [(k) for k, v in LOG_LEVELS.items()] + [
(v) for k, v in LOG_LEVELS.items()
]


@pytest.mark.parametrize("log_lvl_passed", log_lvl_passed)
def test_log_level_set_as_str_or_int(log_lvl_passed,):
config = Config(app=asgi_app, log_level=log_lvl_passed)
config.load()
assert config.log_level == log_lvl_passed


def test_log_access():
config = Config(app=asgi_app, access_log=False)
config.load()
assert not config.access_log


def test_fail_asgi_app_import_and_exit():
asgi_app_wrong = ""
config = Config(app=asgi_app_wrong)
with pytest.raises(SystemExit) as pytest_wrapped_e:
config.load()
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1


def test_should_reload_property():
config = Config(app="tests.test_config:asgi_app", reload=True)
config.load()
assert config.should_reload


@pytest.mark.skipif(
sys.platform.startswith("win") or platform.python_implementation() == "PyPy",
reason="Skipping unix domain tests on Windows and PyPy",
)
def test_config_unix_domain_socket(tmp_path):
uds = tmp_path / "socket"
config = Config(app=asgi_app, uds=uds)
config.load()
assert config.uds == uds


@pytest.mark.skipif(
sys.platform.startswith("win") or platform.python_implementation() == "PyPy",
reason="Skipping file descriptor tests on Windows and PyPy",
)
def test_config_file_descriptor():
config = Config(app=asgi_app, fd=1)
config.load()
assert config.fd == 1


@pytest.mark.skipif(
sys.platform.startswith("win") or platform.python_implementation() == "PyPy",
reason="Skipping unix domain tests on Windows and PyPy",
)
def test_config_rebind_socket():
sock = socket.socket()
config = Config(asgi_app)
try:
sock.bind((config.host, config.port))
with pytest.raises(SystemExit) as pytest_wrapped_e:
config.bind_socket()
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
finally:
sock.close()
84 changes: 84 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import asyncio
import platform
import socket
import sys
import threading
import time

import pytest
import requests

from uvicorn.config import Config
Expand Down Expand Up @@ -121,3 +125,83 @@ def safe_run():
server.should_exit = True
thread.join()
assert exc is None


@pytest.mark.skipif(
sys.platform.startswith("win") or platform.python_implementation() == "PyPy",
reason="Skipping uds test on Windows and pypy",
)
def test_run_uds(tmp_path):
class App:
def __init__(self, scope):
if scope["type"] != "http":
raise Exception()

async def __call__(self, receive, send):
await send({"type": "http.response.start", "status": 204, "headers": []})
await send({"type": "http.response.body", "body": b"", "more_body": False})

class CustomServer(Server):
def install_signal_handlers(self):
pass

uds = str(tmp_path / "socket")
config = Config(app=App, loop="asyncio", limit_max_requests=1, uds=uds)
server = CustomServer(config=config)
thread = threading.Thread(target=server.run)
thread.start()
while not server.started:
time.sleep(0.01)
data = b"GET / HTTP/1.1\r\n\r\n"
sock_client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
sock_client.connect(uds)
r = sock_client.sendall(data)
assert r is None
except Exception as e:
print(e)
finally:
sock_client.close()
thread.join()


@pytest.mark.skipif(
sys.platform.startswith("win") or platform.python_implementation() == "PyPy",
reason="Skipping fd test on Windows and pypy",
)
def test_run_fd(tmp_path):
class App:
def __init__(self, scope):
if scope["type"] != "http":
raise Exception()

async def __call__(self, receive, send):
await send({"type": "http.response.start", "status": 204, "headers": []})
await send({"type": "http.response.body", "body": b"", "more_body": False})

class CustomServer(Server):
def install_signal_handlers(self):
pass

uds = str(tmp_path / "socket")
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
fd = sock.fileno()
sock.bind(uds)
config = Config(app=App, loop="asyncio", limit_max_requests=1, fd=fd)
server = CustomServer(config=config)
thread = threading.Thread(target=server.run)
thread.start()
while not server.started:
time.sleep(0.01)
data = b"GET / HTTP/1.1\r\n\r\n"
sock_client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
sock_client.connect(uds)
r = sock_client.sendall(data)
assert r is None
except Exception as e:
print(e)
finally:
sock_client.close()
sock.close()
thread.join()
18 changes: 3 additions & 15 deletions uvicorn/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,20 +207,6 @@ def is_ssl(self) -> bool:
def configure_logging(self):
logging.addLevelName(TRACE_LOG_LEVEL, "TRACE")

if sys.version_info < (3, 7):
# https://bugs.python.org/issue30520
import pickle

def __reduce__(self):
if isinstance(self, logging.RootLogger):
return logging.getLogger, ()

if logging.getLogger(self.name) is not self:
raise pickle.PicklingError("logger cannot be pickled")
return logging.getLogger, (self.name,)

logging.Logger.__reduce__ = __reduce__

if self.log_config is not None:
if isinstance(self.log_config, dict):
if self.use_colors in (True, False):
Expand All @@ -232,7 +218,9 @@ def __reduce__(self):
] = self.use_colors
logging.config.dictConfig(self.log_config)
else:
logging.config.fileConfig(self.log_config)
logging.config.fileConfig(
self.log_config, disable_existing_loggers=False
)

if self.log_level is not None:
if isinstance(self.log_level, str):
Expand Down
Empty file.