piccolo 1.27.1__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.1.dist-info → piccolo-1.28.0.dist-info}/METADATA +1 -1
  95. {piccolo-1.27.1.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.1.dist-info → piccolo-1.28.0.dist-info}/WHEEL +0 -0
  120. {piccolo-1.27.1.dist-info → piccolo-1.28.0.dist-info}/entry_points.txt +0 -0
  121. {piccolo-1.27.1.dist-info → piccolo-1.28.0.dist-info}/licenses/LICENSE +0 -0
  122. {piccolo-1.27.1.dist-info → piccolo-1.28.0.dist-info}/top_level.txt +0 -0
piccolo/query/mixins.py CHANGED
@@ -3,9 +3,10 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import collections.abc
5
5
  import itertools
6
- import typing as t
6
+ from collections.abc import Callable, Sequence
7
7
  from dataclasses import dataclass, field
8
8
  from enum import Enum, auto
9
+ from typing import TYPE_CHECKING, Any, Literal, Optional, Union
9
10
 
10
11
  from piccolo.columns import And, Column, Or, Where
11
12
  from piccolo.columns.column_types import ForeignKey
@@ -15,7 +16,7 @@ from piccolo.querystring import QueryString
15
16
  from piccolo.utils.list import flatten
16
17
  from piccolo.utils.sql_values import convert_to_sql_value
17
18
 
18
- if t.TYPE_CHECKING: # pragma: no cover
19
+ if TYPE_CHECKING: # pragma: no cover
19
20
  from piccolo.querystring import Selectable
20
21
  from piccolo.table import Table # noqa
21
22
 
@@ -33,7 +34,7 @@ class Distinct:
33
34
  __slots__ = ("enabled", "on")
34
35
 
35
36
  enabled: bool
36
- on: t.Optional[t.Sequence[Column]]
37
+ on: Optional[Sequence[Column]]
37
38
 
38
39
  @property
39
40
  def querystring(self) -> QueryString:
@@ -152,17 +153,17 @@ class OrderByRaw:
152
153
  class OrderByItem:
153
154
  __slots__ = ("columns", "ascending")
154
155
 
155
- columns: t.Sequence[t.Union[Column, OrderByRaw]]
156
+ columns: Sequence[Union[Column, OrderByRaw]]
156
157
  ascending: bool
157
158
 
158
159
 
159
160
  @dataclass
160
161
  class OrderBy:
161
- order_by_items: t.List[OrderByItem] = field(default_factory=list)
162
+ order_by_items: list[OrderByItem] = field(default_factory=list)
162
163
 
163
164
  @property
164
165
  def querystring(self) -> QueryString:
165
- order_by_strings: t.List[str] = []
166
+ order_by_strings: list[str] = []
166
167
  for order_by_item in self.order_by_items:
167
168
  order = "ASC" if order_by_item.ascending else "DESC"
168
169
  for column in order_by_item.columns:
@@ -185,7 +186,7 @@ class OrderBy:
185
186
  class Returning:
186
187
  __slots__ = ("columns",)
187
188
 
188
- columns: t.List[Column]
189
+ columns: list[Column]
189
190
 
190
191
  @property
191
192
  def querystring(self) -> QueryString:
@@ -230,13 +231,13 @@ class CallbackType(Enum):
230
231
  @dataclass
231
232
  class Callback:
232
233
  kind: CallbackType
233
- target: t.Callable
234
+ target: Callable
234
235
 
235
236
 
236
237
  @dataclass
237
238
  class WhereDelegate:
238
- _where: t.Optional[Combinable] = None
239
- _where_columns: t.List[Column] = field(default_factory=list)
239
+ _where: Optional[Combinable] = None
240
+ _where_columns: list[Column] = field(default_factory=list)
240
241
 
241
242
  def get_where_columns(self):
242
243
  """
@@ -257,7 +258,7 @@ class WhereDelegate:
257
258
  elif isinstance(combinable, WhereRaw):
258
259
  self._where_columns.extend(combinable.querystring.columns)
259
260
 
260
- def where(self, *where: t.Union[Combinable, QueryString]):
261
+ def where(self, *where: Union[Combinable, QueryString]):
261
262
  for arg in where:
262
263
  if isinstance(arg, bool):
263
264
  raise ValueError(
@@ -278,7 +279,7 @@ class WhereDelegate:
278
279
  class OrderByDelegate:
279
280
  _order_by: OrderBy = field(default_factory=OrderBy)
280
281
 
281
- def get_order_by_columns(self) -> t.List[Column]:
282
+ def get_order_by_columns(self) -> list[Column]:
282
283
  """
283
284
  Used to work out which columns are needed for joins.
284
285
  """
@@ -290,7 +291,7 @@ class OrderByDelegate:
290
291
  if isinstance(i, Column)
291
292
  ]
292
293
 
293
- def order_by(self, *columns: t.Union[Column, OrderByRaw], ascending=True):
294
+ def order_by(self, *columns: Union[Column, OrderByRaw], ascending=True):
294
295
  if len(columns) < 1:
295
296
  raise ValueError("At least one column must be passed to order_by.")
296
297
 
@@ -301,7 +302,7 @@ class OrderByDelegate:
301
302
 
302
303
  @dataclass
303
304
  class LimitDelegate:
304
- _limit: t.Optional[Limit] = None
305
+ _limit: Optional[Limit] = None
305
306
  _first: bool = False
306
307
 
307
308
  def limit(self, number: int):
@@ -319,7 +320,7 @@ class AsOfDelegate:
319
320
  Currently supports Cockroach using AS OF SYSTEM TIME.
320
321
  """
321
322
 
322
- _as_of: t.Optional[AsOf] = None
323
+ _as_of: Optional[AsOf] = None
323
324
 
324
325
  def as_of(self, interval: str = "-1s"):
325
326
  self._as_of = AsOf(interval)
@@ -331,9 +332,7 @@ class DistinctDelegate:
331
332
  default_factory=lambda: Distinct(enabled=False, on=None)
332
333
  )
333
334
 
334
- def distinct(
335
- self, enabled: bool, on: t.Optional[t.Sequence[Column]] = None
336
- ):
335
+ def distinct(self, enabled: bool, on: Optional[Sequence[Column]] = None):
337
336
  if on and not isinstance(on, collections.abc.Sequence):
338
337
  # Check a sequence is passed in, otherwise the user will get some
339
338
  # unuseful errors later on.
@@ -344,9 +343,9 @@ class DistinctDelegate:
344
343
 
345
344
  @dataclass
346
345
  class ReturningDelegate:
347
- _returning: t.Optional[Returning] = None
346
+ _returning: Optional[Returning] = None
348
347
 
349
- def returning(self, columns: t.Sequence[Column]):
348
+ def returning(self, columns: Sequence[Column]):
350
349
  self._returning = Returning(columns=list(columns))
351
350
 
352
351
 
@@ -360,9 +359,9 @@ class CountDelegate:
360
359
 
361
360
  @dataclass
362
361
  class AddDelegate:
363
- _add: t.List[Table] = field(default_factory=list)
362
+ _add: list[Table] = field(default_factory=list)
364
363
 
365
- def add(self, *instances: Table, table_class: t.Type[Table]):
364
+ def add(self, *instances: Table, table_class: type[Table]):
366
365
  for instance in instances:
367
366
  if not isinstance(instance, table_class):
368
367
  raise TypeError("Incompatible type added.")
@@ -384,10 +383,10 @@ class OutputDelegate:
384
383
 
385
384
  def output(
386
385
  self,
387
- as_list: t.Optional[bool] = None,
388
- as_json: t.Optional[bool] = None,
389
- load_json: t.Optional[bool] = None,
390
- nested: t.Optional[bool] = None,
386
+ as_list: Optional[bool] = None,
387
+ as_json: Optional[bool] = None,
388
+ load_json: Optional[bool] = None,
389
+ nested: Optional[bool] = None,
391
390
  ):
392
391
  """
393
392
  :param as_list:
@@ -429,13 +428,13 @@ class CallbackDelegate:
429
428
  .callback([handler1, handler2])
430
429
  """
431
430
 
432
- _callbacks: t.Dict[CallbackType, t.List[Callback]] = field(
431
+ _callbacks: dict[CallbackType, list[Callback]] = field(
433
432
  default_factory=lambda: {kind: [] for kind in CallbackType}
434
433
  )
435
434
 
436
435
  def callback(
437
436
  self,
438
- callbacks: t.Union[t.Callable, t.List[t.Callable]],
437
+ callbacks: Union[Callable, list[Callable]],
439
438
  *,
440
439
  on: CallbackType,
441
440
  ):
@@ -446,7 +445,7 @@ class CallbackDelegate:
446
445
  else:
447
446
  self._callbacks[on].append(Callback(kind=on, target=callbacks))
448
447
 
449
- async def invoke(self, results: t.Any, *, kind: CallbackType) -> t.Any:
448
+ async def invoke(self, results: Any, *, kind: CallbackType) -> Any:
450
449
  """
451
450
  Utility function that invokes the registered callbacks in the correct
452
451
  way, handling both sync and async callbacks. Only callbacks of the
@@ -472,9 +471,9 @@ class PrefetchDelegate:
472
471
  .prefetch(MyTable.column_a, MyTable.column_b)
473
472
  """
474
473
 
475
- fk_columns: t.List[ForeignKey] = field(default_factory=list)
474
+ fk_columns: list[ForeignKey] = field(default_factory=list)
476
475
 
477
- def prefetch(self, *fk_columns: t.Union[ForeignKey, t.List[ForeignKey]]):
476
+ def prefetch(self, *fk_columns: Union[ForeignKey, list[ForeignKey]]):
478
477
  """
479
478
  :param columns:
480
479
  We accept ``ForeignKey`` and ``List[ForeignKey]`` here, in case
@@ -482,7 +481,7 @@ class PrefetchDelegate:
482
481
  in which case we flatten the list.
483
482
 
484
483
  """
485
- _fk_columns: t.List[ForeignKey] = []
484
+ _fk_columns: list[ForeignKey] = []
486
485
  for column in fk_columns:
487
486
  if isinstance(column, list):
488
487
  _fk_columns.extend(column)
@@ -501,9 +500,9 @@ class ColumnsDelegate:
501
500
  .columns(MyTable.column_a, MyTable.column_b)
502
501
  """
503
502
 
504
- selected_columns: t.Sequence[Selectable] = field(default_factory=list)
503
+ selected_columns: Sequence[Selectable] = field(default_factory=list)
505
504
 
506
- def columns(self, *columns: t.Union[Selectable, t.List[Selectable]]):
505
+ def columns(self, *columns: Union[Selectable, list[Selectable]]):
507
506
  """
508
507
  :param columns:
509
508
  We accept ``Selectable`` and ``List[Selectable]`` here, in case
@@ -531,10 +530,10 @@ class ValuesDelegate:
531
530
  Used to specify new column values - primarily used in update queries.
532
531
  """
533
532
 
534
- table: t.Type[Table]
535
- _values: t.Dict[Column, t.Any] = field(default_factory=dict)
533
+ table: type[Table]
534
+ _values: dict[Column, Any] = field(default_factory=dict)
536
535
 
537
- def values(self, values: t.Dict[t.Union[Column, str], t.Any]):
536
+ def values(self, values: dict[Union[Column, str], Any]):
538
537
  """
539
538
  Example usage:
540
539
 
@@ -549,7 +548,7 @@ class ValuesDelegate:
549
548
  .values(column_a=1})
550
549
 
551
550
  """
552
- cleaned_values: t.Dict[Column, t.Any] = {}
551
+ cleaned_values: dict[Column, Any] = {}
553
552
  for key, value in values.items():
554
553
  if isinstance(key, Column):
555
554
  column = key
@@ -564,7 +563,7 @@ class ValuesDelegate:
564
563
 
565
564
  self._values.update(cleaned_values)
566
565
 
567
- def get_sql_values(self) -> t.List[t.Any]:
566
+ def get_sql_values(self) -> list[Any]:
568
567
  """
569
568
  Convert any Enums into values, and serialise any JSON.
570
569
  """
@@ -587,7 +586,7 @@ class OffsetDelegate:
587
586
 
588
587
  """
589
588
 
590
- _offset: t.Optional[Offset] = None
589
+ _offset: Optional[Offset] = None
591
590
 
592
591
  def offset(self, number: int = 0):
593
592
  self._offset = Offset(number)
@@ -597,7 +596,7 @@ class OffsetDelegate:
597
596
  class GroupBy:
598
597
  __slots__ = ("columns",)
599
598
 
600
- columns: t.Sequence[Column]
599
+ columns: Sequence[Column]
601
600
 
602
601
  @property
603
602
  def querystring(self) -> QueryString:
@@ -620,7 +619,7 @@ class GroupByDelegate:
620
619
 
621
620
  """
622
621
 
623
- _group_by: t.Optional[GroupBy] = None
622
+ _group_by: Optional[GroupBy] = None
624
623
 
625
624
  def group_by(self, *columns: Column):
626
625
  self._group_by = GroupBy(columns=columns)
@@ -637,12 +636,10 @@ class OnConflictAction(str, Enum):
637
636
 
638
637
  @dataclass
639
638
  class OnConflictItem:
640
- target: t.Optional[t.Union[str, Column, t.Tuple[Column, ...]]] = None
641
- action: t.Optional[OnConflictAction] = None
642
- values: t.Optional[t.Sequence[t.Union[Column, t.Tuple[Column, t.Any]]]] = (
643
- None
644
- )
645
- where: t.Optional[Combinable] = None
639
+ target: Optional[Union[str, Column, tuple[Column, ...]]] = None
640
+ action: Optional[OnConflictAction] = None
641
+ values: Optional[Sequence[Union[Column, tuple[Column, Any]]]] = None
642
+ where: Optional[Combinable] = None
646
643
 
647
644
  @property
648
645
  def target_string(self) -> str:
@@ -726,7 +723,7 @@ class OnConflict:
726
723
  parent class.
727
724
  """
728
725
 
729
- on_conflict_items: t.List[OnConflictItem] = field(default_factory=list)
726
+ on_conflict_items: list[OnConflictItem] = field(default_factory=list)
730
727
 
731
728
  @property
732
729
  def querystring(self) -> QueryString:
@@ -757,14 +754,12 @@ class OnConflictDelegate:
757
754
 
758
755
  def on_conflict(
759
756
  self,
760
- target: t.Optional[t.Union[str, Column, t.Tuple[Column, ...]]] = None,
761
- action: t.Union[
762
- OnConflictAction, t.Literal["DO NOTHING", "DO UPDATE"]
757
+ target: Optional[Union[str, Column, tuple[Column, ...]]] = None,
758
+ action: Union[
759
+ OnConflictAction, Literal["DO NOTHING", "DO UPDATE"]
763
760
  ] = OnConflictAction.do_nothing,
764
- values: t.Optional[
765
- t.Sequence[t.Union[Column, t.Tuple[Column, t.Any]]]
766
- ] = None,
767
- where: t.Optional[Combinable] = None,
761
+ values: Optional[Sequence[Union[Column, tuple[Column, Any]]]] = None,
762
+ where: Optional[Combinable] = None,
768
763
  ):
769
764
  action_: OnConflictAction
770
765
  if isinstance(action, OnConflictAction):
@@ -806,7 +801,7 @@ class LockRows:
806
801
  lock_strength: LockStrength
807
802
  nowait: bool
808
803
  skip_locked: bool
809
- of: t.Tuple[t.Type[Table], ...]
804
+ of: tuple[type[Table], ...]
810
805
 
811
806
  def __post_init__(self):
812
807
  if not isinstance(self.lock_strength, LockStrength):
@@ -846,13 +841,13 @@ class LockRows:
846
841
  @dataclass
847
842
  class LockRowsDelegate:
848
843
 
849
- _lock_rows: t.Optional[LockRows] = None
844
+ _lock_rows: Optional[LockRows] = None
850
845
 
851
846
  def lock_rows(
852
847
  self,
853
- lock_strength: t.Union[
848
+ lock_strength: Union[
854
849
  LockStrength,
855
- t.Literal[
850
+ Literal[
856
851
  "UPDATE",
857
852
  "NO KEY UPDATE",
858
853
  "KEY SHARE",
@@ -861,7 +856,7 @@ class LockRowsDelegate:
861
856
  ] = LockStrength.update,
862
857
  nowait=False,
863
858
  skip_locked=False,
864
- of: t.Tuple[type[Table], ...] = (),
859
+ of: tuple[type[Table], ...] = (),
865
860
  ):
866
861
  lock_strength_: LockStrength
867
862
  if isinstance(lock_strength, LockStrength):
@@ -1,17 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
3
+ from typing import TYPE_CHECKING, Any, Optional, Union
4
4
 
5
5
  from piccolo.querystring import QueryString
6
6
  from piccolo.utils.encoding import dump_json
7
7
 
8
- if t.TYPE_CHECKING:
8
+ if TYPE_CHECKING:
9
9
  from piccolo.columns.column_types import JSON
10
10
 
11
11
 
12
12
  class JSONQueryString(QueryString):
13
13
 
14
- def clean_value(self, value: t.Any):
14
+ def clean_value(self, value: Any):
15
15
  if not isinstance(value, (str, QueryString)):
16
16
  value = dump_json(value)
17
17
  return value
@@ -42,9 +42,9 @@ class GetChildElement(JSONQueryString):
42
42
 
43
43
  def __init__(
44
44
  self,
45
- identifier: t.Union[JSON, QueryString],
46
- key: t.Union[str, int, QueryString],
47
- alias: t.Optional[str] = None,
45
+ identifier: Union[JSON, QueryString],
46
+ key: Union[str, int, QueryString],
47
+ alias: Optional[str] = None,
48
48
  ):
49
49
  if isinstance(key, int):
50
50
  # asyncpg only accepts integer keys if we explicitly mark it as an
@@ -53,7 +53,7 @@ class GetChildElement(JSONQueryString):
53
53
 
54
54
  super().__init__("{} -> {}", identifier, key, alias=alias)
55
55
 
56
- def arrow(self, key: t.Union[str, int, QueryString]) -> GetChildElement:
56
+ def arrow(self, key: Union[str, int, QueryString]) -> GetChildElement:
57
57
  """
58
58
  This allows you to drill multiple levels deep into a JSON object if
59
59
  needed.
@@ -77,7 +77,7 @@ class GetChildElement(JSONQueryString):
77
77
  return GetChildElement(identifier=self, key=key, alias=self._alias)
78
78
 
79
79
  def __getitem__(
80
- self, value: t.Union[str, int, QueryString]
80
+ self, value: Union[str, int, QueryString]
81
81
  ) -> GetChildElement:
82
82
  return GetChildElement(identifier=self, key=value, alias=self._alias)
83
83
 
@@ -94,9 +94,9 @@ class GetElementFromPath(JSONQueryString):
94
94
 
95
95
  def __init__(
96
96
  self,
97
- identifier: t.Union[JSON, QueryString],
98
- path: t.List[t.Union[str, int]],
99
- alias: t.Optional[str] = None,
97
+ identifier: Union[JSON, QueryString],
98
+ path: list[Union[str, int]],
99
+ alias: Optional[str] = None,
100
100
  ):
101
101
  """
102
102
  :param path:
piccolo/query/proxy.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import inspect
2
- import typing as t
2
+ from collections.abc import Generator
3
+ from typing import Generic, Optional, TypeVar
3
4
 
4
5
  from typing_extensions import Protocol
5
6
 
@@ -8,22 +9,20 @@ from piccolo.utils.sync import run_sync
8
9
 
9
10
 
10
11
  class Runnable(Protocol):
11
- async def run(
12
- self, node: t.Optional[str] = None, in_pool: bool = True
13
- ): ...
12
+ async def run(self, node: Optional[str] = None, in_pool: bool = True): ...
14
13
 
15
14
 
16
- QueryType = t.TypeVar("QueryType", bound=Runnable)
17
- ResponseType = t.TypeVar("ResponseType")
15
+ QueryType = TypeVar("QueryType", bound=Runnable)
16
+ ResponseType = TypeVar("ResponseType")
18
17
 
19
18
 
20
- class Proxy(t.Generic[QueryType, ResponseType]):
19
+ class Proxy(Generic[QueryType, ResponseType]):
21
20
  def __init__(self, query: QueryType):
22
21
  self.query = query
23
22
 
24
23
  async def run(
25
24
  self,
26
- node: t.Optional[str] = None,
25
+ node: Optional[str] = None,
27
26
  in_pool: bool = True,
28
27
  ) -> ResponseType:
29
28
  return await self.query.run(node=node, in_pool=in_pool)
@@ -33,7 +32,7 @@ class Proxy(t.Generic[QueryType, ResponseType]):
33
32
 
34
33
  def __await__(
35
34
  self,
36
- ) -> t.Generator[None, None, ResponseType]:
35
+ ) -> Generator[None, None, ResponseType]:
37
36
  """
38
37
  If the user doesn't explicity call .run(), proxy to it as a
39
38
  convenience.
piccolo/querystring.py CHANGED
@@ -1,15 +1,16 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
4
3
  from abc import ABCMeta, abstractmethod
4
+ from collections.abc import Sequence
5
5
  from dataclasses import dataclass
6
6
  from datetime import datetime
7
7
  from importlib.util import find_spec
8
8
  from string import Formatter
9
+ from typing import TYPE_CHECKING, Any, Optional
9
10
 
10
- if t.TYPE_CHECKING: # pragma: no cover
11
- from piccolo.table import Table
11
+ if TYPE_CHECKING: # pragma: no cover
12
12
  from piccolo.columns import Column
13
+ from piccolo.table import Table
13
14
 
14
15
  from uuid import UUID
15
16
 
@@ -26,7 +27,7 @@ class Selectable(metaclass=ABCMeta):
26
27
 
27
28
  __slots__ = ("_alias",)
28
29
 
29
- _alias: t.Optional[str]
30
+ _alias: Optional[str]
30
31
 
31
32
  @abstractmethod
32
33
  def get_select_string(
@@ -74,10 +75,10 @@ class QueryString(Selectable):
74
75
  def __init__(
75
76
  self,
76
77
  template: str,
77
- *args: t.Any,
78
+ *args: Any,
78
79
  query_type: str = "generic",
79
- table: t.Optional[t.Type[Table]] = None,
80
- alias: t.Optional[str] = None,
80
+ table: Optional[type[Table]] = None,
81
+ alias: Optional[str] = None,
81
82
  ) -> None:
82
83
  """
83
84
  :param template:
@@ -99,15 +100,13 @@ class QueryString(Selectable):
99
100
  self.template = template
100
101
  self.query_type = query_type
101
102
  self.table = table
102
- self._frozen_compiled_strings: t.Optional[
103
- t.Tuple[str, t.List[t.Any]]
104
- ] = None
103
+ self._frozen_compiled_strings: Optional[tuple[str, list[Any]]] = None
105
104
  self._alias = alias
106
105
  self.args, self.columns = self.process_args(args)
107
106
 
108
107
  def process_args(
109
- self, args: t.Sequence[t.Any]
110
- ) -> t.Tuple[t.Sequence[t.Any], t.Sequence[Column]]:
108
+ self, args: Sequence[Any]
109
+ ) -> tuple[Sequence[Any], Sequence[Column]]:
111
110
  """
112
111
  If a Column is passed in, we convert it to the name of the column
113
112
  (including joins).
@@ -168,8 +167,8 @@ class QueryString(Selectable):
168
167
  def bundle(
169
168
  self,
170
169
  start_index: int = 1,
171
- bundled: t.Optional[t.List[Fragment]] = None,
172
- combined_args: t.Optional[t.List] = None,
170
+ bundled: Optional[list[Fragment]] = None,
171
+ combined_args: Optional[list] = None,
173
172
  ):
174
173
  # Split up the string, separating by {}.
175
174
  fragments = [
@@ -206,7 +205,7 @@ class QueryString(Selectable):
206
205
 
207
206
  def compile_string(
208
207
  self, engine_type: str = "postgres"
209
- ) -> t.Tuple[str, t.List[t.Any]]:
208
+ ) -> tuple[str, list[Any]]:
210
209
  """
211
210
  Compiles the template ready for the engine - keeping the arguments
212
211
  separate from the template.
piccolo/schema.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import abc
4
- import typing as t
4
+ from typing import Optional, cast
5
5
 
6
6
  from piccolo.engine.base import Engine
7
7
  from piccolo.engine.finder import engine_finder
@@ -110,7 +110,7 @@ class MoveTable(SchemaDDLBase):
110
110
  table_name: str,
111
111
  new_schema: str,
112
112
  db: Engine,
113
- current_schema: t.Optional[str] = None,
113
+ current_schema: Optional[str] = None,
114
114
  ):
115
115
  self.table_name = table_name
116
116
  self.current_schema = current_schema
@@ -131,9 +131,9 @@ class ListTables:
131
131
  self.db = db
132
132
  self.schema_name = schema_name
133
133
 
134
- async def run(self) -> t.List[str]:
135
- response = t.cast(
136
- t.List[t.Dict],
134
+ async def run(self) -> list[str]:
135
+ response = cast(
136
+ list[dict],
137
137
  await self.db.run_querystring(
138
138
  QueryString(
139
139
  """
@@ -158,9 +158,9 @@ class ListSchemas:
158
158
  def __init__(self, db: Engine):
159
159
  self.db = db
160
160
 
161
- async def run(self) -> t.List[str]:
162
- response = t.cast(
163
- t.List[t.Dict],
161
+ async def run(self) -> list[str]:
162
+ response = cast(
163
+ list[dict],
164
164
  await self.db.run_querystring(
165
165
  QueryString(
166
166
  "SELECT schema_name FROM information_schema.schemata"
@@ -177,7 +177,7 @@ class ListSchemas:
177
177
 
178
178
 
179
179
  class SchemaManager:
180
- def __init__(self, db: t.Optional[Engine] = None):
180
+ def __init__(self, db: Optional[Engine] = None):
181
181
  """
182
182
  A useful utility class for interacting with schemas.
183
183
 
@@ -267,7 +267,7 @@ class SchemaManager:
267
267
  self,
268
268
  table_name: str,
269
269
  new_schema: str,
270
- current_schema: t.Optional[str] = None,
270
+ current_schema: Optional[str] = None,
271
271
  ) -> MoveTable:
272
272
  """
273
273
  Moves a table to a different schema::