Skip to content

Commit 7eed90d

Browse files
author
Robert Holt
authored
Python: Eliminate runtime package and use sqlalchemy (#939)
* Manually edit python example code The generated example code is edited just to demostrate potential improvements to the python codegen. The main changes are using sqlalchemy as the exection engine and using stdlib dataclasses instead of pydantic. This also eliminates the runtime package. * Remove leftover debug code in python tests * Python: implement new codegen Generated python code now only depends on sqlalchemy. Query functions are now inside classes as well.
1 parent c6ff260 commit 7eed90d

File tree

20 files changed

+726
-768
lines changed

20 files changed

+726
-768
lines changed

examples/python/requirements.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@ pytest~=6.2.2
22
pytest-asyncio~=0.14.0
33
psycopg2-binary~=2.8.6
44
asyncpg~=0.21.0
5-
pydantic~=1.7.3
6-
sqlc-python-runtime~=1.0.0
5+
sqlalchemy==1.4.0

examples/python/sqlc.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"gen": {
99
"python": {
1010
"out": "src/authors",
11-
"package": "authors"
11+
"package": "authors",
12+
"emit_sync_querier": true,
13+
"emit_async_querier": true
1214
}
1315
}
1416
},
@@ -19,7 +21,8 @@
1921
"gen": {
2022
"python": {
2123
"out": "src/booktest",
22-
"package": "booktest"
24+
"package": "booktest",
25+
"emit_async_querier": true
2326
}
2427
}
2528
},
@@ -30,7 +33,8 @@
3033
"gen": {
3134
"python": {
3235
"out": "src/jets",
33-
"package": "jets"
36+
"package": "jets",
37+
"emit_async_querier": true
3438
}
3539
}
3640
},
@@ -41,7 +45,8 @@
4145
"gen": {
4246
"python": {
4347
"out": "src/ondeck",
44-
"package": "ondeck"
48+
"package": "ondeck",
49+
"emit_async_querier": true
4550
}
4651
}
4752
}

examples/python/src/authors/models.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# Code generated by sqlc. DO NOT EDIT.
22
from typing import Optional
33

4-
import pydantic
4+
import dataclasses
55

66

7-
# Enums
87

9-
# Models
10-
class Author(pydantic.BaseModel):
8+
9+
@dataclasses.dataclass()
10+
class Author:
1111
id: int
1212
name: str
1313
bio: Optional[str]

examples/python/src/authors/query.py

Lines changed: 83 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,111 @@
1+
12
# Code generated by sqlc. DO NOT EDIT.
2-
from typing import AsyncIterator, Awaitable, Iterator, Optional, overload
3+
from typing import AsyncIterator, Iterator, Optional
34

4-
import sqlc_runtime as sqlc
5+
import sqlalchemy
6+
import sqlalchemy.ext.asyncio
57

68
from authors import models
79

810

9-
CREATE_AUTHOR = """-- name: create_author :one
11+
CREATE_AUTHOR = """-- name: create_author \\:one
1012
INSERT INTO authors (
1113
name, bio
1214
) VALUES (
13-
$1, $2
15+
:p1, :p2
1416
)
1517
RETURNING id, name, bio
1618
"""
1719

1820

19-
DELETE_AUTHOR = """-- name: delete_author :exec
21+
DELETE_AUTHOR = """-- name: delete_author \\:exec
2022
DELETE FROM authors
21-
WHERE id = $1
23+
WHERE id = :p1
2224
"""
2325

2426

25-
GET_AUTHOR = """-- name: get_author :one
27+
GET_AUTHOR = """-- name: get_author \\:one
2628
SELECT id, name, bio FROM authors
27-
WHERE id = $1 LIMIT 1
29+
WHERE id = :p1 LIMIT 1
2830
"""
2931

3032

31-
LIST_AUTHORS = """-- name: list_authors :many
33+
LIST_AUTHORS = """-- name: list_authors \\:many
3234
SELECT id, name, bio FROM authors
3335
ORDER BY name
3436
"""
3537

3638

37-
@overload
38-
def create_author(conn: sqlc.Connection, name: str, bio: Optional[str]) -> Optional[models.Author]:
39-
pass
40-
41-
42-
@overload
43-
def create_author(conn: sqlc.AsyncConnection, name: str, bio: Optional[str]) -> Awaitable[Optional[models.Author]]:
44-
pass
45-
46-
47-
def create_author(conn: sqlc.GenericConnection, name: str, bio: Optional[str]) -> sqlc.ReturnType[Optional[models.Author]]:
48-
return conn.execute_one_model(models.Author, CREATE_AUTHOR, name, bio)
49-
50-
51-
@overload
52-
def delete_author(conn: sqlc.Connection, id: int) -> None:
53-
pass
54-
55-
56-
@overload
57-
def delete_author(conn: sqlc.AsyncConnection, id: int) -> Awaitable[None]:
58-
pass
59-
60-
61-
def delete_author(conn: sqlc.GenericConnection, id: int) -> sqlc.ReturnType[None]:
62-
return conn.execute_none(DELETE_AUTHOR, id)
63-
64-
65-
@overload
66-
def get_author(conn: sqlc.Connection, id: int) -> Optional[models.Author]:
67-
pass
68-
69-
70-
@overload
71-
def get_author(conn: sqlc.AsyncConnection, id: int) -> Awaitable[Optional[models.Author]]:
72-
pass
73-
74-
75-
def get_author(conn: sqlc.GenericConnection, id: int) -> sqlc.ReturnType[Optional[models.Author]]:
76-
return conn.execute_one_model(models.Author, GET_AUTHOR, id)
77-
78-
79-
@overload
80-
def list_authors(conn: sqlc.Connection) -> Iterator[models.Author]:
81-
pass
82-
83-
84-
@overload
85-
def list_authors(conn: sqlc.AsyncConnection) -> AsyncIterator[models.Author]:
86-
pass
87-
88-
89-
def list_authors(conn: sqlc.GenericConnection) -> sqlc.IteratorReturn[models.Author]:
90-
return conn.execute_many_model(models.Author, LIST_AUTHORS)
91-
39+
class Querier:
40+
def __init__(self, conn: sqlalchemy.engine.Connection):
41+
self._conn = conn
42+
43+
def create_author(self, *, name: str, bio: Optional[str]) -> Optional[models.Author]:
44+
row = self._conn.execute(sqlalchemy.text(CREATE_AUTHOR), {"p1": name, "p2": bio}).first()
45+
if row is None:
46+
return None
47+
return models.Author(
48+
id=row[0],
49+
name=row[1],
50+
bio=row[2],
51+
)
52+
53+
def delete_author(self, *, id: int) -> None:
54+
self._conn.execute(sqlalchemy.text(DELETE_AUTHOR), {"p1": id})
55+
56+
def get_author(self, *, id: int) -> Optional[models.Author]:
57+
row = self._conn.execute(sqlalchemy.text(GET_AUTHOR), {"p1": id}).first()
58+
if row is None:
59+
return None
60+
return models.Author(
61+
id=row[0],
62+
name=row[1],
63+
bio=row[2],
64+
)
65+
66+
def list_authors(self) -> Iterator[models.Author]:
67+
result = self._conn.execute(sqlalchemy.text(LIST_AUTHORS))
68+
for row in result:
69+
yield models.Author(
70+
id=row[0],
71+
name=row[1],
72+
bio=row[2],
73+
)
74+
75+
76+
class AsyncQuerier:
77+
def __init__(self, conn: sqlalchemy.ext.asyncio.AsyncConnection):
78+
self._conn = conn
79+
80+
async def create_author(self, *, name: str, bio: Optional[str]) -> Optional[models.Author]:
81+
row = (await self._conn.execute(sqlalchemy.text(CREATE_AUTHOR), {"p1": name, "p2": bio})).first()
82+
if row is None:
83+
return None
84+
return models.Author(
85+
id=row[0],
86+
name=row[1],
87+
bio=row[2],
88+
)
89+
90+
async def delete_author(self, *, id: int) -> None:
91+
await self._conn.execute(sqlalchemy.text(DELETE_AUTHOR), {"p1": id})
92+
93+
async def get_author(self, *, id: int) -> Optional[models.Author]:
94+
row = (await self._conn.execute(sqlalchemy.text(GET_AUTHOR), {"p1": id})).first()
95+
if row is None:
96+
return None
97+
return models.Author(
98+
id=row[0],
99+
name=row[1],
100+
bio=row[2],
101+
)
102+
103+
async def list_authors(self) -> AsyncIterator[models.Author]:
104+
result = await self._conn.stream(sqlalchemy.text(LIST_AUTHORS))
105+
async for row in result:
106+
yield models.Author(
107+
id=row[0],
108+
name=row[1],
109+
bio=row[2],
110+
)
92111

examples/python/src/booktest/models.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,24 @@
33
import datetime
44
import enum
55

6-
import pydantic
6+
import dataclasses
7+
78

89

9-
# Enums
1010
class BookType(str, enum.Enum):
1111
FICTION = "FICTION"
1212
NONFICTION = "NONFICTION"
1313

1414

15-
# Models
16-
class Author(pydantic.BaseModel):
15+
@dataclasses.dataclass()
16+
class Author:
1717
author_id: int
1818
name: str
1919

2020

21-
class Book(pydantic.BaseModel):
21+
22+
@dataclasses.dataclass()
23+
class Book:
2224
book_id: int
2325
author_id: int
2426
isbn: str

0 commit comments

Comments
 (0)