Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
CameronSima committed Aug 29, 2024
1 parent e826314 commit 92445c5
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 27 deletions.
64 changes: 60 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ Middleware functions are called in the order they are added to the stack, and pa

The first handler in the stack to return something other than a `Request` object (including `Exception`) will short-circuit the stack and return the response.

Middleware can be can be applied at the application, router, or individual route level.

```python
from zipline import ZipLine, middleware
from zipline import ZipLine, Router, middleware


# middleware functions
Expand All @@ -60,21 +62,31 @@ def auth_guard(request, ctx):
if not ctx.get("is_authed"):
raise Exception("Unauthorized")

def is_user_guard(request, ctx):
if ctx.get("user_id") != request.path_params.get("id"):
raise Exception("Forbidden")


app = ZipLine()


# apply middleware to all routes
app.middleware(auth_middleware)
app.middleware([auth_middleware])

user_router = Router("/user")

# apply middleware at the router-level
user_router.middleware([auth_guard])

@app.get("/profile")
@middleware([auth_guard])
@middleware([is_user_guard]) # apply middleware to one router
async def user_profile(request):
return "Hello, World!"
```

## Dependency Injection

Like with middeleware, ZipLine supports dependency injection at the route, router, or application level. Dependencies are passed to the handler function as keyword arguments.
Like with middeleware, ZipLine supports dependency injection at the route, router, or application level. In addition, dependencies can be injected into other dependencies. Dependencies are passed to the handler function as keyword arguments.

```python
from zipline import ZipLine, inject
Expand Down Expand Up @@ -102,6 +114,50 @@ async def home(request, user_service: UserService, logger: LoggingService):
return user_service.get_user()
```

Services can be any class, but Zipline includes a special `Service` class. Classes that inherit from `Service` have the ability to access all other services in their scope.

```python
from zipline import ZipLine, Service, inject

class LoggingService(Service):
def __init__(self):
self.name "logger"

def error(self, message):
print(f"Error! {message}")

class DBService(Service):
def __init__(self, logger: LoggingService):
self.name = "db_service"

def get_connection(self):
try:
return db.connect()
except Exception as e:
self.logger.error(e)

class UserService(Service):
def __init__(self, db_service: DBService, logger: LoggingService):
self.name = "user_service"

def get_user(id: str):
conn = self.db_service.get_connection()
try:
return conn.query("SELECT * FROM users WHERE id = ?", id)
except Exception as e:
self.logger.error(f"User {id} not found")


app = ZipLine()
# inject all services; order doesn't matter
app.inject([LoggingService, DBService, UserService])

@app.route("/user/:id")
async def get_user(request, user_service: UserService):
user_id = request.path_params.get("id")
return user_service.get_user(user_id)
```

## Routing

Like Express.js, ZipLine supports multiple, nested routers.
Expand Down
43 changes: 22 additions & 21 deletions src/ziplineio/app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from ast import Not
import inspect
from typing import Any, Callable, List, Tuple, Type

Expand Down Expand Up @@ -77,32 +76,34 @@ def middleware(self, middlewares: List[Callable]) -> None:
def static(self, path: str, path_prefix: str = "/static") -> None:
self.middleware([staticfiles(path, path_prefix)])

def __call__(self, *args: Any, **kwds: Any) -> Any:
async def uvicorn_handler(scope: dict, receive: Any, send: Any) -> None:
if scope["type"] == "http":
req = parse_scope(scope)
handler, path_params = self.get_handler(req.method, req.path)
req.path_params = path_params
async def _get_and_call_handler(
self, method: str, path: str, req: Request
) -> Callable:
handler, path_params = self.get_handler(method, path)
req.path_params = path_params

if handler is None:
# try running through middlewares
req, ctx, res = await run_middleware_stack(
self._router._router_level_middelwares, req, **{}
)
if handler is None:
# try running through middlewares
req, ctx, res = await run_middleware_stack(
self._router._router_level_middelwares, req, **{}
)

if res is None:
response = NotFoundHttpException()
else:
response = res
if res is None:
response = NotFoundHttpException()
else:
response = res

else:
response = await call_handler(handler, req)
else:
response = await call_handler(handler, req)
return response

def __call__(self, *args: Any, **kwds: Any) -> Any:
async def uvicorn_handler(scope: dict, receive: Any, send: Any) -> None:
if scope["type"] == "http":
req = parse_scope(scope)
response = await self._get_and_call_handler(req.method, req.path, req)
raw_response = format_response(response, settings.DEFAULT_HEADERS)

print("SENDING RESPONSE")
print(raw_response)

await send(
{
"type": "http.response.start",
Expand Down
2 changes: 0 additions & 2 deletions test/test_dependency_injection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from operator import call
from os import name
import unittest

from ziplineio.app import App
Expand Down

0 comments on commit 92445c5

Please sign in to comment.