django-cfg 1.4.71__py3-none-any.whl → 1.4.73__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.
Potentially problematic release.
This version of django-cfg might be problematic. Click here for more details.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/admin/inlines.py +1 -1
- django_cfg/apps/accounts/admin/user_admin.py +10 -0
- django_cfg/modules/django_client/core/__init__.py +2 -1
- django_cfg/modules/django_client/core/archive/manager.py +14 -0
- django_cfg/modules/django_client/core/generator/__init__.py +40 -2
- django_cfg/modules/django_client/core/generator/proto/__init__.py +17 -0
- django_cfg/modules/django_client/core/generator/proto/generator.py +461 -0
- django_cfg/modules/django_client/core/generator/proto/messages_generator.py +260 -0
- django_cfg/modules/django_client/core/generator/proto/services_generator.py +295 -0
- django_cfg/modules/django_client/core/generator/proto/test_proto_generator.py +262 -0
- django_cfg/modules/django_client/core/generator/proto/type_mapper.py +153 -0
- django_cfg/modules/django_client/management/commands/generate_client.py +49 -3
- django_cfg/pyproject.toml +1 -1
- {django_cfg-1.4.71.dist-info → django_cfg-1.4.73.dist-info}/METADATA +1 -1
- {django_cfg-1.4.71.dist-info → django_cfg-1.4.73.dist-info}/RECORD +19 -13
- {django_cfg-1.4.71.dist-info → django_cfg-1.4.73.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.71.dist-info → django_cfg-1.4.73.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.71.dist-info → django_cfg-1.4.73.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simple test for Proto Generator.
|
|
3
|
+
|
|
4
|
+
This script demonstrates basic proto generation functionality.
|
|
5
|
+
Run with: python -m django_cfg.modules.django_client.core.generator.proto.test_proto_generator
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from django_cfg.modules.django_client.core.ir import (
|
|
9
|
+
IRContext,
|
|
10
|
+
IROperationObject,
|
|
11
|
+
IRParameterObject,
|
|
12
|
+
IRRequestBodyObject,
|
|
13
|
+
IRResponseObject,
|
|
14
|
+
IRSchemaObject,
|
|
15
|
+
MediaTypeObject,
|
|
16
|
+
)
|
|
17
|
+
from django_cfg.modules.django_client.core.generator.proto import ProtoGenerator
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def create_test_context() -> IRContext:
|
|
21
|
+
"""Create a simple test IR context."""
|
|
22
|
+
|
|
23
|
+
# User schema
|
|
24
|
+
user_schema = IRSchemaObject(
|
|
25
|
+
name="User",
|
|
26
|
+
type="object",
|
|
27
|
+
properties={
|
|
28
|
+
"id": IRSchemaObject(
|
|
29
|
+
name="id",
|
|
30
|
+
type="integer",
|
|
31
|
+
format="int64",
|
|
32
|
+
nullable=False,
|
|
33
|
+
),
|
|
34
|
+
"username": IRSchemaObject(
|
|
35
|
+
name="username",
|
|
36
|
+
type="string",
|
|
37
|
+
nullable=False,
|
|
38
|
+
),
|
|
39
|
+
"email": IRSchemaObject(
|
|
40
|
+
name="email",
|
|
41
|
+
type="string",
|
|
42
|
+
format="email",
|
|
43
|
+
nullable=True,
|
|
44
|
+
),
|
|
45
|
+
"status": IRSchemaObject(
|
|
46
|
+
name="status",
|
|
47
|
+
type="string",
|
|
48
|
+
enum=["active", "inactive", "banned"],
|
|
49
|
+
nullable=False,
|
|
50
|
+
),
|
|
51
|
+
"created_at": IRSchemaObject(
|
|
52
|
+
name="created_at",
|
|
53
|
+
type="string",
|
|
54
|
+
format="date-time",
|
|
55
|
+
nullable=False,
|
|
56
|
+
),
|
|
57
|
+
},
|
|
58
|
+
required=["id", "username", "status", "created_at"],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# List users operation
|
|
62
|
+
list_users_op = IROperationObject(
|
|
63
|
+
operation_id="users_list",
|
|
64
|
+
method="GET",
|
|
65
|
+
path="/api/users/",
|
|
66
|
+
tags=["Users"],
|
|
67
|
+
description="List all users",
|
|
68
|
+
parameters=[
|
|
69
|
+
IRParameterObject(
|
|
70
|
+
name="page",
|
|
71
|
+
in_location="query",
|
|
72
|
+
required=False,
|
|
73
|
+
schema=IRSchemaObject(type="integer", format="int32"),
|
|
74
|
+
),
|
|
75
|
+
IRParameterObject(
|
|
76
|
+
name="page_size",
|
|
77
|
+
in_location="query",
|
|
78
|
+
required=False,
|
|
79
|
+
schema=IRSchemaObject(type="integer", format="int32"),
|
|
80
|
+
),
|
|
81
|
+
],
|
|
82
|
+
request_body=None,
|
|
83
|
+
responses={
|
|
84
|
+
200: IRResponseObject(
|
|
85
|
+
description="Successful response",
|
|
86
|
+
content={
|
|
87
|
+
"application/json": MediaTypeObject(
|
|
88
|
+
schema=IRSchemaObject(
|
|
89
|
+
type="array",
|
|
90
|
+
items=user_schema,
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Create user operation
|
|
99
|
+
create_user_op = IROperationObject(
|
|
100
|
+
operation_id="users_create",
|
|
101
|
+
method="POST",
|
|
102
|
+
path="/api/users/",
|
|
103
|
+
tags=["Users"],
|
|
104
|
+
description="Create a new user",
|
|
105
|
+
parameters=[],
|
|
106
|
+
request_body=IRRequestBodyObject(
|
|
107
|
+
required=True,
|
|
108
|
+
content={
|
|
109
|
+
"application/json": MediaTypeObject(
|
|
110
|
+
schema=IRSchemaObject(
|
|
111
|
+
name="UserRequest",
|
|
112
|
+
type="object",
|
|
113
|
+
properties={
|
|
114
|
+
"username": IRSchemaObject(
|
|
115
|
+
name="username",
|
|
116
|
+
type="string",
|
|
117
|
+
),
|
|
118
|
+
"email": IRSchemaObject(
|
|
119
|
+
name="email",
|
|
120
|
+
type="string",
|
|
121
|
+
format="email",
|
|
122
|
+
),
|
|
123
|
+
},
|
|
124
|
+
required=["username"],
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
},
|
|
128
|
+
),
|
|
129
|
+
responses={
|
|
130
|
+
201: IRResponseObject(
|
|
131
|
+
description="User created",
|
|
132
|
+
content={
|
|
133
|
+
"application/json": MediaTypeObject(schema=user_schema)
|
|
134
|
+
},
|
|
135
|
+
)
|
|
136
|
+
},
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Get user operation
|
|
140
|
+
get_user_op = IROperationObject(
|
|
141
|
+
operation_id="users_retrieve",
|
|
142
|
+
method="GET",
|
|
143
|
+
path="/api/users/{id}/",
|
|
144
|
+
tags=["Users"],
|
|
145
|
+
description="Get user by ID",
|
|
146
|
+
parameters=[
|
|
147
|
+
IRParameterObject(
|
|
148
|
+
name="id",
|
|
149
|
+
in_location="path",
|
|
150
|
+
required=True,
|
|
151
|
+
schema=IRSchemaObject(type="integer", format="int64"),
|
|
152
|
+
),
|
|
153
|
+
],
|
|
154
|
+
request_body=None,
|
|
155
|
+
responses={
|
|
156
|
+
200: IRResponseObject(
|
|
157
|
+
description="Successful response",
|
|
158
|
+
content={
|
|
159
|
+
"application/json": MediaTypeObject(schema=user_schema)
|
|
160
|
+
},
|
|
161
|
+
),
|
|
162
|
+
404: IRResponseObject(
|
|
163
|
+
description="User not found",
|
|
164
|
+
content={},
|
|
165
|
+
),
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Delete user operation (empty response)
|
|
170
|
+
delete_user_op = IROperationObject(
|
|
171
|
+
operation_id="users_delete",
|
|
172
|
+
method="DELETE",
|
|
173
|
+
path="/api/users/{id}/",
|
|
174
|
+
tags=["Users"],
|
|
175
|
+
description="Delete a user",
|
|
176
|
+
parameters=[
|
|
177
|
+
IRParameterObject(
|
|
178
|
+
name="id",
|
|
179
|
+
in_location="path",
|
|
180
|
+
required=True,
|
|
181
|
+
schema=IRSchemaObject(type="integer", format="int64"),
|
|
182
|
+
),
|
|
183
|
+
],
|
|
184
|
+
request_body=None,
|
|
185
|
+
responses={
|
|
186
|
+
204: IRResponseObject(
|
|
187
|
+
description="User deleted",
|
|
188
|
+
content={},
|
|
189
|
+
)
|
|
190
|
+
},
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Create IR context
|
|
194
|
+
context = IRContext(
|
|
195
|
+
schemas={"User": user_schema},
|
|
196
|
+
operations={
|
|
197
|
+
"users_list": list_users_op,
|
|
198
|
+
"users_create": create_user_op,
|
|
199
|
+
"users_retrieve": get_user_op,
|
|
200
|
+
"users_delete": delete_user_op,
|
|
201
|
+
},
|
|
202
|
+
security_schemes={},
|
|
203
|
+
request_models={},
|
|
204
|
+
response_models={"User": user_schema},
|
|
205
|
+
patch_models={},
|
|
206
|
+
enum_schemas={},
|
|
207
|
+
operations_by_tag={"Users": [list_users_op, create_user_op, get_user_op, delete_user_op]},
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return context
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def main():
|
|
214
|
+
"""Run basic proto generation test."""
|
|
215
|
+
print("🧪 Testing Proto Generator...\n")
|
|
216
|
+
|
|
217
|
+
# Create test context
|
|
218
|
+
context = create_test_context()
|
|
219
|
+
|
|
220
|
+
# Test 1: Combined file generation
|
|
221
|
+
print("📝 Test 1: Generating combined api.proto file")
|
|
222
|
+
generator = ProtoGenerator(
|
|
223
|
+
context=context,
|
|
224
|
+
split_files=False,
|
|
225
|
+
package_name="test.api.v1",
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
files = generator.generate()
|
|
229
|
+
print(f"✅ Generated {len(files)} file(s)")
|
|
230
|
+
|
|
231
|
+
for file in files:
|
|
232
|
+
print(f"\n{'=' * 60}")
|
|
233
|
+
print(f"File: {file.path}")
|
|
234
|
+
print(f"Description: {file.description}")
|
|
235
|
+
print(f"Size: {len(file.content)} bytes")
|
|
236
|
+
print(f"{'=' * 60}")
|
|
237
|
+
print(file.content)
|
|
238
|
+
|
|
239
|
+
# Test 2: Split files generation
|
|
240
|
+
print("\n\n📝 Test 2: Generating split messages.proto and services.proto")
|
|
241
|
+
generator_split = ProtoGenerator(
|
|
242
|
+
context=context,
|
|
243
|
+
split_files=True,
|
|
244
|
+
package_name="test.api.v1",
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
split_files = generator_split.generate()
|
|
248
|
+
print(f"✅ Generated {len(split_files)} file(s)")
|
|
249
|
+
|
|
250
|
+
for file in split_files:
|
|
251
|
+
print(f"\n{'=' * 60}")
|
|
252
|
+
print(f"File: {file.path}")
|
|
253
|
+
print(f"Description: {file.description}")
|
|
254
|
+
print(f"Size: {len(file.content)} bytes")
|
|
255
|
+
print(f"{'=' * 60}")
|
|
256
|
+
print(file.content[:500] + "..." if len(file.content) > 500 else file.content)
|
|
257
|
+
|
|
258
|
+
print("\n\n✅ All tests passed!")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
if __name__ == "__main__":
|
|
262
|
+
main()
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Proto Type Mapper - Maps IR types to Protocol Buffer types.
|
|
3
|
+
|
|
4
|
+
Handles type conversion from OpenAPI/IR types to proto3 types.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ProtoTypeMapper:
|
|
11
|
+
"""
|
|
12
|
+
Maps IR schema types to Protocol Buffer types.
|
|
13
|
+
|
|
14
|
+
Supports proto3 with proper handling of:
|
|
15
|
+
- Basic types (string, int32, int64, double, bool)
|
|
16
|
+
- Complex types (message, repeated)
|
|
17
|
+
- Special types (bytes for binary, google.protobuf.Timestamp for datetime)
|
|
18
|
+
- Nullable fields (optional in proto3)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Core type mappings
|
|
22
|
+
TYPE_MAP = {
|
|
23
|
+
"string": "string",
|
|
24
|
+
"integer": "int64", # Default to int64 for safety
|
|
25
|
+
"number": "double",
|
|
26
|
+
"boolean": "bool",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Format-specific mappings
|
|
30
|
+
FORMAT_MAP = {
|
|
31
|
+
"int32": "int32",
|
|
32
|
+
"int64": "int64",
|
|
33
|
+
"float": "float",
|
|
34
|
+
"double": "double",
|
|
35
|
+
"byte": "bytes",
|
|
36
|
+
"binary": "bytes",
|
|
37
|
+
"date": "string", # ISO 8601 date string
|
|
38
|
+
"date-time": "google.protobuf.Timestamp",
|
|
39
|
+
"password": "string",
|
|
40
|
+
"email": "string",
|
|
41
|
+
"uri": "string",
|
|
42
|
+
"uuid": "string",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
def __init__(self):
|
|
46
|
+
self.imported_types = set() # Track which google types we need to import
|
|
47
|
+
|
|
48
|
+
def map_type(self, ir_type: str, ir_format: str | None = None) -> str:
|
|
49
|
+
"""
|
|
50
|
+
Map IR type to proto type.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
ir_type: IR schema type (string, integer, number, boolean, array, object)
|
|
54
|
+
ir_format: Optional format specifier (int32, date-time, binary, etc.)
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Proto type string (e.g., "string", "int64", "bytes")
|
|
58
|
+
"""
|
|
59
|
+
# Check format first (more specific)
|
|
60
|
+
if ir_format and ir_format in self.FORMAT_MAP:
|
|
61
|
+
proto_type = self.FORMAT_MAP[ir_format]
|
|
62
|
+
|
|
63
|
+
# Track google imports
|
|
64
|
+
if proto_type.startswith("google.protobuf."):
|
|
65
|
+
self.imported_types.add(proto_type)
|
|
66
|
+
|
|
67
|
+
return proto_type
|
|
68
|
+
|
|
69
|
+
# Fall back to base type
|
|
70
|
+
return self.TYPE_MAP.get(ir_type, "string")
|
|
71
|
+
|
|
72
|
+
def get_field_label(self, required: bool, nullable: bool, is_repeated: bool) -> str:
|
|
73
|
+
"""
|
|
74
|
+
Get proto field label (optional, repeated, or none).
|
|
75
|
+
|
|
76
|
+
In proto3:
|
|
77
|
+
- repeated: for arrays
|
|
78
|
+
- optional: for nullable or non-required fields
|
|
79
|
+
- (no label): for required non-repeated fields
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
required: Whether field is required
|
|
83
|
+
nullable: Whether field can be null
|
|
84
|
+
is_repeated: Whether field is an array
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Field label ("optional", "repeated", or "")
|
|
88
|
+
"""
|
|
89
|
+
if is_repeated:
|
|
90
|
+
return "repeated"
|
|
91
|
+
|
|
92
|
+
if nullable or not required:
|
|
93
|
+
return "optional"
|
|
94
|
+
|
|
95
|
+
return ""
|
|
96
|
+
|
|
97
|
+
def get_required_imports(self) -> list[str]:
|
|
98
|
+
"""
|
|
99
|
+
Get list of proto files that need to be imported.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
List of import statements (e.g., ["google/protobuf/timestamp.proto"])
|
|
103
|
+
"""
|
|
104
|
+
imports = []
|
|
105
|
+
|
|
106
|
+
if "google.protobuf.Timestamp" in self.imported_types:
|
|
107
|
+
imports.append("google/protobuf/timestamp.proto")
|
|
108
|
+
|
|
109
|
+
if "google.protobuf.Empty" in self.imported_types:
|
|
110
|
+
imports.append("google/protobuf/empty.proto")
|
|
111
|
+
|
|
112
|
+
return imports
|
|
113
|
+
|
|
114
|
+
def sanitize_field_name(self, name: str) -> str:
|
|
115
|
+
"""
|
|
116
|
+
Sanitize field name for proto compatibility.
|
|
117
|
+
|
|
118
|
+
Proto3 field names should be snake_case.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
name: Original field name
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Sanitized field name
|
|
125
|
+
"""
|
|
126
|
+
# Convert camelCase to snake_case
|
|
127
|
+
import re
|
|
128
|
+
name = re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
|
|
129
|
+
|
|
130
|
+
# Reserved proto keywords
|
|
131
|
+
reserved = {
|
|
132
|
+
"message", "service", "rpc", "option", "import", "package",
|
|
133
|
+
"syntax", "enum", "repeated", "optional", "required"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if name in reserved:
|
|
137
|
+
return f"{name}_value"
|
|
138
|
+
|
|
139
|
+
return name
|
|
140
|
+
|
|
141
|
+
def get_message_name(self, name: str) -> str:
|
|
142
|
+
"""
|
|
143
|
+
Get proto message name (PascalCase).
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
name: Original name
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Proto-compatible message name
|
|
150
|
+
"""
|
|
151
|
+
# Ensure PascalCase
|
|
152
|
+
parts = name.replace("_", " ").replace("-", " ").split()
|
|
153
|
+
return "".join(word.capitalize() for word in parts)
|
|
@@ -43,6 +43,12 @@ class Command(BaseCommand):
|
|
|
43
43
|
help="Generate Go client only",
|
|
44
44
|
)
|
|
45
45
|
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--proto",
|
|
48
|
+
action="store_true",
|
|
49
|
+
help="Generate Protocol Buffer/gRPC definitions only",
|
|
50
|
+
)
|
|
51
|
+
|
|
46
52
|
parser.add_argument(
|
|
47
53
|
"--no-python",
|
|
48
54
|
action="store_true",
|
|
@@ -61,6 +67,12 @@ class Command(BaseCommand):
|
|
|
61
67
|
help="Skip Go client generation",
|
|
62
68
|
)
|
|
63
69
|
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"--no-proto",
|
|
72
|
+
action="store_true",
|
|
73
|
+
help="Skip Protocol Buffer generation",
|
|
74
|
+
)
|
|
75
|
+
|
|
64
76
|
# Utility options
|
|
65
77
|
parser.add_argument(
|
|
66
78
|
"--dry-run",
|
|
@@ -205,22 +217,31 @@ class Command(BaseCommand):
|
|
|
205
217
|
def _generate_clients(self, service, options):
|
|
206
218
|
"""Generate clients."""
|
|
207
219
|
# Determine languages
|
|
208
|
-
if options["python"] and not options["typescript"] and not options["go"]:
|
|
220
|
+
if options["python"] and not options["typescript"] and not options["go"] and not options["proto"]:
|
|
209
221
|
python = True
|
|
210
222
|
typescript = False
|
|
211
223
|
go = False
|
|
212
|
-
|
|
224
|
+
proto = False
|
|
225
|
+
elif options["typescript"] and not options["python"] and not options["go"] and not options["proto"]:
|
|
213
226
|
python = False
|
|
214
227
|
typescript = True
|
|
215
228
|
go = False
|
|
216
|
-
|
|
229
|
+
proto = False
|
|
230
|
+
elif options["go"] and not options["python"] and not options["typescript"] and not options["proto"]:
|
|
217
231
|
python = False
|
|
218
232
|
typescript = False
|
|
219
233
|
go = True
|
|
234
|
+
proto = False
|
|
235
|
+
elif options["proto"] and not options["python"] and not options["typescript"] and not options["go"]:
|
|
236
|
+
python = False
|
|
237
|
+
typescript = False
|
|
238
|
+
go = False
|
|
239
|
+
proto = True
|
|
220
240
|
else:
|
|
221
241
|
python = not options["no_python"]
|
|
222
242
|
typescript = not options["no_typescript"]
|
|
223
243
|
go = not options["no_go"]
|
|
244
|
+
proto = not options["no_proto"]
|
|
224
245
|
|
|
225
246
|
# Get groups
|
|
226
247
|
groups = options.get("groups")
|
|
@@ -254,6 +275,8 @@ class Command(BaseCommand):
|
|
|
254
275
|
self.stdout.write(" → TypeScript")
|
|
255
276
|
if go:
|
|
256
277
|
self.stdout.write(" → Go")
|
|
278
|
+
if proto:
|
|
279
|
+
self.stdout.write(" → Protocol Buffers (proto3)")
|
|
257
280
|
|
|
258
281
|
if dry_run:
|
|
259
282
|
self.stdout.write(self.style.WARNING("\n✅ Dry run completed - no files generated"))
|
|
@@ -271,6 +294,7 @@ class Command(BaseCommand):
|
|
|
271
294
|
ArchiveManager,
|
|
272
295
|
GoGenerator,
|
|
273
296
|
GroupManager,
|
|
297
|
+
ProtoGenerator,
|
|
274
298
|
PythonGenerator,
|
|
275
299
|
TypeScriptGenerator,
|
|
276
300
|
parse_openapi,
|
|
@@ -444,6 +468,26 @@ class Command(BaseCommand):
|
|
|
444
468
|
|
|
445
469
|
self.stdout.write(f" ✅ Go client: {go_dir} ({len(go_files)} files)")
|
|
446
470
|
|
|
471
|
+
# Generate Proto files
|
|
472
|
+
if proto:
|
|
473
|
+
self.stdout.write(" → Generating Protocol Buffer definitions...")
|
|
474
|
+
proto_dir = clients_dir / "proto" / group_name
|
|
475
|
+
proto_dir.mkdir(parents=True, exist_ok=True)
|
|
476
|
+
|
|
477
|
+
proto_generator = ProtoGenerator(
|
|
478
|
+
ir_context,
|
|
479
|
+
split_files=True, # Generate separate messages.proto and services.proto
|
|
480
|
+
package_name=f"{group_name}.v1",
|
|
481
|
+
)
|
|
482
|
+
proto_files = proto_generator.generate()
|
|
483
|
+
|
|
484
|
+
for generated_file in proto_files:
|
|
485
|
+
full_path = proto_dir / generated_file.path
|
|
486
|
+
full_path.parent.mkdir(parents=True, exist_ok=True)
|
|
487
|
+
full_path.write_text(generated_file.content)
|
|
488
|
+
|
|
489
|
+
self.stdout.write(f" ✅ Proto files: {proto_dir} ({len(proto_files)} files)")
|
|
490
|
+
|
|
447
491
|
# Archive if enabled
|
|
448
492
|
if service.config.enable_archive:
|
|
449
493
|
self.stdout.write(" → Archiving...")
|
|
@@ -452,6 +496,8 @@ class Command(BaseCommand):
|
|
|
452
496
|
group_name,
|
|
453
497
|
python_dir=service.config.get_group_python_dir(group_name) if python else None,
|
|
454
498
|
typescript_dir=service.config.get_group_typescript_dir(group_name) if typescript else None,
|
|
499
|
+
go_dir=go_dir if go else None,
|
|
500
|
+
proto_dir=proto_dir if proto else None,
|
|
455
501
|
)
|
|
456
502
|
if archive_result.get('success'):
|
|
457
503
|
self.stdout.write(f" ✅ Archived: {archive_result['archive_path']}")
|
django_cfg/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "django-cfg"
|
|
7
|
-
version = "1.4.
|
|
7
|
+
version = "1.4.73"
|
|
8
8
|
description = "Django AI framework with built-in agents, type-safe Pydantic v2 configuration, and 8 enterprise apps. Replace settings.py, validate at startup, 90% less code. Production-ready AI workflows for Django."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "ai-agents", "enterprise-django", "django-settings", "type-safe-config",]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-cfg
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.73
|
|
4
4
|
Summary: Django AI framework with built-in agents, type-safe Pydantic v2 configuration, and 8 enterprise apps. Replace settings.py, validate at startup, 90% less code. Production-ready AI workflows for Django.
|
|
5
5
|
Project-URL: Homepage, https://djangocfg.com
|
|
6
6
|
Project-URL: Documentation, https://djangocfg.com
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
django_cfg/__init__.py,sha256=
|
|
2
|
+
django_cfg/__init__.py,sha256=J_gAsqhlF9hwvBL3SgdU5q3RzB0SenGbtUn0Kmj6HZQ,1620
|
|
3
3
|
django_cfg/apps.py,sha256=72m3uuvyqGiLx6gOfE-BD3P61jddCCERuBOYpxTX518,1605
|
|
4
4
|
django_cfg/config.py,sha256=y4Z3rnYsHBE0TehpwAIPaxr---mkvyKrZGGsNwYso74,1398
|
|
5
5
|
django_cfg/apps/__init__.py,sha256=JtDmEYt1OcleWM2ZaeX0LKDnRQzPOavfaXBWG4ECB5Q,26
|
|
@@ -15,12 +15,12 @@ django_cfg/apps/accounts/admin/__init__.py,sha256=DODdxlRDj5rA9m_lV4ynefA92Yr0KC
|
|
|
15
15
|
django_cfg/apps/accounts/admin/activity_admin.py,sha256=Dj27DkYQqoJMpaFtt54RK4X5wOSXP6KPP8GoV-fldy4,4103
|
|
16
16
|
django_cfg/apps/accounts/admin/filters.py,sha256=UyiiDrYmgJQeUy9Strr0dmNkBfe9Acng2DNwSaHqg80,6087
|
|
17
17
|
django_cfg/apps/accounts/admin/group_admin.py,sha256=5VCiR1hFwE-Qk9dAJK4PCRygUp-pXFn9wJpDbqfF3R4,2241
|
|
18
|
-
django_cfg/apps/accounts/admin/inlines.py,sha256=
|
|
18
|
+
django_cfg/apps/accounts/admin/inlines.py,sha256=2nRGxheulkNltQlQ6sBajfnhFQmJqOb-fZGx1Orzya4,5969
|
|
19
19
|
django_cfg/apps/accounts/admin/otp_admin.py,sha256=YM-04HFOr7uy9AGUdyuIE7YLwTCW2kQkh0HJjDR-X8g,2712
|
|
20
20
|
django_cfg/apps/accounts/admin/registration_admin.py,sha256=Yohh3px81nKHR6xxD4uV8gkzSOOuA5JmVyOh6LuNEBY,4955
|
|
21
21
|
django_cfg/apps/accounts/admin/resources.py,sha256=LBpBUAQfcvwwEolBDdLikSlaLf2gVcR3ww24SIkXepY,10944
|
|
22
22
|
django_cfg/apps/accounts/admin/twilio_admin.py,sha256=ijZ_1jC4zja7AA2oBQ9rgkQpaogcvICNMzvowJcmYLs,8062
|
|
23
|
-
django_cfg/apps/accounts/admin/user_admin.py,sha256=
|
|
23
|
+
django_cfg/apps/accounts/admin/user_admin.py,sha256=FTaJsQONTMdW1xOuIgIEZP3McVB0eXc6Xsyf3MpFmag,9314
|
|
24
24
|
django_cfg/apps/accounts/management/commands/otp_test.py,sha256=JtOq1vj4cz0UI8i5-1QmjLtPW2l7IBAfp_07iuu0TTY,6861
|
|
25
25
|
django_cfg/apps/accounts/managers/__init__.py,sha256=2PEjf-iHSuXE5TBqoY6tI0d23ihovNdT5F7eKOjl2SI,50
|
|
26
26
|
django_cfg/apps/accounts/managers/user_manager.py,sha256=7NCcfR0tgocXf6XPKTL2aNCHm2t03LrEzkCHqKX9OH0,10298
|
|
@@ -691,16 +691,16 @@ django_cfg/modules/django_client/__init__.py,sha256=iHaGKbsyR2wMmVCWNsETC7cwB60f
|
|
|
691
691
|
django_cfg/modules/django_client/apps.py,sha256=xfkw2aXy08xXlkFhbCiTFveMmRwlDk3SQOAWdqXraFM,1952
|
|
692
692
|
django_cfg/modules/django_client/pytest.ini,sha256=yC1PeQKS4nWQD-jVo5fWF9y1Uv6rywcH0mtHREsiAp0,668
|
|
693
693
|
django_cfg/modules/django_client/urls.py,sha256=0Q0uDhiRqI-jlTU_tsylg-HtRHNXARko6gGGz2-63Aw,1799
|
|
694
|
-
django_cfg/modules/django_client/core/__init__.py,sha256=
|
|
694
|
+
django_cfg/modules/django_client/core/__init__.py,sha256=xAqXcFoY3H0oCBp8g7d9PzO99bOGIDdtHUKgVAOoS_c,1135
|
|
695
695
|
django_cfg/modules/django_client/core/archive/__init__.py,sha256=SKeOKjRnw_NsL7_fOU_2Neo-bcDj4Hv2sVFYztJYiSI,171
|
|
696
|
-
django_cfg/modules/django_client/core/archive/manager.py,sha256=
|
|
696
|
+
django_cfg/modules/django_client/core/archive/manager.py,sha256=2qSi1ymdjfjU-X0cWzl_hq2adt09EmOCz4z4pMQMAQw,4345
|
|
697
697
|
django_cfg/modules/django_client/core/cli/__init__.py,sha256=bqY0TX3uaOyhlDmVVQ0kjoCvEPqKJFgWBYVPj974dQs,167
|
|
698
698
|
django_cfg/modules/django_client/core/cli/main.py,sha256=emfSWMCLiV1qm8naC1jWL9pagRxGJKF3sXmYfaJ1Sf4,6591
|
|
699
699
|
django_cfg/modules/django_client/core/config/__init__.py,sha256=aFniCFG7yC1Iaal5euPLjuQ-VubyDryc3a7cST8APTk,411
|
|
700
700
|
django_cfg/modules/django_client/core/config/config.py,sha256=2OC8WZwiCH6FcYL6SCAbD7YvtnUrQcr4FT-plutFXaA,6681
|
|
701
701
|
django_cfg/modules/django_client/core/config/group.py,sha256=LDITTEhPD0_p5GvwYTqzc_xxT3JaY1WBgUaCiQEWKPQ,2646
|
|
702
702
|
django_cfg/modules/django_client/core/config/service.py,sha256=-TQXpy5z-GmQzDwD-tjpOWmMW31uV_7eH9AHlimnHEU,6287
|
|
703
|
-
django_cfg/modules/django_client/core/generator/__init__.py,sha256=
|
|
703
|
+
django_cfg/modules/django_client/core/generator/__init__.py,sha256=JN1HpxRfO24ZgHjwX3hkW4v4O5Hcx5ewBPhxiwI2Faw,5756
|
|
704
704
|
django_cfg/modules/django_client/core/generator/base.py,sha256=2dWKCbAb1heCaWsWhDn85TR9Z05nI2V3fvi3h8odG7Y,31765
|
|
705
705
|
django_cfg/modules/django_client/core/generator/go/__init__.py,sha256=vY5VIxrnFXjw8pn1DBaazMohyEFOas5WI8RuQmNfFdQ,333
|
|
706
706
|
django_cfg/modules/django_client/core/generator/go/client_generator.py,sha256=DpnIm0AMGqeftT-mNALvz3K5btbMm9A_1EAlCr6xFSA,4427
|
|
@@ -721,6 +721,12 @@ django_cfg/modules/django_client/core/generator/go/templates/middleware.go.j2,sh
|
|
|
721
721
|
django_cfg/modules/django_client/core/generator/go/templates/models.go.j2,sha256=9UThdTAGbILmzxIvu_Mn5cQsxs6dGPLtEdCK-DDrvUU,631
|
|
722
722
|
django_cfg/modules/django_client/core/generator/go/templates/operations_client.go.j2,sha256=Qxjr8kO1n-3FSlWfNWyBlt8ldrdh4Ym-FusC4cMQ9X0,3716
|
|
723
723
|
django_cfg/modules/django_client/core/generator/go/templates/validation.go.j2,sha256=3BzI7pOuuh-19wNwuRq7GtRda-Pmaw57Jug35bdtamA,5154
|
|
724
|
+
django_cfg/modules/django_client/core/generator/proto/__init__.py,sha256=TBbZGr6Cim3apAO1eexEcmIQuo840lVWQpA_FD_QkLs,449
|
|
725
|
+
django_cfg/modules/django_client/core/generator/proto/generator.py,sha256=ewMs1Ld657hos2YYv172A_EtlfMibQy7NeDqdlBusrI,17411
|
|
726
|
+
django_cfg/modules/django_client/core/generator/proto/messages_generator.py,sha256=H9A3DeWUy-wJZ5dtMBrh5o1l1uwF3ZqnGdsx7vNtm-Q,9498
|
|
727
|
+
django_cfg/modules/django_client/core/generator/proto/services_generator.py,sha256=XYXT8f3bTktGBWLyW5hbgE2igzfc3T6caFTxdSXYZ3I,11018
|
|
728
|
+
django_cfg/modules/django_client/core/generator/proto/test_proto_generator.py,sha256=XG4FJJ6MJYTcb_BNTMoXwZLeL6JCn3lNrk4Z25BnBec,7725
|
|
729
|
+
django_cfg/modules/django_client/core/generator/proto/type_mapper.py,sha256=n5GVQHK3h_-7h3xvQaEUqWVJWy-utFOyusdRAP1e1lM,4370
|
|
724
730
|
django_cfg/modules/django_client/core/generator/python/__init__.py,sha256=DOPkZBmNVX8qKZBulnE-49iMlBsMHszidzBhWG17c2E,448
|
|
725
731
|
django_cfg/modules/django_client/core/generator/python/async_client_gen.py,sha256=zFviFo0PLg3Zb-0TL5AS_Ghhvl-jS180Cl-AWIAWIa0,6030
|
|
726
732
|
django_cfg/modules/django_client/core/generator/python/files_generator.py,sha256=cgJVL6OZFXQFiy3trgCK5NDfGQWygQBJNaDepDQx4Vc,6420
|
|
@@ -821,7 +827,7 @@ django_cfg/modules/django_client/core/validation/rules/base.py,sha256=xVJli0eSEz
|
|
|
821
827
|
django_cfg/modules/django_client/core/validation/rules/type_hints.py,sha256=hwjTMADillsTPruDvXZQeZMj4LVV443zxY9o0Gqgg6k,10200
|
|
822
828
|
django_cfg/modules/django_client/management/__init__.py,sha256=mCTPP_bIOmqNnn0WAG2n4BuF6zwc9PTgdZr_dORfNDk,54
|
|
823
829
|
django_cfg/modules/django_client/management/commands/__init__.py,sha256=CJ55pHUNYQ5h-QHUe3axeTtxzlUJv7wbEuZmGN21iCM,36
|
|
824
|
-
django_cfg/modules/django_client/management/commands/generate_client.py,sha256=
|
|
830
|
+
django_cfg/modules/django_client/management/commands/generate_client.py,sha256=NTxVIOLMvrxbKfqNAhVjqChaLEb33nflGAHFQMMN4aU,20642
|
|
825
831
|
django_cfg/modules/django_client/management/commands/validate_openapi.py,sha256=IBKk7oRP3tMQzXjvZNIgQtMPk3k_mB2diNS7bkaSLz4,11011
|
|
826
832
|
django_cfg/modules/django_client/spectacular/__init__.py,sha256=M8fG-odu2ltkG36aMMr0KDkCKGX676TwdrJO8vky2cI,345
|
|
827
833
|
django_cfg/modules/django_client/spectacular/async_detection.py,sha256=S_pwGR7_2SIWHjZJyiu7SCfySF3Nr3P8eqjDyBSkkLs,5731
|
|
@@ -1105,9 +1111,9 @@ django_cfg/utils/version_check.py,sha256=WO51J2m2e-wVqWCRwbultEwu3q1lQasV67Mw2aa
|
|
|
1105
1111
|
django_cfg/CHANGELOG.md,sha256=jtT3EprqEJkqSUh7IraP73vQ8PmKUMdRtznQsEnqDZk,2052
|
|
1106
1112
|
django_cfg/CONTRIBUTING.md,sha256=DU2kyQ6PU0Z24ob7O_OqKWEYHcZmJDgzw-lQCmu6uBg,3041
|
|
1107
1113
|
django_cfg/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
|
|
1108
|
-
django_cfg/pyproject.toml,sha256=
|
|
1109
|
-
django_cfg-1.4.
|
|
1110
|
-
django_cfg-1.4.
|
|
1111
|
-
django_cfg-1.4.
|
|
1112
|
-
django_cfg-1.4.
|
|
1113
|
-
django_cfg-1.4.
|
|
1114
|
+
django_cfg/pyproject.toml,sha256=Ql5SZFIm-2Ysz4QhYMlgH886yVfJJ0s1rDpjbYmTZLY,8164
|
|
1115
|
+
django_cfg-1.4.73.dist-info/METADATA,sha256=CA3kymFiXFO_OMaOdNSQ66RUyX_7vvxJLlkhkNgB81I,22624
|
|
1116
|
+
django_cfg-1.4.73.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
1117
|
+
django_cfg-1.4.73.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
|
|
1118
|
+
django_cfg-1.4.73.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
|
|
1119
|
+
django_cfg-1.4.73.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|