coloco 0.4.4__tar.gz → 0.4.6__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 (46) hide show
  1. {coloco-0.4.4 → coloco-0.4.6}/PKG-INFO +1 -1
  2. {coloco-0.4.4 → coloco-0.4.6}/pyproject.toml +1 -1
  3. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/api.py +5 -3
  4. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/app.py +10 -5
  5. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/cli/db.py +5 -2
  6. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/db.py +28 -15
  7. {coloco-0.4.4 → coloco-0.4.6}/LICENSE +0 -0
  8. {coloco-0.4.4 → coloco-0.4.6}/README.md +0 -0
  9. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/__init__.py +0 -0
  10. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/__main__.py +0 -0
  11. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/cli/api.py +0 -0
  12. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/cli/build.py +0 -0
  13. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/cli/createapp.py +0 -0
  14. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/cli/dev.py +0 -0
  15. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/cli/node.py +0 -0
  16. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/cli/package.py +0 -0
  17. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/cli/serve.py +0 -0
  18. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/codegen.py +0 -0
  19. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/config.py +0 -0
  20. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/dependencies.py +0 -0
  21. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/exceptions.py +0 -0
  22. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/lifespan.py +0 -0
  23. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/static.py +0 -0
  24. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/docker/.dockerignore-tpl +0 -0
  25. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/docker/Dockerfile-tpl +0 -0
  26. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/docker/docker-compose.yml-tpl +0 -0
  27. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+app/404.svelte-tpl +0 -0
  28. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+app/app.css-tpl +0 -0
  29. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+app/app.svelte-tpl +0 -0
  30. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+app/index.html-tpl +0 -0
  31. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+app/index.svelte-tpl +0 -0
  32. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+app/main.ts-tpl +0 -0
  33. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+node/openapi-ts.config.ts-tpl +0 -0
  34. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+node/package.json-tpl +0 -0
  35. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+node/svelte.config.js-tpl +0 -0
  36. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+node/tsconfig.json-tpl +0 -0
  37. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+node/vite-env.d.ts-tpl +0 -0
  38. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/+node/vite.config.ts-tpl +0 -0
  39. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/.gitignore-tpl +0 -0
  40. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/README.md-tpl +0 -0
  41. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/example/api.py-tpl +0 -0
  42. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/example/index.svelte-tpl +0 -0
  43. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/main.py-tpl +0 -0
  44. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/pyproject.toml-tpl +0 -0
  45. {coloco-0.4.4 → coloco-0.4.6}/src/coloco/templates/standard/tsconfig.json-tpl +0 -0
  46. {coloco-0.4.4 → coloco-0.4.6}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: coloco
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: A kit for creating FastAPI + Svelte applications
5
5
  Author-Email: Channel Cat <channelcat@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "coloco"
3
- version = "0.4.4"
3
+ version = "0.4.6"
4
4
  description = "A kit for creating FastAPI + Svelte applications"
5
5
  authors = [
6
6
  { name = "Channel Cat", email = "channelcat@gmail.com" },
@@ -87,12 +87,12 @@ def create_api(is_dev: bool = False):
87
87
  global_routes: list[ColocoRoute] = []
88
88
 
89
89
 
90
- def api(func):
90
+ def api(func: T) -> T:
91
91
  # Auto-route
92
92
  return _add_global_route([f"/{func.__name__}"], {}, func, "GET")
93
93
 
94
94
 
95
- def _add_global_route(args, kwargs, func, method: str):
95
+ def _add_global_route(args, kwargs, func: T, method: str) -> T:
96
96
  module_name = func.__module__.lstrip("src.app")
97
97
 
98
98
  # Prepend module name to path
@@ -113,7 +113,9 @@ def _add_global_route(args, kwargs, func, method: str):
113
113
  else:
114
114
  kwargs["path"] = path
115
115
 
116
- return global_routes.append(ColocoRoute(args, kwargs, func, method, module_name))
116
+ global_routes.append(ColocoRoute(args, kwargs, func, method, module_name))
117
+
118
+ return func
117
119
 
118
120
 
119
121
  def _make_route_decorator(method: str) -> Callable[..., Callable[[T], T]]:
@@ -1,6 +1,7 @@
1
1
  from .api import create_api, global_routes
2
2
  from dataclasses import dataclass
3
3
  from .db import (
4
+ create_model_serializers,
4
5
  get_orm_config,
5
6
  inject_model_serializers,
6
7
  register_db_lifecycle,
@@ -71,10 +72,6 @@ def create_app(name: str, database_url: str = None) -> ColocoApp:
71
72
  print(traceback.format_exc())
72
73
  continue
73
74
 
74
- # Inject type hints
75
- for route in global_routes:
76
- fill_type_hints(route.func, use_literals=True)
77
-
78
75
  # Setup Database
79
76
  # We need this first to grab the models for route type hints
80
77
  has_database = bool(database_url)
@@ -83,10 +80,18 @@ def create_app(name: str, database_url: str = None) -> ColocoApp:
83
80
  database_url,
84
81
  model_files=discover_files(src_path, name="models.py", is_dev=mode == "dev"),
85
82
  )
86
- inject_model_serializers(orm_config, global_routes)
83
+ model_to_serializer = create_model_serializers(orm_config)
87
84
  else:
88
85
  orm_config = None
89
86
 
87
+ # Inject type hints
88
+ for route in global_routes:
89
+ fill_type_hints(route.func, use_literals=True)
90
+
91
+ # Inject model serializers after type hints
92
+ if has_database:
93
+ inject_model_serializers(model_to_serializer, global_routes)
94
+
90
95
  router = APIRouter()
91
96
  for route in global_routes:
92
97
  router.api_route(
@@ -40,8 +40,11 @@ def db_command(func):
40
40
  def wrapper(*args, **kwargs):
41
41
  tortoise_logger.setLevel(logging.WARNING)
42
42
  run(func(*args, **kwargs))
43
- # TODO: figure out why the event loop is closed already when using postgres
44
- # run(Tortoise.close_connections())
43
+ try:
44
+ run(Tortoise.close_connections())
45
+ except RuntimeError:
46
+ # TODO: figure out why the event loop is closed already when using postgres
47
+ pass
45
48
 
46
49
  return app.command(name=func.__name__)(wrapper)
47
50
 
@@ -1,12 +1,14 @@
1
1
  from .api import ColocoRoute
2
2
  from contextlib import asynccontextmanager
3
3
  from collections import defaultdict
4
+ from copy import copy
4
5
  from functools import wraps
5
6
  from .lifespan import register_lifespan
6
7
  from rich import print
8
+ from tortoise.fields import Field
7
9
  from type_less import replace_type_hint_map_deep
8
- from typing import TypeVar
9
-
10
+ from typing import *
11
+ import datetime
10
12
 
11
13
  CLS = TypeVar("CLS")
12
14
 
@@ -76,7 +78,7 @@ def register_db_lifecycle(app):
76
78
  register_lifespan(lifecycle_connect_database)
77
79
 
78
80
 
79
- def inject_model_serializers(orm_config: dict, routes: list[ColocoRoute]):
81
+ def create_model_serializers(orm_config: dict):
80
82
  try:
81
83
  from tortoise import Tortoise
82
84
  from tortoise.contrib.pydantic import pydantic_model_creator
@@ -99,14 +101,25 @@ def inject_model_serializers(orm_config: dict, routes: list[ColocoRoute]):
99
101
  for model_class in model_classes:
100
102
  # Wrapped with @serializable
101
103
  if getattr(model_class, "__model_serializer_create", None):
102
- model_class.__model_serializer = pydantic_model_creator(
104
+ model_to_serializer[model_class] = pydantic_model_creator(
103
105
  model_class, **model_class.__model_serializer_create
104
106
  )
105
107
  # Create a general serializer
106
- elif not getattr(model_class, "__model_serializer", None):
107
- model_class.__model_serializer = pydantic_model_creator(model_class)
108
- model_to_serializer[model_class] = model_class.__model_serializer
108
+ elif not model_class in model_to_serializer:
109
+ model_to_serializer[model_class] = pydantic_model_creator(model_class)
110
+
111
+ # Inject type annotations into model
112
+ # TODO: find a better way to do this that's less dependent on Tortoise internals
113
+ description = model_class.describe(serializable=False)
114
+ fields = [*description["data_fields"], description["pk_field"]]
115
+ for field in fields:
116
+ if field["name"] not in model_class.__annotations__:
117
+ model_class.__annotations__[field["name"]] = field["python_type"]
118
+
119
+ return model_to_serializer
120
+
109
121
 
122
+ def inject_model_serializers(model_to_serializer: dict, routes: list[ColocoRoute]):
110
123
  for route in routes:
111
124
  route.func.__annotations__["return"], occurrences = replace_type_hint_map_deep(
112
125
  route.func.__annotations__["return"],
@@ -120,25 +133,25 @@ def inject_model_serializers(orm_config: dict, routes: list[ColocoRoute]):
120
133
  # - Pydantic - sync serialization only
121
134
  # - JSON Encoder - sync serialization only, + happens after serialization
122
135
  if occurrences:
123
- route.func = _wrap_model_serializer(route.func)
136
+ route.func = _wrap_model_serializer(route.func, model_to_serializer)
124
137
 
125
138
 
126
- async def _serialize_models(obj):
139
+ async def _serialize_models(obj, model_to_serializer):
127
140
  """
128
141
  Auto-runs the serializer for tortoise models
129
142
  """
130
- if hasattr(obj, "__model_serializer"):
131
- return await obj.__model_serializer.from_tortoise_orm(obj)
143
+ if type(obj) in model_to_serializer:
144
+ return await model_to_serializer[type(obj)].from_tortoise_orm(obj)
132
145
  elif isinstance(obj, list):
133
- return [await _serialize_models(item) for item in obj]
146
+ return [await _serialize_models(item, model_to_serializer) for item in obj]
134
147
  elif isinstance(obj, dict):
135
- return {k: await _serialize_models(v) for k, v in obj.items()}
148
+ return {k: await _serialize_models(v, model_to_serializer) for k, v in obj.items()}
136
149
  return obj
137
150
 
138
151
 
139
- def _wrap_model_serializer(func):
152
+ def _wrap_model_serializer(func, model_to_serializer):
140
153
  @wraps(func)
141
154
  async def wrapper(*args, **kwargs):
142
- return await _serialize_models(await func(*args, **kwargs))
155
+ return await _serialize_models(await func(*args, **kwargs), model_to_serializer)
143
156
 
144
157
  return wrapper
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes