piccolo 1.27.1__py3-none-any.whl → 1.29.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 (132) hide show
  1. piccolo/__init__.py +1 -1
  2. piccolo/apps/app/commands/new.py +3 -3
  3. piccolo/apps/asgi/commands/new.py +2 -3
  4. piccolo/apps/asgi/commands/templates/app/_blacksheep_app.py.jinja +57 -29
  5. piccolo/apps/asgi/commands/templates/app/_esmerald_app.py.jinja +48 -21
  6. piccolo/apps/asgi/commands/templates/app/_falcon_app.py.jinja +63 -8
  7. piccolo/apps/asgi/commands/templates/app/_fastapi_app.py.jinja +51 -24
  8. piccolo/apps/asgi/commands/templates/app/_litestar_app.py.jinja +34 -10
  9. piccolo/apps/asgi/commands/templates/app/_quart_app.py.jinja +38 -15
  10. piccolo/apps/asgi/commands/templates/app/_sanic_app.py.jinja +34 -11
  11. piccolo/apps/fixtures/commands/dump.py +8 -8
  12. piccolo/apps/fixtures/commands/load.py +5 -5
  13. piccolo/apps/fixtures/commands/shared.py +9 -9
  14. piccolo/apps/migrations/auto/diffable_table.py +12 -12
  15. piccolo/apps/migrations/auto/migration_manager.py +59 -66
  16. piccolo/apps/migrations/auto/operations.py +14 -14
  17. piccolo/apps/migrations/auto/schema_differ.py +35 -34
  18. piccolo/apps/migrations/auto/schema_snapshot.py +3 -4
  19. piccolo/apps/migrations/auto/serialisation.py +27 -24
  20. piccolo/apps/migrations/auto/serialisation_legacy.py +2 -2
  21. piccolo/apps/migrations/commands/backwards.py +1 -2
  22. piccolo/apps/migrations/commands/base.py +12 -12
  23. piccolo/apps/migrations/commands/check.py +2 -3
  24. piccolo/apps/migrations/commands/clean.py +3 -3
  25. piccolo/apps/migrations/commands/forwards.py +1 -2
  26. piccolo/apps/migrations/commands/new.py +6 -6
  27. piccolo/apps/migrations/tables.py +3 -3
  28. piccolo/apps/playground/commands/run.py +72 -13
  29. piccolo/apps/schema/commands/generate.py +49 -49
  30. piccolo/apps/schema/commands/graph.py +5 -5
  31. piccolo/apps/shell/commands/run.py +1 -2
  32. piccolo/apps/sql_shell/commands/run.py +4 -4
  33. piccolo/apps/tester/commands/run.py +3 -3
  34. piccolo/apps/user/commands/change_permissions.py +6 -6
  35. piccolo/apps/user/commands/create.py +7 -7
  36. piccolo/apps/user/commands/list.py +2 -2
  37. piccolo/apps/user/tables.py +8 -8
  38. piccolo/columns/base.py +84 -52
  39. piccolo/columns/choices.py +2 -2
  40. piccolo/columns/column_types.py +299 -177
  41. piccolo/columns/combination.py +15 -12
  42. piccolo/columns/defaults/base.py +4 -4
  43. piccolo/columns/defaults/date.py +4 -3
  44. piccolo/columns/defaults/interval.py +4 -3
  45. piccolo/columns/defaults/time.py +4 -3
  46. piccolo/columns/defaults/timestamp.py +4 -3
  47. piccolo/columns/defaults/timestamptz.py +4 -3
  48. piccolo/columns/defaults/uuid.py +3 -2
  49. piccolo/columns/m2m.py +28 -35
  50. piccolo/columns/readable.py +4 -3
  51. piccolo/columns/reference.py +9 -9
  52. piccolo/conf/apps.py +53 -54
  53. piccolo/custom_types.py +28 -6
  54. piccolo/engine/base.py +14 -14
  55. piccolo/engine/cockroach.py +5 -4
  56. piccolo/engine/finder.py +2 -2
  57. piccolo/engine/postgres.py +20 -19
  58. piccolo/engine/sqlite.py +23 -22
  59. piccolo/query/base.py +30 -29
  60. piccolo/query/functions/__init__.py +12 -0
  61. piccolo/query/functions/aggregate.py +4 -3
  62. piccolo/query/functions/array.py +151 -0
  63. piccolo/query/functions/base.py +3 -3
  64. piccolo/query/functions/datetime.py +22 -22
  65. piccolo/query/functions/string.py +4 -4
  66. piccolo/query/functions/type_conversion.py +30 -15
  67. piccolo/query/methods/alter.py +47 -46
  68. piccolo/query/methods/count.py +11 -10
  69. piccolo/query/methods/create.py +6 -5
  70. piccolo/query/methods/create_index.py +9 -8
  71. piccolo/query/methods/delete.py +7 -6
  72. piccolo/query/methods/drop_index.py +7 -6
  73. piccolo/query/methods/exists.py +6 -5
  74. piccolo/query/methods/indexes.py +4 -4
  75. piccolo/query/methods/insert.py +21 -14
  76. piccolo/query/methods/objects.py +60 -50
  77. piccolo/query/methods/raw.py +7 -6
  78. piccolo/query/methods/refresh.py +8 -7
  79. piccolo/query/methods/select.py +56 -49
  80. piccolo/query/methods/table_exists.py +5 -5
  81. piccolo/query/methods/update.py +8 -7
  82. piccolo/query/mixins.py +56 -61
  83. piccolo/query/operators/json.py +11 -11
  84. piccolo/query/proxy.py +8 -9
  85. piccolo/querystring.py +14 -15
  86. piccolo/schema.py +10 -10
  87. piccolo/table.py +105 -98
  88. piccolo/table_reflection.py +9 -9
  89. piccolo/testing/model_builder.py +16 -13
  90. piccolo/testing/random_builder.py +14 -2
  91. piccolo/testing/test_case.py +4 -4
  92. piccolo/utils/dictionary.py +3 -3
  93. piccolo/utils/encoding.py +5 -5
  94. piccolo/utils/lazy_loader.py +3 -3
  95. piccolo/utils/list.py +7 -8
  96. piccolo/utils/objects.py +4 -6
  97. piccolo/utils/pydantic.py +21 -24
  98. piccolo/utils/sql_values.py +3 -3
  99. piccolo/utils/sync.py +4 -3
  100. piccolo/utils/warnings.py +1 -2
  101. {piccolo-1.27.1.dist-info → piccolo-1.29.0.dist-info}/METADATA +1 -1
  102. {piccolo-1.27.1.dist-info → piccolo-1.29.0.dist-info}/RECORD +132 -131
  103. tests/apps/fixtures/commands/test_dump_load.py +1 -2
  104. tests/apps/migrations/auto/integration/test_migrations.py +32 -7
  105. tests/apps/migrations/auto/test_migration_manager.py +2 -2
  106. tests/apps/migrations/auto/test_schema_differ.py +22 -23
  107. tests/apps/migrations/commands/test_forwards_backwards.py +3 -3
  108. tests/columns/m2m/base.py +20 -49
  109. tests/columns/test_array.py +176 -10
  110. tests/columns/test_boolean.py +2 -4
  111. tests/columns/test_combination.py +29 -1
  112. tests/columns/test_db_column_name.py +2 -2
  113. tests/engine/test_extra_nodes.py +2 -2
  114. tests/engine/test_pool.py +3 -3
  115. tests/engine/test_transaction.py +4 -4
  116. tests/query/test_freeze.py +4 -4
  117. tests/table/instance/test_get_related.py +2 -2
  118. tests/table/test_alter.py +4 -4
  119. tests/table/test_indexes.py +1 -2
  120. tests/table/test_metaclass.py +7 -3
  121. tests/table/test_refresh.py +2 -2
  122. tests/table/test_select.py +58 -0
  123. tests/table/test_str.py +30 -22
  124. tests/table/test_update.py +18 -3
  125. tests/testing/test_model_builder.py +1 -2
  126. tests/testing/test_random_builder.py +5 -0
  127. tests/utils/test_pydantic.py +152 -134
  128. tests/utils/test_table_reflection.py +1 -2
  129. {piccolo-1.27.1.dist-info → piccolo-1.29.0.dist-info}/WHEEL +0 -0
  130. {piccolo-1.27.1.dist-info → piccolo-1.29.0.dist-info}/entry_points.txt +0 -0
  131. {piccolo-1.27.1.dist-info → piccolo-1.29.0.dist-info}/licenses/LICENSE +0 -0
  132. {piccolo-1.27.1.dist-info → piccolo-1.29.0.dist-info}/top_level.txt +0 -0
piccolo/conf/apps.py CHANGED
@@ -6,11 +6,12 @@ import itertools
6
6
  import os
7
7
  import pathlib
8
8
  import traceback
9
- import typing as t
10
9
  from abc import abstractmethod
10
+ from collections.abc import Callable, Sequence
11
11
  from dataclasses import dataclass, field
12
12
  from importlib import import_module
13
13
  from types import ModuleType
14
+ from typing import Optional, Union, cast
14
15
 
15
16
  import black
16
17
 
@@ -45,12 +46,12 @@ def get_package(name: str) -> str:
45
46
 
46
47
 
47
48
  def table_finder(
48
- modules: t.Sequence[str],
49
- package: t.Optional[str] = None,
50
- include_tags: t.Optional[t.Sequence[str]] = None,
51
- exclude_tags: t.Optional[t.Sequence[str]] = None,
49
+ modules: Sequence[str],
50
+ package: Optional[str] = None,
51
+ include_tags: Optional[Sequence[str]] = None,
52
+ exclude_tags: Optional[Sequence[str]] = None,
52
53
  exclude_imported: bool = False,
53
- ) -> t.List[t.Type[Table]]:
54
+ ) -> list[type[Table]]:
54
55
  """
55
56
  Rather than explicitly importing and registering table classes with the
56
57
  ``AppConfig``, ``table_finder`` can be used instead. It imports any ``Table``
@@ -95,7 +96,7 @@ def table_finder(
95
96
  # 'blog.tables', instead of ['blog.tables'].
96
97
  modules = [modules]
97
98
 
98
- table_subclasses: t.List[t.Type[Table]] = []
99
+ table_subclasses: list[type[Table]] = []
99
100
 
100
101
  for module_path in modules:
101
102
  full_module_path = (
@@ -151,9 +152,9 @@ class Command:
151
152
 
152
153
  """
153
154
 
154
- callable: t.Callable
155
- command_name: t.Optional[str] = None
156
- aliases: t.List[str] = field(default_factory=list)
155
+ callable: Callable
156
+ command_name: Optional[str] = None
157
+ aliases: list[str] = field(default_factory=list)
157
158
 
158
159
 
159
160
  @dataclass
@@ -181,12 +182,10 @@ class AppConfig:
181
182
  """
182
183
 
183
184
  app_name: str
184
- migrations_folder_path: t.Union[str, pathlib.Path]
185
- table_classes: t.List[t.Type[Table]] = field(default_factory=list)
186
- migration_dependencies: t.List[str] = field(default_factory=list)
187
- commands: t.List[t.Union[t.Callable, Command]] = field(
188
- default_factory=list
189
- )
185
+ migrations_folder_path: Union[str, pathlib.Path]
186
+ table_classes: list[type[Table]] = field(default_factory=list)
187
+ migration_dependencies: list[str] = field(default_factory=list)
188
+ commands: list[Union[Callable, Command]] = field(default_factory=list)
190
189
 
191
190
  @property
192
191
  def resolved_migrations_folder_path(self) -> str:
@@ -197,21 +196,21 @@ class AppConfig:
197
196
  )
198
197
 
199
198
  def __post_init__(self) -> None:
200
- self._migration_dependency_app_configs: t.Optional[
201
- t.List[AppConfig]
202
- ] = None
199
+ self._migration_dependency_app_configs: Optional[list[AppConfig]] = (
200
+ None
201
+ )
203
202
 
204
- def register_table(self, table_class: t.Type[Table]):
203
+ def register_table(self, table_class: type[Table]):
205
204
  self.table_classes.append(table_class)
206
205
  return table_class
207
206
 
208
- def get_commands(self) -> t.List[Command]:
207
+ def get_commands(self) -> list[Command]:
209
208
  return [
210
209
  i if isinstance(i, Command) else Command(i) for i in self.commands
211
210
  ]
212
211
 
213
212
  @property
214
- def migration_dependency_app_configs(self) -> t.List[AppConfig]:
213
+ def migration_dependency_app_configs(self) -> list[AppConfig]:
215
214
  """
216
215
  Get all of the ``AppConfig`` instances from this app's migration
217
216
  dependencies.
@@ -219,8 +218,8 @@ class AppConfig:
219
218
  # We cache the value so it's more efficient, and also so we can set the
220
219
  # underlying value in unit tests for easier mocking.
221
220
  if self._migration_dependency_app_configs is None:
222
- modules: t.List[PiccoloAppModule] = [
223
- t.cast(PiccoloAppModule, import_module(module_path))
221
+ modules: list[PiccoloAppModule] = [
222
+ cast(PiccoloAppModule, import_module(module_path))
224
223
  for module_path in self.migration_dependencies
225
224
  ]
226
225
  self._migration_dependency_app_configs = [
@@ -229,7 +228,7 @@ class AppConfig:
229
228
 
230
229
  return self._migration_dependency_app_configs
231
230
 
232
- def get_table_with_name(self, table_class_name: str) -> t.Type[Table]:
231
+ def get_table_with_name(self, table_class_name: str) -> type[Table]:
233
232
  """
234
233
  Returns a ``Table`` subclass with the given name from this app, if it
235
234
  exists. Otherwise raises a ``ValueError``.
@@ -256,9 +255,9 @@ class AppRegistry:
256
255
 
257
256
  """
258
257
 
259
- def __init__(self, apps: t.Optional[t.List[str]] = None):
258
+ def __init__(self, apps: Optional[list[str]] = None):
260
259
  self.apps = apps or []
261
- self.app_configs: t.Dict[str, AppConfig] = {}
260
+ self.app_configs: dict[str, AppConfig] = {}
262
261
  app_names = []
263
262
 
264
263
  for app in self.apps:
@@ -282,7 +281,7 @@ class AppRegistry:
282
281
  self._validate_app_names(app_names)
283
282
 
284
283
  @staticmethod
285
- def _validate_app_names(app_names: t.List[str]):
284
+ def _validate_app_names(app_names: list[str]):
286
285
  """
287
286
  Raise a ValueError if an app_name is repeated.
288
287
  """
@@ -298,10 +297,10 @@ class AppRegistry:
298
297
  "multiple times."
299
298
  )
300
299
 
301
- def get_app_config(self, app_name: str) -> t.Optional[AppConfig]:
300
+ def get_app_config(self, app_name: str) -> Optional[AppConfig]:
302
301
  return self.app_configs.get(app_name)
303
302
 
304
- def get_table_classes(self, app_name: str) -> t.List[t.Type[Table]]:
303
+ def get_table_classes(self, app_name: str) -> list[type[Table]]:
305
304
  """
306
305
  Returns each Table subclass defined in the given app if it exists.
307
306
  Otherwise raises a ValueError.
@@ -317,7 +316,7 @@ class AppRegistry:
317
316
 
318
317
  def get_table_with_name(
319
318
  self, app_name: str, table_class_name: str
320
- ) -> t.Optional[t.Type[Table]]:
319
+ ) -> Optional[type[Table]]:
321
320
  """
322
321
  Returns a Table subclass registered with the given app if it exists.
323
322
  Otherwise raises a ValueError.
@@ -357,8 +356,8 @@ class Finder:
357
356
  self.diagnose = diagnose
358
357
 
359
358
  def _deduplicate(
360
- self, config_modules: t.List[PiccoloAppModule]
361
- ) -> t.List[PiccoloAppModule]:
359
+ self, config_modules: list[PiccoloAppModule]
360
+ ) -> list[PiccoloAppModule]:
362
361
  """
363
362
  Remove all duplicates - just leaving the first instance.
364
363
  """
@@ -366,8 +365,8 @@ class Finder:
366
365
  return list({c: None for c in config_modules}.keys())
367
366
 
368
367
  def _import_app_modules(
369
- self, config_module_paths: t.List[str]
370
- ) -> t.List[PiccoloAppModule]:
368
+ self, config_module_paths: list[str]
369
+ ) -> list[PiccoloAppModule]:
371
370
  """
372
371
  Import all piccolo_app.py modules within your apps, and all
373
372
  dependencies.
@@ -376,7 +375,7 @@ class Finder:
376
375
 
377
376
  for config_module_path in config_module_paths:
378
377
  try:
379
- config_module = t.cast(
378
+ config_module = cast(
380
379
  PiccoloAppModule, import_module(config_module_path)
381
380
  )
382
381
  except ImportError as e:
@@ -392,8 +391,8 @@ class Finder:
392
391
  return config_modules
393
392
 
394
393
  def get_piccolo_conf_module(
395
- self, module_name: t.Optional[str] = None
396
- ) -> t.Optional[PiccoloConfModule]:
394
+ self, module_name: Optional[str] = None
395
+ ) -> Optional[PiccoloConfModule]:
397
396
  """
398
397
  Searches the path for a 'piccolo_conf.py' module to import. The
399
398
  location searched can be overriden by:
@@ -413,7 +412,7 @@ class Finder:
413
412
  module_name = DEFAULT_MODULE_NAME
414
413
 
415
414
  try:
416
- module = t.cast(PiccoloConfModule, import_module(module_name))
415
+ module = cast(PiccoloConfModule, import_module(module_name))
417
416
  except ModuleNotFoundError as exc:
418
417
  if self.diagnose:
419
418
  colored_warning(
@@ -459,10 +458,10 @@ class Finder:
459
458
  return getattr(piccolo_conf_module, "APP_REGISTRY")
460
459
 
461
460
  def get_engine(
462
- self, module_name: t.Optional[str] = None
463
- ) -> t.Optional[Engine]:
461
+ self, module_name: Optional[str] = None
462
+ ) -> Optional[Engine]:
464
463
  piccolo_conf = self.get_piccolo_conf_module(module_name=module_name)
465
- engine: t.Optional[Engine] = getattr(piccolo_conf, ENGINE_VAR, None)
464
+ engine: Optional[Engine] = getattr(piccolo_conf, ENGINE_VAR, None)
466
465
 
467
466
  if not engine:
468
467
  colored_warning(
@@ -478,7 +477,7 @@ class Finder:
478
477
 
479
478
  return engine
480
479
 
481
- def get_app_modules(self) -> t.List[PiccoloAppModule]:
480
+ def get_app_modules(self) -> list[PiccoloAppModule]:
482
481
  """
483
482
  Returns the ``piccolo_app.py`` modules for each registered Piccolo app
484
483
  in your project.
@@ -493,7 +492,7 @@ class Finder:
493
492
 
494
493
  def get_app_names(
495
494
  self, sort_by_migration_dependencies: bool = True
496
- ) -> t.List[str]:
495
+ ) -> list[str]:
497
496
  """
498
497
  Return all of the app names.
499
498
 
@@ -509,15 +508,15 @@ class Finder:
509
508
  )
510
509
  ]
511
510
 
512
- def get_sorted_app_names(self) -> t.List[str]:
511
+ def get_sorted_app_names(self) -> list[str]:
513
512
  """
514
513
  Just here for backwards compatibility - use ``get_app_names`` directly.
515
514
  """
516
515
  return self.get_app_names(sort_by_migration_dependencies=True)
517
516
 
518
517
  def sort_app_configs(
519
- self, app_configs: t.List[AppConfig]
520
- ) -> t.List[AppConfig]:
518
+ self, app_configs: list[AppConfig]
519
+ ) -> list[AppConfig]:
521
520
  app_config_map = {
522
521
  app_config.app_name: app_config for app_config in app_configs
523
522
  }
@@ -536,7 +535,7 @@ class Finder:
536
535
 
537
536
  def get_app_configs(
538
537
  self, sort_by_migration_dependencies: bool = True
539
- ) -> t.List[AppConfig]:
538
+ ) -> list[AppConfig]:
540
539
  """
541
540
  Returns a list of ``AppConfig``, optionally sorted by migration
542
541
  dependencies.
@@ -560,7 +559,7 @@ class Finder:
560
559
 
561
560
  def get_table_with_name(
562
561
  self, app_name: str, table_class_name: str
563
- ) -> t.Type[Table]:
562
+ ) -> type[Table]:
564
563
  """
565
564
  Returns a ``Table`` class registered with the given app if it exists.
566
565
  Otherwise it raises an ``ValueError``.
@@ -572,9 +571,9 @@ class Finder:
572
571
 
573
572
  def get_table_classes(
574
573
  self,
575
- include_apps: t.Optional[t.List[str]] = None,
576
- exclude_apps: t.Optional[t.List[str]] = None,
577
- ) -> t.List[t.Type[Table]]:
574
+ include_apps: Optional[list[str]] = None,
575
+ exclude_apps: Optional[list[str]] = None,
576
+ ) -> list[type[Table]]:
578
577
  """
579
578
  Returns all ``Table`` classes registered with the given apps. If
580
579
  ``include_apps`` is ``None``, then ``Table`` classes will be returned
@@ -590,7 +589,7 @@ class Finder:
590
589
  if exclude_apps:
591
590
  app_names = [i for i in app_names if i not in exclude_apps]
592
591
 
593
- tables: t.List[t.Type[Table]] = []
592
+ tables: list[type[Table]] = []
594
593
 
595
594
  for app_name in app_names:
596
595
  app_config = self.get_app_config(app_name=app_name)
@@ -604,7 +603,7 @@ class Finder:
604
603
 
605
604
  class PiccoloConfUpdater:
606
605
 
607
- def __init__(self, piccolo_conf_path: t.Optional[str] = None):
606
+ def __init__(self, piccolo_conf_path: Optional[str] = None):
608
607
  """
609
608
  :param piccolo_conf_path:
610
609
  The path to the piccolo_conf.py (e.g. `./piccolo_conf.py`). If not
piccolo/custom_types.py CHANGED
@@ -1,19 +1,41 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
3
+ import datetime
4
+ import decimal
5
+ import uuid
6
+ from collections.abc import Iterable
7
+ from typing import TYPE_CHECKING, Any, TypeVar, Union
4
8
 
5
- if t.TYPE_CHECKING: # pragma: no cover
9
+ from typing_extensions import TypeAlias
10
+
11
+ if TYPE_CHECKING: # pragma: no cover
6
12
  from piccolo.columns.combination import And, Or, Where, WhereRaw # noqa
7
13
  from piccolo.table import Table
8
14
 
9
15
 
10
- Combinable = t.Union["Where", "WhereRaw", "And", "Or"]
11
- Iterable = t.Iterable[t.Any]
16
+ Combinable = Union["Where", "WhereRaw", "And", "Or"]
17
+ CustomIterable = Iterable[Any]
18
+
12
19
 
20
+ TableInstance = TypeVar("TableInstance", bound="Table")
21
+ QueryResponseType = TypeVar("QueryResponseType", bound=Any)
13
22
 
14
- TableInstance = t.TypeVar("TableInstance", bound="Table")
15
- QueryResponseType = t.TypeVar("QueryResponseType", bound=t.Any)
16
23
 
24
+ # These are types we can reasonably expect to send to the database.
25
+ BasicTypes: TypeAlias = Union[
26
+ bytes,
27
+ datetime.date,
28
+ datetime.datetime,
29
+ datetime.time,
30
+ datetime.timedelta,
31
+ decimal.Decimal,
32
+ dict,
33
+ float,
34
+ int,
35
+ list,
36
+ str,
37
+ uuid.UUID,
38
+ ]
17
39
 
18
40
  ###############################################################################
19
41
  # For backwards compatibility:
piccolo/engine/base.py CHANGED
@@ -4,8 +4,8 @@ import contextvars
4
4
  import logging
5
5
  import pprint
6
6
  import string
7
- import typing as t
8
7
  from abc import ABCMeta, abstractmethod
8
+ from typing import TYPE_CHECKING, Final, Generic, Optional, TypeVar, Union
9
9
 
10
10
  from typing_extensions import Self
11
11
 
@@ -13,14 +13,14 @@ from piccolo.querystring import QueryString
13
13
  from piccolo.utils.sync import run_sync
14
14
  from piccolo.utils.warnings import Level, colored_string, colored_warning
15
15
 
16
- if t.TYPE_CHECKING: # pragma: no cover
16
+ if TYPE_CHECKING: # pragma: no cover
17
17
  from piccolo.query.base import DDL, Query
18
18
 
19
19
 
20
20
  logger = logging.getLogger(__name__)
21
21
  # This is a set to speed up lookups from O(n) when
22
22
  # using str vs O(1) when using set[str]
23
- VALID_SAVEPOINT_CHARACTERS: t.Final[set[str]] = set(
23
+ VALID_SAVEPOINT_CHARACTERS: Final[set[str]] = set(
24
24
  string.ascii_letters + string.digits + "-" + "_"
25
25
  )
26
26
 
@@ -45,12 +45,12 @@ class BaseBatch(metaclass=ABCMeta):
45
45
  def __aiter__(self: Self) -> Self: ...
46
46
 
47
47
  @abstractmethod
48
- async def __anext__(self) -> t.List[t.Dict]: ...
48
+ async def __anext__(self) -> list[dict]: ...
49
49
 
50
50
 
51
51
  class BaseTransaction(metaclass=ABCMeta):
52
52
 
53
- __slots__: t.Tuple[str, ...] = tuple()
53
+ __slots__: tuple[str, ...] = tuple()
54
54
 
55
55
  @abstractmethod
56
56
  async def __aenter__(self, *args, **kwargs): ...
@@ -61,10 +61,10 @@ class BaseTransaction(metaclass=ABCMeta):
61
61
 
62
62
  class BaseAtomic(metaclass=ABCMeta):
63
63
 
64
- __slots__: t.Tuple[str, ...] = tuple()
64
+ __slots__: tuple[str, ...] = tuple()
65
65
 
66
66
  @abstractmethod
67
- def add(self, *query: t.Union[Query, DDL]): ...
67
+ def add(self, *query: Union[Query, DDL]): ...
68
68
 
69
69
  @abstractmethod
70
70
  async def run(self): ...
@@ -76,10 +76,10 @@ class BaseAtomic(metaclass=ABCMeta):
76
76
  def __await__(self): ...
77
77
 
78
78
 
79
- TransactionClass = t.TypeVar("TransactionClass", bound=BaseTransaction)
79
+ TransactionClass = TypeVar("TransactionClass", bound=BaseTransaction)
80
80
 
81
81
 
82
- class Engine(t.Generic[TransactionClass], metaclass=ABCMeta):
82
+ class Engine(Generic[TransactionClass], metaclass=ABCMeta):
83
83
  __slots__ = (
84
84
  "query_id",
85
85
  "log_queries",
@@ -92,7 +92,7 @@ class Engine(t.Generic[TransactionClass], metaclass=ABCMeta):
92
92
  def __init__(
93
93
  self,
94
94
  engine_type: str,
95
- min_version_number: t.Union[int, float],
95
+ min_version_number: Union[int, float],
96
96
  log_queries: bool = False,
97
97
  log_responses: bool = False,
98
98
  ):
@@ -122,7 +122,7 @@ class Engine(t.Generic[TransactionClass], metaclass=ABCMeta):
122
122
  self,
123
123
  query: Query,
124
124
  batch_size: int = 100,
125
- node: t.Optional[str] = None,
125
+ node: Optional[str] = None,
126
126
  ) -> BaseBatch:
127
127
  pass
128
128
 
@@ -132,7 +132,7 @@ class Engine(t.Generic[TransactionClass], metaclass=ABCMeta):
132
132
  ):
133
133
  pass
134
134
 
135
- def transform_response_to_dicts(self, results) -> t.List[t.Dict]:
135
+ def transform_response_to_dicts(self, results) -> list[dict]:
136
136
  """
137
137
  If the database adapter returns something other than a list of
138
138
  dictionaries, it should perform the transformation here.
@@ -196,7 +196,7 @@ class Engine(t.Generic[TransactionClass], metaclass=ABCMeta):
196
196
 
197
197
  ###########################################################################
198
198
 
199
- current_transaction: contextvars.ContextVar[t.Optional[TransactionClass]]
199
+ current_transaction: contextvars.ContextVar[Optional[TransactionClass]]
200
200
 
201
201
  def transaction_exists(self) -> bool:
202
202
  """
@@ -222,7 +222,7 @@ class Engine(t.Generic[TransactionClass], metaclass=ABCMeta):
222
222
  print(colored_string(f"\nQuery {query_id}:"))
223
223
  print(query)
224
224
 
225
- def print_response(self, query_id: int, response: t.List):
225
+ def print_response(self, query_id: int, response: list):
226
226
  print(
227
227
  colored_string(f"\nQuery {query_id} response:", level=Level.high)
228
228
  )
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
3
+ from collections.abc import Sequence
4
+ from typing import Any, Optional
4
5
 
5
6
  from piccolo.utils.lazy_loader import LazyLoader
6
7
  from piccolo.utils.warnings import Level, colored_warning
@@ -18,11 +19,11 @@ class CockroachEngine(PostgresEngine):
18
19
 
19
20
  def __init__(
20
21
  self,
21
- config: t.Dict[str, t.Any],
22
- extensions: t.Sequence[str] = (),
22
+ config: dict[str, Any],
23
+ extensions: Sequence[str] = (),
23
24
  log_queries: bool = False,
24
25
  log_responses: bool = False,
25
- extra_nodes: t.Optional[t.Dict[str, CockroachEngine]] = None,
26
+ extra_nodes: Optional[dict[str, CockroachEngine]] = None,
26
27
  ) -> None:
27
28
  super().__init__(
28
29
  config=config,
piccolo/engine/finder.py CHANGED
@@ -1,11 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
3
+ from typing import Optional
4
4
 
5
5
  from piccolo.engine.base import Engine
6
6
 
7
7
 
8
- def engine_finder(module_name: t.Optional[str] = None) -> t.Optional[Engine]:
8
+ def engine_finder(module_name: Optional[str] = None) -> Optional[Engine]:
9
9
  """
10
10
  An example module name is `my_piccolo_conf`.
11
11
 
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import contextvars
4
- import typing as t
4
+ from collections.abc import Sequence
5
5
  from dataclasses import dataclass
6
+ from typing import TYPE_CHECKING, Any, Mapping, Optional, Union
6
7
 
7
8
  from typing_extensions import Self
8
9
 
@@ -22,7 +23,7 @@ from piccolo.utils.warnings import Level, colored_warning
22
23
 
23
24
  asyncpg = LazyLoader("asyncpg", globals(), "asyncpg")
24
25
 
25
- if t.TYPE_CHECKING: # pragma: no cover
26
+ if TYPE_CHECKING: # pragma: no cover
26
27
  from asyncpg.connection import Connection
27
28
  from asyncpg.cursor import Cursor
28
29
  from asyncpg.pool import Pool
@@ -36,8 +37,8 @@ class AsyncBatch(BaseBatch):
36
37
  batch_size: int
37
38
 
38
39
  # Set internally
39
- _transaction: t.Optional[Transaction] = None
40
- _cursor: t.Optional[Cursor] = None
40
+ _transaction: Optional[Transaction] = None
41
+ _cursor: Optional[Cursor] = None
41
42
 
42
43
  @property
43
44
  def cursor(self) -> Cursor:
@@ -51,14 +52,14 @@ class AsyncBatch(BaseBatch):
51
52
  raise ValueError("The transaction can't be found.")
52
53
  return self._transaction
53
54
 
54
- async def next(self) -> t.List[t.Dict]:
55
+ async def next(self) -> list[dict]:
55
56
  data = await self.cursor.fetch(self.batch_size)
56
57
  return await self.query._process_results(data)
57
58
 
58
59
  def __aiter__(self: Self) -> Self:
59
60
  return self
60
61
 
61
- async def __anext__(self) -> t.List[t.Dict]:
62
+ async def __anext__(self) -> list[dict]:
62
63
  response = await self.next()
63
64
  if response == []:
64
65
  raise StopAsyncIteration()
@@ -107,9 +108,9 @@ class Atomic(BaseAtomic):
107
108
 
108
109
  def __init__(self, engine: PostgresEngine):
109
110
  self.engine = engine
110
- self.queries: t.List[t.Union[Query, DDL]] = []
111
+ self.queries: list[Union[Query, DDL]] = []
111
112
 
112
- def add(self, *query: t.Union[Query, DDL]):
113
+ def add(self, *query: Union[Query, DDL]):
113
114
  self.queries += list(query)
114
115
 
115
116
  async def run(self):
@@ -250,7 +251,7 @@ class PostgresTransaction(BaseTransaction):
250
251
  self._savepoint_id += 1
251
252
  return self._savepoint_id
252
253
 
253
- async def savepoint(self, name: t.Optional[str] = None) -> Savepoint:
254
+ async def savepoint(self, name: Optional[str] = None) -> Savepoint:
254
255
  name = name or f"savepoint_{self.get_savepoint_id()}"
255
256
  validate_savepoint_name(name)
256
257
  await self.connection.execute(f"SAVEPOINT {name}")
@@ -352,11 +353,11 @@ class PostgresEngine(Engine[PostgresTransaction]):
352
353
 
353
354
  def __init__(
354
355
  self,
355
- config: t.Dict[str, t.Any],
356
- extensions: t.Sequence[str] = ("uuid-ossp",),
356
+ config: dict[str, Any],
357
+ extensions: Sequence[str] = ("uuid-ossp",),
357
358
  log_queries: bool = False,
358
359
  log_responses: bool = False,
359
- extra_nodes: t.Optional[t.Mapping[str, PostgresEngine]] = None,
360
+ extra_nodes: Optional[Mapping[str, PostgresEngine]] = None,
360
361
  ) -> None:
361
362
  if extra_nodes is None:
362
363
  extra_nodes = {}
@@ -366,7 +367,7 @@ class PostgresEngine(Engine[PostgresTransaction]):
366
367
  self.log_queries = log_queries
367
368
  self.log_responses = log_responses
368
369
  self.extra_nodes = extra_nodes
369
- self.pool: t.Optional[Pool] = None
370
+ self.pool: Optional[Pool] = None
370
371
  database_name = config.get("database", "Unknown")
371
372
  self.current_transaction = contextvars.ContextVar(
372
373
  f"pg_current_transaction_{database_name}", default=None
@@ -396,7 +397,7 @@ class PostgresEngine(Engine[PostgresTransaction]):
396
397
  Returns the version of Postgres being run.
397
398
  """
398
399
  try:
399
- response: t.Sequence[t.Dict] = await self._run_in_new_connection(
400
+ response: Sequence[dict] = await self._run_in_new_connection(
400
401
  "SHOW server_version"
401
402
  )
402
403
  except ConnectionRefusedError as exception:
@@ -483,7 +484,7 @@ class PostgresEngine(Engine[PostgresTransaction]):
483
484
  self,
484
485
  query: Query,
485
486
  batch_size: int = 100,
486
- node: t.Optional[str] = None,
487
+ node: Optional[str] = None,
487
488
  ) -> AsyncBatch:
488
489
  """
489
490
  :param query:
@@ -494,7 +495,7 @@ class PostgresEngine(Engine[PostgresTransaction]):
494
495
  Which node to run the query on (see ``extra_nodes``). If not
495
496
  specified, it runs on the main Postgres node.
496
497
  """
497
- engine: t.Any = self.extra_nodes.get(node) if node else self
498
+ engine: Any = self.extra_nodes.get(node) if node else self
498
499
  connection = await engine.get_new_connection()
499
500
  return AsyncBatch(
500
501
  connection=connection, query=query, batch_size=batch_size
@@ -503,7 +504,7 @@ class PostgresEngine(Engine[PostgresTransaction]):
503
504
  ###########################################################################
504
505
 
505
506
  async def _run_in_pool(
506
- self, query: str, args: t.Optional[t.Sequence[t.Any]] = None
507
+ self, query: str, args: Optional[Sequence[Any]] = None
507
508
  ):
508
509
  if args is None:
509
510
  args = []
@@ -516,7 +517,7 @@ class PostgresEngine(Engine[PostgresTransaction]):
516
517
  return response
517
518
 
518
519
  async def _run_in_new_connection(
519
- self, query: str, args: t.Optional[t.Sequence[t.Any]] = None
520
+ self, query: str, args: Optional[Sequence[Any]] = None
520
521
  ):
521
522
  if args is None:
522
523
  args = []
@@ -579,7 +580,7 @@ class PostgresEngine(Engine[PostgresTransaction]):
579
580
 
580
581
  return response
581
582
 
582
- def transform_response_to_dicts(self, results) -> t.List[t.Dict]:
583
+ def transform_response_to_dicts(self, results) -> list[dict]:
583
584
  """
584
585
  asyncpg returns a special Record object, so we need to convert it to
585
586
  a dict.