piccolo 1.22.0__py3-none-any.whl → 1.24.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 (27) hide show
  1. piccolo/__init__.py +1 -1
  2. piccolo/apps/asgi/commands/new.py +3 -0
  3. piccolo/apps/asgi/commands/templates/app/_falcon_app.py.jinja +60 -0
  4. piccolo/apps/asgi/commands/templates/app/_quart_app.py.jinja +119 -0
  5. piccolo/apps/asgi/commands/templates/app/_sanic_app.py.jinja +121 -0
  6. piccolo/apps/asgi/commands/templates/app/app.py.jinja +6 -0
  7. piccolo/apps/asgi/commands/templates/app/home/_falcon_endpoints.py.jinja +19 -0
  8. piccolo/apps/asgi/commands/templates/app/home/_quart_endpoints.py.jinja +18 -0
  9. piccolo/apps/asgi/commands/templates/app/home/_sanic_endpoints.py.jinja +17 -0
  10. piccolo/apps/asgi/commands/templates/app/home/endpoints.py.jinja +6 -0
  11. piccolo/apps/asgi/commands/templates/app/home/templates/home.html.jinja_raw +15 -0
  12. piccolo/apps/sql_shell/commands/run.py +13 -14
  13. piccolo/apps/user/tables.py +3 -1
  14. piccolo/columns/column_types.py +1 -1
  15. piccolo/columns/defaults/timestamptz.py +1 -0
  16. piccolo/engine/sqlite.py +14 -2
  17. piccolo/query/methods/objects.py +5 -7
  18. {piccolo-1.22.0.dist-info → piccolo-1.24.0.dist-info}/METADATA +32 -23
  19. {piccolo-1.22.0.dist-info → piccolo-1.24.0.dist-info}/RECORD +27 -20
  20. {piccolo-1.22.0.dist-info → piccolo-1.24.0.dist-info}/WHEEL +1 -1
  21. {piccolo-1.22.0.dist-info → piccolo-1.24.0.dist-info}/entry_points.txt +0 -1
  22. tests/apps/sql_shell/commands/test_run.py +26 -2
  23. tests/columns/test_integer.py +32 -0
  24. tests/table/test_insert.py +1 -1
  25. tests/table/test_objects.py +31 -0
  26. {piccolo-1.22.0.dist-info → piccolo-1.24.0.dist-info}/LICENSE +0 -0
  27. {piccolo-1.22.0.dist-info → piccolo-1.24.0.dist-info}/top_level.txt +0 -0
piccolo/__init__.py CHANGED
@@ -1 +1 @@
1
- __VERSION__ = "1.22.0"
1
+ __VERSION__ = "1.24.0"
@@ -17,6 +17,9 @@ ROUTER_DEPENDENCIES = {
17
17
  "litestar": ["litestar"],
18
18
  "esmerald": ["esmerald"],
19
19
  "lilya": ["lilya"],
20
+ "quart": ["quart", "quart_schema"],
21
+ "falcon": ["falcon"],
22
+ "sanic": ["sanic", "sanic_ext"],
20
23
  }
21
24
  ROUTERS = list(ROUTER_DEPENDENCIES.keys())
22
25
 
@@ -0,0 +1,60 @@
1
+ import os
2
+ import typing as t
3
+
4
+ import falcon.asgi
5
+ from hypercorn.middleware import DispatcherMiddleware
6
+ from piccolo.engine import engine_finder
7
+ from piccolo_admin.endpoints import create_admin
8
+ from piccolo_api.crud.endpoints import PiccoloCRUD
9
+
10
+ from home.endpoints import HomeEndpoint
11
+ from home.piccolo_app import APP_CONFIG
12
+ from home.tables import Task
13
+
14
+
15
+ async def open_database_connection_pool():
16
+ try:
17
+ engine = engine_finder()
18
+ await engine.start_connection_pool()
19
+ except Exception:
20
+ print("Unable to connect to the database")
21
+
22
+
23
+ async def close_database_connection_pool():
24
+ try:
25
+ engine = engine_finder()
26
+ await engine.close_connection_pool()
27
+ except Exception:
28
+ print("Unable to connect to the database")
29
+
30
+
31
+ class LifespanMiddleware:
32
+ async def process_startup(
33
+ self, scope: t.Dict[str, t.Any], event: t.Dict[str, t.Any]
34
+ ) -> None:
35
+ await open_database_connection_pool()
36
+
37
+ async def process_shutdown(
38
+ self, scope: t.Dict[str, t.Any], event: t.Dict[str, t.Any]
39
+ ) -> None:
40
+ await close_database_connection_pool()
41
+
42
+
43
+ app: t.Any = falcon.asgi.App(middleware=LifespanMiddleware())
44
+ app.add_static_route("/static", directory=os.path.abspath("static"))
45
+ app.add_route("/", HomeEndpoint())
46
+
47
+ PICCOLO_CRUD: t.Any = PiccoloCRUD(table=Task)
48
+
49
+ # enable the Admin and PiccoloCrud app using DispatcherMiddleware
50
+ app = DispatcherMiddleware( # type: ignore
51
+ {
52
+ "/admin": create_admin(
53
+ tables=APP_CONFIG.table_classes,
54
+ # Required when running under HTTPS:
55
+ # allowed_hosts=['my_site.com']
56
+ ),
57
+ "/tasks": PICCOLO_CRUD,
58
+ "": app,
59
+ }
60
+ )
@@ -0,0 +1,119 @@
1
+ import typing as t
2
+ from http import HTTPStatus
3
+
4
+ from hypercorn.middleware import DispatcherMiddleware
5
+ from piccolo.engine import engine_finder
6
+ from piccolo_admin.endpoints import create_admin
7
+ from piccolo_api.crud.serializers import create_pydantic_model
8
+ from quart import Quart
9
+ from quart_schema import (
10
+ Info,
11
+ QuartSchema,
12
+ hide,
13
+ tag,
14
+ validate_request,
15
+ validate_response,
16
+ )
17
+
18
+ from home.endpoints import index
19
+ from home.piccolo_app import APP_CONFIG
20
+ from home.tables import Task
21
+
22
+
23
+ app = Quart(__name__, static_folder="static")
24
+ QuartSchema(app, info=Info(title="Quart API", version="0.1.0"))
25
+
26
+
27
+ TaskModelIn: t.Any = create_pydantic_model(
28
+ table=Task,
29
+ model_name="TaskModelIn",
30
+ )
31
+ TaskModelOut: t.Any = create_pydantic_model(
32
+ table=Task,
33
+ include_default_columns=True,
34
+ model_name="TaskModelOut",
35
+ )
36
+
37
+
38
+ @app.get("/")
39
+ @hide
40
+ def home():
41
+ return index()
42
+
43
+
44
+ @app.get("/tasks/")
45
+ @validate_response(t.List[TaskModelOut])
46
+ @tag(["Task"])
47
+ async def tasks():
48
+ return await Task.select().order_by(Task._meta.primary_key, ascending=False)
49
+
50
+
51
+ @app.post("/tasks/")
52
+ @validate_request(TaskModelIn)
53
+ @validate_response(TaskModelOut)
54
+ @tag(["Task"])
55
+ async def create_task(data: TaskModelIn):
56
+ task = Task(**data.model_dump())
57
+ await task.save()
58
+ return task.to_dict(), HTTPStatus.CREATED
59
+
60
+
61
+ @app.put("/tasks/<int:task_id>/")
62
+ @validate_request(TaskModelIn)
63
+ @validate_response(TaskModelOut)
64
+ @tag(["Task"])
65
+ async def update_task(task_id: int, data: TaskModelIn):
66
+ task = await Task.objects().get(Task._meta.primary_key == task_id)
67
+ if not task:
68
+ return {}, HTTPStatus.NOT_FOUND
69
+
70
+ for key, value in data.model_dump().items():
71
+ setattr(task, key, value)
72
+
73
+ await task.save()
74
+
75
+ return task.to_dict(), HTTPStatus.OK
76
+
77
+
78
+ @app.delete("/tasks/<int:task_id>/")
79
+ @validate_response(TaskModelOut)
80
+ @tag(["Task"])
81
+ async def delete_task(task_id: int):
82
+ task = await Task.objects().get(Task._meta.primary_key == task_id)
83
+ if not task:
84
+ return {}, HTTPStatus.NOT_FOUND
85
+
86
+ await task.remove()
87
+
88
+ return {}, HTTPStatus.OK
89
+
90
+
91
+ @app.before_serving
92
+ async def open_database_connection_pool():
93
+ try:
94
+ engine = engine_finder()
95
+ await engine.start_connection_pool()
96
+ except Exception:
97
+ print("Unable to connect to the database")
98
+
99
+
100
+ @app.after_serving
101
+ async def close_database_connection_pool():
102
+ try:
103
+ engine = engine_finder()
104
+ await engine.close_connection_pool()
105
+ except Exception:
106
+ print("Unable to connect to the database")
107
+
108
+
109
+ # enable the admin application using DispatcherMiddleware
110
+ app = DispatcherMiddleware( # type: ignore
111
+ {
112
+ "/admin": create_admin(
113
+ tables=APP_CONFIG.table_classes,
114
+ # Required when running under HTTPS:
115
+ # allowed_hosts=['my_site.com']
116
+ ),
117
+ "": app,
118
+ }
119
+ )
@@ -0,0 +1,121 @@
1
+ import asyncio
2
+ import typing as t
3
+
4
+ from hypercorn.middleware import DispatcherMiddleware
5
+ from piccolo.engine import engine_finder
6
+ from piccolo_admin.endpoints import create_admin
7
+ from piccolo_api.crud.serializers import create_pydantic_model
8
+ from sanic import Request, Sanic, json
9
+ from sanic_ext import openapi
10
+
11
+ from home.endpoints import index
12
+ from home.piccolo_app import APP_CONFIG
13
+ from home.tables import Task
14
+
15
+ app = Sanic(__name__)
16
+ app.static("/static/", "static")
17
+
18
+
19
+ TaskModelIn: t.Any = create_pydantic_model(
20
+ table=Task,
21
+ model_name="TaskModelIn",
22
+ )
23
+ TaskModelOut: t.Any = create_pydantic_model(
24
+ table=Task,
25
+ include_default_columns=True,
26
+ model_name="TaskModelOut",
27
+ )
28
+
29
+
30
+ @app.get("/")
31
+ @openapi.exclude()
32
+ def home(request: Request):
33
+ return index()
34
+
35
+
36
+ @app.get("/tasks/")
37
+ @openapi.tag("Task")
38
+ @openapi.response(200, {"application/json": TaskModelOut.model_json_schema()})
39
+ async def tasks(request: Request):
40
+ return json(
41
+ await Task.select().order_by(Task._meta.primary_key, ascending=False),
42
+ status=200,
43
+ )
44
+
45
+
46
+ @app.post("/tasks/")
47
+ @openapi.definition(
48
+ body={"application/json": TaskModelIn.model_json_schema()},
49
+ tag="Task",
50
+ )
51
+ @openapi.response(201, {"application/json": TaskModelOut.model_json_schema()})
52
+ async def create_task(request: Request):
53
+ task = Task(**request.json)
54
+ await task.save()
55
+ return json(task.to_dict(), status=201)
56
+
57
+
58
+ @app.put("/tasks/<task_id:int>/")
59
+ @openapi.definition(
60
+ body={"application/json": TaskModelIn.model_json_schema()},
61
+ tag="Task",
62
+ )
63
+ @openapi.response(200, {"application/json": TaskModelOut.model_json_schema()})
64
+ async def update_task(request: Request, task_id: int):
65
+ task = await Task.objects().get(Task._meta.primary_key == task_id)
66
+ if not task:
67
+ return json({}, status=404)
68
+ for key, value in request.json.items():
69
+ setattr(task, key, value)
70
+
71
+ await task.save()
72
+ return json(task.to_dict(), status=200)
73
+
74
+
75
+ @app.delete("/tasks/<task_id:int>/")
76
+ @openapi.tag("Task")
77
+ async def delete_task(request: Request, task_id: int):
78
+ task = await Task.objects().get(Task._meta.primary_key == task_id)
79
+ if not task:
80
+ return json({}, status=404)
81
+ await task.remove()
82
+ return json({}, status=200)
83
+
84
+
85
+ async def open_database_connection_pool():
86
+ try:
87
+ engine = engine_finder()
88
+ await engine.start_connection_pool()
89
+ except Exception:
90
+ print("Unable to connect to the database")
91
+
92
+
93
+ async def close_database_connection_pool():
94
+ try:
95
+ engine = engine_finder()
96
+ await engine.close_connection_pool()
97
+ except Exception:
98
+ print("Unable to connect to the database")
99
+
100
+
101
+ @app.after_server_start
102
+ async def startup(app, loop):
103
+ await open_database_connection_pool()
104
+
105
+
106
+ @app.before_server_stop
107
+ async def shutdown(app, loop):
108
+ await close_database_connection_pool()
109
+
110
+
111
+ # enable the admin application using DispatcherMiddleware
112
+ app = DispatcherMiddleware( # type: ignore
113
+ {
114
+ "/admin": create_admin(
115
+ tables=APP_CONFIG.table_classes,
116
+ # Required when running under HTTPS:
117
+ # allowed_hosts=['my_site.com']
118
+ ),
119
+ "": app,
120
+ }
121
+ )
@@ -10,4 +10,10 @@
10
10
  {% include '_esmerald_app.py.jinja' %}
11
11
  {% elif router == 'lilya' %}
12
12
  {% include '_lilya_app.py.jinja' %}
13
+ {% elif router == 'quart' %}
14
+ {% include '_quart_app.py.jinja' %}
15
+ {% elif router == 'falcon' %}
16
+ {% include '_falcon_app.py.jinja' %}
17
+ {% elif router == 'sanic' %}
18
+ {% include '_sanic_app.py.jinja' %}
13
19
  {% endif %}
@@ -0,0 +1,19 @@
1
+ import os
2
+
3
+ import falcon
4
+ import jinja2
5
+
6
+ ENVIRONMENT = jinja2.Environment(
7
+ loader=jinja2.FileSystemLoader(
8
+ searchpath=os.path.join(os.path.dirname(__file__), "templates")
9
+ )
10
+ )
11
+
12
+
13
+ class HomeEndpoint:
14
+ async def on_get(self, req, resp):
15
+ template = ENVIRONMENT.get_template("home.html.jinja")
16
+ content = template.render(title="Piccolo + ASGI",)
17
+ resp.status = falcon.HTTP_200
18
+ resp.content_type = "text/html"
19
+ resp.text = content
@@ -0,0 +1,18 @@
1
+ import os
2
+
3
+ import jinja2
4
+
5
+ from quart import Response
6
+
7
+ ENVIRONMENT = jinja2.Environment(
8
+ loader=jinja2.FileSystemLoader(
9
+ searchpath=os.path.join(os.path.dirname(__file__), "templates")
10
+ )
11
+ )
12
+
13
+
14
+ def index():
15
+ template = ENVIRONMENT.get_template("home.html.jinja")
16
+ content = template.render(title="Piccolo + ASGI")
17
+ return Response(content)
18
+
@@ -0,0 +1,17 @@
1
+ import os
2
+
3
+ import jinja2
4
+
5
+ from sanic import HTTPResponse
6
+
7
+ ENVIRONMENT = jinja2.Environment(
8
+ loader=jinja2.FileSystemLoader(
9
+ searchpath=os.path.join(os.path.dirname(__file__), "templates")
10
+ )
11
+ )
12
+
13
+
14
+ def index():
15
+ template = ENVIRONMENT.get_template("home.html.jinja")
16
+ content = template.render(title="Piccolo + ASGI")
17
+ return HTTPResponse(content)
@@ -8,4 +8,10 @@
8
8
  {% include '_esmerald_endpoints.py.jinja' %}
9
9
  {% elif router == 'lilya' %}
10
10
  {% include '_lilya_endpoints.py.jinja' %}
11
+ {% elif router == 'quart' %}
12
+ {% include '_quart_endpoints.py.jinja' %}
13
+ {% elif router == 'falcon' %}
14
+ {% include '_falcon_endpoints.py.jinja' %}
15
+ {% elif router == 'sanic' %}
16
+ {% include '_sanic_endpoints.py.jinja' %}
11
17
  {% endif %}
@@ -66,6 +66,21 @@
66
66
  <li><a href="/admin/">Admin</a></li>
67
67
  <li><a href="/tasks/">JSON endpoint</a></li>
68
68
  </ul>
69
+ <h3>Quart</h3>
70
+ <ul>
71
+ <li><a href="/admin/">Admin</a></li>
72
+ <li><a href="/docs">Swagger API</a></li>
73
+ </ul>
74
+ <h3>Falcon</h3>
75
+ <ul>
76
+ <li><a href="/admin/">Admin</a></li>
77
+ <li><a href="/tasks/">JSON endpoint</a></li>
78
+ </ul>
79
+ <h3>Sanic</h3>
80
+ <ul>
81
+ <li><a href="/admin/">Admin</a></li>
82
+ <li><a href="/docs/swagger">Swagger API</a></li>
83
+ </ul>
69
84
  </section>
70
85
  </div>
71
86
  {% endblock content %}
@@ -28,24 +28,23 @@ def run() -> None:
28
28
 
29
29
  args = ["psql"]
30
30
 
31
- host = engine.config.get("host")
32
- port = engine.config.get("port")
33
- user = engine.config.get("user")
34
- password = engine.config.get("password")
35
- database = engine.config.get("database")
31
+ config = engine.config
36
32
 
37
- if user:
38
- args += ["-U", user]
39
- if host:
40
- args += ["-h", host]
41
- if port:
42
- args += ["-p", str(port)]
43
- if database:
44
- args += [database]
33
+ if dsn := config.get("dsn"):
34
+ args += [dsn]
35
+ else:
36
+ if user := config.get("user"):
37
+ args += ["-U", user]
38
+ if host := config.get("host"):
39
+ args += ["-h", host]
40
+ if port := config.get("port"):
41
+ args += ["-p", str(port)]
42
+ if database := config.get("database"):
43
+ args += [database]
45
44
 
46
45
  sigint_handler = signal.getsignal(signal.SIGINT)
47
46
  subprocess_env = os.environ.copy()
48
- if password:
47
+ if password := config.get("password"):
49
48
  subprocess_env["PGPASSWORD"] = str(password)
50
49
  try:
51
50
  # Allow SIGINT to pass to psql to abort queries.
@@ -202,7 +202,9 @@ class BaseUser(Table, tablename="piccolo_user"):
202
202
  The id of the user if a match is found, otherwise ``None``.
203
203
 
204
204
  """
205
- if len(username) > cls.username.length:
205
+ if (max_username_length := cls.username.length) and len(
206
+ username
207
+ ) > max_username_length:
206
208
  logger.warning("Excessively long username provided.")
207
209
  return None
208
210
 
@@ -315,7 +315,7 @@ class Varchar(Column):
315
315
 
316
316
  def __init__(
317
317
  self,
318
- length: int = 255,
318
+ length: t.Optional[int] = 255,
319
319
  default: t.Union[str, Enum, t.Callable[[], str], None] = "",
320
320
  **kwargs,
321
321
  ) -> None:
@@ -73,6 +73,7 @@ TimestamptzArg = t.Union[
73
73
  Enum,
74
74
  None,
75
75
  datetime.datetime,
76
+ t.Callable[[], datetime.datetime],
76
77
  ]
77
78
 
78
79
 
piccolo/engine/sqlite.py CHANGED
@@ -173,9 +173,21 @@ def convert_numeric_out(value: str) -> Decimal:
173
173
  @decode_to_string
174
174
  def convert_int_out(value: str) -> int:
175
175
  """
176
- Make sure Integer values are actually of type int.
176
+ Make sure INTEGER values are actually of type ``int``.
177
+
178
+ SQLite doesn't enforce that the values in INTEGER columns are actually
179
+ integers - they could be strings ('hello'), or floats (1.0).
180
+
181
+ There's not much we can do if the value is something like 'hello' - a
182
+ ``ValueError`` is appropriate in this situation.
183
+
184
+ For a value like ``1.0``, it seems reasonable to handle this, and return a
185
+ value of ``1``.
186
+
177
187
  """
178
- return int(float(value))
188
+ # We used to use int(float(value)), but it was incorrect, because float has
189
+ # limited precision for large numbers.
190
+ return int(Decimal(value))
179
191
 
180
192
 
181
193
  @decode_to_string
@@ -65,22 +65,20 @@ class GetOrCreate(
65
65
  instance._was_created = False
66
66
  return instance
67
67
 
68
- instance = self.table_class(_data=self.defaults)
68
+ data = {**self.defaults}
69
69
 
70
70
  # If it's a complex `where`, there can be several column values to
71
71
  # extract e.g. (Band.name == 'Pythonistas') & (Band.popularity == 1000)
72
72
  if isinstance(self.where, Where):
73
- setattr(
74
- instance,
75
- self.where.column._meta.name, # type: ignore
76
- self.where.value, # type: ignore
77
- )
73
+ data[self.where.column] = self.where.value
78
74
  elif isinstance(self.where, And):
79
75
  for column, value in self.where.get_column_values().items():
80
76
  if len(column._meta.call_chain) == 0:
81
77
  # Make sure we only set the value if the column belongs
82
78
  # to this table.
83
- setattr(instance, column._meta.name, value)
79
+ data[column] = value
80
+
81
+ instance = self.table_class(_data=data)
84
82
 
85
83
  await instance.save().run(node=node, in_pool=in_pool)
86
84
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: piccolo
3
- Version: 1.22.0
3
+ Version: 1.24.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
@@ -9,7 +9,6 @@ License: MIT
9
9
  Project-URL: Documentation, https://piccolo-orm.readthedocs.io/en/latest/index.html
10
10
  Project-URL: Source, https://github.com/piccolo-orm/piccolo
11
11
  Project-URL: Tracker, https://github.com/piccolo-orm/piccolo/issues
12
- Platform: UNKNOWN
13
12
  Classifier: License :: OSI Approved :: MIT License
14
13
  Classifier: Programming Language :: Python
15
14
  Classifier: Programming Language :: Python :: 3
@@ -26,28 +25,40 @@ Requires-Python: >=3.9.0
26
25
  Description-Content-Type: text/markdown
27
26
  License-File: LICENSE
28
27
  Requires-Dist: black
29
- Requires-Dist: colorama (>=0.4.0)
30
- Requires-Dist: Jinja2 (>=2.11.0)
31
- Requires-Dist: targ (>=0.3.7)
32
- Requires-Dist: inflection (>=0.5.1)
33
- Requires-Dist: typing-extensions (>=4.3.0)
34
- Requires-Dist: pydantic[email] (==2.*)
35
- Provides-Extra: all
36
- Requires-Dist: orjson (>=3.5.1) ; extra == 'all'
37
- Requires-Dist: ipython ; extra == 'all'
38
- Requires-Dist: asyncpg (>=0.30.0) ; extra == 'all'
39
- Requires-Dist: aiosqlite (>=0.16.0) ; extra == 'all'
40
- Requires-Dist: uvloop (>=0.12.0) ; (sys_platform != "win32") and extra == 'all'
28
+ Requires-Dist: colorama>=0.4.0
29
+ Requires-Dist: Jinja2>=2.11.0
30
+ Requires-Dist: targ>=0.3.7
31
+ Requires-Dist: inflection>=0.5.1
32
+ Requires-Dist: typing-extensions>=4.3.0
33
+ Requires-Dist: pydantic[email]==2.*
41
34
  Provides-Extra: orjson
42
- Requires-Dist: orjson (>=3.5.1) ; extra == 'orjson'
35
+ Requires-Dist: orjson>=3.5.1; extra == "orjson"
43
36
  Provides-Extra: playground
44
- Requires-Dist: ipython ; extra == 'playground'
37
+ Requires-Dist: ipython; extra == "playground"
45
38
  Provides-Extra: postgres
46
- Requires-Dist: asyncpg (>=0.30.0) ; extra == 'postgres'
39
+ Requires-Dist: asyncpg>=0.30.0; extra == "postgres"
47
40
  Provides-Extra: sqlite
48
- Requires-Dist: aiosqlite (>=0.16.0) ; extra == 'sqlite'
41
+ Requires-Dist: aiosqlite>=0.16.0; extra == "sqlite"
49
42
  Provides-Extra: uvloop
50
- Requires-Dist: uvloop (>=0.12.0) ; (sys_platform != "win32") and extra == 'uvloop'
43
+ Requires-Dist: uvloop>=0.12.0; sys_platform != "win32" and extra == "uvloop"
44
+ Provides-Extra: all
45
+ Requires-Dist: orjson>=3.5.1; extra == "all"
46
+ Requires-Dist: ipython; extra == "all"
47
+ Requires-Dist: asyncpg>=0.30.0; extra == "all"
48
+ Requires-Dist: aiosqlite>=0.16.0; extra == "all"
49
+ Requires-Dist: uvloop>=0.12.0; sys_platform != "win32" and extra == "all"
50
+ Dynamic: author
51
+ Dynamic: author-email
52
+ Dynamic: classifier
53
+ Dynamic: description
54
+ Dynamic: description-content-type
55
+ Dynamic: home-page
56
+ Dynamic: license
57
+ Dynamic: project-url
58
+ Dynamic: provides-extra
59
+ Dynamic: requires-dist
60
+ Dynamic: requires-python
61
+ Dynamic: summary
51
62
 
52
63
  ![Logo](https://raw.githubusercontent.com/piccolo-orm/piccolo/master/docs/logo_hero.png "Piccolo Logo")
53
64
 
@@ -145,7 +156,7 @@ Let Piccolo scaffold you an ASGI web app, using Piccolo as the ORM:
145
156
  piccolo asgi new
146
157
  ```
147
158
 
148
- [Starlette](https://www.starlette.io/), [FastAPI](https://fastapi.tiangolo.com/), [BlackSheep](https://www.neoteroi.dev/blacksheep/), [Litestar](https://litestar.dev/), [Esmerald](https://esmerald.dev/) and [Lilya](https://lilya.dev) are currently supported.
159
+ [Starlette](https://www.starlette.io/), [FastAPI](https://fastapi.tiangolo.com/), [BlackSheep](https://www.neoteroi.dev/blacksheep/), [Litestar](https://litestar.dev/), [Esmerald](https://esmerald.dev/), [Lilya](https://lilya.dev), [Quart](https://quart.palletsprojects.com/en/latest/), [Falcon](https://falconframework.org/) and [Sanic](https://sanic.dev/en/) are currently supported.
149
160
 
150
161
  ## Are you a Django user?
151
162
 
@@ -156,5 +167,3 @@ We have a handy page which shows the equivalent of [common Django queries in Pic
156
167
  Our documentation is on [Read the docs](https://piccolo-orm.readthedocs.io/en/latest/piccolo/getting_started/index.html).
157
168
 
158
169
  We also have some great [tutorial videos on YouTube](https://www.youtube.com/channel/UCE7x5nm1Iy9KDfXPNrNQ5lA).
159
-
160
-
@@ -1,4 +1,4 @@
1
- piccolo/__init__.py,sha256=aVoeQDKbkLtZX6_B3ZNdv8mqfI_4pHUT1Go9Ek88OVI,23
1
+ piccolo/__init__.py,sha256=uyuLf5Q7X3vuYYBK0p5G67YV-evr3rtlsYxNysnS3W8,23
2
2
  piccolo/custom_types.py,sha256=7HMQAze-5mieNLfbQ5QgbRQgR2abR7ol0qehv2SqROY,604
3
3
  piccolo/main.py,sha256=1VsFV67FWTUikPTysp64Fmgd9QBVa_9wcwKfwj2UCEA,5117
4
4
  piccolo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -17,15 +17,18 @@ piccolo/apps/app/commands/templates/tables.py.jinja,sha256=revzdrvDDwe78VedBKz0z
17
17
  piccolo/apps/asgi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  piccolo/apps/asgi/piccolo_app.py,sha256=7VUvqQJbB-ScO0A62S6MiJmQL9F5DS-SdlqlDLbAblE,217
19
19
  piccolo/apps/asgi/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- piccolo/apps/asgi/commands/new.py,sha256=xrN9RJ0Lnz54qY_rxqA9Z2SS4kC_0RXdYYW7tXmGgIo,4232
20
+ piccolo/apps/asgi/commands/new.py,sha256=718mXx7XdDTN0CKK0ZB1WVMkOrQtVfqT5bqO1kDKnRk,4335
21
21
  piccolo/apps/asgi/commands/templates/app/README.md.jinja,sha256=As3gNEZt9qcRmTVkjCzNtXJ8r4-3g0fCSe7Q-P39ezI,214
22
22
  piccolo/apps/asgi/commands/templates/app/_blacksheep_app.py.jinja,sha256=IKOql1G5wrEKm5qErlizOmrwYKlnxkm-d8NY5uVg9KA,3186
23
23
  piccolo/apps/asgi/commands/templates/app/_esmerald_app.py.jinja,sha256=nTzXc5IJLl_al1FuzG5AnaA1vSn-ipMurpPK7BibmB8,2710
24
+ piccolo/apps/asgi/commands/templates/app/_falcon_app.py.jinja,sha256=LOn3auJFeXNW48rtHzRbH3MzxWbRNhFib6Fm6wDS53E,1684
24
25
  piccolo/apps/asgi/commands/templates/app/_fastapi_app.py.jinja,sha256=mKnYfUOnYyWJA1jFoRLCUOGQlK6imaxx_1qaauGjeeQ,2627
25
26
  piccolo/apps/asgi/commands/templates/app/_lilya_app.py.jinja,sha256=PUph5Jj_AXVpxXZmpUzzHXogUchU8vjKBL_7WvgrfCU,1260
26
27
  piccolo/apps/asgi/commands/templates/app/_litestar_app.py.jinja,sha256=VCY4FoA7YlEhtjWB09XWQqi8GgL36VQwGGBpSXUDO5o,3349
28
+ piccolo/apps/asgi/commands/templates/app/_quart_app.py.jinja,sha256=3LoQJ6LWRB0NFIcfQtPUdNWb10csyDGgCIa2zx8w4e8,2837
29
+ piccolo/apps/asgi/commands/templates/app/_sanic_app.py.jinja,sha256=qza84-aV6wnPlPQ9bpcD5DLO-pvkmoSnb_lXmcymv6c,3159
27
30
  piccolo/apps/asgi/commands/templates/app/_starlette_app.py.jinja,sha256=vHcAzsS9I3OevYoznwZp8zucI4OEyUjj-EOAtscmlSE,1443
28
- piccolo/apps/asgi/commands/templates/app/app.py.jinja,sha256=gROY-LbHl8NtHDM_ntkI7Rjcbtg2ypDZ1FunBvpdjE4,458
31
+ piccolo/apps/asgi/commands/templates/app/app.py.jinja,sha256=n2KriWxCnq65vdEvX1USTqZPDbNkYXQqTJT5EmespT8,667
29
32
  piccolo/apps/asgi/commands/templates/app/conftest.py.jinja,sha256=ZG1pRVMv3LhIfOsO3_08c_fF3EV4_EApuDHiIFFPJdk,497
30
33
  piccolo/apps/asgi/commands/templates/app/main.py.jinja,sha256=QxMpsevsxGQdL_xwfvcNalGLXGswgqiVvApitkP1TuQ,533
31
34
  piccolo/apps/asgi/commands/templates/app/piccolo_conf.py.jinja,sha256=f9Nb08_yipi0_mDUYrUvVoGCz7MRRS5QjCdUGBHN760,379
@@ -34,15 +37,18 @@ piccolo/apps/asgi/commands/templates/app/requirements.txt.jinja,sha256=w4FXnehMN
34
37
  piccolo/apps/asgi/commands/templates/app/home/__init__.py.jinja,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
38
  piccolo/apps/asgi/commands/templates/app/home/_blacksheep_endpoints.py.jinja,sha256=Rri_xzDkl87G5ME74qTxY25cwKIKufuzgkRsy__mNts,510
36
39
  piccolo/apps/asgi/commands/templates/app/home/_esmerald_endpoints.py.jinja,sha256=D_Slfc3IfTyBgq_VUIG5_AW5pVvvSqn-YGQspxMJNsE,499
40
+ piccolo/apps/asgi/commands/templates/app/home/_falcon_endpoints.py.jinja,sha256=Fv3MQnCF7oK752yjpJp35ONpj6wlu2Kld1kxYphdcOs,479
37
41
  piccolo/apps/asgi/commands/templates/app/home/_lilya_endpoints.py.jinja,sha256=nKSJ9VPTUTaLD9oDqAUseNuQkcPLBxShPAEfZK15rSE,490
38
42
  piccolo/apps/asgi/commands/templates/app/home/_litestar_endpoints.py.jinja,sha256=zXbYXDXFeeOCXmWBa_QK0kWGlBnv6T_A2jOHuvp9oCs,553
43
+ piccolo/apps/asgi/commands/templates/app/home/_quart_endpoints.py.jinja,sha256=V92EMVnQusgov_i2KC4wa4GiSIDxBLezgKUrT2YPiuc,365
44
+ piccolo/apps/asgi/commands/templates/app/home/_sanic_endpoints.py.jinja,sha256=8Bcq1uVxme8hfDlnenyBS19d0EzwBK2BZ64C91k_F50,368
39
45
  piccolo/apps/asgi/commands/templates/app/home/_starlette_endpoints.py.jinja,sha256=KEjNEUKiZNBIWYAt9EgPHe4yCbkKLtlhaCBce9YI-RQ,498
40
- piccolo/apps/asgi/commands/templates/app/home/endpoints.py.jinja,sha256=Vc7pllhzKeNyj1XPzqdRH1AUpv7D4pooBIrUMt6gK7I,428
46
+ piccolo/apps/asgi/commands/templates/app/home/endpoints.py.jinja,sha256=9S8oXL2I67BLhFehvYU6Ce5pbCeqhwVHhT0SQW5ZMOw,655
41
47
  piccolo/apps/asgi/commands/templates/app/home/piccolo_app.py.jinja,sha256=4gETiW9ukTNsomeJOvrRkqPbToZ_FU0b3LsNIaEYyP8,505
42
48
  piccolo/apps/asgi/commands/templates/app/home/tables.py.jinja,sha256=wk34RAsuoFn5iJ4OHlQzUqgatq6QB2G9tFE0BYkaers,197
43
49
  piccolo/apps/asgi/commands/templates/app/home/piccolo_migrations/README.md,sha256=ji6UOtHvzHX-eS_qhhKTN36ZXNZ7QwtjwjdE4Qgm35A,59
44
50
  piccolo/apps/asgi/commands/templates/app/home/templates/base.html.jinja_raw,sha256=3RqiNuyAap_P-xNK3uhNaQQ6rC365VzPmRqmmXSLO8o,451
45
- piccolo/apps/asgi/commands/templates/app/home/templates/home.html.jinja_raw,sha256=QJI0MY0hydMdqPg9YcW7XmmccJ1PyHvOSRIvMJX3ij0,2637
51
+ piccolo/apps/asgi/commands/templates/app/home/templates/home.html.jinja_raw,sha256=dD6OAlzfJV_fZktv5b9ek3lJcqDMxTdAkRJzmRDIH0g,3166
46
52
  piccolo/apps/asgi/commands/templates/app/static/favicon.ico,sha256=IvcgeJHObd9kj2mNIXkJdXYxMU8OaOymyYQWnWfbtHo,7406
47
53
  piccolo/apps/asgi/commands/templates/app/static/main.css,sha256=vudarPLglQ6NOgJiNeU2x0yQl0DiWScqb09QZv2wAzM,1056
48
54
  piccolo/apps/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -97,14 +103,14 @@ piccolo/apps/shell/commands/run.py,sha256=BiGMnM0wGKNvZOklgQeU_ZhBYWFxtsTQuvVHdo
97
103
  piccolo/apps/sql_shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
104
  piccolo/apps/sql_shell/piccolo_app.py,sha256=uFuMQIPLSMYi7y5x3wG1LPqGmEkwC-dYlmLTKrBaUQQ,221
99
105
  piccolo/apps/sql_shell/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
- piccolo/apps/sql_shell/commands/run.py,sha256=PdBoeA-fsvfLpjwRXc9_MB1q_u0UKR-EzpgFIonwIHc,2060
106
+ piccolo/apps/sql_shell/commands/run.py,sha256=mOt0kuJ_-QhKXrlt0Ceicx4JxxCLlK6OwUyB_HQpLiI,2097
101
107
  piccolo/apps/tester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
108
  piccolo/apps/tester/piccolo_app.py,sha256=LAAzW3SdVKZjvVXpBxwprYxV0rJbeDD3CB5JpRmHm8k,225
103
109
  piccolo/apps/tester/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
110
  piccolo/apps/tester/commands/run.py,sha256=phFxim2ogARAviW-YT11y9F-L5SJxSioAIepUzQeAWU,2431
105
111
  piccolo/apps/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
112
  piccolo/apps/user/piccolo_app.py,sha256=yfw1J9FEnBWdgGdUdNMnqp06Wzm5_s9TO2r5KLchrLM,842
107
- piccolo/apps/user/tables.py,sha256=KgPqONdl1SDV-3cGq5aS-za_v_5_TGHQqNFYXF0M3Jw,9082
113
+ piccolo/apps/user/tables.py,sha256=QD8FD3qhtBqfFAkAMjQVqTnxrPmwa_V2uzVzk16eRaU,9153
108
114
  piccolo/apps/user/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
115
  piccolo/apps/user/commands/change_password.py,sha256=F7mlhtTUY7uENSK8vds5oibIDJTz7QBQdrl7EB-lF9Q,649
110
116
  piccolo/apps/user/commands/change_permissions.py,sha256=LScsKJUMJqIi54cZ1SgS9Wb356KB0t7smu94FDXLfVk,1463
@@ -117,7 +123,7 @@ piccolo/apps/user/piccolo_migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu
117
123
  piccolo/columns/__init__.py,sha256=OYhO_n9anMiU9nL-K6ATq9FhAtm8RyMpqYQ7fTVbhxI,1120
118
124
  piccolo/columns/base.py,sha256=_bg9yMWjMwE76Z7RDqi9iYSmtRuFx5bkx9uYJsFHKjQ,32487
119
125
  piccolo/columns/choices.py,sha256=-HNQuk9vMmVZIPZ5PMeXGTfr23o4nzKPSAkvcG1k0y8,723
120
- piccolo/columns/column_types.py,sha256=XVI6qA_qsP1BD6bqVqQdj7a8dldoYz4VgOPI-wW696I,84747
126
+ piccolo/columns/column_types.py,sha256=Wo6g14aL1vpOFugsY-6n-q6JUJaKih-cIn9NBp-f3fI,84759
121
127
  piccolo/columns/combination.py,sha256=vMXC2dfY7pvnCFhsT71XFVyb4gdQzfRsCMaiduu04Ss,6900
122
128
  piccolo/columns/indexes.py,sha256=NfNok3v_791jgDlN28KmhP9ZCjl6031BXmjxV3ovXJk,372
123
129
  piccolo/columns/m2m.py,sha256=QMeSOnm4DT2cG9U5jC6sOZ6z9DxCWwDyZMSqk0wR2q4,14682
@@ -129,7 +135,7 @@ piccolo/columns/defaults/date.py,sha256=Duuyi-QJ9Rr72aJkCNnjyO1CJBE-inZNGKnyV8tb
129
135
  piccolo/columns/defaults/interval.py,sha256=ypaQpgDm1AL0WTMFEgKCt0I-e9ADUYdRRSBl65IJdiw,1987
130
136
  piccolo/columns/defaults/time.py,sha256=2e0SDjl9_Mrw2YUeLFXDDYhmlC9Qjek3MkhvmWKQFH0,2417
131
137
  piccolo/columns/defaults/timestamp.py,sha256=3Ng_LJ76nic-3j_AIzZfUvj3djIFRVkps98w1b_2lUM,3565
132
- piccolo/columns/defaults/timestamptz.py,sha256=zN945oderS5HU22LMFLcT0iHlYt2wur99w-6lkbmWAI,2057
138
+ piccolo/columns/defaults/timestamptz.py,sha256=RMw9wW20NbvaG_HY-0amBRuD-OLde4at_xQCf0D8NE4,2096
133
139
  piccolo/columns/defaults/uuid.py,sha256=zBBaXlUsDTKcxRFDWwqgpiDRrYd7ptxC_hf7UqYhRjY,470
134
140
  piccolo/columns/operators/__init__.py,sha256=fIIm309C7ddqrP-M9oLlfhcZEM4Fx5B203QMzBm0OpM,310
135
141
  piccolo/columns/operators/base.py,sha256=UfaqPd-ieqydrjhvcGYiwHMOKs199tTiT1gFE15DZzo,34
@@ -144,7 +150,7 @@ piccolo/engine/cockroach.py,sha256=gGnihplotMZMWqHwRnZYnnbKU3jFrwttwOlNtktoeLE,1
144
150
  piccolo/engine/exceptions.py,sha256=X8xZiTF-L9PIqFT-KDXnv1jFIIOZMF8fYK692chttJE,44
145
151
  piccolo/engine/finder.py,sha256=GjzBNtzRzH79fjtRn7OI3nZiOXE8JfoQWAvHVPrPNx4,507
146
152
  piccolo/engine/postgres.py,sha256=DekL3KafCdzSAEQ6_EgOiUB1ERXh2xpePYwI9QvmN-c,18955
147
- piccolo/engine/sqlite.py,sha256=KwJc3UttBP_8qSREbLJshqEfROF17ENf0Ju9BwI5_so,25236
153
+ piccolo/engine/sqlite.py,sha256=Oe0GBrIUSUkutvk5LoXGWC6HFQzKeusfql5-NMssH_s,25735
148
154
  piccolo/query/__init__.py,sha256=bcsMV4813rMRAIqGv4DxI4eyO4FmpXkDv9dfTk5pt3A,699
149
155
  piccolo/query/base.py,sha256=sO5VyicbWjgYaQukr6jqUqUUrOctL6QJ1MjcsgDKHXM,14912
150
156
  piccolo/query/mixins.py,sha256=X9HEYnj6uOjgTkGr4vgqTwN_dokJPzVagwbFx385atQ,24468
@@ -166,7 +172,7 @@ piccolo/query/methods/drop_index.py,sha256=5x3vHpoOmQ1SMhj6L7snKXX6M9l9j1E1PFSO6
166
172
  piccolo/query/methods/exists.py,sha256=lTMjtrFPFygZmaPV3sfQKXc3K0sVqJ2S6PDc3fRK6YQ,1203
167
173
  piccolo/query/methods/indexes.py,sha256=J-QUqaBJwpgahskUH0Cu0Mq7zEKcfVAtDsUVIVX-C4c,943
168
174
  piccolo/query/methods/insert.py,sha256=ssLJ_wn08KnOwwr7t-VILyn1P4hrvM63CfPIcAJWT5k,4701
169
- piccolo/query/methods/objects.py,sha256=BfCOIbNMj7FWkmK5STaINkfDFmwzZvDZi60jCumjs1o,15627
175
+ piccolo/query/methods/objects.py,sha256=wK3poKXVVTpjFgpVfmumVn9q8Qiqz0PZ4ukm7Ih-CGw,15511
170
176
  piccolo/query/methods/raw.py,sha256=wQWR8b-yA_Gr-5lqRMZe9BOAAMBAw8CqTx37qVYvM1A,987
171
177
  piccolo/query/methods/refresh.py,sha256=wg1zghKfwz-VmqK4uWa4GNMiDtK-skTqow591Hb3ONM,5854
172
178
  piccolo/query/methods/select.py,sha256=41OW-DIE_wr5VdxSusMKNT2aUhzQsCwK2Qh1XqgXHg0,22424
@@ -237,7 +243,7 @@ tests/apps/shell/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
237
243
  tests/apps/shell/commands/test_run.py,sha256=wH3ORQwJ1a02kA-WnZUCNmb0AlwXpRKoTntOZVUZAqI,1170
238
244
  tests/apps/sql_shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
239
245
  tests/apps/sql_shell/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
240
- tests/apps/sql_shell/commands/test_run.py,sha256=6p0nqCoG_qNLrKeBuHspmer_SrMwEF-vfp9LbPj2W2E,425
246
+ tests/apps/sql_shell/commands/test_run.py,sha256=MDfuJdgOSEMAEXtBQYWx2N31tRHgKPPQNUpVCRTPyu8,1033
241
247
  tests/apps/tester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
242
248
  tests/apps/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
243
249
  tests/apps/user/test_tables.py,sha256=5ImGAWpIKxlqweGeJRMiVOHaBJxRwSzsp3GZ7x6lzCo,9807
@@ -259,6 +265,7 @@ tests/columns/test_db_column_name.py,sha256=0wz6y4GNGy4nhMdHmYzEnChQGpK2UhWFFKrn
259
265
  tests/columns/test_defaults.py,sha256=rwlU1fXt3cCl7C51eLlZXqgWkE-K5W0pHvTrwkAKyCo,2896
260
266
  tests/columns/test_double_precision.py,sha256=7rhcSfDkb2fBh_zEG4UGwD_GW1sy6U9-8NooHuCS09Q,544
261
267
  tests/columns/test_get_sql_value.py,sha256=mKgsInN374jzV99y9mg_ZiG-AvnJgz36SZi89xL7RZM,1768
268
+ tests/columns/test_integer.py,sha256=IcIQq0gF29gTxLY3CJuXtE13-20emqisY2wRQsu80F4,772
262
269
  tests/columns/test_interval.py,sha256=2M18pfoGxLLosEvwTmuC4zQkM6jWwU0Nv2fqViW3xOs,2780
263
270
  tests/columns/test_json.py,sha256=_cziJvw2uT8e_4u9lKhmU56lgQeE7bEqCXYf6AzfChA,3482
264
271
  tests/columns/test_jsonb.py,sha256=KXPgJTchobzHNss86Gb0CeTDlaa5S3pQ8cM3D06-7J8,8592
@@ -333,11 +340,11 @@ tests/table/test_exists.py,sha256=AHvhodkRof7PVd4IDdGQ2nyOj_1Cag1Rpg1H84s4jU0,28
333
340
  tests/table/test_from_dict.py,sha256=I4PMxuzgkgi3-adaw9Gr3u5tQHexc31Vrq7RSPcPcJs,840
334
341
  tests/table/test_indexes.py,sha256=VfM2FqFO8OOaL88QYQRqPX_PPniSBoPFeLPjXZ8jHBk,2073
335
342
  tests/table/test_inheritance.py,sha256=AAkhEhhixVGweGe7ckzj-yypW-cj6D88Cca4-pjkwKw,3110
336
- tests/table/test_insert.py,sha256=-xaoL6wTNB6UImiCk9NQKQ-B21l96H-9tN2_iEgXu5A,13695
343
+ tests/table/test_insert.py,sha256=c7hJ1SsTiW43l_Z5KHSN3894ICzttOAsTfWN9rUOl0k,13696
337
344
  tests/table/test_join.py,sha256=Ukgvjc8NweBGHM7fVFytGQYG9P9thRaMeEvWXYs2Qes,15910
338
345
  tests/table/test_join_on.py,sha256=cdAV39JwHi0kIas2p9cw7mcsUv6mKLZD--_SUA0zLfI,2771
339
346
  tests/table/test_metaclass.py,sha256=pMv0PHh-2a9p74bweQXCXnq1OFsJ7Gk0uWRFdCTMf58,4123
340
- tests/table/test_objects.py,sha256=bir86ks-Ngy8x9Eu9bekOrh6twBYdEkIgTdbBWY6x9s,8187
347
+ tests/table/test_objects.py,sha256=ptbeIICOfZ2-Fl6K_Llo1SKIyQ5ngUZ-rZtgll5cMWM,9047
341
348
  tests/table/test_output.py,sha256=ZnpPbgVp79JcB6E_ooWQxOpOlhkwNUlMxC-1LSIEc2Y,4304
342
349
  tests/table/test_raw.py,sha256=9PTvYngQi41nYd5lKzkJdTqsEcwrdOXcvZjq-W26CwQ,1683
343
350
  tests/table/test_ref.py,sha256=eYNRnYHzNMXuMbV3B1ca5EidpIg4500q6hr1ccuVaso,269
@@ -372,9 +379,9 @@ tests/utils/test_sql_values.py,sha256=vzxRmy16FfLZPH-sAQexBvsF9MXB8n4smr14qoEOS5
372
379
  tests/utils/test_sync.py,sha256=9ytVo56y2vPQePvTeIi9lHIouEhWJbodl1TmzkGFrSo,799
373
380
  tests/utils/test_table_reflection.py,sha256=SIzuat-IpcVj1GCFyOWKShI8YkhdOPPFH7qVrvfyPNE,3794
374
381
  tests/utils/test_warnings.py,sha256=NvSC_cvJ6uZcwAGf1m-hLzETXCqprXELL8zg3TNLVMw,269
375
- piccolo-1.22.0.dist-info/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
376
- piccolo-1.22.0.dist-info/METADATA,sha256=eL8y2tQr2Vd5Wmmqe0ICPO145ccpTL_5cthuK_zXIVU,5199
377
- piccolo-1.22.0.dist-info/WHEEL,sha256=00yskusixUoUt5ob_CiUp6LsnN5lqzTJpoqOFg_FVIc,92
378
- piccolo-1.22.0.dist-info/entry_points.txt,sha256=zYhu-YNtMlh2N_8wptCS8YWKOgc81UPL3Ji5gly8ouc,47
379
- piccolo-1.22.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
380
- piccolo-1.22.0.dist-info/RECORD,,
382
+ piccolo-1.24.0.dist-info/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
383
+ piccolo-1.24.0.dist-info/METADATA,sha256=Bnqm6WVw_mNMp5G2xHPbJU-bIJTTQgmaiLRlXFaOhKM,5509
384
+ piccolo-1.24.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
385
+ piccolo-1.24.0.dist-info/entry_points.txt,sha256=SJPHET4Fi1bN5F3WqcKkv9SClK3_F1I7m4eQjk6AFh0,46
386
+ piccolo-1.24.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
387
+ piccolo-1.24.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.38.1)
2
+ Generator: setuptools (76.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
2
  piccolo = piccolo.main:main
3
-
@@ -2,13 +2,37 @@ from unittest import TestCase
2
2
  from unittest.mock import MagicMock, patch
3
3
 
4
4
  from piccolo.apps.sql_shell.commands.run import run
5
+ from tests.base import postgres_only, sqlite_only
5
6
 
6
7
 
7
8
  class TestRun(TestCase):
9
+ @postgres_only
8
10
  @patch("piccolo.apps.sql_shell.commands.run.subprocess")
9
- def test_run(self, subprocess: MagicMock):
11
+ def test_psql(self, subprocess: MagicMock):
10
12
  """
11
- A simple test to make sure it executes without raising any exceptions.
13
+ Make sure psql was called correctly.
12
14
  """
13
15
  run()
14
16
  self.assertTrue(subprocess.run.called)
17
+
18
+ assert subprocess.run.call_args.args[0] == [
19
+ "psql",
20
+ "-U",
21
+ "postgres",
22
+ "-h",
23
+ "localhost",
24
+ "-p",
25
+ "5432",
26
+ "piccolo",
27
+ ]
28
+
29
+ @sqlite_only
30
+ @patch("piccolo.apps.sql_shell.commands.run.subprocess")
31
+ def test_sqlite3(self, subprocess: MagicMock):
32
+ """
33
+ Make sure sqlite3 was called correctly.
34
+ """
35
+ run()
36
+ self.assertTrue(subprocess.run.called)
37
+
38
+ assert subprocess.run.call_args.args[0] == ["sqlite3", "test.sqlite"]
@@ -0,0 +1,32 @@
1
+ from piccolo.columns.column_types import Integer
2
+ from piccolo.table import Table
3
+ from piccolo.testing.test_case import AsyncTableTest
4
+ from tests.base import sqlite_only
5
+
6
+
7
+ class MyTable(Table):
8
+ integer = Integer()
9
+
10
+
11
+ @sqlite_only
12
+ class TestInteger(AsyncTableTest):
13
+ tables = [MyTable]
14
+
15
+ async def test_large_integer(self):
16
+ """
17
+ Make sure large integers can be inserted and retrieved correctly.
18
+
19
+ There was a bug with this in SQLite:
20
+
21
+ https://github.com/piccolo-orm/piccolo/issues/1127
22
+
23
+ """
24
+ integer = 625757527765811240
25
+
26
+ row = MyTable(integer=integer)
27
+ await row.save()
28
+
29
+ _row = MyTable.objects().first().run_sync()
30
+ assert _row is not None
31
+
32
+ self.assertEqual(_row.integer, integer)
@@ -201,7 +201,7 @@ class TestOnConflict(TestCase):
201
201
  Make sure that a composite unique constraint can be used as a target.
202
202
 
203
203
  We only run it on Postgres and Cockroach because we use ALTER TABLE
204
- to add a contraint, which SQLite doesn't support.
204
+ to add a constraint, which SQLite doesn't support.
205
205
  """
206
206
  Band = self.Band
207
207
 
@@ -1,3 +1,5 @@
1
+ from piccolo.columns.column_types import ForeignKey
2
+ from piccolo.testing.test_case import AsyncTableTest
1
3
  from tests.base import DBTestCase, engines_only, sqlite_only
2
4
  from tests.example_apps.music.tables import Band, Manager
3
5
 
@@ -268,3 +270,32 @@ class TestGetOrCreate(DBTestCase):
268
270
  self.assertIsInstance(band.manager, Manager)
269
271
  self.assertEqual(band.name, "New Band 2")
270
272
  self.assertEqual(band.manager.name, "Guido")
273
+
274
+
275
+ class BandNotNull(Band, tablename="band"):
276
+ manager = ForeignKey(Manager, null=False)
277
+
278
+
279
+ class TestGetOrCreateNotNull(AsyncTableTest):
280
+
281
+ tables = [BandNotNull, Manager]
282
+
283
+ async def test_not_null(self):
284
+ """
285
+ There was a bug where `get_or_create` would fail for columns with
286
+ `default=None` and `null=False`, even if the value for those columns
287
+ was specified in the where clause.
288
+
289
+ https://github.com/piccolo-orm/piccolo/issues/1152
290
+
291
+ """
292
+
293
+ manager = Manager({Manager.name: "Test"})
294
+ await manager.save()
295
+
296
+ self.assertIsInstance(
297
+ await BandNotNull.objects().get_or_create(
298
+ BandNotNull.manager == manager
299
+ ),
300
+ BandNotNull,
301
+ )