ohmyapi 0.1.11__tar.gz → 0.1.13__tar.gz

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 (24) hide show
  1. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/PKG-INFO +31 -9
  2. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/README.md +30 -8
  3. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/pyproject.toml +1 -1
  4. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/cli.py +39 -5
  5. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/__init__.py +0 -0
  6. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/__main__.py +0 -0
  7. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/builtin/auth/__init__.py +0 -0
  8. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/builtin/auth/models.py +0 -0
  9. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/builtin/auth/permissions.py +0 -0
  10. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/builtin/auth/routes.py +0 -0
  11. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/core/__init__.py +0 -0
  12. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/core/runtime.py +0 -0
  13. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/core/scaffolding.py +0 -0
  14. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/core/templates/app/__init__.py.j2 +0 -0
  15. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/core/templates/app/models.py.j2 +0 -0
  16. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/core/templates/app/routes.py.j2 +0 -0
  17. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/core/templates/project/README.md.j2 +0 -0
  18. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/core/templates/project/pyproject.toml.j2 +0 -0
  19. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/core/templates/project/settings.py.j2 +0 -0
  20. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/db/__init__.py +0 -0
  21. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/db/exceptions.py +0 -0
  22. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/db/model/__init__.py +0 -0
  23. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/db/model/model.py +0 -0
  24. {ohmyapi-0.1.11 → ohmyapi-0.1.13}/src/ohmyapi/router.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ohmyapi
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: A Django-like but async web-framework based on FastAPI and TortoiseORM.
5
5
  License-Expression: MIT
6
6
  Keywords: fastapi,tortoise,orm,async,web-framework
@@ -267,8 +267,6 @@ In your `routes.py`:
267
267
 
268
268
  ```python
269
269
  from ohmyapi.router import APIRouter, Depends
270
-
271
- from ohmyapi_auth.models import User
272
270
  from ohmyapi_auth import (
273
271
  models as auth,
274
272
  permissions,
@@ -276,7 +274,7 @@ from ohmyapi_auth import (
276
274
 
277
275
  from .models import Tournament
278
276
 
279
- router = APIRouter(prefix="/tournament")
277
+ router = APIRouter(prefix="/tournament", tags=["Tournament"])
280
278
 
281
279
 
282
280
  @router.get("/")
@@ -294,10 +292,11 @@ Use Tortoise's `Manager` to implement model-level permissions.
294
292
 
295
293
  ```python
296
294
  from ohmyapi.db import Manager
295
+ from ohmyapi_auth.models import User
297
296
 
298
297
 
299
298
  class TeamManager(Manager):
300
- async def for_user(self, user: ohmyapi_auth.models.User):
299
+ async def for_user(self, user: User):
301
300
  return await self.filter(members=user).all()
302
301
 
303
302
 
@@ -308,6 +307,24 @@ class Team(Model):
308
307
  manager = TeamManager()
309
308
  ```
310
309
 
310
+ Use the custom manager in your FastAPI route handler:
311
+
312
+ ```python
313
+ from ohmyapi.router import APIRouter
314
+ from ohmyapi_auth import (
315
+ models as auth,
316
+ permissions,
317
+ )
318
+
319
+ router = APIRouter(prefix="/tournament", tags=["Tournament"])
320
+
321
+
322
+ @router.get("/teams")
323
+ async def teams(user: auth.User = Depends(permissions.require_authenticated)):
324
+ queryset = Team.for_user(user)
325
+ return await Tournament.Schema.many.from_queryset(queryset)
326
+ ```
327
+
311
328
  ## Shell
312
329
 
313
330
  Similar to Django, you can attach to an interactive shell with your project already loaded inside.
@@ -336,12 +353,17 @@ Out[2]:
336
353
  Routes:
337
354
  - APIRoute(path='/auth/login', name='login', methods=['POST'])
338
355
  - APIRoute(path='/auth/refresh', name='refresh_token', methods=['POST'])
339
- - APIRoute(path='/auth/me', name='me', methods=['GET'])
340
- - APIRoute(path='/auth/introspect', name='introspect', methods=['GET'])}
356
+ - APIRoute(path='/auth/introspect', name='introspect', methods=['GET'])
357
+ - APIRoute(path='/auth/me', name='me', methods=['GET']),
358
+ 'tournament': App: tournament
359
+ Models:
360
+ - Tournament
361
+ - Event
362
+ - Team
363
+ Routes:
364
+ - APIRoute(path='/tournament/', name='list', methods=['GET'])}
341
365
 
342
366
  In [3]: from tournament.models import Tournament
343
- Out[3]:
344
-
345
367
  ```
346
368
 
347
369
 
@@ -235,8 +235,6 @@ In your `routes.py`:
235
235
 
236
236
  ```python
237
237
  from ohmyapi.router import APIRouter, Depends
238
-
239
- from ohmyapi_auth.models import User
240
238
  from ohmyapi_auth import (
241
239
  models as auth,
242
240
  permissions,
@@ -244,7 +242,7 @@ from ohmyapi_auth import (
244
242
 
245
243
  from .models import Tournament
246
244
 
247
- router = APIRouter(prefix="/tournament")
245
+ router = APIRouter(prefix="/tournament", tags=["Tournament"])
248
246
 
249
247
 
250
248
  @router.get("/")
@@ -262,10 +260,11 @@ Use Tortoise's `Manager` to implement model-level permissions.
262
260
 
263
261
  ```python
264
262
  from ohmyapi.db import Manager
263
+ from ohmyapi_auth.models import User
265
264
 
266
265
 
267
266
  class TeamManager(Manager):
268
- async def for_user(self, user: ohmyapi_auth.models.User):
267
+ async def for_user(self, user: User):
269
268
  return await self.filter(members=user).all()
270
269
 
271
270
 
@@ -276,6 +275,24 @@ class Team(Model):
276
275
  manager = TeamManager()
277
276
  ```
278
277
 
278
+ Use the custom manager in your FastAPI route handler:
279
+
280
+ ```python
281
+ from ohmyapi.router import APIRouter
282
+ from ohmyapi_auth import (
283
+ models as auth,
284
+ permissions,
285
+ )
286
+
287
+ router = APIRouter(prefix="/tournament", tags=["Tournament"])
288
+
289
+
290
+ @router.get("/teams")
291
+ async def teams(user: auth.User = Depends(permissions.require_authenticated)):
292
+ queryset = Team.for_user(user)
293
+ return await Tournament.Schema.many.from_queryset(queryset)
294
+ ```
295
+
279
296
  ## Shell
280
297
 
281
298
  Similar to Django, you can attach to an interactive shell with your project already loaded inside.
@@ -304,11 +321,16 @@ Out[2]:
304
321
  Routes:
305
322
  - APIRoute(path='/auth/login', name='login', methods=['POST'])
306
323
  - APIRoute(path='/auth/refresh', name='refresh_token', methods=['POST'])
307
- - APIRoute(path='/auth/me', name='me', methods=['GET'])
308
- - APIRoute(path='/auth/introspect', name='introspect', methods=['GET'])}
324
+ - APIRoute(path='/auth/introspect', name='introspect', methods=['GET'])
325
+ - APIRoute(path='/auth/me', name='me', methods=['GET']),
326
+ 'tournament': App: tournament
327
+ Models:
328
+ - Tournament
329
+ - Event
330
+ - Team
331
+ Routes:
332
+ - APIRoute(path='/tournament/', name='list', methods=['GET'])}
309
333
 
310
334
  In [3]: from tournament.models import Tournament
311
- Out[3]:
312
-
313
335
  ```
314
336
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ohmyapi"
3
- version = "0.1.11"
3
+ version = "0.1.13"
4
4
  description = "A Django-like but async web-framework based on FastAPI and TortoiseORM."
5
5
  license = "MIT"
6
6
  keywords = ["fastapi", "tortoise", "orm", "async", "web-framework"]
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import atexit
2
3
  import importlib
3
4
  import sys
4
5
  import typer
@@ -16,13 +17,13 @@ Find your loaded project singleton via identifier: `p`
16
17
 
17
18
  @app.command()
18
19
  def startproject(name: str):
19
- """Create a new OhMyAPI project in the given directory"""
20
+ """Create a new OhMyAPI project in the given directory."""
20
21
  scaffolding.startproject(name)
21
22
 
22
23
 
23
24
  @app.command()
24
25
  def startapp(app_name: str, root: str = "."):
25
- """Create a new app with the given name in your OhMyAPI project"""
26
+ """Create a new app with the given name in your OhMyAPI project."""
26
27
  scaffolding.startapp(app_name, root)
27
28
 
28
29
 
@@ -45,6 +46,28 @@ def shell(root: str = "."):
45
46
  project_path = Path(root).resolve()
46
47
  project = runtime.Project(project_path)
47
48
 
49
+ # Ensure the ORM is shutdown
50
+ async def close_project():
51
+ try:
52
+ await project.close_orm()
53
+ print("Tortoise ORM closed successfully.")
54
+ except Exception as e:
55
+ print(f"Error closing ORM: {e}")
56
+
57
+ def cleanup():
58
+ loop = None
59
+ try:
60
+ loop = asyncio.get_running_loop()
61
+ except RuntimeError:
62
+ pass
63
+ if loop and loop.is_running():
64
+ asyncio.create_task(close_project())
65
+ else:
66
+ asyncio.run(close_project())
67
+
68
+ # Ensure the ORM is initialized
69
+ asyncio.run(project.init_orm())
70
+
48
71
  try:
49
72
  from IPython import start_ipython
50
73
  shell_vars = {
@@ -56,11 +79,13 @@ def shell(root: str = "."):
56
79
  c.TerminalInteractiveShell.banner2 = banner.format(**{
57
80
  "project_name": f"{f'{project.settings.PROJECT_NAME} ' if getattr(project.settings, 'PROJECT_NAME', '') else ''}[{Path(project_path).resolve()}]",
58
81
  })
82
+ atexit.register(cleanup)
59
83
  start_ipython(argv=[], user_ns=shell_vars, config=c)
60
84
  except ImportError:
61
85
  typer.echo("IPython is not installed. Falling back to built-in Python shell.")
62
86
  import code
63
- code.interact(local={"settings": project.settings})
87
+ atexit.register(cleanup)
88
+ code.interact(local={"p": project})
64
89
 
65
90
 
66
91
  @app.command()
@@ -93,6 +118,10 @@ def migrate(app: str = "*", root: str = "."):
93
118
 
94
119
  @app.command()
95
120
  def createsuperuser(root: str = "."):
121
+ """Create a superuser in the DB.
122
+
123
+ This requires the presence of `ohmyapi_auth` in your INSTALLED_APPS to work.
124
+ """
96
125
  project_path = Path(root).resolve()
97
126
  project = runtime.Project(project_path)
98
127
  if not project.is_app_installed("ohmyapi_auth"):
@@ -103,9 +132,14 @@ def createsuperuser(root: str = "."):
103
132
  import ohmyapi_auth
104
133
  email = input("E-Mail: ")
105
134
  username = input("Username: ")
106
- password = getpass("Password: ")
135
+ password1, password2 = "foo", "bar"
136
+ while password1 != password2:
137
+ password1 = getpass("Password: ")
138
+ password2 = getpass("Repeat Password: ")
139
+ if password1 != password2:
140
+ print("Passwords didn't match!")
107
141
  user = ohmyapi_auth.models.User(email=email, username=username, is_staff=True, is_admin=True)
108
- user.set_password(password)
142
+ user.set_password(password1)
109
143
  asyncio.run(project.init_orm())
110
144
  asyncio.run(user.save())
111
145
  asyncio.run(project.close_orm())
File without changes