Merge branch 'issue2661' into niceback-error-handling

This commit is contained in:
L. Kärkkäinen 2023-02-02 14:14:56 +00:00 committed by GitHub
commit ff47448585
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 59 additions and 56 deletions

View File

@ -9,6 +9,7 @@ omit =
sanic/simple.py sanic/simple.py
sanic/utils.py sanic/utils.py
sanic/cli sanic/cli
sanic/pages
[html] [html]
directory = coverage directory = coverage

View File

@ -17,7 +17,8 @@ ignore:
- "sanic/compat.py" - "sanic/compat.py"
- "sanic/simple.py" - "sanic/simple.py"
- "sanic/utils.py" - "sanic/utils.py"
- "sanic/cli" - "sanic/cli/"
- "sanic/pages/"
- ".github/" - ".github/"
- "changelogs/" - "changelogs/"
- "docker/" - "docker/"

View File

@ -57,6 +57,7 @@ from sanic.config import SANIC_PREFIX, Config
from sanic.exceptions import ( from sanic.exceptions import (
BadRequest, BadRequest,
SanicException, SanicException,
SanicIsADirectoryError,
ServerError, ServerError,
URLBuildError, URLBuildError,
) )
@ -1578,6 +1579,10 @@ class Sanic(BaseSanic, StartupMixin, metaclass=TouchUpMeta):
TouchUp.run(self) TouchUp.run(self)
self.state.is_started = True self.state.is_started = True
self.exception(SanicIsADirectoryError)(
DirectoryHandler.default_handler
)
self.directory_handler.debug = self.debug self.directory_handler.debug = self.debug
def ack(self): def ack(self):

View File

@ -304,9 +304,6 @@ class Blueprint(BaseSanic):
# Routes # Routes
for future in self._future_routes: for future in self._future_routes:
# attach the blueprint name to the handler so that it can be
# prefixed properly in the router
future.handler.__blueprintname__ = self.name
# Prepend the blueprint URI prefix if available # Prepend the blueprint URI prefix if available
uri = self._setup_uri(future.uri, url_prefix) uri = self._setup_uri(future.uri, url_prefix)

View File

@ -3,8 +3,6 @@ from __future__ import annotations
from typing import Dict, List, Optional, Tuple, Type from typing import Dict, List, Optional, Tuple, Type
from sanic.errorpages import BaseRenderer, TextRenderer, exception_response from sanic.errorpages import BaseRenderer, TextRenderer, exception_response
from sanic.exceptions import SanicIsADirectoryError
from sanic.handlers.directory import DirectoryHandler
from sanic.log import deprecation, error_logger from sanic.log import deprecation, error_logger
from sanic.models.handler_types import RouteHandler from sanic.models.handler_types import RouteHandler
from sanic.response import text from sanic.response import text
@ -22,19 +20,13 @@ class ErrorHandler:
realtime alerting system. realtime alerting system.
""" """
DEFAULT_HANDLERS = {
(SanicIsADirectoryError, None): DirectoryHandler.default_handler
}
def __init__( def __init__(
self, self,
base: Type[BaseRenderer] = TextRenderer, base: Type[BaseRenderer] = TextRenderer,
): ):
self.cached_handlers: Dict[ self.cached_handlers: Dict[
Tuple[Type[BaseException], Optional[str]], Optional[RouteHandler] Tuple[Type[BaseException], Optional[str]], Optional[RouteHandler]
] = { ] = {}
**self.DEFAULT_HANDLERS # type: ignore
}
self.debug = False self.debug = False
self.base = base self.base = base

View File

@ -13,23 +13,36 @@ class FileInfo(TypedDict):
file_size: str file_size: str
class AutoIndex(BasePage): class AutoIndex(BasePage): # no cov
EXTRA_STYLE = dedent( EXTRA_STYLE = dedent(
""" f"""
#breadcrumbs .path-0 a::before { content: "🏠"; } #breadcrumbs a:hover {{ text-decoration: underline; }}
#breadcrumbs span:has(> a:hover, > a:focus) * { #breadcrumbs .path-0 a {{ text-decoration: none; }}
color: #ff0d68; text-shadow: 0 0 1rem; #breadcrumbs span::after {{
} content: "/"; text-decoration: none; padding: 0 0.25em;
main a { color: inherit; font-weight: bold; } }}
table.autoindex tr { display: flex; } #breadcrumbs .path-0 a::before {{ content: "🏠"; }}
table.autoindex td { margin: 0 0.5rem; } #breadcrumbs > span > a {{ color: {BasePage.ACCENT}; }}
table.autoindex td:first-child { flex: 1; } main a {{ color: inherit; font-weight: bold; }}
table.autoindex td:nth-child(2) { text-align: right; } table.autoindex {{ width: 100%; font-family: monospace; }}
table.autoindex td:last-child { text-align: right; } table.autoindex tr {{ display: flex; }}
span.icon { margin-right: 1rem; } table.autoindex tr:hover {{ background-color: #ddd; }}
table.autoindex td {{ margin: 0 0.5rem; }}
table.autoindex td:first-child {{ flex: 1; }}
table.autoindex td:nth-child(2) {{ text-align: right; }}
table.autoindex td:last-child {{ text-align: right; }}
@media (min-width: 915px) {{
table.autoindex {{ font-size: 1.75vw; }}
}}
@media (min-width: 1600px) {{
table.autoindex {{ font-size: 1.75rem; }}
}}
@media (prefers-color-scheme: dark) {{
table.autoindex tr:hover {{ background-color: #222; }}
}}
""" """
) )
TITLE = "File browser" TITLE = "File Browser"
def __init__( def __init__(
self, files: Iterable[FileInfo], url: str, debug: bool self, files: Iterable[FileInfo], url: str, debug: bool
@ -56,11 +69,11 @@ class AutoIndex(BasePage):
self.doc.span(class_=f"path-{i}").__enter__() self.doc.span(class_=f"path-{i}").__enter__()
for i, part in enumerate(p): for i, part in enumerate(p):
path = "/".join(p[: i + 1]) + "/" path = "/".join(p[: i + 1]) + "/"
self.doc.a(f"{part}/", href=path) self.doc.a(part, href=path)
self.doc.__exit__(None, None, None) self.doc.__exit__(None, None, None)
def _file_table(self, files: Iterable[FileInfo]): def _file_table(self, files: Iterable[FileInfo]):
with self.doc.table(class_="autoindex"): with self.doc.table(class_="autoindex container"):
for f in files: for f in files:
self._file_row(**f) self._file_row(**f)

View File

@ -7,16 +7,26 @@ from sanic import __version__ as VERSION
from sanic.application.logo import SVG_LOGO from sanic.application.logo import SVG_LOGO
class BasePage(ABC): class BasePage(ABC): # no cov
ACCENT = "#ff0d68"
BASE_STYLE = dedent( BASE_STYLE = dedent(
""" """
body { margin: 0; font: 16px sans-serif; } html { font: 16px sans-serif; background: #eee; color: #111; }
body > * { padding: 0 2rem; } body { margin: 0; font-size: 1.25rem; }
body > * { padding: 1rem 2vw; }
@media (max-width: 1200px) {
body > * { padding: 0.5rem 1.5vw;}
body { font-size: 1rem; }
}
.container { min-width: 600px; max-width: 1600px; }
header { header {
display: flex; align-items: center; justify-content: space-between;
background: #111; color: #e1e1e1; border-bottom: 1px solid #272727; background: #111; color: #e1e1e1; border-bottom: 1px solid #272727;
} }
header .container {
display: flex; align-items: center; justify-content: space-between;
}
main { padding-bottom: 3rem; } main { padding-bottom: 3rem; }
h1 { text-align: left; }
h2 { margin: 2rem 0 1rem 0; } h2 { margin: 2rem 0 1rem 0; }
a:visited { color: inherit; } a:visited { color: inherit; }
a { text-decoration: none; color: #88f; } a { text-decoration: none; color: #88f; }
@ -26,7 +36,6 @@ class BasePage(ABC):
#logo { height: 2.75rem; padding: 0.25rem 0; } #logo { height: 2.75rem; padding: 0.25rem 0; }
.smalltext { font-size: 1rem; } .smalltext { font-size: 1rem; }
.nobr { white-space: nowrap; } .nobr { white-space: nowrap; }
table { width: 100%; max-width: 1200px; }
span.icon { margin-right: 1rem; } span.icon { margin-right: 1rem; }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
html { background: #111; color: #ccc; } html { background: #111; color: #ccc; }
@ -51,11 +60,13 @@ class BasePage(ABC):
def _head(self) -> None: def _head(self) -> None:
self.doc.style(HTML(self.style)) self.doc.style(HTML(self.style))
if self.debug:
with self.doc.header: with self.doc.header:
self.doc(HTML(SVG_LOGO)).div(self.TITLE, id="hdrtext").div( with self.doc.div(class_="container"):
f"Version {VERSION}", id="hdrver" if self.debug:
) self.doc(HTML(SVG_LOGO))
self.doc.div(self.TITLE, id="hdrtext")
if self.debug:
self.doc.div(f"Version {VERSION}", id="hdrver")
@abstractmethod @abstractmethod
def _body(self) -> None: def _body(self) -> None:

View File

@ -1,17 +0,0 @@
from sanic.worker.loader import AppLoader
class AppContainer:
def __init__(self, loader: AppLoader) -> None:
self.loader = loader
def prepare(self, *apps) -> None:
for app in apps:
app.prepare(**app._early_prepare)
def serve(self) -> None:
from sanic import Sanic
primary = self.loader.load()
self.prepare(primary)
Sanic.serve(primary=primary, app_loader=self.loader)