Compare commits

...

8 Commits

Author SHA1 Message Date
taoufik 8a46a87b3e Fix missing openapi title, version and openapi_version and black 2019-12-03 19:38:12 +01:00
taoufik 0678daa880 Bump to v2.0.4 2019-11-19 17:35:16 +01:00
Taoufik 6761e3bdd8 Merge pull request #406 from daphil19/master
fix issues related to using `static=true` in `api.add_route()` and `static_route` in `responder.API()`
2019-11-19 17:30:22 +01:00
David Phillips ead213a506 responder now checks routes added via add_route before mounted routes
routes added by `add_route` (including @api.route()) are now checked
before routes that were added by api.mount()

this especially helps cases for situations where the static route
defined by the API class is '/' but the default route is '/' as well
2019-11-02 13:57:39 -04:00
David Phillips 75b5782eee fix static_response endpoint to return index.html contents 2019-11-02 13:55:34 -04:00
Taoufik a80df809e4 Merge pull request #399 from vbarbaresi/fix_render_async
Async templates require enable_async in jinja2 Environment
2019-10-25 22:19:04 +01:00
taoufik 7f3177f662 v2.0.3 2019-10-22 10:41:34 +01:00
Vincent Barbaresi 86361523e2 fix async templates rendering requiring enable_async in jinja2
When trying to test the render_async() feature I realized that it wasn't working
The template instance needs to be created with enable_async=True
2019-10-19 14:49:50 +02:00
11 changed files with 78 additions and 29 deletions
+10 -1
View File
@@ -5,6 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
## [v2.0.4] - 2019-11-19
### Fixed
- Fix static app resolving
## [v2.0.3] - 2019-09-20
### Fixed
- Fix template conflicts
## [v2.0.2] - 2019-09-20 ## [v2.0.2] - 2019-09-20
### Fixed ### Fixed
@@ -220,7 +227,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Conception! - Conception!
[Unreleased]: https://github.com/taoufik07/responder/compare/v2.0.2..HEAD [Unreleased]: https://github.com/taoufik07/responder/compare/v2.0.4..HEAD
[v2.0.4]: https://github.com/taoufik07/responder/compare/v2.0.3..v2.0.4
[v2.0.3]: https://github.com/taoufik07/responder/compare/v2.0.2..v2.0.3
[v2.0.2]: https://github.com/taoufik07/responder/compare/v2.0.1..v2.0.2 [v2.0.2]: https://github.com/taoufik07/responder/compare/v2.0.1..v2.0.2
[v2.0.1]: https://github.com/taoufik07/responder/compare/v2.0.0..v2.0.1 [v2.0.1]: https://github.com/taoufik07/responder/compare/v2.0.0..v2.0.1
[v2.0.0]: https://github.com/taoufik07/responder/compare/v1.3.2..v2.0.0 [v2.0.0]: https://github.com/taoufik07/responder/compare/v1.3.2..v2.0.0
Generated
+7
View File
@@ -116,6 +116,13 @@
], ],
"version": "==2.8" "version": "==2.8"
}, },
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
"version": "==1.1.0"
},
"jinja2": { "jinja2": {
"hashes": [ "hashes": [
"sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f",
+2 -1
View File
@@ -85,9 +85,10 @@ Usage::
def hello(req, resp, name): def hello(req, resp, name):
resp.html = templates.render("hello.html", name=name) resp.html = templates.render("hello.html", name=name)
Also a ``render_async`` is available:: Also a ``render_async`` is available::
templates = Templates(enable_async=True)
resp.html = await templates.render_async("hello.html", who=who) resp.html = await templates.render_async("hello.html", who=who)
You can also use the existing ``api.template(filename, *args, **kwargs)`` to render templates:: You can also use the existing ``api.template(filename, *args, **kwargs)`` to render templates::
+1 -1
View File
@@ -1 +1 @@
__version__ = "2.0.2" __version__ = "2.0.4"
+6 -5
View File
@@ -44,12 +44,13 @@ class API:
*, *,
debug=False, debug=False,
title=None, title=None,
version=None, version="1.0",
description=None, description=None,
terms_of_service=None, terms_of_service=None,
contact=None, contact=None,
license=None, license=None,
openapi=None, openapi=None,
openapi_version="3.0.2",
openapi_route="/schema.yml", openapi_route="/schema.yml",
static_dir="static", static_dir="static",
static_route="/static", static_route="/static",
@@ -117,9 +118,9 @@ class API:
if openapi or docs_route: if openapi or docs_route:
self.openapi = OpenAPISchema( self.openapi = OpenAPISchema(
app=self, app=self,
title="Web Service", title=title,
version="1.0", version=version,
openapi="3.0.2", openapi=openapi_version,
docs_route=docs_route, docs_route=docs_route,
description=description, description=description,
terms_of_service=terms_of_service, terms_of_service=terms_of_service,
@@ -218,7 +219,7 @@ class API:
index = (self.static_dir / "index.html").resolve() index = (self.static_dir / "index.html").resolve()
if os.path.exists(index): if os.path.exists(index):
with open(index, "r") as f: with open(index, "r") as f:
resp.html = "Hello world !" resp.html = f.read()
else: else:
resp.status_code = status_codes.HTTP_404 resp.status_code = status_codes.HTTP_404
resp.text = "Not found." resp.text = "Not found."
+1 -3
View File
@@ -283,9 +283,7 @@ class Response:
self.content = None #: A bytes representation of the response body. self.content = None #: A bytes representation of the response body.
self.mimetype = None self.mimetype = None
self.encoding = DEFAULT_ENCODING self.encoding = DEFAULT_ENCODING
self.media = ( self.media = None #: A Python object that will be content-negotiated and sent back to the client. Typically, in JSON formatting.
None
) #: A Python object that will be content-negotiated and sent back to the client. Typically, in JSON formatting.
self._stream = None self._stream = None
self.headers = ( self.headers = (
{} {}
+9 -8
View File
@@ -302,6 +302,15 @@ class Router:
path = scope["path"] path = scope["path"]
root_path = scope.get("root_path", "") root_path = scope.get("root_path", "")
# Check "primary" mounted routes first (before submounted apps)
route = self._resolve_route(scope)
scope["before_requests"] = self.before_requests
if route is not None:
await route(scope, receive, send)
return
# Call into a submounted app, if one exists. # Call into a submounted app, if one exists.
for path_prefix, app in self.apps.items(): for path_prefix, app in self.apps.items():
if path.startswith(path_prefix): if path.startswith(path_prefix):
@@ -315,12 +324,4 @@ class Router:
await app(scope, receive, send) await app(scope, receive, send)
return return
route = self._resolve_route(scope)
scope["before_requests"] = self.before_requests
if route is not None:
await route(scope, receive, send)
return
await self.default_response(scope, receive, send) await self.default_response(scope, receive, send)
+6 -2
View File
@@ -4,10 +4,14 @@ import jinja2
class Templates: class Templates:
def __init__(self, directory="templates", autoescape=True, context=None): def __init__(
self, directory="templates", autoescape=True, context=None, enable_async=False
):
self.directory = directory self.directory = directory
self._env = jinja2.Environment( self._env = jinja2.Environment(
loader=jinja2.FileSystemLoader([str(self.directory)]), autoescape=autoescape loader=jinja2.FileSystemLoader([str(self.directory)]),
autoescape=autoescape,
enable_async=enable_async,
) )
self.default_context = {} if context is None else {**context} self.default_context = {} if context is None else {**context}
self._env.globals.update(self.default_context) self._env.globals.update(self.default_context)
+1
View File
@@ -40,6 +40,7 @@ required = [
"docopt", "docopt",
"requests-toolbelt", "requests-toolbelt",
"apistar", "apistar",
"itsdangerous",
] ]
+9
View File
@@ -56,3 +56,12 @@ def schema():
return f"Hello {name}" return f"Hello {name}"
return graphene.Schema(query=Query) return graphene.Schema(query=Query)
@pytest.fixture
def template_path(tmpdir):
# create a Jinja template file on the filesystem
template_name = "test.html"
template_file = tmpdir.mkdir("static").join(template_name)
template_file.write("{{ var }}")
return template_file
+26 -8
View File
@@ -8,6 +8,7 @@ import requests
import string import string
import io import io
from responder.routes import Router, Route, WebSocketRoute from responder.routes import Router, Route, WebSocketRoute
from responder.templates import Templates
from starlette.middleware.base import BaseHTTPMiddleware from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import PlainTextResponse from starlette.responses import PlainTextResponse
@@ -590,22 +591,39 @@ def test_template_string_rendering(api):
assert r.text == "hello" assert r.text == "hello"
def test_template_rendering(tmpdir): def test_template_rendering(template_path):
# create a Jinja template file on the filesystem api = responder.API(templates_dir=template_path.dirpath())
template_name = "test.html"
template_file = tmpdir.mkdir("static").join(template_name)
template_file.write("{{ var }}")
api = responder.API(templates_dir=template_file.dirpath())
@api.route("/") @api.route("/")
def view(req, resp): def view(req, resp):
resp.content = api.template(template_name, var="hello") resp.content = api.template(template_path.basename, var="hello")
r = api.requests.get(api.url_for(view)) r = api.requests.get(api.url_for(view))
assert r.text == "hello" assert r.text == "hello"
def test_template(api, template_path):
templates = Templates(directory=template_path.dirpath())
@api.route("/{var}/")
def view(req, resp, var):
resp.html = templates.render(template_path.basename, var=var)
r = api.requests.get("/test/")
assert r.text == "test"
def test_template_async(api, template_path):
templates = Templates(directory=template_path.dirpath(), enable_async=True)
@api.route("/{var}/async")
async def view_async(req, resp, var):
resp.html = await templates.render_async(template_path.basename, var=var)
r = api.requests.get("/test/async")
assert r.text == "test"
def test_file_uploads(api): def test_file_uploads(api):
@api.route("/") @api.route("/")
async def upload(req, resp): async def upload(req, resp):