kopia lustrzana https://github.com/cirospaciari/socketify.py
decorator router fully implemented
rodzic
e901433da0
commit
e5bf0b201f
|
@ -184,7 +184,7 @@ def route_handler(res, req):
|
||||||
|
|
||||||
|
|
||||||
## Using ujson, orjson or any custom JSON serializer
|
## Using ujson, orjson or any custom JSON serializer
|
||||||
socketify by default uses built in `json` module with have great performance on PyPy, but if you wanna to use another module instead of the default you can just register using `app.json_serializer(module)`
|
socketify by default uses built-in `json` module with has great performance on PyPy, but if you wanna use another module instead of the default you can just register using `app.json_serializer(module)`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from socketify import App
|
from socketify import App
|
||||||
|
|
|
@ -57,6 +57,13 @@ auth_router.get("/another", middleware(another_middie, home))
|
||||||
other_router = MiddlewareRouter(app, auth, another_middie)
|
other_router = MiddlewareRouter(app, auth, another_middie)
|
||||||
other_router.get("/another_way", home)
|
other_router.get("/another_way", home)
|
||||||
|
|
||||||
|
# you can also use middlewares when using the decorator router
|
||||||
|
private = app.router("/api", auth, another_middie)
|
||||||
|
|
||||||
|
# will serve in /api/users and use auth_middleware
|
||||||
|
@private.get("/users")
|
||||||
|
def get_users(res, req, user):
|
||||||
|
res.cork_end("Hello private API!")
|
||||||
|
|
||||||
app.listen(
|
app.listen(
|
||||||
3000,
|
3000,
|
||||||
|
|
|
@ -24,6 +24,34 @@ app.post("/", home)
|
||||||
```
|
```
|
||||||
> Whenever your callback is a coroutine, such as the async/await, automatic corking can only happen in the very first portion of the coroutine (consider await a separator which essentially cuts the coroutine into smaller segments). Only the first "segment" of the coroutine will be called from socketify, the following async segments will be called by the asyncio event loop at a later point in time and will thus not be under our control with default corking enabled, HttpRequest object being stack-allocated and only valid in one single callback invocation so only valid in the first "segment" before the first await. If you just want to preserve headers, url, method, cookies and query string you can use `req.preserve()` to copy all data and keep it in the request object, but will be some performance penalty. Take a look in [Corking](corking.md) for get a more in deph information
|
> Whenever your callback is a coroutine, such as the async/await, automatic corking can only happen in the very first portion of the coroutine (consider await a separator which essentially cuts the coroutine into smaller segments). Only the first "segment" of the coroutine will be called from socketify, the following async segments will be called by the asyncio event loop at a later point in time and will thus not be under our control with default corking enabled, HttpRequest object being stack-allocated and only valid in one single callback invocation so only valid in the first "segment" before the first await. If you just want to preserve headers, url, method, cookies and query string you can use `req.preserve()` to copy all data and keep it in the request object, but will be some performance penalty. Take a look in [Corking](corking.md) for get a more in deph information
|
||||||
|
|
||||||
|
|
||||||
|
You can also use the `Decorator router` as the name suggests this router allows to use of decorators for routing and also comes up with a prefix option, and middleware support.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from socketify import App
|
||||||
|
|
||||||
|
app = App()
|
||||||
|
router = app.router()
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
def home(res, req):
|
||||||
|
res.end("Hello World!")
|
||||||
|
|
||||||
|
api = app.router(prefix="/api")
|
||||||
|
|
||||||
|
# will serve in /api/hello
|
||||||
|
@api.get("/hello")
|
||||||
|
def hello(res, req):
|
||||||
|
res.end("Hello API!")
|
||||||
|
|
||||||
|
private = app.router("/api", auth_middleware)
|
||||||
|
|
||||||
|
# will serve in /api/users and use auth_middleware
|
||||||
|
@private.get("/users")
|
||||||
|
def get_users(res, req, auth):
|
||||||
|
res.end("Hello private API!")
|
||||||
|
```
|
||||||
|
|
||||||
## Pattern matching
|
## Pattern matching
|
||||||
Routes are matched in order of specificity, not by the order you register them:
|
Routes are matched in order of specificity, not by the order you register them:
|
||||||
|
|
||||||
|
|
|
@ -123,8 +123,69 @@ def static_route(app, route, directory):
|
||||||
route = route[:-1]
|
route = route[:-1]
|
||||||
app.get("%s/*" % route, route_handler)
|
app.get("%s/*" % route, route_handler)
|
||||||
|
|
||||||
|
|
||||||
def middleware(*functions):
|
def middleware(*functions):
|
||||||
|
syncs = []
|
||||||
|
asyncs = []
|
||||||
|
for function in functions:
|
||||||
|
# all is async after the first async
|
||||||
|
if inspect.iscoroutinefunction(function) or len(asyncs) > 0:
|
||||||
|
asyncs.append(function)
|
||||||
|
else:
|
||||||
|
syncs.append(function)
|
||||||
|
if len(asyncs) == 0: # pure sync
|
||||||
|
return sync_middleware(*functions)
|
||||||
|
if len(syncs) == 0: # pure async
|
||||||
|
return async_middleware(*functions)
|
||||||
|
|
||||||
|
# we use Optional data=None at the end so you can use and middleware inside a middleware
|
||||||
|
def optimized_middleware_route(res, req, data=None):
|
||||||
|
# cicle to all middlewares
|
||||||
|
for function in syncs:
|
||||||
|
# call middlewares
|
||||||
|
data = function(res, req, data)
|
||||||
|
# stops if returns Falsy
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
async def wrapper(res, req, data):
|
||||||
|
# cicle to all middlewares
|
||||||
|
for function in asyncs:
|
||||||
|
# detect if is coroutine or not
|
||||||
|
if inspect.iscoroutinefunction(function):
|
||||||
|
data = await function(res, req, data)
|
||||||
|
else:
|
||||||
|
# call sync middleware
|
||||||
|
data = function(res, req, data)
|
||||||
|
# stops if returns Falsy
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
return data
|
||||||
|
|
||||||
|
# in async query string, arguments and headers are only valid until the first await
|
||||||
|
# preserve queries, headers, parameters, url, full_url and method
|
||||||
|
req.preserve()
|
||||||
|
|
||||||
|
# go async
|
||||||
|
res.run_async(wrapper(res, req, data))
|
||||||
|
|
||||||
|
return optimized_middleware_route
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def sync_middleware(*functions):
|
||||||
|
# we use Optional data=None at the end so you can use and middleware inside a middleware
|
||||||
|
def middleware_route(res, req, data=None):
|
||||||
|
# cicle to all middlewares
|
||||||
|
for function in functions:
|
||||||
|
# call middlewares
|
||||||
|
data = function(res, req, data)
|
||||||
|
# stops if returns Falsy
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
return data
|
||||||
|
|
||||||
|
return middleware_route
|
||||||
|
|
||||||
|
def async_middleware(*functions):
|
||||||
# we use Optional data=None at the end so you can use and middleware inside a middleware
|
# we use Optional data=None at the end so you can use and middleware inside a middleware
|
||||||
async def middleware_route(res, req, data=None):
|
async def middleware_route(res, req, data=None):
|
||||||
some_async_as_run = False
|
some_async_as_run = False
|
||||||
|
@ -149,67 +210,195 @@ def middleware(*functions):
|
||||||
return middleware_route
|
return middleware_route
|
||||||
|
|
||||||
|
|
||||||
|
class DecoratorRouter:
|
||||||
|
def __init__(self, app, prefix: str="", *middlewares):
|
||||||
|
self.app = app
|
||||||
|
self.middlewares = middlewares
|
||||||
|
self.prefix = prefix
|
||||||
|
|
||||||
|
def get(self, path):
|
||||||
|
path = f"{self.prefix}{path}"
|
||||||
|
|
||||||
|
def decorator(handler):
|
||||||
|
if len(self.middlewares) > 0:
|
||||||
|
middies = list(*self.middlewares)
|
||||||
|
middies.append(handler)
|
||||||
|
self.app.get(path, middleware(*middies))
|
||||||
|
else:
|
||||||
|
self.app.get(path, handler)
|
||||||
|
return handler
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def post(self, path):
|
||||||
|
path = f"{self.prefix}{path}"
|
||||||
|
def decorator(handler):
|
||||||
|
if len(self.middlewares) > 0:
|
||||||
|
middies = list(*self.middlewares)
|
||||||
|
middies.append(handler)
|
||||||
|
self.app.post(path, middleware(*middies))
|
||||||
|
else:
|
||||||
|
self.app.post(path, handler)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def options(self, path):
|
||||||
|
path = f"{self.prefix}{path}"
|
||||||
|
def decorator(handler):
|
||||||
|
if len(self.middlewares) > 0:
|
||||||
|
middies = list(*self.middlewares)
|
||||||
|
middies.append(handler)
|
||||||
|
self.app.options(path, middleware(*middies))
|
||||||
|
else:
|
||||||
|
self.app.options(path, handler)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def delete(self, path):
|
||||||
|
path = f"{self.prefix}{path}"
|
||||||
|
def decorator(handler):
|
||||||
|
if len(self.middlewares) > 0:
|
||||||
|
middies = list(*self.middlewares)
|
||||||
|
middies.append(handler)
|
||||||
|
self.app.delete(path, middleware(*middies))
|
||||||
|
else:
|
||||||
|
self.app.delete(path, handler)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def patch(self, path):
|
||||||
|
path = f"{self.prefix}{path}"
|
||||||
|
def decorator(handler):
|
||||||
|
if len(self.middlewares) > 0:
|
||||||
|
middies = list(*self.middlewares)
|
||||||
|
middies.append(handler)
|
||||||
|
self.app.patch(path, middleware(*middies))
|
||||||
|
else:
|
||||||
|
self.app.patch(path, handler)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def put(self, path: str):
|
||||||
|
path = f"{self.prefix}{path}"
|
||||||
|
def decorator(handler):
|
||||||
|
if len(self.middlewares) > 0:
|
||||||
|
middies = list(*self.middlewares)
|
||||||
|
middies.append(handler)
|
||||||
|
self.app.put(path, middleware(*middies))
|
||||||
|
else:
|
||||||
|
self.app.put(path, handler)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def head(self, path):
|
||||||
|
path = f"{self.prefix}{path}"
|
||||||
|
def decorator(handler):
|
||||||
|
if len(self.middlewares) > 0:
|
||||||
|
middies = list(*self.middlewares)
|
||||||
|
middies.append(handler)
|
||||||
|
self.app.head(path, middleware(*middies))
|
||||||
|
else:
|
||||||
|
self.app.head(path, handler)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def connect(self, path):
|
||||||
|
path = f"{self.prefix}{path}"
|
||||||
|
def decorator(handler):
|
||||||
|
if len(self.middlewares) > 0:
|
||||||
|
middies = list(*self.middlewares)
|
||||||
|
middies.append(handler)
|
||||||
|
self.app.connect(path, middleware(*middies))
|
||||||
|
else:
|
||||||
|
self.app.connect(path, handler)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def trace(self, path):
|
||||||
|
path = f"{self.prefix}{path}"
|
||||||
|
def decorator(handler):
|
||||||
|
if len(self.middlewares) > 0:
|
||||||
|
middies = list(*self.middlewares)
|
||||||
|
middies.append(handler)
|
||||||
|
self.app.trace(path, middleware(*middies))
|
||||||
|
else:
|
||||||
|
self.app.trace(path, handler)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
def any(self, path):
|
||||||
|
path = f"{self.prefix}{path}"
|
||||||
|
def decorator(handler):
|
||||||
|
if len(self.middlewares) > 0:
|
||||||
|
middies = list(*self.middlewares)
|
||||||
|
middies.append(handler)
|
||||||
|
self.app.any(path, middleware(*middies))
|
||||||
|
else:
|
||||||
|
self.app.any(path, handler)
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
class MiddlewareRouter:
|
class MiddlewareRouter:
|
||||||
def __init__(self, app, *middlewares):
|
def __init__(self, app, *middlewares):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.middlewares = middlewares
|
self.middlewares = middlewares
|
||||||
|
|
||||||
def get(self, path, handler):
|
def get(self, path, handler):
|
||||||
middies = list(self.middlewares)
|
middies = list(*self.middlewares)
|
||||||
middies.append(handler)
|
middies.append(handler)
|
||||||
self.app.get(path, middleware(*middies))
|
self.app.get(path, middleware(*middies))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def post(self, path, handler):
|
def post(self, path, handler):
|
||||||
middies = list(self.middlewares)
|
middies = list(*self.middlewares)
|
||||||
middies.append(handler)
|
middies.append(handler)
|
||||||
self.app.post(path, middleware(*middies))
|
self.app.post(path, middleware(*middies))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def options(self, path, handler):
|
def options(self, path, handler):
|
||||||
middies = list(self.middlewares)
|
middies = list(*self.middlewares)
|
||||||
middies.append(handler)
|
middies.append(handler)
|
||||||
self.app.options(path, middleware(*middies))
|
self.app.options(path, middleware(*middies))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def delete(self, path, handler):
|
def delete(self, path, handler):
|
||||||
middies = list(self.middlewares)
|
middies = list(*self.middlewares)
|
||||||
middies.append(handler)
|
middies.append(handler)
|
||||||
self.app.delete(path, middleware(*middies))
|
self.app.delete(path, middleware(*middies))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def patch(self, path, handler):
|
def patch(self, path, handler):
|
||||||
middies = list(self.middlewares)
|
middies = list(*self.middlewares)
|
||||||
middies.append(handler)
|
middies.append(handler)
|
||||||
self.app.patch(path, middleware(*middies))
|
self.app.patch(path, middleware(*middies))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def put(self, path, handler):
|
def put(self, path, handler):
|
||||||
middies = list(self.middlewares)
|
middies = list(*self.middlewares)
|
||||||
middies.append(handler)
|
middies.append(handler)
|
||||||
self.app.put(path, middleware(*middies))
|
self.app.put(path, middleware(*middies))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def head(self, path, handler):
|
def head(self, path, handler):
|
||||||
middies = list(self.middlewares)
|
middies = list(*self.middlewares)
|
||||||
middies.append(handler)
|
middies.append(handler)
|
||||||
self.app.head(path, middleware(*middies))
|
self.app.head(path, middleware(*middies))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def connect(self, path, handler):
|
def connect(self, path, handler):
|
||||||
middies = list(self.middlewares)
|
middies = list(*self.middlewares)
|
||||||
middies.append(handler)
|
middies.append(handler)
|
||||||
self.app.connect(path, middleware(*middies))
|
self.app.connect(path, middleware(*middies))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def trace(self, path, handler):
|
def trace(self, path, handler):
|
||||||
middies = list(self.middlewares)
|
middies = list(*self.middlewares)
|
||||||
middies.append(handler)
|
middies.append(handler)
|
||||||
self.app.trace(path, middleware(*middies))
|
self.app.trace(path, middleware(*middies))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def any(self, path, handler):
|
def any(self, path, handler):
|
||||||
middies = list(self.middlewares)
|
middies = list(*self.middlewares)
|
||||||
middies.append(handler)
|
middies.append(handler)
|
||||||
self.app.any(path, middleware(*middies))
|
self.app.any(path, middleware(*middies))
|
||||||
return self
|
return self
|
||||||
|
|
|
@ -17,6 +17,7 @@ from .loop import Loop
|
||||||
from .status_codes import status_codes
|
from .status_codes import status_codes
|
||||||
from .helpers import static_route
|
from .helpers import static_route
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from .helpers import DecoratorRouter
|
||||||
|
|
||||||
mimetypes.init()
|
mimetypes.init()
|
||||||
|
|
||||||
|
@ -2159,6 +2160,9 @@ class App:
|
||||||
self._response_extension = None
|
self._response_extension = None
|
||||||
self._ws_extension = None
|
self._ws_extension = None
|
||||||
|
|
||||||
|
def router(self, prefix: str="", *middlewares):
|
||||||
|
return DecoratorRouter(self, prefix, middlewares)
|
||||||
|
|
||||||
def register(self, extension):
|
def register(self, extension):
|
||||||
if self._request_extension is None:
|
if self._request_extension is None:
|
||||||
self._request_extension = AppExtension()
|
self._request_extension = AppExtension()
|
||||||
|
|
14
src/tests.py
14
src/tests.py
|
@ -25,7 +25,17 @@ def extension(request, response, ws):
|
||||||
# extensions must be registered before routes
|
# extensions must be registered before routes
|
||||||
app.register(extension)
|
app.register(extension)
|
||||||
|
|
||||||
async def home(res, req):
|
def auth_middleware(res, req, data):
|
||||||
|
token = req.get_query("token")
|
||||||
|
print("token?", token)
|
||||||
|
req.token = token
|
||||||
|
return { "name": "Test" } if token else { "name", "Anonymous" }
|
||||||
|
|
||||||
|
router = app.router("", auth_middleware)
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
async def home(res, req, data=None):
|
||||||
|
print(data)
|
||||||
print("token", req.token)
|
print("token", req.token)
|
||||||
cart = await req.get_cart()
|
cart = await req.get_cart()
|
||||||
print("cart", cart)
|
print("cart", cart)
|
||||||
|
@ -34,7 +44,7 @@ async def home(res, req):
|
||||||
print("token", req.token)
|
print("token", req.token)
|
||||||
res.send("Hello World!")
|
res.send("Hello World!")
|
||||||
|
|
||||||
app.get("/", home)
|
|
||||||
app.listen(
|
app.listen(
|
||||||
3000,
|
3000,
|
||||||
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
|
lambda config: print("Listening on port http://localhost:%d now\n" % config.port),
|
||||||
|
|
Ładowanie…
Reference in New Issue