t-sql 0.1.1__tar.gz → 1.0.0__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.
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025
3
+ Copyright (c) 2025 Nick Humrich
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
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
t_sql-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: t-sql
3
+ Version: 1.0.0
4
+ Summary: Safe SQL. SQL queries for python t-strings (PEP 750)
5
+ Project-URL: Homepage, https://github.com/nhumrich/tsql
6
+ Requires-Python: >=3.14
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Dynamic: license-file
10
+
11
+ # tsql
12
+
13
+ A lightweight SQL templating library that leverages Python 3.14's t-strings (PEP 750).
14
+
15
+ TSQL provides a safe way to write SQL queries using Python's template strings (t-strings) while preventing SQL injection attacks through multiple parameter styling options.
16
+
17
+ ## ⚠️ Python Version Requirement
18
+ This library requires Python 3.14+
19
+
20
+ TSQL is built specifically to take advantage of the new t-string feature introduced in PEP 750, which is only available in Python 3.14+.
21
+
22
+ ## Installing
23
+
24
+ ```
25
+ # with pip
26
+ pip install t-sql
27
+
28
+ # with uv
29
+ uv add t-sql
30
+ ```
31
+
32
+ ## using
33
+
34
+ ```
35
+ import tsql
36
+
37
+ tsql.render(t"select * from users where name={name)")
38
+ ```
39
+
40
+ ## Parameter Styles
41
+
42
+ - **QMARK** (default): Uses `?` placeholders
43
+ - **NUMERIC**: Uses `:1`, `:2`, etc. placeholders
44
+ - **NAMED**: Uses `:name` placeholders
45
+ - **FORMAT**: Uses `%s` placeholders
46
+ - **PYFORMAT**: Uses `%(name)s` placeholders
47
+ - **NUMERIC_DOLLAR**: Uses `$1`, `$2`, etc. (PostgreSQL native)
48
+ - **ESCAPED**: Escapes values directly into SQL (no parameters)
49
+
50
+ ## Examples:
51
+
52
+ ```python
53
+
54
+ # Basic usage with different parameter styles
55
+ import tsql
56
+ import tsql.styles
57
+
58
+ name = 'billy'
59
+ query = t'select * from users where name={name}'
60
+
61
+ # Default QMARK style
62
+ print(tsql.render(query))
63
+ # ('select * from users where name = ?', ['billy'])
64
+
65
+ # PostgreSQL native style
66
+ print(tsql.render(query, style=tsql.styles.NUMERIC_DOLLAR))
67
+ # ('select * from users where name = $1', ['billy'])
68
+
69
+ # ESCAPED style (no parameters)
70
+ print(tsql.render(query, style=tsql.styles.ESCAPED))
71
+ # ("select * from users where name = 'billy'", [])
72
+
73
+ # SQL injection prevention
74
+ name = "billy ' and 1=1 --"
75
+ print(tsql.render(query, style=tsql.styles.ESCAPED))
76
+ # ("select * from users where name = 'billy '' and 1=1 --'", [])
77
+
78
+ ```
79
+
80
+ ## Format-spec helpers
81
+
82
+ There are some built-in format spec helpers that can change the way some
83
+ parts of the library work.
84
+
85
+ ### Literal
86
+ One common example is you may want to set the name
87
+ of a column dynamically. By using the `literal` format spec, the value will
88
+ be sanitized against a valid literal and put straight into the sql query since
89
+ you cannot parameterize that part of a query, example:
90
+
91
+ ```python
92
+ query = t'select * from {table:literal} where {col:literal}={val}'
93
+ ```
94
+
95
+ or, a full example:
96
+ ```python
97
+
98
+ # with a like clause
99
+ min_age = 30
100
+ search_column = "name"
101
+ pattern = "O'Brien"
102
+ is_active = True
103
+ tsql.render(t"SELECT * FROM test_users WHERE age >= {min_age} AND {search_column:literal} LIKE '%' || {pattern} || '%' AND active = {is_active}")
104
+ ```
105
+
106
+ ### unsafe
107
+ You may want to do advanced things that may otherwise be considered unsfe.
108
+ This is okay if you can be sure that a user is not providing input. Like maybe
109
+ you care storing a query for some reason.
110
+ As per the name, this can open you up to sql injection and should be used with
111
+ extreme caution.
112
+ You can use the "unsafe" format spec for these
113
+ cases:
114
+ ```python
115
+ dynamic_where = input('type where clause')
116
+ tsql.render(t"SELECT * FROM users WHERE {dynamic_where:unsafe}")
117
+ ```
118
+
119
+ ### as_values
120
+
121
+ The spec `:as_values` formats a dictionary into the format:
122
+ `(key1, key2, ...) VALUES (value1, value2, ...)` for uses in insert statements.
123
+
124
+ ### as_set
125
+
126
+ The spec `:as_set` formats a dictionary into the format:
127
+ `key1='?', key2='?'` for uses in update statements.
128
+
129
+ ### traditional format_spec
130
+
131
+ All other format specs should be handled as they would in a normal f-string.
132
+
133
+ ## Included helper methods
134
+
135
+ ```python
136
+ # select
137
+ tsql.select('table', 'abc123')
138
+ # SELECT * FROM table WHERE id='abc123'
139
+
140
+ # select with multiple ids and specific columns
141
+ tsql.select('users', ['abc123', 'def456'], columns=['name', 'age'])
142
+ # SELECT name, age FROM users WHERE id in ('abc123', 'def456')
143
+
144
+
145
+ # t_join (joins multiple t-strings together like .join on a str)
146
+ tsql.t_join(t" ", [t"hello", t"there"])
147
+ # t"hello there"
148
+
149
+
150
+ # insert
151
+ table = 'users'
152
+ values = {'id': 'abc123', 'name': 'bob', 'email': 'bob@example.com'}
153
+ tsql.insert(table, values)
154
+ # INSERT INTO users (id, name, email) VALUES ('abc123', 'bob', 'bob@example.com')
155
+
156
+ # update values on a single row
157
+ table = 'users'
158
+ values = {'name': 'joe', 'email': 'joe@example.com'}
159
+ tsql.update(table, values, id='abc123')
160
+ # UPDATE users SET name='joe', email='joe@example.com' WHERE id='abc123'
161
+ ```
162
+
163
+ # Note on usage
164
+
165
+ This library should ideally be used inside middleware or library code
166
+ right before making an actual query. It can be used to enforce
167
+ using t-strings and prevent using raw strings.
168
+
169
+ For example:
170
+
171
+ ```
172
+ from string.templatelib import Template
173
+
174
+ import tsql
175
+
176
+ def execute_sql_query(query):
177
+ if not isinstance(query, Template):
178
+ raise TypeError('Cannot make a query without using t-strings')
179
+
180
+
181
+ return sql_engine.execute(*tsql.render(query))
182
+
183
+ ```
t_sql-1.0.0/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # tsql
2
+
3
+ A lightweight SQL templating library that leverages Python 3.14's t-strings (PEP 750).
4
+
5
+ TSQL provides a safe way to write SQL queries using Python's template strings (t-strings) while preventing SQL injection attacks through multiple parameter styling options.
6
+
7
+ ## ⚠️ Python Version Requirement
8
+ This library requires Python 3.14+
9
+
10
+ TSQL is built specifically to take advantage of the new t-string feature introduced in PEP 750, which is only available in Python 3.14+.
11
+
12
+ ## Installing
13
+
14
+ ```
15
+ # with pip
16
+ pip install t-sql
17
+
18
+ # with uv
19
+ uv add t-sql
20
+ ```
21
+
22
+ ## using
23
+
24
+ ```
25
+ import tsql
26
+
27
+ tsql.render(t"select * from users where name={name)")
28
+ ```
29
+
30
+ ## Parameter Styles
31
+
32
+ - **QMARK** (default): Uses `?` placeholders
33
+ - **NUMERIC**: Uses `:1`, `:2`, etc. placeholders
34
+ - **NAMED**: Uses `:name` placeholders
35
+ - **FORMAT**: Uses `%s` placeholders
36
+ - **PYFORMAT**: Uses `%(name)s` placeholders
37
+ - **NUMERIC_DOLLAR**: Uses `$1`, `$2`, etc. (PostgreSQL native)
38
+ - **ESCAPED**: Escapes values directly into SQL (no parameters)
39
+
40
+ ## Examples:
41
+
42
+ ```python
43
+
44
+ # Basic usage with different parameter styles
45
+ import tsql
46
+ import tsql.styles
47
+
48
+ name = 'billy'
49
+ query = t'select * from users where name={name}'
50
+
51
+ # Default QMARK style
52
+ print(tsql.render(query))
53
+ # ('select * from users where name = ?', ['billy'])
54
+
55
+ # PostgreSQL native style
56
+ print(tsql.render(query, style=tsql.styles.NUMERIC_DOLLAR))
57
+ # ('select * from users where name = $1', ['billy'])
58
+
59
+ # ESCAPED style (no parameters)
60
+ print(tsql.render(query, style=tsql.styles.ESCAPED))
61
+ # ("select * from users where name = 'billy'", [])
62
+
63
+ # SQL injection prevention
64
+ name = "billy ' and 1=1 --"
65
+ print(tsql.render(query, style=tsql.styles.ESCAPED))
66
+ # ("select * from users where name = 'billy '' and 1=1 --'", [])
67
+
68
+ ```
69
+
70
+ ## Format-spec helpers
71
+
72
+ There are some built-in format spec helpers that can change the way some
73
+ parts of the library work.
74
+
75
+ ### Literal
76
+ One common example is you may want to set the name
77
+ of a column dynamically. By using the `literal` format spec, the value will
78
+ be sanitized against a valid literal and put straight into the sql query since
79
+ you cannot parameterize that part of a query, example:
80
+
81
+ ```python
82
+ query = t'select * from {table:literal} where {col:literal}={val}'
83
+ ```
84
+
85
+ or, a full example:
86
+ ```python
87
+
88
+ # with a like clause
89
+ min_age = 30
90
+ search_column = "name"
91
+ pattern = "O'Brien"
92
+ is_active = True
93
+ tsql.render(t"SELECT * FROM test_users WHERE age >= {min_age} AND {search_column:literal} LIKE '%' || {pattern} || '%' AND active = {is_active}")
94
+ ```
95
+
96
+ ### unsafe
97
+ You may want to do advanced things that may otherwise be considered unsfe.
98
+ This is okay if you can be sure that a user is not providing input. Like maybe
99
+ you care storing a query for some reason.
100
+ As per the name, this can open you up to sql injection and should be used with
101
+ extreme caution.
102
+ You can use the "unsafe" format spec for these
103
+ cases:
104
+ ```python
105
+ dynamic_where = input('type where clause')
106
+ tsql.render(t"SELECT * FROM users WHERE {dynamic_where:unsafe}")
107
+ ```
108
+
109
+ ### as_values
110
+
111
+ The spec `:as_values` formats a dictionary into the format:
112
+ `(key1, key2, ...) VALUES (value1, value2, ...)` for uses in insert statements.
113
+
114
+ ### as_set
115
+
116
+ The spec `:as_set` formats a dictionary into the format:
117
+ `key1='?', key2='?'` for uses in update statements.
118
+
119
+ ### traditional format_spec
120
+
121
+ All other format specs should be handled as they would in a normal f-string.
122
+
123
+ ## Included helper methods
124
+
125
+ ```python
126
+ # select
127
+ tsql.select('table', 'abc123')
128
+ # SELECT * FROM table WHERE id='abc123'
129
+
130
+ # select with multiple ids and specific columns
131
+ tsql.select('users', ['abc123', 'def456'], columns=['name', 'age'])
132
+ # SELECT name, age FROM users WHERE id in ('abc123', 'def456')
133
+
134
+
135
+ # t_join (joins multiple t-strings together like .join on a str)
136
+ tsql.t_join(t" ", [t"hello", t"there"])
137
+ # t"hello there"
138
+
139
+
140
+ # insert
141
+ table = 'users'
142
+ values = {'id': 'abc123', 'name': 'bob', 'email': 'bob@example.com'}
143
+ tsql.insert(table, values)
144
+ # INSERT INTO users (id, name, email) VALUES ('abc123', 'bob', 'bob@example.com')
145
+
146
+ # update values on a single row
147
+ table = 'users'
148
+ values = {'name': 'joe', 'email': 'joe@example.com'}
149
+ tsql.update(table, values, id='abc123')
150
+ # UPDATE users SET name='joe', email='joe@example.com' WHERE id='abc123'
151
+ ```
152
+
153
+ # Note on usage
154
+
155
+ This library should ideally be used inside middleware or library code
156
+ right before making an actual query. It can be used to enforce
157
+ using t-strings and prevent using raw strings.
158
+
159
+ For example:
160
+
161
+ ```
162
+ from string.templatelib import Template
163
+
164
+ import tsql
165
+
166
+ def execute_sql_query(query):
167
+ if not isinstance(query, Template):
168
+ raise TypeError('Cannot make a query without using t-strings')
169
+
170
+
171
+ return sql_engine.execute(*tsql.render(query))
172
+
173
+ ```
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "t-sql"
3
+ version = "1.0.0"
4
+ description = "Safe SQL. SQL queries for python t-strings (PEP 750)"
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ dependencies = []
8
+
9
+ [project.urls]
10
+ Homepage = "https://github.com/nhumrich/tsql"
11
+
12
+
13
+ [dependency-groups]
14
+ dev = [
15
+ "anyio>=4.9.0",
16
+ "asyncpg>=0.30.0",
17
+ "pytest>=8.3.5",
18
+ "pytest-asyncio>=0.24.0",
19
+ ]
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: t-sql
3
+ Version: 1.0.0
4
+ Summary: Safe SQL. SQL queries for python t-strings (PEP 750)
5
+ Project-URL: Homepage, https://github.com/nhumrich/tsql
6
+ Requires-Python: >=3.14
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Dynamic: license-file
10
+
11
+ # tsql
12
+
13
+ A lightweight SQL templating library that leverages Python 3.14's t-strings (PEP 750).
14
+
15
+ TSQL provides a safe way to write SQL queries using Python's template strings (t-strings) while preventing SQL injection attacks through multiple parameter styling options.
16
+
17
+ ## ⚠️ Python Version Requirement
18
+ This library requires Python 3.14+
19
+
20
+ TSQL is built specifically to take advantage of the new t-string feature introduced in PEP 750, which is only available in Python 3.14+.
21
+
22
+ ## Installing
23
+
24
+ ```
25
+ # with pip
26
+ pip install t-sql
27
+
28
+ # with uv
29
+ uv add t-sql
30
+ ```
31
+
32
+ ## using
33
+
34
+ ```
35
+ import tsql
36
+
37
+ tsql.render(t"select * from users where name={name)")
38
+ ```
39
+
40
+ ## Parameter Styles
41
+
42
+ - **QMARK** (default): Uses `?` placeholders
43
+ - **NUMERIC**: Uses `:1`, `:2`, etc. placeholders
44
+ - **NAMED**: Uses `:name` placeholders
45
+ - **FORMAT**: Uses `%s` placeholders
46
+ - **PYFORMAT**: Uses `%(name)s` placeholders
47
+ - **NUMERIC_DOLLAR**: Uses `$1`, `$2`, etc. (PostgreSQL native)
48
+ - **ESCAPED**: Escapes values directly into SQL (no parameters)
49
+
50
+ ## Examples:
51
+
52
+ ```python
53
+
54
+ # Basic usage with different parameter styles
55
+ import tsql
56
+ import tsql.styles
57
+
58
+ name = 'billy'
59
+ query = t'select * from users where name={name}'
60
+
61
+ # Default QMARK style
62
+ print(tsql.render(query))
63
+ # ('select * from users where name = ?', ['billy'])
64
+
65
+ # PostgreSQL native style
66
+ print(tsql.render(query, style=tsql.styles.NUMERIC_DOLLAR))
67
+ # ('select * from users where name = $1', ['billy'])
68
+
69
+ # ESCAPED style (no parameters)
70
+ print(tsql.render(query, style=tsql.styles.ESCAPED))
71
+ # ("select * from users where name = 'billy'", [])
72
+
73
+ # SQL injection prevention
74
+ name = "billy ' and 1=1 --"
75
+ print(tsql.render(query, style=tsql.styles.ESCAPED))
76
+ # ("select * from users where name = 'billy '' and 1=1 --'", [])
77
+
78
+ ```
79
+
80
+ ## Format-spec helpers
81
+
82
+ There are some built-in format spec helpers that can change the way some
83
+ parts of the library work.
84
+
85
+ ### Literal
86
+ One common example is you may want to set the name
87
+ of a column dynamically. By using the `literal` format spec, the value will
88
+ be sanitized against a valid literal and put straight into the sql query since
89
+ you cannot parameterize that part of a query, example:
90
+
91
+ ```python
92
+ query = t'select * from {table:literal} where {col:literal}={val}'
93
+ ```
94
+
95
+ or, a full example:
96
+ ```python
97
+
98
+ # with a like clause
99
+ min_age = 30
100
+ search_column = "name"
101
+ pattern = "O'Brien"
102
+ is_active = True
103
+ tsql.render(t"SELECT * FROM test_users WHERE age >= {min_age} AND {search_column:literal} LIKE '%' || {pattern} || '%' AND active = {is_active}")
104
+ ```
105
+
106
+ ### unsafe
107
+ You may want to do advanced things that may otherwise be considered unsfe.
108
+ This is okay if you can be sure that a user is not providing input. Like maybe
109
+ you care storing a query for some reason.
110
+ As per the name, this can open you up to sql injection and should be used with
111
+ extreme caution.
112
+ You can use the "unsafe" format spec for these
113
+ cases:
114
+ ```python
115
+ dynamic_where = input('type where clause')
116
+ tsql.render(t"SELECT * FROM users WHERE {dynamic_where:unsafe}")
117
+ ```
118
+
119
+ ### as_values
120
+
121
+ The spec `:as_values` formats a dictionary into the format:
122
+ `(key1, key2, ...) VALUES (value1, value2, ...)` for uses in insert statements.
123
+
124
+ ### as_set
125
+
126
+ The spec `:as_set` formats a dictionary into the format:
127
+ `key1='?', key2='?'` for uses in update statements.
128
+
129
+ ### traditional format_spec
130
+
131
+ All other format specs should be handled as they would in a normal f-string.
132
+
133
+ ## Included helper methods
134
+
135
+ ```python
136
+ # select
137
+ tsql.select('table', 'abc123')
138
+ # SELECT * FROM table WHERE id='abc123'
139
+
140
+ # select with multiple ids and specific columns
141
+ tsql.select('users', ['abc123', 'def456'], columns=['name', 'age'])
142
+ # SELECT name, age FROM users WHERE id in ('abc123', 'def456')
143
+
144
+
145
+ # t_join (joins multiple t-strings together like .join on a str)
146
+ tsql.t_join(t" ", [t"hello", t"there"])
147
+ # t"hello there"
148
+
149
+
150
+ # insert
151
+ table = 'users'
152
+ values = {'id': 'abc123', 'name': 'bob', 'email': 'bob@example.com'}
153
+ tsql.insert(table, values)
154
+ # INSERT INTO users (id, name, email) VALUES ('abc123', 'bob', 'bob@example.com')
155
+
156
+ # update values on a single row
157
+ table = 'users'
158
+ values = {'name': 'joe', 'email': 'joe@example.com'}
159
+ tsql.update(table, values, id='abc123')
160
+ # UPDATE users SET name='joe', email='joe@example.com' WHERE id='abc123'
161
+ ```
162
+
163
+ # Note on usage
164
+
165
+ This library should ideally be used inside middleware or library code
166
+ right before making an actual query. It can be used to enforce
167
+ using t-strings and prevent using raw strings.
168
+
169
+ For example:
170
+
171
+ ```
172
+ from string.templatelib import Template
173
+
174
+ import tsql
175
+
176
+ def execute_sql_query(query):
177
+ if not isinstance(query, Template):
178
+ raise TypeError('Cannot make a query without using t-strings')
179
+
180
+
181
+ return sql_engine.execute(*tsql.render(query))
182
+
183
+ ```
@@ -0,0 +1,19 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ t_sql.egg-info/PKG-INFO
5
+ t_sql.egg-info/SOURCES.txt
6
+ t_sql.egg-info/dependency_links.txt
7
+ t_sql.egg-info/top_level.txt
8
+ tests/test_asyncpg_integration.py
9
+ tests/test_different_object_types.py
10
+ tests/test_escaped.py
11
+ tests/test_escaped_binary_hex.py
12
+ tests/test_helper_functions.py
13
+ tests/test_injection_edge_cases.py
14
+ tests/test_injection_protection_validation.py
15
+ tests/test_injections_for_escaped.py
16
+ tests/test_styles.py
17
+ tests/test_tsql.py
18
+ tsql/__init__.py
19
+ tsql/styles.py