• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aas-core-works / aas-core-codegen / 21742872372

06 Feb 2026 07:46AM UTC coverage: 82.428% (+0.2%) from 82.275%
21742872372

Pull #580

github

web-flow
Merge 2147a4a2e into b8779a4cd
Pull Request #580: Generate unit tests for Python

344 of 349 new or added lines in 15 files covered. (98.57%)

28980 of 35158 relevant lines covered (82.43%)

3.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.91
/aas_core_codegen/python/main.py
1
"""Generate Python code to handle AAS models based on the meta-model."""
2
import pathlib
4✔
3
from typing import TextIO, Sequence, Callable, Tuple, Optional, List
4✔
4

5
from aas_core_codegen import specific_implementations, run, intermediate
4✔
6
from aas_core_codegen.common import Error
4✔
7
from aas_core_codegen.python import (
4✔
8
    common as python_common,
9
    lib as python_lib,
10
    tests as python_tests,
11
)
12

13

14
def execute(context: run.Context, stdout: TextIO, stderr: TextIO) -> int:
4✔
15
    """Generate the code."""
16
    verified_ir_table, errors = python_lib.verify_for_types(
4✔
17
        symbol_table=context.symbol_table
18
    )
19

20
    if errors is not None:
4✔
21
        run.write_error_report(
×
22
            message=f"Failed to verify the intermediate symbol table "
23
            f"for generation of Python code"
24
            f"based on {context.model_path}",
25
            errors=[context.lineno_columner.error_message(error) for error in errors],
26
            stderr=stderr,
27
        )
28
        return 1
×
29

30
    assert verified_ir_table is not None
4✔
31

32
    unsupported_contracts_errors = (
4✔
33
        intermediate.errors_if_contracts_for_functions_or_methods_defined(
34
            verified_ir_table
35
        )
36
    )
37
    if unsupported_contracts_errors is not None:
4✔
38
        run.write_error_report(
×
39
            message=f"We do not support pre and post-conditions and snapshots "
40
            f"at the moment. Please notify the developers if you need this "
41
            f"feature (based on meta-model {context.model_path})",
42
            errors=[
43
                context.lineno_columner.error_message(error)
44
                for error in unsupported_contracts_errors
45
            ],
46
            stderr=stderr,
47
        )
48
        return 1
×
49

50
    unsupported_methods_errors = (
4✔
51
        intermediate.errors_if_non_implementation_specific_methods(verified_ir_table)
52
    )
53
    if unsupported_methods_errors is not None:
4✔
54
        run.write_error_report(
×
55
            message=f"We added some support for understood methods already and keep "
56
            f"maintaining it as it is only a matter of time when we will "
57
            f"introduce their transpilation. Introducing them after the fact "
58
            f"would have been much more difficult.\n"
59
            f"\n"
60
            f"At the given moment, however, we deliberately focus only on "
61
            f"implementation-specific methods. "
62
            f"(based on meta-model {context.model_path})",
63
            errors=[
64
                context.lineno_columner.error_message(error)
65
                for error in unsupported_methods_errors
66
            ],
67
            stderr=stderr,
68
        )
69
        return 1
×
70

71
    aas_module_key = specific_implementations.ImplementationKey(
4✔
72
        "qualified_module_name.txt"
73
    )
74

75
    aas_module_text = context.spec_impls.get(aas_module_key, None)
4✔
76
    if aas_module_text is None:
4✔
77
        stderr.write(
×
78
            f"The snippet with the qualified module name is missing: {aas_module_key}\n"
79
        )
80
        return 1
×
81

82
    if not python_common.QUALIFIED_MODULE_NAME_RE.fullmatch(aas_module_text):
4✔
83
        stderr.write(
×
84
            f"The text from the snippet {aas_module_key} "
85
            f"is not a valid qualified module name: {aas_module_text!r}\n"
86
        )
87
        return 1
×
88

89
    aas_module = python_common.QualifiedModuleName(aas_module_text)
4✔
90

91
    verify_errors = python_lib.verify_for_verification(
4✔
92
        spec_impls=context.spec_impls,
93
        verification_functions=verified_ir_table.verification_functions,
94
    )
95

96
    if verify_errors is not None:
4✔
97
        run.write_error_report(
×
98
            message="Failed to verify the Python-specific structures",
99
            errors=verify_errors,
100
            stderr=stderr,
101
        )
102
        return 1
×
103

104
    aas_module_rel_path = pathlib.Path(aas_module.replace(".", "/"))
4✔
105
    assert not aas_module_rel_path.is_absolute()
4✔
106

107
    tests_rel_path = pathlib.Path("dev/tests")
4✔
108

109
    rel_paths_generators: Sequence[
4✔
110
        Tuple[pathlib.Path, Callable[[], Tuple[Optional[str], Optional[List[Error]]]]]
111
    ] = [
112
        (
113
            aas_module_rel_path / "common.py",
114
            lambda: (python_lib.generate_common(aas_module=aas_module), None),
115
        ),
116
        (
117
            aas_module_rel_path / "constants.py",
118
            lambda: python_lib.generate_constants(
119
                symbol_table=context.symbol_table, aas_module=aas_module
120
            ),
121
        ),
122
        (
123
            aas_module_rel_path / "jsonization.py",
124
            lambda: python_lib.generate_jsonization(
125
                symbol_table=context.symbol_table,
126
                aas_module=aas_module,
127
                spec_impls=context.spec_impls,
128
            ),
129
        ),
130
        (
131
            aas_module_rel_path / "stringification.py",
132
            lambda: python_lib.generate_stringification(
133
                symbol_table=context.symbol_table, aas_module=aas_module
134
            ),
135
        ),
136
        (
137
            aas_module_rel_path / "types.py",
138
            lambda: python_lib.generate_types(
139
                symbol_table=verified_ir_table,
140
                aas_module=aas_module,
141
                spec_impls=context.spec_impls,
142
            ),
143
        ),
144
        (
145
            aas_module_rel_path / "verification.py",
146
            lambda: python_lib.generate_verification(
147
                symbol_table=verified_ir_table,
148
                aas_module=aas_module,
149
                spec_impls=context.spec_impls,
150
            ),
151
        ),
152
        (
153
            aas_module_rel_path / "xmlization.py",
154
            lambda: python_lib.generate_xmlization(
155
                symbol_table=context.symbol_table,
156
                aas_module=aas_module,
157
                spec_impls=context.spec_impls,
158
            ),
159
        ),
160
        (
161
            tests_rel_path / "common.py",
162
            lambda: (python_tests.generate_common(aas_module=aas_module), None),
163
        ),
164
        (
165
            tests_rel_path / "common_jsonization.py",
166
            lambda: (
167
                python_tests.generate_common_jsonization(
168
                    symbol_table=context.symbol_table, aas_module=aas_module
169
                ),
170
                None,
171
            ),
172
        ),
173
        (
174
            tests_rel_path / "common_xmlization.py",
175
            lambda: (
176
                python_tests.generate_common_xmlization(aas_module=aas_module),
177
                None,
178
            ),
179
        ),
180
        (
181
            tests_rel_path / "test_descend_and_pass_through_visitor.py",
182
            lambda: (
183
                python_tests.generate_test_descend_and_pass_through_visitor(
184
                    aas_module=aas_module
185
                ),
186
                None,
187
            ),
188
        ),
189
        (
190
            tests_rel_path / "test_descend_once.py",
191
            lambda: (
192
                python_tests.generate_test_descend_once(aas_module=aas_module),
193
                None,
194
            ),
195
        ),
196
        (
197
            tests_rel_path / "test_for_over_X_or_empty.py",
198
            lambda: (
199
                python_tests.generate_test_for_over_x_or_empty(
200
                    symbol_table=context.symbol_table, aas_module=aas_module
201
                ),
202
                None,
203
            ),
204
        ),
205
        (
206
            tests_rel_path / "test_for_x_or_default.py",
207
            lambda: (
208
                python_tests.generate_test_for_x_or_default(
209
                    symbol_table=context.symbol_table, aas_module=aas_module
210
                ),
211
                None,
212
            ),
213
        ),
214
        (
215
            tests_rel_path / "test_jsonization_of_classes_with_descendants.py",
216
            lambda: (
217
                python_tests.generate_test_jsonization_of_classes_with_descendants(
218
                    symbol_table=context.symbol_table, aas_module=aas_module
219
                ),
220
                None,
221
            ),
222
        ),
223
        (
224
            tests_rel_path / "test_jsonization_of_concrete_classes.py",
225
            lambda: (
226
                python_tests.generate_test_jsonization_of_concrete_classes(
227
                    symbol_table=context.symbol_table, aas_module=aas_module
228
                ),
229
                None,
230
            ),
231
        ),
232
        (
233
            tests_rel_path / "test_jsonization_of_enums.py",
234
            lambda: (
235
                python_tests.generate_test_jsonization_of_enums(
236
                    symbol_table=context.symbol_table, aas_module=aas_module
237
                ),
238
                None,
239
            ),
240
        ),
241
        (
242
            tests_rel_path / "test_xmlization_of_classes_with_descendants.py",
243
            lambda: (
244
                python_tests.generate_test_xmlization_of_classes_with_descendants(
245
                    symbol_table=context.symbol_table, aas_module=aas_module
246
                ),
247
                None,
248
            ),
249
        ),
250
        (
251
            tests_rel_path / "test_xmlization_of_concrete_classes.py",
252
            lambda: (
253
                python_tests.generate_test_xmlization_of_concrete_classes(
254
                    symbol_table=context.symbol_table, aas_module=aas_module
255
                ),
256
                None,
257
            ),
258
        ),
259
    ]
260

261
    for rel_path, generator_func in rel_paths_generators:
4✔
262
        assert not rel_path.is_absolute()
4✔
263

264
        code, errors = generator_func()
4✔
265

266
        if errors is not None:
4✔
267
            run.write_error_report(
×
268
                message=f"Failed to generate {rel_path} "
269
                f"based on {context.model_path}",
270
                errors=[
271
                    context.lineno_columner.error_message(error) for error in errors
272
                ],
273
                stderr=stderr,
274
            )
275
            return 1
×
276

277
        assert code is not None
4✔
278

279
        pth = context.output_dir / rel_path
4✔
280

281
        try:
4✔
282
            pth.parent.mkdir(parents=True, exist_ok=True)
4✔
NEW
283
        except Exception as exception:
×
NEW
284
            run.write_error_report(
×
285
                message=f"Failed to create the directory {pth.parent}",
286
                errors=[str(exception)],
287
                stderr=stderr,
288
            )
NEW
289
            return 1
×
290

291
        # NOTE (mristin):
292
        # We add this type check since we had many problems during the development.
293
        assert isinstance(
4✔
294
            code, str
295
        ), f"Unexpected code {code} for the generator for {rel_path}"
296

297
        try:
4✔
298
            pth.write_text(code, encoding="utf-8")
4✔
299
        except Exception as exception:
×
300
            run.write_error_report(
×
301
                message=f"Failed to write to {pth}",
302
                errors=[str(exception)],
303
                stderr=stderr,
304
            )
305
            return 1
×
306

307
    stdout.write(f"Code generated to: {context.output_dir}\n")
4✔
308
    return 0
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc