dynamicforms-fastapi-viewsets 0.13.0__tar.gz → 0.13.1__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.
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/PKG-INFO +1 -1
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/__init__.py +1 -1
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/route_viewset.py +7 -3
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/redis.py +3 -4
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/mixins.py +54 -3
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/.gitignore +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/LICENSE +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/README.md +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/action_configuration.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/backends/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/backends/django_orm.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/collection_viewset.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/conf.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/context/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/context/auth/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/context/auth/django.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/context/auth/jwt.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/context/auth/static.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/cursor.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/build_schema.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/celery_viewset/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/celery_viewset/client.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/celery_viewset/payload.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/celery_viewset/result_reader.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/celery_viewset/server.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/conftest.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/lifecycle_runner.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/decorators/primary_key_model_helper.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/endpoint_docs.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/error_namespace.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/error_responses.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/authorizers.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/changes.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/conftest.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/delivery.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/event.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/lifecycle.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/scopes.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/sources.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/store.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/topics.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/events/viewset.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/exceptions.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/filters/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/filters/base.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/filters/declaration.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/filters/operators.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/filters/registry.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/list_query.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/list_shapes.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/middleware/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/middleware/auth/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/middleware/auth/authorization.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/middleware/idempotency.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/middleware/rate_limiter.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/mux_ws/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/mux_ws/asgi.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/mux_ws/protocol.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/mux_ws/registry.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/mux_ws/server.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/mux_ws/transports.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/py.typed +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/response_classes.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/routing.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/save_state/__init__.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/save_state/save_state.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/fastapi_viewsets/save_state/serialize_state.py +0 -0
- {dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: dynamicforms-fastapi-viewsets
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.1
|
|
4
4
|
Summary: Django REST Framework-style viewsets for FastAPI, with optional Celery-backed async execution and a Vue/TypeScript client counterpart.
|
|
5
5
|
Project-URL: Homepage, https://github.com/dynamicforms/fastapi-viewsets
|
|
6
6
|
Project-URL: Repository, https://github.com/dynamicforms/fastapi-viewsets
|
|
@@ -216,12 +216,14 @@ def route_viewset(
|
|
|
216
216
|
on a shared mixin cannot know which viewset it will end up on. Here it can: parameters
|
|
217
217
|
no allowed shape uses are dropped so they never reach the schema, the `X-List-Shape`
|
|
218
218
|
header is kept only when there is a choice to make, and the return annotation becomes
|
|
219
|
-
the union of exactly the models this viewset can produce.
|
|
219
|
+
the union of exactly the models this viewset can produce. A declared `default_sort` becomes
|
|
220
|
+
the `sort` parameter's default.
|
|
220
221
|
"""
|
|
221
222
|
from fastapi_viewsets.list_shapes import response_model, shape_examples, SHAPE_HEADER
|
|
222
|
-
from fastapi_viewsets.mixins import T as ItemVar
|
|
223
|
+
from fastapi_viewsets.mixins import check_declared_sort, T as ItemVar
|
|
223
224
|
|
|
224
225
|
default, allowed = cls.resolve_shapes()
|
|
226
|
+
item_type = type_map.get(ItemVar, ItemVar)
|
|
225
227
|
uses = {
|
|
226
228
|
"offset": "paginated" in allowed,
|
|
227
229
|
"cursor": "cursor" in allowed,
|
|
@@ -243,9 +245,11 @@ def route_viewset(
|
|
|
243
245
|
),
|
|
244
246
|
]
|
|
245
247
|
)
|
|
248
|
+
if name == "sort" and cls.default_sort is not None:
|
|
249
|
+
check_declared_sort(cls.default_sort, item_type)
|
|
250
|
+
parameter = parameter.replace(default=cls.default_sort)
|
|
246
251
|
params.append(parameter)
|
|
247
252
|
|
|
248
|
-
item_type = type_map.get(ItemVar, ItemVar)
|
|
249
253
|
nonlocal shape_openapi_extra
|
|
250
254
|
shape_openapi_extra = shape_examples(allowed, item_type)
|
|
251
255
|
return sig.replace(parameters=params, return_annotation=response_model(allowed, item_type))
|
|
@@ -102,16 +102,15 @@ class PerLoopRedis:
|
|
|
102
102
|
return client
|
|
103
103
|
|
|
104
104
|
async def _close_with_loop(self, loop: asyncio.AbstractEventLoop, client: "Redis") -> None:
|
|
105
|
+
# A task destroyed with its closed loop is finalised by the garbage collector, which can run
|
|
106
|
+
# inside `client()` while it holds `_lock`: taking the lock there deadlocks, so that loop's
|
|
107
|
+
# client is left to the next `client()` call.
|
|
105
108
|
try:
|
|
106
109
|
await loop.create_future()
|
|
107
110
|
except asyncio.CancelledError:
|
|
108
111
|
self._forget(loop, client)
|
|
109
112
|
await client.aclose()
|
|
110
113
|
raise
|
|
111
|
-
except GeneratorExit:
|
|
112
|
-
# The task is destroyed with its loop: there is no loop to close the client on.
|
|
113
|
-
self._forget(loop, client)
|
|
114
|
-
raise
|
|
115
114
|
|
|
116
115
|
def _forget(self, loop: asyncio.AbstractEventLoop, client: "Redis") -> None:
|
|
117
116
|
with self._lock:
|
|
@@ -201,6 +201,15 @@ class SortStateColumn(BaseModel):
|
|
|
201
201
|
SortState = list[SortStateColumn]
|
|
202
202
|
|
|
203
203
|
|
|
204
|
+
SORT_DESCRIPTION = (
|
|
205
|
+
"Ordering: comma-separated `column:direction` entries in priority order (`name:asc,score:desc`)."
|
|
206
|
+
" The direction is `asc` or `desc`, `asc` when omitted; an entry with any other direction is"
|
|
207
|
+
" ignored. Without the parameter the list is ordered by its default value, where the endpoint"
|
|
208
|
+
" declares one; an empty value leaves the list unordered."
|
|
209
|
+
)
|
|
210
|
+
"""The `sort` parameter's description on every list endpoint."""
|
|
211
|
+
|
|
212
|
+
|
|
204
213
|
def parse_sort_param(sort_csv: str | None) -> SortState:
|
|
205
214
|
"""
|
|
206
215
|
Parse the 'sort' query parameter (comma-separated) into a SortState.
|
|
@@ -227,6 +236,31 @@ def parse_sort_param(sort_csv: str | None) -> SortState:
|
|
|
227
236
|
return result
|
|
228
237
|
|
|
229
238
|
|
|
239
|
+
def check_declared_sort(sort_csv: str, model: Any = None) -> SortState:
|
|
240
|
+
"""
|
|
241
|
+
Parses a sort a viewset declares (`ListMixin.default_sort`) in the `sort` parameter's syntax.
|
|
242
|
+
|
|
243
|
+
Raises ValueError for an empty entry and an unrecognised direction, which `parse_sort_param`
|
|
244
|
+
skips in a request, and, when `model` is a pydantic model, for a column that is neither one of
|
|
245
|
+
its fields nor one of its computed fields.
|
|
246
|
+
"""
|
|
247
|
+
entries = [entry.strip() for entry in sort_csv.split(",")]
|
|
248
|
+
if not all(entries):
|
|
249
|
+
raise ValueError(f"default_sort {sort_csv!r} has an empty entry")
|
|
250
|
+
result: SortState = []
|
|
251
|
+
for entry in entries:
|
|
252
|
+
column, _, raw_direction = entry.rpartition(":")
|
|
253
|
+
if not column:
|
|
254
|
+
column, raw_direction = entry, SortDirection.asc.value
|
|
255
|
+
if raw_direction not in SortDirection.__members__:
|
|
256
|
+
raise ValueError(f"default_sort {sort_csv!r}: direction of {column!r} is not 'asc' or 'desc'")
|
|
257
|
+
fields = getattr(model, "model_fields", None)
|
|
258
|
+
if isinstance(fields, dict) and column not in fields and column not in model.model_computed_fields:
|
|
259
|
+
raise ValueError(f"default_sort {sort_csv!r}: {model.__name__} has no field {column!r}")
|
|
260
|
+
result.append(SortStateColumn(column_name=column, direction=SortDirection(raw_direction)))
|
|
261
|
+
return result
|
|
262
|
+
|
|
263
|
+
|
|
230
264
|
###################################################################################################
|
|
231
265
|
# LIST
|
|
232
266
|
###################################################################################################
|
|
@@ -284,6 +318,17 @@ class ListMixin(Generic[T, TFilter], ABC):
|
|
|
284
318
|
what stops a cursor walking straight past the NULL rows.
|
|
285
319
|
"""
|
|
286
320
|
|
|
321
|
+
default_sort: str | None = None
|
|
322
|
+
"""
|
|
323
|
+
The ordering of a list request that sends no `sort` parameter, in that parameter's syntax:
|
|
324
|
+
`"name:asc"`, `"category:asc,score:desc"`. None leaves such a request unordered; so does an empty
|
|
325
|
+
`sort=` on a viewset that declares one.
|
|
326
|
+
|
|
327
|
+
`route_viewset` publishes it as the `sort` parameter's default in the schema, where the TS
|
|
328
|
+
client's `defaultSort()` reads it, and raises ValueError for an empty entry, an unrecognised
|
|
329
|
+
direction or a column the response model has neither a field nor a computed field for.
|
|
330
|
+
"""
|
|
331
|
+
|
|
287
332
|
@property
|
|
288
333
|
def nulls_first(self) -> bool:
|
|
289
334
|
return self.nulls != "last"
|
|
@@ -298,7 +343,7 @@ class ListMixin(Generic[T, TFilter], ABC):
|
|
|
298
343
|
self: "ImplMixin[Any, T] | ListMixin[T]",
|
|
299
344
|
context: Context,
|
|
300
345
|
fltr: Annotated[TFilter, Query()] = None,
|
|
301
|
-
sort: str | None = None,
|
|
346
|
+
sort: Annotated[str | None, Query(description=SORT_DESCRIPTION)] = None,
|
|
302
347
|
offset: Annotated[int, Query(ge=0)] = 0,
|
|
303
348
|
limit: Annotated[int | None, Query(ge=1)] = None,
|
|
304
349
|
cursor: str | None = None,
|
|
@@ -320,7 +365,7 @@ class ListMixin(Generic[T, TFilter], ABC):
|
|
|
320
365
|
|
|
321
366
|
query = build_list_query(
|
|
322
367
|
fltr,
|
|
323
|
-
sort,
|
|
368
|
+
self.default_sort if sort is None else sort,
|
|
324
369
|
offset=offset,
|
|
325
370
|
limit=(
|
|
326
371
|
None
|
|
@@ -797,6 +842,11 @@ class LookupMixin(Generic[TLookupFilter, TLookupItem], ABC):
|
|
|
797
842
|
|
|
798
843
|
The filter travels in `query.fltr`, never on the viewset instance, which serves concurrent
|
|
799
844
|
requests under the default `singleton` lifecycle.
|
|
845
|
+
|
|
846
|
+
The endpoint answers in the order of the source; filtering and conversion keep it. The source
|
|
847
|
+
orders the items by group, then by their text, in its own collation - `order_by` in the
|
|
848
|
+
database - because a select shows each group's items together and only the source knows which
|
|
849
|
+
field the text comes from and how its language orders it.
|
|
800
850
|
"""
|
|
801
851
|
|
|
802
852
|
__router = APIRouter()
|
|
@@ -804,7 +854,8 @@ class LookupMixin(Generic[TLookupFilter, TLookupItem], ABC):
|
|
|
804
854
|
@abstractmethod
|
|
805
855
|
async def perform_lookup(self, context: Context) -> ListRecords:
|
|
806
856
|
"""
|
|
807
|
-
Subclasses implement this to return the lookup source
|
|
857
|
+
Subclasses implement this to return the lookup source, in the order the endpoint answers
|
|
858
|
+
in: by group, then by text.
|
|
808
859
|
|
|
809
860
|
A lazy source - a generator, an unevaluated QuerySet - is what an overridden
|
|
810
861
|
`apply_lookup_filter` pushes the filter into. Rows are TLookupItem once `to_lookup_item` has
|
|
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
|
|
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
|
|
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
|
|
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
|
{dynamicforms_fastapi_viewsets-0.13.0 → dynamicforms_fastapi_viewsets-0.13.1}/pyproject.toml
RENAMED
|
File without changes
|