mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-27 13:34:28 +00:00
Rework constellation, somehow without breaking almost anything
This commit is contained in:
parent
b44b5fc587
commit
5d47c7c510
5 changed files with 158 additions and 20 deletions
52
.idea/codeStyles/Project.xml
Normal file
52
.idea/codeStyles/Project.xml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<DBN-PSQL>
|
||||||
|
<case-options enabled="true">
|
||||||
|
<option name="KEYWORD_CASE" value="lower" />
|
||||||
|
<option name="FUNCTION_CASE" value="lower" />
|
||||||
|
<option name="PARAMETER_CASE" value="lower" />
|
||||||
|
<option name="DATATYPE_CASE" value="lower" />
|
||||||
|
<option name="OBJECT_CASE" value="preserve" />
|
||||||
|
</case-options>
|
||||||
|
<formatting-settings enabled="false" />
|
||||||
|
</DBN-PSQL>
|
||||||
|
<DBN-SQL>
|
||||||
|
<case-options enabled="true">
|
||||||
|
<option name="KEYWORD_CASE" value="lower" />
|
||||||
|
<option name="FUNCTION_CASE" value="lower" />
|
||||||
|
<option name="PARAMETER_CASE" value="lower" />
|
||||||
|
<option name="DATATYPE_CASE" value="lower" />
|
||||||
|
<option name="OBJECT_CASE" value="preserve" />
|
||||||
|
</case-options>
|
||||||
|
<formatting-settings enabled="false">
|
||||||
|
<option name="STATEMENT_SPACING" value="one_line" />
|
||||||
|
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
|
||||||
|
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||||
|
</formatting-settings>
|
||||||
|
</DBN-SQL>
|
||||||
|
<DBN-PSQL>
|
||||||
|
<case-options enabled="true">
|
||||||
|
<option name="KEYWORD_CASE" value="lower" />
|
||||||
|
<option name="FUNCTION_CASE" value="lower" />
|
||||||
|
<option name="PARAMETER_CASE" value="lower" />
|
||||||
|
<option name="DATATYPE_CASE" value="lower" />
|
||||||
|
<option name="OBJECT_CASE" value="preserve" />
|
||||||
|
</case-options>
|
||||||
|
<formatting-settings enabled="false" />
|
||||||
|
</DBN-PSQL>
|
||||||
|
<DBN-SQL>
|
||||||
|
<case-options enabled="true">
|
||||||
|
<option name="KEYWORD_CASE" value="lower" />
|
||||||
|
<option name="FUNCTION_CASE" value="lower" />
|
||||||
|
<option name="PARAMETER_CASE" value="lower" />
|
||||||
|
<option name="DATATYPE_CASE" value="lower" />
|
||||||
|
<option name="OBJECT_CASE" value="preserve" />
|
||||||
|
</case-options>
|
||||||
|
<formatting-settings enabled="false">
|
||||||
|
<option name="STATEMENT_SPACING" value="one_line" />
|
||||||
|
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
|
||||||
|
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||||
|
</formatting-settings>
|
||||||
|
</DBN-SQL>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
11
.idea/dataSources.xml
Normal file
11
.idea/dataSources.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="scaleway.steffo.eu" uuid="fe046003-9eda-4855-a2da-ae3a0de3aacd">
|
||||||
|
<driver-ref>postgresql</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:postgresql://scaleway.steffo.eu:5432/royalnet</jdbc-url>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -7,6 +7,7 @@ import royalnet.herald as rh
|
||||||
import royalnet.utils as ru
|
import royalnet.utils as ru
|
||||||
import royalnet.commands as rc
|
import royalnet.commands as rc
|
||||||
from .star import PageStar, ExceptionStar
|
from .star import PageStar, ExceptionStar
|
||||||
|
from ..utils import init_logging
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
@ -51,7 +52,8 @@ class Constellation:
|
||||||
herald_cfg: Dict[str, Any],
|
herald_cfg: Dict[str, Any],
|
||||||
packs_cfg: Dict[str, Any],
|
packs_cfg: Dict[str, Any],
|
||||||
constellation_cfg: Dict[str, Any],
|
constellation_cfg: Dict[str, Any],
|
||||||
**_):
|
logging_cfg: Dict[str, Any]
|
||||||
|
):
|
||||||
if Starlette is None:
|
if Starlette is None:
|
||||||
raise ImportError("`constellation` extra is not installed")
|
raise ImportError("`constellation` extra is not installed")
|
||||||
|
|
||||||
|
@ -87,9 +89,19 @@ class Constellation:
|
||||||
self.alchemy = ra.Alchemy(alchemy_cfg["database_url"], tables)
|
self.alchemy = ra.Alchemy(alchemy_cfg["database_url"], tables)
|
||||||
log.info(f"Alchemy: {self.alchemy}")
|
log.info(f"Alchemy: {self.alchemy}")
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
self._logging_cfg: Dict[str, Any] = logging_cfg
|
||||||
|
"""The logging config for the :class:`Constellation` is stored to initialize the logger when the first page is
|
||||||
|
requested, as disabling the :mod:`uvicorn` logging also disables all logging in the process in general."""
|
||||||
|
|
||||||
# Herald
|
# Herald
|
||||||
self.herald: Optional[rh.Link] = None
|
self.herald: Optional[rh.Link] = None
|
||||||
"""The :class:`Link` object connecting the :class:`Constellation` to the rest of the herald network."""
|
"""The :class:`Link` object connecting the :class:`Constellation` to the rest of the herald network.
|
||||||
|
As is the case with the logging module, it will be started on the first request received by the
|
||||||
|
:class:`Constellation`, as the event loop won't be available before that."""
|
||||||
|
|
||||||
|
self._herald_cfg: Dict[str, Any] = herald_cfg
|
||||||
|
"""The herald config for the :class:`Constellation` is stored to initialize the :class:`rh.Herald` later."""
|
||||||
|
|
||||||
self.herald_task: Optional[aio.Task] = None
|
self.herald_task: Optional[aio.Task] = None
|
||||||
"""A reference to the :class:`aio.Task` that runs the :class:`rh.Link`."""
|
"""A reference to the :class:`aio.Task` that runs the :class:`rh.Link`."""
|
||||||
|
@ -120,8 +132,7 @@ class Constellation:
|
||||||
elif not herald_cfg["enabled"]:
|
elif not herald_cfg["enabled"]:
|
||||||
log.info("Herald: disabled")
|
log.info("Herald: disabled")
|
||||||
else:
|
else:
|
||||||
self.init_herald(herald_cfg)
|
log.info(f"Herald: will be enabled on first request")
|
||||||
log.info(f"Herald: enabled")
|
|
||||||
|
|
||||||
# Register PageStars and ExceptionStars
|
# Register PageStars and ExceptionStars
|
||||||
for pack_name in packs:
|
for pack_name in packs:
|
||||||
|
@ -151,6 +162,11 @@ class Constellation:
|
||||||
self.port: int = constellation_cfg["port"]
|
self.port: int = constellation_cfg["port"]
|
||||||
"""The port on which the :class:`Constellation` will listen for connection on."""
|
"""The port on which the :class:`Constellation` will listen for connection on."""
|
||||||
|
|
||||||
|
self.loop: Optional[aio.AbstractEventLoop] = None
|
||||||
|
"""The event loop of the :class:`Constellation`.
|
||||||
|
|
||||||
|
Because of how :mod:`uvicorn` runs, it will stay :const:`None` until the first page is requested."""
|
||||||
|
|
||||||
# TODO: is this a good idea?
|
# TODO: is this a good idea?
|
||||||
def interface_factory(self) -> Type[rc.CommandInterface]:
|
def interface_factory(self) -> Type[rc.CommandInterface]:
|
||||||
"""Create the :class:`rc.CommandInterface` class for the :class:`Constellation`."""
|
"""Create the :class:`rc.CommandInterface` class for the :class:`Constellation`."""
|
||||||
|
@ -241,29 +257,52 @@ class Constellation:
|
||||||
log.debug(f"Registering: {SelectedEvent.__qualname__} -> {SelectedEvent.name}")
|
log.debug(f"Registering: {SelectedEvent.__qualname__} -> {SelectedEvent.name}")
|
||||||
self.events[SelectedEvent.name] = event
|
self.events[SelectedEvent.name] = event
|
||||||
|
|
||||||
|
def _first_page_check(self):
|
||||||
|
if self.loop is None:
|
||||||
|
self.loop = aio.get_running_loop()
|
||||||
|
self.init_herald(self._herald_cfg)
|
||||||
|
self.loop.create_task(self.herald.run())
|
||||||
|
init_logging(self._logging_cfg)
|
||||||
|
|
||||||
|
def _page_star_wrapper(self, page_star: PageStar):
|
||||||
|
async def f(request):
|
||||||
|
self._first_page_check()
|
||||||
|
log.info(f"Running {page_star}")
|
||||||
|
return await page_star.page(request)
|
||||||
|
|
||||||
|
return page_star.path, f, page_star.methods
|
||||||
|
|
||||||
|
def _exc_star_wrapper(self, exc_star: ExceptionStar):
|
||||||
|
async def f(request):
|
||||||
|
self._first_page_check()
|
||||||
|
log.info(f"Running {exc_star}")
|
||||||
|
return await exc_star.page(request)
|
||||||
|
|
||||||
|
return exc_star.error, f
|
||||||
|
|
||||||
def register_page_stars(self, page_stars: List[Type[PageStar]], pack_cfg: Dict[str, Any]):
|
def register_page_stars(self, page_stars: List[Type[PageStar]], pack_cfg: Dict[str, Any]):
|
||||||
for SelectedPageStar in page_stars:
|
for SelectedPageStar in page_stars:
|
||||||
log.debug(f"Registering: {SelectedPageStar.path} -> {SelectedPageStar.__qualname__}")
|
log.debug(f"Registering: {SelectedPageStar.path} -> {SelectedPageStar.__qualname__}")
|
||||||
try:
|
try:
|
||||||
page_star_instance = SelectedPageStar(constellation=self, config=pack_cfg)
|
page_star_instance = SelectedPageStar(interface=self.Interface(pack_cfg))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Skipping: "
|
log.error(f"Skipping: "
|
||||||
f"{SelectedPageStar.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
f"{SelectedPageStar.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
||||||
ru.sentry_exc(e)
|
ru.sentry_exc(e)
|
||||||
continue
|
continue
|
||||||
self.starlette.add_route(page_star_instance.path, page_star_instance.page, page_star_instance.methods)
|
self.starlette.add_route(*self._page_star_wrapper(page_star_instance))
|
||||||
|
|
||||||
def register_exc_stars(self, exc_stars: List[Type[ExceptionStar]], pack_cfg: Dict[str, Any]):
|
def register_exc_stars(self, exc_stars: List[Type[ExceptionStar]], pack_cfg: Dict[str, Any]):
|
||||||
for SelectedPageStar in exc_stars:
|
for SelectedExcStar in exc_stars:
|
||||||
log.debug(f"Registering: {SelectedPageStar.error} -> {SelectedPageStar.__qualname__}")
|
log.debug(f"Registering: {SelectedExcStar.error} -> {SelectedExcStar.__qualname__}")
|
||||||
try:
|
try:
|
||||||
page_star_instance = SelectedPageStar(constellation=self, config=pack_cfg)
|
exc_star_instance = SelectedExcStar(interface=self.Interface(pack_cfg))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Skipping: "
|
log.error(f"Skipping: "
|
||||||
f"{SelectedPageStar.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
f"{SelectedExcStar.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
||||||
ru.sentry_exc(e)
|
ru.sentry_exc(e)
|
||||||
continue
|
continue
|
||||||
self.starlette.add_exception_handler(page_star_instance.error, page_star_instance.page)
|
self.starlette.add_exception_handler(*self._exc_star_wrapper(exc_star_instance))
|
||||||
|
|
||||||
def run_blocking(self):
|
def run_blocking(self):
|
||||||
log.info(f"Running Constellation on https://{self.address}:{self.port}/...")
|
log.info(f"Running Constellation on https://{self.address}:{self.port}/...")
|
||||||
|
@ -300,7 +339,8 @@ class Constellation:
|
||||||
constellation = cls(alchemy_cfg=alchemy_cfg,
|
constellation = cls(alchemy_cfg=alchemy_cfg,
|
||||||
herald_cfg=herald_cfg,
|
herald_cfg=herald_cfg,
|
||||||
packs_cfg=packs_cfg,
|
packs_cfg=packs_cfg,
|
||||||
constellation_cfg=constellation_cfg)
|
constellation_cfg=constellation_cfg,
|
||||||
|
logging_cfg=logging_cfg)
|
||||||
|
|
||||||
# Run the server
|
# Run the server
|
||||||
constellation.run_blocking()
|
constellation.run_blocking()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from typing import *
|
from typing import *
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.responses import Response
|
from starlette.responses import Response
|
||||||
|
from royalnet.commands import CommandInterface
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .constellation import Constellation
|
from .constellation import Constellation
|
||||||
|
@ -10,9 +11,8 @@ class Star:
|
||||||
"""A Star is a class representing a part of the website.
|
"""A Star is a class representing a part of the website.
|
||||||
|
|
||||||
It shouldn't be used directly: please use :class:`PageStar` and :class:`ExceptionStar` instead!"""
|
It shouldn't be used directly: please use :class:`PageStar` and :class:`ExceptionStar` instead!"""
|
||||||
def __init__(self, config: Dict[str, Any], constellation: "Constellation"):
|
def __init__(self, interface: CommandInterface):
|
||||||
self.config: Dict[str, Any] = config
|
self.interface: CommandInterface = interface
|
||||||
self.constellation: "Constellation" = constellation
|
|
||||||
|
|
||||||
async def page(self, request: Request) -> Response:
|
async def page(self, request: Request) -> Response:
|
||||||
"""The function generating the :class:`~starlette.Response` to a web :class:`~starlette.Request`.
|
"""The function generating the :class:`~starlette.Response` to a web :class:`~starlette.Request`.
|
||||||
|
@ -20,21 +20,31 @@ class Star:
|
||||||
If it raises an error, the corresponding :class:`ExceptionStar` will be used to handle the request instead."""
|
If it raises an error, the corresponding :class:`ExceptionStar` will be used to handle the request instead."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def constellation(self) -> "Constellation":
|
||||||
|
"""A shortcut for the :class:`Constellation`."""
|
||||||
|
return self.interface.constellation
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alchemy(self):
|
def alchemy(self):
|
||||||
"""A shortcut for the :class:`~royalnet.alchemy.Alchemy` of the :class:`Constellation`."""
|
"""A shortcut for the :class:`~royalnet.alchemy.Alchemy` of the :class:`Constellation`."""
|
||||||
return self.constellation.alchemy
|
return self.interface.constellation.alchemy
|
||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
@property
|
@property
|
||||||
def Session(self):
|
def Session(self):
|
||||||
"""A shortcut for the :class:`~royalnet.alchemy.Alchemy` :class:`Session` of the :class:`Constellation`."""
|
"""A shortcut for the :class:`~royalnet.alchemy.Alchemy` :class:`Session` of the :class:`Constellation`."""
|
||||||
return self.constellation.alchemy.Session
|
return self.interface.constellation.alchemy.Session
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def session_acm(self):
|
def session_acm(self):
|
||||||
"""A shortcut for :func:`.alchemy.session_acm` of the :class:`Constellation`."""
|
"""A shortcut for :func:`.alchemy.session_acm` of the :class:`Constellation`."""
|
||||||
return self.constellation.alchemy.session_acm
|
return self.interface.constellation.alchemy.session_acm
|
||||||
|
|
||||||
|
@property
|
||||||
|
def config(self) -> Dict[str, Any]:
|
||||||
|
"""A shortcut for the Pack configuration of the :class:`Constellation`."""
|
||||||
|
return self.interface.config
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.__class__.__qualname__}>"
|
return f"<{self.__class__.__qualname__}>"
|
||||||
|
|
|
@ -9,7 +9,32 @@ except ImportError:
|
||||||
l: logging.Logger = logging.getLogger(__name__)
|
l: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# From https://stackoverflow.com/a/56810619/4334568
|
||||||
|
def reset_logging():
|
||||||
|
manager = logging.root.manager
|
||||||
|
manager.disabled = logging.NOTSET
|
||||||
|
for logger in manager.loggerDict.values():
|
||||||
|
if isinstance(logger, logging.Logger):
|
||||||
|
logger.setLevel(logging.NOTSET)
|
||||||
|
logger.propagate = True
|
||||||
|
logger.disabled = False
|
||||||
|
logger.filters.clear()
|
||||||
|
handlers = logger.handlers.copy()
|
||||||
|
for handler in handlers:
|
||||||
|
# Copied from `logging.shutdown`.
|
||||||
|
try:
|
||||||
|
handler.acquire()
|
||||||
|
handler.flush()
|
||||||
|
handler.close()
|
||||||
|
except (OSError, ValueError):
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
handler.release()
|
||||||
|
logger.removeHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
def init_logging(logging_cfg: Dict[str, Any]):
|
def init_logging(logging_cfg: Dict[str, Any]):
|
||||||
|
reset_logging()
|
||||||
loggers_cfg = logging_cfg["Loggers"]
|
loggers_cfg = logging_cfg["Loggers"]
|
||||||
for logger_name in loggers_cfg:
|
for logger_name in loggers_cfg:
|
||||||
if logger_name == "root":
|
if logger_name == "root":
|
||||||
|
@ -23,7 +48,7 @@ def init_logging(logging_cfg: Dict[str, Any]):
|
||||||
stream_handler.formatter = coloredlogs.ColoredFormatter(logging_cfg["log_format"], style="{")
|
stream_handler.formatter = coloredlogs.ColoredFormatter(logging_cfg["log_format"], style="{")
|
||||||
else:
|
else:
|
||||||
stream_handler.formatter = logging.Formatter(logging_cfg["log_format"], style="{")
|
stream_handler.formatter = logging.Formatter(logging_cfg["log_format"], style="{")
|
||||||
if len(logging.root.handlers) < 1:
|
logging.root.handlers.clear()
|
||||||
logging.root.addHandler(stream_handler)
|
logging.root.addHandler(stream_handler)
|
||||||
|
|
||||||
l.debug("Logging: ready")
|
l.debug("Logging: ready")
|
||||||
|
|
Loading…
Reference in a new issue