asyncpg-typed 0.1.3__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/LICENSE +1 -1
  2. {asyncpg_typed-0.1.3/asyncpg_typed.egg-info → asyncpg_typed-0.1.4}/PKG-INFO +47 -18
  3. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/README.md +46 -17
  4. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/asyncpg_typed/__init__.py +265 -60
  5. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4/asyncpg_typed.egg-info}/PKG-INFO +47 -18
  6. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/tests/test_code.py +11 -16
  7. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/tests/test_data.py +53 -0
  8. asyncpg_typed-0.1.4/tests/test_sql.py +428 -0
  9. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/tests/test_vector.py +2 -2
  10. asyncpg_typed-0.1.3/tests/test_sql.py +0 -229
  11. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/MANIFEST.in +0 -0
  12. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/asyncpg_typed/py.typed +0 -0
  13. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/asyncpg_typed.egg-info/SOURCES.txt +0 -0
  14. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/asyncpg_typed.egg-info/dependency_links.txt +0 -0
  15. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/asyncpg_typed.egg-info/requires.txt +0 -0
  16. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/asyncpg_typed.egg-info/top_level.txt +0 -0
  17. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/asyncpg_typed.egg-info/zip-safe +0 -0
  18. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/pyproject.toml +0 -0
  19. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/setup.cfg +0 -0
  20. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/tests/__init__.py +0 -0
  21. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/tests/connection.py +0 -0
  22. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/tests/test_template.py +0 -0
  23. {asyncpg_typed-0.1.3 → asyncpg_typed-0.1.4}/tests/test_type.py +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Levente Hunyadi
3
+ Copyright (c) 2025-2026 Levente Hunyadi
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: asyncpg_typed
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Type-safe queries for asyncpg
5
5
  Author-email: Levente Hunyadi <hunyadi@gmail.com>
6
6
  Maintainer-email: Levente Hunyadi <hunyadi@gmail.com>
@@ -45,6 +45,8 @@ This Python library provides "compile-time" validation for SQL queries that lint
45
45
 
46
46
  ## Motivating example
47
47
 
48
+ ### Using a plain tuple
49
+
48
50
  ```python
49
51
  # create a typed object, setting expected and returned types
50
52
  select_where_sql = sql(
@@ -66,7 +68,7 @@ try:
66
68
  # ✅ Type of "rows" is "list[tuple[bool, int, str | None]]"
67
69
  reveal_type(rows)
68
70
 
69
- # ⚠️ Argument missing for parameter "arg2"
71
+ # ⚠️ Expected 1 more positional argument
70
72
  rows = await select_where_sql.fetch(conn, False)
71
73
 
72
74
  # ⚠️ Argument of type "float" cannot be assigned to parameter "arg2" of type "int" in function "fetch"; "float" is not assignable to "int"
@@ -74,27 +76,52 @@ try:
74
76
 
75
77
  finally:
76
78
  await conn.close()
79
+ ```
77
80
 
78
- # create a list of data-class instances from a list of typed tuples
79
- @dataclass
80
- class DataObject:
81
+ ### Using a named tuple
82
+
83
+ ```python
84
+ # capture resultset column names and data types as fields of a named tuple
85
+ class Resultset(NamedTuple):
81
86
  boolean_value: bool
82
87
  integer_value: int
83
88
  string_value: str | None
84
89
 
85
- # ✅ Valid initializer call
86
- items = [DataObject(*row) for row in rows]
87
90
 
88
- @dataclass
89
- class MismatchedObject:
90
- boolean_value: bool
91
- integer_value: int
92
- string_value: str
91
+ # create a typed object, declaring return types with the named tuple
92
+ select_sql = sql(
93
+ """--sql
94
+ SELECT boolean_value, integer_value, string_value
95
+ FROM sample_data
96
+ ORDER BY integer_value;
97
+ """,
98
+ resultset=Resultset,
99
+ )
93
100
 
94
- # ⚠️ Argument of type "int | None" cannot be assigned to parameter "integer_value" of type "int" in function "__init__"; "None" is not assignable to "int"
95
- items = [MismatchedObject(*row) for row in rows]
96
- ```
101
+ conn = await asyncpg.connect(host="localhost", port=5432, user="postgres", password="postgres")
102
+ try:
103
+ rows = await select_sql.fetch(conn)
104
+
105
+ # ✅ Type of "rows" is "list[Resultset]"
106
+ reveal_type(rows)
107
+
108
+ for row in rows:
109
+ # use dot notation to access properties
110
+ if row.string_value is not None:
111
+ print(f"#{row.integer_value}: {row.string_value}")
112
+
113
+ # ✅ Type of "row.boolean_value" is "bool"
114
+ reveal_type(row.boolean_value)
97
115
 
116
+ # unpack named tuple
117
+ b, i, s = row
118
+
119
+ # ✅ Type of "s" is "str | None"
120
+ reveal_type(s)
121
+
122
+ finally:
123
+ await conn.close()
124
+ ```
98
125
 
99
126
  ## Syntax
100
127
 
@@ -284,10 +311,12 @@ Only those functions are prompted on code completion that make sense in the cont
284
311
 
285
312
  #### Run-time behavior
286
313
 
287
- When a call such as `sql.executemany(conn, records)` or `sql.fetch(conn, param1, param2)` is made on a `SQL` object at run time, the library invokes `connection.prepare(sql)` to create a `PreparedStatement` and compares the actual statement signature against the expected Python types. If the expected and actual signatures don't match, an exception `TypeMismatchError` (subclass of `TypeError`) is raised.
314
+ When a call such as `sql.executemany(conn, records)` or `sql.fetch(conn, param1, param2)` is made on a `SQL` object at run time, the library invokes `connection.prepare(sql)` to create a `PreparedStatement` and compares the actual statement signature against the expected Python types. If the expected and actual signatures don't match, a `TypeMismatchError` exception is raised.
315
+
316
+ If the resultset type has been declared with a subclass of `NamedTuple`, the field names of the tuple are compared against the column names of the PostgreSQL resultset. Should there be a mismatch, a `NameMismatchError` is raised. Field and column order is relevant.
288
317
 
289
- The set of values for an enumeration type is validated when a prepared statement is created. The string values declared in a Python `StrEnum` are compared against the values listed in PostgreSQL `CREATE TYPE ... AS ENUM` by querying the system table `pg_enum`. If there are missing or extra values on either side, an exception `EnumMismatchError` (subclass of `TypeError`) is raised.
318
+ The set of values for an enumeration type is validated when a prepared statement is created. The string values declared in a Python `StrEnum` are compared against the values listed in PostgreSQL `CREATE TYPE ... AS ENUM` by querying the system table `pg_enum`. If there are missing or extra values on either side, an `EnumMismatchError` exception is raised.
290
319
 
291
- Unfortunately, PostgreSQL doesn't propagate nullability via prepared statements: resultset types that are declared as required (e.g. `T` as opposed to `T | None`) are validated at run time. When a `None` value is encountered for a required type, an exception `NoneTypeError` (subclass of `TypeError`) is raised.
320
+ Unfortunately, PostgreSQL doesn't propagate nullability via prepared statements: resultset types that are declared as required (e.g. `T` as opposed to `T | None`) have to be validated at run time. When a `None` value is encountered for a required type, a `NoneTypeError` exception is raised.
292
321
 
293
322
  PostgreSQL doesn't differentiate between IPv4 and IPv6 network definitions, or IPv4 and IPv6 addresses in the types `cidr` and `inet`. This means that semantically a union type is returned. If you specify a more restrictive type, the resultset data is validated dynamically at run time.
@@ -7,6 +7,8 @@ This Python library provides "compile-time" validation for SQL queries that lint
7
7
 
8
8
  ## Motivating example
9
9
 
10
+ ### Using a plain tuple
11
+
10
12
  ```python
11
13
  # create a typed object, setting expected and returned types
12
14
  select_where_sql = sql(
@@ -28,7 +30,7 @@ try:
28
30
  # ✅ Type of "rows" is "list[tuple[bool, int, str | None]]"
29
31
  reveal_type(rows)
30
32
 
31
- # ⚠️ Argument missing for parameter "arg2"
33
+ # ⚠️ Expected 1 more positional argument
32
34
  rows = await select_where_sql.fetch(conn, False)
33
35
 
34
36
  # ⚠️ Argument of type "float" cannot be assigned to parameter "arg2" of type "int" in function "fetch"; "float" is not assignable to "int"
@@ -36,27 +38,52 @@ try:
36
38
 
37
39
  finally:
38
40
  await conn.close()
41
+ ```
39
42
 
40
- # create a list of data-class instances from a list of typed tuples
41
- @dataclass
42
- class DataObject:
43
+ ### Using a named tuple
44
+
45
+ ```python
46
+ # capture resultset column names and data types as fields of a named tuple
47
+ class Resultset(NamedTuple):
43
48
  boolean_value: bool
44
49
  integer_value: int
45
50
  string_value: str | None
46
51
 
47
- # ✅ Valid initializer call
48
- items = [DataObject(*row) for row in rows]
49
52
 
50
- @dataclass
51
- class MismatchedObject:
52
- boolean_value: bool
53
- integer_value: int
54
- string_value: str
53
+ # create a typed object, declaring return types with the named tuple
54
+ select_sql = sql(
55
+ """--sql
56
+ SELECT boolean_value, integer_value, string_value
57
+ FROM sample_data
58
+ ORDER BY integer_value;
59
+ """,
60
+ resultset=Resultset,
61
+ )
55
62
 
56
- # ⚠️ Argument of type "int | None" cannot be assigned to parameter "integer_value" of type "int" in function "__init__"; "None" is not assignable to "int"
57
- items = [MismatchedObject(*row) for row in rows]
58
- ```
63
+ conn = await asyncpg.connect(host="localhost", port=5432, user="postgres", password="postgres")
64
+ try:
65
+ rows = await select_sql.fetch(conn)
66
+
67
+ # ✅ Type of "rows" is "list[Resultset]"
68
+ reveal_type(rows)
69
+
70
+ for row in rows:
71
+ # use dot notation to access properties
72
+ if row.string_value is not None:
73
+ print(f"#{row.integer_value}: {row.string_value}")
74
+
75
+ # ✅ Type of "row.boolean_value" is "bool"
76
+ reveal_type(row.boolean_value)
59
77
 
78
+ # unpack named tuple
79
+ b, i, s = row
80
+
81
+ # ✅ Type of "s" is "str | None"
82
+ reveal_type(s)
83
+
84
+ finally:
85
+ await conn.close()
86
+ ```
60
87
 
61
88
  ## Syntax
62
89
 
@@ -246,10 +273,12 @@ Only those functions are prompted on code completion that make sense in the cont
246
273
 
247
274
  #### Run-time behavior
248
275
 
249
- When a call such as `sql.executemany(conn, records)` or `sql.fetch(conn, param1, param2)` is made on a `SQL` object at run time, the library invokes `connection.prepare(sql)` to create a `PreparedStatement` and compares the actual statement signature against the expected Python types. If the expected and actual signatures don't match, an exception `TypeMismatchError` (subclass of `TypeError`) is raised.
276
+ When a call such as `sql.executemany(conn, records)` or `sql.fetch(conn, param1, param2)` is made on a `SQL` object at run time, the library invokes `connection.prepare(sql)` to create a `PreparedStatement` and compares the actual statement signature against the expected Python types. If the expected and actual signatures don't match, a `TypeMismatchError` exception is raised.
277
+
278
+ If the resultset type has been declared with a subclass of `NamedTuple`, the field names of the tuple are compared against the column names of the PostgreSQL resultset. Should there be a mismatch, a `NameMismatchError` is raised. Field and column order is relevant.
250
279
 
251
- The set of values for an enumeration type is validated when a prepared statement is created. The string values declared in a Python `StrEnum` are compared against the values listed in PostgreSQL `CREATE TYPE ... AS ENUM` by querying the system table `pg_enum`. If there are missing or extra values on either side, an exception `EnumMismatchError` (subclass of `TypeError`) is raised.
280
+ The set of values for an enumeration type is validated when a prepared statement is created. The string values declared in a Python `StrEnum` are compared against the values listed in PostgreSQL `CREATE TYPE ... AS ENUM` by querying the system table `pg_enum`. If there are missing or extra values on either side, an `EnumMismatchError` exception is raised.
252
281
 
253
- Unfortunately, PostgreSQL doesn't propagate nullability via prepared statements: resultset types that are declared as required (e.g. `T` as opposed to `T | None`) are validated at run time. When a `None` value is encountered for a required type, an exception `NoneTypeError` (subclass of `TypeError`) is raised.
282
+ Unfortunately, PostgreSQL doesn't propagate nullability via prepared statements: resultset types that are declared as required (e.g. `T` as opposed to `T | None`) have to be validated at run time. When a `None` value is encountered for a required type, a `NoneTypeError` exception is raised.
254
283
 
255
284
  PostgreSQL doesn't differentiate between IPv4 and IPv6 network definitions, or IPv4 and IPv6 addresses in the types `cidr` and `inet`. This means that semantically a union type is returned. If you specify a more restrictive type, the resultset data is validated dynamically at run time.