diff --git a/.gitignore b/.gitignore
index 73e923d3..4a834a7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ settings.py
docs/_build/
docs/_api/
build/*
+.DS_Store
diff --git a/.travis.yml b/.travis.yml
index d8e17093..afac549a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,5 @@
sudo: false
+dist: precise
language: python
cache:
directories:
diff --git a/README.rst b/README.rst
index 410bd0b8..a9bfa982 100644
--- a/README.rst
+++ b/README.rst
@@ -55,6 +55,16 @@ Documentation
:target: https://pypi.python.org/pypi/sanic/
.. |PyPI version| image:: https://img.shields.io/pypi/pyversions/sanic.svg
:target: https://pypi.python.org/pypi/sanic/
+
+
+Examples
+--------
+`Non-Core examples `_. Examples of plugins and Sanic that are outside the scope of Sanic core.
+
+`Extensions `_. Sanic extensions created by the community.
+
+`Projects `_. Sanic in production use.
+
TODO
----
diff --git a/docs/sanic/testing.md b/docs/sanic/testing.md
index b8427a00..0aca9184 100644
--- a/docs/sanic/testing.md
+++ b/docs/sanic/testing.md
@@ -59,7 +59,7 @@ the available arguments to aiohttp can be found
[in the documentation for ClientSession](https://aiohttp.readthedocs.io/en/stable/client_reference.html#client-session).
-# pytest-sanic
+## pytest-sanic
[pytest-sanic](https://github.com/yunstanford/pytest-sanic) is a pytest plugin, it helps you to test your code asynchronously.
Just write tests like,
diff --git a/docs/sanic/versioning.md b/docs/sanic/versioning.md
index 85cbd278..ab6dab22 100644
--- a/docs/sanic/versioning.md
+++ b/docs/sanic/versioning.md
@@ -10,11 +10,11 @@ You can pass a version number to the routes directly.
from sanic import response
-@app.route('/text', verion=1)
+@app.route('/text', version=1)
def handle_request(request):
return response.text('Hello world! Version 1')
-@app.route('/text', verion=2)
+@app.route('/text', version=2)
def handle_request(request):
return response.text('Hello world! Version 2')
diff --git a/sanic/__init__.py b/sanic/__init__.py
index 4cc0710f..8f35a283 100644
--- a/sanic/__init__.py
+++ b/sanic/__init__.py
@@ -1,6 +1,6 @@
from sanic.app import Sanic
from sanic.blueprints import Blueprint
-__version__ = '0.5.4'
+__version__ = '0.6.0'
__all__ = ['Sanic', 'Blueprint']
diff --git a/sanic/app.py b/sanic/app.py
index f1e8be7e..f0ccad86 100644
--- a/sanic/app.py
+++ b/sanic/app.py
@@ -33,9 +33,7 @@ class Sanic:
logging.config.dictConfig(log_config)
# Only set up a default log handler if the
# end-user application didn't set anything up.
- if not (logging.root.handlers and
- log.level == logging.NOTSET and
- log_config):
+ if not logging.root.handlers and log.level == logging.NOTSET:
formatter = logging.Formatter(
"%(asctime)s: %(levelname)s: %(message)s")
handler = logging.StreamHandler()
diff --git a/sanic/exceptions.py b/sanic/exceptions.py
index 21ab2a94..0edb0562 100644
--- a/sanic/exceptions.py
+++ b/sanic/exceptions.py
@@ -208,44 +208,39 @@ class Unauthorized(SanicException):
"""
Unauthorized exception (401 HTTP status code).
+ :param message: Message describing the exception.
:param scheme: Name of the authentication scheme to be used.
- :param challenge: A dict containing values to add to the WWW-Authenticate
- header that is generated. This is especially useful when dealing with
- the Digest scheme. (optional)
+
+ When present, kwargs is used to complete the WWW-Authentication header.
Examples::
# With a Basic auth-scheme, realm MUST be present:
- challenge = {"realm": "Restricted Area"}
- raise Unauthorized("Auth required.", "Basic", challenge)
+ raise Unauthorized("Auth required.", "Basic", realm="Restricted Area")
# With a Digest auth-scheme, things are a bit more complicated:
- challenge = {
- "realm": "Restricted Area",
- "qop": "auth, auth-int",
- "algorithm": "MD5",
- "nonce": "abcdef",
- "opaque": "zyxwvu"
- }
- raise Unauthorized("Auth required.", "Digest", challenge)
+ raise Unauthorized("Auth required.",
+ "Digest",
+ realm="Restricted Area",
+ qop="auth, auth-int",
+ algorithm="MD5",
+ nonce="abcdef",
+ opaque="zyxwvu")
- # With a Bearer auth-scheme, realm is optional:
- challenge = {"realm": "Restricted Area"}
- raise Unauthorized("Auth required.", "Bearer", challenge)
+ # With a Bearer auth-scheme, realm is optional so you can write:
+ raise Unauthorized("Auth required.", "Bearer")
+
+ # or, if you want to specify the realm:
+ raise Unauthorized("Auth required.", "Bearer", realm="Restricted Area")
"""
- pass
-
- def __init__(self, message, scheme, challenge=None):
+ def __init__(self, message, scheme, **kwargs):
super().__init__(message)
- chal = ""
-
- if challenge is not None:
- values = ["{!s}={!r}".format(k, v) for k, v in challenge.items()]
- chal = ', '.join(values)
+ values = ["{!s}={!r}".format(k, v) for k, v in kwargs.items()]
+ challenge = ', '.join(values)
self.headers = {
- "WWW-Authenticate": "{} {}".format(scheme, chal).rstrip()
+ "WWW-Authenticate": "{} {}".format(scheme, challenge).rstrip()
}
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
index 620e7891..1521c9ed 100644
--- a/tests/test_exceptions.py
+++ b/tests/test_exceptions.py
@@ -33,18 +33,17 @@ def exception_app():
@app.route('/401/basic')
def handler_401_basic(request):
- raise Unauthorized("Unauthorized", "Basic", {"realm": "Sanic"})
+ raise Unauthorized("Unauthorized", "Basic", realm="Sanic")
@app.route('/401/digest')
def handler_401_digest(request):
- challenge = {
- "realm": "Sanic",
- "qop": "auth, auth-int",
- "algorithm": "MD5",
- "nonce": "abcdef",
- "opaque": "zyxwvu",
- }
- raise Unauthorized("Unauthorized", "Digest", challenge)
+ raise Unauthorized("Unauthorized",
+ "Digest",
+ realm="Sanic",
+ qop="auth, auth-int",
+ algorithm="MD5",
+ nonce="abcdef",
+ opaque="zyxwvu")
@app.route('/401/bearer')
def handler_401_bearer(request):
@@ -122,7 +121,7 @@ def test_forbidden_exception(exception_app):
request, response = exception_app.test_client.get('/403')
assert response.status == 403
-
+
def test_unauthorized_exception(exception_app):
"""Test the built-in Unauthorized exception"""
request, response = exception_app.test_client.get('/401/basic')
@@ -132,7 +131,7 @@ def test_unauthorized_exception(exception_app):
request, response = exception_app.test_client.get('/401/digest')
assert response.status == 401
-
+
auth_header = response.headers.get('WWW-Authenticate')
assert auth_header is not None
assert auth_header.startswith('Digest')
diff --git a/tests/test_exceptions_handler.py b/tests/test_exceptions_handler.py
index 006c2cc4..6a959382 100644
--- a/tests/test_exceptions_handler.py
+++ b/tests/test_exceptions_handler.py
@@ -24,7 +24,7 @@ def handler_3(request):
@exception_handler_app.route('/4')
def handler_4(request):
- foo = bar
+ foo = bar # noqa -- F821 undefined name 'bar' is done to throw exception
return text(foo)
diff --git a/tests/test_logging.py b/tests/test_logging.py
index fc26ca93..d6911d86 100644
--- a/tests/test_logging.py
+++ b/tests/test_logging.py
@@ -1,5 +1,7 @@
-import asyncio
import uuid
+from importlib import reload
+
+from sanic.config import LOGGING
from sanic.response import text
from sanic import Sanic
from io import StringIO
@@ -10,6 +12,11 @@ function: %(funcName)s(); \
message: %(message)s'''
+def reset_logging():
+ logging.shutdown()
+ reload(logging)
+
+
def test_log():
log_stream = StringIO()
for handler in logging.root.handlers[:]:
@@ -32,5 +39,19 @@ def test_log():
log_text = log_stream.getvalue()
assert rand_string in log_text
+
+def test_default_log_fmt():
+
+ reset_logging()
+ Sanic()
+ for fmt in [h.formatter for h in logging.getLogger('sanic').handlers]:
+ assert fmt._fmt == LOGGING['formatters']['simple']['format']
+
+ reset_logging()
+ Sanic(log_config=None)
+ for fmt in [h.formatter for h in logging.getLogger('sanic').handlers]:
+ assert fmt._fmt == "%(asctime)s: %(levelname)s: %(message)s"
+
+
if __name__ == "__main__":
test_log()