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
@@ -2,8 +2,8 @@ import datetime
2
2
  import enum
3
3
  import random
4
4
  import string
5
- import typing as t
6
5
  import uuid
6
+ from typing import Any
7
7
 
8
8
 
9
9
  class RandomBuilder:
@@ -36,7 +36,7 @@ class RandomBuilder:
36
36
  )
37
37
 
38
38
  @classmethod
39
- def next_enum(cls, e: t.Type[enum.Enum]) -> t.Any:
39
+ def next_enum(cls, e: type[enum.Enum]) -> Any:
40
40
  return random.choice([item.value for item in e])
41
41
 
42
42
  @classmethod
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
3
+ from typing import Optional
4
4
  from unittest import IsolatedAsyncioTestCase, TestCase
5
5
 
6
6
  from piccolo.engine import Engine, engine_finder
@@ -30,7 +30,7 @@ class TableTest(TestCase):
30
30
 
31
31
  """ # noqa: E501
32
32
 
33
- tables: t.List[t.Type[Table]]
33
+ tables: list[type[Table]]
34
34
 
35
35
  def setUp(self) -> None:
36
36
  create_db_tables_sync(*self.tables)
@@ -54,7 +54,7 @@ class AsyncTableTest(IsolatedAsyncioTestCase):
54
54
 
55
55
  """
56
56
 
57
- tables: t.List[t.Type[Table]]
57
+ tables: list[type[Table]]
58
58
 
59
59
  async def asyncSetUp(self) -> None:
60
60
  await create_db_tables(*self.tables)
@@ -106,7 +106,7 @@ class AsyncTransactionTest(IsolatedAsyncioTestCase):
106
106
  #
107
107
  # ...
108
108
  #
109
- db: t.Optional[Engine] = None
109
+ db: Optional[Engine] = None
110
110
 
111
111
  async def asyncSetUp(self) -> None:
112
112
  db = self.db or engine_finder()
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
3
+ from typing import Any
4
4
 
5
5
 
6
- def make_nested(dictionary: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
6
+ def make_nested(dictionary: dict[str, Any]) -> dict[str, Any]:
7
7
  """
8
8
  Rows are returned from the database as a flat dictionary, with keys such
9
9
  as ``'manager.name'`` if the column belongs to a related table.
@@ -20,7 +20,7 @@ def make_nested(dictionary: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
20
20
  {'name': 'Pythonistas', 'band': {'name': 'Guido'}}
21
21
 
22
22
  """
23
- output: t.Dict[str, t.Any] = {}
23
+ output: dict[str, Any] = {}
24
24
 
25
25
  items = list(dictionary.items())
26
26
  items.sort(key=lambda x: x[0])
piccolo/utils/encoding.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
3
+ from typing import Any
4
4
 
5
5
  try:
6
6
  import orjson
@@ -12,9 +12,9 @@ except ImportError:
12
12
  ORJSON = False
13
13
 
14
14
 
15
- def dump_json(data: t.Any, pretty: bool = False) -> str:
15
+ def dump_json(data: Any, pretty: bool = False) -> str:
16
16
  if ORJSON:
17
- orjson_params: t.Dict[str, t.Any] = {"default": str}
17
+ orjson_params: dict[str, Any] = {"default": str}
18
18
  if pretty:
19
19
  orjson_params["option"] = (
20
20
  orjson.OPT_INDENT_2 | orjson.OPT_APPEND_NEWLINE # type: ignore
@@ -23,7 +23,7 @@ def dump_json(data: t.Any, pretty: bool = False) -> str:
23
23
  "utf8"
24
24
  )
25
25
  else:
26
- params: t.Dict[str, t.Any] = {"default": str}
26
+ params: dict[str, Any] = {"default": str}
27
27
  if pretty:
28
28
  params["indent"] = 2
29
29
  return json.dumps(data, **params) # type: ignore
@@ -63,7 +63,7 @@ class JSONDict(dict):
63
63
  ...
64
64
 
65
65
 
66
- def load_json(data: str) -> t.Any:
66
+ def load_json(data: str) -> Any:
67
67
  response = (
68
68
  orjson.loads(data) if ORJSON else json.loads(data) # type: ignore
69
69
  )
@@ -3,7 +3,7 @@ from __future__ import absolute_import, division, print_function
3
3
 
4
4
  import importlib
5
5
  import types
6
- import typing as t
6
+ from typing import Any
7
7
 
8
8
 
9
9
  class LazyLoader(types.ModuleType):
@@ -48,10 +48,10 @@ class LazyLoader(types.ModuleType):
48
48
  else:
49
49
  raise exc from exc
50
50
 
51
- def __getattr__(self, item) -> t.Any:
51
+ def __getattr__(self, item) -> Any:
52
52
  module = self._load()
53
53
  return getattr(module, item)
54
54
 
55
- def __dir__(self) -> t.List[str]:
55
+ def __dir__(self) -> list[str]:
56
56
  module = self._load()
57
57
  return dir(module)
piccolo/utils/list.py CHANGED
@@ -1,11 +1,12 @@
1
- import typing as t
1
+ from collections.abc import Sequence
2
+ from typing import TypeVar, Union
2
3
 
3
- ElementType = t.TypeVar("ElementType")
4
+ ElementType = TypeVar("ElementType")
4
5
 
5
6
 
6
7
  def flatten(
7
- items: t.Sequence[t.Union[ElementType, t.List[ElementType]]]
8
- ) -> t.List[ElementType]:
8
+ items: Sequence[Union[ElementType, list[ElementType]]]
9
+ ) -> list[ElementType]:
9
10
  """
10
11
  Takes a sequence of elements, and flattens it out. For example::
11
12
 
@@ -17,7 +18,7 @@ def flatten(
17
18
  await Band.select(Band.name, Band.manager.all_columns())
18
19
 
19
20
  """
20
- _items: t.List[ElementType] = []
21
+ _items: list[ElementType] = []
21
22
  for item in items:
22
23
  if isinstance(item, list):
23
24
  _items.extend(item)
@@ -27,9 +28,7 @@ def flatten(
27
28
  return _items
28
29
 
29
30
 
30
- def batch(
31
- data: t.List[ElementType], chunk_size: int
32
- ) -> t.List[t.List[ElementType]]:
31
+ def batch(data: list[ElementType], chunk_size: int) -> list[list[ElementType]]:
33
32
  """
34
33
  Breaks the list down into sublists of the given ``chunk_size``. The last
35
34
  sublist may have fewer elements than ``chunk_size``::
piccolo/utils/objects.py CHANGED
@@ -1,16 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from piccolo.columns.column_types import ForeignKey
6
6
 
7
- if t.TYPE_CHECKING: # pragma: no cover
7
+ if TYPE_CHECKING: # pragma: no cover
8
8
  from piccolo.table import Table
9
9
 
10
10
 
11
- def make_nested_object(
12
- row: t.Dict[str, t.Any], table_class: t.Type[Table]
13
- ) -> Table:
11
+ def make_nested_object(row: dict[str, Any], table_class: type[Table]) -> Table:
14
12
  """
15
13
  Takes a nested dictionary such as this:
16
14
 
@@ -38,7 +36,7 @@ def make_nested_object(
38
36
  1
39
37
 
40
38
  """
41
- table_params: t.Dict[str, t.Any] = {}
39
+ table_params: dict[str, Any] = {}
42
40
 
43
41
  for key, value in row.items():
44
42
  if isinstance(value, dict):
piccolo/utils/pydantic.py CHANGED
@@ -2,9 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import itertools
4
4
  import json
5
- import typing as t
6
5
  from collections import defaultdict
6
+ from collections.abc import Callable
7
7
  from functools import partial
8
+ from typing import Any, Optional, Union
8
9
 
9
10
  import pydantic
10
11
 
@@ -30,7 +31,7 @@ except ImportError:
30
31
  JsonDict = dict # type: ignore
31
32
 
32
33
 
33
- def pydantic_json_validator(value: t.Optional[str], required: bool = True):
34
+ def pydantic_json_validator(value: Optional[str], required: bool = True):
34
35
  if value is None:
35
36
  if required:
36
37
  raise ValueError("The JSON value wasn't provided.")
@@ -45,7 +46,7 @@ def pydantic_json_validator(value: t.Optional[str], required: bool = True):
45
46
  return value
46
47
 
47
48
 
48
- def is_table_column(column: Column, table: t.Type[Table]) -> bool:
49
+ def is_table_column(column: Column, table: type[Table]) -> bool:
49
50
  """
50
51
  Verify that the given ``Column`` belongs to the given ``Table``.
51
52
  """
@@ -60,9 +61,7 @@ def is_table_column(column: Column, table: t.Type[Table]) -> bool:
60
61
  return False
61
62
 
62
63
 
63
- def validate_columns(
64
- columns: t.Tuple[Column, ...], table: t.Type[Table]
65
- ) -> bool:
64
+ def validate_columns(columns: tuple[Column, ...], table: type[Table]) -> bool:
66
65
  """
67
66
  Verify that each column is a ``Column``` instance, and its parent is the
68
67
  given ``Table``.
@@ -74,9 +73,7 @@ def validate_columns(
74
73
  )
75
74
 
76
75
 
77
- def get_array_value_type(
78
- column: Array, inner: t.Optional[t.Type] = None
79
- ) -> t.Type:
76
+ def get_array_value_type(column: Array, inner: Optional[type] = None) -> type:
80
77
  """
81
78
  Gets the correct type for an ``Array`` column (which might be
82
79
  multidimensional).
@@ -86,14 +83,14 @@ def get_array_value_type(
86
83
  else:
87
84
  inner_type = get_pydantic_value_type(column.base_column)
88
85
 
89
- return t.List[inner_type] # type: ignore
86
+ return list[inner_type] # type: ignore
90
87
 
91
88
 
92
- def get_pydantic_value_type(column: Column) -> t.Type:
89
+ def get_pydantic_value_type(column: Column) -> type:
93
90
  """
94
91
  Map the Piccolo ``Column`` to a Pydantic type.
95
92
  """
96
- value_type: t.Type
93
+ value_type: type
97
94
 
98
95
  if isinstance(column, (Decimal, Numeric)):
99
96
  value_type = pydantic.condecimal(
@@ -112,20 +109,20 @@ def get_pydantic_value_type(column: Column) -> t.Type:
112
109
 
113
110
 
114
111
  def create_pydantic_model(
115
- table: t.Type[Table],
116
- nested: t.Union[bool, t.Tuple[ForeignKey, ...]] = False,
117
- exclude_columns: t.Tuple[Column, ...] = (),
118
- include_columns: t.Tuple[Column, ...] = (),
112
+ table: type[Table],
113
+ nested: Union[bool, tuple[ForeignKey, ...]] = False,
114
+ exclude_columns: tuple[Column, ...] = (),
115
+ include_columns: tuple[Column, ...] = (),
119
116
  include_default_columns: bool = False,
120
117
  include_readable: bool = False,
121
118
  all_optional: bool = False,
122
- model_name: t.Optional[str] = None,
119
+ model_name: Optional[str] = None,
123
120
  deserialize_json: bool = False,
124
121
  recursion_depth: int = 0,
125
122
  max_recursion_depth: int = 5,
126
- pydantic_config: t.Optional[pydantic.config.ConfigDict] = None,
127
- json_schema_extra: t.Optional[t.Dict[str, t.Any]] = None,
128
- ) -> t.Type[pydantic.BaseModel]:
123
+ pydantic_config: Optional[pydantic.config.ConfigDict] = None,
124
+ json_schema_extra: Optional[dict[str, Any]] = None,
125
+ ) -> type[pydantic.BaseModel]:
129
126
  """
130
127
  Create a Pydantic model representing a table.
131
128
 
@@ -205,8 +202,8 @@ def create_pydantic_model(
205
202
 
206
203
  ###########################################################################
207
204
 
208
- columns: t.Dict[str, t.Any] = {}
209
- validators: t.Dict[str, t.Callable] = {}
205
+ columns: dict[str, Any] = {}
206
+ validators: dict[str, Callable] = {}
210
207
 
211
208
  piccolo_columns = tuple(
212
209
  table._meta.columns
@@ -264,11 +261,11 @@ def create_pydantic_model(
264
261
  else:
265
262
  value_type = get_pydantic_value_type(column=column)
266
263
 
267
- _type = t.Optional[value_type] if is_optional else value_type
264
+ _type = Optional[value_type] if is_optional else value_type
268
265
 
269
266
  #######################################################################
270
267
 
271
- params: t.Dict[str, t.Any] = {}
268
+ params: dict[str, Any] = {}
272
269
  if is_optional:
273
270
  params["default"] = None
274
271
 
@@ -1,17 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import functools
4
- import typing as t
5
4
  from enum import Enum
5
+ from typing import TYPE_CHECKING, Any
6
6
 
7
7
  from piccolo.utils.encoding import dump_json
8
8
  from piccolo.utils.warnings import colored_warning
9
9
 
10
- if t.TYPE_CHECKING: # pragma: no cover
10
+ if TYPE_CHECKING: # pragma: no cover
11
11
  from piccolo.columns import Column
12
12
 
13
13
 
14
- def convert_to_sql_value(value: t.Any, column: Column) -> t.Any:
14
+ def convert_to_sql_value(value: Any, column: Column) -> Any:
15
15
  """
16
16
  Some values which can be passed into Piccolo queries aren't valid in the
17
17
  database. For example, Enums, Table instances, and dictionaries for JSON
piccolo/utils/sync.py CHANGED
@@ -1,14 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- import typing as t
4
+ from collections.abc import Coroutine
5
5
  from concurrent.futures import Future, ThreadPoolExecutor
6
+ from typing import Any, TypeVar
6
7
 
7
- ReturnType = t.TypeVar("ReturnType")
8
+ ReturnType = TypeVar("ReturnType")
8
9
 
9
10
 
10
11
  def run_sync(
11
- coroutine: t.Coroutine[t.Any, t.Any, ReturnType],
12
+ coroutine: Coroutine[Any, Any, ReturnType],
12
13
  ) -> ReturnType:
13
14
  """
14
15
  Run the coroutine synchronously - trying to accommodate as many edge cases
piccolo/utils/warnings.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing as t
4
3
  import warnings
5
4
  from enum import Enum
6
5
 
@@ -21,7 +20,7 @@ def colored_string(message: str, level: Level = Level.medium) -> str:
21
20
 
22
21
  def colored_warning(
23
22
  message: str,
24
- category: t.Type[Warning] = Warning,
23
+ category: type[Warning] = Warning,
25
24
  stacklevel: int = 3,
26
25
  level: Level = Level.medium,
27
26
  ):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: piccolo
3
- Version: 1.27.0
3
+ Version: 1.28.0
4
4
  Summary: A fast, user friendly ORM and query builder which supports asyncio.
5
5
  Home-page: https://github.com/piccolo-orm/piccolo
6
6
  Author: Daniel Townsend