sqliter-py 0.12.0__py3-none-any.whl → 0.16.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.
Files changed (41) hide show
  1. sqliter/constants.py +4 -3
  2. sqliter/exceptions.py +13 -0
  3. sqliter/model/model.py +42 -3
  4. sqliter/orm/__init__.py +16 -0
  5. sqliter/orm/fields.py +412 -0
  6. sqliter/orm/foreign_key.py +8 -0
  7. sqliter/orm/model.py +243 -0
  8. sqliter/orm/query.py +221 -0
  9. sqliter/orm/registry.py +169 -0
  10. sqliter/query/query.py +573 -51
  11. sqliter/sqliter.py +141 -47
  12. sqliter/tui/__init__.py +62 -0
  13. sqliter/tui/__main__.py +6 -0
  14. sqliter/tui/app.py +179 -0
  15. sqliter/tui/demos/__init__.py +96 -0
  16. sqliter/tui/demos/base.py +114 -0
  17. sqliter/tui/demos/caching.py +283 -0
  18. sqliter/tui/demos/connection.py +150 -0
  19. sqliter/tui/demos/constraints.py +211 -0
  20. sqliter/tui/demos/crud.py +154 -0
  21. sqliter/tui/demos/errors.py +231 -0
  22. sqliter/tui/demos/field_selection.py +150 -0
  23. sqliter/tui/demos/filters.py +389 -0
  24. sqliter/tui/demos/models.py +248 -0
  25. sqliter/tui/demos/ordering.py +156 -0
  26. sqliter/tui/demos/orm.py +460 -0
  27. sqliter/tui/demos/results.py +241 -0
  28. sqliter/tui/demos/string_filters.py +210 -0
  29. sqliter/tui/demos/timestamps.py +126 -0
  30. sqliter/tui/demos/transactions.py +177 -0
  31. sqliter/tui/runner.py +116 -0
  32. sqliter/tui/styles/app.tcss +130 -0
  33. sqliter/tui/widgets/__init__.py +7 -0
  34. sqliter/tui/widgets/code_display.py +81 -0
  35. sqliter/tui/widgets/demo_list.py +65 -0
  36. sqliter/tui/widgets/output_display.py +92 -0
  37. {sqliter_py-0.12.0.dist-info → sqliter_py-0.16.0.dist-info}/METADATA +23 -7
  38. sqliter_py-0.16.0.dist-info/RECORD +47 -0
  39. {sqliter_py-0.12.0.dist-info → sqliter_py-0.16.0.dist-info}/WHEEL +2 -2
  40. sqliter_py-0.16.0.dist-info/entry_points.txt +3 -0
  41. sqliter_py-0.12.0.dist-info/RECORD +0 -15
@@ -0,0 +1,150 @@
1
+ """Field Selection demos."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import io
6
+
7
+ from sqliter import SqliterDB
8
+ from sqliter.model import BaseDBModel
9
+ from sqliter.tui.demos.base import Demo, DemoCategory, extract_demo_code
10
+
11
+
12
+ def _run_select_fields() -> str:
13
+ """Select specific fields from a query to reduce data transfer.
14
+
15
+ Use fields() to fetch only the columns you need, leaving unspecified
16
+ fields as None.
17
+ """
18
+ output = io.StringIO()
19
+
20
+ class User(BaseDBModel):
21
+ name: str
22
+ email: str
23
+ age: int
24
+ city: str
25
+
26
+ db = SqliterDB(memory=True)
27
+ db.create_table(User)
28
+
29
+ db.insert(User(name="Alice", email="alice@example.com", age=30, city="NYC"))
30
+ db.insert(User(name="Bob", email="bob@example.com", age=25, city="LA"))
31
+
32
+ # Select only name and email
33
+ users = db.select(User).fields(["name", "email"]).fetch_all()
34
+ output.write("Selected only name and email fields:\n")
35
+ for user in users:
36
+ output.write(f" - {user.name}, {user.email}\n")
37
+
38
+ # Note: age and city are None since they weren't selected
39
+ output.write("(age and city not selected, set to None)\n")
40
+
41
+ db.close()
42
+ return output.getvalue()
43
+
44
+
45
+ def _run_exclude_fields() -> str:
46
+ """Exclude specific fields from query results.
47
+
48
+ Use exclude() to fetch all fields except the ones you specify,
49
+ useful for hiding large or sensitive fields.
50
+ """
51
+ output = io.StringIO()
52
+
53
+ class Product(BaseDBModel):
54
+ name: str
55
+ price: float
56
+ description: str
57
+ stock: int
58
+
59
+ db = SqliterDB(memory=True)
60
+ db.create_table(Product)
61
+
62
+ db.insert(
63
+ Product(
64
+ name="Laptop",
65
+ price=999.99,
66
+ description="Fast laptop",
67
+ stock=10,
68
+ )
69
+ )
70
+
71
+ # Exclude description and stock
72
+ product = db.select(Product).exclude(["description", "stock"]).fetch_one()
73
+ if product is not None:
74
+ output.write(f"Product: {product.name}\n")
75
+ output.write(f"Price: ${product.price}\n")
76
+ output.write("(description and stock excluded)\n")
77
+
78
+ db.close()
79
+ return output.getvalue()
80
+
81
+
82
+ def _run_only_field() -> str:
83
+ """Select a single field from query results.
84
+
85
+ Use only() when you only need one specific field from your query,
86
+ useful for getting IDs or names.
87
+ """
88
+ output = io.StringIO()
89
+
90
+ class Task(BaseDBModel):
91
+ title: str
92
+ status: str
93
+ priority: int
94
+ assigned_to: str
95
+
96
+ db = SqliterDB(memory=True)
97
+ db.create_table(Task)
98
+
99
+ db.insert(
100
+ Task(title="Fix bug", status="todo", priority=1, assigned_to="Alice")
101
+ )
102
+ db.insert(
103
+ Task(title="Add feature", status="done", priority=2, assigned_to="Bob")
104
+ )
105
+
106
+ # Select only the title field
107
+ tasks = db.select(Task).only("title").fetch_all()
108
+ output.write("Selected only title field:\n")
109
+ for task in tasks:
110
+ output.write(f" - {task.title}\n")
111
+
112
+ output.write("(status, priority, assigned_to not selected)\n")
113
+
114
+ db.close()
115
+ return output.getvalue()
116
+
117
+
118
+ def get_category() -> DemoCategory:
119
+ """Get the Field Selection demo category."""
120
+ return DemoCategory(
121
+ id="field_selection",
122
+ title="Field Selection",
123
+ icon="",
124
+ demos=[
125
+ Demo(
126
+ id="field_select",
127
+ title="Select Fields",
128
+ description="Choose specific fields to fetch",
129
+ category="field_selection",
130
+ code=extract_demo_code(_run_select_fields),
131
+ execute=_run_select_fields,
132
+ ),
133
+ Demo(
134
+ id="field_exclude",
135
+ title="Exclude Fields",
136
+ description="Exclude specific fields from results",
137
+ category="field_selection",
138
+ code=extract_demo_code(_run_exclude_fields),
139
+ execute=_run_exclude_fields,
140
+ ),
141
+ Demo(
142
+ id="field_only",
143
+ title="Select Single Field",
144
+ description="Fetch only one specific field",
145
+ category="field_selection",
146
+ code=extract_demo_code(_run_only_field),
147
+ execute=_run_only_field,
148
+ ),
149
+ ],
150
+ )
@@ -0,0 +1,389 @@
1
+ """Query Builder filter demos."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import io
6
+ from typing import Optional
7
+
8
+ from sqliter import SqliterDB
9
+ from sqliter.model import BaseDBModel
10
+ from sqliter.tui.demos.base import Demo, DemoCategory, extract_demo_code
11
+
12
+
13
+ def _run_equals() -> str:
14
+ """Filter records where a field exactly matches a value.
15
+
16
+ Use __eq to find records with the specified exact value.
17
+ """
18
+ output = io.StringIO()
19
+
20
+ class User(BaseDBModel):
21
+ name: str
22
+ age: int
23
+
24
+ db = SqliterDB(memory=True)
25
+ db.create_table(User)
26
+
27
+ db.insert(User(name="Alice", age=30))
28
+ db.insert(User(name="Bob", age=25))
29
+ db.insert(User(name="Alice", age=35))
30
+
31
+ results = db.select(User).filter(name__eq="Alice").fetch_all()
32
+ output.write(f"Found {len(results)} users named 'Alice':\n")
33
+ for user in results:
34
+ output.write(f" - {user.name}, age {user.age}\n")
35
+
36
+ db.close()
37
+ return output.getvalue()
38
+
39
+
40
+ def _run_comparison() -> str:
41
+ """Filter records using comparison operators.
42
+
43
+ Use __gt, __lt, __gte, __lte for greater/less than filtering.
44
+ """
45
+ output = io.StringIO()
46
+
47
+ class Product(BaseDBModel):
48
+ name: str
49
+ price: float
50
+
51
+ db = SqliterDB(memory=True)
52
+ db.create_table(Product)
53
+
54
+ db.insert(Product(name="Item A", price=10.0))
55
+ db.insert(Product(name="Item B", price=20.0))
56
+ db.insert(Product(name="Item C", price=30.0))
57
+
58
+ # Greater than
59
+ expensive = db.select(Product).filter(price__gt=15.0).fetch_all()
60
+ output.write(f"Products > $15: {len(expensive)}\n")
61
+
62
+ # Less than or equal
63
+ cheap = db.select(Product).filter(price__lte=20.0).fetch_all()
64
+ output.write(f"Products <= $20: {len(cheap)}\n")
65
+
66
+ db.close()
67
+ return output.getvalue()
68
+
69
+
70
+ def _run_in_operator() -> str:
71
+ """Filter records matching any value in a list.
72
+
73
+ Use __in to match records against multiple possible values.
74
+ """
75
+ output = io.StringIO()
76
+
77
+ class Task(BaseDBModel):
78
+ title: str
79
+ status: str
80
+
81
+ db = SqliterDB(memory=True)
82
+ db.create_table(Task)
83
+
84
+ db.insert(Task(title="Task 1", status="todo"))
85
+ db.insert(Task(title="Task 2", status="done"))
86
+ db.insert(Task(title="Task 3", status="in_progress"))
87
+ db.insert(Task(title="Task 4", status="done"))
88
+
89
+ results = (
90
+ db.select(Task).filter(status__in=["todo", "in_progress"]).fetch_all()
91
+ )
92
+ output.write(f"Active tasks: {len(results)}\n")
93
+ for task in results:
94
+ output.write(f" - {task.title}: {task.status}\n")
95
+
96
+ db.close()
97
+ return output.getvalue()
98
+
99
+
100
+ def _run_like_operator() -> str:
101
+ """Filter strings using SQL LIKE pattern matching.
102
+
103
+ Use __like with % wildcards for flexible string matching.
104
+ """
105
+ output = io.StringIO()
106
+
107
+ class File(BaseDBModel):
108
+ name: str
109
+
110
+ db = SqliterDB(memory=True)
111
+ db.create_table(File)
112
+
113
+ db.insert(File(name="document.txt"))
114
+ db.insert(File(name="image.png"))
115
+ db.insert(File(name="data.csv"))
116
+ db.insert(File(name="notes.txt"))
117
+
118
+ results = db.select(File).filter(name__like="%.txt").fetch_all()
119
+ output.write(f"Text files: {len(results)}\n")
120
+ for file in results:
121
+ output.write(f" - {file.name}\n")
122
+
123
+ db.close()
124
+ return output.getvalue()
125
+
126
+
127
+ def _run_not_equals() -> str:
128
+ """Filter records that don't match a specific value.
129
+
130
+ Use __ne to exclude records with the specified value.
131
+ """
132
+ output = io.StringIO()
133
+
134
+ class Item(BaseDBModel):
135
+ name: str
136
+ status: str
137
+
138
+ db = SqliterDB(memory=True)
139
+ db.create_table(Item)
140
+
141
+ db.insert(Item(name="Item 1", status="active"))
142
+ db.insert(Item(name="Item 2", status="archived"))
143
+ db.insert(Item(name="Item 3", status="active"))
144
+
145
+ results = db.select(Item).filter(status__ne="archived").fetch_all()
146
+ output.write(f"Non-archived items: {len(results)}\n")
147
+
148
+ db.close()
149
+ return output.getvalue()
150
+
151
+
152
+ def _run_multiple_filters() -> str:
153
+ """Chain multiple filters for complex queries.
154
+
155
+ Combine multiple filter() calls to narrow results with AND logic.
156
+ """
157
+ output = io.StringIO()
158
+
159
+ class User(BaseDBModel):
160
+ name: str
161
+ age: int
162
+ city: str
163
+
164
+ db = SqliterDB(memory=True)
165
+ db.create_table(User)
166
+
167
+ db.insert(User(name="Alice", age=30, city="NYC"))
168
+ db.insert(User(name="Bob", age=25, city="LA"))
169
+ db.insert(User(name="Charlie", age=30, city="NYC"))
170
+
171
+ results = (
172
+ db.select(User).filter(age__gte=30).filter(city__eq="NYC").fetch_all()
173
+ )
174
+ output.write(f"Users in NYC aged 30+: {len(results)}\n")
175
+ for user in results:
176
+ output.write(f" - {user.name}, {user.age}\n")
177
+
178
+ db.close()
179
+ return output.getvalue()
180
+
181
+
182
+ def _run_range_filters() -> str:
183
+ """Filter records within a specific value range.
184
+
185
+ Combine __gte and __lte to find records in a range.
186
+ """
187
+ output = io.StringIO()
188
+
189
+ class Product(BaseDBModel):
190
+ name: str
191
+ price: float
192
+
193
+ db = SqliterDB(memory=True)
194
+ db.create_table(Product)
195
+
196
+ for i in range(1, 11):
197
+ db.insert(Product(name=f"Product {i}", price=float(i * 10)))
198
+
199
+ results = (
200
+ db.select(Product)
201
+ .filter(price__gte=30.0)
202
+ .filter(price__lte=70.0)
203
+ .fetch_all()
204
+ )
205
+ output.write(f"Products $30-$70: {len(results)}\n")
206
+
207
+ db.close()
208
+ return output.getvalue()
209
+
210
+
211
+ def _run_combined_operators() -> str:
212
+ """Combine different filter types for precise queries.
213
+
214
+ Mix equality, comparison, and other operators in a single query.
215
+ """
216
+ output = io.StringIO()
217
+
218
+ class Order(BaseDBModel):
219
+ id: str
220
+ amount: float
221
+ status: str
222
+
223
+ db = SqliterDB(memory=True)
224
+ db.create_table(Order)
225
+
226
+ db.insert(Order(id="001", amount=100.0, status="pending"))
227
+ db.insert(Order(id="002", amount=250.0, status="completed"))
228
+ db.insert(Order(id="003", amount=50.0, status="pending"))
229
+ db.insert(Order(id="004", amount=300.0, status="completed"))
230
+
231
+ results = (
232
+ db.select(Order)
233
+ .filter(status__eq="pending")
234
+ .filter(amount__gt=50.0)
235
+ .fetch_all()
236
+ )
237
+ output.write(f"Pending orders > $50: {len(results)}\n")
238
+
239
+ db.close()
240
+ return output.getvalue()
241
+
242
+
243
+ def _run_isnull() -> str:
244
+ """Find records with null (empty) field values.
245
+
246
+ Use __isnull=True to find records where a field is None.
247
+ """
248
+ output = io.StringIO()
249
+
250
+ class Task(BaseDBModel):
251
+ title: str
252
+ assigned_to: Optional[str] = None
253
+
254
+ db = SqliterDB(memory=True)
255
+ db.create_table(Task)
256
+
257
+ db.insert(Task(title="Task 1", assigned_to="Alice"))
258
+ db.insert(Task(title="Task 2", assigned_to=None)) # Unassigned
259
+ db.insert(Task(title="Task 3", assigned_to="Bob"))
260
+ db.insert(Task(title="Task 4", assigned_to=None)) # Unassigned
261
+
262
+ # Find unassigned tasks
263
+ unassigned = db.select(Task).filter(assigned_to__isnull=True).fetch_all()
264
+ output.write(f"Unassigned tasks: {len(unassigned)}\n")
265
+ for task in unassigned:
266
+ output.write(f" - {task.title}\n")
267
+
268
+ db.close()
269
+ return output.getvalue()
270
+
271
+
272
+ def _run_notnull() -> str:
273
+ """Find records without null (empty) field values.
274
+
275
+ Use __notnull=True to find records where a field has a value.
276
+ """
277
+ output = io.StringIO()
278
+
279
+ class Task(BaseDBModel):
280
+ title: str
281
+ assigned_to: Optional[str] = None
282
+
283
+ db = SqliterDB(memory=True)
284
+ db.create_table(Task)
285
+
286
+ db.insert(Task(title="Task 1", assigned_to="Alice"))
287
+ db.insert(Task(title="Task 2", assigned_to=None))
288
+ db.insert(Task(title="Task 3", assigned_to="Bob"))
289
+ db.insert(Task(title="Task 4", assigned_to=None))
290
+
291
+ # Find assigned tasks
292
+ assigned = db.select(Task).filter(assigned_to__notnull=True).fetch_all()
293
+ output.write(f"Assigned tasks: {len(assigned)}\n")
294
+ for task in assigned:
295
+ output.write(f" - {task.title}: {task.assigned_to}\n")
296
+
297
+ db.close()
298
+ return output.getvalue()
299
+
300
+
301
+ def get_category() -> DemoCategory:
302
+ """Get the Query Filters demo category."""
303
+ return DemoCategory(
304
+ id="filters",
305
+ title="Query Filters",
306
+ icon="",
307
+ demos=[
308
+ Demo(
309
+ id="filter_eq",
310
+ title="Equals (__eq)",
311
+ description="Exact match filter",
312
+ category="filters",
313
+ code=extract_demo_code(_run_equals),
314
+ execute=_run_equals,
315
+ ),
316
+ Demo(
317
+ id="filter_comparison",
318
+ title="Comparison Operators",
319
+ description="__gt, __lt, __gte, __lte (less/greater than)",
320
+ category="filters",
321
+ code=extract_demo_code(_run_comparison),
322
+ execute=_run_comparison,
323
+ ),
324
+ Demo(
325
+ id="filter_in",
326
+ title="IN Operator (__in)",
327
+ description="Match against list of values",
328
+ category="filters",
329
+ code=extract_demo_code(_run_in_operator),
330
+ execute=_run_in_operator,
331
+ ),
332
+ Demo(
333
+ id="filter_like",
334
+ title="LIKE Operator (__like)",
335
+ description="Pattern matching with wildcards",
336
+ category="filters",
337
+ code=extract_demo_code(_run_like_operator),
338
+ execute=_run_like_operator,
339
+ ),
340
+ Demo(
341
+ id="filter_ne",
342
+ title="Not Equals (__ne)",
343
+ description="Exclude specific values",
344
+ category="filters",
345
+ code=extract_demo_code(_run_not_equals),
346
+ execute=_run_not_equals,
347
+ ),
348
+ Demo(
349
+ id="filter_multiple",
350
+ title="Multiple Filters",
351
+ description="Chain filters for AND logic",
352
+ category="filters",
353
+ code=extract_demo_code(_run_multiple_filters),
354
+ execute=_run_multiple_filters,
355
+ ),
356
+ Demo(
357
+ id="filter_range",
358
+ title="Range Queries",
359
+ description="Query within a value range",
360
+ category="filters",
361
+ code=extract_demo_code(_run_range_filters),
362
+ execute=_run_range_filters,
363
+ ),
364
+ Demo(
365
+ id="filter_combined",
366
+ title="Combined Operators",
367
+ description="Multiple filter types together",
368
+ category="filters",
369
+ code=extract_demo_code(_run_combined_operators),
370
+ execute=_run_combined_operators,
371
+ ),
372
+ Demo(
373
+ id="filter_isnull",
374
+ title="IS NULL (__isnull)",
375
+ description="Find records with null values",
376
+ category="filters",
377
+ code=extract_demo_code(_run_isnull),
378
+ execute=_run_isnull,
379
+ ),
380
+ Demo(
381
+ id="filter_notnull",
382
+ title="IS NOT NULL (__notnull)",
383
+ description="Find records without null values",
384
+ category="filters",
385
+ code=extract_demo_code(_run_notnull),
386
+ execute=_run_notnull,
387
+ ),
388
+ ],
389
+ )