Compare commits

...

14 Commits

Author SHA1 Message Date
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
taoufik 906cd2fbbf Fix templates conflicts 2019-10-20 12:48:02 +01:00
taoufik 9d0129da56 Fix templates conflicts 2019-10-20 12:39:32 +01:00
taoufik aedcf12d99 v2.0.1 2019-10-20 12:20:54 +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
Taoufik a7110ef441 Merge pull request #395 from vbarbaresi/master
add a few tests for API and remove some dead code
2019-10-19 13:06:01 +01:00
Vincent Barbaresi d3e4968546 add a few tests for API and remove some dead code
Increasing the coverage of api.py to 86%

- use pytest.parametrize to test cors and hsts parameters usage
- use pytest.parametrize to factorize test_staticfiles_custom_route
- add tests for path_matches_route and Route() with no endpoint
- test Jinja template rendering from a file

- remove   _notfound_wsgi_app, before_ws_requests, before_http_requests

they didn't seem used anywhere and the before_* method were broken, referring to
self.before_requests that doesn't exist anymore
2019-10-19 13:47:39 +02:00
Taoufik 03e34d56ab Merge pull request #398 from taoufik07/v2
v2.0.0
2019-10-19 12:22:32 +01:00
taoufik b470d10416 v2.0.0 2019-10-19 12:21:06 +01:00
10 changed files with 124 additions and 68 deletions
+25 -1
View File
@@ -5,6 +5,25 @@ 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).
## [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
### Fixed
- Fix template conflicts
## [v2.0.1] - 2019-09-20
### Fixed
- Fix template import
## [v2.0.0] - 2019-09-19
### Changed
- Refactor Router and Schema
## [v1.3.2] - 2019-08-15
### Added
@@ -208,7 +227,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Conception!
[Unreleased]: https://github.com/taoufik07/responder/compare/v1.3.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.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
[v1.3.2]: https://github.com/taoufik07/responder/compare/v1.3.1..v1.3.2
[v1.3.1]: https://github.com/taoufik07/responder/compare/v1.3.0..v1.3.1
[v1.3.0]: https://github.com/taoufik07/responder/compare/v1.2.0..v1.3.0
Generated
+7
View File
@@ -116,6 +116,13 @@
],
"version": "==2.8"
},
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
"version": "==1.1.0"
},
"jinja2": {
"hashes": [
"sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f",
+2 -1
View File
@@ -85,9 +85,10 @@ Usage::
def hello(req, resp, name):
resp.html = templates.render("hello.html", name=name)
Also a ``render_async`` is available::
templates = Templates(enable_async=True)
resp.html = await templates.render_async("hello.html", who=who)
You can also use the existing ``api.template(filename, *args, **kwargs)`` to render templates::
+1 -1
View File
@@ -1 +1 @@
__version__ = "1.3.2"
__version__ = "2.0.4"
+3 -27
View File
@@ -76,15 +76,6 @@ class API:
self.static_dir = static_dir
self.static_route = static_route
self.built_in_templates_dir = Path(
os.path.abspath(os.path.dirname(__file__) + "/templates")
)
if templates_dir is not None:
templates_dir = Path(os.path.abspath(templates_dir))
self.templates_dir = templates_dir or self.built_in_templates_dir
self.hsts_enabled = enable_hsts
self.cors = cors
self.cors_params = cors_params
@@ -98,10 +89,8 @@ class API:
allowed_hosts = ["*"]
self.allowed_hosts = allowed_hosts
# Make the static/templates directory if they don't exist.
for _dir in (self.static_dir, self.templates_dir):
if _dir is not None:
os.makedirs(_dir, exist_ok=True)
if self.static_dir is not None:
os.makedirs(self.static_dir, exist_ok=True)
if self.static_dir is not None:
self.mount(self.static_route, self.static_app)
@@ -153,11 +142,6 @@ class API:
self._static_app = StaticFiles(directory=self.static_dir)
return self._static_app
@staticmethod
def _notfound_wsgi_app(environ, start_response):
start_response("404 NOT FOUND", [("Content-Type", "text/plain")])
return [b"Not Found."]
def before_request(self, websocket=False):
def decorator(f):
self.router.before_request(f, websocket=websocket)
@@ -165,14 +149,6 @@ class API:
return decorator
@property
def before_http_requests(self):
return self.before_requests.get("http", [])
@property
def before_ws_requests(self):
return self.before_requests.get("ws", [])
def add_middleware(self, middleware_cls, **middleware_config):
self.app = middleware_cls(self.app, **middleware_config)
@@ -242,7 +218,7 @@ class API:
index = (self.static_dir / "index.html").resolve()
if os.path.exists(index):
with open(index, "r") as f:
resp.html = "Hello world !"
resp.html = f.read()
else:
resp.status_code = status_codes.HTTP_404
resp.text = "Not found."
+9 -8
View File
@@ -302,6 +302,15 @@ class Router:
path = scope["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.
for path_prefix, app in self.apps.items():
if path.startswith(path_prefix):
@@ -315,12 +324,4 @@ class Router:
await app(scope, receive, send)
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)
+6 -2
View File
@@ -4,10 +4,14 @@ import jinja2
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._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._env.globals.update(self.default_context)
+1
View File
@@ -40,6 +40,7 @@ required = [
"docopt",
"requests-toolbelt",
"apistar",
"itsdangerous",
]
+9
View File
@@ -56,3 +56,12 @@ def schema():
return f"Hello {name}"
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
+61 -28
View File
@@ -8,6 +8,7 @@ import requests
import string
import io
from responder.routes import Router, Route, WebSocketRoute
from responder.templates import Templates
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import PlainTextResponse
@@ -590,6 +591,39 @@ def test_template_string_rendering(api):
assert r.text == "hello"
def test_template_rendering(template_path):
api = responder.API(templates_dir=template_path.dirpath())
@api.route("/")
def view(req, resp):
resp.content = api.template(template_path.basename, var="hello")
r = api.requests.get(api.url_for(view))
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):
@api.route("/")
async def upload(req, resp):
@@ -751,8 +785,12 @@ def test_before_response(api, session):
assert "x-pizza" in r.headers
def test_allowed_hosts():
api = responder.API(allowed_hosts=[";", "tenant.;"])
@pytest.mark.parametrize("enable_hsts", [True, False])
@pytest.mark.parametrize("cors", [True, False])
def test_allowed_hosts(enable_hsts, cors):
api = responder.API(
allowed_hosts=[";", "tenant.;"], enable_hsts=enable_hsts, cors=cors
)
@api.route("/")
def get(req, resp):
@@ -816,14 +854,15 @@ def create_asset(static_dir, name=None, parent_dir=None):
return asset
def test_staticfiles(tmpdir):
@pytest.mark.parametrize("static_route", [None, "/static", "/custom/static/route"])
def test_staticfiles(tmpdir, static_route):
static_dir = tmpdir.mkdir("static")
asset1 = create_asset(static_dir)
parent_dir = "css"
asset2 = create_asset(static_dir, name="asset2", parent_dir=parent_dir)
api = responder.API(static_dir=str(static_dir))
api = responder.API(static_dir=str(static_dir), static_route=static_route)
session = api.session()
static_route = api.static_route
@@ -847,30 +886,6 @@ def test_staticfiles(tmpdir):
assert r.status_code == api.status_codes.HTTP_404
def test_staticfiles_custom_route(tmpdir):
static_dir = tmpdir.mkdir("static")
static_route = "/custom/static/route"
asset = create_asset(static_dir)
api = responder.API(static_dir=str(static_dir), static_route=static_route)
session = api.session()
static_route = api.static_route
# ok
r = session.get(f"{static_route}/{asset.basename}")
assert r.status_code == api.status_codes.HTTP_200
# Asset not found
r = session.get(f"{static_route}/not_found.css")
assert r.status_code == api.status_codes.HTTP_404
# Not found on dir listing
r = session.get(f"{static_route}")
assert r.status_code == api.status_codes.HTTP_404
def test_staticfiles_none_dir(tmpdir):
api = responder.API(static_dir=None)
session = api.session()
@@ -985,3 +1000,21 @@ def test_empty_req_text(api):
resp.text = "{}_{}".format(req.state.test2, req.state.test1)
assert api.requests.get(url("/")).text == "Foo_42"
def test_path_matches_route(api):
@api.route("/hello")
def home(req, resp):
resp.text = "hello world!"
route = api.path_matches_route({"type": "http", "path": "/hello"})
assert route.endpoint_name == "home"
assert not api.path_matches_route({"type": "http", "path": "/foo"})
def test_route_without_endpoint(api):
# test that a route without endpoint gets a default static response
api.add_route("/")
route = api.router.routes[0]
assert route.endpoint_name == "_static_response"