From 17cc2928d0affef009e0bcad332d1c7059dc363b Mon Sep 17 00:00:00 2001 From: narzeja Date: Sat, 15 Oct 2016 21:38:12 +0200 Subject: [PATCH 1/8] Blueprint support, with docs, example, and tests --- docs/blueprints.md | 56 +++++++++++++++++++++++++++++++++++++ examples/blueprints.py | 24 ++++++++++++++++ sanic/__init__.py | 1 + sanic/blueprints.py | 24 ++++++++++++++++ sanic/sanic.py | 20 ++++++++++++++ tests/test_blueprints.py | 60 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 185 insertions(+) create mode 100644 docs/blueprints.md create mode 100644 examples/blueprints.py create mode 100644 sanic/blueprints.py create mode 100644 tests/test_blueprints.py diff --git a/docs/blueprints.md b/docs/blueprints.md new file mode 100644 index 00000000..ba3173e1 --- /dev/null +++ b/docs/blueprints.md @@ -0,0 +1,56 @@ +# Blueprints + +Blueprints are objects that can be used for sub-routing within an application. +Instead of adding routes to the application object, blueprints define similar +methods for adding routes, which are then registered with the application in a +flexible and plugable manner. + +## Why? + +Blueprints are especially useful for larger applications, where your application +logic can be broken down into several groups or areas of responsibility. + +It is also useful for API versioning, where one blueprint may point at +`/v1/`, and another pointing at `/v2/`. + + +## My First Blueprint + +The following shows a very simple blueprint that registers a handler-function at +the root `/` of your application. + +Suppose you save this file as `my_blueprint.py`, this can be imported in your +main application later. + +```python +from sanic.response import json +from sanic import Blueprint + +bp = Blueprint('my_blueprint') + +@bp.route('/') +async def bp_root(): + return json({'my': 'blueprint'}) + +``` + +## Registering Blueprints +Blueprints must be registered with the application. + +```python +from sanic import Sanic +from my_blueprint import bp + +app = Sanic(__name__) +app.register_blueprint(bp) + +app.run(host='0.0.0.0', port=8000, debug=True) +``` + +This will add the blueprint to the application and register any routes defined +by that blueprint. +In this example, the registered routes in the `app.router` will look like: + +```python +[Route(handler=, methods=None, pattern=re.compile('^/$'), parameters=[])] +``` diff --git a/examples/blueprints.py b/examples/blueprints.py new file mode 100644 index 00000000..dd009ce6 --- /dev/null +++ b/examples/blueprints.py @@ -0,0 +1,24 @@ +from sanic import Sanic +from sanic import Blueprint +from sanic.response import json, text + + +app = Sanic(__name__) +blueprint = Blueprint('name', url_prefix='/my_blueprint') +blueprint2 = Blueprint('name2', url_prefix='/my_blueprint2') + + +@blueprint.route('/foo') +async def foo(request): + return json({'msg': 'hi from blueprint'}) + + +@blueprint2.route('/foo') +async def foo2(request): + return json({'msg': 'hi from blueprint2'}) + + +app.register_blueprint(blueprint) +app.register_blueprint(blueprint2) + +app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/sanic/__init__.py b/sanic/__init__.py index 52dc810a..5b893df4 100644 --- a/sanic/__init__.py +++ b/sanic/__init__.py @@ -1 +1,2 @@ from .sanic import Sanic +from .blueprints import Blueprint diff --git a/sanic/blueprints.py b/sanic/blueprints.py new file mode 100644 index 00000000..dd009ce6 --- /dev/null +++ b/sanic/blueprints.py @@ -0,0 +1,24 @@ +from sanic import Sanic +from sanic import Blueprint +from sanic.response import json, text + + +app = Sanic(__name__) +blueprint = Blueprint('name', url_prefix='/my_blueprint') +blueprint2 = Blueprint('name2', url_prefix='/my_blueprint2') + + +@blueprint.route('/foo') +async def foo(request): + return json({'msg': 'hi from blueprint'}) + + +@blueprint2.route('/foo') +async def foo2(request): + return json({'msg': 'hi from blueprint2'}) + + +app.register_blueprint(blueprint) +app.register_blueprint(blueprint2) + +app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/sanic/sanic.py b/sanic/sanic.py index f17cd4af..a9a1ee41 100644 --- a/sanic/sanic.py +++ b/sanic/sanic.py @@ -22,6 +22,8 @@ class Sanic: self.config = Config() self.request_middleware = [] self.response_middleware = [] + self.blueprints = {} + self._blueprint_order = [] # -------------------------------------------------------------------- # # Registration @@ -86,6 +88,24 @@ class Sanic: return middleware + def register_blueprint(self, blueprint, **options): + """ + Registers a blueprint on the application. + :param blueprint: Blueprint object + :param options: option dictionary with blueprint defaults + :return: Nothing + """ + if blueprint.name in self.blueprints: + assert self.blueprints[blueprint.name] is blueprint, \ + 'A blueprint\'s name collision occurred between %r and ' \ + '%r. Both share the same name "%s". ' % \ + (blueprint, self.blueprints[blueprint.name], blueprint.name) + else: + self.blueprints[blueprint.name] = blueprint + self._blueprint_order.append(blueprint) + blueprint.register(self, options) + + # -------------------------------------------------------------------- # # Request Handling # -------------------------------------------------------------------- # diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py new file mode 100644 index 00000000..a88f2d19 --- /dev/null +++ b/tests/test_blueprints.py @@ -0,0 +1,60 @@ +from json import loads as json_loads, dumps as json_dumps +from sanic import Sanic +from sanic import Blueprint +from sanic.response import json, text +from sanic.utils import sanic_endpoint_test +from sanic.exceptions import SanicException + + +# ------------------------------------------------------------ # +# GET +# ------------------------------------------------------------ # + +def test_bp(): + app = Sanic('test_text') + bp = Blueprint('test_text') + + @bp.route('/') + def handler(request): + return text('Hello') + + app.register_blueprint(bp) + request, response = sanic_endpoint_test(app) + + assert response.text == 'Hello' + +def test_bp_with_url_prefix(): + app = Sanic('test_text') + bp = Blueprint('test_text', url_prefix='/test1') + + @bp.route('/') + def handler(request): + return text('Hello') + + app.register_blueprint(bp) + request, response = sanic_endpoint_test(app, uri='/test1/') + + assert response.text == 'Hello' + + +def test_several_bp_with_url_prefix(): + app = Sanic('test_text') + bp = Blueprint('test_text', url_prefix='/test1') + bp2 = Blueprint('test_text2', url_prefix='/test2') + + @bp.route('/') + def handler(request): + return text('Hello') + + @bp2.route('/') + def handler2(request): + return text('Hello2') + + app.register_blueprint(bp) + app.register_blueprint(bp2) + request, response = sanic_endpoint_test(app, uri='/test1/') + assert response.text == 'Hello' + + request, response = sanic_endpoint_test(app, uri='/test2/') + assert response.text == 'Hello2' + From fb79f747d4353893c65df0f08a8338bf7e5c239a Mon Sep 17 00:00:00 2001 From: narzeja Date: Sat, 15 Oct 2016 21:53:51 +0200 Subject: [PATCH 2/8] basic blueprint functionality --- sanic/blueprints.py | 74 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/sanic/blueprints.py b/sanic/blueprints.py index dd009ce6..585863bb 100644 --- a/sanic/blueprints.py +++ b/sanic/blueprints.py @@ -1,24 +1,68 @@ -from sanic import Sanic -from sanic import Blueprint -from sanic.response import json, text -app = Sanic(__name__) -blueprint = Blueprint('name', url_prefix='/my_blueprint') -blueprint2 = Blueprint('name2', url_prefix='/my_blueprint2') +class BlueprintSetup(): + """ + """ + + def __init__(self, blueprint, app, options): + self.app = app + self.blueprint = blueprint + self.options = options + + url_prefix = self.options.get('url_prefix') + if url_prefix is None: + url_prefix = self.blueprint.url_prefix + + #: The prefix that should be used for all URLs defined on the + #: blueprint. + self.url_prefix = url_prefix + + def add_url_rule(self, uri, methods=None, handler=None, **options): + """A helper method to register a handler to the application url routes. + + """ + if self.url_prefix: + uri = self.url_prefix + uri + + self.app.router.add(uri, methods, handler) -@blueprint.route('/foo') -async def foo(request): - return json({'msg': 'hi from blueprint'}) +class Blueprint(): + def __init__(self, name, url_prefix=None): + self.name = name + self.url_prefix = url_prefix + self.deferred_functions = [] + + def record(self, func): + """Registers a callback function that is invoked when the blueprint is + registered on the application. + + """ + self.deferred_functions.append(func) -@blueprint2.route('/foo') -async def foo2(request): - return json({'msg': 'hi from blueprint2'}) + def make_setup_state(self, app, options): + """ + """ + return BlueprintSetup(self, app, options) + def register(self, app, options): + """ + """ + state = self.make_setup_state(app, options) + for deferred in self.deferred_functions: + deferred(state) -app.register_blueprint(blueprint) -app.register_blueprint(blueprint2) + def route(self, uri, methods=None): + """ + """ + def decorator(handler): + self.add_url_rule(uri=uri, methods=methods, handler=handler) + return handler + return decorator -app.run(host="0.0.0.0", port=8000, debug=True) + def add_url_rule(self, uri, methods=None, handler=None): + """ + """ + self.record(lambda s: + s.add_url_rule(uri, methods, handler)) From aaf571fae4f26409f18e2ed353a0a51e9d6b0d98 Mon Sep 17 00:00:00 2001 From: narzeja Date: Sat, 15 Oct 2016 21:38:12 +0200 Subject: [PATCH 3/8] Blueprint support, with docs, example, and tests --- docs/blueprints.md | 56 +++++++++++++++++++++++++++++++++++++ examples/blueprints.py | 24 ++++++++++++++++ sanic/__init__.py | 1 + sanic/blueprints.py | 24 ++++++++++++++++ sanic/sanic.py | 17 ++++++++++++ tests/test_blueprints.py | 60 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 182 insertions(+) create mode 100644 docs/blueprints.md create mode 100644 examples/blueprints.py create mode 100644 sanic/blueprints.py create mode 100644 tests/test_blueprints.py diff --git a/docs/blueprints.md b/docs/blueprints.md new file mode 100644 index 00000000..ba3173e1 --- /dev/null +++ b/docs/blueprints.md @@ -0,0 +1,56 @@ +# Blueprints + +Blueprints are objects that can be used for sub-routing within an application. +Instead of adding routes to the application object, blueprints define similar +methods for adding routes, which are then registered with the application in a +flexible and plugable manner. + +## Why? + +Blueprints are especially useful for larger applications, where your application +logic can be broken down into several groups or areas of responsibility. + +It is also useful for API versioning, where one blueprint may point at +`/v1/`, and another pointing at `/v2/`. + + +## My First Blueprint + +The following shows a very simple blueprint that registers a handler-function at +the root `/` of your application. + +Suppose you save this file as `my_blueprint.py`, this can be imported in your +main application later. + +```python +from sanic.response import json +from sanic import Blueprint + +bp = Blueprint('my_blueprint') + +@bp.route('/') +async def bp_root(): + return json({'my': 'blueprint'}) + +``` + +## Registering Blueprints +Blueprints must be registered with the application. + +```python +from sanic import Sanic +from my_blueprint import bp + +app = Sanic(__name__) +app.register_blueprint(bp) + +app.run(host='0.0.0.0', port=8000, debug=True) +``` + +This will add the blueprint to the application and register any routes defined +by that blueprint. +In this example, the registered routes in the `app.router` will look like: + +```python +[Route(handler=, methods=None, pattern=re.compile('^/$'), parameters=[])] +``` diff --git a/examples/blueprints.py b/examples/blueprints.py new file mode 100644 index 00000000..dd009ce6 --- /dev/null +++ b/examples/blueprints.py @@ -0,0 +1,24 @@ +from sanic import Sanic +from sanic import Blueprint +from sanic.response import json, text + + +app = Sanic(__name__) +blueprint = Blueprint('name', url_prefix='/my_blueprint') +blueprint2 = Blueprint('name2', url_prefix='/my_blueprint2') + + +@blueprint.route('/foo') +async def foo(request): + return json({'msg': 'hi from blueprint'}) + + +@blueprint2.route('/foo') +async def foo2(request): + return json({'msg': 'hi from blueprint2'}) + + +app.register_blueprint(blueprint) +app.register_blueprint(blueprint2) + +app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/sanic/__init__.py b/sanic/__init__.py index 52dc810a..5b893df4 100644 --- a/sanic/__init__.py +++ b/sanic/__init__.py @@ -1 +1,2 @@ from .sanic import Sanic +from .blueprints import Blueprint diff --git a/sanic/blueprints.py b/sanic/blueprints.py new file mode 100644 index 00000000..dd009ce6 --- /dev/null +++ b/sanic/blueprints.py @@ -0,0 +1,24 @@ +from sanic import Sanic +from sanic import Blueprint +from sanic.response import json, text + + +app = Sanic(__name__) +blueprint = Blueprint('name', url_prefix='/my_blueprint') +blueprint2 = Blueprint('name2', url_prefix='/my_blueprint2') + + +@blueprint.route('/foo') +async def foo(request): + return json({'msg': 'hi from blueprint'}) + + +@blueprint2.route('/foo') +async def foo2(request): + return json({'msg': 'hi from blueprint2'}) + + +app.register_blueprint(blueprint) +app.register_blueprint(blueprint2) + +app.run(host="0.0.0.0", port=8000, debug=True) diff --git a/sanic/sanic.py b/sanic/sanic.py index f7ed0dc0..e8ab5ff7 100644 --- a/sanic/sanic.py +++ b/sanic/sanic.py @@ -85,6 +85,23 @@ class Sanic: return middleware + def register_blueprint(self, blueprint, **options): + """ + Registers a blueprint on the application. + :param blueprint: Blueprint object + :param options: option dictionary with blueprint defaults + :return: Nothing + """ + if blueprint.name in self.blueprints: + assert self.blueprints[blueprint.name] is blueprint, \ + 'A blueprint\'s name collision occurred between %r and ' \ + '%r. Both share the same name "%s". ' % \ + (blueprint, self.blueprints[blueprint.name], blueprint.name) + else: + self.blueprints[blueprint.name] = blueprint + self._blueprint_order.append(blueprint) + blueprint.register(self, options) + # -------------------------------------------------------------------- # # Request Handling # -------------------------------------------------------------------- # diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py new file mode 100644 index 00000000..a88f2d19 --- /dev/null +++ b/tests/test_blueprints.py @@ -0,0 +1,60 @@ +from json import loads as json_loads, dumps as json_dumps +from sanic import Sanic +from sanic import Blueprint +from sanic.response import json, text +from sanic.utils import sanic_endpoint_test +from sanic.exceptions import SanicException + + +# ------------------------------------------------------------ # +# GET +# ------------------------------------------------------------ # + +def test_bp(): + app = Sanic('test_text') + bp = Blueprint('test_text') + + @bp.route('/') + def handler(request): + return text('Hello') + + app.register_blueprint(bp) + request, response = sanic_endpoint_test(app) + + assert response.text == 'Hello' + +def test_bp_with_url_prefix(): + app = Sanic('test_text') + bp = Blueprint('test_text', url_prefix='/test1') + + @bp.route('/') + def handler(request): + return text('Hello') + + app.register_blueprint(bp) + request, response = sanic_endpoint_test(app, uri='/test1/') + + assert response.text == 'Hello' + + +def test_several_bp_with_url_prefix(): + app = Sanic('test_text') + bp = Blueprint('test_text', url_prefix='/test1') + bp2 = Blueprint('test_text2', url_prefix='/test2') + + @bp.route('/') + def handler(request): + return text('Hello') + + @bp2.route('/') + def handler2(request): + return text('Hello2') + + app.register_blueprint(bp) + app.register_blueprint(bp2) + request, response = sanic_endpoint_test(app, uri='/test1/') + assert response.text == 'Hello' + + request, response = sanic_endpoint_test(app, uri='/test2/') + assert response.text == 'Hello2' + From d761c34e8ba0e5ff79a34ff7ba40569f977c11dc Mon Sep 17 00:00:00 2001 From: narzeja Date: Sat, 15 Oct 2016 21:53:51 +0200 Subject: [PATCH 4/8] basic blueprint functionality --- sanic/blueprints.py | 74 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/sanic/blueprints.py b/sanic/blueprints.py index dd009ce6..585863bb 100644 --- a/sanic/blueprints.py +++ b/sanic/blueprints.py @@ -1,24 +1,68 @@ -from sanic import Sanic -from sanic import Blueprint -from sanic.response import json, text -app = Sanic(__name__) -blueprint = Blueprint('name', url_prefix='/my_blueprint') -blueprint2 = Blueprint('name2', url_prefix='/my_blueprint2') +class BlueprintSetup(): + """ + """ + + def __init__(self, blueprint, app, options): + self.app = app + self.blueprint = blueprint + self.options = options + + url_prefix = self.options.get('url_prefix') + if url_prefix is None: + url_prefix = self.blueprint.url_prefix + + #: The prefix that should be used for all URLs defined on the + #: blueprint. + self.url_prefix = url_prefix + + def add_url_rule(self, uri, methods=None, handler=None, **options): + """A helper method to register a handler to the application url routes. + + """ + if self.url_prefix: + uri = self.url_prefix + uri + + self.app.router.add(uri, methods, handler) -@blueprint.route('/foo') -async def foo(request): - return json({'msg': 'hi from blueprint'}) +class Blueprint(): + def __init__(self, name, url_prefix=None): + self.name = name + self.url_prefix = url_prefix + self.deferred_functions = [] + + def record(self, func): + """Registers a callback function that is invoked when the blueprint is + registered on the application. + + """ + self.deferred_functions.append(func) -@blueprint2.route('/foo') -async def foo2(request): - return json({'msg': 'hi from blueprint2'}) + def make_setup_state(self, app, options): + """ + """ + return BlueprintSetup(self, app, options) + def register(self, app, options): + """ + """ + state = self.make_setup_state(app, options) + for deferred in self.deferred_functions: + deferred(state) -app.register_blueprint(blueprint) -app.register_blueprint(blueprint2) + def route(self, uri, methods=None): + """ + """ + def decorator(handler): + self.add_url_rule(uri=uri, methods=methods, handler=handler) + return handler + return decorator -app.run(host="0.0.0.0", port=8000, debug=True) + def add_url_rule(self, uri, methods=None, handler=None): + """ + """ + self.record(lambda s: + s.add_url_rule(uri, methods, handler)) From b19452a1a70662d5679dd5cb3a2a926f13307b2b Mon Sep 17 00:00:00 2001 From: narzeja Date: Sun, 16 Oct 2016 06:48:49 +0200 Subject: [PATCH 5/8] assert warning clarification and adding blueprint containers to app object --- examples/blueprints.py | 2 +- sanic/sanic.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/blueprints.py b/examples/blueprints.py index dd009ce6..f612185c 100644 --- a/examples/blueprints.py +++ b/examples/blueprints.py @@ -5,7 +5,7 @@ from sanic.response import json, text app = Sanic(__name__) blueprint = Blueprint('name', url_prefix='/my_blueprint') -blueprint2 = Blueprint('name2', url_prefix='/my_blueprint2') +blueprint2 = Blueprint('name', url_prefix='/my_blueprint2') @blueprint.route('/foo') diff --git a/sanic/sanic.py b/sanic/sanic.py index e8ab5ff7..45b00aeb 100644 --- a/sanic/sanic.py +++ b/sanic/sanic.py @@ -21,6 +21,8 @@ class Sanic: self.config = Config() self.request_middleware = [] self.response_middleware = [] + self.blueprints = {} + self._blueprint_order = [] # -------------------------------------------------------------------- # # Registration @@ -94,9 +96,9 @@ class Sanic: """ if blueprint.name in self.blueprints: assert self.blueprints[blueprint.name] is blueprint, \ - 'A blueprint\'s name collision occurred between %r and ' \ - '%r. Both share the same name "%s". ' % \ - (blueprint, self.blueprints[blueprint.name], blueprint.name) + 'A blueprint with the name "%s" is already registered. ' \ + 'Blueprint names must be unique.' % \ + (blueprint.name,) else: self.blueprints[blueprint.name] = blueprint self._blueprint_order.append(blueprint) From 95124fc584907b7016168a1c1378b0a578a37926 Mon Sep 17 00:00:00 2001 From: narzeja Date: Sun, 16 Oct 2016 06:50:10 +0200 Subject: [PATCH 6/8] update readme with links to blueprint docs, remove from TODO list --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e268881f..107905b3 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ app.run(host="0.0.0.0", port=8000) * [Routing](docs/routing.md) * [Middleware](docs/middleware.md) * [Exceptions](docs/exceptions.md) + * [Blueprints](docs/blueprints.md) * [Contributing](docs/contributing.md) * [License](LICENSE) @@ -48,7 +49,6 @@ app.run(host="0.0.0.0", port=8000) * File output * Examples of integrations with 3rd-party modules * RESTful router - * Blueprints? ## Limitations: * No wheels for uvloop and httptools on Windows :( From 40b1ec91f084080813e8b17d4fc20c308973bd6e Mon Sep 17 00:00:00 2001 From: Channel Cat Date: Sun, 16 Oct 2016 01:48:51 -0700 Subject: [PATCH 7/8] Added blueprint exception and middleware support, and tests --- sanic/blueprints.py | 57 +++++++++++++++++++++++++++++++--------- tests/test_blueprints.py | 57 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/sanic/blueprints.py b/sanic/blueprints.py index 585863bb..506c2e90 100644 --- a/sanic/blueprints.py +++ b/sanic/blueprints.py @@ -1,6 +1,4 @@ - - -class BlueprintSetup(): +class BlueprintSetup: """ """ @@ -17,26 +15,41 @@ class BlueprintSetup(): #: blueprint. self.url_prefix = url_prefix - def add_url_rule(self, uri, methods=None, handler=None, **options): - """A helper method to register a handler to the application url routes. - + def add_route(self, handler, uri, methods): + """ + A helper method to register a handler to the application url routes. """ if self.url_prefix: uri = self.url_prefix + uri self.app.router.add(uri, methods, handler) + def add_exception(self, handler, *args, **kwargs): + """ + Registers exceptions to sanic + """ + self.app.exception(*args, **kwargs)(handler) -class Blueprint(): + def add_middleware(self, middleware, *args, **kwargs): + """ + Registers middleware to sanic + """ + if args or kwargs: + self.app.middleware(*args, **kwargs)(middleware) + else: + self.app.middleware(middleware) + + +class Blueprint: def __init__(self, name, url_prefix=None): self.name = name self.url_prefix = url_prefix self.deferred_functions = [] def record(self, func): - """Registers a callback function that is invoked when the blueprint is + """ + Registers a callback function that is invoked when the blueprint is registered on the application. - """ self.deferred_functions.append(func) @@ -57,12 +70,30 @@ class Blueprint(): """ """ def decorator(handler): - self.add_url_rule(uri=uri, methods=methods, handler=handler) + self.record(lambda s: s.add_route(handler, uri, methods)) return handler return decorator - def add_url_rule(self, uri, methods=None, handler=None): + def middleware(self, *args, **kwargs): """ """ - self.record(lambda s: - s.add_url_rule(uri, methods, handler)) + + def register_middleware(middleware): + self.record(lambda s: s.add_middleware(middleware, *args, **kwargs)) + return middleware + + # Detect which way this was called, @middleware or @middleware('AT') + if len(args) == 1 and len(kwargs) == 0 and callable(args[0]): + args = [] + return register_middleware(args[0]) + else: + return register_middleware + + def exception(self, *args, **kwargs): + """ + """ + def decorator(handler): + self.record(lambda s: s.add_exception(handler, *args, **kwargs)) + return handler + return decorator + diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index a88f2d19..8068160f 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -1,9 +1,8 @@ -from json import loads as json_loads, dumps as json_dumps from sanic import Sanic -from sanic import Blueprint +from sanic.blueprints import Blueprint from sanic.response import json, text from sanic.utils import sanic_endpoint_test -from sanic.exceptions import SanicException +from sanic.exceptions import NotFound, ServerError, InvalidUsage # ------------------------------------------------------------ # @@ -58,3 +57,55 @@ def test_several_bp_with_url_prefix(): request, response = sanic_endpoint_test(app, uri='/test2/') assert response.text == 'Hello2' + +def test_bp_middleware(): + app = Sanic('test_middleware') + blueprint = Blueprint('test_middleware') + + @blueprint.middleware('response') + async def process_response(request, response): + return text('OK') + + @app.route('/') + async def handler(request): + return text('FAIL') + + app.register_blueprint(blueprint) + + request, response = sanic_endpoint_test(app) + + assert response.status == 200 + assert response.text == 'OK' + +def test_bp_exception_handler(): + app = Sanic('test_middleware') + blueprint = Blueprint('test_middleware') + + @blueprint.route('/1') + def handler_1(request): + raise InvalidUsage("OK") + + @blueprint.route('/2') + def handler_2(request): + raise ServerError("OK") + + @blueprint.route('/3') + def handler_3(request): + raise NotFound("OK") + + @blueprint.exception(NotFound, ServerError) + def handler_exception(request, exception): + return text("OK") + + app.register_blueprint(blueprint) + + request, response = sanic_endpoint_test(app, uri='/1') + assert response.status == 400 + + + request, response = sanic_endpoint_test(app, uri='/2') + assert response.status == 200 + assert response.text == 'OK' + + request, response = sanic_endpoint_test(app, uri='/3') + assert response.status == 200 \ No newline at end of file From 2ba2ee156d3911cf3814ee15d4c6b915700ed3ab Mon Sep 17 00:00:00 2001 From: Channel Cat Date: Sun, 16 Oct 2016 01:59:04 -0700 Subject: [PATCH 8/8] Added documentation for middleware and exceptions in blueprints --- docs/blueprints.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/blueprints.md b/docs/blueprints.md index ba3173e1..1880ae88 100644 --- a/docs/blueprints.md +++ b/docs/blueprints.md @@ -54,3 +54,29 @@ In this example, the registered routes in the `app.router` will look like: ```python [Route(handler=, methods=None, pattern=re.compile('^/$'), parameters=[])] ``` + +## Middleware +Blueprints must be registered with the application. + +```python +@bp.middleware +async def halt_request(request): + print("I am a spy") + +@bp.middleware('request') +async def halt_request(request): + return text('I halted the request') + +@bp.middleware('response') +async def halt_response(request, response): + return text('I halted the response') +``` + +## Exceptions +Blueprints must be registered with the application. + +```python +@bp.exception(NotFound) +def ignore_404s(request, exception): + return text("Yep, I totally found the page: {}".format(request.url)) +``` \ No newline at end of file