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.commands as rc
|
||||
from .star import PageStar, ExceptionStar
|
||||
from ..utils import init_logging
|
||||
|
||||
try:
|
||||
import uvicorn
|
||||
|
@ -51,7 +52,8 @@ class Constellation:
|
|||
herald_cfg: Dict[str, Any],
|
||||
packs_cfg: Dict[str, Any],
|
||||
constellation_cfg: Dict[str, Any],
|
||||
**_):
|
||||
logging_cfg: Dict[str, Any]
|
||||
):
|
||||
if Starlette is None:
|
||||
raise ImportError("`constellation` extra is not installed")
|
||||
|
||||
|
@ -87,9 +89,19 @@ class Constellation:
|
|||
self.alchemy = ra.Alchemy(alchemy_cfg["database_url"], tables)
|
||||
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
|
||||
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
|
||||
"""A reference to the :class:`aio.Task` that runs the :class:`rh.Link`."""
|
||||
|
@ -120,8 +132,7 @@ class Constellation:
|
|||
elif not herald_cfg["enabled"]:
|
||||
log.info("Herald: disabled")
|
||||
else:
|
||||
self.init_herald(herald_cfg)
|
||||
log.info(f"Herald: enabled")
|
||||
log.info(f"Herald: will be enabled on first request")
|
||||
|
||||
# Register PageStars and ExceptionStars
|
||||
for pack_name in packs:
|
||||
|
@ -151,6 +162,11 @@ class Constellation:
|
|||
self.port: int = constellation_cfg["port"]
|
||||
"""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?
|
||||
def interface_factory(self) -> Type[rc.CommandInterface]:
|
||||
"""Create the :class:`rc.CommandInterface` class for the :class:`Constellation`."""
|
||||
|
@ -241,29 +257,52 @@ class Constellation:
|
|||
log.debug(f"Registering: {SelectedEvent.__qualname__} -> {SelectedEvent.name}")
|
||||
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]):
|
||||
for SelectedPageStar in page_stars:
|
||||
log.debug(f"Registering: {SelectedPageStar.path} -> {SelectedPageStar.__qualname__}")
|
||||
try:
|
||||
page_star_instance = SelectedPageStar(constellation=self, config=pack_cfg)
|
||||
page_star_instance = SelectedPageStar(interface=self.Interface(pack_cfg))
|
||||
except Exception as e:
|
||||
log.error(f"Skipping: "
|
||||
f"{SelectedPageStar.__qualname__} - {e.__class__.__qualname__} in the initialization.")
|
||||
ru.sentry_exc(e)
|
||||
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]):
|
||||
for SelectedPageStar in exc_stars:
|
||||
log.debug(f"Registering: {SelectedPageStar.error} -> {SelectedPageStar.__qualname__}")
|
||||
for SelectedExcStar in exc_stars:
|
||||
log.debug(f"Registering: {SelectedExcStar.error} -> {SelectedExcStar.__qualname__}")
|
||||
try:
|
||||
page_star_instance = SelectedPageStar(constellation=self, config=pack_cfg)
|
||||
exc_star_instance = SelectedExcStar(interface=self.Interface(pack_cfg))
|
||||
except Exception as e:
|
||||
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)
|
||||
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):
|
||||
log.info(f"Running Constellation on https://{self.address}:{self.port}/...")
|
||||
|
@ -300,7 +339,8 @@ class Constellation:
|
|||
constellation = cls(alchemy_cfg=alchemy_cfg,
|
||||
herald_cfg=herald_cfg,
|
||||
packs_cfg=packs_cfg,
|
||||
constellation_cfg=constellation_cfg)
|
||||
constellation_cfg=constellation_cfg,
|
||||
logging_cfg=logging_cfg)
|
||||
|
||||
# Run the server
|
||||
constellation.run_blocking()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from typing import *
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
from royalnet.commands import CommandInterface
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .constellation import Constellation
|
||||
|
@ -10,9 +11,8 @@ class Star:
|
|||
"""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!"""
|
||||
def __init__(self, config: Dict[str, Any], constellation: "Constellation"):
|
||||
self.config: Dict[str, Any] = config
|
||||
self.constellation: "Constellation" = constellation
|
||||
def __init__(self, interface: CommandInterface):
|
||||
self.interface: CommandInterface = interface
|
||||
|
||||
async def page(self, request: Request) -> Response:
|
||||
"""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."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def constellation(self) -> "Constellation":
|
||||
"""A shortcut for the :class:`Constellation`."""
|
||||
return self.interface.constellation
|
||||
|
||||
@property
|
||||
def alchemy(self):
|
||||
"""A shortcut for the :class:`~royalnet.alchemy.Alchemy` of the :class:`Constellation`."""
|
||||
return self.constellation.alchemy
|
||||
return self.interface.constellation.alchemy
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@property
|
||||
def Session(self):
|
||||
"""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
|
||||
def session_acm(self):
|
||||
"""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):
|
||||
return f"<{self.__class__.__qualname__}>"
|
||||
|
|
|
@ -9,7 +9,32 @@ except ImportError:
|
|||
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]):
|
||||
reset_logging()
|
||||
loggers_cfg = logging_cfg["Loggers"]
|
||||
for logger_name in loggers_cfg:
|
||||
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="{")
|
||||
else:
|
||||
stream_handler.formatter = logging.Formatter(logging_cfg["log_format"], style="{")
|
||||
if len(logging.root.handlers) < 1:
|
||||
logging.root.addHandler(stream_handler)
|
||||
logging.root.handlers.clear()
|
||||
logging.root.addHandler(stream_handler)
|
||||
|
||||
l.debug("Logging: ready")
|
||||
|
|
Loading…
Reference in a new issue