Skip to content

Commit e7bf124

Browse files
authored
Merge pull request piccolo-orm#110 from piccolo-orm/blacksheep_support
BlackSheep support
2 parents 03b2f27 + d68401e commit e7bf124

20 files changed

+149
-8
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Let Piccolo scaffold you an ASGI web app, using Piccolo as the ORM:
7878
piccolo asgi new
7979
```
8080

81-
[Starlette](https://www.starlette.io/) and [FastAPI](https://fastapi.tiangolo.com/) are currently supported.
81+
[Starlette](https://www.starlette.io/), [FastAPI](https://fastapi.tiangolo.com/), and [BlackSheep](https://www.neoteroi.dev/blacksheep/) are currently supported.
8282

8383
## Are you a Django user?
8484

docs/src/piccolo/asgi/index.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ will ask for your preferences on which libraries to use.
1818
Routing frameworks
1919
******************
2020

21-
Currently, `Starlette <https://www.starlette.io/>`_ and `FastAPI <https://fastapi.tiangolo.com/>`_
22-
are supported.
21+
Currently, `Starlette <https://www.starlette.io/>`_, `FastAPI <https://fastapi.tiangolo.com/>`_,
22+
and `BlackSheep <https://www.neoteroi.dev/blacksheep/>`_ are supported.
2323

2424
Other great ASGI routing frameworks exist, and may be supported in the future
2525
(`Quart <https://pgjones.gitlab.io/quart/>`_ ,
@@ -29,7 +29,7 @@ Other great ASGI routing frameworks exist, and may be supported in the future
2929
Which to use?
3030
=============
3131

32-
Both are great choices. FastAPI is built on top of Starlette, so they're
32+
All are great choices. FastAPI is built on top of Starlette, so they're
3333
very similar. FastAPI is useful if you want to document a REST API.
3434

3535
Web servers

piccolo/apps/asgi/commands/new.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
from jinja2 import Environment, FileSystemLoader
99

1010

11-
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "templates/starlette/")
11+
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "templates/app/")
1212
SERVERS = ["uvicorn", "Hypercorn"]
13-
ROUTERS = ["starlette", "fastapi"]
13+
ROUTERS = ["starlette", "fastapi", "blacksheep"]
1414

1515

1616
def print_instruction(message: str):
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import typing as t
2+
3+
from piccolo_api.crud.serializers import create_pydantic_model
4+
from piccolo.engine import engine_finder
5+
6+
from blacksheep.server import Application
7+
from blacksheep.server.bindings import FromJSON
8+
from blacksheep.server.responses import json
9+
from blacksheep.server.openapi.v3 import OpenAPIHandler
10+
from openapidocs.v3 import Info
11+
12+
from home.endpoints import home
13+
from home.tables import Task
14+
15+
16+
app = Application()
17+
18+
19+
docs = OpenAPIHandler(info=Info(title="Example API", version="0.0.1"))
20+
docs.bind_app(app)
21+
22+
23+
app.serve_files("static", root_path="/static")
24+
25+
26+
app.router.add_get("/", home)
27+
28+
29+
TaskModelIn = create_pydantic_model(table=Task, model_name="TaskModelIn")
30+
TaskModelOut = create_pydantic_model(
31+
table=Task, include_default_columns=True, model_name="TaskModelOut"
32+
)
33+
TaskModelPartial = create_pydantic_model(
34+
table=Task, model_name="TaskModelPartial", all_optional=True
35+
)
36+
37+
38+
@app.router.get("/tasks/")
39+
async def tasks() -> t.List[TaskModelOut]:
40+
return await Task.select().order_by(Task.id).run()
41+
42+
43+
@app.router.post("/tasks/")
44+
async def create_task(task: FromJSON[TaskModelIn]) -> TaskModelOut:
45+
task = Task(**task.value.__dict__)
46+
await task.save().run()
47+
return TaskModelOut(**task.__dict__)
48+
49+
50+
@app.router.put("/tasks/{task_id}/")
51+
async def put_task(
52+
task_id: int, task: FromJSON[TaskModelIn]
53+
) -> TaskModelOut:
54+
_task = await Task.objects().where(Task.id == task_id).first().run()
55+
if not _task:
56+
return json({}, status=404)
57+
58+
for key, value in task.value.__dict__.items():
59+
setattr(_task, key, value)
60+
61+
await _task.save().run()
62+
63+
return TaskModelOut(**_task.__dict__)
64+
65+
66+
@app.router.patch("/tasks/{task_id}/")
67+
async def patch_task(
68+
task_id: int, task: FromJSON[TaskModelPartial]
69+
) -> TaskModelOut:
70+
_task = await Task.objects().where(Task.id == task_id).first().run()
71+
if not _task:
72+
return json({}, status=404)
73+
74+
for key, value in task.value.__dict__.items():
75+
if value is not None:
76+
setattr(_task, key, value)
77+
78+
await _task.save().run()
79+
80+
return TaskModelOut(**_task.__dict__)
81+
82+
83+
@app.router.delete("/tasks/{task_id}/")
84+
async def delete_task(task_id: int):
85+
task = await Task.objects().where(Task.id == task_id).first().run()
86+
if not task:
87+
return json({}, status=404)
88+
89+
await task.remove().run()
90+
91+
return json({})
92+
93+
94+
async def open_database_connection_pool(application):
95+
try:
96+
engine = engine_finder()
97+
await engine.start_connection_pool()
98+
except Exception:
99+
print("Unable to connect to the database")
100+
101+
102+
async def close_database_connection_pool(application):
103+
try:
104+
engine = engine_finder()
105+
await engine.close_connection_pool()
106+
except Exception:
107+
print("Unable to connect to the database")
108+
109+
110+
app.on_start += open_database_connection_pool
111+
app.on_stop += close_database_connection_pool

piccolo/apps/asgi/commands/templates/starlette/_fastapi_app.py.jinja renamed to piccolo/apps/asgi/commands/templates/app/_fastapi_app.py.jinja

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ from starlette.staticfiles import StaticFiles
1010

1111
from home.endpoints import HomeEndpoint
1212
from home.tables import Task
13-
from home.piccolo_app import APP_CONFIG
1413

1514

1615
app = FastAPI(

piccolo/apps/asgi/commands/templates/starlette/_starlette_app.py.jinja renamed to piccolo/apps/asgi/commands/templates/app/_starlette_app.py.jinja

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ from starlette.applications import Starlette
66
from starlette.staticfiles import StaticFiles
77

88
from home.endpoints import HomeEndpoint
9-
from home.piccolo_app import APP_CONFIG
109
from home.tables import Task
1110

1211

piccolo/apps/asgi/commands/templates/starlette/app.py.jinja renamed to piccolo/apps/asgi/commands/templates/app/app.py.jinja

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
{% include '_fastapi_app.py.jinja' %}
33
{% elif router == 'starlette' %}
44
{% include '_starlette_app.py.jinja' %}
5+
{% elif router == 'blacksheep' %}
6+
{% include '_blacksheep_app.py.jinja' %}
57
{% endif %}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import os
2+
3+
from blacksheep.contents import Content
4+
from blacksheep.messages import Response
5+
import jinja2
6+
7+
8+
ENVIRONMENT = jinja2.Environment(
9+
loader=jinja2.FileSystemLoader(
10+
searchpath=os.path.join(os.path.dirname(__file__), "templates")
11+
)
12+
)
13+
14+
15+
def home():
16+
template = ENVIRONMENT.get_template("home.html.jinja")
17+
content = template.render(title="Piccolo + ASGI",)
18+
return Response(
19+
200,
20+
content=Content(b"text/html", bytes(content, encoding='utf8'))
21+
)

piccolo/apps/asgi/commands/templates/starlette/home/endpoints.py.jinja renamed to piccolo/apps/asgi/commands/templates/app/home/_starlette_endpoints.py.jinja

File renamed without changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{% if router in ['fastapi', 'starlette'] %}
2+
{% include '_starlette_endpoints.py.jinja' %}
3+
{% elif router == 'blacksheep' %}
4+
{% include '_blacksheep_endpoints.py.jinja' %}
5+
{% endif %}

0 commit comments

Comments
 (0)