mirror of
https://github.com/kennethreitz/responder.git
synced 2026-06-05 06:46:14 +00:00
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:
+69
-2
@@ -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
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[pytest]
|
||||
addopts= -rsxX -s -vvv --strict
|
||||
filterwarnings =
|
||||
error::UserWarning
|
||||
@@ -1,2 +1,9 @@
|
||||
from .core import *
|
||||
from . import ext
|
||||
from .core import API, Request, Response
|
||||
|
||||
__all__ = [
|
||||
"API",
|
||||
"Request",
|
||||
"Response",
|
||||
"ext",
|
||||
]
|
||||
|
||||
+25
-24
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
from .api import API
|
||||
from .models import Request, Response
|
||||
|
||||
__all__ = [
|
||||
"API",
|
||||
"Request",
|
||||
"Response",
|
||||
]
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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,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": (),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -1,8 +1,8 @@
|
||||
import responder
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import multiprocessing
|
||||
import concurrent.futures
|
||||
|
||||
import responder
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def test_custom_encoding(api, session):
|
||||
data = "hi alex!"
|
||||
|
||||
|
||||
@@ -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
@@ -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,4 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from responder import status_codes
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user