sqlengine-lite 2.2.1__py3-none-any.whl → 2.2.2__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.
@@ -20,8 +20,7 @@ class ConnectionManager:
20
20
 
21
21
  Args:
22
22
 
23
- database (str): database filename to connect to. If `":memory:"` is passed, then database will be set in memory and you will have to
24
- create table manually with `create_table()` method inside `transaction()` block.
23
+ database (str): database filename to connect to. If `":memory:"` is passed, then database will be set in memory.
25
24
  **connection_params: Params to create connection with. Reference: https://docs.python.org/3/library/sqlite3.html#sqlite3.connect
26
25
  """
27
26
 
@@ -139,7 +138,7 @@ class ConnectionManager:
139
138
  Args:
140
139
  query (str): SQL query
141
140
  *args (tuple[SqlValue, ...]): Arguments to the execution
142
- size (str): Number of rows to return
141
+ size (int): Number of rows to return
143
142
 
144
143
  Returns:
145
144
  rows (list[SqlRow]): list of `size` rows
@@ -197,10 +196,14 @@ class ConnectionManager:
197
196
  if self._is_managed_transaction:
198
197
  raise TransactionError("Can't manually close managed transaction")
199
198
 
200
- self._trans_cursor.close()
201
- self._trans.close()
202
- del(self._trans_cursor)
203
- del(self._trans)
199
+ try:
200
+ self._trans_cursor.close()
201
+ self._trans.close()
202
+ except sqlite3.ProgrammingError as e:
203
+ raise TransactionError("Can't close connection properly") from e
204
+ finally:
205
+ del(self._trans_cursor)
206
+ del(self._trans)
204
207
 
205
208
 
206
209
  def commit(self) -> None:
@@ -32,39 +32,46 @@ class Where[T : "Statement"]:
32
32
 
33
33
 
34
34
  def op(self, column : str, value : SqlValue, operator : str) -> Self:
35
+ """ Adds operator to the where clause """
35
36
  self._clause.append(f"{column} {operator} ?")
36
37
  self._args.append(value)
37
38
  return self
38
39
 
39
40
 
40
41
  def join(self, lop : str = "AND") -> Self:
41
- """ Joins previous expression via logical operator `lop` """
42
+ """ Join previous expression via logical operator `lop` """
42
43
  joined = f" {lop} ".join(self._clause)
43
44
  self._clause = [f"({joined})"]
44
45
  return self
45
46
 
46
47
 
47
48
  def eq(self, column : str, value : SqlValue) -> Self:
49
+ """ Add `column = value` to the where clause """
48
50
  return self.op(column, value, "=")
49
51
 
50
52
 
51
53
  def neq(self, column : str, value : SqlValue) -> Self:
54
+ """ Add `column != value` to the where clause """
52
55
  return self.op(column, value, "!=")
53
56
 
54
57
 
55
58
  def gt(self, column : str, value : SqlValue) -> Self:
59
+ """ Add `column > value` to the where clause """
56
60
  return self.op(column, value, ">")
57
61
 
58
62
 
59
63
  def gte(self, column : str, value : SqlValue) -> Self:
64
+ """ Add `column >= value` to the where clause """
60
65
  return self.op(column, value, ">=")
61
66
 
62
67
 
63
68
  def lt(self, column : str, value : SqlValue) -> Self:
69
+ """ Add `column < value` to the where clause """
64
70
  return self.op(column, value, "<")
65
71
 
66
72
 
67
73
  def lte(self, column : str, value : SqlValue) -> Self:
74
+ """ Add `column <= value` to the where clause """
68
75
  return self.op(column, value, "<=")
69
76
 
70
77
 
@@ -77,12 +84,13 @@ class Where[T : "Statement"]:
77
84
 
78
85
 
79
86
  def is_null(self, column : str) -> Self:
87
+ """ Add `column IS NULL` to the where clause """
80
88
  self._clause.append(f"{column} IS NULL")
81
89
  return self
82
90
 
83
91
 
84
92
  def inverted(self) -> Self:
85
- """ Invert last where clause with NOT """
93
+ """ Invert previous where clauses with `NOT` """
86
94
  self._clause[-1] = f"NOT ({self._clause[-1]})"
87
95
  return self
88
96
 
@@ -128,14 +136,16 @@ class Where[T : "Statement"]:
128
136
  return self._statement.__repr__()
129
137
 
130
138
 
139
+ def __iter__(self):
140
+ if not isinstance(self._statement, Select):
141
+ raise SqlEngineError("Can iterate only over `Select` statements")
142
+ return iter(self._statement)
143
+
144
+
131
145
  def _repr_html_(self) -> str | None:
132
146
  if isinstance(self._statement, Select):
133
147
  return self._statement._repr_html_()
134
148
  return None
135
-
136
-
137
- def __len__(self) -> int:
138
- return len(self._args)
139
149
 
140
150
 
141
151
  class Statement(ABC):
@@ -276,12 +286,12 @@ class Select(Statement):
276
286
 
277
287
  def fetchmany_iterator(self, batch_size: int) -> Generator[list[SqlRow], None, None]:
278
288
  """
279
- Yields all rows in batches, each batch in its own transaction.
289
+ Yields all rows in batches within a single transaction.
280
290
 
281
291
  Args:
282
292
  batch_size (int): Size of each batch
283
293
 
284
- Examples:
294
+ Example:
285
295
 
286
296
  ```python
287
297
  with table.transaction():
@@ -290,8 +300,8 @@ class Select(Statement):
290
300
  ```
291
301
  """
292
302
  if not self._connection.in_transaction():
293
- raise OutsideTransactionError("To use the `fetchall_iterator()` method you have \
294
- to keep open the transaction of the table")
303
+ raise OutsideTransactionError("To use the `fetchall_iterator()` method you have "
304
+ "to keep open the transaction of the table")
295
305
 
296
306
  query, exec_args = self.build()
297
307
 
@@ -306,19 +316,18 @@ class Select(Statement):
306
316
  """
307
317
  Select statement rows iterator
308
318
 
309
- Examples:
319
+ Example:
310
320
 
311
321
  ```python
312
322
  with table.transaction():
313
- # here `then` is used to link back to the `select` instance from `where` object
314
- for row in table.select.where.gt("Age", 30).then:
323
+ for row in table.select.where.gt("Age", 30):
315
324
  process_row(row)
316
325
  ```
317
326
  """
318
327
 
319
328
  if not self._connection.in_transaction():
320
- raise OutsideTransactionError("To use the __iter__ method you have \
321
- to keep open the transaction of the table")
329
+ raise OutsideTransactionError("To use the `__iter__` method you have "
330
+ "to keep open the transaction of the table")
322
331
 
323
332
  query, exec_args = self.build()
324
333
 
sqlengine/schema.py CHANGED
@@ -62,15 +62,9 @@ def get_database_schemas(database : str) -> list[Schema]:
62
62
  cursor = conn.cursor()
63
63
  names = get_database_tablenames(database, cursor)
64
64
 
65
- schemas : list[Schema] = []
65
+ schemas = (get_table_schema(database, table_name, cursor) for table_name in names)
66
66
 
67
- for tablename in names:
68
- schema = get_table_schema(database, tablename, cursor)
69
-
70
- if schema:
71
- schemas.append(schema)
72
-
73
- return schemas
67
+ return [sh for sh in schemas if sh]
74
68
 
75
69
 
76
70
  def table_from_schema(database : str, schema : Schema, **kwargs) -> SqlTableMixin:
sqlengine/sqltable.py CHANGED
@@ -24,7 +24,7 @@ class SqlTableMixin:
24
24
  Lightweight wrapper for SQLite3 tables
25
25
 
26
26
  Args:
27
- database (str): database filename to connect to. If it not exists - will create new one first.
27
+ database (str): database filename to connect to. If it does not exists - will create new one first.
28
28
  If `":memory:"` is passed, then database will be set in memory and you will have to
29
29
  create table manually with `create_table()` method inside `transaction()` block.
30
30
  force_drop (bool): If `True` - will drop existing table.
@@ -37,7 +37,7 @@ class SqlTableMixin:
37
37
  __types__ (list[ColumnType]): Column types of the table
38
38
  __primary__ (list[str]): List of primary keys
39
39
 
40
- Examples:
40
+ Example:
41
41
  ```python
42
42
  from sqlengine import SqlTableMixin, Primary
43
43
 
@@ -189,7 +189,7 @@ class SqlTableMixin:
189
189
  Args:
190
190
  autocommit (bool): If `True`, will commit changes at the end of transaction
191
191
 
192
- Examples:
192
+ Example:
193
193
 
194
194
  ```python
195
195
  with table.transaction():
@@ -209,7 +209,7 @@ class SqlTableMixin:
209
209
  *args (SqlValue): Arguments in order of declared __columns__
210
210
  **kwargs (SqlValue): Column to value mapping
211
211
 
212
- Examples:
212
+ Example:
213
213
 
214
214
  ```python
215
215
  table = MyTable("mydb.db")
@@ -397,7 +397,7 @@ class SqlTableMixin:
397
397
  """
398
398
  UPDATE statement builder and executor
399
399
 
400
- Examples:
400
+ Example:
401
401
 
402
402
  ```python
403
403
  table.update.set("City", "Karaganda").where.eq("Country", "Czech Republic").then.execute()
@@ -411,7 +411,7 @@ class SqlTableMixin:
411
411
  """
412
412
  DELETE statement builder and executor
413
413
 
414
- Examples:
414
+ Example:
415
415
 
416
416
  ```python
417
417
  table.delete.where.eq("ID", 0).then.execute()
@@ -425,7 +425,7 @@ class SqlTableMixin:
425
425
  """
426
426
  SELECT statement builder and fetcher
427
427
 
428
- Examples:
428
+ Example:
429
429
 
430
430
  ```python
431
431
  table.select("Email").where.eq("SupportRepId", 3).then.aggregate("COUNT").fetchone()
@@ -25,7 +25,7 @@ def shared_connection(*args : SqlTableMixin, autocommit : bool = True, **connect
25
25
  **connection_params (dict): Params to create connections with. This argument will be shared
26
26
  across different connections. Reference: https://docs.python.org/3/library/sqlite3.html#sqlite3.connect
27
27
 
28
- Examples:
28
+ Example:
29
29
 
30
30
  ```python
31
31
  from sqlengine.utils import shared_connection
@@ -4,6 +4,7 @@ from typing import Generator
4
4
  from ..sqltable import SqlTableMixin
5
5
  from .._internal.statements import Where, Select
6
6
  from .._internal.types import SqlValue
7
+ from ..exceptions import OutsideTransactionError, SqlEngineError
7
8
 
8
9
 
9
10
  def _get_select(builder : Select | Where[Select] | SqlTableMixin) -> Select:
@@ -30,16 +31,16 @@ def to_csv(
30
31
  Args:
31
32
  builder (Select | Where[Select] | SqlTableMixin): Object to convert to csv
32
33
  filename (str): Path to write to
33
- stream_bach_size (int | None): If not None or 0, will stream all rows to csv in batches of provided size
34
+ stream_batch_size (int | None): If not None or 0, will stream all rows to csv in batches of provided size
34
35
  """
35
36
 
36
37
  builder = _get_select(builder)
37
38
 
38
39
  if builder._aggregate:
39
- raise AssertionError("Aggregated queries are not supported")
40
+ raise SqlEngineError("Aggregated queries are not supported")
40
41
 
41
42
  if stream_batch_size and not builder._connection.in_transaction():
42
- raise RuntimeError("To stream to csv you have to keep open the `transaction`")
43
+ raise OutsideTransactionError("To stream to csv you have to keep open the `transaction`")
43
44
 
44
45
  columns = builder._resolve_columns()
45
46
 
@@ -66,19 +67,19 @@ def to_dicts(builder : Select | Where[Select] | SqlTableMixin) -> list[dict[str,
66
67
  Returns:
67
68
  out (list[dict[str, SqlValue]]): list of rows mappings
68
69
 
69
- Examples:
70
+ Example:
70
71
 
71
72
  ```python
72
73
  import pandas as pd
73
74
 
74
- df = pd.DataFrame(to_dict(table))
75
+ df = pd.DataFrame(to_dicts(table))
75
76
  ```
76
77
  """
77
78
 
78
79
  builder = _get_select(builder)
79
80
 
80
81
  if builder._aggregate:
81
- raise AssertionError("Aggregated queries are not supported")
82
+ raise SqlEngineError("Aggregated queries are not supported")
82
83
 
83
84
  columns = builder._resolve_columns()
84
85
  rows = builder.fetchall()
@@ -95,12 +96,12 @@ def to_dicts_stream(
95
96
 
96
97
  Args:
97
98
  builder (Select | Where[Select] | SqlTableMixin): Object to convert to list of dicts
98
- batch_size (int): Size of each yiedled batch
99
+ batch_size (int): Size of each yielded batch
99
100
 
100
101
  Yields:
101
102
  batch (list[dict[str, SqlValue]]): list of rows mappings
102
103
 
103
- Examples:
104
+ Example:
104
105
 
105
106
  ```python
106
107
  import pandas as pd
@@ -108,7 +109,7 @@ def to_dicts_stream(
108
109
  df = pd.DataFrame(columns=table.columns)
109
110
 
110
111
  with table.transaction():
111
- for batch in to_dict_stream(table, 100):
112
+ for batch in to_dicts_stream(table, 100):
112
113
  df = pd.concat([df, pd.DataFrame(batch)], axis=0)
113
114
 
114
115
  df.set_index("ID", inplace=True)
@@ -118,10 +119,10 @@ def to_dicts_stream(
118
119
  builder = _get_select(builder)
119
120
 
120
121
  if builder._aggregate:
121
- raise AssertionError("Aggregated queries are not supported")
122
+ raise SqlEngineError("Aggregated queries are not supported")
122
123
 
123
124
  if not builder._connection.in_transaction():
124
- raise RuntimeError("To stream to csv you have to keep open the `transaction`")
125
+ raise OutsideTransactionError("To stream to csv you have to keep open the `transaction`")
125
126
 
126
127
  columns = builder._resolve_columns()
127
128
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlengine-lite
3
- Version: 2.2.1
3
+ Version: 2.2.2
4
4
  Summary: Cute sqlite3 wrapper for sql tables
5
5
  Project-URL: Homepage, https://github.com/suffermuffin/SQL-Engine
6
6
  Project-URL: Repository, https://github.com/suffermuffin/SQL-Engine.git
@@ -10,38 +10,33 @@ Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
11
  Dynamic: license-file
12
12
 
13
- - [Sql-Engine](#sql-engine)
14
- - [Features](#features)
15
- - [Purpose](#purpose)
16
- - [Installation](#installation)
17
- - [Env](#env)
18
- - [Quick Start](#quick-start)
19
- - [Table Declaration](#table-declaration)
20
- - [Instantiation](#instantiation)
21
- - [Row insertion](#row-insertion)
22
- - [Jupyter view](#jupyter-view)
23
- - [Select Query](#select-query)
24
- - [Update Query](#update-query)
25
- - [Delete Query](#delete-query)
26
- - [Transaction](#transaction)
27
- - [Get Item](#get-item)
28
- - [Custom Types](#custom-types)
29
- - [Csv Converter](#csv-converter)
30
- - [Pandas-like Converter](#pandas-like-converter)
31
- - [Full Documentation](#full-documentation)
13
+ <p align="center">
14
+ <a href="https://github.com/suffermuffin/SQL-Engine/actions/workflows/test.yml?query=event%3Apush">
15
+ <img src="https://github.com/suffermuffin/SQL-Engine/actions/workflows/test.yml/badge.svg?event=push&branch=main" alt="Tests">
16
+ </a>
17
+ <a href="https://github.com/suffermuffin/SQL-Engine/actions?query=workflow%3APublish">
18
+ <img src="https://github.com/suffermuffin/SQL-Engine/actions/workflows/publish.yml/badge.svg" alt="Publishing">
19
+ </a>
20
+ <a href="https://pypi.org/project/sqlengine-lite/">
21
+ <img alt="PyPI" src="https://img.shields.io/pypi/v/sqlengine-lite?logoSize=amd&labelColor=black&color=royalblue">
22
+ </a>
23
+ </p>
32
24
 
33
25
 
34
- # Sql-Engine
26
+ # SqlEngine
35
27
 
36
- My Sql-Engine is a cute little wrapper for `sqlite3` table manipulations without any third party dependencies.
28
+ My SqlEngine is a cute little wrapper for `sqlite3` table manipulations without any third party dependencies.
29
+
30
+
31
+ [**Home Page**](https://github.com/suffermuffin/SQL-Engine) | [**Installation**](#installation) | [**Quick Start**](#quick-start) | [**Documentation**](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/index.md)
37
32
 
38
33
 
39
34
  ## Features
40
35
 
41
- Abstracts SQL queries into tiny little methods like, `insert`, `insert_many`, `upsert`, and not so little and tiny query builders a-la `select`, `delete`, `update`, etc. Sql-Engine also provides bulk insertion and transaction methods, like `insert_many` and `select.fetchmany_iterator`. Methods can be executed in transaction mode thanks to `transaction` context manager.
36
+ SqlEngine abstracts SQL queries into tiny little methods like `insert`, `insert_many`, `upsert`, and not so tiny (but still cute and little) query builders like `select`, `delete` and `update`. SqlEngine also provides bulk insertion with `insert_many` and transaction operations like `select.fetchmany_iterator`. Methods can be executed either in transaction mode (thanks to `transaction` context manager) or right on the spot.
42
37
 
43
38
 
44
- Sql-Engine implements Jupyter integration and dynamic schema building. You can easily instantiate existing database table and view it in cute little html representation.
39
+ SqlEngine implements Jupyter integration and dynamic schema building. You can easily instantiate existing database table and view it in a cute little html representation.
45
40
 
46
41
  ```py
47
42
  from sqlengine import schema
@@ -74,66 +69,40 @@ table.select("InvoiceId", "CustomerId", "BillingAddress", "BillingCountry", "Tot
74
69
 
75
70
  ## Purpose
76
71
 
77
- It's a tiny little modern ORM-like that lets you prototype your databases locally with great flexibility. Also, it can be used in production apps to store and retrieve data, because all select, update, delete queries are parametrized. But it does not restrict you from using your own queries which might not be paramerized with methods like `select.custom()` and `where.custom()`.
72
+ It's a tiny little modern ORM-like that lets you prototype your databases locally with great flexibility. Also, it can be used in production apps to store and retrieve data, because all select, update, delete queries are parametrized. But it does not restrict you from using your own queries which might not be parameterized with methods like `select.custom()` and `where.custom()`. Flexibility is a go to for this library.
78
73
 
79
- And last (but not least) is data inspection. If you need to quickly inspect existing .db file but don't want to install yet another heavy ORM with a lot of unused dependencies, you might look into Sql-Engine, as it uses only native python modules.
74
+ And last (but not least) is data inspection. If you need to quickly inspect existing .db file but don't want to install yet another heavy ORM with a lot of unused dependencies and features, you might look into SqlEngine, as it uses only native python modules, implements dynamic schema builder and has a good synergy with Jupyter Notebook.
80
75
 
81
76
 
82
77
  ## Installation
83
78
 
84
- To install `sqlengine`, you can use `pip`:
79
+ To install SqlEngine, you can use `pip`:
85
80
 
86
- ```sh
81
+ ```bash
87
82
  pip install sqlengine-lite
88
83
  ```
89
84
 
90
85
  ## Env
91
86
 
92
- You may set environment variable for logging. By default it's `WARNING`.
87
+ You can set environment variable for logging. By default it's `WARNING`.
93
88
 
94
89
  ```console
95
90
  SQL_ENGINE_LOG_LEVEL=INFO
96
91
  ```
97
92
 
98
- # Quick Start
99
-
100
- All you have to do to create your own cute little table is to [inherit](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#class-declaration) `SqlTableMixin` class or to create your own [schema](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#schema-declaration) and declare desired properties of your table's columns. They are:
101
93
 
94
+ # Quick Start
102
95
 
103
- _Name of the table that will be used in queries. If omitted in inherited class declaration, then it will take the class name._
104
- ```py
105
- __tablename__ : Optional[str]
106
- ```
107
-
108
- _Column names of the table_
109
- ```py
110
- __columns__ : list[str]
111
- ```
112
-
113
- _Column types of the table_
114
- ```py
115
- __types__ : list[SqlType | str]
116
- ```
117
-
118
- _List of primary keys_
119
- ```py
120
- __primary__ : list[str]
121
- ```
96
+ Here lays everything you need to know to start working with SqlEngine. For detailed usage, API reference, and advanced examples, see the [full documentation](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/index.md).
122
97
 
123
98
  ## Table Declaration
124
99
 
125
- More details at [Declaration](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#table-declaration).
100
+ All you have to do to create your own cute little table is to [inherit](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#class-declaration) `SqlTableMixin` class or to create your own [schema](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#schema-declaration) and declare desired types of your table's columns.
101
+
126
102
 
127
103
  ```py
128
104
  from sqlengine import SqlTableMixin, Primary
129
105
 
130
- # Helper constants for column names
131
- ID = "ID"
132
- Name = "Name"
133
- Occupation = "Occupation"
134
- Salary = "Salary"
135
-
136
-
137
106
  class Employees(SqlTableMixin):
138
107
 
139
108
  ID : Primary[int]
@@ -143,35 +112,35 @@ class Employees(SqlTableMixin):
143
112
 
144
113
  ```
145
114
 
115
+ More details at [Declaration](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#table-declaration).
116
+
146
117
  ## Instantiation
147
118
 
148
- More details at [Instantiation](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#table-instantiation).
119
+ Create an instance of the table class with provided path to create or connect to. `force_drop=True` to overwrite existing table if it exists.
149
120
 
150
121
  ```py
151
- # Create an instance of the table class
152
- # with provided path to create or connect to
153
- # `force_drop=True` to overwrite existing table if exists
154
-
155
122
  table = Employees("temp/data.db", force_drop=True)
156
123
  ```
157
124
 
125
+ More details at [Instantiation](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#table-instantiation).
126
+
158
127
  ## Row insertion
159
128
 
160
- ```py
161
- # Insert one row
129
+ Insert one row in a `*args` style.
162
130
 
131
+ ```py
163
132
  table.insert(1, "John Doe", "Software Engineer", 75000.0)
164
133
  ```
165
134
 
166
- ```py
167
- # Use kwargs mapping to insert/upsert one row
135
+ Use **kwargs mapping** to insert/upsert one row
168
136
 
137
+ ```py
169
138
  table.insert(2, salary=80000.0, name="Jane Smith", occupation="Data Scientist")
170
139
  ```
171
140
 
172
- ```py
173
- # Bulk insert multiple rows
141
+ Bulk insert multiple rows.
174
142
 
143
+ ```py
175
144
  employees_data = [
176
145
  (3, "Alice Johnson", "Product Manager", 90000.0),
177
146
  (4, "Bob Brown", "Project Manager", 78000.0),
@@ -187,17 +156,17 @@ table.insert_many(employees_data)
187
156
  ```
188
157
 
189
158
 
190
- ```py
191
- # Upsert one row
159
+ Upsert one row.
192
160
 
161
+ ```py
193
162
  table.upsert(1, "Jane Doe", "Data Scientist", 80000.0)
194
163
  ```
195
164
 
196
165
  ## Jupyter view
197
166
 
198
- ```py
199
- # Inspect tables in Jupyter Notebook
167
+ Inspect table in Jupyter Notebook.
200
168
 
169
+ ```py
201
170
  table
202
171
  ```
203
172
 
@@ -205,11 +174,18 @@ table
205
174
 
206
175
  ## Select Query
207
176
 
208
- More details at [Statements](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/statements.md).
177
+ Helper constants for column names.
209
178
 
210
179
  ```py
211
- # Query select and fetch
180
+ ID = "ID"
181
+ Name = "Name"
182
+ Occupation = "Occupation"
183
+ Salary = "Salary"
184
+ ```
212
185
 
186
+ Query **select** and **fetch** in one go.
187
+
188
+ ```py
213
189
  table.select.where.between(ID, 3, 5).then.fetchall()
214
190
 
215
191
  # ->
@@ -218,9 +194,24 @@ table.select.where.between(ID, 3, 5).then.fetchall()
218
194
  # (5, 'Charlie Davis', 'UI/UX Designer', 65000.0)]
219
195
  ```
220
196
 
197
+ **Iterate** over select statements.
198
+
221
199
  ```py
222
- # Inspect query select in Jupyter
200
+ with table.transaction():
201
+ for id, occupation in table.select(ID, Occupation).where.gt(Salary, 70_000):
202
+ print(id, occupation)
203
+ ```
223
204
 
205
+ ```console
206
+ 1 Data Scientist
207
+ 3 Product Manager
208
+ 4 Project Manager
209
+ 6 DevOps Engineer
210
+ ```
211
+
212
+ Inspect select query in Jupyter.
213
+
214
+ ```py
224
215
  table.select(Name, Salary).where.lt(Salary, 70_000)
225
216
  ```
226
217
 
@@ -239,33 +230,32 @@ table.update(Salary, 50_000).where.eq(Name, "Eve Taylor").then.execute()
239
230
  table.delete.where.eq(ID, 5).then.execute()
240
231
  ```
241
232
 
233
+ More details at [Statements](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/statements.md).
234
+
242
235
  ## Transaction
243
236
 
244
- More details at [Transaction](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/transactions.md).
237
+ Operate within a transaction
245
238
 
246
239
  ```py
247
- # Operate within a transaction
248
240
 
249
241
  with table.transaction():
250
242
  for row in employees_data:
251
243
  table.upsert(*row)
252
244
  ```
253
245
 
246
+ More details at [Transaction](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/transactions.md).
247
+
254
248
  ## Get Item
255
249
 
256
- More details at [Syntax Sugar](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/syntax_sugar.md).
250
+ Fetch row by primary key.
257
251
 
258
252
  ```py
259
- # Fetch row by primary key
260
-
261
- table[9]
262
-
263
- # -> (9, 'Grace Hall', 'Marketing Manager', 68000.0)
253
+ table[9] # -> (9, 'Grace Hall', 'Marketing Manager', 68000.0)
264
254
  ```
265
255
 
266
- ```py
267
- # Fetch slice by integer primary key
256
+ Fetch slice by integer primary key.
268
257
 
258
+ ```py
269
259
  table[4:10:2]
270
260
 
271
261
  # ->
@@ -274,9 +264,11 @@ table[4:10:2]
274
264
  # (8, 'Frank White', 'Quality Assurance', 53000.0)]
275
265
  ```
276
266
 
267
+ More details at [Syntax Sugar](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/syntax_sugar.md).
268
+
277
269
  ## Custom Types
278
270
 
279
- More details at [Custom Types](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/custom_types.md).
271
+ Declare your own non-native SQL type to be compatible with sqlite3.
280
272
 
281
273
  ```py
282
274
  from datetime import datetime
@@ -305,36 +297,68 @@ class ReservationIndex(SqlTableMixin):
305
297
 
306
298
  table = ReservationIndex("temp/data.db")
307
299
 
308
- table.insert(1, "loft_1", DateTime.now(), None)
300
+ table.insert(
301
+ user_id=1,
302
+ room_id="loft_1",
303
+ time_at=DateTime.now()
304
+ )
309
305
  table
310
306
  ```
311
307
 
312
308
  <table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">ReservationIndex</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">user_id</td><td style="border: 1px solid #555; text-align: center;">room_id</td><td style="border: 1px solid #555; text-align: center;">time_at</td><td style="border: 1px solid #555; text-align: center;">user_name</td></tr></thead><tbody><tr><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">loft_1</td><td style="border: 1px solid #000; text-align: center;">2026-05-29 21:29:00</td><td style="border: 1px solid #000; text-align: center;">None</td></tr></tbody></table>
313
309
 
310
+ More details at [Custom Types](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/custom_types.md).
311
+
312
+ ## Pure SQL
313
+
314
+ Use SQL queries directly.
315
+
316
+ ```py
317
+ with table.transaction(autocommit=False):
318
+ table.conn.execute("DELETE FROM ReservationIndex WHERE user_id = 1;")
319
+ table.commit()
320
+ ```
321
+
322
+ Same as:
323
+
324
+ ```py
325
+ table.conn.execute("DELETE FROM ReservationIndex WHERE user_id = 1;")
326
+ ```
327
+
328
+ ```py
329
+ table
330
+ ```
331
+
332
+ <table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">ReservationIndex</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">user_id</td><td style="border: 1px solid #555; text-align: center;">room_id</td><td style="border: 1px solid #555; text-align: center;">time_at</td><td style="border: 1px solid #555; text-align: center;">user_name</td></tr></thead><tbody></tbody></table>
333
+
314
334
  ## Csv Converter
315
335
 
336
+ Save table to csv.
337
+
316
338
  ```py
317
- # Save table to csv
318
339
  from sqlengine.utils import to_csv
319
340
 
320
341
  to_csv(table, "temp/table.csv")
321
342
  ```
322
343
 
344
+ Save query result to csv.
345
+
323
346
  ```py
324
- # Save query result to csv
325
347
  to_csv(table.select.where.gt(Salary, 70_000), "temp/query.csv")
326
348
  ```
327
349
 
350
+ Stream big tables or query results to csv.
351
+
328
352
  ```py
329
- # Stream to csv
330
353
  with table.transaction():
331
354
  to_csv(table, "temp/query.csv", stream_batch_size=1000)
332
355
  ```
333
356
 
334
357
  ## Pandas-like Converter
335
358
 
359
+ Convert tables or query results to pandas **DataFrame** in one shot.
360
+
336
361
  ```py
337
- # via one shot
338
362
  import pandas as pd
339
363
  from sqlengine.utils import to_dicts
340
364
 
@@ -342,8 +366,9 @@ df = pd.DataFrame(to_dicts(table))
342
366
  df.set_index("ID", inplace=True)
343
367
  ```
344
368
 
369
+ Or stream them via generator.
370
+
345
371
  ```py
346
- # via generator
347
372
  import pandas as pd
348
373
  from sqlengine.utils import to_dicts_stream
349
374
 
@@ -356,6 +381,34 @@ with table.transaction():
356
381
  df.set_index("ID", inplace=True)
357
382
  ```
358
383
 
359
- # Full Documentation
384
+ # Contributions
385
+
386
+ Your impact is welcome. Install module from source if you want to contribute:
387
+
388
+ ```bash
389
+ git clone https://github.com/suffermuffin/SQL-Engine.git
390
+ cd SQL-Engine
391
+ ```
392
+
393
+ Use `uv` to sync dependencies and checkout to your new branch:
394
+
395
+ ```bash
396
+ uv sync
397
+ git checkout -b "<your_feature_or_fix_name>"
398
+ ```
399
+
400
+ Don't forget to run tests after the implementation:
401
+
402
+ ```bash
403
+ uv run python -m unittest discover -s tests
404
+ ```
405
+
406
+ And update api documentation with your docstrings:
407
+
408
+ ```bash
409
+ pydoc-markdown
410
+ ```
411
+
412
+ # License
360
413
 
361
- For detailed usage, API reference, and advanced examples, see the [full documentation](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/index.md).
414
+ This project is licensed under the terms of the [MIT license](https://github.com/suffermuffin/SQL-Engine/blob/main/LICENSE).
@@ -0,0 +1,18 @@
1
+ sqlengine/__init__.py,sha256=_t3mutixVp60g1MwD6BZ6WcvXZPFz7JkV29nXXl1bOw,305
2
+ sqlengine/exceptions.py,sha256=Sd12r8dHgtKr6RKUaiHNhaEpdJr3Jr-JXePwrBQojC4,506
3
+ sqlengine/schema.py,sha256=SL-7eXib56wIVHSgykqMJKZGRPJuOffpwA0GJEgChSc,3593
4
+ sqlengine/sqltable.py,sha256=ZgkPQi5L1IGHauamTSjqyKVGNMKHCKmt3tEGTI-pCnY,16197
5
+ sqlengine/_internal/__init__.py,sha256=9cQIPDgLssRJNeROpuDYA0rJzY9B_N9VsY4rJbgzBnk,159
6
+ sqlengine/_internal/connection_manager.py,sha256=j4H7BiPu7iUxqOSRMmApgGnCY9dRBdL4javwMwAi_BU,8522
7
+ sqlengine/_internal/repr.py,sha256=PEwC3MdOoTE8ORvcVXTCmY502boJfr5V2LU5jJ6qWG0,1478
8
+ sqlengine/_internal/sqlgen.py,sha256=b8usEqBAX5JDEdMR6Xeaona0ZuOi7vtptl858qXhD78,3473
9
+ sqlengine/_internal/statements.py,sha256=8D6uZCwm4iMwfq0Lzg5-Z91x68xN874xQbHyZyFPBKE,12876
10
+ sqlengine/_internal/types.py,sha256=bA-tsS8r13pvRB3M13wFNsd3Fc9KAaqGW47aXhkKkDQ,3265
11
+ sqlengine/utils/__init__.py,sha256=EGg576qs61VsXlXd1bxF0oDytlmpcgqouVLdPxwqTrI,173
12
+ sqlengine/utils/connection.py,sha256=wnA21QCUd7meRHL114ZzghchNIgvmq-cEmIJ-8L_ozQ,3118
13
+ sqlengine/utils/convert.py,sha256=8bUNdxNNNkBnUIcTNkLGqA9h_X-D8BHtssHhe0UMPx0,3782
14
+ sqlengine_lite-2.2.2.dist-info/licenses/LICENSE,sha256=aepvve4t1ho5uHMQG88dErp5L5CgoNAXctIjMdoceqY,1069
15
+ sqlengine_lite-2.2.2.dist-info/METADATA,sha256=2WfgmOiu-lq7b6fBamCTtVhuVmYNHwCDuujYz1H9WL0,22586
16
+ sqlengine_lite-2.2.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
17
+ sqlengine_lite-2.2.2.dist-info/top_level.txt,sha256=KG_FG0LCB_mIEu2qJ5LGLQMpan4QmF5d8xLEcqTghCE,10
18
+ sqlengine_lite-2.2.2.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- sqlengine/__init__.py,sha256=_t3mutixVp60g1MwD6BZ6WcvXZPFz7JkV29nXXl1bOw,305
2
- sqlengine/exceptions.py,sha256=Sd12r8dHgtKr6RKUaiHNhaEpdJr3Jr-JXePwrBQojC4,506
3
- sqlengine/schema.py,sha256=HUk11gqOSzQTdiKBYU65EEQ52iCbUj3fxv3jigi1tG8,3692
4
- sqlengine/sqltable.py,sha256=FOwXyTghcIxF8vhxMgzFeF6wAZNRjVt6r7Ebhqs-iJ0,16198
5
- sqlengine/_internal/__init__.py,sha256=9cQIPDgLssRJNeROpuDYA0rJzY9B_N9VsY4rJbgzBnk,159
6
- sqlengine/_internal/connection_manager.py,sha256=2ooDsAUvSfXTBpUJtNDS2GIO96tJo1GnE_R7dRBVflY,8466
7
- sqlengine/_internal/repr.py,sha256=PEwC3MdOoTE8ORvcVXTCmY502boJfr5V2LU5jJ6qWG0,1478
8
- sqlengine/_internal/sqlgen.py,sha256=b8usEqBAX5JDEdMR6Xeaona0ZuOi7vtptl858qXhD78,3473
9
- sqlengine/_internal/statements.py,sha256=ntJrOeomPW15KowXUbnma9_ah-H5_ZmWKjemCIsf3Rw,12395
10
- sqlengine/_internal/types.py,sha256=bA-tsS8r13pvRB3M13wFNsd3Fc9KAaqGW47aXhkKkDQ,3265
11
- sqlengine/utils/__init__.py,sha256=EGg576qs61VsXlXd1bxF0oDytlmpcgqouVLdPxwqTrI,173
12
- sqlengine/utils/connection.py,sha256=H86Y2PUJcTIc9iD398MJoRR-IyMYSVjOSfu_bk9UuoM,3119
13
- sqlengine/utils/convert.py,sha256=Il5KAbc0Mbe2SIxBWyvj9rJ68XTJmpDFboJc1vifaaI,3694
14
- sqlengine_lite-2.2.1.dist-info/licenses/LICENSE,sha256=aepvve4t1ho5uHMQG88dErp5L5CgoNAXctIjMdoceqY,1069
15
- sqlengine_lite-2.2.1.dist-info/METADATA,sha256=XAAXT6IKmRIUkYtVkqij11U1W5UMZNwjfGg9sgbT81w,20593
16
- sqlengine_lite-2.2.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
17
- sqlengine_lite-2.2.1.dist-info/top_level.txt,sha256=KG_FG0LCB_mIEu2qJ5LGLQMpan4QmF5d8xLEcqTghCE,10
18
- sqlengine_lite-2.2.1.dist-info/RECORD,,