avrotize 2.21.1__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.
- avrotize/__init__.py +66 -0
- avrotize/__main__.py +6 -0
- avrotize/_version.py +34 -0
- avrotize/asn1toavro.py +160 -0
- avrotize/avrotize.py +152 -0
- avrotize/avrotocpp/CMakeLists.txt.jinja +77 -0
- avrotize/avrotocpp/build.bat.jinja +7 -0
- avrotize/avrotocpp/build.sh.jinja +7 -0
- avrotize/avrotocpp/dataclass_body.jinja +108 -0
- avrotize/avrotocpp/vcpkg.json.jinja +21 -0
- avrotize/avrotocpp.py +483 -0
- avrotize/avrotocsharp/README.md.jinja +166 -0
- avrotize/avrotocsharp/class_test.cs.jinja +266 -0
- avrotize/avrotocsharp/dataclass_core.jinja +293 -0
- avrotize/avrotocsharp/enum_test.cs.jinja +20 -0
- avrotize/avrotocsharp/project.csproj.jinja +30 -0
- avrotize/avrotocsharp/project.sln.jinja +34 -0
- avrotize/avrotocsharp/run_coverage.ps1.jinja +98 -0
- avrotize/avrotocsharp/run_coverage.sh.jinja +149 -0
- avrotize/avrotocsharp/testproject.csproj.jinja +19 -0
- avrotize/avrotocsharp.py +1180 -0
- avrotize/avrotocsv.py +121 -0
- avrotize/avrotodatapackage.py +173 -0
- avrotize/avrotodb.py +1383 -0
- avrotize/avrotogo/go_enum.jinja +12 -0
- avrotize/avrotogo/go_helpers.jinja +31 -0
- avrotize/avrotogo/go_struct.jinja +151 -0
- avrotize/avrotogo/go_test.jinja +47 -0
- avrotize/avrotogo/go_union.jinja +38 -0
- avrotize/avrotogo.py +476 -0
- avrotize/avrotographql.py +197 -0
- avrotize/avrotoiceberg.py +210 -0
- avrotize/avrotojava/class_test.java.jinja +212 -0
- avrotize/avrotojava/enum_test.java.jinja +21 -0
- avrotize/avrotojava/testproject.pom.jinja +54 -0
- avrotize/avrotojava.py +2156 -0
- avrotize/avrotojs.py +250 -0
- avrotize/avrotojsons.py +481 -0
- avrotize/avrotojstruct.py +345 -0
- avrotize/avrotokusto.py +364 -0
- avrotize/avrotomd/README.md.jinja +49 -0
- avrotize/avrotomd.py +137 -0
- avrotize/avrotools.py +168 -0
- avrotize/avrotoparquet.py +208 -0
- avrotize/avrotoproto.py +359 -0
- avrotize/avrotopython/dataclass_core.jinja +241 -0
- avrotize/avrotopython/enum_core.jinja +87 -0
- avrotize/avrotopython/pyproject_toml.jinja +18 -0
- avrotize/avrotopython/test_class.jinja +97 -0
- avrotize/avrotopython/test_enum.jinja +23 -0
- avrotize/avrotopython.py +626 -0
- avrotize/avrotorust/dataclass_enum.rs.jinja +74 -0
- avrotize/avrotorust/dataclass_struct.rs.jinja +204 -0
- avrotize/avrotorust/dataclass_union.rs.jinja +105 -0
- avrotize/avrotorust.py +435 -0
- avrotize/avrotots/class_core.ts.jinja +140 -0
- avrotize/avrotots/class_test.ts.jinja +77 -0
- avrotize/avrotots/enum_core.ts.jinja +46 -0
- avrotize/avrotots/gitignore.jinja +34 -0
- avrotize/avrotots/index.ts.jinja +0 -0
- avrotize/avrotots/package.json.jinja +23 -0
- avrotize/avrotots/tsconfig.json.jinja +21 -0
- avrotize/avrotots.py +687 -0
- avrotize/avrotoxsd.py +344 -0
- avrotize/cddltostructure.py +1841 -0
- avrotize/commands.json +3496 -0
- avrotize/common.py +834 -0
- avrotize/constants.py +87 -0
- avrotize/csvtoavro.py +132 -0
- avrotize/datapackagetoavro.py +76 -0
- avrotize/dependencies/cpp/vcpkg/vcpkg.json +19 -0
- avrotize/dependencies/cs/net90/dependencies.csproj +29 -0
- avrotize/dependencies/go/go121/go.mod +6 -0
- avrotize/dependencies/java/jdk21/pom.xml +91 -0
- avrotize/dependencies/python/py312/requirements.txt +13 -0
- avrotize/dependencies/rust/stable/Cargo.toml +17 -0
- avrotize/dependencies/typescript/node22/package.json +16 -0
- avrotize/dependency_resolver.py +348 -0
- avrotize/dependency_version.py +432 -0
- avrotize/generic/generic.avsc +57 -0
- avrotize/jsonstoavro.py +2167 -0
- avrotize/jsonstostructure.py +2864 -0
- avrotize/jstructtoavro.py +878 -0
- avrotize/kstructtoavro.py +93 -0
- avrotize/kustotoavro.py +455 -0
- avrotize/openapitostructure.py +717 -0
- avrotize/parquettoavro.py +157 -0
- avrotize/proto2parser.py +498 -0
- avrotize/proto3parser.py +403 -0
- avrotize/prototoavro.py +382 -0
- avrotize/prototypes/any.avsc +19 -0
- avrotize/prototypes/api.avsc +106 -0
- avrotize/prototypes/duration.avsc +20 -0
- avrotize/prototypes/field_mask.avsc +18 -0
- avrotize/prototypes/struct.avsc +60 -0
- avrotize/prototypes/timestamp.avsc +20 -0
- avrotize/prototypes/type.avsc +253 -0
- avrotize/prototypes/wrappers.avsc +117 -0
- avrotize/structuretocddl.py +597 -0
- avrotize/structuretocpp/CMakeLists.txt.jinja +76 -0
- avrotize/structuretocpp/build.bat.jinja +3 -0
- avrotize/structuretocpp/build.sh.jinja +3 -0
- avrotize/structuretocpp/dataclass_body.jinja +50 -0
- avrotize/structuretocpp/vcpkg.json.jinja +11 -0
- avrotize/structuretocpp.py +697 -0
- avrotize/structuretocsharp/class_test.cs.jinja +180 -0
- avrotize/structuretocsharp/dataclass_core.jinja +156 -0
- avrotize/structuretocsharp/enum_test.cs.jinja +36 -0
- avrotize/structuretocsharp/json_structure_converters.cs.jinja +399 -0
- avrotize/structuretocsharp/program.cs.jinja +49 -0
- avrotize/structuretocsharp/project.csproj.jinja +17 -0
- avrotize/structuretocsharp/project.sln.jinja +34 -0
- avrotize/structuretocsharp/testproject.csproj.jinja +18 -0
- avrotize/structuretocsharp/tuple_converter.cs.jinja +121 -0
- avrotize/structuretocsharp.py +2295 -0
- avrotize/structuretocsv.py +365 -0
- avrotize/structuretodatapackage.py +659 -0
- avrotize/structuretodb.py +1125 -0
- avrotize/structuretogo/go_enum.jinja +12 -0
- avrotize/structuretogo/go_helpers.jinja +26 -0
- avrotize/structuretogo/go_interface.jinja +18 -0
- avrotize/structuretogo/go_struct.jinja +187 -0
- avrotize/structuretogo/go_test.jinja +70 -0
- avrotize/structuretogo.py +729 -0
- avrotize/structuretographql.py +502 -0
- avrotize/structuretoiceberg.py +355 -0
- avrotize/structuretojava/choice_core.jinja +34 -0
- avrotize/structuretojava/class_core.jinja +23 -0
- avrotize/structuretojava/enum_core.jinja +18 -0
- avrotize/structuretojava/equals_hashcode.jinja +30 -0
- avrotize/structuretojava/pom.xml.jinja +26 -0
- avrotize/structuretojava/tuple_core.jinja +49 -0
- avrotize/structuretojava.py +938 -0
- avrotize/structuretojs/class_core.js.jinja +33 -0
- avrotize/structuretojs/enum_core.js.jinja +10 -0
- avrotize/structuretojs/package.json.jinja +12 -0
- avrotize/structuretojs/test_class.js.jinja +84 -0
- avrotize/structuretojs/test_enum.js.jinja +58 -0
- avrotize/structuretojs/test_runner.js.jinja +45 -0
- avrotize/structuretojs.py +657 -0
- avrotize/structuretojsons.py +498 -0
- avrotize/structuretokusto.py +639 -0
- avrotize/structuretomd/README.md.jinja +204 -0
- avrotize/structuretomd.py +322 -0
- avrotize/structuretoproto.py +764 -0
- avrotize/structuretopython/dataclass_core.jinja +363 -0
- avrotize/structuretopython/enum_core.jinja +45 -0
- avrotize/structuretopython/map_alias.jinja +21 -0
- avrotize/structuretopython/pyproject_toml.jinja +23 -0
- avrotize/structuretopython/test_class.jinja +103 -0
- avrotize/structuretopython/test_enum.jinja +34 -0
- avrotize/structuretopython.py +799 -0
- avrotize/structuretorust/dataclass_enum.rs.jinja +63 -0
- avrotize/structuretorust/dataclass_struct.rs.jinja +121 -0
- avrotize/structuretorust/dataclass_union.rs.jinja +81 -0
- avrotize/structuretorust.py +714 -0
- avrotize/structuretots/class_core.ts.jinja +78 -0
- avrotize/structuretots/enum_core.ts.jinja +6 -0
- avrotize/structuretots/gitignore.jinja +8 -0
- avrotize/structuretots/index.ts.jinja +1 -0
- avrotize/structuretots/package.json.jinja +39 -0
- avrotize/structuretots/test_class.ts.jinja +35 -0
- avrotize/structuretots/tsconfig.json.jinja +21 -0
- avrotize/structuretots.py +740 -0
- avrotize/structuretoxsd.py +679 -0
- avrotize/xsdtoavro.py +413 -0
- avrotize-2.21.1.dist-info/METADATA +1319 -0
- avrotize-2.21.1.dist-info/RECORD +171 -0
- avrotize-2.21.1.dist-info/WHEEL +4 -0
- avrotize-2.21.1.dist-info/entry_points.txt +3 -0
- avrotize-2.21.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
""" {{ class_name }} dataclass. """
|
|
2
|
+
|
|
3
|
+
# pylint: disable=too-many-lines, too-many-locals, too-many-branches, too-many-statements, too-many-arguments, line-too-long, wildcard-import
|
|
4
|
+
|
|
5
|
+
{%- if avro_annotation or dataclasses_json_annotation %}
|
|
6
|
+
import io
|
|
7
|
+
import gzip
|
|
8
|
+
{%- endif %}
|
|
9
|
+
import enum
|
|
10
|
+
import typing
|
|
11
|
+
import dataclasses
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
{%- if is_abstract %}
|
|
14
|
+
from abc import ABC
|
|
15
|
+
{%- endif %}
|
|
16
|
+
{%- if dataclasses_json_annotation %}
|
|
17
|
+
import dataclasses_json
|
|
18
|
+
from dataclasses_json import Undefined, dataclass_json
|
|
19
|
+
{%- for field in fields if field.type == "datetime.datetime" or field.type == "typing.Optional[datetime.datetime]" %}
|
|
20
|
+
{%- if loop.first %}
|
|
21
|
+
from marshmallow import fields
|
|
22
|
+
{%- endif %}
|
|
23
|
+
{%- endfor %}
|
|
24
|
+
{%- endif %}
|
|
25
|
+
{%- if avro_annotation or dataclasses_json_annotation %}
|
|
26
|
+
import json
|
|
27
|
+
{%- endif %}
|
|
28
|
+
{%- if avro_annotation %}
|
|
29
|
+
import avro.schema
|
|
30
|
+
import avro.io
|
|
31
|
+
{%- endif %}
|
|
32
|
+
{%- for import_type in import_types if import_type not in ['datetime.datetime', 'datetime.date', 'datetime.time', 'datetime.timedelta', 'decimal.Decimal', 'uuid.UUID'] %}
|
|
33
|
+
from {{ '.'.join(import_type.split('.')[:-1]) | lower }} import {{ import_type.split('.')[-1] }}
|
|
34
|
+
{%- endfor %}
|
|
35
|
+
{%- for import_type in import_types if import_type in ['datetime.datetime', 'datetime.date', 'datetime.time', 'datetime.timedelta'] %}
|
|
36
|
+
{%- if loop.first %}
|
|
37
|
+
import datetime
|
|
38
|
+
{%- endif %}
|
|
39
|
+
{%- endfor %}
|
|
40
|
+
{%- for import_type in import_types if import_type == 'decimal.Decimal' %}
|
|
41
|
+
{%- if loop.first %}
|
|
42
|
+
import decimal
|
|
43
|
+
{%- endif %}
|
|
44
|
+
{%- endfor %}
|
|
45
|
+
{%- for import_type in import_types if import_type == 'uuid.UUID' %}
|
|
46
|
+
{%- if loop.first %}
|
|
47
|
+
import uuid
|
|
48
|
+
{%- endif %}
|
|
49
|
+
{%- endfor %}
|
|
50
|
+
|
|
51
|
+
{% if dataclasses_json_annotation %}
|
|
52
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
53
|
+
{%- endif %}
|
|
54
|
+
@dataclass
|
|
55
|
+
class {{ class_name }}{% if base_class or is_abstract %}({% if base_class %}{{ base_class.split('.')[-1] }}{% if is_abstract %}, {% endif %}{% endif %}{% if is_abstract %}ABC{% endif %}){% endif %}:
|
|
56
|
+
"""
|
|
57
|
+
{{ docstring }}
|
|
58
|
+
{% if fields %}
|
|
59
|
+
Attributes:
|
|
60
|
+
{%- for field in fields %}
|
|
61
|
+
{{ field.docstring }}
|
|
62
|
+
{%- endfor -%}
|
|
63
|
+
{% endif %}
|
|
64
|
+
"""
|
|
65
|
+
{% if avro_annotation %}
|
|
66
|
+
AvroType: typing.ClassVar[avro.schema.Schema] = avro.schema.parse(
|
|
67
|
+
"{{ avro_schema_json }}"
|
|
68
|
+
)
|
|
69
|
+
{% endif %}
|
|
70
|
+
{% for field in fields %}
|
|
71
|
+
{%- set isdate = field.type == "datetime.datetime" or field.type == "typing.Optional[datetime.datetime]" %}
|
|
72
|
+
{{ field.name }}: {{ field.type }}=dataclasses.field(kw_only=True{% if dataclasses_json_annotation %}, metadata=dataclasses_json.config(field_name="{{ field.original_name }}"{%- if isdate -%}, encoder=lambda d: d.isoformat() if isinstance(d, datetime.datetime) else d if d else None, decoder=lambda d: datetime.datetime.fromisoformat(d) if isinstance(d, str) else d if d else None, mm_field=fields.DateTime(format='iso'){%- endif -%}){%- endif %})
|
|
73
|
+
{%- endfor %}
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def from_serializer_dict(cls, data: dict) -> '{{ class_name }}':
|
|
77
|
+
"""
|
|
78
|
+
Converts a dictionary to a dataclass instance.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
data: The dictionary to convert to a dataclass.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
The dataclass representation of the dataclass.
|
|
85
|
+
"""
|
|
86
|
+
{%- for field in fields %}
|
|
87
|
+
{%- if field.name != field.original_name %}
|
|
88
|
+
if '{{ field.original_name }}' in data:
|
|
89
|
+
data['{{ field.name }}'] = data.pop('{{ field.original_name }}')
|
|
90
|
+
{%- endif %}
|
|
91
|
+
{%- endfor %}
|
|
92
|
+
return cls(**data)
|
|
93
|
+
|
|
94
|
+
{%- if avro_annotation %}
|
|
95
|
+
@classmethod
|
|
96
|
+
def from_avro_dict(cls, data: dict) -> '{{ class_name }}':
|
|
97
|
+
"""
|
|
98
|
+
Converts a dictionary from Avro deserialization to a dataclass instance.
|
|
99
|
+
Handles conversion of string representations back to Python types for
|
|
100
|
+
extended logical types.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
data: The dictionary from Avro deserialization.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
The dataclass representation.
|
|
107
|
+
"""
|
|
108
|
+
# Convert string values back to Python types for Avro string-based logical types
|
|
109
|
+
converted = data.copy()
|
|
110
|
+
|
|
111
|
+
{%- for field in fields %}
|
|
112
|
+
{%- set field_key = field.original_name if field.name != field.original_name else field.name %}
|
|
113
|
+
if '{{ field_key }}' in converted and converted['{{ field_key }}'] is not None:
|
|
114
|
+
value = converted['{{ field_key }}']
|
|
115
|
+
{%- if field.source_type in ['date', 'datetime', 'timestamp', 'time', 'duration', 'decimal', 'uuid', 'int128', 'uint128'] %}
|
|
116
|
+
{%- if field.source_type == 'date' %}
|
|
117
|
+
if isinstance(value, str):
|
|
118
|
+
converted['{{ field_key }}'] = datetime.date.fromisoformat(value)
|
|
119
|
+
{%- elif field.source_type in ['datetime', 'timestamp'] %}
|
|
120
|
+
if isinstance(value, str):
|
|
121
|
+
converted['{{ field_key }}'] = datetime.datetime.fromisoformat(value)
|
|
122
|
+
{%- elif field.source_type == 'time' %}
|
|
123
|
+
if isinstance(value, str):
|
|
124
|
+
converted['{{ field_key }}'] = datetime.time.fromisoformat(value)
|
|
125
|
+
{%- elif field.source_type == 'duration' %}
|
|
126
|
+
if isinstance(value, str):
|
|
127
|
+
converted['{{ field_key }}'] = datetime.timedelta(seconds=float(value))
|
|
128
|
+
{%- elif field.source_type == 'decimal' %}
|
|
129
|
+
if isinstance(value, str):
|
|
130
|
+
converted['{{ field_key }}'] = decimal.Decimal(value)
|
|
131
|
+
{%- elif field.source_type == 'uuid' %}
|
|
132
|
+
if isinstance(value, str):
|
|
133
|
+
converted['{{ field_key }}'] = uuid.UUID(value)
|
|
134
|
+
{%- elif field.source_type in ['int128', 'uint128'] %}
|
|
135
|
+
if isinstance(value, str):
|
|
136
|
+
converted['{{ field_key }}'] = int(value)
|
|
137
|
+
{%- endif %}
|
|
138
|
+
{%- endif %}
|
|
139
|
+
{%- if field.name != field.original_name %}
|
|
140
|
+
if '{{ field.original_name }}' in converted:
|
|
141
|
+
converted['{{ field.name }}'] = converted.pop('{{ field.original_name }}')
|
|
142
|
+
{%- endif %}
|
|
143
|
+
{%- endfor %}
|
|
144
|
+
|
|
145
|
+
return cls(**converted)
|
|
146
|
+
{%- endif %}
|
|
147
|
+
|
|
148
|
+
def to_serializer_dict(self) -> dict:
|
|
149
|
+
"""
|
|
150
|
+
Converts the dataclass to a dictionary.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
The dictionary representation of the dataclass.
|
|
154
|
+
"""
|
|
155
|
+
asdict_result = dataclasses.asdict(self, dict_factory=self._dict_resolver)
|
|
156
|
+
{%- for field in fields %}
|
|
157
|
+
{%- if field.name != field.original_name and field.original_name+'_' != field.name %}
|
|
158
|
+
if '{{ field.name }}' in asdict_result:
|
|
159
|
+
asdict_result['{{ field.original_name }}'] = asdict_result.pop('{{ field.name }}')
|
|
160
|
+
{%- endif %}
|
|
161
|
+
{%- endfor %}
|
|
162
|
+
return asdict_result
|
|
163
|
+
|
|
164
|
+
def _dict_resolver(self, data):
|
|
165
|
+
"""
|
|
166
|
+
Helps resolving the Enum values to their actual values and fixes the key names.
|
|
167
|
+
"""
|
|
168
|
+
def _resolve_enum(v):
|
|
169
|
+
if isinstance(v, enum.Enum):
|
|
170
|
+
return v.value
|
|
171
|
+
return v
|
|
172
|
+
def _fix_key(k):
|
|
173
|
+
return k[:-1] if k.endswith('_') else k
|
|
174
|
+
return {_fix_key(k): _resolve_enum(v) for k, v in iter(data)}
|
|
175
|
+
{%- if avro_annotation %}
|
|
176
|
+
|
|
177
|
+
def to_avro_dict(self) -> dict:
|
|
178
|
+
"""
|
|
179
|
+
Converts the dataclass to a dictionary suitable for Avro serialization.
|
|
180
|
+
Handles conversion of Python types to Avro-compatible string representations
|
|
181
|
+
for extended logical types.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
The dictionary representation suitable for Avro serialization.
|
|
185
|
+
"""
|
|
186
|
+
result = self.to_serializer_dict()
|
|
187
|
+
converted = result.copy()
|
|
188
|
+
|
|
189
|
+
# Convert specific fields based on their source types
|
|
190
|
+
{%- for field in fields %}
|
|
191
|
+
{%- set field_key = field.original_name if field.name != field.original_name else field.name %}
|
|
192
|
+
{%- if field.source_type in ['date', 'datetime', 'timestamp', 'time', 'duration', 'decimal', 'uuid', 'int128', 'uint128'] %}
|
|
193
|
+
if '{{ field_key }}' in converted and converted['{{ field_key }}'] is not None:
|
|
194
|
+
value = converted['{{ field_key }}']
|
|
195
|
+
{%- if field.source_type == 'date' %}
|
|
196
|
+
if isinstance(value, datetime.date):
|
|
197
|
+
converted['{{ field_key }}'] = value.isoformat()
|
|
198
|
+
{%- elif field.source_type in ['datetime', 'timestamp'] %}
|
|
199
|
+
if isinstance(value, datetime.datetime):
|
|
200
|
+
converted['{{ field_key }}'] = value.isoformat()
|
|
201
|
+
{%- elif field.source_type == 'time' %}
|
|
202
|
+
if isinstance(value, datetime.time):
|
|
203
|
+
converted['{{ field_key }}'] = value.isoformat()
|
|
204
|
+
{%- elif field.source_type == 'duration' %}
|
|
205
|
+
if isinstance(value, datetime.timedelta):
|
|
206
|
+
converted['{{ field_key }}'] = str(value.total_seconds())
|
|
207
|
+
{%- elif field.source_type == 'decimal' %}
|
|
208
|
+
if isinstance(value, decimal.Decimal):
|
|
209
|
+
converted['{{ field_key }}'] = str(value)
|
|
210
|
+
{%- elif field.source_type == 'uuid' %}
|
|
211
|
+
if isinstance(value, uuid.UUID):
|
|
212
|
+
converted['{{ field_key }}'] = str(value)
|
|
213
|
+
{%- elif field.source_type in ['int128', 'uint128'] %}
|
|
214
|
+
if isinstance(value, int):
|
|
215
|
+
converted['{{ field_key }}'] = str(value)
|
|
216
|
+
{%- endif %}
|
|
217
|
+
{%- endif %}
|
|
218
|
+
{%- endfor %}
|
|
219
|
+
|
|
220
|
+
return converted
|
|
221
|
+
{%- endif %}
|
|
222
|
+
{%- if avro_annotation or dataclasses_json_annotation %}
|
|
223
|
+
|
|
224
|
+
def to_byte_array(self, content_type_string: str) -> bytes:
|
|
225
|
+
"""
|
|
226
|
+
Converts the dataclass to a byte array based on the content type string.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
content_type_string: The content type string to convert the dataclass to.
|
|
230
|
+
Supported content types:
|
|
231
|
+
{%- if dataclasses_json_annotation %}
|
|
232
|
+
'application/json': Encodes the data to JSON format.
|
|
233
|
+
{%- endif %}
|
|
234
|
+
{%- if avro_annotation %}
|
|
235
|
+
'avro/binary': Encodes the data to Avro binary format.
|
|
236
|
+
'application/vnd.apache.avro+avro': Encodes the data to Avro binary format.
|
|
237
|
+
{%- endif %}
|
|
238
|
+
Supported content type extensions:
|
|
239
|
+
'+gzip': Compresses the byte array using gzip, e.g. 'application/json+gzip'.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
The byte array representation of the dataclass.
|
|
243
|
+
"""
|
|
244
|
+
content_type = content_type_string.split(';')[0].strip()
|
|
245
|
+
result = None
|
|
246
|
+
|
|
247
|
+
# Strip compression suffix for base type matching
|
|
248
|
+
base_content_type = content_type.replace('+gzip', '')
|
|
249
|
+
|
|
250
|
+
{%- if avro_annotation %}
|
|
251
|
+
if base_content_type in ['avro/binary', 'application/vnd.apache.avro+avro']:
|
|
252
|
+
# Convert to Avro binary format using the embedded schema
|
|
253
|
+
writer = avro.io.DatumWriter(self.AvroType)
|
|
254
|
+
with io.BytesIO() as stream:
|
|
255
|
+
encoder = avro.io.BinaryEncoder(stream)
|
|
256
|
+
writer.write(self.to_avro_dict(), encoder)
|
|
257
|
+
result = stream.getvalue()
|
|
258
|
+
{%- endif %}
|
|
259
|
+
{%- if dataclasses_json_annotation %}
|
|
260
|
+
if base_content_type == 'application/json':
|
|
261
|
+
#pylint: disable=no-member
|
|
262
|
+
result = self.to_json()
|
|
263
|
+
#pylint: enable=no-member
|
|
264
|
+
{%- endif %}
|
|
265
|
+
|
|
266
|
+
if result is not None and content_type.endswith('+gzip'):
|
|
267
|
+
# Handle string result from to_json()
|
|
268
|
+
if isinstance(result, str):
|
|
269
|
+
result = result.encode('utf-8')
|
|
270
|
+
with io.BytesIO() as stream:
|
|
271
|
+
with gzip.GzipFile(fileobj=stream, mode='wb') as gzip_file:
|
|
272
|
+
gzip_file.write(result)
|
|
273
|
+
result = stream.getvalue()
|
|
274
|
+
|
|
275
|
+
if result is None:
|
|
276
|
+
raise NotImplementedError(f"Unsupported media type {content_type}")
|
|
277
|
+
|
|
278
|
+
return result
|
|
279
|
+
|
|
280
|
+
@classmethod
|
|
281
|
+
def from_data(cls, data: typing.Any, content_type_string: typing.Optional[str] = None) -> typing.Optional['{{ class_name }}']:
|
|
282
|
+
"""
|
|
283
|
+
Converts the data to a dataclass based on the content type string.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
data: The data to convert to a dataclass.
|
|
287
|
+
content_type_string: The content type string to convert the data to.
|
|
288
|
+
Supported content types:
|
|
289
|
+
{%- if dataclasses_json_annotation %}
|
|
290
|
+
'application/json': Attempts to decode the data from JSON encoded format.
|
|
291
|
+
{%- endif %}
|
|
292
|
+
{%- if avro_annotation %}
|
|
293
|
+
'avro/binary': Attempts to decode the data from Avro binary format.
|
|
294
|
+
'application/vnd.apache.avro+avro': Attempts to decode the data from Avro binary format.
|
|
295
|
+
{%- endif %}
|
|
296
|
+
Supported content type extensions:
|
|
297
|
+
'+gzip': First decompresses the data using gzip, e.g. 'application/json+gzip'.
|
|
298
|
+
Returns:
|
|
299
|
+
The dataclass representation of the data.
|
|
300
|
+
"""
|
|
301
|
+
if data is None:
|
|
302
|
+
return None
|
|
303
|
+
if isinstance(data, cls):
|
|
304
|
+
return data
|
|
305
|
+
if isinstance(data, dict):
|
|
306
|
+
return cls.from_serializer_dict(data)
|
|
307
|
+
|
|
308
|
+
content_type = (content_type_string or 'application/octet-stream').split(';')[0].strip()
|
|
309
|
+
|
|
310
|
+
if content_type.endswith('+gzip'):
|
|
311
|
+
if isinstance(data, (bytes, io.BytesIO)):
|
|
312
|
+
stream = io.BytesIO(data) if isinstance(data, bytes) else data
|
|
313
|
+
else:
|
|
314
|
+
raise NotImplementedError('Data is not of a supported type for gzip decompression')
|
|
315
|
+
with gzip.GzipFile(fileobj=stream, mode='rb') as gzip_file:
|
|
316
|
+
data = gzip_file.read()
|
|
317
|
+
|
|
318
|
+
# Strip compression suffix for base type matching
|
|
319
|
+
base_content_type = content_type.replace('+gzip', '')
|
|
320
|
+
|
|
321
|
+
{%- if avro_annotation %}
|
|
322
|
+
if base_content_type in ['avro/binary', 'application/vnd.apache.avro+avro']:
|
|
323
|
+
if isinstance(data, bytes):
|
|
324
|
+
# Decode from Avro binary format using the embedded schema
|
|
325
|
+
reader = avro.io.DatumReader(cls.AvroType)
|
|
326
|
+
with io.BytesIO(data) as stream:
|
|
327
|
+
decoder = avro.io.BinaryDecoder(stream)
|
|
328
|
+
_record = reader.read(decoder)
|
|
329
|
+
return {{ class_name }}.from_avro_dict(_record)
|
|
330
|
+
else:
|
|
331
|
+
raise NotImplementedError('Data is not of a supported type for Avro deserialization')
|
|
332
|
+
{%- endif %}
|
|
333
|
+
{%- if dataclasses_json_annotation %}
|
|
334
|
+
if base_content_type == 'application/json':
|
|
335
|
+
if isinstance(data, (bytes, str)):
|
|
336
|
+
data_str = data.decode('utf-8') if isinstance(data, bytes) else data
|
|
337
|
+
_record = json.loads(data_str)
|
|
338
|
+
{%- for field in fields %}
|
|
339
|
+
{%- if field.name != field.original_name %}
|
|
340
|
+
if '{{ field.original_name }}' in _record:
|
|
341
|
+
_record['{{ field.name }}'] = _record.pop('{{ field.original_name }}')
|
|
342
|
+
{%- endif %}
|
|
343
|
+
{%- endfor %}
|
|
344
|
+
return {{ class_name }}.from_serializer_dict(_record)
|
|
345
|
+
else:
|
|
346
|
+
raise NotImplementedError('Data is not of a supported type for JSON deserialization')
|
|
347
|
+
{%- endif %}
|
|
348
|
+
raise NotImplementedError(f'Unsupported media type {content_type}')
|
|
349
|
+
{%- endif %}
|
|
350
|
+
|
|
351
|
+
@classmethod
|
|
352
|
+
def create_instance(cls) -> '{{ class_name }}':
|
|
353
|
+
"""
|
|
354
|
+
Creates an instance of the dataclass with test values.
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
An instance of the dataclass.
|
|
358
|
+
"""
|
|
359
|
+
return cls(
|
|
360
|
+
{%- for field in fields %}
|
|
361
|
+
{{ field.name }}={{ field.test_value }}{{ "," if not loop.last else "" }}
|
|
362
|
+
{%- endfor %}
|
|
363
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class {{ class_name }}(Enum):
|
|
5
|
+
"""
|
|
6
|
+
{{ docstring }}
|
|
7
|
+
"""
|
|
8
|
+
{%- for symbol in symbols %}
|
|
9
|
+
{{ symbol }} = '{{ symbol }}'
|
|
10
|
+
{%- endfor %}
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def from_ordinal(cls, ordinal: int | str) -> '{{ class_name }}':
|
|
14
|
+
"""
|
|
15
|
+
Get enum member by ordinal
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
ordinal (int | str): The ordinal of the enum member. This can be an integer or a string representation of an integer.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
The enum member corresponding to the ordinal.
|
|
22
|
+
"""
|
|
23
|
+
if ordinal is None:
|
|
24
|
+
raise ValueError("ordinal must not be None")
|
|
25
|
+
if isinstance(ordinal, str) and ordinal.isdigit():
|
|
26
|
+
ordinal = int(ordinal)
|
|
27
|
+
members = list(cls)
|
|
28
|
+
if 0 <= int(ordinal) < len(members):
|
|
29
|
+
return members[ordinal]
|
|
30
|
+
else:
|
|
31
|
+
raise IndexError("Ordinal out of range for enum")
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def to_ordinal(cls, member: '{{ class_name }}') -> int:
|
|
35
|
+
"""
|
|
36
|
+
Get enum ordinal
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
member ({{ class_name }}): The enum member to get the ordinal of.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
The ordinal of the enum member.
|
|
43
|
+
"""
|
|
44
|
+
members = list(cls)
|
|
45
|
+
return members.index(member)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
""" {{ class_name }} type alias. """
|
|
2
|
+
|
|
3
|
+
# pylint: disable=line-too-long
|
|
4
|
+
from typing import TypeAlias
|
|
5
|
+
{%- for import_type in import_types %}
|
|
6
|
+
{%- if import_type.startswith('datetime.') %}
|
|
7
|
+
import datetime
|
|
8
|
+
{%- elif import_type == 'decimal.Decimal' %}
|
|
9
|
+
import decimal
|
|
10
|
+
{%- elif import_type == 'uuid.UUID' %}
|
|
11
|
+
import uuid
|
|
12
|
+
{%- elif import_type.startswith(base_package + '.') %}
|
|
13
|
+
from {{ '.'.join(import_type.split('.')[:-1]) }} import {{ import_type.split('.')[-1] }}
|
|
14
|
+
{%- endif %}
|
|
15
|
+
{%- endfor %}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
{{ class_name }}: TypeAlias = dict[str, {{ values_type }}]
|
|
19
|
+
"""
|
|
20
|
+
{{ docstring }}
|
|
21
|
+
"""
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "{{ package_name }}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A package for handling JSON Structure schema data"
|
|
5
|
+
authors = ["Your Name"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
packages = [ { include = "{{ package_name|replace('-','_')|lower }}", from="src" } ]
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
python = "<4.0,>=3.10"
|
|
11
|
+
{%- if dataclasses_json_annotation %}
|
|
12
|
+
dataclasses-json = "^0.6.7"
|
|
13
|
+
{%- endif %}
|
|
14
|
+
{%- if avro_annotation %}
|
|
15
|
+
avro-python3 = "^1.10.2"
|
|
16
|
+
{%- endif %}
|
|
17
|
+
|
|
18
|
+
[tool.poetry.dev-dependencies]
|
|
19
|
+
pylint = "^3.2.3"
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["poetry-core>=1.0.0"]
|
|
23
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test case for {{ class_name }}
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import unittest
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '../src'.replace('/', os.sep))))
|
|
10
|
+
|
|
11
|
+
from {{ package_name | lower }} import {{ class_name }}
|
|
12
|
+
|
|
13
|
+
{%- for import_type in import_types if import_type not in ['decimal.Decimal', 'datetime.datetime', 'datetime.date', 'datetime.time', 'datetime.timedelta', 'uuid.UUID'] %}
|
|
14
|
+
{%- set import_type_name = import_type.split('.')[-1] %}
|
|
15
|
+
{%- set import_package_name = '.'.join(import_type.split('.')[:-1]) | lower %}
|
|
16
|
+
{%- if import_package_name %}
|
|
17
|
+
from {{ import_package_name }} import {{ import_type_name }}
|
|
18
|
+
{%- endif -%}
|
|
19
|
+
{%- endfor %}
|
|
20
|
+
{%- for import_type in import_types if import_type in ['datetime.datetime', 'datetime.date', 'datetime.time', 'datetime.timedelta'] %}
|
|
21
|
+
{%- if loop.first %}
|
|
22
|
+
import datetime
|
|
23
|
+
{%- endif %}
|
|
24
|
+
{%- endfor %}
|
|
25
|
+
{%- for import_type in import_types if import_type == 'decimal.Decimal' %}
|
|
26
|
+
{%- if loop.first %}
|
|
27
|
+
import decimal
|
|
28
|
+
{%- endif %}
|
|
29
|
+
{%- endfor %}
|
|
30
|
+
{%- for import_type in import_types if import_type == 'uuid.UUID' %}
|
|
31
|
+
{%- if loop.first %}
|
|
32
|
+
import uuid
|
|
33
|
+
{%- endif %}
|
|
34
|
+
{%- endfor %}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class {{ test_class_name }}(unittest.TestCase):
|
|
38
|
+
"""
|
|
39
|
+
Test case for {{ class_name }}
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def setUp(self):
|
|
43
|
+
"""
|
|
44
|
+
Set up test case
|
|
45
|
+
"""
|
|
46
|
+
self.instance = {{ test_class_name }}.create_instance()
|
|
47
|
+
|
|
48
|
+
@staticmethod
|
|
49
|
+
def create_instance():
|
|
50
|
+
"""
|
|
51
|
+
Create instance of {{ class_name }} for testing
|
|
52
|
+
"""
|
|
53
|
+
instance = {{ class_name }}(
|
|
54
|
+
{%- for field in fields %}
|
|
55
|
+
{{ field.name }}={{ field.test_value }}{%- if not loop.last %},{%- endif %}
|
|
56
|
+
{%- endfor %}
|
|
57
|
+
)
|
|
58
|
+
return instance
|
|
59
|
+
|
|
60
|
+
{% for field in fields %}
|
|
61
|
+
def test_{{ field.name }}_property(self):
|
|
62
|
+
"""
|
|
63
|
+
Test {{ field.name }} property
|
|
64
|
+
"""
|
|
65
|
+
test_value = {{ field.test_value }}
|
|
66
|
+
self.instance.{{ field.name }} = test_value
|
|
67
|
+
self.assertEqual(self.instance.{{ field.name }}, test_value)
|
|
68
|
+
{% endfor %}
|
|
69
|
+
|
|
70
|
+
{%- if avro_annotation %}
|
|
71
|
+
def test_to_byte_array_avro(self):
|
|
72
|
+
"""
|
|
73
|
+
Test to_byte_array method with avro media type
|
|
74
|
+
"""
|
|
75
|
+
media_type = "application/vnd.apache.avro+avro"
|
|
76
|
+
bytes_data = self.instance.to_byte_array(media_type)
|
|
77
|
+
new_instance = {{ class_name }}.from_data(bytes_data, media_type)
|
|
78
|
+
bytes_data2 = new_instance.to_byte_array(media_type)
|
|
79
|
+
self.assertEqual(bytes_data, bytes_data2)
|
|
80
|
+
{%- endif %}
|
|
81
|
+
|
|
82
|
+
{%- if dataclasses_json_annotation %}
|
|
83
|
+
def test_to_byte_array_json(self):
|
|
84
|
+
"""
|
|
85
|
+
Test to_byte_array method with json media type
|
|
86
|
+
"""
|
|
87
|
+
media_type = "application/json"
|
|
88
|
+
bytes_data = self.instance.to_byte_array(media_type)
|
|
89
|
+
new_instance = {{ class_name }}.from_data(bytes_data, media_type)
|
|
90
|
+
bytes_data2 = new_instance.to_byte_array(media_type)
|
|
91
|
+
self.assertEqual(bytes_data, bytes_data2)
|
|
92
|
+
|
|
93
|
+
def test_to_json(self):
|
|
94
|
+
"""
|
|
95
|
+
Test to_json method
|
|
96
|
+
"""
|
|
97
|
+
json_data = self.instance.to_json()
|
|
98
|
+
new_instance = {{ class_name }}.from_json(json_data)
|
|
99
|
+
json_data2 = new_instance.to_json()
|
|
100
|
+
self.assertEqual(json_data, json_data2)
|
|
101
|
+
{%- endif %}
|
|
102
|
+
|
|
103
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '../src'.replace('/', os.sep))))
|
|
6
|
+
|
|
7
|
+
from {{ package_name | lower }} import {{ class_name }}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class {{ test_class_name }}(unittest.TestCase):
|
|
11
|
+
"""
|
|
12
|
+
Test case for {{ class_name }}
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def setUp(self):
|
|
16
|
+
"""
|
|
17
|
+
Setup test
|
|
18
|
+
"""
|
|
19
|
+
self.instance = {{ test_class_name }}.create_instance()
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def create_instance():
|
|
23
|
+
"""
|
|
24
|
+
Create instance of {{ class_name }}
|
|
25
|
+
"""
|
|
26
|
+
return {{ class_name }}.{{ symbols[0] }}
|
|
27
|
+
|
|
28
|
+
def test_enum_values(self):
|
|
29
|
+
"""
|
|
30
|
+
Test that all enum values are defined
|
|
31
|
+
"""
|
|
32
|
+
{%- for symbol in symbols %}
|
|
33
|
+
self.assertEqual({{ class_name }}.{{ symbol }}.value, "{{ symbol }}")
|
|
34
|
+
{%- endfor %}
|