mirror of
https://github.com/kennethreitz/responder.git
synced 2026-06-05 23:00:17 +00:00
readme
This commit is contained in:
@@ -10,6 +10,7 @@ pyyaml = "*"
|
||||
requests = "*"
|
||||
requests-wsgi-adapter = "*"
|
||||
graphene = "*"
|
||||
whitenoise = "*"
|
||||
|
||||
[dev-packages]
|
||||
pytest = "*"
|
||||
|
||||
Generated
+9
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "35fa1f64486f787f137dd769806ab0b753ec56ae94e6f9ebb953be588c81d3be"
|
||||
"sha256": "f6b7cc3cf0ac2760ea99bcb8d18c743eff418c6269da29823ccfdbdea19a8c1e"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -135,6 +135,14 @@
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.14.1"
|
||||
},
|
||||
"whitenoise": {
|
||||
"hashes": [
|
||||
"sha256:133a92ff0ab8fb9509f77d4f7d0de493eca19c6fea973f4195d4184f888f2e02",
|
||||
"sha256:32b57d193478908a48acb66bf73e7a3c18679263e3e64bfebcfac1144a430039"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.1"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Responder: a Sorta Familar HTTP Framework for Python
|
||||
|
||||

|
||||
|
||||
I'm adept to keep the "for humans" tagline off this project, until it comes out of the prototyping phase. I'm building this to learn, and to have fun -- while, at the same time, trying to bring something new to the table.
|
||||
|
||||
The Python world certianly doesn't need more web frameworks. But, it does need more creativity, so I thought I'd bring some of my ideas to the table and see what I could come up with.
|
||||
|
||||
# The Basic Idea
|
||||
|
||||
The primary concept here is to bring the nicities that are brought forth from both Flask and Falcon and unify them into a single framework, along with some new ideas I have. I also wanted to take some of the API primitaves that are instilled in the Requests library and put them into a web framework. So, you'll find a lot of parallels here with Requests.
|
||||
|
||||
## Old Ideas
|
||||
|
||||
- Flask-style route expression, with new capabilities -- primarily, the ability to cast a parameter to integers as well as other types that are missing from Flask, all while using Python 3.6+'s new f-string syntax.
|
||||
|
||||
- I love Falcon's "every request and response is passed into to each view and mutated" methodology, especially `response.media`, and have used it here. In addition to supporting JSON, I have decided to support YAML as well, as Kubernetes is slowly taking over the world, and it uses YAML for all the things. Content-negotiation and all that.
|
||||
|
||||
## New Ideas
|
||||
|
||||
- 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)
|
||||
- Waitress (will-be) built-in as a production web server. I would have chosen Gunicorn, but it doesn't run on Windows. Plus, Waitress serves well to protect against slowloris attacks, making nginx unneccessary in production.
|
||||
- GraphQL support, via Graphene. The goal here is to eventually have an embedded version of GraphiQL exposable at any route.
|
||||
|
||||
## Future Ideas
|
||||
|
||||
- I want to be able to "mount" any WSGI app into a sub-route.
|
||||
- Cooke-based sessions are currently an afterthrought, as this is an API framework, but websites are APIs too.
|
||||
- Potentially support ASGI instead of WSGI. Will the tradeoffs be worth it? This is a question to ask. Procedural code works well for 90% use cases.
|
||||
|
||||
# The Goal
|
||||
|
||||
The primary goal here is to learn, not to get adoption. Though, who knows how these things will pan out.
|
||||
@@ -1,7 +1,7 @@
|
||||
import responder
|
||||
import graphene
|
||||
|
||||
api = responder.API()
|
||||
api = responder.API(static="static")
|
||||
# api.mount('/subapp', other_wsgi_app)
|
||||
|
||||
|
||||
|
||||
+24
-4
@@ -1,5 +1,9 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import graphene
|
||||
|
||||
from whitenoise import WhiteNoise
|
||||
from wsgiadapter import WSGIAdapter as RequestsWSGIAdapter
|
||||
from requests import Session as RequestsSession
|
||||
|
||||
@@ -13,7 +17,7 @@ class BaseAPI:
|
||||
def __init__(self):
|
||||
self.routes = {}
|
||||
|
||||
def wsgi_app(self, environ, start_response):
|
||||
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
|
||||
@@ -45,6 +49,9 @@ class BaseAPI:
|
||||
|
||||
return resp(environ, start_response)
|
||||
|
||||
def wsgi_app(self, environ, start_response):
|
||||
return self.whitenoise(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
|
||||
@@ -90,18 +97,31 @@ class BaseAPI:
|
||||
|
||||
return resp
|
||||
|
||||
@property
|
||||
def static_dir(self):
|
||||
return Path(".")
|
||||
|
||||
|
||||
class API(BaseAPI):
|
||||
__slots__ = ("routes", "_session")
|
||||
__slots__ = ("routes", "_session", "whitenoise", "static_dir")
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, static="static"):
|
||||
super().__init__()
|
||||
self._session = None
|
||||
self.static_dir = Path(os.path.abspath(static))
|
||||
|
||||
def add_route(self, route, view, *, check_existing=True):
|
||||
# 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
|
||||
|
||||
# TODO: Support grpahiql.
|
||||
|
||||
self.routes[route] = view
|
||||
|
||||
def default_response(self, req, resp):
|
||||
|
||||
Reference in New Issue
Block a user