mirror of
https://github.com/kennethreitz/responder.git
synced 2026-06-05 06:46:14 +00:00
mount other wsgi apps
This commit is contained in:
@@ -11,6 +11,7 @@ pytest = "*"
|
||||
"flake8" = "*"
|
||||
black = "*"
|
||||
twine = "*"
|
||||
flask = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.7"
|
||||
|
||||
Generated
+38
-14
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "13d6d0b2229e162611f4355a6887db0719a0c47471facb391b524635567d6884"
|
||||
"sha256": "5843d79d019341544a1c9456b537125203079f127721132c8111421095660524"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -117,7 +117,6 @@
|
||||
"sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
|
||||
"sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
|
||||
],
|
||||
"markers": "python_version < '4' and python_version != '3.3.*' and python_version != '3.2.*' and python_version != '3.0.*' and python_version != '3.1.*' and python_version >= '2.6'",
|
||||
"version": "==1.23"
|
||||
},
|
||||
"waitress": {
|
||||
@@ -155,7 +154,6 @@
|
||||
"sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0",
|
||||
"sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"
|
||||
],
|
||||
"markers": "python_version != '3.2.*' and python_version >= '2.7' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*'",
|
||||
"version": "==1.2.1"
|
||||
},
|
||||
"attrs": {
|
||||
@@ -175,10 +173,10 @@
|
||||
},
|
||||
"bleach": {
|
||||
"hashes": [
|
||||
"sha256:9c471c0dd9c820f6bf4ee5ca3e348ceccefbc1475d9a40c397ed5d04e0b42c54",
|
||||
"sha256:b407b2612b37e6cdc6704f84cec18c1f140b78e6c625652a844e89d6b9855f6b"
|
||||
"sha256:c39d25d9ada62009853b0281efdc35a792db8cdee89465433e6d0aaaf5defc3f",
|
||||
"sha256:f680cc08e2eea821f3173b875f68763960006f1e764c92b5d2b8354af3a47468"
|
||||
],
|
||||
"version": "==3.0.0"
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
@@ -236,7 +234,6 @@
|
||||
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
||||
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
|
||||
],
|
||||
"markers": "python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.2.*' and python_version != '3.1.*' and python_version >= '2.7'",
|
||||
"version": "==7.0"
|
||||
},
|
||||
"cmarkgfm": {
|
||||
@@ -270,7 +267,6 @@
|
||||
"sha256:e8932bddf159064f04e946fbb64693753488de21586f20e840b3be51745c8c09",
|
||||
"sha256:f20900f16377f2109783ae9348d34bc80530808439591c3d3df73d5c7ef1a00c"
|
||||
],
|
||||
"markers": "python_version != '3.2.*' and python_version >= '2.7' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*'",
|
||||
"version": "==0.4.2"
|
||||
},
|
||||
"colorama": {
|
||||
@@ -297,6 +293,14 @@
|
||||
"index": "pypi",
|
||||
"version": "==3.5.0"
|
||||
},
|
||||
"flask": {
|
||||
"hashes": [
|
||||
"sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
|
||||
"sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.2"
|
||||
},
|
||||
"future": {
|
||||
"hashes": [
|
||||
"sha256:e39ced1ab767b5936646cedba8bcce582398233d6a627067d4c6a454c90cfedb"
|
||||
@@ -310,6 +314,25 @@
|
||||
],
|
||||
"version": "==2.7"
|
||||
},
|
||||
"itsdangerous": {
|
||||
"hashes": [
|
||||
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
|
||||
],
|
||||
"version": "==0.24"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
|
||||
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
|
||||
],
|
||||
"version": "==2.10"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
|
||||
],
|
||||
"version": "==1.0"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||
@@ -337,7 +360,6 @@
|
||||
"sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1",
|
||||
"sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1"
|
||||
],
|
||||
"markers": "python_version != '3.2.*' and python_version >= '2.7' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*'",
|
||||
"version": "==0.7.1"
|
||||
},
|
||||
"py": {
|
||||
@@ -345,7 +367,6 @@
|
||||
"sha256:06a30435d058473046be836d3fc4f27167fd84c45b99704f2fb5509ef61f9af1",
|
||||
"sha256:50402e9d1c9005d759426988a492e0edaadb7f4e68bcddfea586bc7432d009c6"
|
||||
],
|
||||
"markers": "python_version != '3.2.*' and python_version >= '2.7' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*'",
|
||||
"version": "==1.6.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
@@ -359,7 +380,6 @@
|
||||
"hashes": [
|
||||
"sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
|
||||
],
|
||||
"markers": "python_version != '3.2.*' and python_version != '3.1.*' and python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.3.*'",
|
||||
"version": "==2.19"
|
||||
},
|
||||
"pyflakes": {
|
||||
@@ -389,7 +409,6 @@
|
||||
"sha256:237ca8705ffea849870de41101dba41543561da05c0ae45b2f1c547efa9843d2",
|
||||
"sha256:f75049a3a7afa57165551e030dd8f9882ebf688b9600535a3f7e23596651875d"
|
||||
],
|
||||
"markers": "python_version != '3.2.*' and python_version >= '2.7' and python_version != '3.3.*' and python_version != '3.0.*' and python_version != '3.1.*'",
|
||||
"version": "==22.0"
|
||||
},
|
||||
"requests": {
|
||||
@@ -425,7 +444,6 @@
|
||||
"sha256:18f1818ce951aeb9ea162ae1098b43f583f7d057b34d706f66939353d1208889",
|
||||
"sha256:df02c0650160986bac0218bb07952245fc6960d23654648b5d5526ad5a4128c9"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version != '3.0.*' and python_version != '3.1.*'",
|
||||
"version": "==4.26.0"
|
||||
},
|
||||
"twine": {
|
||||
@@ -441,7 +459,6 @@
|
||||
"sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
|
||||
"sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
|
||||
],
|
||||
"markers": "python_version < '4' and python_version != '3.3.*' and python_version != '3.2.*' and python_version != '3.0.*' and python_version != '3.1.*' and python_version >= '2.6'",
|
||||
"version": "==1.23"
|
||||
},
|
||||
"webencodings": {
|
||||
@@ -450,6 +467,13 @@
|
||||
"sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
|
||||
],
|
||||
"version": "==0.5.1"
|
||||
},
|
||||
"werkzeug": {
|
||||
"hashes": [
|
||||
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
|
||||
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
|
||||
],
|
||||
"version": "==0.14.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ The primary concept here is to bring the nicities that are brought forth from bo
|
||||
## New Ideas
|
||||
|
||||
- **A built in testing client that uses the actual Requests you know and love**.
|
||||
- The ability to mount other WSGI apps easily.
|
||||
- Automatic gzipped-responses (still working on that).
|
||||
- In addition to Falcon's `on_get`, `on_post`, etc methods, Responder features an `on_request` method, which gets called on every type of request, much like Requests.
|
||||
- WhiteNoise is built-in, for serving static files (this has yet to be built out, there's no templating or `static_url` yet)
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import responder
|
||||
import graphene
|
||||
|
||||
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def hello_world():
|
||||
return "Hello, World from flask!"
|
||||
|
||||
|
||||
api = responder.API()
|
||||
# api.mount('/subapp', other_wsgi_app)
|
||||
api.mount("/hello", app)
|
||||
|
||||
|
||||
@api.route("/")
|
||||
@@ -44,7 +55,7 @@ print(
|
||||
print(
|
||||
api.session()
|
||||
.get(
|
||||
"http://app/graph",
|
||||
"http://app/hello/",
|
||||
data="{ hello }",
|
||||
headers={"Accept": "application/x-yaml"},
|
||||
# data="hello",
|
||||
@@ -53,4 +64,4 @@ print(
|
||||
)
|
||||
# {hello: Hello stranger}
|
||||
|
||||
api.run(port=5000, expose_tracebacks=True)
|
||||
# api.run(port=5000, expose_tracebacks=True)
|
||||
|
||||
+37
-29
@@ -6,18 +6,28 @@ import waitress
|
||||
from whitenoise import WhiteNoise
|
||||
from wsgiadapter import WSGIAdapter as RequestsWSGIAdapter
|
||||
from requests import Session as RequestsSession
|
||||
from werkzeug.wsgi import DispatcherMiddleware
|
||||
|
||||
from . import models
|
||||
from .status import HTTP_404
|
||||
|
||||
|
||||
class BaseAPI:
|
||||
__slots__ = ["routes"]
|
||||
|
||||
def __init__(self):
|
||||
class API:
|
||||
def __init__(self, static="static"):
|
||||
self.static_dir = Path(os.path.abspath(static))
|
||||
self.routes = {}
|
||||
self.apps = {"/": self._wsgi_app}
|
||||
|
||||
def _wsgi_app(self, environ, start_response):
|
||||
# Make the static directory if it doesn't exist.
|
||||
os.makedirs(self.static_dir, exist_ok=True)
|
||||
|
||||
# Mount the whitenoise application.
|
||||
self.whitenoise = WhiteNoise(self.__wsgi_app, root=str(self.static_dir))
|
||||
|
||||
# Cached requests session.
|
||||
self._session = None
|
||||
|
||||
def __wsgi_app(self, environ, start_response):
|
||||
# def wsgi_app(self, request):
|
||||
"""The actual WSGI application. This is not implemented in
|
||||
:meth:`__call__` so that middlewares can be applied without
|
||||
@@ -44,14 +54,20 @@ class BaseAPI:
|
||||
start the response.
|
||||
"""
|
||||
|
||||
req = models.Request.from_environ(environ)
|
||||
req = models.Request.from_environ(environ, start_response)
|
||||
# if not req.dispatched:
|
||||
resp = self._dispatch_request(req)
|
||||
|
||||
return resp(environ, start_response)
|
||||
|
||||
def wsgi_app(self, environ, start_response):
|
||||
def _wsgi_app(self, environ, start_response):
|
||||
return self.whitenoise(environ, start_response)
|
||||
|
||||
def wsgi_app(self, environ, start_response):
|
||||
apps = self.apps.copy()
|
||||
main = apps.pop("/")
|
||||
|
||||
return DispatcherMiddleware(main, apps)(environ, start_response)
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
"""The WSGI server calls the Flask application object as the
|
||||
WSGI application. This calls :meth:`wsgi_app` which can be
|
||||
@@ -74,10 +90,18 @@ class BaseAPI:
|
||||
except TypeError:
|
||||
try:
|
||||
view = self.routes[route]()
|
||||
# GraphQL Schema.
|
||||
except TypeError:
|
||||
view = self.routes[route]
|
||||
self.graphql_response(req, resp, schema=view)
|
||||
try:
|
||||
# GraphQL Schema.
|
||||
assert hasattr(view, "execute")
|
||||
self.graphql_response(req, resp, schema=view)
|
||||
except AssertionError:
|
||||
# WSGI App.
|
||||
req.dispatched = True
|
||||
return view(
|
||||
environ=req._environ, start_response=req._start_response
|
||||
)
|
||||
|
||||
# Run on_request first.
|
||||
try:
|
||||
@@ -97,25 +121,6 @@ class BaseAPI:
|
||||
|
||||
return resp
|
||||
|
||||
@property
|
||||
def static_dir(self):
|
||||
return Path(".")
|
||||
|
||||
|
||||
class API(BaseAPI):
|
||||
__slots__ = ("routes", "_session", "whitenoise", "static_dir")
|
||||
|
||||
def __init__(self, static="static"):
|
||||
super().__init__()
|
||||
self._session = None
|
||||
self.static_dir = Path(os.path.abspath(static))
|
||||
|
||||
# Make the static directory if it doesn't exist.
|
||||
os.makedirs(self.static_dir, exist_ok=True)
|
||||
|
||||
# Mount the whitenoise application.
|
||||
self.whitenoise = WhiteNoise(self._wsgi_app, root=str(self.static_dir))
|
||||
|
||||
def add_route(self, route, view, *, check_existing=True, graphiql=False):
|
||||
if check_existing:
|
||||
assert route not in self.routes
|
||||
@@ -158,6 +163,9 @@ class API(BaseAPI):
|
||||
|
||||
return decorator
|
||||
|
||||
def mount(self, route, wsgi_app):
|
||||
self.apps.update({route: wsgi_app})
|
||||
|
||||
def session(self, base_url="http://app"):
|
||||
if self._session is None:
|
||||
session = RequestsSession()
|
||||
|
||||
+5
-2
@@ -8,7 +8,7 @@ from requests.models import Request as RequestsRequest
|
||||
from requests.structures import CaseInsensitiveDict
|
||||
from werkzeug.wrappers import Request as WerkzeugRequest
|
||||
from werkzeug.wrappers import BaseResponse as WerkzeugResponse
|
||||
|
||||
from werkzeug.wsgi import DispatcherMiddleware
|
||||
|
||||
from urllib.parse import parse_qs
|
||||
|
||||
@@ -25,7 +25,7 @@ class Request:
|
||||
self._wz = None
|
||||
|
||||
@classmethod
|
||||
def from_environ(kls, environ):
|
||||
def from_environ(kls, environ, start_response=None):
|
||||
self = kls()
|
||||
self._wz = WerkzeugRequest(environ)
|
||||
self.headers = CaseInsensitiveDict(self._wz.headers.to_list())
|
||||
@@ -39,6 +39,9 @@ class Request:
|
||||
self.content = self._wz.get_data(cache=True, as_text=False)
|
||||
self.text = self._wz.get_data(cache=True, as_text=True)
|
||||
self.data = self._wz.get_data(cache=True, as_text=True, parse_form_data=True)
|
||||
self.dispatched = False
|
||||
self._start_response = start_response
|
||||
self._environ = environ
|
||||
|
||||
return self
|
||||
|
||||
|
||||
Reference in New Issue
Block a user