Skip to content

Commit c95b5ef

Browse files
author
Robert Holt
committed
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.
1 parent c6ff260 commit c95b5ef

File tree

6 files changed

+96
-88
lines changed

6 files changed

+96
-88
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.0b3

examples/python/src/authors/models.py

Lines changed: 3 additions & 3 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
3-
4-
import pydantic
3+
import dataclasses
54

65

76
# Enums
87

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

examples/python/src/authors/query.py

Lines changed: 40 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,77 @@
11
# Code generated by sqlc. DO NOT EDIT.
2-
from typing import AsyncIterator, Awaitable, Iterator, Optional, overload
2+
from typing import AsyncIterator, Iterator, Optional
33

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

67
from authors import models
78

89

9-
CREATE_AUTHOR = """-- name: create_author :one
10+
CREATE_AUTHOR = """-- name: create_author \\:one
1011
INSERT INTO authors (
1112
name, bio
1213
) VALUES (
13-
$1, $2
14+
:p1, :p2
1415
)
1516
RETURNING id, name, bio
1617
"""
1718

1819

19-
DELETE_AUTHOR = """-- name: delete_author :exec
20+
DELETE_AUTHOR = """-- name: delete_author \\:exec
2021
DELETE FROM authors
21-
WHERE id = $1
22+
WHERE id = :p1
2223
"""
2324

2425

25-
GET_AUTHOR = """-- name: get_author :one
26+
GET_AUTHOR = """-- name: get_author \\:one
2627
SELECT id, name, bio FROM authors
27-
WHERE id = $1 LIMIT 1
28+
WHERE id = :p1 LIMIT 1
2829
"""
2930

3031

31-
LIST_AUTHORS = """-- name: list_authors :many
32+
LIST_AUTHORS = """-- name: list_authors \\:many
3233
SELECT id, name, bio FROM authors
3334
ORDER BY name
3435
"""
3536

3637

37-
@overload
38-
def create_author(conn: sqlc.Connection, name: str, bio: Optional[str]) -> Optional[models.Author]:
39-
pass
38+
class Query:
39+
def __init__(self, conn: sqlalchemy.engine.Connection):
40+
self._conn = conn
4041

42+
def create_author(self, name: str, bio: Optional[str]) -> Optional[models.Author]:
43+
result = self._conn.execute(sqlalchemy.text(CREATE_AUTHOR), {"p1": name, "p2": bio})
44+
return models.Author(**dict(result.first()))
4145

42-
@overload
43-
def create_author(conn: sqlc.AsyncConnection, name: str, bio: Optional[str]) -> Awaitable[Optional[models.Author]]:
44-
pass
46+
def delete_author(self, id: int) -> None:
47+
self._conn.execute(sqlalchemy.text(DELETE_AUTHOR), {"p1": id})
4548

49+
def get_author(self, id: int) -> Optional[models.Author]:
50+
result = self._conn.execute(sqlalchemy.text(GET_AUTHOR), {"p1": id})
51+
return models.Author(**dict(result.first()))
4652

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)
53+
def list_authors(self) -> Iterator[models.Author]:
54+
result = self._conn.execute(sqlalchemy.text(LIST_AUTHORS))
55+
for row in result:
56+
yield models.Author(**dict(row))
4957

5058

51-
@overload
52-
def delete_author(conn: sqlc.Connection, id: int) -> None:
53-
pass
59+
class AsyncQuery:
60+
def __init__(self, conn: sqlalchemy.ext.asyncio.AsyncConnection):
61+
self._conn = conn
5462

63+
async def create_author(self, name: str, bio: Optional[str]) -> Optional[models.Author]:
64+
result = await self._conn.execute(sqlalchemy.text(CREATE_AUTHOR), {"p1": name, "p2": bio})
65+
return models.Author(**dict(result.first()))
5566

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)
67+
async def delete_author(self, id: int) -> None:
68+
await self._conn.execute(sqlalchemy.text(DELETE_AUTHOR), {"p1": id})
9169

70+
async def get_author(self, id: int) -> Optional[models.Author]:
71+
result = await self._conn.execute(sqlalchemy.text(GET_AUTHOR), {"p1": id})
72+
return models.Author(**dict(result.first()))
9273

74+
async def list_authors(self) -> AsyncIterator[models.Author]:
75+
result = await self._conn.stream(sqlalchemy.text(LIST_AUTHORS))
76+
async for row in result:
77+
yield models.Author(**dict(row))

examples/python/src/dbtest/migrations.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,26 @@
11
import os
22
from typing import List
33

4-
import asyncpg
5-
import psycopg2.extensions
4+
import sqlalchemy
5+
import sqlalchemy.ext.asyncio
66

77

8-
def apply_migrations(db: psycopg2.extensions.connection, paths: List[str]):
8+
def apply_migrations(conn: sqlalchemy.engine.Connection, paths: List[str]):
99
files = _find_sql_files(paths)
1010

1111
for file in files:
1212
with open(file, "r") as fd:
1313
blob = fd.read()
14-
cur = db.cursor()
15-
cur.execute(blob)
16-
cur.close()
17-
db.commit()
14+
conn.execute(blob)
1815

1916

20-
async def apply_migrations_async(db: asyncpg.Connection, paths: List[str]):
17+
async def apply_migrations_async(conn: sqlalchemy.ext.asyncio.AsyncConnection, paths: List[str]):
2118
files = _find_sql_files(paths)
2219

2320
for file in files:
2421
with open(file, "r") as fd:
2522
blob = fd.read()
26-
await db.execute(blob)
23+
await conn.execute(sqlalchemy.text(blob))
2724

2825

2926
def _find_sql_files(paths: List[str]) -> List[str]:

examples/python/src/tests/conftest.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import psycopg2
77
import psycopg2.extensions
88
import pytest
9+
import sqlalchemy
10+
import sqlalchemy.ext.asyncio
911

1012

1113
@pytest.fixture(scope="session")
@@ -16,7 +18,34 @@ def postgres_uri() -> str:
1618
pg_password = os.environ.get("PG_PASSWORD", "mysecretpassword")
1719
pg_db = os.environ.get("PG_DATABASE", "dinotest")
1820

19-
return f"postgres://{pg_user}:{pg_password}@{pg_host}:{pg_port}/{pg_db}?sslmode=disable"
21+
return f"postgresql://{pg_user}:{pg_password}@{pg_host}:{pg_port}/{pg_db}"
22+
23+
24+
@pytest.fixture(scope="session")
25+
def sqlalchemy_connection(postgres_uri) -> sqlalchemy.engine.Connection:
26+
schema_name = f"sqltest_{random.randint(0, 1000)}"
27+
engine = sqlalchemy.create_engine(postgres_uri)
28+
with engine.connect() as conn:
29+
conn.execute(f"CREATE SCHEMA {schema_name}")
30+
conn.execute(f"SET search_path TO {schema_name}")
31+
yield conn
32+
conn.execute(f"DROP SCHEMA {schema_name} CASCADE")
33+
conn.execute("SET search_path TO public")
34+
35+
36+
@pytest.fixture(scope="session")
37+
async def async_sqlalchemy_connection(postgres_uri) -> sqlalchemy.ext.asyncio.AsyncConnection:
38+
postgres_uri = postgres_uri.replace("postgresql", "postgresql+asyncpg")
39+
schema_name = f"sqltest_{random.randint(0, 1000)}"
40+
engine = sqlalchemy.ext.asyncio.create_async_engine(postgres_uri)
41+
async with engine.connect() as conn:
42+
await conn.execute(sqlalchemy.text(f"CREATE SCHEMA {schema_name}"))
43+
await conn.execute(sqlalchemy.text(f"SET search_path TO {schema_name}"))
44+
await conn.commit()
45+
yield conn
46+
await conn.rollback()
47+
await conn.execute(sqlalchemy.text(f"DROP SCHEMA {schema_name} CASCADE"))
48+
await conn.execute(sqlalchemy.text("SET search_path TO public"))
2049

2150

2251
@pytest.fixture(scope="session")
@@ -29,6 +58,7 @@ def postgres_connection(postgres_uri) -> psycopg2.extensions.connection:
2958
@pytest.fixture()
3059
def postgres_db(postgres_connection) -> psycopg2.extensions.connection:
3160
schema_name = f"sqltest_{random.randint(0, 1000)}"
61+
# schema_name = "sqltest_1"
3262
cur = postgres_connection.cursor()
3363
cur.execute(f"CREATE SCHEMA {schema_name}")
3464
cur.execute(f"SET search_path TO {schema_name}")
Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,56 @@
11
import os
22

3-
import asyncpg
4-
import psycopg2.extensions
53
import pytest
6-
from sqlc_runtime.psycopg2 import build_psycopg2_connection
7-
from sqlc_runtime.asyncpg import build_asyncpg_connection
4+
import sqlalchemy.ext.asyncio
85

96
from authors import query
107
from dbtest.migrations import apply_migrations, apply_migrations_async
118

129

13-
def test_authors(postgres_db: psycopg2.extensions.connection):
14-
apply_migrations(postgres_db, [os.path.dirname(__file__) + "/../../../authors/postgresql/schema.sql"])
10+
def test_authors(sqlalchemy_connection: sqlalchemy.engine.Connection):
11+
apply_migrations(sqlalchemy_connection, [os.path.dirname(__file__) + "/../../../authors/postgresql/schema.sql"])
1512

16-
db = build_psycopg2_connection(postgres_db)
13+
db = query.Query(sqlalchemy_connection)
1714

18-
authors = list(query.list_authors(db))
15+
authors = list(db.list_authors())
1916
assert authors == []
2017

2118
author_name = "Brian Kernighan"
2219
author_bio = "Co-author of The C Programming Language and The Go Programming Language"
23-
new_author = query.create_author(db, name=author_name, bio=author_bio)
20+
new_author = db.create_author(name=author_name, bio=author_bio)
2421
assert new_author.id > 0
2522
assert new_author.name == author_name
2623
assert new_author.bio == author_bio
2724

28-
db_author = query.get_author(db, new_author.id)
25+
db_author = db.get_author(new_author.id)
2926
assert db_author == new_author
3027

31-
author_list = list(query.list_authors(db))
28+
author_list = list(db.list_authors())
3229
assert len(author_list) == 1
3330
assert author_list[0] == new_author
3431

3532

3633
@pytest.mark.asyncio
37-
async def test_authors_async(async_postgres_db: asyncpg.Connection):
38-
await apply_migrations_async(async_postgres_db, [os.path.dirname(__file__) + "/../../../authors/postgresql/schema.sql"])
34+
async def test_authors_async(async_sqlalchemy_connection: sqlalchemy.ext.asyncio.AsyncConnection):
35+
await apply_migrations_async(async_sqlalchemy_connection, [os.path.dirname(__file__) + "/../../../authors/postgresql/schema.sql"])
3936

40-
db = build_asyncpg_connection(async_postgres_db)
37+
db = query.AsyncQuery(async_sqlalchemy_connection)
4138

42-
async for _ in query.list_authors(db):
39+
async for _ in db.list_authors():
4340
assert False, "No authors should exist"
4441

4542
author_name = "Brian Kernighan"
4643
author_bio = "Co-author of The C Programming Language and The Go Programming Language"
47-
new_author = await query.create_author(db, name=author_name, bio=author_bio)
44+
new_author = await db.create_author(name=author_name, bio=author_bio)
4845
assert new_author.id > 0
4946
assert new_author.name == author_name
5047
assert new_author.bio == author_bio
5148

52-
db_author = await query.get_author(db, new_author.id)
49+
db_author = await db.get_author(new_author.id)
5350
assert db_author == new_author
5451

5552
author_list = []
56-
async for author in query.list_authors(db):
53+
async for author in db.list_authors():
5754
author_list.append(author)
5855
assert len(author_list) == 1
5956
assert author_list[0] == new_author

0 commit comments

Comments
 (0)