def create_app(): app = Vibora(router_strategy=RouterStrategy.CLONE) # app.configure_static_files() app.add_blueprint(bp_api) app.logger = logger job_queue = Queue(connection=StrictRedis.from_url(app_config.redis_conn)) app.components.add(job_queue) return app
async def test_prefix_with_dynamic_route(self): app = Vibora() bp = Blueprint() @bp.route("/<name>") async def home(name: str): return JsonResponse({"name": name}) app.add_blueprint(bp, prefixes={"test": "/test"}) response = await app.test_client().request("/test/test") self.assertEqual(response.json(), {"name": "test"})
def create_app(config_name): app = Vibora() @app.handle(Events.AFTER_ENDPOINT) async def before_response(response: Response): response.headers['x-my-custom-header'] = 'Hello :)' app.components.add(config[config_name]()) app.add_blueprint(api, prefixes={'v1': '/v1'}) return app
class WebApp: def __init__(self, app): self.app = app print('\nInitializing Vibora webapp.') self.vibora = Vibora(static=StaticHandler( # Add the statics handler paths=self.app.config.statics, url_prefix='/static', max_cache_size=1 * 1024 * 1024)) print(f'Added static folders: {self.app.config.statics}') # Miyagi app and config should be avaiable in every handler self.vibora.components.add(self.app) self.vibora.components.add(self.app.config) # Make all the Miyagi webapp components self._make_gui() self._make_json_api() def _make_json_api(self): print('\nInitializing JsonApi routes:') # Import in function for it's a circular import from .apis.jsonapi import JsonApi self._make_blueprint('json_api', JsonApi) def _make_gui(self): print('\nInitializing Web frontend:') # Import in function for it's a circular import from .gui import Gui self._make_blueprint('web', Gui) def _make_blueprint(self, bp_name: str, bp: MiyagiBlueprint): # Make a blueprint vibora_bp = Blueprint() # Make it accessible in self setattr(self, bp_name, vibora_bp) # get all the routes in the given MiyagiBlueprint for route in bp(self.app).endpoints: # Register the route in the new blueprint self._add_route(vibora_bp, route) # Register the Blueprint in the Vibora App self.vibora.add_blueprint(vibora_bp) def _add_route(self, blueprint: Blueprint, route: MiyagiRoute): print( f'Adding route: {self.vibora.url_scheme}://{self.app.config.host}:{self.app.config.port}{route.uri}' ) # Call the Vibora blueprint's decorator blueprint.route(route.uri, methods=route.methods)(route.handler)
class BlueprintsTestCase(TestSuite): def setUp(self): self.app = Vibora(router_strategy=RouterStrategy.STRICT) async def test_simple_add_blueprint__expects_added(self): b1 = Blueprint() @b1.route('/') async def home(): return Response(b'123') self.app.add_blueprint(b1) async with self.app.test_client() as client: response = await client.request('/') self.assertEqual(response.content, b'123') async def test_simple_add_blueprint_with_prefix_expects_added(self): b1 = Blueprint() @b1.route('/') async def home(): return Response(b'123') self.app.add_blueprint(b1, prefixes={'home': '/home'}) async with self.app.test_client() as client: response = await client.request('/home/') self.assertEqual(response.content, b'123') async def test_simple_add_nested_blueprints(self): b1 = Blueprint() b2 = Blueprint() @b2.route('/123') async def home(): return Response(b'123') b1.add_blueprint(b2) self.app.add_blueprint(b1) async with self.app.test_client() as client: response = await client.request('/123') self.assertEqual(response.content, b'123') async def test_simple_add_nested_blueprints_with_prefixes(self): b1 = Blueprint() b2 = Blueprint() @b2.route('/123') async def home(): return Response(b'123') b1.add_blueprint(b2, prefixes={'a': '/a', 'b': '/b'}) self.app.add_blueprint(b1, prefixes={'a': '/a', 'b': '/b'}) async with self.app.test_client() as client: response = await client.request('/a/a/123') self.assertEqual(response.content, b'123') response = await self.app.test_client().request('/b/b/123') self.assertEqual(response.content, b'123')
class BlueprintsTestCase(TestSuite): def setUp(self): self.app = Vibora(router_strategy=RouterStrategy.STRICT) async def test_simple_add_blueprint__expects_added(self): b1 = Blueprint() @b1.route("/") async def home(): return Response(b"123") self.app.add_blueprint(b1) async with self.app.test_client() as client: response = await client.request("/") self.assertEqual(response.content, b"123") async def test_simple_add_blueprint_with_prefix_expects_added(self): b1 = Blueprint() @b1.route("/") async def home(): return Response(b"123") self.app.add_blueprint(b1, prefixes={"home": "/home"}) async with self.app.test_client() as client: response = await client.request("/home/") self.assertEqual(response.content, b"123") async def test_simple_add_nested_blueprints(self): b1 = Blueprint() b2 = Blueprint() @b2.route("/123") async def home(): return Response(b"123") b1.add_blueprint(b2) self.app.add_blueprint(b1) async with self.app.test_client() as client: response = await client.request("/123") self.assertEqual(response.content, b"123") async def test_simple_add_nested_blueprints_with_prefixes(self): b1 = Blueprint() b2 = Blueprint() @b2.route("/123") async def home(): return Response(b"123") b1.add_blueprint(b2, prefixes={"a": "/a", "b": "/b"}) self.app.add_blueprint(b1, prefixes={"a": "/a", "b": "/b"}) async with self.app.test_client() as client: response = await client.request("/a/a/123") self.assertEqual(response.content, b"123") response = await self.app.test_client().request("/b/b/123") self.assertEqual(response.content, b"123")
class WebApp: def __init__(self, app): self.app = app print('\nInitializing Vibora webapp.') self.vibora = Vibora( static=StaticHandler(paths=self.app.config.statics, url_prefix='/static', max_cache_size=1 * 1024 * 1024)) self.vibora.components.add(self.app) self.vibora.components.add(self.app.config) print(f'Added static folders: {self.app.config.statics}') self._make_gui() self._make_json_api() def _make_json_api(self): print('\nInitializing JsonApi routes:') from .apis.jsonapi import JsonApi self.json_api = Blueprint() for route in JsonApi(self.app).endpoints: self._add_route(self.json_api, route) self.vibora.add_blueprint(self.json_api) def _make_gui(self): print('\nInitializing Web frontend:') from .gui import Gui self.web = Blueprint() for route in Gui(self.app).pages: self._add_route(self.web, route) self.vibora.add_blueprint(self.web) def _add_route(self, blueprint: Blueprint, route: MiyagiRoute): print( f'Adding route: {self.vibora.url_scheme}://{self.app.config.host}:{self.app.config.port}{route.uri}' ) blueprint.route(route.uri, methods=route.methods)(route.handler)
from vibora import Vibora from vibora.router import RouterStrategy from .api import api from .user.views import user_api from app.user.model import db app = Vibora(router_strategy=RouterStrategy.CLONE) app.add_blueprint(user_api, prefixes={'v1': '/v1'}) app.add_blueprint(api, prefixes={'v1': '/v1'}) db.bind(provider='sqlite', filename='database.sqlite', create_db=True) db.generate_mapping(create_tables=True)
class HooksTestSuite(TestSuite): def setUp(self): self.app = Vibora() async def test_simple_before_request_with_component(self): @self.app.handle(Events.BEFORE_ENDPOINT) def before_endpoint(request: Request): request.context['status_code'] = 200 @self.app.route('/') async def home(request: Request): return Response(b'', status_code=request.context.get( 'status_code', 500)) async with self.app.test_client() as client: response = await client.get('/') self.assertEqual(response.status_code, 200) async def test_before_request_halting(self): @self.app.handle(Events.BEFORE_ENDPOINT) def before_endpoint(): return Response(b'', status_code=200) @self.app.route('/') async def home(): return Response(b'', status_code=500) async with self.app.test_client() as client: response = await client.get('/') self.assertEqual(response.status_code, 200) async def test_before_request_propagation(self): @self.app.handle(Events.BEFORE_ENDPOINT) def before_endpoint_1(request: Request): request.context['counter'] = 1 @self.app.handle(Events.BEFORE_ENDPOINT) def before_endpoint_2(request: Request): request.context['counter'] += 1 @self.app.route('/') async def home(request: Request): return Response(str(request.context['counter']).encode(), status_code=500) async with self.app.test_client() as client: response = await client.get('/') self.assertEqual(response.content, b'2') async def test_before_server_start(self): secret = uuid.uuid4().hex.encode() class RuntimeConfig: def __init__(self): self.secret = secret @self.app.handle(Events.BEFORE_SERVER_START) def before_server_start(app: Vibora): app.components.add(RuntimeConfig()) @self.app.route('/') async def home(request: Request): return Response(request.app.components.get(RuntimeConfig).secret, status_code=200) async with self.app.test_client() as client: response = await client.get('/') self.assertEqual(secret, response.content) async def test_after_server_start(self): secret = uuid.uuid4().hex.encode() class RuntimeConfig: def __init__(self): self.secret = secret @self.app.route('/') async def home(request: Request): try: return Response( request.app.components.get(RuntimeConfig).secret, status_code=200) except MissingComponent: return Response(b'', status_code=500) @self.app.handle(Events.AFTER_SERVER_START) async def before_server_start(app: Vibora): await asyncio.sleep(1) app.components.add(RuntimeConfig()) async with self.app.test_client() as client: # This must return an internal server error because the component is not registered yet # and the server is already online. response = await client.get('/') self.assertEqual(response.status_code, 500) # We wait a little to let the after_server_start hook finish his job. await asyncio.sleep(1) async with self.app.test_client() as client: response = await client.get('/') self.assertEqual(response.status_code, 200) self.assertEqual(secret, response.content) async def test_after_endpoint_modify_response(self): @self.app.route('/') async def home(): return Response(b'', status_code=500) @self.app.handle(Events.AFTER_ENDPOINT) async def after_response(r: Response): r.status_code = 200 async with self.app.test_client() as client: response = await client.get('/') self.assertEqual(response.status_code, 200) async def test_after_response_sent(self): class Mock: def __init__(self): self.test = 'test' @self.app.route('/') async def home(request: Request): try: request.app.components.get(Mock) return Response(b'Second') except Exception as error: return Response(str(error).encode(), status_code=500) @self.app.handle(Events.AFTER_RESPONSE_SENT) async def after_response_sent(app: Vibora): try: app.components.add(Mock()) except ValueError: pass async with self.app.test_client() as client: response = await client.get('/') self.assertEqual(response.status_code, 500) await asyncio.sleep(0) response = await client.get('/') self.assertEqual(response.status_code, 200) async def test_before_server_stop_expects_polite_shutdown(self): """ This test tries to ensure that the server will respect current streaming connections and give it some time before closing them so requests are closed abruptly. :return: """ @self.app.route('/') async def home(): async def slow_streaming(): for _ in range(0, 5): yield b'123' await asyncio.sleep(1) return StreamingResponse(slow_streaming) async with self.app.test_client() as client: response = await client.get('/', stream=True) # This sends a kill signal to all workers, pretty much like someone is trying to stop the server. # Our HTTP client already sent the request but didn't consumed the response yet, the server must # respectfully wait for us. time.sleep(1) self.app.clean_up() await response.read_content() self.assertEqual(response.content, b'123' * 5) async def test_after_response_sent_called_from_blueprint(self): b1 = Blueprint() class Mock: def __init__(self): self.test = 'test' @b1.route('/') async def home(request: Request): try: request.app.components.get(Mock) return Response(b'Second') except Exception as error: return Response(str(error).encode(), status_code=500) @b1.handle(Events.AFTER_RESPONSE_SENT) async def after_response_sent(app: Vibora): try: app.components.add(Mock()) except ValueError: pass self.app.add_blueprint(b1, prefixes={'v1': '/v1'}) async with self.app.test_client() as client: response = await client.get('/v1') self.assertEqual(response.status_code, 500) await asyncio.sleep(0) response = await client.get('/v1') self.assertEqual(response.status_code, 200) async def test_before_response_called_from_blueprint(self): b1 = Blueprint() class Mock: def __init__(self): self.test = 'test' @b1.handle(Events.BEFORE_SERVER_START) async def after_response_sent(app: Vibora): try: app.components.add(Mock()) except ValueError: pass @b1.route('/') async def home(mock: Mock): return Response(mock.test.encode()) self.app.add_blueprint(b1, prefixes={'v1': '/v1'}) async with self.app.test_client() as client: response = await client.get('/v1') self.assertEqual(response.status_code, 200)
class BlueprintsTestCase(TestSuite): def setUp(self): self.app = Vibora() async def test_simple_sub_domain_expects_match(self): b1 = Blueprint(hosts=['.*']) @b1.route('/') async def home(): return Response(b'123') self.app.add_blueprint(b1) with self.app.test_client() as client: response = await client.request('/') self.assertEqual(response.content, b'123') async def test_exact_match_sub_domain_expects_match(self): b1 = Blueprint(hosts=['test.vibora.io']) @b1.route('/') async def home(): return Response(b'123') self.app.add_blueprint(b1) with self.app.test_client() as client: response = await client.request('/', headers={'Host': 'test.vibora.io'}) self.assertEqual(response.content, b'123') async def test_different_sub_domain_expects_404(self): b1 = Blueprint(hosts=['test.vibora.io']) @b1.route('/') async def home(): return Response(b'123') self.app.add_blueprint(b1) with self.app.test_client() as client: response = await client.request( '/', headers={'Host': 'test2.vibora.io'}) self.assertEqual(response.status_code, 404) async def test_sub_domain_working_with_non_hosts_based(self): b1 = Blueprint(hosts=['test.vibora.io']) b2 = Blueprint() @b1.route('/') async def home(): return Response(b'123') @b2.route('/test') async def home(): return Response(b'123') self.app.add_blueprint(b1) self.app.add_blueprint(b2) with self.app.test_client() as client: response = await client.request('/', headers={'Host': 'test.vibora.io'}) self.assertEqual(response.status_code, 200) response = await client.request( '/', headers={'Host': 'test2.vibora.io'}) self.assertEqual(response.status_code, 404) response = await client.request( '/test', headers={'Host': 'anything.should.work'}) self.assertEqual(response.status_code, 200) response = await client.request( '/test2', headers={'Host': 'anything.should.404'}) self.assertEqual(response.status_code, 404)
class ExceptionsTestCase(TestSuite): def setUp(self): self.app = Vibora() async def test_simple_exception_expects_handled(self): @self.app.route("/") async def home(): raise Exception("Vibora ;)") @self.app.handle(Exception) async def handle_errors(): return Response(b"Catch!", status_code=500) async with self.app.test_client() as client: response = await client.get("/") self.assertEqual(response.status_code, 500) self.assertEqual(response.content, b"Catch!") async def test_simple_exception_with_default_handler(self): @self.app.route("/") async def handle_errors(): raise Exception("Vibora ;)") response = await self.app.test_client().request("/") self.assertEqual(response.status_code, 500) async def test_different_exception__expects_not_handled(self): class NewException(Exception): pass @self.app.route("/") async def handle_errors(): raise Exception("Vibora ;)") @self.app.handle(NewException) async def handle_errors2(): return Response(b"Catch!", status_code=500) response = await self.app.test_client().request("/") self.assertEqual(response.status_code, 500) self.assertNotEqual(response.content, b"Catch!") async def test_subclassed_exception__expects_handled(self): class ParentException(Exception): pass class ChildException(ParentException): pass @self.app.route("/") async def handle_errors(): raise ChildException("Vibora ;)") @self.app.handle(ParentException) async def handle_errors2(): return Response(b"Catch!", status_code=500) response = await self.app.test_client().request("/") self.assertEqual(response.status_code, 500) self.assertEqual(response.content, b"Catch!") async def test_specific_priority_exception_catch_expects_handled(self): class ParentException(Exception): pass class ChildException(ParentException): pass @self.app.route("/") async def handle_errors(): raise ChildException("Vibora ;)") @self.app.handle(ParentException) async def handle_errors2(): return Response(b"Parent!", status_code=500) @self.app.handle(ChildException) async def handle_errors3(): return Response(b"Child!", status_code=500) response = await self.app.test_client().request("/") self.assertEqual(response.status_code, 500) self.assertEqual(response.content, b"Child!") async def test_harder_subclass_exception_catch_expects_handled(self): class ExceptionA(Exception): pass class ExceptionB(ExceptionA): pass class ExceptionC(ExceptionB): pass @self.app.route("/") async def handle_errors(): raise ExceptionC("Vibora ;)") @self.app.handle(ExceptionA) async def handle_errors2(): return Response(b"Wrong!", status_code=500) @self.app.handle(ExceptionB) async def handle_errors3(): return Response(b"Correct!", status_code=500) response = await self.app.test_client().request("/") self.assertEqual(response.status_code, 500) self.assertEqual(response.content, b"Correct!") async def test_blueprint_exception_propagation(self): b1 = Blueprint() class ExceptionA(Exception): pass class ExceptionB(ExceptionA): pass class ExceptionC(ExceptionB): pass @b1.route("/") async def handle_errors(): raise ExceptionC("Vibora ;)") @self.app.handle(ExceptionA) async def handle_errors2(): return Response(b"Wrong!", status_code=500) @self.app.handle(ExceptionB) async def handle_errors3(): return Response(b"Correct!", status_code=500) self.app.add_blueprint(b1, prefixes={"": ""}) response = await self.app.test_client().request("/") self.assertEqual(response.status_code, 500) self.assertEqual(response.content, b"Correct!") async def test_nested_blueprint_exception_propagation(self): b1 = Blueprint() b2 = Blueprint() class ExceptionA(Exception): pass @b2.route("/") async def handle_errors(): raise ExceptionA("Vibora ;)") @self.app.handle(ExceptionA) async def handle_errors2(): return Response(b"Wrong!", status_code=500) b1.add_blueprint(b2, prefixes={"": ""}) self.app.add_blueprint(b1, prefixes={"": ""}) response = await self.app.test_client().request("/") self.assertEqual(response.status_code, 500) self.assertEqual(response.content, b"Wrong!") async def test_nested_blueprint_exception_propagation_conflicts(self): b1 = Blueprint() b2 = Blueprint() class ExceptionA(Exception): pass @b2.route("/") async def handle_errors(): raise ExceptionA("Vibora ;)") @self.app.handle(ExceptionA) async def handle_errors2(): return Response(b"Wrong!", status_code=500) b1.add_blueprint(b2, prefixes={"": ""}) self.app.add_blueprint(b1, prefixes={"": ""}) response = await self.app.test_client().request("/") self.assertEqual(response.status_code, 500) self.assertEqual(response.content, b"Wrong!") async def test_multiple_exceptions_at_a_single_handle(self): b1 = Blueprint() b2 = Blueprint() class ExceptionA(Exception): pass @b2.route("/") async def handle_errors(): raise ExceptionA("Vibora ;)") @self.app.handle((IOError, ExceptionA)) async def handle_errors2(): return Response(b"Correct!", status_code=500) b1.add_blueprint(b2, prefixes={"": ""}) self.app.add_blueprint(b1, prefixes={"": ""}) response = await self.app.test_client().request("/") self.assertEqual(response.status_code, 500) self.assertEqual(response.content, b"Correct!") async def test_exception_flow_expects_parent_response(self): @self.app.route("/") async def handle_errors(): raise IOError("Vibora ;)") @self.app.handle(IOError) async def handle_io(request: Request): request.context["called"] = True @self.app.handle(Exception) async def handle_errors2(request: Request): return JsonResponse({"called": request.context.get("called")}, status_code=500) async with self.app.test_client() as client: response = await client.request("/") self.assertEqual(response.status_code, 500) self.assertDictEqual(response.json(), {"called": True})
class BlueprintsTestCase(TestSuite): def setUp(self): self.app = Vibora() async def test_simple_sub_domain_expects_match(self): b1 = Blueprint(hosts=[".*"]) @b1.route("/") async def home(): return Response(b"123") self.app.add_blueprint(b1) async with self.app.test_client() as client: response = await client.request("/") self.assertEqual(response.content, b"123") async def test_exact_match_sub_domain_expects_match(self): b1 = Blueprint(hosts=["test.vibora.io"]) @b1.route("/") async def home(): return Response(b"123") self.app.add_blueprint(b1) async with self.app.test_client() as client: response = await client.request("/", headers={"Host": "test.vibora.io"}) self.assertEqual(response.content, b"123") async def test_different_sub_domain_expects_404(self): b1 = Blueprint(hosts=["test.vibora.io"]) @b1.route("/") async def home(): return Response(b"123") self.app.add_blueprint(b1) async with self.app.test_client() as client: response = await client.request( "/", headers={"Host": "test2.vibora.io"}) self.assertEqual(response.status_code, 404) async def test_sub_domain_working_with_non_hosts_based(self): b1 = Blueprint(hosts=["test.vibora.io"]) b2 = Blueprint() @b1.route("/") async def home(): return Response(b"123") @b2.route("/test") async def home2(): return Response(b"123") self.app.add_blueprint(b1) self.app.add_blueprint(b2) async with self.app.test_client() as client: response = await client.request("/", headers={"Host": "test.vibora.io"}) self.assertEqual(response.status_code, 200) response = await client.request( "/", headers={"Host": "test2.vibora.io"}) self.assertEqual(response.status_code, 404) response = await client.request( "/test", headers={"Host": "anything.should.work"}) self.assertEqual(response.status_code, 200) response = await client.request( "/test2", headers={"Host": "anything.should.404"}) self.assertEqual(response.status_code, 404)
from vibora import Vibora from vibora.responses import JsonResponse from samples.blueprints.v1.routes import v1 from samples.blueprints.v2.routes import v2 app = Vibora() @app.route('/') def home(): return JsonResponse({'a': 1}) @app.handle(404) def handle_anything(): app.url_for('') return JsonResponse({'global': 'handler'}) if __name__ == '__main__': v1.add_blueprint(v2, prefixes={'v2': '/v2'}) app.add_blueprint(v1, prefixes={'v1': '/v1', '': '/'}) app.add_blueprint(v2, prefixes={'v2': '/v2'}) app.run(debug=True, port=8000, host='0.0.0.0', workers=1)