piccolo 1.27.0__py3-none-any.whl → 1.28.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 (122) hide show
  1. piccolo/__init__.py +1 -1
  2. piccolo/apps/app/commands/new.py +3 -3
  3. piccolo/apps/asgi/commands/new.py +1 -2
  4. piccolo/apps/fixtures/commands/dump.py +8 -8
  5. piccolo/apps/fixtures/commands/load.py +5 -5
  6. piccolo/apps/fixtures/commands/shared.py +9 -9
  7. piccolo/apps/migrations/auto/diffable_table.py +12 -12
  8. piccolo/apps/migrations/auto/migration_manager.py +59 -66
  9. piccolo/apps/migrations/auto/operations.py +14 -14
  10. piccolo/apps/migrations/auto/schema_differ.py +35 -34
  11. piccolo/apps/migrations/auto/schema_snapshot.py +3 -4
  12. piccolo/apps/migrations/auto/serialisation.py +27 -24
  13. piccolo/apps/migrations/auto/serialisation_legacy.py +2 -2
  14. piccolo/apps/migrations/commands/backwards.py +1 -2
  15. piccolo/apps/migrations/commands/base.py +12 -12
  16. piccolo/apps/migrations/commands/check.py +2 -3
  17. piccolo/apps/migrations/commands/clean.py +3 -3
  18. piccolo/apps/migrations/commands/forwards.py +1 -2
  19. piccolo/apps/migrations/commands/new.py +6 -6
  20. piccolo/apps/migrations/tables.py +3 -3
  21. piccolo/apps/playground/commands/run.py +29 -13
  22. piccolo/apps/schema/commands/generate.py +49 -49
  23. piccolo/apps/schema/commands/graph.py +5 -5
  24. piccolo/apps/shell/commands/run.py +1 -2
  25. piccolo/apps/sql_shell/commands/run.py +4 -4
  26. piccolo/apps/tester/commands/run.py +3 -3
  27. piccolo/apps/user/commands/change_permissions.py +6 -6
  28. piccolo/apps/user/commands/create.py +7 -7
  29. piccolo/apps/user/commands/list.py +2 -2
  30. piccolo/apps/user/tables.py +8 -8
  31. piccolo/columns/base.py +84 -52
  32. piccolo/columns/choices.py +2 -2
  33. piccolo/columns/column_types.py +297 -175
  34. piccolo/columns/combination.py +15 -12
  35. piccolo/columns/defaults/base.py +4 -4
  36. piccolo/columns/defaults/date.py +4 -3
  37. piccolo/columns/defaults/interval.py +4 -3
  38. piccolo/columns/defaults/time.py +4 -3
  39. piccolo/columns/defaults/timestamp.py +4 -3
  40. piccolo/columns/defaults/timestamptz.py +4 -3
  41. piccolo/columns/defaults/uuid.py +3 -2
  42. piccolo/columns/m2m.py +28 -35
  43. piccolo/columns/readable.py +4 -3
  44. piccolo/columns/reference.py +9 -9
  45. piccolo/conf/apps.py +53 -54
  46. piccolo/custom_types.py +28 -6
  47. piccolo/engine/base.py +14 -14
  48. piccolo/engine/cockroach.py +5 -4
  49. piccolo/engine/finder.py +2 -2
  50. piccolo/engine/postgres.py +20 -19
  51. piccolo/engine/sqlite.py +23 -22
  52. piccolo/query/base.py +30 -29
  53. piccolo/query/functions/__init__.py +12 -0
  54. piccolo/query/functions/aggregate.py +4 -3
  55. piccolo/query/functions/array.py +151 -0
  56. piccolo/query/functions/base.py +3 -3
  57. piccolo/query/functions/datetime.py +22 -22
  58. piccolo/query/functions/string.py +4 -4
  59. piccolo/query/functions/type_conversion.py +30 -15
  60. piccolo/query/methods/alter.py +47 -46
  61. piccolo/query/methods/count.py +11 -10
  62. piccolo/query/methods/create.py +6 -5
  63. piccolo/query/methods/create_index.py +9 -8
  64. piccolo/query/methods/delete.py +7 -6
  65. piccolo/query/methods/drop_index.py +7 -6
  66. piccolo/query/methods/exists.py +6 -5
  67. piccolo/query/methods/indexes.py +4 -4
  68. piccolo/query/methods/insert.py +21 -14
  69. piccolo/query/methods/objects.py +60 -50
  70. piccolo/query/methods/raw.py +7 -6
  71. piccolo/query/methods/refresh.py +8 -7
  72. piccolo/query/methods/select.py +56 -49
  73. piccolo/query/methods/table_exists.py +5 -5
  74. piccolo/query/methods/update.py +8 -7
  75. piccolo/query/mixins.py +56 -61
  76. piccolo/query/operators/json.py +11 -11
  77. piccolo/query/proxy.py +8 -9
  78. piccolo/querystring.py +14 -15
  79. piccolo/schema.py +10 -10
  80. piccolo/table.py +93 -94
  81. piccolo/table_reflection.py +9 -9
  82. piccolo/testing/model_builder.py +12 -11
  83. piccolo/testing/random_builder.py +2 -2
  84. piccolo/testing/test_case.py +4 -4
  85. piccolo/utils/dictionary.py +3 -3
  86. piccolo/utils/encoding.py +5 -5
  87. piccolo/utils/lazy_loader.py +3 -3
  88. piccolo/utils/list.py +7 -8
  89. piccolo/utils/objects.py +4 -6
  90. piccolo/utils/pydantic.py +21 -24
  91. piccolo/utils/sql_values.py +3 -3
  92. piccolo/utils/sync.py +4 -3
  93. piccolo/utils/warnings.py +1 -2
  94. {piccolo-1.27.0.dist-info → piccolo-1.28.0.dist-info}/METADATA +1 -1
  95. {piccolo-1.27.0.dist-info → piccolo-1.28.0.dist-info}/RECORD +122 -121
  96. tests/apps/fixtures/commands/test_dump_load.py +1 -2
  97. tests/apps/migrations/auto/integration/test_migrations.py +32 -7
  98. tests/apps/migrations/auto/test_migration_manager.py +2 -2
  99. tests/apps/migrations/auto/test_schema_differ.py +22 -23
  100. tests/apps/migrations/commands/test_forwards_backwards.py +3 -3
  101. tests/columns/m2m/base.py +2 -2
  102. tests/columns/test_array.py +176 -10
  103. tests/columns/test_boolean.py +2 -4
  104. tests/columns/test_combination.py +29 -1
  105. tests/columns/test_db_column_name.py +2 -2
  106. tests/engine/test_extra_nodes.py +2 -2
  107. tests/engine/test_pool.py +3 -3
  108. tests/engine/test_transaction.py +4 -4
  109. tests/query/test_freeze.py +4 -4
  110. tests/table/instance/test_get_related.py +2 -2
  111. tests/table/test_alter.py +4 -4
  112. tests/table/test_indexes.py +1 -2
  113. tests/table/test_refresh.py +2 -2
  114. tests/table/test_select.py +58 -0
  115. tests/table/test_update.py +3 -3
  116. tests/testing/test_model_builder.py +1 -2
  117. tests/utils/test_pydantic.py +36 -36
  118. tests/utils/test_table_reflection.py +1 -2
  119. {piccolo-1.27.0.dist-info → piccolo-1.28.0.dist-info}/WHEEL +0 -0
  120. {piccolo-1.27.0.dist-info → piccolo-1.28.0.dist-info}/entry_points.txt +0 -0
  121. {piccolo-1.27.0.dist-info → piccolo-1.28.0.dist-info}/licenses/LICENSE +0 -0
  122. {piccolo-1.27.0.dist-info → piccolo-1.28.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
4
3
  from unittest import TestCase
5
4
  from unittest.mock import MagicMock, call, patch
6
5
 
@@ -24,12 +23,12 @@ class TestSchemaDiffer(TestCase):
24
23
  """
25
24
  name_column = Varchar()
26
25
  name_column._meta.name = "name"
27
- schema: t.List[DiffableTable] = [
26
+ schema: list[DiffableTable] = [
28
27
  DiffableTable(
29
28
  class_name="Band", tablename="band", columns=[name_column]
30
29
  )
31
30
  ]
32
- schema_snapshot: t.List[DiffableTable] = []
31
+ schema_snapshot: list[DiffableTable] = []
33
32
  schema_differ = SchemaDiffer(
34
33
  schema=schema, schema_snapshot=schema_snapshot, auto_input="y"
35
34
  )
@@ -52,8 +51,8 @@ class TestSchemaDiffer(TestCase):
52
51
  """
53
52
  Test dropping an existing table.
54
53
  """
55
- schema: t.List[DiffableTable] = []
56
- schema_snapshot: t.List[DiffableTable] = [
54
+ schema: list[DiffableTable] = []
55
+ schema_snapshot: list[DiffableTable] = [
57
56
  DiffableTable(class_name="Band", tablename="band", columns=[])
58
57
  ]
59
58
  schema_differ = SchemaDiffer(
@@ -73,12 +72,12 @@ class TestSchemaDiffer(TestCase):
73
72
  name_column = Varchar()
74
73
  name_column._meta.name = "name"
75
74
 
76
- schema: t.List[DiffableTable] = [
75
+ schema: list[DiffableTable] = [
77
76
  DiffableTable(
78
77
  class_name="Act", tablename="act", columns=[name_column]
79
78
  )
80
79
  ]
81
- schema_snapshot: t.List[DiffableTable] = [
80
+ schema_snapshot: list[DiffableTable] = [
82
81
  DiffableTable(
83
82
  class_name="Band", tablename="band", columns=[name_column]
84
83
  )
@@ -101,7 +100,7 @@ class TestSchemaDiffer(TestCase):
101
100
  """
102
101
  Testing changing the schema.
103
102
  """
104
- schema: t.List[DiffableTable] = [
103
+ schema: list[DiffableTable] = [
105
104
  DiffableTable(
106
105
  class_name="Band",
107
106
  tablename="band",
@@ -109,7 +108,7 @@ class TestSchemaDiffer(TestCase):
109
108
  schema="schema_1",
110
109
  )
111
110
  ]
112
- schema_snapshot: t.List[DiffableTable] = [
111
+ schema_snapshot: list[DiffableTable] = [
113
112
  DiffableTable(
114
113
  class_name="Band",
115
114
  tablename="band",
@@ -142,14 +141,14 @@ class TestSchemaDiffer(TestCase):
142
141
  genre_column = Varchar()
143
142
  genre_column._meta.name = "genre"
144
143
 
145
- schema: t.List[DiffableTable] = [
144
+ schema: list[DiffableTable] = [
146
145
  DiffableTable(
147
146
  class_name="Band",
148
147
  tablename="band",
149
148
  columns=[name_column, genre_column],
150
149
  )
151
150
  ]
152
- schema_snapshot: t.List[DiffableTable] = [
151
+ schema_snapshot: list[DiffableTable] = [
153
152
  DiffableTable(
154
153
  class_name="Band",
155
154
  tablename="band",
@@ -177,14 +176,14 @@ class TestSchemaDiffer(TestCase):
177
176
  genre_column = Varchar()
178
177
  genre_column._meta.name = "genre"
179
178
 
180
- schema: t.List[DiffableTable] = [
179
+ schema: list[DiffableTable] = [
181
180
  DiffableTable(
182
181
  class_name="Band",
183
182
  tablename="band",
184
183
  columns=[name_column],
185
184
  )
186
185
  ]
187
- schema_snapshot: t.List[DiffableTable] = [
186
+ schema_snapshot: list[DiffableTable] = [
188
187
  DiffableTable(
189
188
  class_name="Band",
190
189
  tablename="band",
@@ -213,14 +212,14 @@ class TestSchemaDiffer(TestCase):
213
212
  title_column = Varchar()
214
213
  title_column._meta.name = "title"
215
214
 
216
- schema_snapshot: t.List[DiffableTable] = [
215
+ schema_snapshot: list[DiffableTable] = [
217
216
  DiffableTable(
218
217
  class_name="Band",
219
218
  tablename="band",
220
219
  columns=[title_column],
221
220
  )
222
221
  ]
223
- schema: t.List[DiffableTable] = [
222
+ schema: list[DiffableTable] = [
224
223
  DiffableTable(
225
224
  class_name="Band",
226
225
  tablename="band",
@@ -278,14 +277,14 @@ class TestSchemaDiffer(TestCase):
278
277
  b2 = Varchar()
279
278
  b2._meta.name = "b2"
280
279
 
281
- schema_snapshot: t.List[DiffableTable] = [
280
+ schema_snapshot: list[DiffableTable] = [
282
281
  DiffableTable(
283
282
  class_name="Band",
284
283
  tablename="band",
285
284
  columns=[a1, b1],
286
285
  )
287
286
  ]
288
- schema: t.List[DiffableTable] = [
287
+ schema: list[DiffableTable] = [
289
288
  DiffableTable(
290
289
  class_name="Band",
291
290
  tablename="band",
@@ -355,14 +354,14 @@ class TestSchemaDiffer(TestCase):
355
354
  b2 = Varchar()
356
355
  b2._meta.name = "b2"
357
356
 
358
- schema_snapshot: t.List[DiffableTable] = [
357
+ schema_snapshot: list[DiffableTable] = [
359
358
  DiffableTable(
360
359
  class_name="Band",
361
360
  tablename="band",
362
361
  columns=[a1, b1],
363
362
  )
364
363
  ]
365
- schema: t.List[DiffableTable] = [
364
+ schema: list[DiffableTable] = [
366
365
  DiffableTable(
367
366
  class_name="Band",
368
367
  tablename="band",
@@ -425,14 +424,14 @@ class TestSchemaDiffer(TestCase):
425
424
  price_2 = Numeric(digits=(5, 2))
426
425
  price_2._meta.name = "price"
427
426
 
428
- schema: t.List[DiffableTable] = [
427
+ schema: list[DiffableTable] = [
429
428
  DiffableTable(
430
429
  class_name="Ticket",
431
430
  tablename="ticket",
432
431
  columns=[price_1],
433
432
  )
434
433
  ]
435
- schema_snapshot: t.List[DiffableTable] = [
434
+ schema_snapshot: list[DiffableTable] = [
436
435
  DiffableTable(
437
436
  class_name="Ticket",
438
437
  tablename="ticket",
@@ -463,14 +462,14 @@ class TestSchemaDiffer(TestCase):
463
462
  price_2 = Numeric(digits=(5, 2), db_column_name="custom")
464
463
  price_2._meta.name = "price"
465
464
 
466
- schema: t.List[DiffableTable] = [
465
+ schema: list[DiffableTable] = [
467
466
  DiffableTable(
468
467
  class_name="Ticket",
469
468
  tablename="ticket",
470
469
  columns=[price_1],
471
470
  )
472
471
  ]
473
- schema_snapshot: t.List[DiffableTable] = [
472
+ schema_snapshot: list[DiffableTable] = [
474
473
  DiffableTable(
475
474
  class_name="Ticket",
476
475
  tablename="ticket",
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import sys
4
- import typing as t
4
+ from typing import TYPE_CHECKING
5
5
  from unittest import TestCase
6
6
  from unittest.mock import MagicMock, call, patch
7
7
 
@@ -22,10 +22,10 @@ from tests.example_apps.music.tables import (
22
22
  Venue,
23
23
  )
24
24
 
25
- if t.TYPE_CHECKING: # pragma: no cover
25
+ if TYPE_CHECKING: # pragma: no cover
26
26
  from piccolo.table import Table
27
27
 
28
- TABLE_CLASSES: t.List[t.Type[Table]] = [
28
+ TABLE_CLASSES: list[type[Table]] = [
29
29
  Manager,
30
30
  Band,
31
31
  Venue,
tests/columns/m2m/base.py CHANGED
@@ -1,4 +1,4 @@
1
- import typing as t
1
+ from typing import Optional
2
2
 
3
3
  from piccolo.columns.column_types import (
4
4
  ForeignKey,
@@ -41,7 +41,7 @@ class M2MBase:
41
41
  (public vs non-public).
42
42
  """
43
43
 
44
- def _setUp(self, schema: t.Optional[str] = None):
44
+ def _setUp(self, schema: Optional[str] = None):
45
45
  self.schema = schema
46
46
 
47
47
  for table_class in (Band, Genre, GenreToBand):
@@ -186,7 +186,7 @@ class TestArray(TableTest):
186
186
  @pytest.mark.cockroach_array_slow
187
187
  def test_cat(self):
188
188
  """
189
- Make sure values can be appended to an array.
189
+ Make sure values can be appended to an array and that we can concatenate two arrays.
190
190
 
191
191
  In CockroachDB <= v22.2.0 we had this error:
192
192
 
@@ -197,37 +197,47 @@ class TestArray(TableTest):
197
197
  * https://github.com/piccolo-orm/piccolo/issues/1005
198
198
 
199
199
  """ # noqa: E501
200
- MyTable(value=[1, 1, 1]).save().run_sync()
200
+ MyTable(value=[5]).save().run_sync()
201
201
 
202
202
  MyTable.update(
203
- {MyTable.value: MyTable.value.cat([2])}, force=True
203
+ {MyTable.value: MyTable.value.cat([6])}, force=True
204
204
  ).run_sync()
205
205
 
206
206
  self.assertEqual(
207
207
  MyTable.select(MyTable.value).run_sync(),
208
- [{"value": [1, 1, 1, 2]}],
208
+ [{"value": [5, 6]}],
209
209
  )
210
210
 
211
- # Try plus symbol
211
+ # Try plus symbol - add array to the end
212
212
 
213
213
  MyTable.update(
214
- {MyTable.value: MyTable.value + [3]}, force=True
214
+ {MyTable.value: MyTable.value + [7]}, force=True
215
215
  ).run_sync()
216
216
 
217
217
  self.assertEqual(
218
218
  MyTable.select(MyTable.value).run_sync(),
219
- [{"value": [1, 1, 1, 2, 3]}],
219
+ [{"value": [5, 6, 7]}],
220
220
  )
221
221
 
222
- # Make sure non-list values work
222
+ # Add array to the start
223
+
224
+ MyTable.update(
225
+ {MyTable.value: [4] + MyTable.value}, force=True
226
+ ).run_sync()
227
+
228
+ self.assertEqual(
229
+ MyTable.select(MyTable.value).run_sync(),
230
+ [{"value": [4, 5, 6, 7]}],
231
+ )
223
232
 
233
+ # Add array to the start and end
224
234
  MyTable.update(
225
- {MyTable.value: MyTable.value + 4}, force=True
235
+ {MyTable.value: [3] + MyTable.value + [8]}, force=True
226
236
  ).run_sync()
227
237
 
228
238
  self.assertEqual(
229
239
  MyTable.select(MyTable.value).run_sync(),
230
- [{"value": [1, 1, 1, 2, 3, 4]}],
240
+ [{"value": [3, 4, 5, 6, 7, 8]}],
231
241
  )
232
242
 
233
243
  @sqlite_only
@@ -238,11 +248,167 @@ class TestArray(TableTest):
238
248
  with self.assertRaises(ValueError) as manager:
239
249
  MyTable.value.cat([2])
240
250
 
251
+ self.assertEqual(
252
+ str(manager.exception),
253
+ "Only Postgres and Cockroach support array concatenation.",
254
+ )
255
+
256
+ @engines_skip("sqlite")
257
+ @pytest.mark.cockroach_array_slow
258
+ def test_prepend(self):
259
+ """
260
+ Make sure values can be added to the beginning of the array.
261
+
262
+ In CockroachDB <= v22.2.0 we had this error:
263
+
264
+ * https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
265
+
266
+ In newer CockroachDB versions, it runs but is very slow:
267
+
268
+ * https://github.com/piccolo-orm/piccolo/issues/1005
269
+
270
+ """ # noqa: E501
271
+ MyTable(value=[1, 1, 1]).save().run_sync()
272
+
273
+ MyTable.update(
274
+ {MyTable.value: MyTable.value.prepend(3)}, force=True
275
+ ).run_sync()
276
+
277
+ self.assertEqual(
278
+ MyTable.select(MyTable.value).run_sync(),
279
+ [{"value": [3, 1, 1, 1]}],
280
+ )
281
+
282
+ @sqlite_only
283
+ def test_prepend_sqlite(self):
284
+ """
285
+ If using SQLite then an exception should be raised currently.
286
+ """
287
+ with self.assertRaises(ValueError) as manager:
288
+ MyTable.value.prepend(2)
289
+
290
+ self.assertEqual(
291
+ str(manager.exception),
292
+ "Only Postgres and Cockroach support array prepending.",
293
+ )
294
+
295
+ @engines_skip("sqlite")
296
+ @pytest.mark.cockroach_array_slow
297
+ def test_append(self):
298
+ """
299
+ Make sure values can be appended to an array.
300
+
301
+ In CockroachDB <= v22.2.0 we had this error:
302
+
303
+ * https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
304
+
305
+ In newer CockroachDB versions, it runs but is very slow:
306
+
307
+ * https://github.com/piccolo-orm/piccolo/issues/1005
308
+
309
+ """ # noqa: E501
310
+ MyTable(value=[1, 1, 1]).save().run_sync()
311
+
312
+ MyTable.update(
313
+ {MyTable.value: MyTable.value.append(3)}, force=True
314
+ ).run_sync()
315
+
316
+ self.assertEqual(
317
+ MyTable.select(MyTable.value).run_sync(),
318
+ [{"value": [1, 1, 1, 3]}],
319
+ )
320
+
321
+ @sqlite_only
322
+ def test_append_sqlite(self):
323
+ """
324
+ If using SQLite then an exception should be raised currently.
325
+ """
326
+ with self.assertRaises(ValueError) as manager:
327
+ MyTable.value.append(2)
328
+
241
329
  self.assertEqual(
242
330
  str(manager.exception),
243
331
  "Only Postgres and Cockroach support array appending.",
244
332
  )
245
333
 
334
+ @engines_skip("sqlite")
335
+ @pytest.mark.cockroach_array_slow
336
+ def test_replace(self):
337
+ """
338
+ Make sure values can be swapped in the array.
339
+
340
+ In CockroachDB <= v22.2.0 we had this error:
341
+
342
+ * https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
343
+
344
+ In newer CockroachDB versions, it runs but is very slow:
345
+
346
+ * https://github.com/piccolo-orm/piccolo/issues/1005
347
+
348
+ """ # noqa: E501
349
+ MyTable(value=[1, 1, 1]).save().run_sync()
350
+
351
+ MyTable.update(
352
+ {MyTable.value: MyTable.value.replace(1, 2)}, force=True
353
+ ).run_sync()
354
+
355
+ self.assertEqual(
356
+ MyTable.select(MyTable.value).run_sync(),
357
+ [{"value": [2, 2, 2]}],
358
+ )
359
+
360
+ @sqlite_only
361
+ def test_replace_sqlite(self):
362
+ """
363
+ If using SQLite then an exception should be raised currently.
364
+ """
365
+ with self.assertRaises(ValueError) as manager:
366
+ MyTable.value.replace(1, 2)
367
+
368
+ self.assertEqual(
369
+ str(manager.exception),
370
+ "Only Postgres and Cockroach support array substitution.",
371
+ )
372
+
373
+ @engines_skip("sqlite")
374
+ @pytest.mark.cockroach_array_slow
375
+ def test_remove(self):
376
+ """
377
+ Make sure values can be removed from an array.
378
+
379
+ In CockroachDB <= v22.2.0 we had this error:
380
+
381
+ * https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
382
+
383
+ In newer CockroachDB versions, it runs but is very slow:
384
+
385
+ * https://github.com/piccolo-orm/piccolo/issues/1005
386
+
387
+ """ # noqa: E501
388
+ MyTable(value=[1, 2, 3]).save().run_sync()
389
+
390
+ MyTable.update(
391
+ {MyTable.value: MyTable.value.remove(2)}, force=True
392
+ ).run_sync()
393
+
394
+ self.assertEqual(
395
+ MyTable.select(MyTable.value).run_sync(),
396
+ [{"value": [1, 3]}],
397
+ )
398
+
399
+ @sqlite_only
400
+ def test_remove_sqlite(self):
401
+ """
402
+ If using SQLite then an exception should be raised currently.
403
+ """
404
+ with self.assertRaises(ValueError) as manager:
405
+ MyTable.value.remove(2)
406
+
407
+ self.assertEqual(
408
+ str(manager.exception),
409
+ "Only Postgres and Cockroach support array removing.",
410
+ )
411
+
246
412
 
247
413
  ###############################################################################
248
414
  # Date and time arrays
@@ -1,4 +1,4 @@
1
- import typing as t
1
+ from typing import Any
2
2
 
3
3
  from piccolo.columns.column_types import Boolean
4
4
  from piccolo.table import Table
@@ -14,9 +14,7 @@ class TestBoolean(TableTest):
14
14
 
15
15
  def test_return_type(self) -> None:
16
16
  for value in (True, False, None, ...):
17
- kwargs: t.Dict[str, t.Any] = (
18
- {} if value is ... else {"boolean": value}
19
- )
17
+ kwargs: dict[str, Any] = {} if value is ... else {"boolean": value}
20
18
  expected = MyTable.boolean.default if value is ... else value
21
19
 
22
20
  row = MyTable(**kwargs)
@@ -1,6 +1,6 @@
1
1
  import unittest
2
2
 
3
- from tests.example_apps.music.tables import Band
3
+ from tests.example_apps.music.tables import Band, Concert
4
4
 
5
5
 
6
6
  class TestWhere(unittest.TestCase):
@@ -29,6 +29,20 @@ class TestWhere(unittest.TestCase):
29
29
  with self.assertRaises(ValueError):
30
30
  Band.name.is_in([])
31
31
 
32
+ def test_is_in_subquery(self):
33
+ _where = Band.id.is_in(
34
+ Concert.select(Concert.band_1).where(Concert.band_1 == 1)
35
+ )
36
+ sql = _where.__str__()
37
+ self.assertEqual(
38
+ sql,
39
+ '"band"."id" IN (SELECT ALL "concert"."band_1" AS "band_1" FROM "concert" WHERE "concert"."band_1" = 1)', # noqa: E501
40
+ )
41
+
42
+ # a sub select must only return a single column
43
+ with self.assertRaises(ValueError):
44
+ Band.id.is_in(Concert.select().where(Concert.band_1 == 1))
45
+
32
46
  def test_not_in(self):
33
47
  _where = Band.name.not_in(["CSharps"])
34
48
  sql = _where.__str__()
@@ -37,6 +51,20 @@ class TestWhere(unittest.TestCase):
37
51
  with self.assertRaises(ValueError):
38
52
  Band.name.not_in([])
39
53
 
54
+ def test_not_in_subquery(self):
55
+ _where = Band.id.not_in(
56
+ Concert.select(Concert.band_1).where(Concert.band_1 == 1)
57
+ )
58
+ sql = _where.__str__()
59
+ self.assertEqual(
60
+ sql,
61
+ '"band"."id" NOT IN (SELECT ALL "concert"."band_1" AS "band_1" FROM "concert" WHERE "concert"."band_1" = 1)', # noqa: E501
62
+ )
63
+
64
+ # a sub select must only return a single column
65
+ with self.assertRaises(ValueError):
66
+ Band.id.not_in(Concert.select().where(Concert.band_1 == 1))
67
+
40
68
 
41
69
  class TestAnd(unittest.TestCase):
42
70
  def test_get_column_values(self):
@@ -1,4 +1,4 @@
1
- import typing as t
1
+ from typing import Optional
2
2
 
3
3
  from piccolo.columns.column_types import ForeignKey, Integer, Serial, Varchar
4
4
  from piccolo.table import Table, create_db_tables_sync, drop_db_tables_sync
@@ -35,7 +35,7 @@ class TestDBColumnName(DBTestCase):
35
35
  def tearDown(self):
36
36
  drop_db_tables_sync(Band, Manager)
37
37
 
38
- def insert_band(self, manager: t.Optional[Manager] = None) -> Band:
38
+ def insert_band(self, manager: Optional[Manager] = None) -> Band:
39
39
  band = Band(name="Pythonistas", popularity=1000, manager=manager)
40
40
  band.save().run_sync()
41
41
  return band
@@ -1,4 +1,4 @@
1
- import typing as t
1
+ from typing import cast
2
2
  from unittest import TestCase
3
3
  from unittest.mock import MagicMock
4
4
 
@@ -19,7 +19,7 @@ class TestExtraNodes(TestCase):
19
19
  test_engine = engine_finder()
20
20
  assert test_engine is not None
21
21
 
22
- test_engine = t.cast(PostgresEngine, test_engine)
22
+ test_engine = cast(PostgresEngine, test_engine)
23
23
 
24
24
  EXTRA_NODE = MagicMock(spec=PostgresEngine(config=test_engine.config))
25
25
  EXTRA_NODE.run_querystring = AsyncMock(return_value=[])
tests/engine/test_pool.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  import os
3
3
  import tempfile
4
- import typing as t
4
+ from typing import cast
5
5
  from unittest import TestCase
6
6
  from unittest.mock import call, patch
7
7
 
@@ -14,7 +14,7 @@ from tests.example_apps.music.tables import Manager
14
14
  @engines_only("postgres", "cockroach")
15
15
  class TestPool(DBTestCase):
16
16
  async def _create_pool(self) -> None:
17
- engine = t.cast(PostgresEngine, Manager._meta.db)
17
+ engine = cast(PostgresEngine, Manager._meta.db)
18
18
 
19
19
  await engine.start_connection_pool()
20
20
  assert engine.pool is not None
@@ -72,7 +72,7 @@ class TestPool(DBTestCase):
72
72
  @engines_only("postgres", "cockroach")
73
73
  class TestPoolProxyMethods(DBTestCase):
74
74
  async def _create_pool(self) -> None:
75
- engine = t.cast(PostgresEngine, Manager._meta.db)
75
+ engine = cast(PostgresEngine, Manager._meta.db)
76
76
 
77
77
  # Deliberate typo ('nnn'):
78
78
  await engine.start_connnection_pool()
@@ -1,5 +1,5 @@
1
1
  import asyncio
2
- import typing as t
2
+ from typing import cast
3
3
  from unittest import TestCase
4
4
 
5
5
  import pytest
@@ -169,7 +169,7 @@ class TestTransactionExists(TestCase):
169
169
  """
170
170
  Make sure we can detect when code is within a transaction.
171
171
  """
172
- engine = t.cast(SQLiteEngine, Manager._meta.db)
172
+ engine = cast(SQLiteEngine, Manager._meta.db)
173
173
 
174
174
  async def run_inside_transaction():
175
175
  async with engine.transaction():
@@ -198,7 +198,7 @@ class TestTransactionType(TestCase):
198
198
 
199
199
  https://github.com/piccolo-orm/piccolo/issues/687
200
200
  """
201
- engine = t.cast(SQLiteEngine, Manager._meta.db)
201
+ engine = cast(SQLiteEngine, Manager._meta.db)
202
202
 
203
203
  async def run_transaction(name: str):
204
204
  async with engine.transaction(
@@ -234,7 +234,7 @@ class TestTransactionType(TestCase):
234
234
  """
235
235
  Similar to above, but with ``Atomic``.
236
236
  """
237
- engine = t.cast(SQLiteEngine, Manager._meta.db)
237
+ engine = cast(SQLiteEngine, Manager._meta.db)
238
238
 
239
239
  async def run_atomic(name: str):
240
240
  atomic = engine.atomic(transaction_type=TransactionType.immediate)
@@ -1,6 +1,6 @@
1
1
  import timeit
2
- import typing as t
3
2
  from dataclasses import dataclass
3
+ from typing import Any, Union
4
4
  from unittest import mock
5
5
 
6
6
  from piccolo.columns import Integer, Varchar
@@ -12,8 +12,8 @@ from tests.example_apps.music.tables import Band
12
12
 
13
13
  @dataclass
14
14
  class QueryResponse:
15
- query: t.Union[Query, FrozenQuery]
16
- response: t.Any
15
+ query: Union[Query, FrozenQuery]
16
+ response: Any
17
17
 
18
18
 
19
19
  class TestFreeze(DBTestCase):
@@ -23,7 +23,7 @@ class TestFreeze(DBTestCase):
23
23
  """
24
24
  self.insert_rows()
25
25
 
26
- query_responses: t.List[QueryResponse] = [
26
+ query_responses: list[QueryResponse] = [
27
27
  QueryResponse(
28
28
  query=(
29
29
  Band.select(Band.name)
@@ -1,4 +1,4 @@
1
- import typing as t
1
+ from typing import cast
2
2
 
3
3
  from piccolo.testing.test_case import AsyncTableTest
4
4
  from tests.example_apps.music.tables import Band, Concert, Manager, Venue
@@ -52,7 +52,7 @@ class TestGetRelated(AsyncTableTest):
52
52
  """
53
53
  Make sure it also works using a string representation of a foreign key.
54
54
  """
55
- manager = t.cast(Manager, await self.band.get_related("manager"))
55
+ manager = cast(Manager, await self.band.get_related("manager"))
56
56
  self.assertTrue(manager.id == self.manager.id)
57
57
 
58
58
  async def test_invalid_string(self):