namedquery 0.1.0__py3-none-any.whl

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.
namedquery/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ from .core import NamedQuery
2
+ from .core import named
namedquery/core.py ADDED
@@ -0,0 +1,20 @@
1
+ import re
2
+
3
+ _named_param_re = re.compile(r'(?<!:):(\w+)')
4
+
5
+ class NamedQuery:
6
+ def __init__(self, sql):
7
+ params = []
8
+ def replace(m):
9
+ params.append(m.group(1))
10
+ return f'${len(params)}'
11
+
12
+ self.sql = _named_param_re.sub(replace, sql)
13
+ self.params = params
14
+
15
+ def args(self, kwargs):
16
+ return [kwargs[p] for p in self.params]
17
+
18
+
19
+ def named(sql):
20
+ return NamedQuery(sql)
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: namedquery
3
+ Version: 0.1.0
4
+ Summary: Named SQL parameters with once-at-definition parsing
5
+ Author-email: Gustavo Venturini <gustavo.c.venturini@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Gustavo Venturini
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/vottini/namedquery
29
+ Classifier: Development Status :: 4 - Beta
30
+ Classifier: Intended Audience :: Developers
31
+ Classifier: License :: OSI Approved :: MIT License
32
+ Classifier: Operating System :: OS Independent
33
+ Classifier: Programming Language :: Python :: 3 :: Only
34
+ Classifier: Topic :: Database
35
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
36
+ Requires-Python: >=3.11
37
+ Description-Content-Type: text/markdown
38
+ License-File: LICENSE
39
+ Dynamic: license-file
40
+
41
+
42
+ # namedquery
43
+
44
+ A minimal Python utility that converts named SQL parameters (`:name` style) into
45
+ positional `$N` parameters, parsing the query once at definition time so repeated
46
+ executions pay no parsing cost.
47
+
48
+ > Most named-parameter libraries re-parse the SQL string on every call. `namedquery`
49
+ > exists because that's unnecessary work — if your queries are fixed at startup,
50
+ > the regex should run exactly once. Every subsequent execution is just a list lookup.
51
+
52
+ ---
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install namedquery
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Why
63
+
64
+ Most Python SQL drivers that target PostgreSQL (`asyncpg`, `psycopg3`) expect
65
+ positional parameters (`$1`, `$2`, …), but writing positional SQL by hand is
66
+ error-prone — parameter order must match the argument list exactly.
67
+
68
+ Named parameters (`:id`, `:active`) are readable and order-independent, but need
69
+ to be converted before the driver sees them.
70
+
71
+ `namedquery` does that conversion **once**, at the time you define the query. Every
72
+ subsequent execution is a plain list lookup — no regex, no re-parsing.
73
+
74
+ ---
75
+
76
+ ## Usage
77
+
78
+ ```python
79
+ from namedquery import named
80
+
81
+ GET_USER = named("SELECT * FROM users WHERE id = :id AND active = :active")
82
+
83
+ # Transformed SQL, ready for the driver
84
+ GET_USER.sql
85
+ # 'SELECT * FROM users WHERE id = $1 AND active = $2'
86
+
87
+ # Arguments in the correct positional order
88
+ GET_USER.args({'id': 7, 'active': True})
89
+ # [7, True]
90
+ ```
91
+
92
+ Pass directly to your driver:
93
+
94
+ ```python
95
+ await conn.fetch(GET_USER.sql, *GET_USER.args({'id': 7, 'active': True}))
96
+ ```
97
+
98
+ ### Defining queries at startup
99
+
100
+ The intended pattern is to define all queries once as module-level constants and
101
+ reuse them throughout the application lifetime:
102
+
103
+ ```python
104
+ # queries.py
105
+ from namedquery import named
106
+
107
+ GET_USER = named("SELECT * FROM users WHERE id = :id")
108
+ LIST_ACTIVE = named("SELECT * FROM users WHERE active = :active ORDER BY :order_by")
109
+ INSERT_USER = named("INSERT INTO users (name, email) VALUES (:name, :email) RETURNING id")
110
+ ```
111
+
112
+ ### PostgreSQL cast syntax
113
+
114
+ `::type` casts are left untouched:
115
+
116
+ ```python
117
+ named("SELECT col::text FROM t WHERE id = :id").sql
118
+ # 'SELECT col::text FROM t WHERE id = $1'
119
+ ```
120
+
121
+ ---
122
+
123
+ ## API
124
+
125
+ ### `named(sql) -> NamedQuery`
126
+
127
+ Parses `sql`, replaces every `:name` placeholder with a `$N` positional parameter,
128
+ and returns a `NamedQuery` instance.
129
+
130
+ ### `NamedQuery.sql`
131
+
132
+ The transformed SQL string with `$N` placeholders. Pass this to your driver.
133
+
134
+ ### `NamedQuery.args(kwargs) -> list`
135
+
136
+ Returns the values from `kwargs` in the order the corresponding parameters appear
137
+ in the SQL. Pass the result as positional arguments to your driver call.
138
+
139
+ ---
140
+
141
+ ## License
142
+
143
+ MIT
@@ -0,0 +1,7 @@
1
+ namedquery/__init__.py,sha256=Yt64fNzWZNRhMjgaJnbZWnP6H3ZiteCZgqVFQcR1ttQ,53
2
+ namedquery/core.py,sha256=9dADofx9LTWJ820jgBJdxO9PXaKfUSI1gNfLn9cKSII,372
3
+ namedquery-0.1.0.dist-info/licenses/LICENSE,sha256=tYW3_ojXxJ8z3MdTV2oAvQgNq_THeGpvt5e0HFOJKTo,1074
4
+ namedquery-0.1.0.dist-info/METADATA,sha256=DzArxQZrFe7r19AKqrbjAh0W6Vb2IOPfF5lvGv3ipRQ,4569
5
+ namedquery-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
6
+ namedquery-0.1.0.dist-info/top_level.txt,sha256=SRU-bjnno4A11gBv_PSGWTsMMvFgzHaqwStaJGYZaOw,11
7
+ namedquery-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gustavo Venturini
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ namedquery