From fcdd33e0254b32097e80f0de8433976b5f6f3918 Mon Sep 17 00:00:00 2001 From: Na'aman Hirschfeld Date: Mon, 13 Dec 2021 13:20:50 +0100 Subject: [PATCH 1/3] added discord badge --- README.md | 2 ++ docs/0-introduction.md | 8 +++++--- docs/usage/0-the-starlite-app.md | 11 +++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 docs/usage/0-the-starlite-app.md diff --git a/README.md b/README.md index 3c7060ffee..e3ccd4c444 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ StarLite logo +![Discord](https://img.shields.io/discord/919193495116337154?logo=discord) + # starlite Pre-Alpha WIP. diff --git a/docs/0-introduction.md b/docs/0-introduction.md index 086c2a8f28..705152b6e2 100644 --- a/docs/0-introduction.md +++ b/docs/0-introduction.md @@ -1,10 +1,12 @@ -# Starlite +# StarLite -Starlite is a flexible and extensible ASGI API framework built on top of Starlette and Pydantic. +Starlite is a simple, flexible and extensible ASGI API framework built on top of Starlette and Pydantic. It was inspired +by FastAPI and NestJS. ## Example: Controller Pattern -Starlite supports class API components called "Controllers". Controllers are meant to group logical subcomponents, for example - consider the following `UserController`: +Starlite supports class API components called "Controllers". Controllers are meant to group logical subcomponents, for +example - consider the following `UserController`: ```python3 from pydantic import BaseModel, UUID4 diff --git a/docs/usage/0-the-starlite-app.md b/docs/usage/0-the-starlite-app.md new file mode 100644 index 0000000000..22057e1890 --- /dev/null +++ b/docs/usage/0-the-starlite-app.md @@ -0,0 +1,11 @@ +# The Starlite App + +At the root of every starlite application is a single instance of the `Starlite` class or a subclass of it. Instantiating the app is straightforward: + +```python +from starlite import Starlite + +app = Starlite() +``` + +The From 24a6e937bded1fa9cf551afb9521d4a5d1a14dce Mon Sep 17 00:00:00 2001 From: Na'aman Hirschfeld Date: Mon, 13 Dec 2021 18:02:10 +0100 Subject: [PATCH 2/3] added default logging config --- docs/usage/0-the-starlite-app.md | 19 +++++++++++++++-- docs/usage/1-route-handlers.md | 0 docs/usage/2-life-cycle.md | 0 docs/usage/3-dependency-injection.md | 0 docs/usage/4-middleware.md | 0 docs/usage/5-exceptions.md | 0 docs/usage/6-testing.md | 0 starlite/app.py | 5 +++++ starlite/logging.py | 31 ++++++++++++++++++++++++++++ starlite/routing.py | 1 + tests/test_logging.py | 13 ++++++++++++ 11 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 docs/usage/1-route-handlers.md create mode 100644 docs/usage/2-life-cycle.md create mode 100644 docs/usage/3-dependency-injection.md create mode 100644 docs/usage/4-middleware.md create mode 100644 docs/usage/5-exceptions.md create mode 100644 docs/usage/6-testing.md create mode 100644 starlite/logging.py create mode 100644 tests/test_logging.py diff --git a/docs/usage/0-the-starlite-app.md b/docs/usage/0-the-starlite-app.md index 22057e1890..56becd8cd5 100644 --- a/docs/usage/0-the-starlite-app.md +++ b/docs/usage/0-the-starlite-app.md @@ -1,6 +1,8 @@ # The Starlite App -At the root of every starlite application is a single instance of the `Starlite` class or a subclass of it. Instantiating the app is straightforward: +At the root of every starlite application is an instance of the `Starlite` class or a subclass of it: + +Instantiating the app is straightforward: ```python from starlite import Starlite @@ -8,4 +10,17 @@ from starlite import Starlite app = Starlite() ``` -The +The `Starlite` class supports the following (optional) kwargs: + +* `debug`: a boolean flag toggling debug mode on and off +* `middleware`: a sequence of `Middleware` subclasses +* `exception_handlers`: a dictionary mapping exceptions or exception codes to callables +* `route_handlers`: a sequence of route handlers +* `on_startup`: a sequence of callables to be called during the application startup +* `on_shutdown`: a sequence of callables to be called during the application shutdown +* `lifespan`: an async-context that handles startup and shutdown +* `dependencies`: a dictionary mapping keys to dependency providers +* `logging_config`: either a subclass of `starlite.logging.LoggingConfig` or None (disable logging) + +> :warning: **Warning**: debug should not be used in production +> :warning: **Warning**: you can specify either `on_startup`/`on_shutdown` or `lifespan` but not both diff --git a/docs/usage/1-route-handlers.md b/docs/usage/1-route-handlers.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/usage/2-life-cycle.md b/docs/usage/2-life-cycle.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/usage/3-dependency-injection.md b/docs/usage/3-dependency-injection.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/usage/4-middleware.md b/docs/usage/4-middleware.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/usage/5-exceptions.md b/docs/usage/5-exceptions.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/usage/6-testing.md b/docs/usage/6-testing.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/starlite/app.py b/starlite/app.py index ad904fcf0c..72077db13e 100644 --- a/starlite/app.py +++ b/starlite/app.py @@ -15,6 +15,7 @@ from typing_extensions import Type from starlite.handlers import RouteHandler +from starlite.logging import LoggingConfig from starlite.provide import Provide from starlite.routing import Router from starlite.utils import DeprecatedProperty @@ -27,6 +28,7 @@ class Starlite(Starlette): def __init__( # pylint: disable=super-init-not-called self, + *, debug: bool = False, middleware: Sequence[Middleware] = None, exception_handlers: Dict[Union[int, Type[Exception]], Callable] = None, @@ -35,7 +37,10 @@ def __init__( # pylint: disable=super-init-not-called on_shutdown: Optional[Sequence[Callable]] = None, lifespan: Optional[Callable[[Any], AsyncContextManager]] = None, dependencies: Optional[Dict[str, Provide]] = None, + logging_config: Optional[LoggingConfig] = LoggingConfig() ): + if logging_config: + logging_config.configure(debug) self._debug = debug self.state = State() self.router = Router( diff --git a/starlite/logging.py b/starlite/logging.py new file mode 100644 index 0000000000..df3d470823 --- /dev/null +++ b/starlite/logging.py @@ -0,0 +1,31 @@ +from logging import config +from typing import Dict, Literal, Optional, Union + +from pydantic import BaseModel + + +class LoggingConfig(BaseModel): + version: Literal[1] = 1 + incremental: bool = False + disable_existing_loggers: bool = False + filters: Optional[Dict[str, dict]] = None + formatters: Dict[str, dict] = { + "standard": {"format": "%(levelname)s - %(asctime)s - %(name)s - %(module)s - %(message)s"} + } + handlers: Dict[str, dict] = { + "console": {"class": "logging.StreamHandler", "level": "DEBUG", "formatter": "standard"} + } + loggers: Dict[str, dict] = { + "starlite": { + "level": "INFO", + "handlers": ["console"], + }, + } + root: Dict[str, Union[dict, list, str]] = {"handlers": ["console"], "level": "WARNING"} + + def configure(self, debug: bool = False): + """Configure logging by converting 'self' to dict and passing it to logging.config.dictConfig""" + logging_config = self.dict(exclude_none=True) + if debug and "starlite" in logging_config["loggers"]: + logging_config["loggers"]["starlite"]["level"] = "DEBUG" + config.dictConfig(logging_config) diff --git a/starlite/routing.py b/starlite/routing.py index 1b292c0058..7995959fd0 100644 --- a/starlite/routing.py +++ b/starlite/routing.py @@ -78,6 +78,7 @@ class Router(StarletteRouter): def __init__( self, + *, path: str, route_handlers: Optional[Sequence[Union[Type[Controller], RouteHandler, "Router", Callable]]] = None, redirect_slashes: bool = True, diff --git a/tests/test_logging.py b/tests/test_logging.py new file mode 100644 index 0000000000..babb408a75 --- /dev/null +++ b/tests/test_logging.py @@ -0,0 +1,13 @@ +from unittest.mock import Mock, patch + +from starlite.logging import LoggingConfig + + +@patch("logging.config.dictConfig") +def test_logging_debug(dictConfigMock: Mock): + config = LoggingConfig() + config.configure() + assert dictConfigMock.call_args.args[0]["loggers"]["starlite"]["level"] == "INFO" + dictConfigMock.reset_mock() + config.configure(debug=True) + assert dictConfigMock.call_args.args[0]["loggers"]["starlite"]["level"] == "DEBUG" From f68687478811699c537fada716e04ef489c393dd Mon Sep 17 00:00:00 2001 From: Na'aman Hirschfeld Date: Mon, 13 Dec 2021 18:51:47 +0100 Subject: [PATCH 3/3] fixed import of Literal for py3.7 --- starlite/logging.py | 3 ++- tests/test_logging.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/starlite/logging.py b/starlite/logging.py index df3d470823..a3a060b1ab 100644 --- a/starlite/logging.py +++ b/starlite/logging.py @@ -1,7 +1,8 @@ from logging import config -from typing import Dict, Literal, Optional, Union +from typing import Dict, Optional, Union from pydantic import BaseModel +from typing_extensions import Literal class LoggingConfig(BaseModel): diff --git a/tests/test_logging.py b/tests/test_logging.py index babb408a75..f399dc11ed 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -7,7 +7,7 @@ def test_logging_debug(dictConfigMock: Mock): config = LoggingConfig() config.configure() - assert dictConfigMock.call_args.args[0]["loggers"]["starlite"]["level"] == "INFO" + assert dictConfigMock.mock_calls[0][1][0]["loggers"]["starlite"]["level"] == "INFO" dictConfigMock.reset_mock() config.configure(debug=True) - assert dictConfigMock.call_args.args[0]["loggers"]["starlite"]["level"] == "DEBUG" + assert dictConfigMock.mock_calls[0][1][0]["loggers"]["starlite"]["level"] == "DEBUG"