Skip to content

Commit ee535ab

Browse files
committed
- Fixed GoConfig/PythonConfig __dict__ error with slots=True dataclasses
1 parent ee3070f commit ee535ab

4 files changed

Lines changed: 94 additions & 36 deletions

File tree

json_explorer/codegen/languages/go/config.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,27 +65,41 @@ class GoConfig:
6565
use_pointers_for_optional: bool = True
6666

6767
# Declaration
68-
type_map: dict = field(init=False)
68+
type_map: dict = field(init=False, repr=False, default_factory=dict)
6969

7070
def __post_init__(self) -> None:
7171
"""Build type map with configured types."""
72-
self.type_map = GO_TYPE_MAP.copy()
73-
self.type_map[FieldType.INTEGER] = self.int_type
74-
self.type_map[FieldType.FLOAT] = self.float_type
75-
self.type_map[FieldType.STRING] = self.string_type
76-
self.type_map[FieldType.BOOLEAN] = self.bool_type
77-
self.type_map[FieldType.TIMESTAMP] = self.time_type
78-
self.type_map[FieldType.UNKNOWN] = self.unknown_type
79-
self.type_map[FieldType.CONFLICT] = self.unknown_type
72+
type_map = GO_TYPE_MAP.copy()
73+
type_map[FieldType.INTEGER] = self.int_type
74+
type_map[FieldType.FLOAT] = self.float_type
75+
type_map[FieldType.STRING] = self.string_type
76+
type_map[FieldType.BOOLEAN] = self.bool_type
77+
type_map[FieldType.TIMESTAMP] = self.time_type
78+
type_map[FieldType.UNKNOWN] = self.unknown_type
79+
type_map[FieldType.CONFLICT] = self.unknown_type
80+
81+
object.__setattr__(self, "type_map", type_map)
8082

8183
logger.debug(
8284
f"GoConfig initialized: int={self.int_type}, "
8385
f"float={self.float_type}, pointers={self.use_pointers_for_optional}"
8486
)
8587

88+
def to_dict(self) -> dict:
89+
"""Convert to dict, excluding computed fields."""
90+
return {
91+
"int_type": self.int_type,
92+
"float_type": self.float_type,
93+
"string_type": self.string_type,
94+
"bool_type": self.bool_type,
95+
"time_type": self.time_type,
96+
"unknown_type": self.unknown_type,
97+
"use_pointers_for_optional": self.use_pointers_for_optional,
98+
}
99+
86100
def get_go_type(
87101
self,
88-
field_type: FieldType,
102+
field_type: FieldType | None,
89103
*,
90104
is_optional: bool = False,
91105
is_array: bool = False,
@@ -104,10 +118,18 @@ def get_go_type(
104118
Go type string (e.g., "string", "*int64", "[]User")
105119
"""
106120
if is_array:
107-
base = element_type or self.type_map.get(field_type, self.unknown_type)
121+
base = (
122+
element_type or self.type_map.get(field_type, self.unknown_type)
123+
if field_type
124+
else self.unknown_type
125+
)
108126
return f"[]{base}"
109127

110-
go_type = self.type_map.get(field_type, self.unknown_type)
128+
# Handle None field_type
129+
if field_type is None:
130+
go_type = self.unknown_type
131+
else:
132+
go_type = self.type_map.get(field_type, self.unknown_type)
111133

112134
# Add pointer for optional fields if configured
113135
if is_optional and self.use_pointers_for_optional:

json_explorer/codegen/languages/go/generator.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,8 @@ def create_web_api_generator() -> GoGenerator:
314314
generate_json_tags=True,
315315
json_tag_omitempty=True,
316316
add_comments=True,
317-
language_config=get_web_api_config().__dict__,
317+
language_config=get_web_api_config().to_dict(),
318318
)
319-
320319
return GoGenerator(config)
321320

322321

@@ -329,7 +328,6 @@ def create_strict_generator() -> GoGenerator:
329328
generate_json_tags=True,
330329
json_tag_omitempty=False,
331330
add_comments=True,
332-
language_config=get_strict_config().__dict__,
331+
language_config=get_strict_config().to_dict(),
333332
)
334-
335333
return GoGenerator(config)

json_explorer/codegen/languages/python/config.py

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,29 +123,54 @@ class PythonConfig:
123123
typeddict_total: bool = False
124124

125125
# Declaration
126-
type_map: dict = field(init=False)
126+
type_map: dict = field(init=False, repr=False, default_factory=dict)
127127

128128
def __post_init__(self) -> None:
129129
"""Initialize type map and validate style."""
130130
# Convert string style to enum if needed
131131
if isinstance(self.style, str):
132-
self.style = PythonStyle(self.style)
132+
object.__setattr__(self, "style", PythonStyle(self.style))
133133

134134
# Build type map
135-
self.type_map = PYTHON_TYPE_MAP.copy()
136-
self.type_map[FieldType.INTEGER] = self.int_type
137-
self.type_map[FieldType.FLOAT] = self.float_type
138-
self.type_map[FieldType.STRING] = self.string_type
139-
self.type_map[FieldType.BOOLEAN] = self.bool_type
140-
self.type_map[FieldType.TIMESTAMP] = self.datetime_type
141-
self.type_map[FieldType.UNKNOWN] = self.unknown_type
142-
self.type_map[FieldType.CONFLICT] = self.unknown_type
135+
type_map = PYTHON_TYPE_MAP.copy()
136+
type_map[FieldType.INTEGER] = self.int_type
137+
type_map[FieldType.FLOAT] = self.float_type
138+
type_map[FieldType.STRING] = self.string_type
139+
type_map[FieldType.BOOLEAN] = self.bool_type
140+
type_map[FieldType.TIMESTAMP] = self.datetime_type
141+
type_map[FieldType.UNKNOWN] = self.unknown_type
142+
type_map[FieldType.CONFLICT] = self.unknown_type
143+
144+
object.__setattr__(self, "type_map", type_map)
143145

144146
logger.debug(
145147
f"PythonConfig initialized: style={self.style.value}, "
146148
f"optional={self.use_optional}"
147149
)
148150

151+
def to_dict(self) -> dict:
152+
"""Convert to dict, excluding computed fields."""
153+
return {
154+
"style": (
155+
self.style.value if isinstance(self.style, PythonStyle) else self.style
156+
),
157+
"int_type": self.int_type,
158+
"float_type": self.float_type,
159+
"string_type": self.string_type,
160+
"bool_type": self.bool_type,
161+
"datetime_type": self.datetime_type,
162+
"unknown_type": self.unknown_type,
163+
"use_optional": self.use_optional,
164+
"dataclass_frozen": self.dataclass_frozen,
165+
"dataclass_slots": self.dataclass_slots,
166+
"dataclass_kw_only": self.dataclass_kw_only,
167+
"pydantic_use_field": self.pydantic_use_field,
168+
"pydantic_use_alias": self.pydantic_use_alias,
169+
"pydantic_config_dict": self.pydantic_config_dict,
170+
"pydantic_extra_forbid": self.pydantic_extra_forbid,
171+
"typeddict_total": self.typeddict_total,
172+
}
173+
149174
def get_python_type(
150175
self,
151176
field_type: FieldType,
@@ -244,6 +269,22 @@ def _extract_base_types(self, type_string: str) -> set[str]:
244269
# ============================================================================
245270
# Preset Configurations
246271
# ============================================================================
272+
def get_python_generator_config(**overrides) -> dict:
273+
"""
274+
Get GeneratorConfig dict with Python-friendly defaults.
275+
276+
Python conventions:
277+
- Classes: PascalCase (struct_case="pascal")
278+
- Fields: snake_case (field_case="snake")
279+
"""
280+
defaults = {
281+
"package_name": "models",
282+
"struct_case": "pascal",
283+
"field_case": "snake", # Python convention
284+
"add_comments": True,
285+
}
286+
defaults.update(overrides)
287+
return defaults
247288

248289

249290
def get_dataclass_config() -> PythonConfig:

json_explorer/codegen/languages/python/generator.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from ...core.config import GeneratorConfig
1212
from ...core.generator import CodeGenerator
1313
from ...core.schema import Schema, Field, FieldType
14-
from .config import PythonConfig, PythonStyle
14+
from .config import PythonConfig, PythonStyle, get_python_generator_config
1515
from .naming import create_python_name_tracker
1616

1717
logger = logging.getLogger(__name__)
@@ -391,9 +391,8 @@ def create_dataclass_generator() -> PythonGenerator:
391391
from .config import get_dataclass_config
392392

393393
config = GeneratorConfig(
394-
package_name="models",
395-
add_comments=True,
396-
language_config=get_dataclass_config().__dict__,
394+
**get_python_generator_config(),
395+
language_config=get_dataclass_config().to_dict(),
397396
)
398397

399398
return PythonGenerator(config)
@@ -404,9 +403,8 @@ def create_pydantic_generator() -> PythonGenerator:
404403
from .config import get_pydantic_config
405404

406405
config = GeneratorConfig(
407-
package_name="models",
408-
add_comments=True,
409-
language_config=get_pydantic_config().__dict__,
406+
**get_python_generator_config(),
407+
language_config=get_pydantic_config().to_dict(),
410408
)
411409

412410
return PythonGenerator(config)
@@ -417,9 +415,8 @@ def create_typeddict_generator() -> PythonGenerator:
417415
from .config import get_typeddict_config
418416

419417
config = GeneratorConfig(
420-
package_name="types",
421-
add_comments=True,
422-
language_config=get_typeddict_config().__dict__,
418+
**get_python_generator_config(),
419+
language_config=get_typeddict_config().to_dict(),
423420
)
424421

425422
return PythonGenerator(config)

0 commit comments

Comments
 (0)