Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ asyncpg). Some options raise the minimum Python version; see each option.
| `emit_str_enum` | `false` | `enum.StrEnum` (Python 3.11+) instead of `(str, enum.Enum)` |
| `emit_exact_table_names` | `false` | Don't singularize table names for model classes |
| `inflection_exclude_table_names` | `[]` | Table names never singularized |
| `query_parameter_limit` | `4` | Params above this become a params class; `0` always uses one |
| `query_parameter_limit` | `4` | Params above this become a keyword-only `arg` params class; `0` always uses one |
| `emit_modern_types` | `false` | `X \| None`, `list[X]`, `collections.abc` (Python 3.10+) |
| `emit_generic_querier` | `false` | PEP 695 generic queriers (Python 3.12+) |
| `emit_aware_datetime` | `false` | `timestamptz` as `pydantic.AwareDatetime` (needs `emit_pydantic_models`) |
Expand Down Expand Up @@ -177,7 +177,8 @@ class Querier[_ConnT: Union[sqlalchemy.engine.Connection, sqlalchemy.orm.Session
Commands: `:copyfrom`, `:batchexec`, `:batchone`, `:batchmany`

All four always generate a params class, whatever `query_parameter_limit` is,
and take one positional `Sequence` of it. An empty sequence runs nothing.
and take it as a keyword-only `args: Sequence[Params]`. An empty sequence runs
nothing.

- `:copyfrom` runs a single `executemany` and returns the row count, as
reported by the driver (some report `-1`).
Expand All @@ -203,12 +204,12 @@ class CreateAuthorsParams:
class Querier:
# ...

def create_authors(self, arg: Sequence[CreateAuthorsParams]) -> None:
if not arg:
def create_authors(self, *, args: Sequence[CreateAuthorsParams]) -> None:
if not args:
return None
self._conn.execute(
sqlalchemy.text(CREATE_AUTHORS),
[{"p1": a.name, "p2": a.bio} for a in arg],
[{"p1": a.name, "p2": a.bio} for a in args],
)
```

Expand Down Expand Up @@ -474,15 +475,18 @@ Leaves the `# versions:` / `# sqlc vX.Y.Z` lines out of file headers.
unchanged and new behaviour is opt-in. Expect diffs where upstream produced
invalid code (keyword names, empty classes, enum names) or `Any`
(`varchar`, `timestamp`, …), plus the annotation-only `cast()` and
`_conn: Union[...]` changes.
`_conn: Union[...]` changes. Params-class methods take a keyword-only
`arg=` instead of a positional argument.
- From [asavoy/alt-sqlc-gen-python][asavoy]:
- Set `emit_modern_types`, `emit_aware_datetime` and `emit_generic_querier`
to get its output style; here they are opt-in.
- `emit_query_errors` keeps the same class names, constructor, `query_name`
and `cause`; `constraint_name` is new.
- `overrides` accepts its `py_type` + `py_import` form unchanged.
- `:batchexec` takes one positional `arg: Sequence[Params]` instead of a
keyword-only `args: list[Params]`.
- `:batchexec` (like `:copyfrom`, `:batchone` and `:batchmany`) takes a
keyword-only `args: Sequence[Params]` instead of `args: list[Params]`.
- Other params-class methods take a keyword-only `arg=` instead of a
positional argument.

## Development

Expand Down
2 changes: 1 addition & 1 deletion examples/src/booktest/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ async def create_author(self, *, name: str) -> Optional[models.Author]:
name=cast(str, row[1]),
)

async def create_book(self, arg: CreateBookParams) -> Optional[models.Book]:
async def create_book(self, *, arg: CreateBookParams) -> Optional[models.Book]:
row = (await self._conn.execute(sqlalchemy.text(CREATE_BOOK), {
"p1": arg.author_id,
"p2": arg.isbn,
Expand Down
68 changes: 34 additions & 34 deletions examples/src/library/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,21 +109,21 @@ def create_author(self, *, name: str, bio: Optional[str]) -> Optional[models.Aut

def create_author_at(self, *, name: str, created_at: datetime.datetime) -> Optional[models.Author]: ...

def create_authors(self, arg: Sequence[CreateAuthorsParams]) -> int: ...
def create_authors(self, *, args: Sequence[CreateAuthorsParams]) -> int: ...

def create_book(self, *, author_id: int, title: str) -> Optional[models.Book]: ...

def create_books(self, arg: Sequence[CreateBooksParams]) -> None: ...
def create_books(self, *, args: Sequence[CreateBooksParams]) -> None: ...

def get_authors_by_id(self, arg: Sequence[GetAuthorsByIDParams]) -> Iterator[Optional[models.Author]]: ...
def get_authors_by_id(self, *, args: Sequence[GetAuthorsByIDParams]) -> Iterator[Optional[models.Author]]: ...

def get_book_with_author(self, *, id: int) -> Optional[GetBookWithAuthorRow]: ...

def insert_authors_returning(self, arg: Sequence[InsertAuthorsReturningParams]) -> Iterator[Optional[models.Author]]: ...
def insert_authors_returning(self, *, args: Sequence[InsertAuthorsReturningParams]) -> Iterator[Optional[models.Author]]: ...

def list_authors(self) -> Iterator[models.Author]: ...

def list_books_by_author(self, arg: Sequence[ListBooksByAuthorParams]) -> Iterator[List[models.Book]]: ...
def list_books_by_author(self, *, args: Sequence[ListBooksByAuthorParams]) -> Iterator[List[models.Book]]: ...

def set_book_title(self, *, id: int, title: str) -> None: ...

Expand All @@ -133,21 +133,21 @@ async def create_author(self, *, name: str, bio: Optional[str]) -> Optional[mode

async def create_author_at(self, *, name: str, created_at: datetime.datetime) -> Optional[models.Author]: ...

async def create_authors(self, arg: Sequence[CreateAuthorsParams]) -> int: ...
async def create_authors(self, *, args: Sequence[CreateAuthorsParams]) -> int: ...

async def create_book(self, *, author_id: int, title: str) -> Optional[models.Book]: ...

async def create_books(self, arg: Sequence[CreateBooksParams]) -> None: ...
async def create_books(self, *, args: Sequence[CreateBooksParams]) -> None: ...

def get_authors_by_id(self, arg: Sequence[GetAuthorsByIDParams]) -> AsyncIterator[Optional[models.Author]]: ...
def get_authors_by_id(self, *, args: Sequence[GetAuthorsByIDParams]) -> AsyncIterator[Optional[models.Author]]: ...

async def get_book_with_author(self, *, id: int) -> Optional[GetBookWithAuthorRow]: ...

def insert_authors_returning(self, arg: Sequence[InsertAuthorsReturningParams]) -> AsyncIterator[Optional[models.Author]]: ...
def insert_authors_returning(self, *, args: Sequence[InsertAuthorsReturningParams]) -> AsyncIterator[Optional[models.Author]]: ...

def list_authors(self) -> AsyncIterator[models.Author]: ...

def list_books_by_author(self, arg: Sequence[ListBooksByAuthorParams]) -> AsyncIterator[List[models.Book]]: ...
def list_books_by_author(self, *, args: Sequence[ListBooksByAuthorParams]) -> AsyncIterator[List[models.Book]]: ...

async def set_book_title(self, *, id: int, title: str) -> None: ...

Expand Down Expand Up @@ -183,11 +183,11 @@ def create_author_at(self, *, name: str, created_at: datetime.datetime) -> Optio
created_at=cast(datetime.datetime, row[3]),
)

def create_authors(self, arg: Sequence[CreateAuthorsParams]) -> int:
def create_authors(self, *, args: Sequence[CreateAuthorsParams]) -> int:
with errors._wrap_errors("create_authors"):
if not arg:
if not args:
return 0
result = self._conn.execute(sqlalchemy.text(CREATE_AUTHORS), [{"p1": a.name, "p2": a.bio} for a in arg])
result = self._conn.execute(sqlalchemy.text(CREATE_AUTHORS), [{"p1": a.name, "p2": a.bio} for a in args])
return cast("sqlalchemy.engine.CursorResult[Any]", result).rowcount

def create_book(self, *, author_id: int, title: str) -> Optional[models.Book]:
Expand All @@ -202,15 +202,15 @@ def create_book(self, *, author_id: int, title: str) -> Optional[models.Book]:
status=cast(models.BookStatus, row[3]),
)

def create_books(self, arg: Sequence[CreateBooksParams]) -> None:
def create_books(self, *, args: Sequence[CreateBooksParams]) -> None:
with errors._wrap_errors("create_books"):
if not arg:
if not args:
return None
self._conn.execute(sqlalchemy.text(CREATE_BOOKS), [{"p1": a.author_id, "p2": a.title, "p3": a.status} for a in arg])
self._conn.execute(sqlalchemy.text(CREATE_BOOKS), [{"p1": a.author_id, "p2": a.title, "p3": a.status} for a in args])

def get_authors_by_id(self, arg: Sequence[GetAuthorsByIDParams]) -> Iterator[Optional[models.Author]]:
def get_authors_by_id(self, *, args: Sequence[GetAuthorsByIDParams]) -> Iterator[Optional[models.Author]]:
with errors._wrap_errors("get_authors_by_id"):
for a in arg:
for a in args:
row = self._conn.execute(sqlalchemy.text(GET_AUTHORS_BY_ID), {"p1": a.id}).first()
if row is None:
yield None
Expand Down Expand Up @@ -242,9 +242,9 @@ def get_book_with_author(self, *, id: int) -> Optional[GetBookWithAuthorRow]:
),
)

def insert_authors_returning(self, arg: Sequence[InsertAuthorsReturningParams]) -> Iterator[Optional[models.Author]]:
def insert_authors_returning(self, *, args: Sequence[InsertAuthorsReturningParams]) -> Iterator[Optional[models.Author]]:
with errors._wrap_errors("insert_authors_returning"):
for a in arg:
for a in args:
row = self._conn.execute(sqlalchemy.text(INSERT_AUTHORS_RETURNING), {"p1": a.name}).first()
if row is None:
yield None
Expand All @@ -267,9 +267,9 @@ def list_authors(self) -> Iterator[models.Author]:
created_at=cast(datetime.datetime, row[3]),
)

def list_books_by_author(self, arg: Sequence[ListBooksByAuthorParams]) -> Iterator[List[models.Book]]:
def list_books_by_author(self, *, args: Sequence[ListBooksByAuthorParams]) -> Iterator[List[models.Book]]:
with errors._wrap_errors("list_books_by_author"):
for a in arg:
for a in args:
yield [models.Book(
id=cast(int, row[0]),
author_id=cast(int, row[1]),
Expand Down Expand Up @@ -313,11 +313,11 @@ async def create_author_at(self, *, name: str, created_at: datetime.datetime) ->
created_at=cast(datetime.datetime, row[3]),
)

async def create_authors(self, arg: Sequence[CreateAuthorsParams]) -> int:
async def create_authors(self, *, args: Sequence[CreateAuthorsParams]) -> int:
with errors._wrap_errors("create_authors"):
if not arg:
if not args:
return 0
result = await self._conn.execute(sqlalchemy.text(CREATE_AUTHORS), [{"p1": a.name, "p2": a.bio} for a in arg])
result = await self._conn.execute(sqlalchemy.text(CREATE_AUTHORS), [{"p1": a.name, "p2": a.bio} for a in args])
return cast("sqlalchemy.engine.CursorResult[Any]", result).rowcount

async def create_book(self, *, author_id: int, title: str) -> Optional[models.Book]:
Expand All @@ -332,15 +332,15 @@ async def create_book(self, *, author_id: int, title: str) -> Optional[models.Bo
status=cast(models.BookStatus, row[3]),
)

async def create_books(self, arg: Sequence[CreateBooksParams]) -> None:
async def create_books(self, *, args: Sequence[CreateBooksParams]) -> None:
with errors._wrap_errors("create_books"):
if not arg:
if not args:
return None
await self._conn.execute(sqlalchemy.text(CREATE_BOOKS), [{"p1": a.author_id, "p2": a.title, "p3": a.status} for a in arg])
await self._conn.execute(sqlalchemy.text(CREATE_BOOKS), [{"p1": a.author_id, "p2": a.title, "p3": a.status} for a in args])

async def get_authors_by_id(self, arg: Sequence[GetAuthorsByIDParams]) -> AsyncIterator[Optional[models.Author]]:
async def get_authors_by_id(self, *, args: Sequence[GetAuthorsByIDParams]) -> AsyncIterator[Optional[models.Author]]:
with errors._wrap_errors("get_authors_by_id"):
for a in arg:
for a in args:
row = (await self._conn.execute(sqlalchemy.text(GET_AUTHORS_BY_ID), {"p1": a.id})).first()
if row is None:
yield None
Expand Down Expand Up @@ -372,9 +372,9 @@ async def get_book_with_author(self, *, id: int) -> Optional[GetBookWithAuthorRo
),
)

async def insert_authors_returning(self, arg: Sequence[InsertAuthorsReturningParams]) -> AsyncIterator[Optional[models.Author]]:
async def insert_authors_returning(self, *, args: Sequence[InsertAuthorsReturningParams]) -> AsyncIterator[Optional[models.Author]]:
with errors._wrap_errors("insert_authors_returning"):
for a in arg:
for a in args:
row = (await self._conn.execute(sqlalchemy.text(INSERT_AUTHORS_RETURNING), {"p1": a.name})).first()
if row is None:
yield None
Expand All @@ -397,9 +397,9 @@ async def list_authors(self) -> AsyncIterator[models.Author]:
created_at=cast(datetime.datetime, row[3]),
)

async def list_books_by_author(self, arg: Sequence[ListBooksByAuthorParams]) -> AsyncIterator[List[models.Book]]:
async def list_books_by_author(self, *, args: Sequence[ListBooksByAuthorParams]) -> AsyncIterator[List[models.Book]]:
with errors._wrap_errors("list_books_by_author"):
for a in arg:
for a in args:
yield [models.Book(
id=cast(int, row[0]),
author_id=cast(int, row[1]),
Expand Down
Loading
Loading