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
@@ -5,9 +5,9 @@ import dataclasses
5
5
  import itertools
6
6
  import json
7
7
  import re
8
- import typing as t
9
8
  import uuid
10
9
  from datetime import date, datetime
10
+ from typing import TYPE_CHECKING, Any, Literal, Optional, Union
11
11
 
12
12
  import black
13
13
 
@@ -43,7 +43,7 @@ from piccolo.engine.postgres import PostgresEngine
43
43
  from piccolo.table import Table, create_table_class, sort_table_classes
44
44
  from piccolo.utils.naming import _snake_to_camel
45
45
 
46
- if t.TYPE_CHECKING: # pragma: no cover
46
+ if TYPE_CHECKING: # pragma: no cover
47
47
  from piccolo.engine.base import Engine
48
48
 
49
49
 
@@ -61,13 +61,13 @@ class ConstraintTable:
61
61
  class RowMeta:
62
62
  column_default: str
63
63
  column_name: str
64
- is_nullable: t.Literal["YES", "NO"]
64
+ is_nullable: Literal["YES", "NO"]
65
65
  table_name: str
66
- character_maximum_length: t.Optional[int]
66
+ character_maximum_length: Optional[int]
67
67
  data_type: str
68
- numeric_precision: t.Optional[t.Union[int, str]]
69
- numeric_scale: t.Optional[t.Union[int, str]]
70
- numeric_precision_radix: t.Optional[t.Literal[2, 10]]
68
+ numeric_precision: Optional[Union[int, str]]
69
+ numeric_scale: Optional[Union[int, str]]
70
+ numeric_precision_radix: Optional[Literal[2, 10]]
71
71
 
72
72
  @classmethod
73
73
  def get_column_name_str(cls) -> str:
@@ -76,10 +76,10 @@ class RowMeta:
76
76
 
77
77
  @dataclasses.dataclass
78
78
  class Constraint:
79
- constraint_type: t.Literal["PRIMARY KEY", "UNIQUE", "FOREIGN KEY", "CHECK"]
79
+ constraint_type: Literal["PRIMARY KEY", "UNIQUE", "FOREIGN KEY", "CHECK"]
80
80
  constraint_name: str
81
- constraint_schema: t.Optional[str] = None
82
- column_name: t.Optional[str] = None
81
+ constraint_schema: Optional[str] = None
82
+ column_name: Optional[str] = None
83
83
 
84
84
 
85
85
  @dataclasses.dataclass
@@ -89,12 +89,12 @@ class TableConstraints:
89
89
  """
90
90
 
91
91
  tablename: str
92
- constraints: t.List[Constraint]
92
+ constraints: list[Constraint]
93
93
 
94
94
  def __post_init__(self) -> None:
95
- foreign_key_constraints: t.List[Constraint] = []
96
- unique_constraints: t.List[Constraint] = []
97
- primary_key_constraints: t.List[Constraint] = []
95
+ foreign_key_constraints: list[Constraint] = []
96
+ unique_constraints: list[Constraint] = []
97
+ primary_key_constraints: list[Constraint] = []
98
98
 
99
99
  for constraint in self.constraints:
100
100
  if constraint.constraint_type == "FOREIGN KEY":
@@ -141,7 +141,7 @@ class Trigger:
141
141
  table_name: str
142
142
  column_name: str
143
143
  on_update: str
144
- on_delete: t.Literal[
144
+ on_delete: Literal[
145
145
  "NO ACTION", "RESTRICT", "CASCADE", "SET NULL", "SET_DEFAULT"
146
146
  ]
147
147
  references_table: str
@@ -155,14 +155,14 @@ class TableTriggers:
155
155
  """
156
156
 
157
157
  tablename: str
158
- triggers: t.List[Trigger]
158
+ triggers: list[Trigger]
159
159
 
160
- def get_column_triggers(self, column_name: str) -> t.List[Trigger]:
160
+ def get_column_triggers(self, column_name: str) -> list[Trigger]:
161
161
  return [i for i in self.triggers if i.column_name == column_name]
162
162
 
163
163
  def get_column_ref_trigger(
164
164
  self, column_name: str, references_table: str
165
- ) -> t.Optional[Trigger]:
165
+ ) -> Optional[Trigger]:
166
166
  for trigger in self.triggers:
167
167
  if (
168
168
  trigger.column_name == column_name
@@ -221,14 +221,14 @@ class TableIndexes:
221
221
  """
222
222
 
223
223
  tablename: str
224
- indexes: t.List[Index]
224
+ indexes: list[Index]
225
225
 
226
- def get_column_index(self, column_name: str) -> t.Optional[Index]:
226
+ def get_column_index(self, column_name: str) -> Optional[Index]:
227
227
  return next(
228
228
  (i for i in self.indexes if i.column_name == column_name), None
229
229
  )
230
230
 
231
- def get_warnings(self) -> t.List[str]:
231
+ def get_warnings(self) -> list[str]:
232
232
  return list(
233
233
  itertools.chain(*[index.warnings for index in self.indexes])
234
234
  )
@@ -250,13 +250,13 @@ class OutputSchema:
250
250
  e.g. ["class MyTable(Table): ..."]
251
251
  """
252
252
 
253
- imports: t.List[str] = dataclasses.field(default_factory=list)
254
- warnings: t.List[str] = dataclasses.field(default_factory=list)
255
- index_warnings: t.List[str] = dataclasses.field(default_factory=list)
256
- trigger_warnings: t.List[str] = dataclasses.field(default_factory=list)
257
- tables: t.List[t.Type[Table]] = dataclasses.field(default_factory=list)
253
+ imports: list[str] = dataclasses.field(default_factory=list)
254
+ warnings: list[str] = dataclasses.field(default_factory=list)
255
+ index_warnings: list[str] = dataclasses.field(default_factory=list)
256
+ trigger_warnings: list[str] = dataclasses.field(default_factory=list)
257
+ tables: list[type[Table]] = dataclasses.field(default_factory=list)
258
258
 
259
- def get_table_with_name(self, tablename: str) -> t.Optional[t.Type[Table]]:
259
+ def get_table_with_name(self, tablename: str) -> Optional[type[Table]]:
260
260
  """
261
261
  Used to search for a table by name.
262
262
  """
@@ -287,7 +287,7 @@ class OutputSchema:
287
287
  return self
288
288
 
289
289
 
290
- COLUMN_TYPE_MAP: t.Dict[str, t.Type[Column]] = {
290
+ COLUMN_TYPE_MAP: dict[str, type[Column]] = {
291
291
  "bigint": BigInt,
292
292
  "boolean": Boolean,
293
293
  "bytea": Bytea,
@@ -308,12 +308,12 @@ COLUMN_TYPE_MAP: t.Dict[str, t.Type[Column]] = {
308
308
  }
309
309
 
310
310
  # Re-map for Cockroach compatibility.
311
- COLUMN_TYPE_MAP_COCKROACH: t.Dict[str, t.Type[Column]] = {
311
+ COLUMN_TYPE_MAP_COCKROACH: dict[str, type[Column]] = {
312
312
  **COLUMN_TYPE_MAP,
313
313
  **{"integer": BigInt, "json": JSONB},
314
314
  }
315
315
 
316
- COLUMN_DEFAULT_PARSER: t.Dict[t.Type[Column], t.Any] = {
316
+ COLUMN_DEFAULT_PARSER: dict[type[Column], Any] = {
317
317
  BigInt: re.compile(r"^'?(?P<value>-?[0-9]\d*)'?(?:::bigint)?$"),
318
318
  Boolean: re.compile(r"^(?P<value>true|false)$"),
319
319
  Bytea: re.compile(r"'(?P<value>.*)'::bytea$"),
@@ -373,15 +373,15 @@ COLUMN_DEFAULT_PARSER: t.Dict[t.Type[Column], t.Any] = {
373
373
  }
374
374
 
375
375
  # Re-map for Cockroach compatibility.
376
- COLUMN_DEFAULT_PARSER_COCKROACH: t.Dict[t.Type[Column], t.Any] = {
376
+ COLUMN_DEFAULT_PARSER_COCKROACH: dict[type[Column], Any] = {
377
377
  **COLUMN_DEFAULT_PARSER,
378
378
  BigInt: re.compile(r"^(?P<value>-?\d+)$"),
379
379
  }
380
380
 
381
381
 
382
382
  def get_column_default(
383
- column_type: t.Type[Column], column_default: str, engine_type: str
384
- ) -> t.Any:
383
+ column_type: type[Column], column_default: str, engine_type: str
384
+ ) -> Any:
385
385
  if engine_type == "cockroach":
386
386
  pat = COLUMN_DEFAULT_PARSER_COCKROACH.get(column_type)
387
387
  else:
@@ -455,7 +455,7 @@ def get_column_default(
455
455
  return column_type.value_type(value["value"])
456
456
 
457
457
 
458
- INDEX_METHOD_MAP: t.Dict[str, IndexMethod] = {
458
+ INDEX_METHOD_MAP: dict[str, IndexMethod] = {
459
459
  "btree": IndexMethod.btree,
460
460
  "hash": IndexMethod.hash,
461
461
  "gist": IndexMethod.gist,
@@ -465,7 +465,7 @@ INDEX_METHOD_MAP: t.Dict[str, IndexMethod] = {
465
465
 
466
466
  # 'Indices' seems old-fashioned and obscure in this context.
467
467
  async def get_indexes( # noqa: E302
468
- table_class: t.Type[Table], tablename: str, schema_name: str = "public"
468
+ table_class: type[Table], tablename: str, schema_name: str = "public"
469
469
  ) -> TableIndexes:
470
470
  """
471
471
  Get all of the constraints for a table.
@@ -492,7 +492,7 @@ async def get_indexes( # noqa: E302
492
492
 
493
493
 
494
494
  async def get_fk_triggers(
495
- table_class: t.Type[Table], tablename: str, schema_name: str = "public"
495
+ table_class: type[Table], tablename: str, schema_name: str = "public"
496
496
  ) -> TableTriggers:
497
497
  """
498
498
  Get all of the constraints for a table.
@@ -540,7 +540,7 @@ async def get_fk_triggers(
540
540
 
541
541
 
542
542
  async def get_constraints(
543
- table_class: t.Type[Table], tablename: str, schema_name: str = "public"
543
+ table_class: type[Table], tablename: str, schema_name: str = "public"
544
544
  ) -> TableConstraints:
545
545
  """
546
546
  Get all of the constraints for a table.
@@ -572,8 +572,8 @@ async def get_constraints(
572
572
 
573
573
 
574
574
  async def get_tablenames(
575
- table_class: t.Type[Table], schema_name: str = "public"
576
- ) -> t.List[str]:
575
+ table_class: type[Table], schema_name: str = "public"
576
+ ) -> list[str]:
577
577
  """
578
578
  Get the tablenames for the schema.
579
579
 
@@ -598,8 +598,8 @@ async def get_tablenames(
598
598
 
599
599
 
600
600
  async def get_table_schema(
601
- table_class: t.Type[Table], tablename: str, schema_name: str = "public"
602
- ) -> t.List[RowMeta]:
601
+ table_class: type[Table], tablename: str, schema_name: str = "public"
602
+ ) -> list[RowMeta]:
603
603
  """
604
604
  Get the schema from the database.
605
605
 
@@ -629,7 +629,7 @@ async def get_table_schema(
629
629
 
630
630
 
631
631
  async def get_foreign_key_reference(
632
- table_class: t.Type[Table], constraint_name: str, constraint_schema: str
632
+ table_class: type[Table], constraint_name: str, constraint_schema: str
633
633
  ) -> ConstraintTable:
634
634
  """
635
635
  Retrieve the name of the table that a foreign key is referencing.
@@ -652,7 +652,7 @@ async def get_foreign_key_reference(
652
652
 
653
653
 
654
654
  async def create_table_class_from_db(
655
- table_class: t.Type[Table],
655
+ table_class: type[Table],
656
656
  tablename: str,
657
657
  schema_name: str,
658
658
  engine_type: str,
@@ -674,7 +674,7 @@ async def create_table_class_from_db(
674
674
  table_class=table_class, tablename=tablename, schema_name=schema_name
675
675
  )
676
676
 
677
- columns: t.Dict[str, Column] = {}
677
+ columns: dict[str, Column] = {}
678
678
 
679
679
  for pg_row_meta in table_schema:
680
680
  data_type = pg_row_meta.data_type
@@ -692,7 +692,7 @@ async def create_table_class_from_db(
692
692
  )
693
693
  column_type = Column
694
694
 
695
- kwargs: t.Dict[str, t.Any] = {
695
+ kwargs: dict[str, Any] = {
696
696
  "null": pg_row_meta.is_nullable == "YES",
697
697
  "unique": constraints.is_unique(column_name=column_name),
698
698
  }
@@ -721,7 +721,7 @@ async def create_table_class_from_db(
721
721
  constraint_schema=fk_constraint_table.schema,
722
722
  )
723
723
  if constraint_table.name:
724
- referenced_table: t.Union[str, t.Optional[t.Type[Table]]]
724
+ referenced_table: Union[str, Optional[type[Table]]]
725
725
 
726
726
  if constraint_table.name == tablename:
727
727
  referenced_output_schema = output_schema
@@ -805,9 +805,9 @@ async def create_table_class_from_db(
805
805
 
806
806
  async def get_output_schema(
807
807
  schema_name: str = "public",
808
- include: t.Optional[t.List[str]] = None,
809
- exclude: t.Optional[t.List[str]] = None,
810
- engine: t.Optional[Engine] = None,
808
+ include: Optional[list[str]] = None,
809
+ exclude: Optional[list[str]] = None,
810
+ engine: Optional[Engine] = None,
811
811
  ) -> OutputSchema:
812
812
  """
813
813
  :param schema_name:
@@ -5,7 +5,7 @@ Credit to the Django Extensions team for inspiring this tool.
5
5
  import dataclasses
6
6
  import os
7
7
  import sys
8
- import typing as t
8
+ from typing import Optional
9
9
 
10
10
  import jinja2
11
11
 
@@ -29,7 +29,7 @@ class GraphColumn:
29
29
  @dataclasses.dataclass
30
30
  class GraphTable:
31
31
  name: str
32
- columns: t.List[GraphColumn]
32
+ columns: list[GraphColumn]
33
33
 
34
34
 
35
35
  @dataclasses.dataclass
@@ -45,7 +45,7 @@ def render_template(**kwargs):
45
45
 
46
46
 
47
47
  def graph(
48
- apps: str = "all", direction: str = "LR", output: t.Optional[str] = None
48
+ apps: str = "all", direction: str = "LR", output: Optional[str] = None
49
49
  ):
50
50
  """
51
51
  Prints out a graphviz .dot file for your schema.
@@ -73,8 +73,8 @@ def graph(
73
73
  sys.exit(f"These apps aren't recognised: {', '.join(delta)}.")
74
74
  app_names = given_app_names
75
75
 
76
- tables: t.List[GraphTable] = []
77
- relations: t.List[GraphRelation] = []
76
+ tables: list[GraphTable] = []
77
+ relations: list[GraphRelation] = []
78
78
 
79
79
  for app_name in app_names:
80
80
  app_config = finder.get_app_config(app_name=app_name)
@@ -1,5 +1,4 @@
1
1
  import sys
2
- import typing as t
3
2
 
4
3
  from piccolo.conf.apps import Finder
5
4
  from piccolo.table import Table
@@ -13,7 +12,7 @@ except ImportError:
13
12
  IPYTHON = False
14
13
 
15
14
 
16
- def start_ipython_shell(**tables: t.Type[Table]): # pragma: no cover
15
+ def start_ipython_shell(**tables: type[Table]): # pragma: no cover
17
16
  if not IPYTHON:
18
17
  sys.exit(
19
18
  "Install iPython using `pip install ipython` to use this feature."
@@ -2,7 +2,7 @@ import os
2
2
  import signal
3
3
  import subprocess
4
4
  import sys
5
- import typing as t
5
+ from typing import cast
6
6
 
7
7
  from piccolo.engine.finder import engine_finder
8
8
  from piccolo.engine.postgres import PostgresEngine
@@ -24,7 +24,7 @@ def run() -> None:
24
24
 
25
25
  # Heavily inspired by Django's dbshell command
26
26
  if isinstance(engine, PostgresEngine):
27
- engine = t.cast(PostgresEngine, engine)
27
+ engine = cast(PostgresEngine, engine)
28
28
 
29
29
  args = ["psql"]
30
30
 
@@ -56,9 +56,9 @@ def run() -> None:
56
56
  signal.signal(signal.SIGINT, sigint_handler)
57
57
 
58
58
  elif isinstance(engine, SQLiteEngine):
59
- engine = t.cast(SQLiteEngine, engine)
59
+ engine = cast(SQLiteEngine, engine)
60
60
 
61
- database = t.cast(str, engine.connection_kwargs.get("database"))
61
+ database = cast(str, engine.connection_kwargs.get("database"))
62
62
  if not database:
63
63
  sys.exit("Unable to determine which database to connect to.")
64
64
 
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  import sys
5
- import typing as t
5
+ from typing import Optional
6
6
 
7
7
  from piccolo.table import TABLE_REGISTRY
8
8
 
@@ -25,7 +25,7 @@ class set_env_var:
25
25
  def set_var(self, value: str):
26
26
  os.environ[self.var_name] = value
27
27
 
28
- def get_var(self) -> t.Optional[str]:
28
+ def get_var(self) -> Optional[str]:
29
29
  return os.environ.get(self.var_name)
30
30
 
31
31
  def __enter__(self):
@@ -39,7 +39,7 @@ class set_env_var:
39
39
  self.set_var(self.existing_value)
40
40
 
41
41
 
42
- def run_pytest(pytest_args: t.List[str]) -> int: # pragma: no cover
42
+ def run_pytest(pytest_args: list[str]) -> int: # pragma: no cover
43
43
  try:
44
44
  import pytest
45
45
  except ImportError:
@@ -1,17 +1,17 @@
1
- import typing as t
1
+ from typing import TYPE_CHECKING, Optional, Union
2
2
 
3
3
  from piccolo.apps.user.tables import BaseUser
4
4
  from piccolo.utils.warnings import Level, colored_string
5
5
 
6
- if t.TYPE_CHECKING: # pragma: no cover
6
+ if TYPE_CHECKING: # pragma: no cover
7
7
  from piccolo.columns import Column
8
8
 
9
9
 
10
10
  async def change_permissions(
11
11
  username: str,
12
- admin: t.Optional[bool] = None,
13
- superuser: t.Optional[bool] = None,
14
- active: t.Optional[bool] = None,
12
+ admin: Optional[bool] = None,
13
+ superuser: Optional[bool] = None,
14
+ active: Optional[bool] = None,
15
15
  ):
16
16
  """
17
17
  Change a user's permissions.
@@ -34,7 +34,7 @@ async def change_permissions(
34
34
  )
35
35
  return
36
36
 
37
- params: t.Dict[t.Union[Column, str], bool] = {}
37
+ params: dict[Union[Column, str], bool] = {}
38
38
 
39
39
  if admin is not None:
40
40
  params[BaseUser.admin] = admin
@@ -1,6 +1,6 @@
1
1
  import sys
2
- import typing as t
3
2
  from getpass import getpass, getuser
3
+ from typing import Optional
4
4
 
5
5
  from piccolo.apps.user.tables import BaseUser
6
6
 
@@ -57,12 +57,12 @@ def get_is_active() -> bool:
57
57
 
58
58
 
59
59
  def create(
60
- username: t.Optional[str] = None,
61
- email: t.Optional[str] = None,
62
- password: t.Optional[str] = None,
63
- is_admin: t.Optional[bool] = None,
64
- is_superuser: t.Optional[bool] = None,
65
- is_active: t.Optional[bool] = None,
60
+ username: Optional[str] = None,
61
+ email: Optional[str] = None,
62
+ password: Optional[str] = None,
63
+ is_admin: Optional[bool] = None,
64
+ is_superuser: Optional[bool] = None,
65
+ is_active: Optional[bool] = None,
66
66
  ):
67
67
  """
68
68
  Create a new user.
@@ -1,4 +1,4 @@
1
- import typing as t
1
+ from typing import Any
2
2
 
3
3
  from piccolo.apps.user.tables import BaseUser
4
4
  from piccolo.columns import Column
@@ -11,7 +11,7 @@ ORDER_BY_COLUMN_NAMES = [
11
11
 
12
12
  async def get_users(
13
13
  order_by: Column, ascending: bool, limit: int, page: int
14
- ) -> t.List[t.Dict[str, t.Any]]:
14
+ ) -> list[dict[str, Any]]:
15
15
  return (
16
16
  await BaseUser.select(
17
17
  *BaseUser.all_columns(exclude=[BaseUser.password])
@@ -8,7 +8,7 @@ import datetime
8
8
  import hashlib
9
9
  import logging
10
10
  import secrets
11
- import typing as t
11
+ from typing import Any, Optional, Union
12
12
 
13
13
  from piccolo.columns import Boolean, Secret, Timestamp, Varchar
14
14
  from piccolo.columns.column_types import Serial
@@ -109,14 +109,14 @@ class BaseUser(Table, tablename="piccolo_user"):
109
109
  ###########################################################################
110
110
 
111
111
  @classmethod
112
- def update_password_sync(cls, user: t.Union[str, int], password: str):
112
+ def update_password_sync(cls, user: Union[str, int], password: str):
113
113
  """
114
114
  A sync equivalent of :meth:`update_password`.
115
115
  """
116
116
  return run_sync(cls.update_password(user, password))
117
117
 
118
118
  @classmethod
119
- async def update_password(cls, user: t.Union[str, int], password: str):
119
+ async def update_password(cls, user: Union[str, int], password: str):
120
120
  """
121
121
  The password is the raw password string e.g. ``'password123'``.
122
122
  The user can be a user ID, or a username.
@@ -139,7 +139,7 @@ class BaseUser(Table, tablename="piccolo_user"):
139
139
 
140
140
  @classmethod
141
141
  def hash_password(
142
- cls, password: str, salt: str = "", iterations: t.Optional[int] = None
142
+ cls, password: str, salt: str = "", iterations: Optional[int] = None
143
143
  ) -> str:
144
144
  """
145
145
  Hashes the password, ready for storage, and for comparing during
@@ -167,7 +167,7 @@ class BaseUser(Table, tablename="piccolo_user"):
167
167
  ).hex()
168
168
  return f"pbkdf2_sha256${iterations}${salt}${hashed}"
169
169
 
170
- def __setattr__(self, name: str, value: t.Any):
170
+ def __setattr__(self, name: str, value: Any):
171
171
  """
172
172
  Make sure that if the password is set, it's stored in a hashed form.
173
173
  """
@@ -177,7 +177,7 @@ class BaseUser(Table, tablename="piccolo_user"):
177
177
  super().__setattr__(name, value)
178
178
 
179
179
  @classmethod
180
- def split_stored_password(cls, password: str) -> t.List[str]:
180
+ def split_stored_password(cls, password: str) -> list[str]:
181
181
  elements = password.split("$")
182
182
  if len(elements) != 4:
183
183
  raise ValueError("Unable to split hashed password")
@@ -186,14 +186,14 @@ class BaseUser(Table, tablename="piccolo_user"):
186
186
  ###########################################################################
187
187
 
188
188
  @classmethod
189
- def login_sync(cls, username: str, password: str) -> t.Optional[int]:
189
+ def login_sync(cls, username: str, password: str) -> Optional[int]:
190
190
  """
191
191
  A sync equivalent of :meth:`login`.
192
192
  """
193
193
  return run_sync(cls.login(username, password))
194
194
 
195
195
  @classmethod
196
- async def login(cls, username: str, password: str) -> t.Optional[int]:
196
+ async def login(cls, username: str, password: str) -> Optional[int]:
197
197
  """
198
198
  Make sure the user exists and the password is valid. If so, the
199
199
  ``last_login`` value is updated in the database.