schon/evibes/utils/renderers.py
Egor fureunoir Gorbunov 13e7af52aa Features: 1) Improved request processing in middleware by adding mutable QueryDict implementation; 2) Extended type annotations across various modules for enhanced type safety; 3) Refined JWT token lifetime configuration for environment-specific logic.
Fixes: 1) Addressed missing or incorrect imports and type hints with `# ty:ignore` markers; 2) Fixed search queryset error handling in filters module; 3) Resolved issues in viewsets with updated `@action` method usage.

Extra: Removed unused classes and dependencies (e.g., `BaseMutation`, `basedpyright`, and related packages); streamlined GraphQL mutation implementations; cleaned up unused arguments in model `save` methods.
2025-12-19 15:17:17 +03:00

118 lines
3.8 KiB
Python

import re
from typing import Any, Collection, MutableMapping
from django.utils.module_loading import import_string
from drf_orjson_renderer.renderers import ORJSONRenderer
from evibes.settings.base import MIDDLEWARE
from evibes.settings.drf import JSON_UNDERSCOREIZE
camelize_re = re.compile(r"[a-z0-9]?_[a-z0-9]")
def underscore_to_camel(match):
group = match.group()
if len(group) == 3:
return group[0] + group[2].upper()
else:
return group[1].upper()
def _camelize_key(key: str) -> str:
if not isinstance(key, str) or not key:
return key
if "_" not in key:
return key
parts = key.split("_")
first = parts[0]
rest = [p.capitalize() if p else "" for p in parts[1:]]
return first + "".join(rest)
def camelize(obj: Any) -> Any:
if isinstance(obj, dict):
return {
(_camelize_key(k) if isinstance(k, str) else k): camelize(v)
for k, v in obj.items()
}
if isinstance(obj, list):
return [camelize(v) for v in obj]
if isinstance(obj, tuple):
return tuple(camelize(v) for v in obj)
return obj
def camelize_serializer_fields(result, generator, request, public):
ignore_fields: Collection[Any] = JSON_UNDERSCOREIZE.get("ignore_fields", ())
ignore_keys: Collection[Any] = JSON_UNDERSCOREIZE.get("ignore_keys", ())
def has_middleware_installed():
try:
from evibes.middleware import CamelCaseMiddleWare
except ImportError:
return False
return any(
isinstance(m, type) and issubclass(m, CamelCaseMiddleWare)
for m in map(import_string, MIDDLEWARE)
)
def camelize_str(key: str) -> str:
new_key = re.sub(camelize_re, underscore_to_camel, key) if "_" in key else key
if key in ignore_keys or new_key in ignore_keys:
return key
return new_key
def camelize_component(
schema: MutableMapping, name: str | None = None
) -> MutableMapping:
if name is not None and (
name in ignore_fields or camelize_str(name) in ignore_fields
):
return schema
elif schema.get("type") == "object":
if "properties" in schema:
schema["properties"] = {
camelize_str(field_name): camelize_component(
field_schema, field_name
)
for field_name, field_schema in schema["properties"].items()
}
if "required" in schema:
schema["required"] = [
camelize_str(field) for field in schema["required"]
]
elif schema.get("type") == "array" and isinstance(
schema["items"], MutableMapping
):
camelize_component(schema["items"])
return schema
for (_, component_type), component in generator.registry._components.items():
if component_type == "schemas":
camelize_component(component.schema)
if has_middleware_installed():
for url_schema in result["paths"].values():
for method_schema in url_schema.values():
for parameter in method_schema.get("parameters", []):
parameter["name"] = camelize_str(parameter["name"])
return result
class CamelCaseRenderer(ORJSONRenderer):
def render(
self, data: Any, media_type: str | None = None, renderer_context: Any = None
) -> bytes: # ty:ignore[invalid-method-override]
if data is None:
return b""
ctx = renderer_context or {}
camelize_disabled = ctx.get("camelize", True) is False
payload = data if camelize_disabled else camelize(data)
return super().render(payload, media_type=media_type, renderer_context=ctx)
__all__ = ["CamelCaseRenderer", "camelize"]