Chore: Format code using Ruff, and fix linter errors (#531)

## About
- Add Ruff configuration to `pyproject.toml`, apply its formatter, and
satisfy its linter.
- Migrate pytest configuration to `pyproject.toml`.
This commit is contained in:
Andreas Motl
2024-10-24 13:30:18 +02:00
committed by GitHub
parent 398ac3343e
commit b3c7252197
19 changed files with 236 additions and 169 deletions
+69 -2
View File
@@ -1,3 +1,70 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta:__legacy__"
build-backend = "setuptools.build_meta"
requires = [
"setuptools>=42", # At least v42 of setuptools required.
]
[tool.ruff]
line-length = 90
extend-exclude = [
"docs/source/conf.py",
"setup.py",
]
lint.select = [
# Builtins
"A",
# Bugbear
"B",
# comprehensions
"C4",
# Pycodestyle
"E",
# eradicate
"ERA",
# Pyflakes
"F",
# isort
"I",
# pandas-vet
"PD",
# return
"RET",
# Bandit
"S",
# print
"T20",
"W",
# flake8-2020
"YTT",
]
lint.extend-ignore = [
"S101", # Allow use of `assert`.
]
lint.per-file-ignores."tests/*" = [
"ERA001", # Found commented-out code.
"S101", # Allow use of `assert`, and `print`.
]
[tool.pytest.ini_options]
addopts = """
-rfEXs -p pytester --strict-markers --verbosity=3
--cov --cov-report=term-missing --cov-report=xml
"""
filterwarnings = [
"error::UserWarning",
]
log_level = "DEBUG"
log_cli_level = "DEBUG"
log_format = "%(asctime)-15s [%(name)-36s] %(levelname)-8s: %(message)s"
minversion = "2.0"
testpaths = [
"responder",
"tests",
]
markers = [
]
xfail_strict = true
-4
View File
@@ -1,4 +0,0 @@
[pytest]
addopts= -rsxX -s -vvv --strict
filterwarnings =
error::UserWarning
+8 -1
View File
@@ -1,2 +1,9 @@
from .core import *
from . import ext
from .core import API, Request, Response
__all__ = [
"API",
"Request",
"Response",
"ext",
]
+25 -24
View File
@@ -1,29 +1,23 @@
import json
import os
from pathlib import Path
import jinja2
import uvicorn
from starlette.exceptions import ExceptionMiddleware
from starlette.middleware.wsgi import WSGIMiddleware
from starlette.middleware.errors import ServerErrorMiddleware
from starlette.middleware.cors import CORSMiddleware
from starlette.middleware.errors import ServerErrorMiddleware
from starlette.middleware.gzip import GZipMiddleware
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
from starlette.middleware.trustedhost import TrustedHostMiddleware
from starlette.middleware.sessions import SessionMiddleware
from starlette.staticfiles import StaticFiles
from starlette.middleware.trustedhost import TrustedHostMiddleware
from starlette.testclient import TestClient
from starlette.websockets import WebSocket
from . import models, status_codes
from . import status_codes
from .background import BackgroundQueue
from .ext.schema import OpenAPISchema as OpenAPISchema
from .formats import get_formats
from .routes import Router
from .statics import DEFAULT_API_THEME, DEFAULT_CORS_PARAMS, DEFAULT_SECRET_KEY
from .ext.schema import OpenAPISchema as OpenAPISchema
from .staticfiles import StaticFiles
from .statics import DEFAULT_CORS_PARAMS, DEFAULT_SECRET_KEY
from .templates import Templates
@@ -34,7 +28,7 @@ class API:
:param templates_dir: The directory to use for templates. Will be created for you if it doesn't already exist.
:param auto_escape: If ``True``, HTML and XML templates will automatically be escaped.
:param enable_hsts: If ``True``, send all responses to HTTPS URLs.
"""
""" # noqa: E501
status_codes = status_codes
@@ -47,7 +41,7 @@ class API:
description=None,
terms_of_service=None,
contact=None,
license=None,
license=None, # noqa: A002
openapi=None,
openapi_route="/schema.yml",
static_dir="static",
@@ -84,7 +78,7 @@ class API:
# if not debug:
# raise RuntimeError(
# "You need to specify `allowed_hosts` when debug is set to False"
# )
# ) # noqa: ERA001
allowed_hosts = ["*"]
self.allowed_hosts = allowed_hosts
@@ -170,11 +164,12 @@ class API:
"""Given a path portion of a URL, tests that it matches against any registered route.
:param path: The path portion of a URL, to test all known routes against.
"""
""" # noqa: E501 (Line too long)
for route in self.router.routes:
match, _ = route.matches(path)
if match:
return route
return None
def add_route(
self,
@@ -192,8 +187,9 @@ class API:
:param route: A string representation of the route.
:param endpoint: The endpoint for the route -- can be a callable, or a class.
:param default: If ``True``, all unknown requests will route to this view.
:param static: If ``True``, and no endpoint was passed, render "static/index.html", and it will become a default route.
"""
:param static: If ``True``, and no endpoint was passed, render "static/index.html".
Also, it will become a default route.
""" # noqa: E501
# Path
if static:
@@ -229,7 +225,8 @@ class API:
:param resp: The Response to mutate.
:param location: The location of the redirect.
:param set_text: If ``True``, sets the Redirect body content automatically.
:param status_code: an `API.status_codes` attribute, or an integer, representing the HTTP status code of the redirect.
:param status_code: an `API.status_codes` attribute, or an integer,
representing the HTTP status code of the redirect.
"""
resp.redirect(location, set_text=set_text, status_code=status_code)
@@ -284,13 +281,15 @@ class API:
def mount(self, route, app):
"""Mounts an WSGI / ASGI application at a given route.
:param route: String representation of the route to be used (shouldn't be parameterized).
:param route: String representation of the route to be used
(shouldn't be parameterized).
:param app: The other WSGI / ASGI app.
"""
self.router.apps.update({route: app})
def session(self, base_url="http://;"):
"""Testing HTTP client. Returns a Requests session object, able to send HTTP requests to the Responder application.
"""Testing HTTP client. Returns a Requests session object,
able to send HTTP requests to the Responder application.
:param base_url: The URL to mount the connection adaptor to.
"""
@@ -310,11 +309,13 @@ class API:
def template(self, filename, *args, **kwargs):
"""Renders the given `jinja2 <http://jinja.pocoo.org/docs/>`_ template, with provided values supplied.
Note: The current ``api`` instance is by default passed into the view. This is set in the dict ``api.jinja_values_base``.
:param filename: The filename of the jinja2 template, in ``templates_dir``.
:param *args: Data to pass into the template.
:param *kwargs: Date to pass into the template.
"""
""" # noqa: E501
return self.templates.render(filename, *args, **kwargs)
def template_string(self, source, *args, **kwargs):
@@ -323,7 +324,7 @@ class API:
:param source: The template to use.
:param *args: Data to pass into the template.
:param **kwargs: Data to pass into the template.
"""
""" # noqa: E501
return self.templates.render_string(source, *args, **kwargs)
def serve(self, *, address=None, port=None, **options):
@@ -334,11 +335,11 @@ class API:
:param address: The address to bind to.
:param port: The port to bind to. If none is provided, one will be selected at random.
:param options: Additional keyword arguments to send to ``uvicorn.run()``.
"""
""" # noqa: E501
if "PORT" in os.environ:
if address is None:
address = "0.0.0.0"
address = "0.0.0.0" # noqa: S104
port = int(os.environ["PORT"])
if address is None:
+3 -4
View File
@@ -1,8 +1,8 @@
import asyncio
import functools
import concurrent.futures
import multiprocessing
import traceback
from starlette.concurrency import run_in_threadpool
@@ -27,7 +27,7 @@ class BackgroundQueue:
def on_future_done(fs):
try:
fs.result()
except:
except Exception:
traceback.print_exc()
def do_task(*args, **kwargs):
@@ -40,5 +40,4 @@ class BackgroundQueue:
async def __call__(self, func, *args, **kwargs) -> None:
if asyncio.iscoroutinefunction(func):
return await asyncio.ensure_future(func(*args, **kwargs))
else:
return await run_in_threadpool(func, *args, **kwargs)
return await run_in_threadpool(func, *args, **kwargs)
+6
View File
@@ -1,2 +1,8 @@
from .api import API
from .models import Request, Response
__all__ = [
"API",
"Request",
"Response",
]
+3 -7
View File
@@ -7,8 +7,8 @@ import yaml
from apispec import APISpec, yaml_utils
from apispec.ext.marshmallow import MarshmallowPlugin
from responder.statics import DEFAULT_API_THEME
from responder import status_codes
from responder.statics import DEFAULT_API_THEME
class OpenAPISchema:
@@ -21,7 +21,7 @@ class OpenAPISchema:
description=None,
terms_of_service=None,
contact=None,
license=None,
license=None, # noqa: A002
openapi=None,
openapi_route="/schema.yml",
docs_route="/docs/",
@@ -60,7 +60,6 @@ class OpenAPISchema:
@property
def _apispec(self):
info = {}
if self.description is not None:
info["description"] = self.description
@@ -81,9 +80,7 @@ class OpenAPISchema:
for route in self.app.router.routes:
if route.description:
operations = yaml_utils.load_operations_from_docstring(
route.description
)
operations = yaml_utils.load_operations_from_docstring(route.description)
spec.path(path=route.route, operations=operations)
for name, schema in self.schemas.items():
@@ -123,7 +120,6 @@ class OpenAPISchema:
@property
def docs(self):
loader = jinja2.PrefixLoader(
{
self.docs_theme: jinja2.PackageLoader(
+34 -38
View File
@@ -9,10 +9,10 @@ from .models import QueryDict
async def format_form(r, encode=False):
if encode:
pass
elif "multipart/form-data" in r.headers.get("Content-Type"):
return None
if "multipart/form-data" in r.headers.get("Content-Type"):
decode = decoder.MultipartDecoder(await r.content, r.mimetype)
querys = list()
queries = []
for part in decode.parts:
header = part.headers.get(b"Content-Disposition").decode("utf-8")
text = part.text
@@ -22,63 +22,59 @@ async def format_form(r, encode=False):
if len(split) > 1:
key = split[1]
key = key[1:-1]
querys.append((key, text))
queries.append((key, text))
content = urlencode(querys)
content = urlencode(queries)
return QueryDict(content)
else:
return QueryDict(await r.text)
return QueryDict(await r.text)
async def format_yaml(r, encode=False):
if encode:
r.headers.update({"Content-Type": "application/x-yaml"})
return yaml.safe_dump(r.media)
else:
return yaml.safe_load(await r.content)
return yaml.safe_load(await r.content)
async def format_json(r, encode=False):
if encode:
r.headers.update({"Content-Type": "application/json"})
return json.dumps(r.media)
else:
return json.loads(await r.content)
return json.loads(await r.content)
async def format_files(r, encode=False):
if encode:
pass
else:
decoded = decoder.MultipartDecoder(await r.content, r.mimetype)
dump = {}
for part in decoded.parts:
header = part.headers[b"Content-Disposition"].decode("utf-8")
mimetype = part.headers.get(b"Content-Type", None)
filename = None
return None
decoded = decoder.MultipartDecoder(await r.content, r.mimetype)
dump = {}
for part in decoded.parts:
header = part.headers[b"Content-Disposition"].decode("utf-8")
mimetype = part.headers.get(b"Content-Type", None)
filename = None
for section in [h.strip() for h in header.split(";")]:
split = section.split("=")
if len(split) > 1:
key = split[0]
value = split[1]
for section in [h.strip() for h in header.split(";")]:
split = section.split("=")
if len(split) > 1:
key = split[0]
value = split[1]
value = value[1:-1]
value = value[1:-1]
if key == "filename":
filename = value
elif key == "name":
formname = value
if key == "filename":
filename = value
elif key == "name":
formname = value
if mimetype is None:
dump[formname] = part.content
else:
dump[formname] = {
"filename": filename,
"content": part.content,
"content-type": mimetype.decode("utf-8"),
}
return dump
if mimetype is None:
dump[formname] = part.content
else:
dump[formname] = {
"filename": filename,
"content": part.content,
"content-type": mimetype.decode("utf-8"),
}
return dump
def get_formats():
+22 -19
View File
@@ -1,22 +1,24 @@
import functools
import inspect
from urllib.parse import parse_qs
import typing as t
from http.cookies import SimpleCookie
from urllib.parse import parse_qs
import chardet
import rfc3986
from requests.structures import CaseInsensitiveDict
from requests.cookies import RequestsCookieJar
from starlette.requests import Request as StarletteRequest, State
from requests.structures import CaseInsensitiveDict
from starlette.requests import Request as StarletteRequest
from starlette.requests import State
from starlette.responses import (
Response as StarletteResponse,
)
from starlette.responses import (
StreamingResponse as StarletteStreamingResponse,
)
from .status_codes import HTTP_301
from .statics import DEFAULT_ENCODING
from .status_codes import HTTP_301
class QueryDict(dict):
@@ -206,6 +208,7 @@ class Request:
async def declared_encoding(self):
if "Encoding" in self.headers:
return self.headers["Encoding"]
return None
@property
async def apparent_encoding(self):
@@ -225,20 +228,20 @@ class Request:
"""Returns ``True`` if the incoming Request accepts the given ``content_type``."""
return content_type in self.headers.get("Accept", [])
async def media(self, format=None):
async def media(self, format: t.Union[str, t.Callable] = None): # noqa: A001, A002
"""Renders incoming json/yaml/form data as Python objects. Must be awaited.
:param format: The name of the format being used. Alternatively accepts a custom callable for the format type.
:param format: The name of the format being used.
Alternatively, accepts a custom callable for the format type.
"""
if format is None:
format = "yaml" if "yaml" in self.mimetype or "" else "json"
format = "form" if "form" in self.mimetype or "" else format
format = "yaml" if "yaml" in self.mimetype or "" else "json" # noqa: A001
format = "form" if "form" in self.mimetype or "" else format # noqa: A001
if format in self.formats:
return await self.formats[format](self)
else:
return await format(self)
return await format(self)
def content_setter(mimetype):
@@ -276,11 +279,11 @@ class Response:
self.content = None #: A bytes representation of the response body.
self.mimetype = None
self.encoding = DEFAULT_ENCODING
self.media = None #: A Python object that will be content-negotiated and sent back to the client. Typically, in JSON formatting.
self.media = None #: A Python object that will be content-negotiated and
#: sent back to the client. Typically, in JSON formatting.
self._stream = None
self.headers = (
{}
) #: A Python dictionary of ``{key: value}``, representing the headers of the response.
self.headers = {} #: A Python dictionary of ``{key: value}``,
#: representing the headers of the response.
self.formats = formats
self.cookies = SimpleCookie() #: The cookies set in the Response
self.session = (
@@ -316,9 +319,9 @@ class Response:
content = content.encode(self.encoding)
return (content, headers)
for format in self.formats:
if self.req.accepts(format):
return (await self.formats[format](self, encode=True)), {}
for format_ in self.formats:
if self.req.accepts(format_):
return (await self.formats[format_](self, encode=True)), {}
# Default to JSON anyway.
return (
+8 -8
View File
@@ -1,18 +1,17 @@
import asyncio
import re
import inspect
import re
import traceback
from collections import defaultdict
from starlette.middleware.wsgi import WSGIMiddleware
from starlette.websockets import WebSocket, WebSocketClose
from starlette.concurrency import run_in_threadpool
from starlette.exceptions import HTTPException
from starlette.middleware.wsgi import WSGIMiddleware
from starlette.websockets import WebSocket, WebSocketClose
from .models import Request, Response
from . import status_codes
from .formats import get_formats
from .models import Request, Response
_CONVERTORS = {
"int": (int, r"\d+"),
@@ -120,9 +119,9 @@ class Route(BaseRoute):
try:
view = getattr(endpoint, method_name)
views.append(view)
except AttributeError:
except AttributeError as ex:
if on_request is None:
raise HTTPException(status_code=status_codes.HTTP_405)
raise HTTPException(status_code=status_codes.HTTP_405) from ex
else:
views.append(self.endpoint)
@@ -291,8 +290,9 @@ class Router:
await websocket_close(receive, send)
return
# FIXME: Please review!
request = Request(scope, receive)
response = Response(request, formats=get_formats())
response = Response(request, formats=get_formats()) # noqa: F841
raise HTTPException(status_code=status_codes.HTTP_404)
+8 -6
View File
@@ -1,14 +1,16 @@
import typing
from starlette.staticfiles import StaticFiles
from starlette.staticfiles import StaticFiles as StarletteStaticFiles
class StaticFiles(StaticFiles):
"""I've created an issue to disccuss allowing multiple directories in starletter's `StaticFiles`.
class StaticFiles(StarletteStaticFiles):
"""
Extension to Starlette's `StaticFiles`.
I've created an issue to discuss allowing multiple directories in
Starlette's `StaticFiles`.
https://github.com/encode/starlette/issues/625
I've also made a PR to add this method to starlette StaticFiles
I've also made a PR to add this method to Starlette StaticFiles
Once accepted we will remove this.
https://github.com/encode/starlette/pull/626
+1 -1
View File
@@ -1,7 +1,7 @@
DEFAULT_ENCODING = "utf-8"
DEFAULT_API_THEME = "swaggerui"
DEFAULT_SESSION_COOKIE = "Responder-Session"
DEFAULT_SECRET_KEY = "NOTASECRET"
DEFAULT_SECRET_KEY = "NOTASECRET" # noqa: S105
DEFAULT_CORS_PARAMS = {
"allow_origins": (),
+3 -3
View File
@@ -10,7 +10,7 @@ class Templates:
self.directory = directory
self._env = jinja2.Environment(
loader=jinja2.FileSystemLoader([str(self.directory)]),
autoescape=autoescape,
autoescape=autoescape, # noqa: S701
enable_async=enable_async,
)
self.default_context = {} if context is None else {**context}
@@ -33,7 +33,7 @@ class Templates:
:param template: The filename of the jinja2 template.
:param **kwargs: Data to pass into the template.
:param **kwargs: Data to pass into the template.
"""
""" # noqa: E501
return self.get_template(template).render(*args, **kwargs)
@contextmanager
@@ -54,6 +54,6 @@ class Templates:
:param source: The template to use.
:param *args, **kwargs: Data to pass into the template.
:param **kwargs: Data to pass into the template.
"""
""" # noqa: E501
template = self._env.from_string(source)
return template.render(*args, **kwargs)
+2 -2
View File
@@ -5,7 +5,7 @@ import os
import sys
from shutil import rmtree
from setuptools import find_packages, setup, Command
from setuptools import Command, find_packages, setup
here = os.path.abspath(os.path.dirname(__file__))
@@ -120,7 +120,7 @@ setup(
install_requires=required,
extras_require={
"graphql": ["graphene"],
"test": ["pytest", "pytest-cov", "pytest-mock", "flask"]
"test": ["pytest", "pytest-cov", "pytest-mock", "flask"],
},
include_package_data=True,
license="Apache 2.0",
+3 -3
View File
@@ -1,8 +1,8 @@
import responder
from pathlib import Path
import pytest
import multiprocessing
import concurrent.futures
import responder
@pytest.fixture
-3
View File
@@ -1,6 +1,3 @@
import pytest
def test_custom_encoding(api, session):
data = "hi alex!"
+1 -1
View File
@@ -1,9 +1,9 @@
import inspect
import pytest
from responder import models
_default_query = "q=%7b%20hello%20%7d&name=myname&user_name=test_user"
+39 -43
View File
@@ -1,16 +1,15 @@
import pytest
import yaml
import random
import responder
import string
from responder.routes import Route, WebSocketRoute
from responder.templates import Templates
import pytest
import yaml
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.testclient import TestClient as StarletteTestClient
import responder
from responder.routes import Route, WebSocketRoute
from responder.templates import Templates
def test_api_basic_route(api):
@api.route("/")
@@ -133,7 +132,7 @@ def test_yaml_media(api):
r = api.requests.get("http://;/", headers={"Accept": "yaml"})
assert "yaml" in r.headers["Content-Type"]
assert yaml.load(r.content, Loader=yaml.FullLoader) == dump
assert yaml.load(r.content, Loader=yaml.FullLoader) == dump # noqa: S506
def test_argumented_routing(api):
@@ -305,9 +304,7 @@ def test_json_downloads(api):
def route(req, resp):
resp.media = dump
r = api.requests.get(
api.url_for(route), headers={"Content-Type": "application/json"}
)
r = api.requests.get(api.url_for(route), headers={"Content-Type": "application/json"})
assert r.json() == dump
@@ -325,9 +322,10 @@ def test_yaml_downloads(api):
def test_schema_generation_explicit():
import marshmallow
import responder
from responder.ext.schema import OpenAPISchema as OpenAPISchema
import marshmallow
api = responder.API()
@@ -359,9 +357,10 @@ def test_schema_generation_explicit():
def test_schema_generation():
import responder
from marshmallow import Schema, fields
import responder
api = responder.API(title="Web Service", openapi="3.0.2")
@api.schema("Pet")
@@ -390,11 +389,11 @@ def test_schema_generation():
def test_documentation_explicit():
import marshmallow
import responder
from responder.ext.schema import OpenAPISchema as OpenAPISchema
import marshmallow
description = "This is a sample server for a pet store."
terms_of_service = "http://example.com/terms/"
contact = {
@@ -402,7 +401,7 @@ def test_documentation_explicit():
"url": "http://www.example.com/support",
"email": "support@example.com",
}
license = {
license_ = {
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
}
@@ -418,7 +417,7 @@ def test_documentation_explicit():
description=description,
terms_of_service=terms_of_service,
contact=contact,
license=license,
license=license_,
)
@schema.schema("Pet")
@@ -444,9 +443,10 @@ def test_documentation_explicit():
def test_documentation():
import responder
from marshmallow import Schema, fields
import responder
description = "This is a sample server for a pet store."
terms_of_service = "http://example.com/terms/"
contact = {
@@ -454,7 +454,7 @@ def test_documentation():
"url": "http://www.example.com/support",
"email": "support@example.com",
}
license = {
license_ = {
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
}
@@ -467,7 +467,7 @@ def test_documentation():
description=description,
terms_of_service=terms_of_service,
contact=contact,
license=license,
license=license_,
allowed_hosts=["testserver", ";"],
)
@@ -551,8 +551,7 @@ def test_sessions(api):
r = api.requests.get(api.url_for(view))
assert (
r.cookies[api.session_cookie]
== '{"hello": "world"}.r3EB04hEEyLYIJaAXCEq3d4YEbs'
r.cookies[api.session_cookie] == '{"hello": "world"}.r3EB04hEEyLYIJaAXCEq3d4YEbs'
)
assert r.json() == {"hello": "world"}
@@ -602,7 +601,6 @@ def test_template_async(api, template_path):
def test_file_uploads(api):
@api.route("/")
async def upload(req, resp):
files = await req.media("files")
result = {}
result["hello"] = files["hello"]["content"].decode("utf-8")
@@ -645,8 +643,8 @@ def test_websockets_text(api):
await ws.close()
client = StarletteTestClient(api)
with client.websocket_connect("ws://;/ws") as websocket:
data = websocket.receive_text()
with client.websocket_connect("ws://;/ws") as ws:
data = ws.receive_text()
assert data == payload
@@ -660,8 +658,8 @@ def test_websockets_bytes(api):
await ws.close()
client = StarletteTestClient(api)
with client.websocket_connect("ws://;/ws") as websocket:
data = websocket.receive_bytes()
with client.websocket_connect("ws://;/ws") as ws:
data = ws.receive_bytes()
assert data == payload
@@ -675,8 +673,8 @@ def test_websockets_json(api):
await ws.close()
client = StarletteTestClient(api)
with client.websocket_connect("ws://;/ws") as websocket:
data = websocket.receive_json()
with client.websocket_connect("ws://;/ws") as ws:
data = ws.receive_json()
assert data == payload
@@ -694,10 +692,10 @@ def test_before_websockets(api):
await ws.send_json({"before": "request"})
client = StarletteTestClient(api)
with client.websocket_connect("ws://;/ws") as websocket:
data = websocket.receive_json()
with client.websocket_connect("ws://;/ws") as ws:
data = ws.receive_json()
assert data == {"before": "request"}
data = websocket.receive_json()
data = ws.receive_json()
assert data == payload
@@ -713,7 +711,7 @@ def test_startup(api):
who[0] = "world"
with api.requests as session:
r = session.get(f"http://;/hello")
r = session.get("http://;/hello")
assert r.text == "hello, world!"
@@ -731,16 +729,16 @@ def test_redirects(api, session):
def test_session_thoroughly(api, session):
@api.route("/set")
def set(req, resp):
def setter(req, resp):
resp.session["hello"] = "world"
api.redirect(resp, location="/get")
@api.route("/get")
def get(req, resp):
def getter(req, resp):
resp.media = {"session": req.session}
r = session.get(api.url_for(set))
r = session.get(api.url_for(get))
r = session.get(api.url_for(setter))
r = session.get(api.url_for(getter))
assert r.json() == {"session": {"hello": "world"}}
@@ -815,9 +813,9 @@ def test_allowed_hosts(enable_hsts, cors):
def create_asset(static_dir, name=None, parent_dir=None):
if name is None:
name = random.choices(string.ascii_letters, k=6)
name = random.choices(string.ascii_letters, k=6) # noqa: S311
# :3
ext = random.choices(string.ascii_letters, k=2)
ext = random.choices(string.ascii_letters, k=2) # noqa: S311
name = f"{name}.{ext}"
if parent_dir is None:
@@ -881,7 +879,7 @@ def test_staticfiles_none_dir(tmpdir):
assert r.status_code == api.status_codes.HTTP_404
# SPA
with pytest.raises(Exception) as excinfo:
with pytest.raises(Exception): # noqa: B017
api.add_route("/spa", static=True)
@@ -918,7 +916,6 @@ def test_stream(api, session):
@api.route("/{who}")
async def greeting(req, resp, *, who):
resp.stream(shout_stream, who)
r = session.get("/morocco")
@@ -966,8 +963,7 @@ def test_empty_req_text(api):
request.state.test1 = 42
request.state.test2 = "Foo"
response = await call_next(request)
return response
return await call_next(request)
api.add_middleware(StateMiddleware)
+1
View File
@@ -1,4 +1,5 @@
import pytest
from responder import status_codes