def __init__( self, debug: bool = False, routes: typing.Sequence[BaseRoute] = None, middleware: typing.Sequence[Middleware] = None, exception_handlers: typing.Dict[typing.Union[int, typing.Type[Exception]], typing.Callable] = None, on_startup: typing.Sequence[typing.Callable] = None, on_shutdown: typing.Sequence[typing.Callable] = None, lifespan: typing.Callable[["Starlette"], typing.AsyncGenerator] = None, ) -> None: # The lifespan context function is a newer style that replaces # on_startup / on_shutdown handlers. Use one or the other, not both. assert lifespan is None or ( on_startup is None and on_shutdown is None ), "Use either 'lifespan' or 'on_startup'/'on_shutdown', not both." self._debug = debug self.state = State() self.router = Router(routes, on_startup=on_startup, on_shutdown=on_shutdown, lifespan=lifespan) self.exception_handlers = ({} if exception_handlers is None else dict(exception_handlers)) self.user_middleware = [] if middleware is None else list(middleware) self.middleware_stack = self.build_middleware_stack()
def __init__( self, *, engine: Engine = None, sdl: str = None, graphiql: typing.Union[None, bool, GraphiQL] = True, path: str = "/", subscriptions: typing.Union[bool, Subscriptions] = None, context: dict = None, schema_name: str = "default", ) -> None: if engine is None: assert sdl, "`sdl` expected if `engine` not given" engine = Engine(sdl=sdl, schema_name=schema_name) assert engine, "`engine` expected if `sdl` not given" self.engine = engine if context is None: context = {} if graphiql is True: graphiql = GraphiQL() elif not graphiql: graphiql = None assert graphiql is None or isinstance(graphiql, GraphiQL) if subscriptions is True: subscriptions = Subscriptions(path="/subscriptions") elif not subscriptions: subscriptions = None assert subscriptions is None or isinstance(subscriptions, Subscriptions) self.router = Router(on_startup=[self.startup]) if graphiql and graphiql.path is not None: self.router.add_route(path=graphiql.path, endpoint=GraphiQLEndpoint) self.router.add_route(path=path, endpoint=GraphQLEndpoint) if subscriptions is not None: self.router.add_websocket_route(path=subscriptions.path, endpoint=SubscriptionEndpoint) config = GraphQLConfig( engine=self.engine, context=context, graphiql=graphiql, path=path, subscriptions=subscriptions, ) self.app = GraphQLMiddleware(self.router, config=config) self._started_up = False
def __init__( self, debug: bool = False, exception_handlers: Dict[Union[int, Type[Exception]], Callable] = None, middleware: Sequence[Union[Middleware, Callable]] = None, response_class: Optional[Type[Response]] = None, modules: Optional[List[ModuleType]] = None, auto_bind: bool = False, ): self._debug = debug self.injector = Injector(auto_bind=auto_bind) self.router = Router() self.exception_handlers = { exc_type: self._inject_exception_handler(handler) for exc_type, handler in ( {} if exception_handlers is None else dict(exception_handlers) ).items() } self.user_middleware = ( [] if middleware is None else [self.prepare_middleware(m) for m in middleware] ) self.user_middleware.insert(0, Middleware(RequestScopeMiddleware)) self.middleware_stack = self.build_middleware_stack() self.cli = click.Group() self.response_class = response_class or JSONResponse self.injector.binder.bind(AppUnit, to=self, scope=SingletonScope) self.injector.binder.bind(Injector, to=self.injector, scope=SingletonScope) self.injector.binder.bind(Request, to=context.get_current_request) modules = modules or [] for module in modules: self.add_module(module)
async def init_app(app: Application): swagger = Router() swagger.add_route('/schema/{chain}/{network}', custom_schema, include_in_schema=False) create_swagger(apispec, router=swagger) app.mount('/', swagger)
def __init__(self, debug: bool = False, routes: typing.List[BaseRoute] = None) -> None: self._debug = debug self.router = Router(routes) self.exception_middleware = ExceptionMiddleware(self.router, debug=debug) self.error_middleware = ServerErrorMiddleware( self.exception_middleware, debug=debug)
def create_swagger(apispec: APISpec, *, router: Router = None): handler = create_schema_handler(apispec) router.add_route('/schema', handler, include_in_schema=False) router.mount( path='/', app=StaticFiles(packages=[__package__], html=True), name='statics', ) return router
def __init__(self, debug: bool = False, template_directory: str = None) -> None: self._debug = debug self.router = Router() self.exception_middleware = ExceptionMiddleware(self.router, debug=debug) self.error_middleware = ServerErrorMiddleware( self.exception_middleware, debug=debug) self.lifespan_middleware = LifespanMiddleware(self.error_middleware) self.schema_generator = None # type: typing.Optional[BaseSchemaGenerator] self.template_env = self.load_template_env(template_directory)
def test_limit(self): """ Make sure a request is rejected if the client has exceeded the limit. """ app = RateLimitingMiddleware( Router([Route("/", Endpoint)]), InMemoryLimitProvider(limit=5, timespan=1, block_duration=1), ) client = TestClient(app) successful = 0 for i in range(20): response = client.get("/") if response.status_code == 429: break else: successful += 1 self.assertTrue(successful == 5) # After the 'block_duration' has expired, requests should be allowed # again. sleep(1.1) response = client.get("/") self.assertTrue(response.status_code == 200)
def test_lifespan_sync(): startup_complete = False shutdown_complete = False def hello_world(request): return PlainTextResponse("hello, world") def run_startup(): nonlocal startup_complete startup_complete = True def run_shutdown(): nonlocal shutdown_complete shutdown_complete = True app = Router( on_startup=[run_startup], on_shutdown=[run_shutdown], routes=[Route("/", hello_world)], ) assert not startup_complete assert not shutdown_complete with TestClient(app) as client: assert startup_complete assert not shutdown_complete client.get("/") assert startup_complete assert shutdown_complete
def routes(cls): mount = cls.mount_name() return Router([ Route("/", endpoint=cls.list_view, methods=["GET"], name=f"{mount}_list"), Route( "/create", endpoint=cls.create_view, methods=["GET", "POST"], name=f"{mount}_create", ), Route( f"/{cls.routing_id_part}/edit", endpoint=cls.update_view, methods=["GET", "POST"], name=f"{mount}_edit", ), Route( f"/{cls.routing_id_part}/delete", endpoint=cls.delete_view, methods=["GET", "POST"], name=f"{mount}_delete", ), ])
def __init__(self, debug: bool = False) -> None: self.router = Router(routes=[]) self.lifespan_handler = LifespanHandler() self.app = self.router self.exception_middleware = ExceptionMiddleware(self.router, debug=debug) self.executor = ThreadPoolExecutor()
class BackendStarlette(ABCBackend): def __init__(self, api): self.api = api self.router = Router() def bind(self, app): """Bind the current API to given Starlette app.""" app.router.routes.append(Mount(self.api.prefix, self.router)) def register(self, endpoint, *paths, methods=None): """Register the given Endpoint in routing.""" for path in paths: self.router.add_route(path, endpoint.as_view(self.api), methods=methods) return endpoint def to_response(self, response, status_code=200, **options): """Prepare a response.""" if isinstance(response, Response): return response return JSONResponse(response, status_code=status_code, **options) def get_params(self, request): """Get Path params from the given request.""" return dict(request.path_params) def get_query(self, request): """Get Query params from the given request.""" return dict(request.query_params) async def get_data(self, request): """Get data from the given request.""" content_type = request.headers.get('content-type') if content_type in { 'application/x-www-form-urlencoded', 'multipart/form-data' }: return await request.form() if content_type == 'application/json': return await request.json() data = await request.body() return data.decode('utf-8')
def test_mount_urls(): mounted = Router([Mount("/users", ok, name="users")]) client = TestClient(mounted) assert client.get("/users").status_code == 200 assert client.get("/users").url == "http://testserver/users/" assert client.get("/users/").status_code == 200 assert client.get("/users/a").status_code == 200 assert client.get("/usersa").status_code == 404
def test_reverse_mount_urls(): mounted = Router([Mount("/users", ok, name="users")]) assert mounted.url_path_for("users", path="/a") == "/users/a" users = Router([Route("/{username}", ok, name="user")]) mounted = Router([Mount("/{subpath}/users", users, name="users")]) assert (mounted.url_path_for("users:user", subpath="test", username="******") == "/test/users/tom") assert (mounted.url_path_for("users", subpath="test", path="/tom") == "/test/users/tom")
def test_datetime_convertor(test_client_factory, app: Router): client = test_client_factory(app) response = client.get("/datetime/2020-01-01T00:00:00") assert response.json() == {"datetime": "2020-01-01T00:00:00"} assert (app.url_path_for("datetime-convertor", param=datetime( 1996, 1, 22, 23, 0, 0)) == "/datetime/1996-01-22T23:00:00")
def launch_emergency_app(): """Launches a minimal application to inform the user about the incorrect configuration""" emergency_app = Starlette(debug=True) emergency_app = Router([ Route('/{whatever:path}', endpoint=emergency_response, methods=['GET', 'POST']), ]) uvicorn.run(emergency_app, host=WEBGUI_HOST, port=WEBGUI_PORT)
def serverStart(): from starlette.applications import Starlette from starlette.routing import Router from starlette.routing import Route import uvicorn app = Router([Route('/', endpoint=graphQL, methods=['GET', 'POST'])]) uvicorn.run(app, host='0.0.0.0', port=8000) return None
def test_raise_on_startup(): def run_startup(): raise RuntimeError() app = Router(routes=[Lifespan(on_startup=run_startup)]) with pytest.raises(RuntimeError): with TestClient(app): pass # pragma: nocover
def test_raise_on_shutdown(): def run_shutdown(): raise RuntimeError() app = Router(on_shutdown=[run_shutdown]) with pytest.raises(RuntimeError): with TestClient(app): pass # pragma: nocover
def test_routes(): assert app.routes == [ Route("/func", endpoint=func_homepage, methods=["GET"]), Route("/async", endpoint=async_homepage, methods=["GET"]), Route("/class", endpoint=Homepage), Mount( "/users", app=Router(routes=[ Route("/", endpoint=all_users_page), Route("/{username}", endpoint=user_page), ]), ), Host( "{subdomain}.example.org", app=Router(routes=[Route("/", endpoint=custom_subdomain)]), ), Route("/500", endpoint=runtime_error, methods=["GET"]), WebSocketRoute("/ws", endpoint=websocket_endpoint), ]
def __init__( self, debug: bool = False, routes: typing.List[BaseRoute] = None, middleware: typing.List[typing.Optional[Middleware]] = None, exception_handlers: typing.Dict[ typing.Union[int, typing.Type[Exception]], typing.Callable ] = None, on_startup: typing.List[typing.Callable] = None, on_shutdown: typing.List[typing.Callable] = None, ) -> None: self._debug = debug self.state = State() self.router = Router(routes, on_startup=on_startup, on_shutdown=on_shutdown) self.exception_handlers = ( {} if exception_handlers is None else dict(exception_handlers) ) self.user_middleware = [item for item in middleware or [] if item is not None] self.middleware_stack = self.build_middleware_stack()
def __init__( self, debug: bool = False, routes: typing.Sequence[BaseRoute] = None, middleware: typing.Sequence[Middleware] = None, exception_handlers: typing.Dict[ typing.Union[int, typing.Type[Exception]], typing.Callable ] = None, on_startup: typing.Sequence[typing.Callable] = None, on_shutdown: typing.Sequence[typing.Callable] = None, ) -> None: self._debug = debug self.state = State() self.router = Router(routes, on_startup=on_startup, on_shutdown=on_shutdown) self.exception_handlers = ( {} if exception_handlers is None else dict(exception_handlers) ) self.user_middleware = [] if middleware is None else list(middleware) self.middleware_stack = self.build_middleware_stack()
def __init__( self, *, engine: Engine = None, sdl: str = None, graphiql: typing.Union[bool, GraphiQL] = True, path: str = "/", subscriptions: typing.Union[bool, Subscriptions] = None, context: dict = None, schema_name: str = "default", ): if engine is None: assert sdl, "`sdl` expected if `engine` not given" engine = Engine(sdl=sdl, schema_name=schema_name) assert engine, "`engine` expected if `sdl` not given" self.engine = engine if context is None: context = {} if graphiql is True: graphiql = GraphiQL() if subscriptions is True: subscriptions = Subscriptions(path="/subscriptions") routes = [] if graphiql and graphiql.path is not None: routes.append(Route(path=graphiql.path, endpoint=GraphiQLEndpoint)) graphql_route = Route(path=path, endpoint=GraphQLEndpoint) routes.append(graphql_route) if subscriptions is not None: subscription_route = WebSocketRoute(path=subscriptions.path, endpoint=SubscriptionEndpoint) routes.append(subscription_route) config = GraphQLConfig( engine=self.engine, context=context, graphiql=graphiql, path=graphql_route.path, subscriptions=subscriptions, ) router = Router(routes=routes) self.app = GraphQLMiddleware(router, config=config) self.lifespan = Lifespan(on_startup=self.startup) self._started_up = False
def setup_routes(app: Starlette): app.mount( '/', Router([ Route('/', endpoint=word_form, methods=['GET', 'POST'], name='index'), Route('/', endpoint=learn_word, methods=['GET'], name='learn'), Mount('/learn', name='learn', app=Router([ Route('/', endpoint=learn_word, methods=['GET'], name='index'), Route('/{word_id:int}/{answer:int}', endpoint=learned_word, methods=['GET'], name='learned'), ])), Mount('/w', name='word', app=Router([ Route('/create', endpoint=word_create, methods=['GET', 'POST'], name='create'), Route('/{word_id:int}/detail', endpoint=word_detail, methods=['GET'], name='detail'), Route('/{word_id:int}/delete', endpoint=word_delete, methods=['POST'], name='delete'), ])), # Auth Mount('/auth', name='auth', app=Router([*auth_routes])), # API Mount('/api', app=Router([ Mount('/v1', app=Router([ Mount('/status', app=Router([ Route('/', endpoint=status, methods=['GET']), ])), ])), ])), Mount('/static', static_files, name='static'), ]))
def __init__(self, visible=True): # self.app can be any ASGI app, but it must exist self.visible = visible self.app = Router() self.url = "" # will be replaced during webserver init self.cache = {"skip": {}, "deps": {}} self.listeners = { "startup": set(), "shutdown": set(), "pipeline_update": set() } self.lastPipeline = None
def swagger_ui( schema_url: str = "/openapi.json", swagger_ui_title: str = "Piccolo Swagger UI", csrf_cookie_name: t.Optional[str] = DEFAULT_COOKIE_NAME, csrf_header_name: t.Optional[str] = DEFAULT_HEADER_NAME, ): """ Even though ASGI frameworks such as FastAPI and BlackSheep have endpoints for viewing OpenAPI / Swagger docs, out of the box they don't work well with some Piccolo middleware (namely CSRF middleware, which requires Swagger UI to add the CSRF token to the header of each API call). By using this endpoint instead, it will work correctly with CSRF. **FastAPI example** .. code-block:: python from fastapi import FastAPI from piccolo_api.openapi.endpoints import swagger_ui # By setting these values to None, we disable the builtin endpoints. app = FastAPI(docs_url=None, redoc_url=None) app.mount('/docs', swagger_ui()) :param schema_url: The URL to the OpenAPI schema. :param csrf_cookie_name: The name of the CSRF cookie. :param csrf_header_name: The HTTP header name which the CSRF cookie value will be added to. """ # We return a router, because it's effectively a mini ASGI # app, which can be mounted in any ASGI app which supports mounting. router = Router() class DocsEndpoint(HTTPEndpoint): def get(self, request: Request): template = ENVIRONMENT.get_template("swagger_ui.html.jinja") html = template.render( schema_url=schema_url, swagger_ui_title=swagger_ui_title, csrf_cookie_name=csrf_cookie_name, csrf_header_name=csrf_header_name, ) return HTMLResponse(content=html) class OAuthRedirectEndpoint(HTTPEndpoint): def get(self, request: Request): return get_swagger_ui_oauth2_redirect_html() router.add_route("/", endpoint=DocsEndpoint) router.add_route("/oauth2-redirect/", endpoint=OAuthRedirectEndpoint) return router
def setup_server(self): # This could maybe be added to the API... log.info("Setting up server") self.add_exception_handler(WebargsHTTPException, http_exception) if self.api_loaded: return self.setup_database() route_table = [] self.run_hook("add-routes", route_table) self.mount("/", Router(route_table)) self.run_hook("finalize-routes") self.run_hook("asgi-setup", self) self.api_loaded = True log.info("Finished setting up server")
def app() -> Router: register_url_convertor("datetime", DateTimeConvertor()) def datetime_convertor(request): param = request.path_params["param"] assert isinstance(param, datetime) return JSONResponse({"datetime": param.strftime("%Y-%m-%dT%H:%M:%S")}) return Router(routes=[ Route( "/datetime/{param:datetime}", endpoint=datetime_convertor, name="datetime-convertor", ) ])
def test_hooks(self): # TODO Replace these with mocks ... def pre_login_test(username): assert isinstance(username, str) async def pre_login_test_async(username): assert isinstance(username, str) def login_success_test(username, user_id): assert isinstance(username, str) assert isinstance(user_id, int) async def login_success_test_async(username, user_id): assert isinstance(username, str) assert isinstance(user_id, int) def login_failure_test(username): assert isinstance(username, str) def login_failure_test_async(username): assert isinstance(username, str) router = Router( routes=[ Route( "/login/", session_login( hooks=LoginHooks( pre_login=[pre_login_test, pre_login_test_async], login_success=[ login_success_test, login_success_test_async, ], login_failure=[ login_failure_test, login_failure_test_async, ], ) ), ), ] ) app = ExceptionMiddleware(router) BaseUser(**self.credentials, active=True).save().run_sync() client = TestClient(app) client.post("/login/", json=self.credentials)
def __init__(self, visible=True): self.visible = visible self.app = Router() if NT_AVAIL: self.netdict = NetworkDict("/CameraPublisher") self.funcs = {} # {name: route} self.index_route = [ Route("/", LiteralTemplate(self.TEMPLATE, funcs=self.funcs.keys())) ] self.listeners = { "startup": set(), "shutdown": set(), "pipeline_update": set() } self._update()