Compare commits
1 Commits
ruff
...
websocket-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50cec6b41c |
@@ -19,7 +19,7 @@ import sys
|
||||
root_directory = os.path.dirname(os.getcwd())
|
||||
sys.path.insert(0, root_directory)
|
||||
|
||||
import sanic # noqa: E402
|
||||
import sanic
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
@@ -25,6 +25,5 @@ def key_exist_handler(request):
|
||||
|
||||
return text("num does not exist in request")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000, debug=True)
|
||||
|
||||
@@ -69,6 +69,5 @@ async def runner(app: Sanic, app_server: AsyncioServer):
|
||||
app.is_running = False
|
||||
app.is_stopping = True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
https.run(port=HTTPS_PORT, debug=True)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import requests
|
||||
|
||||
|
||||
# Warning: This is a heavy process.
|
||||
|
||||
data = ""
|
||||
|
||||
@@ -35,7 +35,6 @@ async def after_server_stop(app, loop):
|
||||
async def test(request):
|
||||
return response.json({"answer": "42"})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.set_event_loop(uvloop.new_event_loop())
|
||||
serv_coro = app.create_server(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import re
|
||||
|
||||
from pathlib import Path
|
||||
from textwrap import indent
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
[tool.ruff]
|
||||
extend = "../pyproject.toml"
|
||||
|
||||
[tool.ruff.isort]
|
||||
known-first-party = ["webapp"]
|
||||
lines-after-imports = 1
|
||||
lines-between-types = 1
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from html5tagger import Builder, Document # type: ignore
|
||||
|
||||
|
||||
class BaseRenderer:
|
||||
def __init__(self, base_title: str):
|
||||
self.base_title = base_title
|
||||
|
||||
@@ -7,6 +7,7 @@ from pygments.token import ( # Error,; Generic,; Number,; Operator,
|
||||
Token,
|
||||
)
|
||||
|
||||
|
||||
class SanicCodeStyle(Style):
|
||||
styles = {
|
||||
Token: "#777",
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Generator
|
||||
from html5tagger import Builder
|
||||
from sanic import Request
|
||||
|
||||
|
||||
class BaseLayout:
|
||||
def __init__(self, builder: Builder):
|
||||
self.builder = builder
|
||||
|
||||
@@ -3,6 +3,7 @@ from datetime import datetime
|
||||
from html5tagger import Builder, E # type: ignore
|
||||
from sanic import Request
|
||||
|
||||
|
||||
def do_footer(builder: Builder, request: Request) -> None:
|
||||
builder.footer(
|
||||
_pagination(request),
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from webapp.display.layouts.models import MenuItem
|
||||
|
||||
from html5tagger import Builder, E # type: ignore
|
||||
from sanic import Request
|
||||
|
||||
from webapp.display.layouts.models import MenuItem
|
||||
|
||||
def do_navbar(builder: Builder, request: Request) -> None:
|
||||
navbar_items = [
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from webapp.display.layouts.models import MenuItem
|
||||
from webapp.display.text import slugify
|
||||
|
||||
from html5tagger import Builder, E # type: ignore
|
||||
from sanic import Request
|
||||
|
||||
from webapp.display.layouts.models import MenuItem
|
||||
from webapp.display.text import slugify
|
||||
|
||||
def do_sidebar(builder: Builder, request: Request) -> None:
|
||||
builder.a(class_="burger")(E.span().span().span().span())
|
||||
|
||||
@@ -8,6 +8,7 @@ from sanic import Request
|
||||
|
||||
from .base import BaseLayout
|
||||
|
||||
|
||||
class HomeLayout(BaseLayout):
|
||||
@contextmanager
|
||||
def layout(
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
from contextlib import contextmanager
|
||||
from typing import Generator
|
||||
|
||||
from sanic import Request
|
||||
|
||||
from webapp.display.layouts.elements.footer import do_footer
|
||||
from webapp.display.layouts.elements.navbar import do_navbar
|
||||
from webapp.display.layouts.elements.sidebar import do_sidebar
|
||||
|
||||
from sanic import Request
|
||||
|
||||
from .base import BaseLayout
|
||||
|
||||
|
||||
class MainLayout(BaseLayout):
|
||||
@contextmanager
|
||||
def layout(
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from msgspec import Struct, field
|
||||
|
||||
|
||||
class MenuItem(Struct, kw_only=False, omit_defaults=True):
|
||||
label: str
|
||||
path: str | None = None
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import re
|
||||
|
||||
from textwrap import dedent
|
||||
|
||||
from html5tagger import HTML, Builder, E # type: ignore
|
||||
from mistune import HTMLRenderer, create_markdown, escape
|
||||
from mistune.directives import RSTDirective, TableOfContents
|
||||
from mistune.util import safe_entity
|
||||
@@ -10,6 +8,8 @@ from pygments import highlight
|
||||
from pygments.formatters import html
|
||||
from pygments.lexers import get_lexer_by_name
|
||||
|
||||
from html5tagger import HTML, Builder, E # type: ignore
|
||||
|
||||
from .code_style import SanicCodeStyle
|
||||
from .plugins.attrs import Attributes
|
||||
from .plugins.columns import Column
|
||||
@@ -20,6 +20,7 @@ from .plugins.span import span
|
||||
from .plugins.tabs import Tabs
|
||||
from .text import slugify
|
||||
|
||||
|
||||
class DocsRenderer(HTMLRenderer):
|
||||
def block_code(self, code: str, info: str | None = None):
|
||||
builder = Builder("Block")
|
||||
|
||||
@@ -3,7 +3,6 @@ from __future__ import annotations
|
||||
import importlib
|
||||
import inspect
|
||||
import pkgutil
|
||||
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass, field
|
||||
from html import escape
|
||||
@@ -11,10 +10,12 @@ from html import escape
|
||||
from docstring_parser import Docstring, DocstringParam, DocstringRaises
|
||||
from docstring_parser import parse as parse_docstring
|
||||
from docstring_parser.common import DocstringExample
|
||||
|
||||
from html5tagger import HTML, Builder, E # type: ignore
|
||||
|
||||
from ..markdown import render_markdown, slugify
|
||||
|
||||
|
||||
@dataclass
|
||||
class DocObject:
|
||||
name: str
|
||||
|
||||
@@ -3,14 +3,15 @@ from __future__ import annotations
|
||||
from contextlib import contextmanager
|
||||
from typing import Type
|
||||
|
||||
from webapp.display.base import BaseRenderer
|
||||
|
||||
from html5tagger import HTML, Builder # type: ignore
|
||||
from sanic import Request
|
||||
|
||||
from webapp.display.base import BaseRenderer
|
||||
|
||||
from ..layouts.base import BaseLayout
|
||||
from .page import Page
|
||||
|
||||
|
||||
class PageRenderer(BaseRenderer):
|
||||
def render(self, request: Request, language: str, path: str) -> Builder:
|
||||
builder = self.get_builder(
|
||||
|
||||
@@ -2,11 +2,13 @@ from re import Match
|
||||
from textwrap import dedent
|
||||
from typing import Any
|
||||
|
||||
from html5tagger import HTML, E
|
||||
from mistune.block_parser import BlockParser
|
||||
from mistune.core import BlockState
|
||||
from mistune.directives import DirectivePlugin
|
||||
|
||||
from html5tagger import HTML, E
|
||||
|
||||
|
||||
class Attributes(DirectivePlugin):
|
||||
def __call__(self, directive, md):
|
||||
directive.register("attrs", self.parse)
|
||||
|
||||
@@ -8,6 +8,7 @@ from mistune.core import BlockState
|
||||
from mistune.directives import DirectivePlugin, RSTDirective
|
||||
from mistune.markdown import Markdown
|
||||
|
||||
|
||||
class Column(DirectivePlugin):
|
||||
def parse(
|
||||
self, block: BlockParser, m: Match, state: BlockState
|
||||
|
||||
@@ -2,6 +2,7 @@ from mistune.core import BlockState
|
||||
from mistune.directives import DirectivePlugin, RSTDirective
|
||||
from mistune.markdown import Markdown
|
||||
|
||||
|
||||
class Hook(DirectivePlugin):
|
||||
def __call__( # type: ignore
|
||||
self, directive: RSTDirective, md: Markdown
|
||||
|
||||
@@ -3,13 +3,15 @@ from re import Match
|
||||
from textwrap import dedent
|
||||
from typing import Any
|
||||
|
||||
from html5tagger import HTML, E
|
||||
from mistune import HTMLRenderer
|
||||
from mistune.block_parser import BlockParser
|
||||
from mistune.core import BlockState
|
||||
from mistune.directives import DirectivePlugin, RSTDirective
|
||||
from mistune.markdown import Markdown
|
||||
|
||||
from html5tagger import HTML, E
|
||||
|
||||
|
||||
class Mermaid(DirectivePlugin):
|
||||
def parse(
|
||||
self, block: BlockParser, m: Match, state: BlockState
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from html5tagger import HTML, E
|
||||
from mistune.directives import Admonition
|
||||
|
||||
from html5tagger import HTML, E
|
||||
|
||||
|
||||
class Notification(Admonition):
|
||||
SUPPORTED_NAMES = {
|
||||
"success",
|
||||
|
||||
@@ -2,6 +2,7 @@ import re
|
||||
|
||||
from mistune.markdown import Markdown
|
||||
|
||||
|
||||
def parse_inline_span(inline, m: re.Match, state):
|
||||
state.append_token(
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ from mistune.core import BlockState
|
||||
from mistune.directives import DirectivePlugin, RSTDirective
|
||||
from mistune.markdown import Markdown
|
||||
|
||||
|
||||
class Tabs(DirectivePlugin):
|
||||
def parse(
|
||||
self, block: BlockParser, m: Match, state: BlockState
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
from contextlib import contextmanager
|
||||
from urllib.parse import unquote
|
||||
|
||||
from webapp.display.search.search import Searcher
|
||||
|
||||
from html5tagger import Builder, E # type: ignore
|
||||
from sanic import Request
|
||||
|
||||
from webapp.display.search.search import Searcher
|
||||
|
||||
from ..base import BaseRenderer
|
||||
from ..layouts.main import MainLayout
|
||||
|
||||
|
||||
class SearchRenderer(BaseRenderer):
|
||||
def render(
|
||||
self, request: Request, language: str, searcher: Searcher, full: bool
|
||||
|
||||
@@ -5,9 +5,9 @@ from pathlib import Path
|
||||
from typing import ClassVar
|
||||
|
||||
from msgspec import Struct
|
||||
|
||||
from webapp.display.page import Page
|
||||
|
||||
|
||||
class Stemmer:
|
||||
STOP_WORDS: ClassVar[set[str]] = set(
|
||||
"a about above after again against all am an and any are aren't as at be because been before being below between both but by can't cannot could couldn't did didn't do does doesn't doing don't down during each few for from further had hadn't has hasn't have haven't having he he'd he'll he's her here here's hers herself him himself his how how's i i'd i'll i'm i've if in into is isn't it it's its itself let's me more most mustn't my myself no nor not of off on once only or other ought our ours ourselves out over own same shan't she she'd she'll she's should shouldn't so some such than that that's the their theirs them themselves then there there's these they they'd they'll they're they've this those through to too under until up very was wasn't we we'd we'll we're we've were weren't what what's when when's where where's which while who who's whom why why's with won't would wouldn't you you'd you'll you're you've your yours yourself yourselves".split() # noqa: E501
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# from urllib.parse import unquote
|
||||
|
||||
from sanic import Blueprint, Request, Sanic, html
|
||||
|
||||
from webapp.display.page import Page
|
||||
from webapp.display.search.renderer import SearchRenderer
|
||||
from webapp.display.search.search import Document, Searcher, Stemmer
|
||||
|
||||
from sanic import Blueprint, Request, Sanic, html
|
||||
|
||||
bp = Blueprint("search", url_prefix="/<language>/search")
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from pathlib import Path
|
||||
|
||||
from msgspec import yaml
|
||||
|
||||
from webapp.display.layouts.models import GeneralConfig, MenuItem
|
||||
|
||||
|
||||
def load_menu(path: Path) -> list[MenuItem]:
|
||||
loaded = yaml.decode(path.read_bytes(), type=dict[str, list[MenuItem]])
|
||||
return loaded["root"]
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
from pathlib import Path
|
||||
|
||||
from sanic import Request, Sanic, html, redirect
|
||||
|
||||
from webapp.display.layouts.models import MenuItem
|
||||
from webapp.display.page import Page, PageRenderer
|
||||
from webapp.endpoint.view import bp
|
||||
@@ -9,6 +7,9 @@ from webapp.worker.config import load_config, load_menu
|
||||
from webapp.worker.reload import setup_livereload
|
||||
from webapp.worker.style import setup_style
|
||||
|
||||
from sanic import Request, Sanic, html, redirect
|
||||
|
||||
|
||||
def _compile_sidebar_order(items: list[MenuItem]) -> list[str]:
|
||||
order = []
|
||||
for item in items:
|
||||
|
||||
@@ -8,6 +8,7 @@ import ujson
|
||||
|
||||
from sanic import Request, Sanic, Websocket
|
||||
|
||||
|
||||
def setup_livereload(app: Sanic) -> None:
|
||||
@app.main_process_start
|
||||
async def main_process_start(app: Sanic):
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# from scss.compiler import compile_string
|
||||
|
||||
from pygments.formatters import html
|
||||
from sanic import Sanic
|
||||
from sass import compile as compile_scss
|
||||
|
||||
from webapp.display.code_style import SanicCodeStyle
|
||||
|
||||
from sanic import Sanic
|
||||
|
||||
|
||||
def setup_style(app: Sanic) -> None:
|
||||
index = app.config.STYLE_DIR / "index.scss"
|
||||
style_output = app.config.PUBLIC_DIR / "assets" / "style.css"
|
||||
|
||||
@@ -2,19 +2,6 @@
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.ruff]
|
||||
extend-select = ["I"]
|
||||
ignore = ["D100", "D101", "D102", "D103", "E402", "E741", "F811", "F821"]
|
||||
line-length = 79
|
||||
show-source = true
|
||||
show-fixes = true
|
||||
|
||||
[tool.ruff.isort]
|
||||
known-first-party = ["sanic"]
|
||||
known-third-party = ["pytest"]
|
||||
lines-after-imports = 2
|
||||
lines-between-types = 1
|
||||
|
||||
[tool.black]
|
||||
line-length = 79
|
||||
|
||||
|
||||
20
sanic/app.py
20
sanic/app.py
@@ -1369,6 +1369,12 @@ class Sanic(
|
||||
protocol = request.transport.get_protocol()
|
||||
ws = await protocol.websocket_handshake(request, subprotocols)
|
||||
|
||||
await self.dispatch(
|
||||
"websocket.handler.before",
|
||||
inline=True,
|
||||
context={"request": request, "websocket": ws},
|
||||
fail_not_found=False,
|
||||
)
|
||||
# schedule the application handler
|
||||
# its future is kept in self.websocket_tasks in case it
|
||||
# needs to be cancelled due to the server being stopped
|
||||
@@ -1377,10 +1383,24 @@ class Sanic(
|
||||
cancelled = False
|
||||
try:
|
||||
await fut
|
||||
await self.dispatch(
|
||||
"websocket.handler.after",
|
||||
inline=True,
|
||||
context={"request": request, "websocket": ws},
|
||||
reverse=True,
|
||||
fail_not_found=False,
|
||||
)
|
||||
except (CancelledError, ConnectionClosed): # type: ignore
|
||||
cancelled = True
|
||||
except Exception as e:
|
||||
self.error_handler.log(request, e)
|
||||
await self.dispatch(
|
||||
"websocket.handler.exception",
|
||||
inline=True,
|
||||
context={"request": request, "websocket": ws, "exception": e},
|
||||
reverse=True,
|
||||
fail_not_found=False,
|
||||
)
|
||||
finally:
|
||||
self.websocket_tasks.remove(fut)
|
||||
if cancelled:
|
||||
|
||||
@@ -349,7 +349,8 @@ def parse_content_header(value: str) -> Tuple[str, Options]:
|
||||
options: Dict[str, Union[int, str]] = {}
|
||||
else:
|
||||
options = {
|
||||
m.group(1).lower(): (m.group(2) or m.group(3))
|
||||
m.group(1)
|
||||
.lower(): (m.group(2) or m.group(3))
|
||||
.replace("%22", '"')
|
||||
.replace("%0D%0A", "\n")
|
||||
for m in _param.finditer(value[pos:])
|
||||
|
||||
@@ -14,7 +14,7 @@ class ExceptionMixin(metaclass=SanicMeta):
|
||||
def exception(
|
||||
self,
|
||||
*exceptions: Union[Type[Exception], List[Type[Exception]]],
|
||||
apply: bool = True,
|
||||
apply: bool = True
|
||||
) -> Callable:
|
||||
"""Decorator used to register an exception handler for the current application or blueprint instance.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class MiddlewareMixin(metaclass=SanicMeta):
|
||||
attach_to: str = "request",
|
||||
apply: bool = True,
|
||||
*,
|
||||
priority: int = 0,
|
||||
priority: int = 0
|
||||
) -> MiddlewareType:
|
||||
...
|
||||
|
||||
@@ -36,7 +36,7 @@ class MiddlewareMixin(metaclass=SanicMeta):
|
||||
attach_to: str = "request",
|
||||
apply: bool = True,
|
||||
*,
|
||||
priority: int = 0,
|
||||
priority: int = 0
|
||||
) -> Callable[[MiddlewareType], MiddlewareType]:
|
||||
...
|
||||
|
||||
@@ -46,7 +46,7 @@ class MiddlewareMixin(metaclass=SanicMeta):
|
||||
attach_to: str = "request",
|
||||
apply: bool = True,
|
||||
*,
|
||||
priority: int = 0,
|
||||
priority: int = 0
|
||||
) -> Union[MiddlewareType, Callable[[MiddlewareType], MiddlewareType]]:
|
||||
"""Decorator for registering middleware.
|
||||
|
||||
|
||||
@@ -44,8 +44,12 @@ class ConnInfo:
|
||||
self.server_name = ""
|
||||
self.cert: Dict[str, Any] = {}
|
||||
self.network_paths: List[Any] = []
|
||||
sslobj: Optional[SSLObject] = transport.get_extra_info("ssl_object") # type: ignore
|
||||
sslctx: Optional[SSLContext] = transport.get_extra_info("ssl_context") # type: ignore
|
||||
sslobj: Optional[SSLObject] = transport.get_extra_info(
|
||||
"ssl_object"
|
||||
) # type: ignore
|
||||
sslctx: Optional[SSLContext] = transport.get_extra_info(
|
||||
"ssl_context"
|
||||
) # type: ignore
|
||||
if sslobj:
|
||||
self.ssl = True
|
||||
self.server_name = getattr(sslobj, "sanic_server_name", None) or ""
|
||||
|
||||
@@ -57,7 +57,9 @@ class WebsocketFrameAssembler:
|
||||
self.read_mutex = asyncio.Lock()
|
||||
self.write_mutex = asyncio.Lock()
|
||||
|
||||
self.completed_queue = asyncio.Queue(maxsize=1) # type: asyncio.Queue[Data]
|
||||
self.completed_queue = asyncio.Queue(
|
||||
maxsize=1
|
||||
) # type: asyncio.Queue[Data]
|
||||
|
||||
# put() sets this event to tell get() that a message can be fetched.
|
||||
self.message_complete = asyncio.Event()
|
||||
|
||||
@@ -38,6 +38,9 @@ class Event(Enum):
|
||||
HTTP_LIFECYCLE_SEND = "http.lifecycle.send"
|
||||
HTTP_MIDDLEWARE_AFTER = "http.middleware.after"
|
||||
HTTP_MIDDLEWARE_BEFORE = "http.middleware.before"
|
||||
WEBSOCKET_HANDLER_AFTER = "websocket.handler.after"
|
||||
WEBSOCKET_HANDLER_BEFORE = "websocket.handler.before"
|
||||
WEBSOCKET_HANDLER_EXCEPTION = "websocket.handler.exception"
|
||||
|
||||
|
||||
RESERVED_NAMESPACES = {
|
||||
@@ -65,6 +68,11 @@ RESERVED_NAMESPACES = {
|
||||
Event.HTTP_MIDDLEWARE_AFTER.value,
|
||||
Event.HTTP_MIDDLEWARE_BEFORE.value,
|
||||
),
|
||||
"websocket": {
|
||||
Event.WEBSOCKET_HANDLER_AFTER.value,
|
||||
Event.WEBSOCKET_HANDLER_BEFORE.value,
|
||||
Event.WEBSOCKET_HANDLER_EXCEPTION.value,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from collections.abc import Mapping
|
||||
from typing import Any, Dict, ItemsView, Iterator, KeysView, List, ValuesView
|
||||
from typing import Any, Dict, ItemsView, Iterator, KeysView, List
|
||||
from typing import Mapping as MappingType
|
||||
from typing import ValuesView
|
||||
|
||||
|
||||
dict
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
||||
from os import path
|
||||
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
import click
|
||||
import towncrier
|
||||
import click
|
||||
except ImportError:
|
||||
print(
|
||||
"Please make sure you have a installed towncrier and click before using this tool"
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
||||
from argparse import ArgumentParser, Namespace
|
||||
from collections import OrderedDict
|
||||
from configparser import RawConfigParser
|
||||
from datetime import datetime
|
||||
from json import dumps
|
||||
from os import chdir, path
|
||||
from subprocess import PIPE, Popen
|
||||
from os import path, chdir
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
import towncrier
|
||||
|
||||
from jinja2 import BaseLoader, Environment
|
||||
from jinja2 import Environment, BaseLoader
|
||||
from requests import patch
|
||||
|
||||
import sys
|
||||
import towncrier
|
||||
|
||||
GIT_COMMANDS = {
|
||||
"get_tag": ["git describe --tags --abbrev=0"],
|
||||
@@ -81,7 +78,7 @@ def _run_shell_command(command: list):
|
||||
output, error = process.communicate()
|
||||
return_code = process.returncode
|
||||
return output.decode("utf-8"), error, return_code
|
||||
except Exception:
|
||||
except:
|
||||
return None, None, -1
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import bottle
|
||||
import ujson
|
||||
|
||||
from bottle import route
|
||||
from bottle import route, run
|
||||
|
||||
|
||||
@route("/")
|
||||
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
import sys
|
||||
import timeit
|
||||
|
||||
import asyncpg
|
||||
|
||||
from sanic.response import json
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import ujson
|
||||
|
||||
from wheezy.http import HTTPResponse, WSGIApplication
|
||||
from wheezy.http.response import json_response
|
||||
from wheezy.routing import url
|
||||
from wheezy.web.handlers import BaseHandler
|
||||
from wheezy.web.middleware import (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from collections import deque, namedtuple
|
||||
@@ -11,7 +12,7 @@ from pytest import MonkeyPatch
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.application.state import Mode
|
||||
from sanic.asgi import Lifespan, MockTransport
|
||||
from sanic.asgi import ASGIApp, Lifespan, MockTransport
|
||||
from sanic.exceptions import BadRequest, Forbidden, ServiceUnavailable
|
||||
from sanic.request import Request
|
||||
from sanic.response import json, text
|
||||
|
||||
@@ -22,7 +22,7 @@ def test_bp_group_indexing(app: Sanic):
|
||||
group = Blueprint.group(blueprint_1, blueprint_2)
|
||||
assert group[0] == blueprint_1
|
||||
|
||||
with raises(expected_exception=IndexError):
|
||||
with raises(expected_exception=IndexError) as e:
|
||||
_ = group[3]
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import asyncio
|
||||
|
||||
from asyncio import CancelledError
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic import Request, Sanic, json
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from unittest.mock import Mock
|
||||
import pytest
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from pytest import LogCaptureFixture, MonkeyPatch
|
||||
from pytest import LogCaptureFixture, MonkeyPatch, WarningsRecorder
|
||||
|
||||
from sanic import Sanic, handlers
|
||||
from sanic.exceptions import BadRequest, Forbidden, NotFound, ServerError
|
||||
@@ -169,7 +169,7 @@ def test_exception_handler_lookup(exception_handler_app: Sanic):
|
||||
pass
|
||||
|
||||
try:
|
||||
ModuleNotFoundError # noqa: F823
|
||||
ModuleNotFoundError
|
||||
except Exception:
|
||||
|
||||
class ModuleNotFoundError(ImportError):
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import sys
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic import Sanic
|
||||
|
||||
|
||||
try:
|
||||
import sanic_ext # noqa: F401
|
||||
import sanic_ext
|
||||
|
||||
SANIC_EXT_IN_ENV = True
|
||||
except ImportError:
|
||||
|
||||
@@ -57,7 +57,7 @@ def raised_ceiling():
|
||||
# Chrome, Firefox:
|
||||
# Content-Disposition: form-data; name="foo%22;bar\"; filename="😀"
|
||||
'form-data; name="foo%22;bar\\"; filename="😀"',
|
||||
("form-data", {"name": 'foo";bar\\', "filename": "😀"}),
|
||||
("form-data", {"name": 'foo";bar\\', "filename": "😀"})
|
||||
# cgi: ('form-data', {'name': 'foo%22;bar"; filename="😀'})
|
||||
# werkzeug (pre 2.3.0): ('form-data', {'name': 'foo%22;bar"; filename='})
|
||||
),
|
||||
|
||||
@@ -14,6 +14,7 @@ from sanic_testing.testing import HOST, PORT
|
||||
from sanic import Blueprint, text
|
||||
from sanic.compat import use_context
|
||||
from sanic.log import logger
|
||||
from sanic.server.socket import configure_socket
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
|
||||
@@ -573,7 +573,7 @@ def test_streaming_echo():
|
||||
|
||||
async def client(app, reader, writer):
|
||||
# Unfortunately httpx does not support 2-way streaming, so do it by hand.
|
||||
host = "host: localhost:8000\r\n".encode()
|
||||
host = f"host: localhost:8000\r\n".encode()
|
||||
writer.write(
|
||||
b"POST /echo HTTP/1.1\r\n" + host + b"content-length: 2\r\n"
|
||||
b"content-type: text/plain; charset=utf-8\r\n"
|
||||
@@ -581,7 +581,7 @@ def test_streaming_echo():
|
||||
)
|
||||
# Read response
|
||||
res = b""
|
||||
while b"\r\n\r\n" not in res:
|
||||
while not b"\r\n\r\n" in res:
|
||||
res += await reader.read(4096)
|
||||
assert res.startswith(b"HTTP/1.1 200 OK\r\n")
|
||||
assert res.endswith(b"\r\n\r\n")
|
||||
@@ -589,7 +589,7 @@ def test_streaming_echo():
|
||||
|
||||
async def read_chunk():
|
||||
nonlocal buffer
|
||||
while b"\r\n" not in buffer:
|
||||
while not b"\r\n" in buffer:
|
||||
data = await reader.read(4096)
|
||||
assert data
|
||||
buffer += data
|
||||
@@ -618,6 +618,6 @@ def test_streaming_echo():
|
||||
assert res == b"-"
|
||||
|
||||
res = await read_chunk()
|
||||
assert res is None
|
||||
assert res == None
|
||||
|
||||
app.run(access_log=False, single_process=True)
|
||||
|
||||
@@ -2011,11 +2011,11 @@ def test_server_name_and_url_for(app):
|
||||
app.config.SERVER_NAME = "my-server" # This means default port
|
||||
assert app.url_for("handler", _external=True) == "http://my-server/foo"
|
||||
request, response = app.test_client.get("/foo")
|
||||
assert request.url_for("handler") == "http://my-server/foo"
|
||||
assert request.url_for("handler") == f"http://my-server/foo"
|
||||
|
||||
app.config.SERVER_NAME = "https://my-server/path"
|
||||
request, response = app.test_client.get("/foo")
|
||||
url = "https://my-server/path/foo"
|
||||
url = f"https://my-server/path/foo"
|
||||
assert app.url_for("handler", _external=True) == url
|
||||
assert request.url_for("handler") == url
|
||||
|
||||
@@ -2180,7 +2180,7 @@ def test_safe_method_with_body_ignored(app):
|
||||
)
|
||||
|
||||
assert request.body == b""
|
||||
assert request.json is None
|
||||
assert request.json == None
|
||||
assert response.body == b"OK"
|
||||
|
||||
|
||||
|
||||
@@ -610,7 +610,7 @@ def test_multiple_responses(
|
||||
|
||||
@app.get("/4")
|
||||
async def handler4(request: Request):
|
||||
await request.respond(headers={"one": "one"})
|
||||
response = await request.respond(headers={"one": "one"})
|
||||
return json({"foo": "bar"}, headers={"one": "two"})
|
||||
|
||||
@app.get("/5")
|
||||
@@ -641,6 +641,10 @@ def test_multiple_responses(
|
||||
"been responded to."
|
||||
)
|
||||
|
||||
error_msg3 = (
|
||||
"Response stream was ended, no more "
|
||||
"response data is allowed to be sent."
|
||||
)
|
||||
|
||||
with caplog.at_level(ERROR):
|
||||
_, response = app.test_client.get("/1")
|
||||
@@ -765,7 +769,7 @@ def test_file_response_headers(
|
||||
assert (
|
||||
"cache-control" in headers
|
||||
and f"max-age={test_max_age}" in headers.get("cache-control")
|
||||
and "public" in headers.get("cache-control")
|
||||
and f"public" in headers.get("cache-control")
|
||||
)
|
||||
assert (
|
||||
"expires" in headers
|
||||
@@ -796,14 +800,14 @@ def test_file_response_headers(
|
||||
|
||||
_, response = app.test_client.get(f"/files/no_cache/{file_name}")
|
||||
headers = response.headers
|
||||
assert "cache-control" in headers and "no-cache" == headers.get(
|
||||
assert "cache-control" in headers and f"no-cache" == headers.get(
|
||||
"cache-control"
|
||||
)
|
||||
assert response.status == 200
|
||||
|
||||
_, response = app.test_client.get(f"/files/no_store/{file_name}")
|
||||
headers = response.headers
|
||||
assert "cache-control" in headers and "no-store" == headers.get(
|
||||
assert "cache-control" in headers and f"no-store" == headers.get(
|
||||
"cache-control"
|
||||
)
|
||||
assert response.status == 200
|
||||
|
||||
@@ -54,3 +54,65 @@ def test_ws_handler_async_for(
|
||||
)
|
||||
assert ws_proxy.client_sent == ["test 1", "test 2", ""]
|
||||
assert ws_proxy.client_received == ["test 1", "test 2"]
|
||||
|
||||
|
||||
def signalapp(app):
|
||||
@app.signal("websocket.handler.before")
|
||||
async def ws_before(request: Request, websocket: Websocket):
|
||||
app.ctx.seq.append("before")
|
||||
print("before")
|
||||
await websocket.send("before: " + await websocket.recv())
|
||||
print("before2")
|
||||
|
||||
@app.signal("websocket.handler.after")
|
||||
async def ws_after(request: Request, websocket: Websocket):
|
||||
app.ctx.seq.append("after")
|
||||
await websocket.send("after: " + await websocket.recv())
|
||||
await websocket.recv()
|
||||
|
||||
@app.signal("websocket.handler.exception")
|
||||
async def ws_exception(
|
||||
request: Request, websocket: Websocket, exception: Exception
|
||||
):
|
||||
app.ctx.seq.append("exception")
|
||||
await websocket.send(f"exception: {exception}")
|
||||
await websocket.recv()
|
||||
|
||||
@app.websocket("/ws")
|
||||
async def ws_handler(request: Request, ws: Websocket):
|
||||
app.ctx.seq.append("ws")
|
||||
|
||||
@app.websocket("/wserror")
|
||||
async def ws_error(request: Request, ws: Websocket):
|
||||
print("wserr")
|
||||
app.ctx.seq.append("wserror")
|
||||
raise Exception(await ws.recv())
|
||||
print("wserr2")
|
||||
|
||||
|
||||
def test_ws_signals(
|
||||
app: Sanic,
|
||||
simple_ws_mimic_client: MimicClientType,
|
||||
):
|
||||
signalapp(app)
|
||||
|
||||
app.ctx.seq = []
|
||||
_, ws_proxy = app.test_client.websocket(
|
||||
"/ws", mimic=simple_ws_mimic_client
|
||||
)
|
||||
assert ws_proxy.client_received == ["before: test 1", "after: test 2"]
|
||||
assert app.ctx.seq == ["before", "ws", "after"]
|
||||
|
||||
|
||||
def test_ws_signals_exception(
|
||||
app: Sanic,
|
||||
simple_ws_mimic_client: MimicClientType,
|
||||
):
|
||||
signalapp(app)
|
||||
|
||||
app.ctx.seq = []
|
||||
_, ws_proxy = app.test_client.websocket(
|
||||
"/wserror", mimic=simple_ws_mimic_client
|
||||
)
|
||||
assert ws_proxy.client_received == ["before: test 1", "exception: test 2"]
|
||||
assert app.ctx.seq == ["before", "wserror", "exception"]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# flake8: noqa: E501
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
|
||||
Reference in New Issue
Block a user