django-cfg 1.4.9__py3-none-any.whl → 1.4.11__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 (193) hide show
  1. django_cfg/apps/agents/management/commands/create_agent.py +1 -1
  2. django_cfg/apps/agents/management/commands/orchestrator_status.py +3 -3
  3. django_cfg/apps/newsletter/serializers.py +40 -3
  4. django_cfg/apps/newsletter/views/campaigns.py +12 -3
  5. django_cfg/apps/newsletter/views/emails.py +14 -3
  6. django_cfg/apps/newsletter/views/subscriptions.py +12 -2
  7. django_cfg/apps/payments/middleware/api_access.py +6 -2
  8. django_cfg/apps/payments/middleware/rate_limiting.py +2 -1
  9. django_cfg/apps/payments/middleware/usage_tracking.py +5 -1
  10. django_cfg/apps/payments/models/managers/api_key_managers.py +0 -1
  11. django_cfg/apps/payments/models/managers/subscription_managers.py +0 -1
  12. django_cfg/apps/payments/services/core/balance_service.py +5 -5
  13. django_cfg/apps/payments/services/core/subscription_service.py +1 -2
  14. django_cfg/apps/payments/views/api/balances.py +8 -7
  15. django_cfg/apps/payments/views/api/base.py +10 -6
  16. django_cfg/apps/payments/views/api/currencies.py +53 -10
  17. django_cfg/apps/payments/views/api/payments.py +3 -1
  18. django_cfg/apps/payments/views/api/subscriptions.py +2 -5
  19. django_cfg/apps/payments/views/api/webhooks.py +72 -7
  20. django_cfg/apps/payments/views/overview/serializers.py +34 -1
  21. django_cfg/apps/payments/views/overview/views.py +2 -1
  22. django_cfg/apps/payments/views/serializers/payments.py +6 -6
  23. django_cfg/apps/urls.py +106 -45
  24. django_cfg/core/base/config_model.py +2 -2
  25. django_cfg/core/constants.py +1 -1
  26. django_cfg/core/generation/integration_generators/__init__.py +1 -1
  27. django_cfg/core/generation/integration_generators/api.py +82 -41
  28. django_cfg/core/integration/display/startup.py +30 -22
  29. django_cfg/core/integration/url_integration.py +15 -16
  30. django_cfg/dashboard/sections/documentation.py +391 -0
  31. django_cfg/management/commands/check_endpoints.py +11 -160
  32. django_cfg/management/commands/check_settings.py +13 -265
  33. django_cfg/management/commands/clear_constance.py +13 -201
  34. django_cfg/management/commands/create_token.py +13 -321
  35. django_cfg/management/commands/generate_clients.py +23 -0
  36. django_cfg/management/commands/list_urls.py +13 -306
  37. django_cfg/management/commands/migrate_all.py +13 -126
  38. django_cfg/management/commands/migrator.py +13 -396
  39. django_cfg/management/commands/rundramatiq.py +15 -247
  40. django_cfg/management/commands/rundramatiq_simulator.py +12 -429
  41. django_cfg/management/commands/runserver_ngrok.py +15 -160
  42. django_cfg/management/commands/script.py +12 -488
  43. django_cfg/management/commands/show_config.py +12 -215
  44. django_cfg/management/commands/show_urls.py +12 -342
  45. django_cfg/management/commands/superuser.py +15 -295
  46. django_cfg/management/commands/task_clear.py +14 -217
  47. django_cfg/management/commands/task_status.py +13 -248
  48. django_cfg/management/commands/test_email.py +15 -86
  49. django_cfg/management/commands/test_telegram.py +14 -61
  50. django_cfg/management/commands/test_twilio.py +15 -105
  51. django_cfg/management/commands/tree.py +13 -383
  52. django_cfg/management/commands/validate_openapi.py +10 -0
  53. django_cfg/middleware/README.md +1 -1
  54. django_cfg/middleware/user_activity.py +3 -3
  55. django_cfg/models/__init__.py +2 -2
  56. django_cfg/models/api/drf/spectacular.py +6 -6
  57. django_cfg/models/django/__init__.py +2 -2
  58. django_cfg/models/django/openapi.py +238 -0
  59. django_cfg/models/django/{revolution.py → revolution_legacy.py} +8 -0
  60. django_cfg/modules/django_admin/management/__init__.py +0 -0
  61. django_cfg/modules/django_admin/management/commands/__init__.py +0 -0
  62. django_cfg/modules/django_admin/management/commands/check_endpoints.py +169 -0
  63. django_cfg/modules/django_admin/management/commands/check_settings.py +355 -0
  64. django_cfg/modules/django_admin/management/commands/clear_constance.py +208 -0
  65. django_cfg/modules/django_admin/management/commands/create_token.py +328 -0
  66. django_cfg/modules/django_admin/management/commands/list_urls.py +313 -0
  67. django_cfg/modules/django_admin/management/commands/migrate_all.py +133 -0
  68. django_cfg/modules/django_admin/management/commands/migrator.py +403 -0
  69. django_cfg/modules/django_admin/management/commands/script.py +496 -0
  70. django_cfg/modules/django_admin/management/commands/show_config.py +225 -0
  71. django_cfg/modules/django_admin/management/commands/show_urls.py +361 -0
  72. django_cfg/modules/django_admin/management/commands/superuser.py +302 -0
  73. django_cfg/modules/django_admin/management/commands/tree.py +390 -0
  74. django_cfg/modules/django_client/__init__.py +20 -0
  75. django_cfg/modules/django_client/apps.py +35 -0
  76. django_cfg/modules/django_client/core/__init__.py +56 -0
  77. django_cfg/modules/django_client/core/archive/__init__.py +11 -0
  78. django_cfg/modules/django_client/core/archive/manager.py +134 -0
  79. django_cfg/modules/django_client/core/cli/__init__.py +12 -0
  80. django_cfg/modules/django_client/core/cli/main.py +235 -0
  81. django_cfg/modules/django_client/core/config/__init__.py +18 -0
  82. django_cfg/modules/django_client/core/config/config.py +188 -0
  83. django_cfg/modules/django_client/core/config/group.py +101 -0
  84. django_cfg/modules/django_client/core/config/service.py +209 -0
  85. django_cfg/modules/django_client/core/generator/__init__.py +115 -0
  86. django_cfg/modules/django_client/core/generator/base.py +767 -0
  87. django_cfg/modules/django_client/core/generator/python.py +751 -0
  88. django_cfg/modules/django_client/core/generator/templates/python/__init__.py.jinja +9 -0
  89. django_cfg/modules/django_client/core/generator/templates/python/api_wrapper.py.jinja +130 -0
  90. django_cfg/modules/django_client/core/generator/templates/python/app_init.py.jinja +6 -0
  91. django_cfg/modules/django_client/core/generator/templates/python/client/app_client.py.jinja +18 -0
  92. django_cfg/modules/django_client/core/generator/templates/python/client/flat_client.py.jinja +38 -0
  93. django_cfg/modules/django_client/core/generator/templates/python/client/main_client.py.jinja +50 -0
  94. django_cfg/modules/django_client/core/generator/templates/python/client/main_client_file.py.jinja +13 -0
  95. django_cfg/modules/django_client/core/generator/templates/python/client/operation_method.py.jinja +7 -0
  96. django_cfg/modules/django_client/core/generator/templates/python/client/sub_client.py.jinja +11 -0
  97. django_cfg/modules/django_client/core/generator/templates/python/client_file.py.jinja +13 -0
  98. django_cfg/modules/django_client/core/generator/templates/python/main_init.py.jinja +50 -0
  99. django_cfg/modules/django_client/core/generator/templates/python/models/app_models.py.jinja +17 -0
  100. django_cfg/modules/django_client/core/generator/templates/python/models/enum_class.py.jinja +15 -0
  101. django_cfg/modules/django_client/core/generator/templates/python/models/enums.py.jinja +8 -0
  102. django_cfg/modules/django_client/core/generator/templates/python/models/models.py.jinja +17 -0
  103. django_cfg/modules/django_client/core/generator/templates/python/models/schema_class.py.jinja +19 -0
  104. django_cfg/modules/django_client/core/generator/templates/python/utils/logger.py.jinja +255 -0
  105. django_cfg/modules/django_client/core/generator/templates/python/utils/schema.py.jinja +12 -0
  106. django_cfg/modules/django_client/core/generator/templates/typescript/app_index.ts.jinja +2 -0
  107. django_cfg/modules/django_client/core/generator/templates/typescript/client/app_client.ts.jinja +18 -0
  108. django_cfg/modules/django_client/core/generator/templates/typescript/client/client.ts.jinja +327 -0
  109. django_cfg/modules/django_client/core/generator/templates/typescript/client/flat_client.ts.jinja +109 -0
  110. django_cfg/modules/django_client/core/generator/templates/typescript/client/main_client_file.ts.jinja +9 -0
  111. django_cfg/modules/django_client/core/generator/templates/typescript/client/operation.ts.jinja +61 -0
  112. django_cfg/modules/django_client/core/generator/templates/typescript/client/sub_client.ts.jinja +15 -0
  113. django_cfg/modules/django_client/core/generator/templates/typescript/client_file.ts.jinja +9 -0
  114. django_cfg/modules/django_client/core/generator/templates/typescript/index.ts.jinja +5 -0
  115. django_cfg/modules/django_client/core/generator/templates/typescript/main_index.ts.jinja +206 -0
  116. django_cfg/modules/django_client/core/generator/templates/typescript/models/app_models.ts.jinja +8 -0
  117. django_cfg/modules/django_client/core/generator/templates/typescript/models/enums.ts.jinja +4 -0
  118. django_cfg/modules/django_client/core/generator/templates/typescript/models/models.ts.jinja +8 -0
  119. django_cfg/modules/django_client/core/generator/templates/typescript/utils/errors.ts.jinja +114 -0
  120. django_cfg/modules/django_client/core/generator/templates/typescript/utils/http.ts.jinja +98 -0
  121. django_cfg/modules/django_client/core/generator/templates/typescript/utils/logger.ts.jinja +251 -0
  122. django_cfg/modules/django_client/core/generator/templates/typescript/utils/schema.ts.jinja +7 -0
  123. django_cfg/modules/django_client/core/generator/templates/typescript/utils/storage.ts.jinja +114 -0
  124. django_cfg/modules/django_client/core/generator/typescript.py +872 -0
  125. django_cfg/modules/django_client/core/groups/__init__.py +13 -0
  126. django_cfg/modules/django_client/core/groups/detector.py +178 -0
  127. django_cfg/modules/django_client/core/groups/manager.py +314 -0
  128. django_cfg/modules/django_client/core/ir/__init__.py +57 -0
  129. django_cfg/modules/django_client/core/ir/context.py +387 -0
  130. django_cfg/modules/django_client/core/ir/operation.py +518 -0
  131. django_cfg/modules/django_client/core/ir/schema.py +353 -0
  132. django_cfg/modules/django_client/core/parser/__init__.py +74 -0
  133. django_cfg/modules/django_client/core/parser/base.py +648 -0
  134. django_cfg/modules/django_client/core/parser/models/__init__.py +74 -0
  135. django_cfg/modules/django_client/core/parser/models/base.py +212 -0
  136. django_cfg/modules/django_client/core/parser/models/components.py +160 -0
  137. django_cfg/modules/django_client/core/parser/models/openapi.py +203 -0
  138. django_cfg/modules/django_client/core/parser/models/operation.py +207 -0
  139. django_cfg/modules/django_client/core/parser/models/schema.py +266 -0
  140. django_cfg/modules/django_client/core/parser/openapi30.py +56 -0
  141. django_cfg/modules/django_client/core/parser/openapi31.py +64 -0
  142. django_cfg/modules/django_client/core/validation/__init__.py +22 -0
  143. django_cfg/modules/django_client/core/validation/checker.py +134 -0
  144. django_cfg/modules/django_client/core/validation/fixer.py +216 -0
  145. django_cfg/modules/django_client/core/validation/reporter.py +480 -0
  146. django_cfg/modules/django_client/core/validation/rules/__init__.py +11 -0
  147. django_cfg/modules/django_client/core/validation/rules/base.py +96 -0
  148. django_cfg/modules/django_client/core/validation/rules/type_hints.py +288 -0
  149. django_cfg/modules/django_client/core/validation/safety.py +266 -0
  150. django_cfg/modules/django_client/management/__init__.py +3 -0
  151. django_cfg/modules/django_client/management/commands/__init__.py +3 -0
  152. django_cfg/modules/django_client/management/commands/generate_client.py +422 -0
  153. django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
  154. django_cfg/modules/django_client/spectacular/__init__.py +9 -0
  155. django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
  156. django_cfg/modules/django_client/urls.py +72 -0
  157. django_cfg/modules/django_email/management/__init__.py +0 -0
  158. django_cfg/modules/django_email/management/commands/__init__.py +0 -0
  159. django_cfg/modules/django_email/management/commands/test_email.py +93 -0
  160. django_cfg/modules/django_logging/django_logger.py +6 -6
  161. django_cfg/modules/django_ngrok/management/__init__.py +0 -0
  162. django_cfg/modules/django_ngrok/management/commands/__init__.py +0 -0
  163. django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +167 -0
  164. django_cfg/modules/django_tasks/management/__init__.py +0 -0
  165. django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
  166. django_cfg/modules/django_tasks/management/commands/rundramatiq.py +254 -0
  167. django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +437 -0
  168. django_cfg/modules/django_tasks/management/commands/task_clear.py +226 -0
  169. django_cfg/modules/django_tasks/management/commands/task_status.py +257 -0
  170. django_cfg/modules/django_telegram/management/__init__.py +0 -0
  171. django_cfg/modules/django_telegram/management/commands/__init__.py +0 -0
  172. django_cfg/modules/django_telegram/management/commands/test_telegram.py +68 -0
  173. django_cfg/modules/django_twilio/management/__init__.py +0 -0
  174. django_cfg/modules/django_twilio/management/commands/__init__.py +0 -0
  175. django_cfg/modules/django_twilio/management/commands/test_twilio.py +112 -0
  176. django_cfg/modules/django_unfold/callbacks/main.py +16 -5
  177. django_cfg/modules/django_unfold/callbacks/revolution.py +41 -36
  178. django_cfg/modules/django_unfold/dashboard.py +1 -1
  179. django_cfg/pyproject.toml +2 -6
  180. django_cfg/registry/third_party.py +5 -7
  181. django_cfg/routing/callbacks.py +1 -1
  182. django_cfg/static/admin/css/prose-unfold.css +666 -0
  183. django_cfg/templates/admin/index.html +8 -0
  184. django_cfg/templates/admin/index_new.html +13 -0
  185. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +15 -3
  186. django_cfg/templates/admin/sections/documentation_section.html +172 -0
  187. django_cfg/templates/admin/snippets/tabs/documentation_tab.html +231 -0
  188. {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/METADATA +2 -2
  189. {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/RECORD +192 -71
  190. django_cfg/management/commands/generate.py +0 -107
  191. {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/WHEEL +0 -0
  192. {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/entry_points.txt +0 -0
  193. {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,9 @@
1
+ from .client import APIClient
2
+ from .models import *
3
+ {% if has_enums %}
4
+ from .enums import *
5
+ {% endif %}
6
+
7
+ __all__ = [
8
+ "APIClient",
9
+ ]
@@ -0,0 +1,130 @@
1
+ class API:
2
+ """
3
+ API Client wrapper with JWT token management.
4
+
5
+ This class provides:
6
+ - Thread-safe JWT token storage
7
+ - Automatic Authorization header injection
8
+ - Context manager support for async operations
9
+
10
+ Example:
11
+ >>> api = API('https://api.example.com')
12
+ >>> api.set_token('jwt-token')
13
+ >>> async with api:
14
+ ... users = await api.users.list()
15
+ """
16
+
17
+ def __init__(self, base_url: str, **kwargs: Any):
18
+ """
19
+ Initialize API client.
20
+
21
+ Args:
22
+ base_url: Base API URL (e.g., 'https://api.example.com')
23
+ **kwargs: Additional httpx.AsyncClient kwargs
24
+ """
25
+ self.base_url = base_url.rstrip('/')
26
+ self._kwargs = kwargs
27
+ self._token: str | None = None
28
+ self._refresh_token: str | None = None
29
+ self._lock = threading.Lock()
30
+ self._client: APIClient | None = None
31
+ self._init_clients()
32
+
33
+ def _init_clients(self) -> None:
34
+ """Initialize API client with current token."""
35
+ # Create httpx client with auth header if token exists
36
+ headers = {}
37
+ if self._token:
38
+ headers['Authorization'] = f'Bearer {self._token}'
39
+
40
+ kwargs = {**self._kwargs}
41
+ if headers:
42
+ kwargs['headers'] = headers
43
+
44
+ # Create new APIClient
45
+ self._client = APIClient(self.base_url, **kwargs)
46
+
47
+ {% for prop in properties %}
48
+ @property
49
+ def {{ prop.property }}(self) -> {{ prop.class_name }}:
50
+ """Access {{ prop.tag }} endpoints."""
51
+ return self._client.{{ prop.property }}
52
+
53
+ {% endfor %}
54
+ def get_token(self) -> str | None:
55
+ """Get current JWT token."""
56
+ with self._lock:
57
+ return self._token
58
+
59
+ def get_refresh_token(self) -> str | None:
60
+ """Get current refresh token."""
61
+ with self._lock:
62
+ return self._refresh_token
63
+
64
+ def set_token(self, token: str, refresh_token: str | None = None) -> None:
65
+ """
66
+ Set JWT token and refresh token.
67
+
68
+ Args:
69
+ token: JWT access token
70
+ refresh_token: JWT refresh token (optional)
71
+ """
72
+ with self._lock:
73
+ self._token = token
74
+ if refresh_token:
75
+ self._refresh_token = refresh_token
76
+
77
+ # Reinitialize clients with new token
78
+ self._init_clients()
79
+
80
+ def clear_tokens(self) -> None:
81
+ """Clear all tokens."""
82
+ with self._lock:
83
+ self._token = None
84
+ self._refresh_token = None
85
+
86
+ # Reinitialize clients without token
87
+ self._init_clients()
88
+
89
+ def is_authenticated(self) -> bool:
90
+ """Check if user is authenticated."""
91
+ return self.get_token() is not None
92
+
93
+ def set_base_url(self, url: str) -> None:
94
+ """
95
+ Update base URL and reinitialize clients.
96
+
97
+ Args:
98
+ url: New base URL
99
+ """
100
+ self.base_url = url.rstrip('/')
101
+ self._init_clients()
102
+
103
+ def get_base_url(self) -> str:
104
+ """Get current base URL."""
105
+ return self.base_url
106
+
107
+ def get_schema(self) -> dict[str, Any]:
108
+ """
109
+ Get OpenAPI schema.
110
+
111
+ Returns:
112
+ Complete OpenAPI specification for this API
113
+ """
114
+ return OPENAPI_SCHEMA
115
+
116
+ async def __aenter__(self) -> 'API':
117
+ """Async context manager entry."""
118
+ if self._client:
119
+ await self._client.__aenter__()
120
+ return self
121
+
122
+ async def __aexit__(self, *args: Any) -> None:
123
+ """Async context manager exit."""
124
+ if self._client:
125
+ await self._client.__aexit__(*args)
126
+
127
+ async def close(self) -> None:
128
+ """Close HTTP client."""
129
+ if self._client:
130
+ await self._client.close()
@@ -0,0 +1,6 @@
1
+ from .client import {{ class_name }}
2
+ from .models import *
3
+
4
+ __all__ = [
5
+ "{{ class_name }}",
6
+ ]
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ import httpx
4
+
5
+ from .models import *
6
+
7
+
8
+ class {{ class_name }}:
9
+ """API endpoints for {{ tag }}."""
10
+
11
+ def __init__(self, client: httpx.AsyncClient):
12
+ """Initialize sub-client with shared httpx client."""
13
+ self._client = client
14
+
15
+ {% for operation in operations %}
16
+ {{ operation | indent(4, first=True) }}
17
+
18
+ {% endfor %}
@@ -0,0 +1,38 @@
1
+ class APIClient:
2
+ """
3
+ Async API client for {{ api_title }}.
4
+
5
+ Usage:
6
+ >>> async with APIClient(base_url='https://api.example.com') as client:
7
+ ... users = await client.users_list()
8
+ """
9
+
10
+ def __init__(self, base_url: str, **kwargs: Any):
11
+ """
12
+ Initialize API client.
13
+
14
+ Args:
15
+ base_url: Base API URL (e.g., 'https://api.example.com')
16
+ **kwargs: Additional httpx.AsyncClient kwargs
17
+ """
18
+ self.base_url = base_url.rstrip('/')
19
+ self._client = httpx.AsyncClient(
20
+ base_url=self.base_url,
21
+ timeout=30.0,
22
+ **kwargs,
23
+ )
24
+
25
+ async def __aenter__(self) -> 'APIClient':
26
+ return self
27
+
28
+ async def __aexit__(self, *args: Any) -> None:
29
+ await self._client.aclose()
30
+
31
+ async def close(self) -> None:
32
+ """Close HTTP client."""
33
+ await self._client.aclose()
34
+
35
+ {% for operation in operations %}
36
+ {{ operation }}
37
+
38
+ {% endfor %}
@@ -0,0 +1,50 @@
1
+ class APIClient:
2
+ """
3
+ Async API client for {{ api_title }}.
4
+
5
+ Usage:
6
+ >>> async with APIClient(base_url='https://api.example.com') as client:
7
+ ... users = await client.users.list()
8
+ ... post = await client.posts.create(data=new_post)
9
+ """
10
+
11
+ def __init__(
12
+ self,
13
+ base_url: str,
14
+ logger_config: Optional[LoggerConfig] = None,
15
+ **kwargs: Any,
16
+ ):
17
+ """
18
+ Initialize API client.
19
+
20
+ Args:
21
+ base_url: Base API URL (e.g., 'https://api.example.com')
22
+ logger_config: Logger configuration (None to disable logging)
23
+ **kwargs: Additional httpx.AsyncClient kwargs
24
+ """
25
+ self.base_url = base_url.rstrip('/')
26
+ self._client = httpx.AsyncClient(
27
+ base_url=self.base_url,
28
+ timeout=30.0,
29
+ **kwargs,
30
+ )
31
+
32
+ # Initialize logger
33
+ self.logger: Optional[APILogger] = None
34
+ if logger_config is not None:
35
+ self.logger = APILogger(logger_config)
36
+
37
+ # Initialize sub-clients
38
+ {% for tag in tags %}
39
+ self.{{ tag.property }} = {{ tag.class_name }}(self._client)
40
+ {% endfor %}
41
+
42
+ async def __aenter__(self) -> 'APIClient':
43
+ return self
44
+
45
+ async def __aexit__(self, *args: Any) -> None:
46
+ await self._client.aclose()
47
+
48
+ async def close(self) -> None:
49
+ """Close HTTP client."""
50
+ await self._client.aclose()
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional
4
+
5
+ import httpx
6
+
7
+ {% for tag in tags %}
8
+ from .{{ tag.slug }} import {{ tag.class_name }}
9
+ {% endfor %}
10
+ from .logger import APILogger, LoggerConfig
11
+
12
+
13
+ {{ client_code }}
@@ -0,0 +1,7 @@
1
+ async def {{ method_name }}({{ params | join(', ') }}) -> {{ return_type }}:
2
+ {% if docstring %}
3
+ """{{ docstring }}"""
4
+ {% endif %}
5
+ {% for line in body_lines %}
6
+ {{ line }}
7
+ {% endfor %}
@@ -0,0 +1,11 @@
1
+ class {{ class_name }}:
2
+ """API endpoints for {{ tag }}."""
3
+
4
+ def __init__(self, client: httpx.AsyncClient):
5
+ """Initialize sub-client with shared httpx client."""
6
+ self._client = client
7
+
8
+ {% for operation in operations %}
9
+ {{ operation }}
10
+
11
+ {% endfor %}
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import httpx
6
+
7
+ from .models import *
8
+ {% if has_enums %}
9
+ from .enums import *
10
+ {% endif %}
11
+
12
+
13
+ {{ client_code }}
@@ -0,0 +1,50 @@
1
+ """
2
+ {{ api_title }} - API Client with JWT Management
3
+
4
+ Usage:
5
+ >>> from api import API
6
+ >>>
7
+ >>> api = API('https://api.example.com')
8
+ >>>
9
+ >>> # Set JWT token
10
+ >>> api.set_token('your-jwt-token', 'refresh-token')
11
+ >>>
12
+ >>> # Use API
13
+ >>> async with api:
14
+ ... posts = await api.posts.list()
15
+ ... user = await api.users.retrieve(1)
16
+ >>>
17
+ >>> # Check authentication
18
+ >>> if api.is_authenticated():
19
+ ... # ...
20
+ >>>
21
+ >>> # Get OpenAPI schema
22
+ >>> schema = api.get_schema()
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import threading
28
+ from typing import Any
29
+
30
+ import httpx
31
+
32
+ from .client import APIClient
33
+ from .schema import OPENAPI_SCHEMA
34
+ {% for tag in tags %}
35
+ from .{{ tag.slug }} import {{ tag.class_name }}
36
+ {% endfor %}
37
+ {% if has_enums %}
38
+ from . import enums
39
+ from .enums import {{ enum_names | join(', ') }}
40
+ {% endif %}
41
+
42
+ TOKEN_KEY = "auth_token"
43
+ REFRESH_TOKEN_KEY = "refresh_token"
44
+
45
+ {{ api_class }}
46
+
47
+ __all__ = [
48
+ "API",
49
+ "APIClient",
50
+ ]
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+ {% if has_enums and enum_names %}
8
+
9
+ from ..enums import {{ enum_names | join(', ') }}
10
+ {% endif %}
11
+
12
+
13
+ {% for schema in schemas %}
14
+ {{ schema }}
15
+
16
+
17
+ {% endfor %}
@@ -0,0 +1,15 @@
1
+ class {{ name }}({{ base_class }}):
2
+ {% if docstring %}
3
+ """{{ docstring }}"""
4
+ {% endif %}
5
+ {% if members %}
6
+ {% if docstring %}
7
+
8
+ {% endif %}
9
+ {% for member in members %}
10
+ {{ member }}
11
+ {% endfor %}
12
+ {% else %}
13
+
14
+ pass
15
+ {% endif %}
@@ -0,0 +1,8 @@
1
+ from enum import IntEnum, StrEnum
2
+
3
+
4
+ {% for enum in enums %}
5
+ {{ enum }}
6
+
7
+
8
+ {% endfor %}
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+ {% if has_enums %}
8
+
9
+ from .enums import *
10
+ {% endif %}
11
+
12
+
13
+ {% for schema in schemas %}
14
+ {{ schema }}
15
+
16
+
17
+ {% endfor %}
@@ -0,0 +1,19 @@
1
+ class {{ name }}(BaseModel):
2
+ {% if docstring %}
3
+ """{{ docstring }}"""
4
+ {% endif %}
5
+
6
+ model_config = ConfigDict(
7
+ validate_assignment=True,
8
+ extra="forbid",
9
+ frozen=False,
10
+ )
11
+ {% if fields %}
12
+
13
+ {% for field in fields %}
14
+ {{ field }}
15
+ {% endfor %}
16
+ {% else %}
17
+
18
+ pass
19
+ {% endif %}
@@ -0,0 +1,255 @@
1
+ """
2
+ API Logger with Rich
3
+ Beautiful console logging for API requests and responses
4
+
5
+ Installation:
6
+ pip install rich
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import time
12
+ from dataclasses import dataclass, field
13
+ from typing import Any, Dict, Optional
14
+
15
+ from rich.console import Console
16
+ from rich.panel import Panel
17
+ from rich.table import Table
18
+ from rich.text import Text
19
+
20
+
21
+ @dataclass
22
+ class RequestLog:
23
+ """Request log data."""
24
+
25
+ method: str
26
+ url: str
27
+ headers: Optional[Dict[str, str]] = None
28
+ body: Optional[Any] = None
29
+ timestamp: float = field(default_factory=time.time)
30
+
31
+
32
+ @dataclass
33
+ class ResponseLog:
34
+ """Response log data."""
35
+
36
+ status: int
37
+ status_text: str
38
+ data: Optional[Any] = None
39
+ duration: float = 0.0
40
+ timestamp: float = field(default_factory=time.time)
41
+
42
+
43
+ @dataclass
44
+ class ErrorLog:
45
+ """Error log data."""
46
+
47
+ message: str
48
+ status_code: Optional[int] = None
49
+ field_errors: Optional[Dict[str, list[str]]] = None
50
+ duration: float = 0.0
51
+ timestamp: float = field(default_factory=time.time)
52
+
53
+
54
+ @dataclass
55
+ class LoggerConfig:
56
+ """Logger configuration."""
57
+
58
+ enabled: bool = True
59
+ log_requests: bool = True
60
+ log_responses: bool = True
61
+ log_errors: bool = True
62
+ log_bodies: bool = True
63
+ log_headers: bool = False
64
+ console: Optional[Console] = None
65
+
66
+
67
+ # Sensitive header names to filter out
68
+ SENSITIVE_HEADERS = [
69
+ "authorization",
70
+ "cookie",
71
+ "set-cookie",
72
+ "x-api-key",
73
+ "x-csrf-token",
74
+ ]
75
+
76
+
77
+ class APILogger:
78
+ """API Logger class."""
79
+
80
+ def __init__(self, config: Optional[LoggerConfig] = None):
81
+ """Initialize logger."""
82
+ self.config = config or LoggerConfig()
83
+ self.console = self.config.console or Console()
84
+
85
+ def enable(self) -> None:
86
+ """Enable logging."""
87
+ self.config.enabled = True
88
+
89
+ def disable(self) -> None:
90
+ """Disable logging."""
91
+ self.config.enabled = False
92
+
93
+ def set_config(self, **kwargs: Any) -> None:
94
+ """Update configuration."""
95
+ for key, value in kwargs.items():
96
+ if hasattr(self.config, key):
97
+ setattr(self.config, key, value)
98
+
99
+ def _filter_headers(self, headers: Optional[Dict[str, str]]) -> Dict[str, str]:
100
+ """Filter sensitive headers."""
101
+ if not headers:
102
+ return {}
103
+
104
+ filtered = {}
105
+ for key, value in headers.items():
106
+ if key.lower() in SENSITIVE_HEADERS:
107
+ filtered[key] = "***"
108
+ else:
109
+ filtered[key] = value
110
+
111
+ return filtered
112
+
113
+ def log_request(self, request: RequestLog) -> None:
114
+ """Log request."""
115
+ if not self.config.enabled or not self.config.log_requests:
116
+ return
117
+
118
+ # Create request info
119
+ text = Text()
120
+ text.append("→ ", style="bold blue")
121
+ text.append(request.method, style="bold yellow")
122
+ text.append(" ", style="")
123
+ text.append(request.url, style="cyan")
124
+
125
+ self.console.print(text)
126
+
127
+ if self.config.log_headers and request.headers:
128
+ headers = self._filter_headers(request.headers)
129
+ self.console.print(" Headers:", style="dim")
130
+ for key, value in headers.items():
131
+ self.console.print(f" {key}: {value}", style="dim")
132
+
133
+ if self.config.log_bodies and request.body:
134
+ self.console.print(" Body:", style="dim")
135
+ self.console.print(request.body, style="dim")
136
+
137
+ def log_response(self, request: RequestLog, response: ResponseLog) -> None:
138
+ """Log response."""
139
+ if not self.config.enabled or not self.config.log_responses:
140
+ return
141
+
142
+ # Determine color based on status
143
+ if response.status >= 500:
144
+ status_style = "bold red"
145
+ elif response.status >= 400:
146
+ status_style = "bold yellow"
147
+ elif response.status >= 300:
148
+ status_style = "bold cyan"
149
+ else:
150
+ status_style = "bold green"
151
+
152
+ # Create response info
153
+ text = Text()
154
+ text.append("← ", style="bold green")
155
+ text.append(request.method, style="bold yellow")
156
+ text.append(" ", style="")
157
+ text.append(request.url, style="cyan")
158
+ text.append(" ", style="")
159
+ text.append(str(response.status), style=status_style)
160
+ text.append(" ", style="")
161
+ text.append(response.status_text, style=status_style)
162
+ text.append(f" ({response.duration:.0f}ms)", style="dim")
163
+
164
+ self.console.print(text)
165
+
166
+ if self.config.log_bodies and response.data:
167
+ self.console.print(" Response:", style="dim")
168
+ self.console.print(response.data, style="dim")
169
+
170
+ def log_error(self, request: RequestLog, error: ErrorLog) -> None:
171
+ """Log error."""
172
+ if not self.config.enabled or not self.config.log_errors:
173
+ return
174
+
175
+ # Create error header
176
+ text = Text()
177
+ text.append("✗ ", style="bold red")
178
+ text.append(request.method, style="bold yellow")
179
+ text.append(" ", style="")
180
+ text.append(request.url, style="cyan")
181
+ text.append(" ", style="")
182
+ text.append(
183
+ str(error.status_code) if error.status_code else "Network",
184
+ style="bold red",
185
+ )
186
+ text.append(" Error", style="bold red")
187
+ text.append(f" ({error.duration:.0f}ms)", style="dim")
188
+
189
+ self.console.print(text)
190
+ self.console.print(f" Message: {error.message}", style="red")
191
+
192
+ if error.field_errors:
193
+ self.console.print(" Field Errors:", style="red")
194
+ for field, errors in error.field_errors.items():
195
+ for err in errors:
196
+ self.console.print(f" • {field}: {err}", style="red dim")
197
+
198
+ def info(self, message: str, **kwargs: Any) -> None:
199
+ """Log info message."""
200
+ if not self.config.enabled:
201
+ return
202
+ self.console.print(f"ℹ {message}", style="blue", **kwargs)
203
+
204
+ def warn(self, message: str, **kwargs: Any) -> None:
205
+ """Log warning message."""
206
+ if not self.config.enabled:
207
+ return
208
+ self.console.print(f"⚠ {message}", style="yellow", **kwargs)
209
+
210
+ def error(self, message: str, **kwargs: Any) -> None:
211
+ """Log error message."""
212
+ if not self.config.enabled:
213
+ return
214
+ self.console.print(f"✗ {message}", style="red", **kwargs)
215
+
216
+ def success(self, message: str, **kwargs: Any) -> None:
217
+ """Log success message."""
218
+ if not self.config.enabled:
219
+ return
220
+ self.console.print(f"✓ {message}", style="green", **kwargs)
221
+
222
+ def debug(self, message: str, **kwargs: Any) -> None:
223
+ """Log debug message."""
224
+ if not self.config.enabled:
225
+ return
226
+ self.console.print(f"🔍 {message}", style="dim", **kwargs)
227
+
228
+ def panel(self, content: Any, title: str, style: str = "blue") -> None:
229
+ """Log content in a panel."""
230
+ if not self.config.enabled:
231
+ return
232
+ self.console.print(Panel(content, title=title, border_style=style))
233
+
234
+ def table(
235
+ self,
236
+ headers: list[str],
237
+ rows: list[list[Any]],
238
+ title: Optional[str] = None,
239
+ ) -> None:
240
+ """Log data in a table."""
241
+ if not self.config.enabled:
242
+ return
243
+
244
+ table = Table(title=title)
245
+ for header in headers:
246
+ table.add_column(header, style="cyan")
247
+
248
+ for row in rows:
249
+ table.add_row(*[str(cell) for cell in row])
250
+
251
+ self.console.print(table)
252
+
253
+
254
+ # Default logger instance
255
+ default_logger = APILogger()