aas-core-codegen 0.0.16__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.
- aas_core_codegen/__init__.py +6 -0
- aas_core_codegen/__main__.py +8 -0
- aas_core_codegen/common.py +500 -0
- aas_core_codegen/cpp/__init__.py +1 -0
- aas_core_codegen/cpp/aas_common/__init__.py +6 -0
- aas_core_codegen/cpp/aas_common/_generate.py +713 -0
- aas_core_codegen/cpp/common.py +681 -0
- aas_core_codegen/cpp/constants/__init__.py +6 -0
- aas_core_codegen/cpp/constants/_generate.py +568 -0
- aas_core_codegen/cpp/description.py +654 -0
- aas_core_codegen/cpp/enhancing/__init__.py +4 -0
- aas_core_codegen/cpp/enhancing/_generate.py +993 -0
- aas_core_codegen/cpp/iteration/__init__.py +6 -0
- aas_core_codegen/cpp/iteration/_generate.py +2332 -0
- aas_core_codegen/cpp/jsonization/__init__.py +6 -0
- aas_core_codegen/cpp/jsonization/_generate.py +2619 -0
- aas_core_codegen/cpp/main.py +694 -0
- aas_core_codegen/cpp/naming.py +170 -0
- aas_core_codegen/cpp/optionaling.py +557 -0
- aas_core_codegen/cpp/pattern/__init__.py +6 -0
- aas_core_codegen/cpp/pattern/_generate.py +508 -0
- aas_core_codegen/cpp/revm/__init__.py +6 -0
- aas_core_codegen/cpp/revm/_generate.py +1149 -0
- aas_core_codegen/cpp/stringification/__init__.py +5 -0
- aas_core_codegen/cpp/stringification/_generate.py +955 -0
- aas_core_codegen/cpp/structure/__init__.py +7 -0
- aas_core_codegen/cpp/structure/_generate.py +1503 -0
- aas_core_codegen/cpp/transpilation.py +1383 -0
- aas_core_codegen/cpp/unrolling.py +159 -0
- aas_core_codegen/cpp/verification/__init__.py +6 -0
- aas_core_codegen/cpp/verification/_generate.py +3073 -0
- aas_core_codegen/cpp/visitation/__init__.py +6 -0
- aas_core_codegen/cpp/visitation/_generate.py +521 -0
- aas_core_codegen/cpp/wstringification/__init__.py +5 -0
- aas_core_codegen/cpp/wstringification/_generate.py +586 -0
- aas_core_codegen/cpp/xmlization/__init__.py +6 -0
- aas_core_codegen/cpp/xmlization/_generate.py +5373 -0
- aas_core_codegen/cpp/yielding.py +201 -0
- aas_core_codegen/csharp/__init__.py +1 -0
- aas_core_codegen/csharp/common.py +224 -0
- aas_core_codegen/csharp/constants/__init__.py +5 -0
- aas_core_codegen/csharp/constants/_generate.py +409 -0
- aas_core_codegen/csharp/copying/__init__.py +4 -0
- aas_core_codegen/csharp/copying/_generate.py +498 -0
- aas_core_codegen/csharp/description.py +1103 -0
- aas_core_codegen/csharp/enhancing/__init__.py +4 -0
- aas_core_codegen/csharp/enhancing/_generate.py +667 -0
- aas_core_codegen/csharp/jsonization/__init__.py +4 -0
- aas_core_codegen/csharp/jsonization/_generate.py +1630 -0
- aas_core_codegen/csharp/main.py +421 -0
- aas_core_codegen/csharp/naming.py +157 -0
- aas_core_codegen/csharp/reporting/__init__.py +4 -0
- aas_core_codegen/csharp/reporting/_generate.py +266 -0
- aas_core_codegen/csharp/stringification/__init__.py +4 -0
- aas_core_codegen/csharp/stringification/_generate.py +243 -0
- aas_core_codegen/csharp/structure/__init__.py +6 -0
- aas_core_codegen/csharp/structure/_generate.py +1341 -0
- aas_core_codegen/csharp/transpilation.py +990 -0
- aas_core_codegen/csharp/unrolling.py +211 -0
- aas_core_codegen/csharp/verification/__init__.py +6 -0
- aas_core_codegen/csharp/verification/_generate.py +1457 -0
- aas_core_codegen/csharp/visitation/__init__.py +5 -0
- aas_core_codegen/csharp/visitation/_generate.py +579 -0
- aas_core_codegen/csharp/xmlization/__init__.py +4 -0
- aas_core_codegen/csharp/xmlization/_generate.py +1980 -0
- aas_core_codegen/golang/__init__.py +1 -0
- aas_core_codegen/golang/aas_common/__init__.py +4 -0
- aas_core_codegen/golang/aas_common/_generate.py +152 -0
- aas_core_codegen/golang/common.py +303 -0
- aas_core_codegen/golang/constants/__init__.py +5 -0
- aas_core_codegen/golang/constants/_generate.py +339 -0
- aas_core_codegen/golang/description.py +501 -0
- aas_core_codegen/golang/enhancing/__init__.py +4 -0
- aas_core_codegen/golang/enhancing/_generate.py +527 -0
- aas_core_codegen/golang/jsonization/__init__.py +4 -0
- aas_core_codegen/golang/jsonization/_generate.py +1740 -0
- aas_core_codegen/golang/main.py +368 -0
- aas_core_codegen/golang/naming.py +412 -0
- aas_core_codegen/golang/pointering.py +631 -0
- aas_core_codegen/golang/reporting/__init__.py +4 -0
- aas_core_codegen/golang/reporting/_generate.py +218 -0
- aas_core_codegen/golang/stringification/__init__.py +4 -0
- aas_core_codegen/golang/stringification/_generate.py +394 -0
- aas_core_codegen/golang/structure/__init__.py +6 -0
- aas_core_codegen/golang/structure/_generate.py +1493 -0
- aas_core_codegen/golang/transpilation.py +1191 -0
- aas_core_codegen/golang/unrolling.py +159 -0
- aas_core_codegen/golang/verification/__init__.py +6 -0
- aas_core_codegen/golang/verification/_generate.py +1513 -0
- aas_core_codegen/golang/xmlization/__init__.py +4 -0
- aas_core_codegen/golang/xmlization/_generate.py +2507 -0
- aas_core_codegen/infer_for_schema/__init__.py +21 -0
- aas_core_codegen/infer_for_schema/_inline.py +693 -0
- aas_core_codegen/infer_for_schema/_len.py +527 -0
- aas_core_codegen/infer_for_schema/_pattern.py +311 -0
- aas_core_codegen/infer_for_schema/_set.py +394 -0
- aas_core_codegen/infer_for_schema/_stringify.py +201 -0
- aas_core_codegen/infer_for_schema/_types.py +135 -0
- aas_core_codegen/infer_for_schema/match.py +122 -0
- aas_core_codegen/intermediate/__init__.py +78 -0
- aas_core_codegen/intermediate/_hierarchy.py +397 -0
- aas_core_codegen/intermediate/_stringify.py +989 -0
- aas_core_codegen/intermediate/_translate.py +5128 -0
- aas_core_codegen/intermediate/_types.py +2901 -0
- aas_core_codegen/intermediate/construction.py +750 -0
- aas_core_codegen/intermediate/doc.py +344 -0
- aas_core_codegen/intermediate/pattern_verification.py +428 -0
- aas_core_codegen/intermediate/revm.py +985 -0
- aas_core_codegen/intermediate/type_inference.py +2266 -0
- aas_core_codegen/java/__init__.py +1 -0
- aas_core_codegen/java/common.py +197 -0
- aas_core_codegen/java/constants/__init__.py +5 -0
- aas_core_codegen/java/constants/_generate.py +334 -0
- aas_core_codegen/java/copying/__init__.py +4 -0
- aas_core_codegen/java/copying/_generate.py +502 -0
- aas_core_codegen/java/description.py +774 -0
- aas_core_codegen/java/enhancing/__init__.py +4 -0
- aas_core_codegen/java/enhancing/_generate.py +820 -0
- aas_core_codegen/java/generation/__init__.py +5 -0
- aas_core_codegen/java/generation/_generate.py +285 -0
- aas_core_codegen/java/jsonization/__init__.py +4 -0
- aas_core_codegen/java/jsonization/_generate.py +1472 -0
- aas_core_codegen/java/main.py +438 -0
- aas_core_codegen/java/naming.py +187 -0
- aas_core_codegen/java/optional.py +514 -0
- aas_core_codegen/java/reporting/__init__.py +4 -0
- aas_core_codegen/java/reporting/_generate.py +248 -0
- aas_core_codegen/java/stringification/__init__.py +4 -0
- aas_core_codegen/java/stringification/_generate.py +212 -0
- aas_core_codegen/java/structure/__init__.py +6 -0
- aas_core_codegen/java/structure/_generate.py +1767 -0
- aas_core_codegen/java/transpilation.py +1111 -0
- aas_core_codegen/java/verification/__init__.py +6 -0
- aas_core_codegen/java/verification/_generate.py +1536 -0
- aas_core_codegen/java/visitation/__init__.py +5 -0
- aas_core_codegen/java/visitation/_generate.py +689 -0
- aas_core_codegen/java/xmlization/__init__.py +4 -0
- aas_core_codegen/java/xmlization/_generate.py +2274 -0
- aas_core_codegen/jsonld/__init__.py +1 -0
- aas_core_codegen/jsonld/main.py +455 -0
- aas_core_codegen/jsonschema/__init__.py +1 -0
- aas_core_codegen/jsonschema/main.py +982 -0
- aas_core_codegen/main.py +245 -0
- aas_core_codegen/naming.py +133 -0
- aas_core_codegen/opcua/__init__.py +1 -0
- aas_core_codegen/opcua/main.py +1525 -0
- aas_core_codegen/opcua/naming.py +126 -0
- aas_core_codegen/parse/__init__.py +46 -0
- aas_core_codegen/parse/_rules.py +796 -0
- aas_core_codegen/parse/_stringify.py +532 -0
- aas_core_codegen/parse/_translate.py +3940 -0
- aas_core_codegen/parse/_types.py +973 -0
- aas_core_codegen/parse/retree/__init__.py +46 -0
- aas_core_codegen/parse/retree/_fix.py +434 -0
- aas_core_codegen/parse/retree/_parse.py +1143 -0
- aas_core_codegen/parse/retree/_render.py +298 -0
- aas_core_codegen/parse/retree/_stringify.py +199 -0
- aas_core_codegen/parse/retree/_types.py +362 -0
- aas_core_codegen/parse/retree/_visitor.py +70 -0
- aas_core_codegen/parse/tree.py +1303 -0
- aas_core_codegen/protobuf/__init__.py +1 -0
- aas_core_codegen/protobuf/common.py +225 -0
- aas_core_codegen/protobuf/description.py +1102 -0
- aas_core_codegen/protobuf/main.py +115 -0
- aas_core_codegen/protobuf/naming.py +143 -0
- aas_core_codegen/protobuf/structure/__init__.py +6 -0
- aas_core_codegen/protobuf/structure/_generate.py +502 -0
- aas_core_codegen/py.typed +1 -0
- aas_core_codegen/python/__init__.py +1 -0
- aas_core_codegen/python/aas_common/__init__.py +4 -0
- aas_core_codegen/python/aas_common/_generate.py +63 -0
- aas_core_codegen/python/common.py +406 -0
- aas_core_codegen/python/constants/__init__.py +5 -0
- aas_core_codegen/python/constants/_generate.py +377 -0
- aas_core_codegen/python/description.py +508 -0
- aas_core_codegen/python/jsonization/__init__.py +4 -0
- aas_core_codegen/python/jsonization/_generate.py +1391 -0
- aas_core_codegen/python/main.py +323 -0
- aas_core_codegen/python/naming.py +255 -0
- aas_core_codegen/python/stringification/__init__.py +4 -0
- aas_core_codegen/python/stringification/_generate.py +129 -0
- aas_core_codegen/python/structure/__init__.py +6 -0
- aas_core_codegen/python/structure/_generate.py +1801 -0
- aas_core_codegen/python/transpilation.py +958 -0
- aas_core_codegen/python/unrolling.py +156 -0
- aas_core_codegen/python/verification/__init__.py +6 -0
- aas_core_codegen/python/verification/_generate.py +1471 -0
- aas_core_codegen/python/xmlization/__init__.py +4 -0
- aas_core_codegen/python/xmlization/_generate.py +3003 -0
- aas_core_codegen/python_protobuf/__init__.py +1 -0
- aas_core_codegen/python_protobuf/main.py +1424 -0
- aas_core_codegen/python_protobuf/naming.py +85 -0
- aas_core_codegen/rdf_shacl/__init__.py +1 -0
- aas_core_codegen/rdf_shacl/_description.py +351 -0
- aas_core_codegen/rdf_shacl/common.py +206 -0
- aas_core_codegen/rdf_shacl/main.py +114 -0
- aas_core_codegen/rdf_shacl/naming.py +145 -0
- aas_core_codegen/rdf_shacl/rdf.py +435 -0
- aas_core_codegen/rdf_shacl/shacl.py +453 -0
- aas_core_codegen/run.py +124 -0
- aas_core_codegen/smoke/__init__.py +1 -0
- aas_core_codegen/smoke/main.py +219 -0
- aas_core_codegen/specific_implementations.py +72 -0
- aas_core_codegen/stringify.py +333 -0
- aas_core_codegen/typescript/__init__.py +1 -0
- aas_core_codegen/typescript/aas_common/__init__.py +4 -0
- aas_core_codegen/typescript/aas_common/_generate.py +472 -0
- aas_core_codegen/typescript/common.py +340 -0
- aas_core_codegen/typescript/constants/__init__.py +5 -0
- aas_core_codegen/typescript/constants/_generate.py +347 -0
- aas_core_codegen/typescript/description.py +530 -0
- aas_core_codegen/typescript/jsonization/__init__.py +4 -0
- aas_core_codegen/typescript/jsonization/_generate.py +1510 -0
- aas_core_codegen/typescript/main.py +258 -0
- aas_core_codegen/typescript/naming.py +189 -0
- aas_core_codegen/typescript/stringification/__init__.py +4 -0
- aas_core_codegen/typescript/stringification/_generate.py +367 -0
- aas_core_codegen/typescript/structure/__init__.py +6 -0
- aas_core_codegen/typescript/structure/_generate.py +2500 -0
- aas_core_codegen/typescript/transpilation.py +1051 -0
- aas_core_codegen/typescript/unrolling.py +159 -0
- aas_core_codegen/typescript/verification/__init__.py +6 -0
- aas_core_codegen/typescript/verification/_generate.py +1578 -0
- aas_core_codegen/xsd/__init__.py +1 -0
- aas_core_codegen/xsd/main.py +1187 -0
- aas_core_codegen/xsd/naming.py +83 -0
- aas_core_codegen/yielding/__init__.py +1 -0
- aas_core_codegen/yielding/flow.py +139 -0
- aas_core_codegen/yielding/linear.py +754 -0
- aas_core_codegen-0.0.16.dist-info/METADATA +211 -0
- aas_core_codegen-0.0.16.dist-info/RECORD +604 -0
- aas_core_codegen-0.0.16.dist-info/WHEEL +5 -0
- aas_core_codegen-0.0.16.dist-info/entry_points.txt +3 -0
- aas_core_codegen-0.0.16.dist-info/licenses/AUTHORS +9 -0
- aas_core_codegen-0.0.16.dist-info/licenses/LICENSE +23 -0
- aas_core_codegen-0.0.16.dist-info/top_level.txt +2 -0
- dev/continuous_integration/__init__.py +1 -0
- dev/continuous_integration/check_help_in_readme.py +208 -0
- dev/continuous_integration/check_init_and_pyproject_consistent.py +154 -0
- dev/continuous_integration/precommit.py +400 -0
- dev/dev_scripts/__init__.py +1 -0
- dev/dev_scripts/compare_rendered_regexes_against_source_py.py +42 -0
- dev/dev_scripts/copy_to_aas_core3_cpp.py +100 -0
- dev/dev_scripts/copy_to_aas_core3_java.py +90 -0
- dev/dev_scripts/download_latest_aas_core_meta_v3.py +114 -0
- dev/dev_scripts/draw_bipartite_graph_based_on_lines.py +37 -0
- dev/dev_scripts/run_tests_with_rerecord.py +69 -0
- dev/dev_scripts/update_to_aas_core_meta.py +174 -0
- dev/integration_tests/input/jsonschema/boilerplate/main.py +55 -0
- dev/integration_tests/input/meta_model.py +38 -0
- dev/integration_tests/input/python/boilerplate/main.py +153 -0
- dev/integration_tests/main.py +258 -0
- dev/test_data/csharp/test_structure/concrete_class_with_descendants/meta_model.py +15 -0
- dev/test_data/csharp/test_structure/constructor_without_arguments/all_properties_optional/meta_model.py +9 -0
- dev/test_data/csharp/test_structure/constructor_without_arguments/no_properties/meta_model.py +6 -0
- dev/test_data/csharp/test_verification/builtin_functions/len/on_list/meta_model.py +20 -0
- dev/test_data/csharp/test_verification/builtin_functions/len/on_str/meta_model.py +16 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/as_prefix/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/as_suffix/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/in_group_with_quantifier/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/in_the_middle/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/in_union/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/single_utf32_literal/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/with_quantifier_within_group/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/with_quantifier_without_group/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/at_the_beginning/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/at_the_end/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/in_the_middle/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/multiple/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/single/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/single_with_quantifier/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/mixed_with_non_utf32/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/more_than_two_high_surrogates/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/multiple_utf32_ranges/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/multiple_utf32_ranges_mixed_with_non_utf32/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/same_high_surrogate/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/same_high_surrogate_with_quantifier/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/two_high_surrogates/meta_model.py +8 -0
- dev/test_data/intermediate/expected/class/empty/meta_model.py +6 -0
- dev/test_data/intermediate/expected/class/implementation_specific_method/meta_model.py +8 -0
- dev/test_data/intermediate/expected/class/inheritance/meta_model.py +41 -0
- dev/test_data/intermediate/expected/class/methods_with_contracts/meta_model.py +15 -0
- dev/test_data/intermediate/expected/class/only_method_no_property/meta_model.py +7 -0
- dev/test_data/intermediate/expected/class/only_property_no_method/meta_model.py +9 -0
- dev/test_data/intermediate/expected/constant/constant_set/of_enum/meta_model.py +12 -0
- dev/test_data/intermediate/expected/constant/constant_set/of_str/meta_model.py +4 -0
- dev/test_data/intermediate/expected/constant/constant_set/with_description/meta_model.py +6 -0
- dev/test_data/intermediate/expected/constant/constant_set/with_superset_of/meta_model.py +14 -0
- dev/test_data/intermediate/expected/constant/constant_str/only_value/meta_model.py +7 -0
- dev/test_data/intermediate/expected/constant/constant_str/with_description/meta_model.py +5 -0
- dev/test_data/intermediate/expected/documentation/docstring_with_special_characters_in_literal/meta_model.py +20 -0
- dev/test_data/intermediate/expected/documentation/docstring_with_special_characters_outside_literal/meta_model.py +20 -0
- dev/test_data/intermediate/expected/empty/meta_model.py +2 -0
- dev/test_data/intermediate/expected/enumeration/meta_model.py +9 -0
- dev/test_data/intermediate/expected/interface/basic/meta_model.py +14 -0
- dev/test_data/intermediate/expected/interface/empty/meta_model.py +7 -0
- dev/test_data/intermediate/expected/interface/inheritance/meta_model.py +27 -0
- dev/test_data/intermediate/expected/interface/method_signature/meta_model.py +10 -0
- dev/test_data/intermediate/expected/interface/only_constructor/meta_model.py +11 -0
- dev/test_data/intermediate/expected/method/non_mutating/implementation_specific/meta_model.py +12 -0
- dev/test_data/intermediate/expected/method/non_mutating/understood/meta_model.py +11 -0
- dev/test_data/intermediate/expected/type_annotation/atomic/meta_model.py +9 -0
- dev/test_data/intermediate/expected/type_annotation/subscripted/class/meta_model.py +13 -0
- dev/test_data/intermediate/expected/type_annotation/subscripted/primitive/meta_model.py +9 -0
- dev/test_data/intermediate/unexpected/constant_set/of_enum/enumeration_literals_in_subset_outside_of_superset/meta_model.py +20 -0
- dev/test_data/intermediate/unexpected/constant_set/of_enum/invalid_literal/meta_model.py +11 -0
- dev/test_data/intermediate/unexpected/constant_set/of_enum/mismatch_between_enumeration_and_literal/meta_model.py +30 -0
- dev/test_data/intermediate/unexpected/constant_set/of_enum/mismatch_in_enumerations_between_subset_and_superset/meta_model.py +21 -0
- dev/test_data/intermediate/unexpected/constant_set/of_str/literals_in_subset_outside_of_superset/meta_model.py +18 -0
- dev/test_data/intermediate/unexpected/constant_set/of_str/mismatch_between_type_annotation_and_literals/meta_model.py +10 -0
- dev/test_data/intermediate/unexpected/constant_set/of_str/superset_and_subset_mismatch_in_type/meta_model.py +12 -0
- dev/test_data/intermediate/unexpected/constraints/dangling_constraintref/meta_model.py +18 -0
- dev/test_data/intermediate/unexpected/constraints/duplicate_constraints/meta_model.py +20 -0
- dev/test_data/intermediate/unexpected/documentation/unexpected_documentation_elements/meta_model.py +34 -0
- dev/test_data/intermediate/unexpected/invariant/class_invariant_uses_re/meta_model.py +10 -0
- dev/test_data/intermediate/unexpected/invariant/invariant_of_constrained_primitive_uses_re/meta_model.py +7 -0
- dev/test_data/intermediate/unexpected/invariant/unexpected_argument_count_to_len/meta_model.py +20 -0
- dev/test_data/intermediate/unexpected/invariant/unhandled_built_in_function/meta_model.py +16 -0
- dev/test_data/intermediate/unexpected/method_definitions/non_constant_default/meta_model.py +7 -0
- dev/test_data/intermediate/unexpected/optional_constructor_arguments_wo_default/default_non_none/meta_model.py +16 -0
- dev/test_data/intermediate/unexpected/optional_constructor_arguments_wo_default/no_default/meta_model.py +16 -0
- dev/test_data/intermediate/unexpected/properties_and_constructor_arguments_do_not_match/after_inheritance/meta_model.py +58 -0
- dev/test_data/intermediate/unexpected/properties_and_constructor_arguments_do_not_match/type_missmatch/meta_model.py +9 -0
- dev/test_data/intermediate/unexpected/properties_and_constructor_arguments_do_not_match/within_class/meta_model.py +22 -0
- dev/test_data/jsonschema/test_main/regression_when_len_constraints_on_inherited_property/meta_model.py +28 -0
- dev/test_data/opcua/test_main/abstract_and_concrete_classes/meta_model.py +37 -0
- dev/test_data/opcua/test_main/classes_with_invariants/meta_model.py +21 -0
- dev/test_data/opcua/test_main/concrete_class_with_descendant/meta_model.py +27 -0
- dev/test_data/opcua/test_main/concrete_class_with_enum/meta_model.py +21 -0
- dev/test_data/opcua/test_main/concrete_class_with_list_of_instances/meta_model.py +21 -0
- dev/test_data/opcua/test_main/concrete_class_with_primitive_attributes/meta_model.py +41 -0
- dev/test_data/opcua/test_main/concrete_class_with_string/meta_model.py +13 -0
- dev/test_data/opcua/test_main/constrained_primitive/meta_model.py +20 -0
- dev/test_data/opcua/test_main/multiple_inheritance/meta_model.py +25 -0
- dev/test_data/parse/expected/constant/constant_set/of_enum/meta_model.py +12 -0
- dev/test_data/parse/expected/constant/constant_set/of_str/meta_model.py +4 -0
- dev/test_data/parse/expected/constant/constant_set/with_description/meta_model.py +6 -0
- dev/test_data/parse/expected/constant/constant_set/with_superset_of/meta_model.py +14 -0
- dev/test_data/parse/expected/constant/constant_str/only_value/meta_model.py +7 -0
- dev/test_data/parse/expected/constant/constant_str/with_description/meta_model.py +5 -0
- dev/test_data/parse/expected/enum/ok/meta_model.py +15 -0
- dev/test_data/parse/expected/implementation_specific_class/properties_and_methods_in_implementation_specific_class/meta_model.py +17 -0
- dev/test_data/parse/expected/inheritance/basic/meta_model.py +11 -0
- dev/test_data/parse/expected/inheritance/diamond/meta_model.py +26 -0
- dev/test_data/parse/expected/inheritance/inheritance_from_concrete_class/meta_model.py +10 -0
- dev/test_data/parse/expected/invariants/in_relation/meta_model.py +16 -0
- dev/test_data/parse/expected/method/arguments/meta_model.py +7 -0
- dev/test_data/parse/expected/method/basic/meta_model.py +7 -0
- dev/test_data/parse/expected/method/contracts/condition_as_keyword_argument/meta_model.py +8 -0
- dev/test_data/parse/expected/method/contracts/condition_as_positional_argument/meta_model.py +8 -0
- dev/test_data/parse/expected/method/contracts/description_as_keyword_argument/meta_model.py +8 -0
- dev/test_data/parse/expected/method/contracts/description_as_positional_argument/meta_model.py +8 -0
- dev/test_data/parse/expected/method/contracts/multiple_contracts_in_order/meta_model.py +17 -0
- dev/test_data/parse/expected/method/contracts/postcondition/basic/meta_model.py +10 -0
- dev/test_data/parse/expected/method/contracts/postcondition/snapshot/with_keyword_arguments/meta_model.py +9 -0
- dev/test_data/parse/expected/method/contracts/postcondition/snapshot/with_positional_arguments/meta_model.py +9 -0
- dev/test_data/parse/expected/method/default/meta_model.py +9 -0
- dev/test_data/parse/expected/method/description/meta_model.py +8 -0
- dev/test_data/parse/expected/method/is_implementation_specific/meta_model.py +8 -0
- dev/test_data/parse/expected/method/non_mutating/meta_model.py +11 -0
- dev/test_data/parse/expected/method/returns_none/meta_model.py +7 -0
- dev/test_data/parse/expected/method/returns_something/meta_model.py +7 -0
- dev/test_data/parse/expected/single_class/description/meta_model.py +12 -0
- dev/test_data/parse/expected/single_class/empty/meta_model.py +6 -0
- dev/test_data/parse/expected/single_class/property/description/meta_model.py +14 -0
- dev/test_data/parse/expected/single_class/property/mandatory/meta_model.py +6 -0
- dev/test_data/parse/expected/single_class/property/optional/meta_model.py +6 -0
- dev/test_data/parse/expected/single_class/property/recursion_to_entity/meta_model.py +6 -0
- dev/test_data/parse/unexpected/class_decorators/non_name_decorator/meta_model.py +7 -0
- dev/test_data/parse/unexpected/class_decorators/unknown_decorator/meta_model.py +7 -0
- dev/test_data/parse/unexpected/class_definitions/is_abstract_and_implementation_specific/meta_model.py +8 -0
- dev/test_data/parse/unexpected/class_definitions/unexpected_docstring_before_a_method/meta_model.py +11 -0
- dev/test_data/parse/unexpected/class_definitions/unexpected_docstring_for_a_pass/meta_model.py +9 -0
- dev/test_data/parse/unexpected/class_definitions/unexpected_double_description_for_a_property/meta_model.py +10 -0
- dev/test_data/parse/unexpected/class_inheritances/inheriting_from_implementation_specific_parent/meta_model.py +20 -0
- dev/test_data/parse/unexpected/class_inheritances/non_name_super_class/meta_model.py +6 -0
- dev/test_data/parse/unexpected/enum/expression_as_assignment_value/meta_model.py +6 -0
- dev/test_data/parse/unexpected/enum/non_assignment/meta_model.py +8 -0
- dev/test_data/parse/unexpected/enum/non_string_literal/meta_model.py +6 -0
- dev/test_data/parse/unexpected/enum/unexpected_inheritance/meta_model.py +6 -0
- dev/test_data/parse/unexpected/method_contracts/contract/non_lambda_condition/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/contract/non_string_literal_description/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/contract/without_any_arguments/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/contract/without_condition/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/postcondition/OLD_in_postcondition_without_snapshot/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/postcondition/argument_missing_in_function/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/precondition/argument_missing_in_function/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/argument_missing_in_function/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/capture_not_a_lambda/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/invalid_name/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/name_not_a_string_literal/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/without_a_capture/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/without_a_name/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_decorators/non_mutating/non_mutating_constructor/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_decorators/non_mutating/non_mutating_verification_function/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_decorators/non_name_decorator/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_decorators/unknown_call_decorator/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_decorators/unknown_name_decorator/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_definitions/argument_with_final/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/argument_without_a_type_annotation/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/default_for_self/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/dunder/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/init_with_return_type/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/with_keyword_only_arguments/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/with_positional_arguments/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/with_type_annotation_for_self/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/with_variable_arguments/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/with_variable_keyword_arguments/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/without_arguments/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/without_self/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/without_type_annotation_for_result/meta_model.py +7 -0
- dev/test_data/parse/unexpected/property_definitions/final_without_subscript/meta_model.py +6 -0
- dev/test_data/parse/unexpected/property_definitions/nested_final/meta_model.py +6 -0
- dev/test_data/parse/unexpected/property_definitions/non_simple/meta_model.py +6 -0
- dev/test_data/parse/unexpected/property_definitions/unexpected_assignment/meta_model.py +6 -0
- dev/test_data/parse/unexpected/property_definitions/unexpected_non_name_property/meta_model.py +6 -0
- dev/test_data/parse/unexpected/property_definitions/without_type_annotation/meta_model.py +6 -0
- dev/test_data/parse/unexpected/symbol_table/constant_set_with_a_non_set_subset/meta_model.py +6 -0
- dev/test_data/parse/unexpected/symbol_table/dangling_inheritance/meta_model.py +6 -0
- dev/test_data/parse/unexpected/symbol_table/dangling_reference_in_type_annotation_of_a_property/meta_model.py +6 -0
- dev/test_data/parse/unexpected/symbol_table/dangling_reference_in_type_annotation_of_an_argument/meta_model.py +7 -0
- dev/test_data/parse/unexpected/symbol_table/dangling_reference_in_type_annotation_of_constant_set/meta_model.py +6 -0
- dev/test_data/parse/unexpected/symbol_table/dangling_subset_in_constant_set/meta_model.py +4 -0
- dev/test_data/parse/unexpected/symbol_table/inheritance_from_non_class/meta_model.py +10 -0
- dev/test_data/parse_retree/expected/character_set/common_escaping/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/complementing/double_caret/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/complementing/multiple_ranges/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/complementing/suffix_dash/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/escape_first_caret/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/literals_which_need_no_escaping_in_characters_set_but_need_escaping_outside/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/multiple_ranges/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/single_literal/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/single_range/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/unescaped_dash/only_dash/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/unescaped_dash/prefix_dash/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/unescaped_dash/suffix_dash/source.py +1 -0
- dev/test_data/parse_retree/expected/dot/source.py +1 -0
- dev/test_data/parse_retree/expected/empty/group/source.py +1 -0
- dev/test_data/parse_retree/expected/empty/group_in_a_group/source.py +1 -0
- dev/test_data/parse_retree/expected/empty/group_of_union_of_empty_concatenations/source.py +1 -0
- dev/test_data/parse_retree/expected/empty/regex/source.py +1 -0
- dev/test_data/parse_retree/expected/empty/union_of_empty_concatenations/source.py +1 -0
- dev/test_data/parse_retree/expected/escaped_literals/source.py +1 -0
- dev/test_data/parse_retree/expected/formatted_value/at_the_beginning/source.py +1 -0
- dev/test_data/parse_retree/expected/formatted_value/at_the_end/source.py +1 -0
- dev/test_data/parse_retree/expected/formatted_value/in_the_middle/source.py +1 -0
- dev/test_data/parse_retree/expected/formatted_value/single_formatted_value/source.py +1 -0
- dev/test_data/parse_retree/expected/literal/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/at_least_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/at_least_one/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/at_most_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/exactly_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/maybe/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/zero_or_more/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/at_least_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/at_least_one/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/at_most_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/exactly_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/maybe/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/zero_or_more/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/on_a_character_set/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/on_a_formatted_value/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/on_a_group/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/on_a_literal/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/double_end_symbol/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/double_start_symbol/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/end_symbol_in_the_middle/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/only_start_symbol/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/only_stop_symbol/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/start_symbol_at_the_beginning/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/start_symbol_in_the_middle/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/stop_symbol_at_the_end/source.py +1 -0
- dev/test_data/parse_retree/expected/union/of_character_sets/source.py +1 -0
- dev/test_data/parse_retree/expected/union/of_groups/source.py +1 -0
- dev/test_data/parse_retree/expected/union/of_string_literals/source.py +1 -0
- dev/test_data/parse_retree/expected/union/within_group/source.py +1 -0
- dev/test_data/parse_retree/expected/whitespace/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/Uxxxxxxxx_out_of_range/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/only_backslash/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/short_Uxxxxxxxx/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/short_uxxxx/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/short_x/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unexpected_escaping/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/digit/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/not_digit/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/not_whitespace/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/not_word/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/whitespace/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/word/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/Uxxxxxxxx_out_of_range/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/only_backslash/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/short_Uxxxxxxxx/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/short_uxxxx/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/short_x/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unexpected_escaping/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/digit/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/not_digit/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/not_whitespace/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/not_word/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/whitespace/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/word/source.py +1 -0
- dev/test_data/parse_retree/unexpected/invalid_character_range/source.py +1 -0
- dev/test_data/parse_retree/unexpected/invalid_quantifier/at_least_x/source.py +1 -0
- dev/test_data/parse_retree/unexpected/invalid_quantifier/between_3_and_x/source.py +1 -0
- dev/test_data/parse_retree/unexpected/invalid_quantifier/exactly_x/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unhandled_group_directives/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/character_set/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/group/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/group_of_union_of_empty_concatenations/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/quantifier/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/quantifier_with_comma/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/quantifier_with_number_and_comma/source.py +1 -0
- dev/test_data/proto/test_main/expected/abstract_and_concrete_classes/meta_model.py +37 -0
- dev/test_data/proto/test_main/expected/concrete_class_with_descendants/meta_model.py +30 -0
- dev/test_data/proto/test_main/expected/concrete_class_with_enum/meta_model.py +21 -0
- dev/test_data/proto/test_main/expected/concrete_class_with_list_of_instances/meta_model.py +21 -0
- dev/test_data/proto/test_main/expected/concrete_class_with_primitive_attributes/meta_model.py +41 -0
- dev/test_data/python_protobuf/test_main/abstract_and_concrete_classes/expected_output/pbization.py +532 -0
- dev/test_data/python_protobuf/test_main/abstract_and_concrete_classes/meta_model.py +37 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_descendant/expected_output/pbization.py +527 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_descendant/meta_model.py +27 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_enum/expected_output/pbization.py +290 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_enum/meta_model.py +21 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_list_of_instances/expected_output/pbization.py +328 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_list_of_instances/meta_model.py +23 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_primitive_attributes/expected_output/pbization.py +274 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_primitive_attributes/meta_model.py +41 -0
- dev/test_data/rdf_shacl/test_main/expected/regression_when_lang_string_class_is_missing/meta_model.py +29 -0
- dev/test_data/rdf_shacl/test_main/expected/regression_when_len_constraints_on_inherited_property/meta_model.py +27 -0
- dev/test_data/rdf_shacl/test_main/unexpected/regression_len_constraint_on_class_property/meta_model.py +61 -0
- dev/test_data/real_meta_models/aas_core_meta.v3.py +5721 -0
- dev/test_data/smoke/test_main/unexpected/infer_for_schema_error/meta_model.py +12 -0
- dev/test_data/smoke/test_main/unexpected/intermediate_error/meta_model.py +18 -0
- dev/test_data/smoke/test_main/unexpected/parse_error/meta_model.py +5 -0
- dev/test_data/smoke/test_main/unexpected/pattern_verification_unparsable_regex/direct_match/meta_model.py +8 -0
- dev/test_data/smoke/test_main/unexpected/type_error/meta_model.py +18 -0
- dev/tests/__init__.py +1 -0
- dev/tests/common.py +197 -0
- dev/tests/cpp/__init__.py +0 -0
- dev/tests/cpp/test_common.py +32 -0
- dev/tests/cpp/test_main.py +144 -0
- dev/tests/cpp/test_pattern.py +188 -0
- dev/tests/cpp/test_verification.py +189 -0
- dev/tests/cpp/test_yielding.py +225 -0
- dev/tests/csharp/__init__.py +0 -0
- dev/tests/csharp/live_test_main.py +109 -0
- dev/tests/csharp/test_common.py +28 -0
- dev/tests/csharp/test_description.py +684 -0
- dev/tests/csharp/test_main.py +129 -0
- dev/tests/csharp/test_structure.py +93 -0
- dev/tests/csharp/test_verification.py +82 -0
- dev/tests/description.py +29 -0
- dev/tests/golang/__init__.py +0 -0
- dev/tests/golang/test_common.py +78 -0
- dev/tests/golang/test_main.py +128 -0
- dev/tests/infer_for_schema/__init__.py +0 -0
- dev/tests/infer_for_schema/common.py +47 -0
- dev/tests/infer_for_schema/test_len_on_properties.py +955 -0
- dev/tests/infer_for_schema/test_len_on_self.py +580 -0
- dev/tests/infer_for_schema/test_patterns_on_properties.py +686 -0
- dev/tests/infer_for_schema/test_patterns_on_self.py +258 -0
- dev/tests/infer_for_schema/test_property_in_set_of_enumeration_literals.py +600 -0
- dev/tests/infer_for_schema/test_property_in_set_of_primitives.py +549 -0
- dev/tests/intermediate/__init__.py +0 -0
- dev/tests/intermediate/test_constructor.py +719 -0
- dev/tests/intermediate/test_hierarchy.py +221 -0
- dev/tests/intermediate/test_revm.py +134 -0
- dev/tests/intermediate/test_translate.py +337 -0
- dev/tests/intermediate/test_type_inference.py +333 -0
- dev/tests/intermediate/test_types.py +169 -0
- dev/tests/java/__init__.py +0 -0
- dev/tests/java/test_common.py +20 -0
- dev/tests/java/test_description.py +128 -0
- dev/tests/java/test_main.py +234 -0
- dev/tests/jsonld_context/test_main.py +79 -0
- dev/tests/opcua/__init__.py +3 -0
- dev/tests/opcua/test_main.py +110 -0
- dev/tests/our_jsonschema/__init__.py +3 -0
- dev/tests/our_jsonschema/test_main.py +232 -0
- dev/tests/parse/__init__.py +0 -0
- dev/tests/parse/test_parse.py +503 -0
- dev/tests/parse/test_retree.py +272 -0
- dev/tests/proto/__init__.py +0 -0
- dev/tests/proto/test_main.py +112 -0
- dev/tests/python/__init__.py +0 -0
- dev/tests/python/test_common.py +124 -0
- dev/tests/python/test_main.py +126 -0
- dev/tests/python/test_xml_playground.py +254 -0
- dev/tests/python_protobuf/__init__.py +0 -0
- dev/tests/python_protobuf/test_main.py +111 -0
- dev/tests/rdf_shacl/__init__.py +0 -0
- dev/tests/rdf_shacl/test_common.py +32 -0
- dev/tests/rdf_shacl/test_description.py +223 -0
- dev/tests/rdf_shacl/test_main.py +194 -0
- dev/tests/smoke/__init__.py +0 -0
- dev/tests/smoke/test_main.py +83 -0
- dev/tests/test_common.py +94 -0
- dev/tests/typescript/__init__.py +0 -0
- dev/tests/typescript/test_common.py +108 -0
- dev/tests/typescript/test_main.py +125 -0
- dev/tests/xsd/__init__.py +0 -0
- dev/tests/xsd/test_main.py +227 -0
- dev/tests/yielding/__init__.py +0 -0
- dev/tests/yielding/test_linear.py +558 -0
|
@@ -0,0 +1,1391 @@
|
|
|
1
|
+
"""Generate Python code for JSON-ization based on the intermediate representation."""
|
|
2
|
+
|
|
3
|
+
import io
|
|
4
|
+
import textwrap
|
|
5
|
+
from typing import Tuple, Optional, List
|
|
6
|
+
|
|
7
|
+
from icontract import ensure, require
|
|
8
|
+
|
|
9
|
+
from aas_core_codegen import intermediate, naming, specific_implementations
|
|
10
|
+
from aas_core_codegen.common import (
|
|
11
|
+
Error,
|
|
12
|
+
Stripped,
|
|
13
|
+
Identifier,
|
|
14
|
+
assert_never,
|
|
15
|
+
indent_but_first_line,
|
|
16
|
+
)
|
|
17
|
+
from aas_core_codegen.python import (
|
|
18
|
+
common as python_common,
|
|
19
|
+
naming as python_naming,
|
|
20
|
+
)
|
|
21
|
+
from aas_core_codegen.python.common import (
|
|
22
|
+
INDENT as I,
|
|
23
|
+
INDENT2 as II,
|
|
24
|
+
INDENT3 as III,
|
|
25
|
+
INDENT4 as IIII,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# region De-serialization
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _generate_bool_from_jsonable() -> Stripped:
|
|
33
|
+
"""Generate the function to decode a ``bool`` from a JSON-able."""
|
|
34
|
+
return Stripped(
|
|
35
|
+
f"""\
|
|
36
|
+
def _bool_from_jsonable(
|
|
37
|
+
{I}jsonable: Jsonable
|
|
38
|
+
) -> bool:
|
|
39
|
+
{I}\"\"\"
|
|
40
|
+
{I}Parse :paramref:`jsonable` as a boolean.
|
|
41
|
+
|
|
42
|
+
{I}:param jsonable: JSON-able structure to be parsed
|
|
43
|
+
{I}:return: parsed boolean
|
|
44
|
+
{I}:raise: :py:class:`DeserializationException` if unexpected :paramref:`jsonable`
|
|
45
|
+
{I}\"\"\"
|
|
46
|
+
{I}if not isinstance(jsonable, bool):
|
|
47
|
+
{II}raise DeserializationException(
|
|
48
|
+
{III}f"Expected a bool, but got: {{type(jsonable)}}"
|
|
49
|
+
{II})
|
|
50
|
+
{I}return jsonable"""
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _generate_int_from_jsonable() -> Stripped:
|
|
55
|
+
"""Generate the function to decode an ``int`` from a JSON-able."""
|
|
56
|
+
return Stripped(
|
|
57
|
+
f"""\
|
|
58
|
+
def _int_from_jsonable(
|
|
59
|
+
{I}jsonable: Jsonable
|
|
60
|
+
) -> int:
|
|
61
|
+
{I}\"\"\"
|
|
62
|
+
{I}Parse :paramref:`jsonable` as an integer.
|
|
63
|
+
|
|
64
|
+
{I}:param jsonable: JSON-able structure to be parsed
|
|
65
|
+
{I}:return: parsed integer
|
|
66
|
+
{I}:raise: :py:class:`DeserializationException` if unexpected :paramref:`jsonable`
|
|
67
|
+
{I}\"\"\"
|
|
68
|
+
{I}if not isinstance(jsonable, int):
|
|
69
|
+
{II}raise DeserializationException(
|
|
70
|
+
{III}f"Expected an int, but got: {{type(jsonable)}}"
|
|
71
|
+
{II})
|
|
72
|
+
{I}return jsonable"""
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _generate_float_from_jsonable() -> Stripped:
|
|
77
|
+
"""Generate the function to decode a ``float`` from a JSON-able."""
|
|
78
|
+
return Stripped(
|
|
79
|
+
f"""\
|
|
80
|
+
def _float_from_jsonable(
|
|
81
|
+
{I}jsonable: Jsonable
|
|
82
|
+
) -> float:
|
|
83
|
+
{I}\"\"\"
|
|
84
|
+
{I}Parse :paramref:`jsonable` as a floating-point number.
|
|
85
|
+
|
|
86
|
+
{I}:param jsonable: JSON-able structure to be parsed
|
|
87
|
+
{I}:return: parsed floating-point number
|
|
88
|
+
{I}:raise: :py:class:`DeserializationException` if unexpected :paramref:`jsonable`
|
|
89
|
+
{I}\"\"\"
|
|
90
|
+
{I}if not isinstance(jsonable, float):
|
|
91
|
+
{II}raise DeserializationException(
|
|
92
|
+
{III}f"Expected a float, but got: {{type(jsonable)}}"
|
|
93
|
+
{II})
|
|
94
|
+
{I}return jsonable"""
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _generate_str_from_jsonable() -> Stripped:
|
|
99
|
+
"""Generate the function to decode a ``str`` from a JSON-able."""
|
|
100
|
+
return Stripped(
|
|
101
|
+
f"""\
|
|
102
|
+
def _str_from_jsonable(
|
|
103
|
+
{I}jsonable: Jsonable
|
|
104
|
+
) -> str:
|
|
105
|
+
{I}\"\"\"
|
|
106
|
+
{I}Parse :paramref:`jsonable` as a string.
|
|
107
|
+
|
|
108
|
+
{I}:param jsonable: JSON-able structure to be parsed
|
|
109
|
+
{I}:return: parsed string
|
|
110
|
+
{I}:raise: :py:class:`DeserializationException` if unexpected :paramref:`jsonable`
|
|
111
|
+
{I}\"\"\"
|
|
112
|
+
{I}if not isinstance(jsonable, str):
|
|
113
|
+
{II}raise DeserializationException(
|
|
114
|
+
{III}f"Expected a str, but got: {{type(jsonable)}}"
|
|
115
|
+
{II})
|
|
116
|
+
{I}return jsonable"""
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _generate_bytes_from_jsonable() -> Stripped:
|
|
121
|
+
"""Generate the function to decode ``bytes`` from a JSON-able."""
|
|
122
|
+
return Stripped(
|
|
123
|
+
f"""\
|
|
124
|
+
def _bytes_from_jsonable(
|
|
125
|
+
{I}jsonable: Jsonable
|
|
126
|
+
) -> bytes:
|
|
127
|
+
{I}\"\"\"
|
|
128
|
+
{I}Decode :paramref:`jsonable` as base64 string to a ``bytearray``.
|
|
129
|
+
|
|
130
|
+
{I}:param jsonable: JSON-able structure to be decoded
|
|
131
|
+
{I}:return: decoded bytearray
|
|
132
|
+
{I}:raise: :py:class:`DeserializationException` if unexpected :paramref:`jsonable`
|
|
133
|
+
{I}\"\"\"
|
|
134
|
+
{I}if not isinstance(jsonable, str):
|
|
135
|
+
{II}raise DeserializationException(
|
|
136
|
+
{III}f"Expected a str, but got: {{type(jsonable)}}"
|
|
137
|
+
{II})
|
|
138
|
+
|
|
139
|
+
{I}return base64.b64decode(
|
|
140
|
+
{II}jsonable.encode('ascii')
|
|
141
|
+
{I})"""
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _generate_enumeration_from_jsonable(
|
|
146
|
+
enumeration: intermediate.Enumeration,
|
|
147
|
+
) -> Stripped:
|
|
148
|
+
"""Generate the deserialization method for an enumeration."""
|
|
149
|
+
enum_name = python_naming.enum_name(identifier=enumeration.name)
|
|
150
|
+
|
|
151
|
+
function_name = python_naming.function_name(
|
|
152
|
+
Identifier(f"{enumeration.name}_from_jsonable")
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
enum_from_str = python_naming.function_name(
|
|
156
|
+
Identifier(f"{enumeration.name}_from_str")
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
return Stripped(
|
|
160
|
+
f"""\
|
|
161
|
+
def {function_name}(
|
|
162
|
+
{I}jsonable: Jsonable
|
|
163
|
+
) -> aas_types.{enum_name}:
|
|
164
|
+
{I}\"\"\"
|
|
165
|
+
{I}Convert the JSON-able structure :paramref:`jsonable` to a literal of
|
|
166
|
+
{I}:py:class:`.types.{enum_name}`.
|
|
167
|
+
|
|
168
|
+
{I}:param jsonable: JSON-able structure to be parsed
|
|
169
|
+
{I}:return: parsed literal
|
|
170
|
+
{I}:raise: :py:class:`.DeserializationException` if unexpected :paramref:`jsonable`
|
|
171
|
+
{I}\"\"\"
|
|
172
|
+
{I}if not isinstance(jsonable, str):
|
|
173
|
+
{II}raise DeserializationException(
|
|
174
|
+
{III}"Expected a str, but got: {{type(jsonable)}}"
|
|
175
|
+
{II})
|
|
176
|
+
|
|
177
|
+
{I}literal = aas_stringification.{enum_from_str}(jsonable)
|
|
178
|
+
{I}if literal is None:
|
|
179
|
+
{II}raise DeserializationException(
|
|
180
|
+
{III}f"Not a valid string representation of "
|
|
181
|
+
{III}f"a literal of {enum_name}: {{jsonable}}"
|
|
182
|
+
{II})
|
|
183
|
+
|
|
184
|
+
{I}return literal"""
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _generate_is_array_like() -> Stripped:
|
|
189
|
+
"""Generate the function to check that the jsonable is an array-like object."""
|
|
190
|
+
return Stripped(
|
|
191
|
+
f'''\
|
|
192
|
+
def _try_to_cast_to_array_like(
|
|
193
|
+
{I}jsonable: Jsonable
|
|
194
|
+
) -> Optional[Iterable[Any]]:
|
|
195
|
+
{I}"""
|
|
196
|
+
{I}Try to cast the ``jsonable`` to something like a JSON array.
|
|
197
|
+
|
|
198
|
+
{I}In particular, we explicitly check that the ``jsonable`` is not a mapping, as we
|
|
199
|
+
{I}do not want to mistake dictionaries (*i.e.* de-serialized JSON objects) for lists.
|
|
200
|
+
|
|
201
|
+
{I}>>> assert _try_to_cast_to_array_like(True) is None
|
|
202
|
+
|
|
203
|
+
{I}>>> assert _try_to_cast_to_array_like(0) is None
|
|
204
|
+
|
|
205
|
+
{I}>>> assert _try_to_cast_to_array_like(2.2) is None
|
|
206
|
+
|
|
207
|
+
{I}>>> assert _try_to_cast_to_array_like("hello") is None
|
|
208
|
+
|
|
209
|
+
{I}>>> assert _try_to_cast_to_array_like(b"hello") is None
|
|
210
|
+
|
|
211
|
+
{I}>>> _try_to_cast_to_array_like([1, 2])
|
|
212
|
+
{I}[1, 2]
|
|
213
|
+
|
|
214
|
+
{I}>>> assert _try_to_cast_to_array_like({{"a": 3}}) is None
|
|
215
|
+
|
|
216
|
+
{I}>>> assert _try_to_cast_to_array_like(collections.OrderedDict()) is None
|
|
217
|
+
|
|
218
|
+
{I}>>> _try_to_cast_to_array_like(range(1, 2))
|
|
219
|
+
{I}range(1, 2)
|
|
220
|
+
|
|
221
|
+
{I}>>> _try_to_cast_to_array_like((1, 2))
|
|
222
|
+
{I}(1, 2)
|
|
223
|
+
|
|
224
|
+
{I}>>> assert _try_to_cast_to_array_like({{1, 2, 3}}) is None
|
|
225
|
+
{I}"""
|
|
226
|
+
{I}if (
|
|
227
|
+
{II}
|
|
228
|
+
{II}not isinstance(jsonable, (str, bytearray, bytes))
|
|
229
|
+
{II}and hasattr(jsonable, "__iter__")
|
|
230
|
+
{II}and not hasattr(jsonable, "keys")
|
|
231
|
+
{II}# NOTE (mristin):
|
|
232
|
+
{II}# There is no easy way to check for sets as opposed to sequence except
|
|
233
|
+
{II}# for checking for direct inheritance. A sequence also inherits from
|
|
234
|
+
{II}# a collection, so both sequences and sets provide ``__contains__`` method.
|
|
235
|
+
{II}#
|
|
236
|
+
{II}# See: https://docs.python.org/3/library/collections.abc.html
|
|
237
|
+
{II}and not isinstance(jsonable, collections.abc.Set)
|
|
238
|
+
{I}):
|
|
239
|
+
{II}return cast(Iterable[Any], jsonable)
|
|
240
|
+
|
|
241
|
+
{I}return None'''
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _generate_dispatch_map_for_abstract_class(
|
|
246
|
+
cls: intermediate.AbstractClass,
|
|
247
|
+
) -> Stripped:
|
|
248
|
+
"""Generate a mapping model type 🠒 de-serialization function."""
|
|
249
|
+
assert len(cls.concrete_descendants) > 0, (
|
|
250
|
+
"Expected an abstract class to have concrete descendants. "
|
|
251
|
+
"Otherwise we do not know how to de-serialize it."
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
mapping_name = python_naming.private_constant_name(
|
|
255
|
+
Identifier(f"{cls.name}_from_jsonable_dispatch")
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
cls_name = python_naming.class_name(cls.name)
|
|
259
|
+
|
|
260
|
+
mapping_writer = io.StringIO()
|
|
261
|
+
mapping_writer.write(
|
|
262
|
+
f"""\
|
|
263
|
+
{mapping_name}: Mapping[
|
|
264
|
+
{I}str,
|
|
265
|
+
{I}Callable[[Jsonable], aas_types.{cls_name}]
|
|
266
|
+
] = {{
|
|
267
|
+
"""
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
for descendant in cls.concrete_descendants:
|
|
271
|
+
function_name_for_descendant = python_naming.function_name(
|
|
272
|
+
Identifier(f"{descendant.name}_from_jsonable")
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
descendant_literal = python_common.string_literal(
|
|
276
|
+
naming.json_model_type(descendant.name)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
mapping_writer.write(
|
|
280
|
+
f"""\
|
|
281
|
+
{I}{descendant_literal}: {function_name_for_descendant},
|
|
282
|
+
"""
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
mapping_writer.write("}")
|
|
286
|
+
|
|
287
|
+
return Stripped(mapping_writer.getvalue())
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@require(lambda cls: len(cls.concrete_descendants) > 0)
|
|
291
|
+
def _generate_dispatch_map_for_concrete_class(
|
|
292
|
+
cls: intermediate.ConcreteClass,
|
|
293
|
+
) -> Stripped:
|
|
294
|
+
"""
|
|
295
|
+
Generate a mapping model type 🠒 de-serialization function.
|
|
296
|
+
|
|
297
|
+
The ``jsonable`` can not be parsed straight-away as we don't know which class
|
|
298
|
+
(the actual one or one of the descendants) needs to be parsed.
|
|
299
|
+
"""
|
|
300
|
+
mapping_name = python_naming.private_constant_name(
|
|
301
|
+
Identifier(f"{cls.name}_from_jsonable_dispatch")
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
cls_name = python_naming.class_name(cls.name)
|
|
305
|
+
|
|
306
|
+
mapping_writer = io.StringIO()
|
|
307
|
+
mapping_writer.write(
|
|
308
|
+
f"""\
|
|
309
|
+
{mapping_name}: Mapping[
|
|
310
|
+
{I}str,
|
|
311
|
+
{I}Callable[[Jsonable], aas_types.{cls_name}]
|
|
312
|
+
] = {{
|
|
313
|
+
"""
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
function_name_for_cls = python_naming.function_name(
|
|
317
|
+
Identifier(f"_{cls.name}_from_jsonable_without_dispatch")
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
cls_literal = python_common.string_literal(naming.json_model_type(cls.name))
|
|
321
|
+
|
|
322
|
+
mapping_writer.write(
|
|
323
|
+
f"""\
|
|
324
|
+
{I}{cls_literal}: {function_name_for_cls},
|
|
325
|
+
"""
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
for descendant in cls.concrete_descendants:
|
|
329
|
+
function_name_for_descendant = python_naming.function_name(
|
|
330
|
+
Identifier(f"{descendant.name}_from_jsonable")
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
descendant_literal = python_common.string_literal(
|
|
334
|
+
naming.json_model_type(descendant.name)
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
mapping_writer.write(
|
|
338
|
+
f"""\
|
|
339
|
+
{I}{descendant_literal}: {function_name_for_descendant},
|
|
340
|
+
"""
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
mapping_writer.write("}")
|
|
344
|
+
|
|
345
|
+
return Stripped(mapping_writer.getvalue())
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
# fmt: off
|
|
349
|
+
@require(
|
|
350
|
+
lambda cls: len(cls.concrete_descendants) > 0,
|
|
351
|
+
"Dispatch is only possible if there are one or more concrete descendants"
|
|
352
|
+
)
|
|
353
|
+
# fmt: on
|
|
354
|
+
def _generate_dispatch_from_jsonable(cls: intermediate.ClassUnion) -> Stripped:
|
|
355
|
+
"""Generate the de-serialization dispatch for an abstract class."""
|
|
356
|
+
function_name = python_naming.function_name(Identifier(f"{cls.name}_from_jsonable"))
|
|
357
|
+
|
|
358
|
+
cls_name = python_naming.class_name(cls.name)
|
|
359
|
+
|
|
360
|
+
mapping_name = python_naming.private_constant_name(
|
|
361
|
+
Identifier(f"{cls.name}_from_jsonable_dispatch")
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
return Stripped(
|
|
365
|
+
f"""\
|
|
366
|
+
def {function_name}(
|
|
367
|
+
{II}jsonable: Jsonable
|
|
368
|
+
) -> aas_types.{cls_name}:
|
|
369
|
+
{I}\"\"\"
|
|
370
|
+
{I}Parse an instance of :py:class:`.types.{cls_name}` from the JSON-able
|
|
371
|
+
{I}structure :paramref:`jsonable`.
|
|
372
|
+
|
|
373
|
+
{I}:param jsonable: structure to be parsed
|
|
374
|
+
{I}:return: Concrete instance of :py:class:`.types.{cls_name}`
|
|
375
|
+
{I}:raise: :py:class:`DeserializationException` if unexpected :paramref:`jsonable`
|
|
376
|
+
{I}\"\"\"
|
|
377
|
+
{I}if not isinstance(jsonable, collections.abc.Mapping):
|
|
378
|
+
{II}raise DeserializationException(
|
|
379
|
+
{III}f"Expected a mapping, but got: {{type(jsonable)}}"
|
|
380
|
+
{II})
|
|
381
|
+
|
|
382
|
+
{I}model_type = jsonable.get("modelType", None)
|
|
383
|
+
{I}if model_type is None:
|
|
384
|
+
{II}raise DeserializationException(
|
|
385
|
+
{III}"Expected the property modelType, but found none"
|
|
386
|
+
{II})
|
|
387
|
+
|
|
388
|
+
{I}if not isinstance(model_type, str):
|
|
389
|
+
{II}raise DeserializationException(
|
|
390
|
+
{III}"Expected the property modelType to be a str, but got: {{type(model_type)}}"
|
|
391
|
+
{II})
|
|
392
|
+
|
|
393
|
+
{I}dispatch = {mapping_name}.get(model_type, None)
|
|
394
|
+
{I}if dispatch is None:
|
|
395
|
+
{II}raise DeserializationException(
|
|
396
|
+
{III}f"Unexpected model type for {cls_name}: {{model_type}}"
|
|
397
|
+
{II})
|
|
398
|
+
|
|
399
|
+
{I}return dispatch(jsonable)"""
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
_PARSE_FUNCTION_BY_PRIMITIVE_TYPE = {
|
|
404
|
+
intermediate.PrimitiveType.BOOL: "_bool_from_jsonable",
|
|
405
|
+
intermediate.PrimitiveType.INT: "_int_from_jsonable",
|
|
406
|
+
intermediate.PrimitiveType.FLOAT: "_float_from_jsonable",
|
|
407
|
+
intermediate.PrimitiveType.STR: "_str_from_jsonable",
|
|
408
|
+
intermediate.PrimitiveType.BYTEARRAY: "_bytes_from_jsonable",
|
|
409
|
+
}
|
|
410
|
+
assert all(
|
|
411
|
+
literal in _PARSE_FUNCTION_BY_PRIMITIVE_TYPE
|
|
412
|
+
for literal in intermediate.PrimitiveType
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def _parse_function_for_atomic_value(
|
|
417
|
+
type_annotation: intermediate.AtomicTypeAnnotation,
|
|
418
|
+
) -> Stripped:
|
|
419
|
+
"""Determine the parse function for deserializing an atomic non-optional value."""
|
|
420
|
+
function_name: str
|
|
421
|
+
|
|
422
|
+
if isinstance(type_annotation, intermediate.PrimitiveTypeAnnotation):
|
|
423
|
+
function_name = _PARSE_FUNCTION_BY_PRIMITIVE_TYPE[type_annotation.a_type]
|
|
424
|
+
|
|
425
|
+
elif isinstance(type_annotation, intermediate.OurTypeAnnotation):
|
|
426
|
+
our_type = type_annotation.our_type
|
|
427
|
+
|
|
428
|
+
if isinstance(
|
|
429
|
+
our_type,
|
|
430
|
+
(
|
|
431
|
+
intermediate.Enumeration,
|
|
432
|
+
intermediate.AbstractClass,
|
|
433
|
+
intermediate.ConcreteClass,
|
|
434
|
+
),
|
|
435
|
+
):
|
|
436
|
+
function_name = python_naming.function_name(
|
|
437
|
+
Identifier(f"{our_type.name}_from_jsonable")
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
elif isinstance(our_type, intermediate.ConstrainedPrimitive):
|
|
441
|
+
function_name = _PARSE_FUNCTION_BY_PRIMITIVE_TYPE[our_type.constrainee]
|
|
442
|
+
|
|
443
|
+
else:
|
|
444
|
+
assert_never(our_type)
|
|
445
|
+
else:
|
|
446
|
+
assert_never(type_annotation)
|
|
447
|
+
|
|
448
|
+
return Stripped(function_name)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _generate_setter(cls: intermediate.ConcreteClass) -> Stripped:
|
|
452
|
+
"""Generate a class which allows us to dispatch de-serialization for properties."""
|
|
453
|
+
methods = [] # type: List[Stripped]
|
|
454
|
+
|
|
455
|
+
init_writer = io.StringIO()
|
|
456
|
+
for i, prop in enumerate(cls.properties):
|
|
457
|
+
prop_name = python_naming.property_name(prop.name)
|
|
458
|
+
prop_type = python_common.generate_type(
|
|
459
|
+
prop.type_annotation, types_module=Identifier("aas_types")
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
# NOTE (mristin, 2022-07-22):
|
|
463
|
+
# We make all the properties optional since we switch over the properties
|
|
464
|
+
# during the de-serialization.
|
|
465
|
+
if not isinstance(prop.type_annotation, intermediate.OptionalTypeAnnotation):
|
|
466
|
+
prop_type = Stripped(f"Optional[{prop_type}]")
|
|
467
|
+
|
|
468
|
+
if i > 0:
|
|
469
|
+
init_writer.write("\n")
|
|
470
|
+
init_writer.write(f"self.{prop_name}: {prop_type} = None")
|
|
471
|
+
|
|
472
|
+
methods.append(
|
|
473
|
+
Stripped(
|
|
474
|
+
f"""\
|
|
475
|
+
def __init__(self) -> None:
|
|
476
|
+
{I}\"\"\"Initialize with all the properties unset.\"\"\"
|
|
477
|
+
{I}{indent_but_first_line(init_writer.getvalue(), I)}"""
|
|
478
|
+
)
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
methods.append(
|
|
482
|
+
Stripped(
|
|
483
|
+
f"""\
|
|
484
|
+
def ignore(self, jsonable: Jsonable) -> None:
|
|
485
|
+
{I}\"\"\"Ignore :paramref:`jsonable` and do not set anything.\"\"\"
|
|
486
|
+
{I}pass"""
|
|
487
|
+
)
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
for i, prop in enumerate(cls.properties):
|
|
491
|
+
prop_name = python_naming.property_name(prop.name)
|
|
492
|
+
|
|
493
|
+
method_name = python_naming.method_name(
|
|
494
|
+
Identifier(f"set_{prop.name}_from_jsonable")
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
type_anno = intermediate.beneath_optional(prop.type_annotation)
|
|
498
|
+
if isinstance(
|
|
499
|
+
type_anno,
|
|
500
|
+
(intermediate.PrimitiveTypeAnnotation, intermediate.OurTypeAnnotation),
|
|
501
|
+
):
|
|
502
|
+
function_name = _parse_function_for_atomic_value(type_anno)
|
|
503
|
+
body = Stripped(
|
|
504
|
+
f"""\
|
|
505
|
+
self.{prop_name} = {function_name}(
|
|
506
|
+
{I}jsonable
|
|
507
|
+
)"""
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
elif isinstance(type_anno, intermediate.ListTypeAnnotation):
|
|
511
|
+
assert not isinstance(
|
|
512
|
+
type_anno.items,
|
|
513
|
+
(intermediate.OptionalTypeAnnotation, intermediate.ListTypeAnnotation),
|
|
514
|
+
), (
|
|
515
|
+
"We chose to implement only a very limited pattern matching; "
|
|
516
|
+
"see intermediate._translate_._verify_only_simple_type_patterns"
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
items_type = python_common.generate_type(
|
|
520
|
+
type_anno.items, types_module=Identifier("aas_types")
|
|
521
|
+
)
|
|
522
|
+
parse_function = _parse_function_for_atomic_value(type_anno.items)
|
|
523
|
+
|
|
524
|
+
body = Stripped(
|
|
525
|
+
f"""\
|
|
526
|
+
array_like = _try_to_cast_to_array_like(jsonable)
|
|
527
|
+
if array_like is None:
|
|
528
|
+
{I}raise DeserializationException(
|
|
529
|
+
{II}f"Expected something array-like, but got: {{type(jsonable)}}"
|
|
530
|
+
{I})
|
|
531
|
+
|
|
532
|
+
items: List[
|
|
533
|
+
{I}{items_type}
|
|
534
|
+
] = []
|
|
535
|
+
for i, jsonable_item in enumerate(array_like):
|
|
536
|
+
{I}try:
|
|
537
|
+
{II}item = {parse_function}(
|
|
538
|
+
{III}jsonable_item
|
|
539
|
+
{II})
|
|
540
|
+
{I}except DeserializationException as exception:
|
|
541
|
+
{II}exception.path._prepend(
|
|
542
|
+
{III}IndexSegment(
|
|
543
|
+
{IIII}array_like,
|
|
544
|
+
{IIII}i
|
|
545
|
+
{III})
|
|
546
|
+
{II})
|
|
547
|
+
{II}raise
|
|
548
|
+
|
|
549
|
+
{I}items.append(item)
|
|
550
|
+
|
|
551
|
+
self.{prop_name} = items"""
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
else:
|
|
555
|
+
assert_never(type_anno)
|
|
556
|
+
|
|
557
|
+
method_writer = io.StringIO()
|
|
558
|
+
method_writer.write(
|
|
559
|
+
f"""\
|
|
560
|
+
def {method_name}(
|
|
561
|
+
{II}self,
|
|
562
|
+
{II}jsonable: Jsonable
|
|
563
|
+
) -> None:
|
|
564
|
+
{I}\"\"\"
|
|
565
|
+
{I}Parse :paramref:`jsonable` as the value of :py:attr:`~{prop_name}`.
|
|
566
|
+
|
|
567
|
+
{I}:param jsonable: input to be parsed
|
|
568
|
+
{I}\"\"\"
|
|
569
|
+
{I}{indent_but_first_line(body, I)}"""
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
methods.append(Stripped(method_writer.getvalue()))
|
|
573
|
+
|
|
574
|
+
cls_name = python_naming.private_class_name(Identifier(f"Setter_for_{cls.name}"))
|
|
575
|
+
|
|
576
|
+
writer = io.StringIO()
|
|
577
|
+
writer.write(
|
|
578
|
+
f"""\
|
|
579
|
+
class {cls_name}:
|
|
580
|
+
{I}\"\"\"Provide de-serialization-setters for properties.\"\"\"
|
|
581
|
+
|
|
582
|
+
"""
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
assert len(methods) > 0, (
|
|
586
|
+
"Expected at least one method, otherwise the newlines are invalid and "
|
|
587
|
+
"have to be fixed"
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
for i, method in enumerate(methods):
|
|
591
|
+
if i > 0:
|
|
592
|
+
writer.write("\n\n")
|
|
593
|
+
writer.write(textwrap.indent(method, I))
|
|
594
|
+
|
|
595
|
+
return Stripped(writer.getvalue())
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def _generate_setter_map(cls: intermediate.ConcreteClass) -> Stripped:
|
|
599
|
+
"""Generate a mapping ``JSON property name`` -> deserialization on a setter."""
|
|
600
|
+
# fmt: off
|
|
601
|
+
assert (
|
|
602
|
+
sorted(
|
|
603
|
+
(arg.name, str(arg.type_annotation))
|
|
604
|
+
for arg in cls.constructor.arguments
|
|
605
|
+
) == sorted(
|
|
606
|
+
(prop.name, str(prop.type_annotation))
|
|
607
|
+
for prop in cls.properties
|
|
608
|
+
)
|
|
609
|
+
), (
|
|
610
|
+
"(mristin, 2022-10-03) We assume that the properties and constructor arguments "
|
|
611
|
+
"are identical at this point. If this is not the case, we have to re-write the "
|
|
612
|
+
"logic substantially! Please contact the developers if you see this."
|
|
613
|
+
)
|
|
614
|
+
# fmt: on
|
|
615
|
+
|
|
616
|
+
identifiers_expressions = [] # type: List[Tuple[Identifier, Stripped]]
|
|
617
|
+
|
|
618
|
+
setter_cls_name = python_naming.private_class_name(
|
|
619
|
+
Identifier(f"Setter_for_{cls.name}")
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
for prop in cls.properties:
|
|
623
|
+
json_identifier = naming.json_property(prop.name)
|
|
624
|
+
method_name = python_naming.method_name(
|
|
625
|
+
Identifier(f"set_{prop.name}_from_jsonable")
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
identifiers_expressions.append(
|
|
629
|
+
(json_identifier, Stripped(f"{setter_cls_name}.{method_name}"))
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
map_name = python_naming.private_constant_name(
|
|
633
|
+
Identifier(f"setter_map_for_{cls.name}")
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
writer = io.StringIO()
|
|
637
|
+
writer.write(
|
|
638
|
+
f"""\
|
|
639
|
+
{map_name}: Mapping[
|
|
640
|
+
{I}str,
|
|
641
|
+
{I}Callable[
|
|
642
|
+
{II}[{setter_cls_name}, Jsonable],
|
|
643
|
+
{II}None
|
|
644
|
+
{I}]
|
|
645
|
+
] = {{
|
|
646
|
+
"""
|
|
647
|
+
)
|
|
648
|
+
for identifier, expression in identifiers_expressions:
|
|
649
|
+
writer.write(
|
|
650
|
+
f"""\
|
|
651
|
+
{I}{python_common.string_literal(identifier)}:
|
|
652
|
+
{II}{indent_but_first_line(expression, II)},
|
|
653
|
+
"""
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
writer.write(
|
|
657
|
+
f"""\
|
|
658
|
+
{I}'modelType':
|
|
659
|
+
{II}{setter_cls_name}.ignore
|
|
660
|
+
"""
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
writer.write("}")
|
|
664
|
+
return Stripped(writer.getvalue())
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def _generate_concrete_class_from_jsonable(
|
|
668
|
+
cls: intermediate.ConcreteClass,
|
|
669
|
+
) -> Stripped:
|
|
670
|
+
"""
|
|
671
|
+
Generate the deserialization function for a concrete class.
|
|
672
|
+
|
|
673
|
+
This function performs no dispatch. If it de-serializes a concrete class with
|
|
674
|
+
concrete descendants, we have to provide a different name. Otherwise, it would
|
|
675
|
+
shadow the name for the dispatch function.
|
|
676
|
+
"""
|
|
677
|
+
# fmt: off
|
|
678
|
+
assert (
|
|
679
|
+
sorted(
|
|
680
|
+
(arg.name, str(arg.type_annotation))
|
|
681
|
+
for arg in cls.constructor.arguments
|
|
682
|
+
) == sorted(
|
|
683
|
+
(prop.name, str(prop.type_annotation))
|
|
684
|
+
for prop in cls.properties
|
|
685
|
+
)
|
|
686
|
+
), (
|
|
687
|
+
"(mristin, 2022-10-03) We assume that the properties and constructor arguments "
|
|
688
|
+
"are identical at this point. If this is not the case, we have to re-write the "
|
|
689
|
+
"logic substantially! Please contact the developers if you see this."
|
|
690
|
+
)
|
|
691
|
+
# fmt: on
|
|
692
|
+
|
|
693
|
+
setter_cls_name = python_naming.private_class_name(
|
|
694
|
+
Identifier(f"Setter_for_{cls.name}")
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
blocks = [
|
|
698
|
+
Stripped(
|
|
699
|
+
f"""\
|
|
700
|
+
if not isinstance(jsonable, collections.abc.Mapping):
|
|
701
|
+
{I}raise DeserializationException(
|
|
702
|
+
{II}f"Expected a mapping, but got: {{type(jsonable)}}"
|
|
703
|
+
{I})"""
|
|
704
|
+
),
|
|
705
|
+
Stripped(f"setter = {setter_cls_name}()"),
|
|
706
|
+
] # type: List[Stripped]
|
|
707
|
+
|
|
708
|
+
# NOTE (mristin):
|
|
709
|
+
# If the class has no concrete descendants, this function will be the sole entry
|
|
710
|
+
# point for the de-serialization. In that case, we have to explicitly check if
|
|
711
|
+
# the ``modelType`` is present — for cases where the serialization rules impose
|
|
712
|
+
# a model type for backwards compatibility even though it is redundant.
|
|
713
|
+
#
|
|
714
|
+
# If the class has concrete descendants, we can only publicly de-serialize it
|
|
715
|
+
# through a dispatching function, which will innately check for model type, so we
|
|
716
|
+
# do not have to repeat the check here.
|
|
717
|
+
if len(cls.concrete_descendants) == 0 and cls.serialization.with_model_type:
|
|
718
|
+
expected_model_type = naming.json_model_type(cls.name)
|
|
719
|
+
blocks.append(
|
|
720
|
+
Stripped(
|
|
721
|
+
f"""\
|
|
722
|
+
model_type = jsonable.get("modelType", None)
|
|
723
|
+
if model_type is None:
|
|
724
|
+
{I}raise DeserializationException(
|
|
725
|
+
{II}"Expected the property modelType, but found none"
|
|
726
|
+
{I})
|
|
727
|
+
|
|
728
|
+
if model_type != {python_common.string_literal(expected_model_type)}:
|
|
729
|
+
{I}raise DeserializationException(
|
|
730
|
+
{II}f"Invalid modelType, expected '{expected_model_type}', "
|
|
731
|
+
{II}f"but got: {{model_type!r}}"
|
|
732
|
+
{I})"""
|
|
733
|
+
)
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
# region Switch on property name
|
|
737
|
+
|
|
738
|
+
map_name = python_naming.private_constant_name(
|
|
739
|
+
Identifier(f"setter_map_for_{cls.name}")
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
blocks.append(
|
|
743
|
+
Stripped(
|
|
744
|
+
f"""\
|
|
745
|
+
for key, jsonable_value in jsonable.items():
|
|
746
|
+
{I}setter_method = (
|
|
747
|
+
{II}{map_name}.get(key)
|
|
748
|
+
{I})
|
|
749
|
+
{I}if setter_method is None:
|
|
750
|
+
{II}raise DeserializationException(
|
|
751
|
+
{III}f"Unexpected property: {{key}}"
|
|
752
|
+
{II})
|
|
753
|
+
|
|
754
|
+
{I}try:
|
|
755
|
+
{II}setter_method(setter, jsonable_value)
|
|
756
|
+
{I}except DeserializationException as exception:
|
|
757
|
+
{II}exception.path._prepend(
|
|
758
|
+
{III}PropertySegment(
|
|
759
|
+
{IIII}jsonable_value,
|
|
760
|
+
{IIII}key
|
|
761
|
+
{III})
|
|
762
|
+
{II})
|
|
763
|
+
{II}raise exception"""
|
|
764
|
+
)
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
# region Check required properties
|
|
768
|
+
|
|
769
|
+
required_checks = [] # type: List[Stripped]
|
|
770
|
+
for i, prop in enumerate(cls.properties):
|
|
771
|
+
if isinstance(prop.type_annotation, intermediate.OptionalTypeAnnotation):
|
|
772
|
+
continue
|
|
773
|
+
|
|
774
|
+
prop_name = python_naming.property_name(prop.name)
|
|
775
|
+
|
|
776
|
+
cause_literal = python_common.string_literal(
|
|
777
|
+
f"The required property {naming.json_property(prop.name)!r} is missing"
|
|
778
|
+
)
|
|
779
|
+
required_checks.append(
|
|
780
|
+
Stripped(
|
|
781
|
+
f"""\
|
|
782
|
+
if setter.{prop_name} is None:
|
|
783
|
+
{I}raise DeserializationException(
|
|
784
|
+
{II}{cause_literal}
|
|
785
|
+
{I})"""
|
|
786
|
+
)
|
|
787
|
+
)
|
|
788
|
+
|
|
789
|
+
if len(required_checks) > 0:
|
|
790
|
+
blocks.append(Stripped("\n\n".join(required_checks)))
|
|
791
|
+
|
|
792
|
+
# endregion
|
|
793
|
+
|
|
794
|
+
# region Pass in arguments to the constructor
|
|
795
|
+
|
|
796
|
+
cls_name = python_naming.class_name(cls.name)
|
|
797
|
+
|
|
798
|
+
if len(cls.constructor.arguments) == 0:
|
|
799
|
+
blocks.append(Stripped(f"return aas_types.{cls_name}()"))
|
|
800
|
+
else:
|
|
801
|
+
init_writer = io.StringIO()
|
|
802
|
+
init_writer.write(f"return aas_types.{cls_name}(\n")
|
|
803
|
+
|
|
804
|
+
for i, arg in enumerate(cls.constructor.arguments):
|
|
805
|
+
prop = cls.properties_by_name[arg.name]
|
|
806
|
+
|
|
807
|
+
prop_name = python_naming.property_name(prop.name)
|
|
808
|
+
|
|
809
|
+
init_writer.write(f"{I}setter.{prop_name}")
|
|
810
|
+
|
|
811
|
+
if i < len(cls.constructor.arguments) - 1:
|
|
812
|
+
init_writer.write(",\n")
|
|
813
|
+
else:
|
|
814
|
+
init_writer.write("\n")
|
|
815
|
+
|
|
816
|
+
init_writer.write(")")
|
|
817
|
+
|
|
818
|
+
blocks.append(Stripped(init_writer.getvalue()))
|
|
819
|
+
# endregion
|
|
820
|
+
|
|
821
|
+
writer = io.StringIO()
|
|
822
|
+
|
|
823
|
+
if len(cls.concrete_descendants) == 0:
|
|
824
|
+
function_name = python_naming.function_name(
|
|
825
|
+
Identifier(f"{cls.name}_from_jsonable")
|
|
826
|
+
)
|
|
827
|
+
else:
|
|
828
|
+
function_name = python_naming.function_name(
|
|
829
|
+
Identifier(f"_{cls.name}_from_jsonable_without_dispatch")
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
docstring_blocks = [
|
|
833
|
+
Stripped(
|
|
834
|
+
f"""\
|
|
835
|
+
Parse an instance of :py:class:`.types.{cls_name}` from the JSON-able
|
|
836
|
+
structure :paramref:`jsonable`."""
|
|
837
|
+
)
|
|
838
|
+
] # type: List[Stripped]
|
|
839
|
+
|
|
840
|
+
if len(cls.concrete_descendants) > 0:
|
|
841
|
+
function_name_with_dispatch = python_naming.function_name(
|
|
842
|
+
Identifier(f"{cls.name}_from_jsonable")
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
docstring_blocks.append(
|
|
846
|
+
Stripped(
|
|
847
|
+
f"""\
|
|
848
|
+
This function performs no dispatch! It is used to parse the properties
|
|
849
|
+
as-are, and already assumes the exact model type. Usually, this function
|
|
850
|
+
is called from within a dispatching function, and you never call it
|
|
851
|
+
directly. If you want to de-serialize an instance of
|
|
852
|
+
:py:class:`.types.{cls_name}`, call
|
|
853
|
+
:py:func:`{function_name_with_dispatch}`."""
|
|
854
|
+
)
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
docstring_blocks.append(
|
|
858
|
+
Stripped(
|
|
859
|
+
f"""\
|
|
860
|
+
:param jsonable: structure to be parsed
|
|
861
|
+
:return: Parsed instance of :py:class:`.types.{cls_name}`
|
|
862
|
+
:raise: :py:class:`DeserializationException` if unexpected :paramref:`jsonable`"""
|
|
863
|
+
)
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
docstring = "\n\n".join(docstring_blocks)
|
|
867
|
+
|
|
868
|
+
writer.write(
|
|
869
|
+
f"""\
|
|
870
|
+
def {function_name}(
|
|
871
|
+
{II}jsonable: Jsonable
|
|
872
|
+
) -> aas_types.{cls_name}:
|
|
873
|
+
{I}\"\"\"
|
|
874
|
+
{I}{indent_but_first_line(docstring, I)}
|
|
875
|
+
{I}\"\"\"
|
|
876
|
+
"""
|
|
877
|
+
)
|
|
878
|
+
|
|
879
|
+
for i, block in enumerate(blocks):
|
|
880
|
+
if i > 0:
|
|
881
|
+
writer.write("\n\n")
|
|
882
|
+
writer.write(textwrap.indent(block, I))
|
|
883
|
+
|
|
884
|
+
return Stripped(writer.getvalue())
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
# endregion
|
|
888
|
+
|
|
889
|
+
# region Serialization
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def _generate_bytes_to_base64_str() -> Stripped:
|
|
893
|
+
"""Generate the function to encode bytes as base64-encoded string."""
|
|
894
|
+
return Stripped(
|
|
895
|
+
f"""\
|
|
896
|
+
def _bytes_to_base64_str(
|
|
897
|
+
{I}value: bytes
|
|
898
|
+
) -> str:
|
|
899
|
+
{I}\"\"\"
|
|
900
|
+
{I}Encode :paramref:`value` as a base64 string.
|
|
901
|
+
|
|
902
|
+
{I}:param value: to be encoded
|
|
903
|
+
{I}:return: encoded :paramref:`value` in base64
|
|
904
|
+
{I}\"\"\"
|
|
905
|
+
{I}# We need to decode as ascii as ``base64.b64encode`` returns bytes,
|
|
906
|
+
{I}# not a string!
|
|
907
|
+
{I}return base64.b64encode(value).decode('ascii')"""
|
|
908
|
+
)
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
def _generate_transform_atomic_value(
|
|
912
|
+
access_expression: str, type_anno: intermediate.AtomicTypeAnnotation
|
|
913
|
+
) -> Stripped:
|
|
914
|
+
"""
|
|
915
|
+
Generate the snippet to transform the ``access_expression``.
|
|
916
|
+
|
|
917
|
+
The ``access_expression`` should either be a name or a member access.
|
|
918
|
+
"""
|
|
919
|
+
if isinstance(type_anno, intermediate.PrimitiveTypeAnnotation) or (
|
|
920
|
+
isinstance(type_anno, intermediate.OurTypeAnnotation)
|
|
921
|
+
and isinstance(type_anno.our_type, intermediate.ConstrainedPrimitive)
|
|
922
|
+
):
|
|
923
|
+
a_type = intermediate.try_primitive_type(type_anno)
|
|
924
|
+
assert a_type is not None
|
|
925
|
+
|
|
926
|
+
if (
|
|
927
|
+
a_type is intermediate.PrimitiveType.BOOL
|
|
928
|
+
or a_type is intermediate.PrimitiveType.INT
|
|
929
|
+
or a_type is intermediate.PrimitiveType.FLOAT
|
|
930
|
+
or a_type is intermediate.PrimitiveType.STR
|
|
931
|
+
):
|
|
932
|
+
return Stripped(f"{access_expression}")
|
|
933
|
+
|
|
934
|
+
elif a_type is intermediate.PrimitiveType.BYTEARRAY:
|
|
935
|
+
return Stripped(f"_bytes_to_base64_str({access_expression})")
|
|
936
|
+
|
|
937
|
+
else:
|
|
938
|
+
assert_never(a_type)
|
|
939
|
+
|
|
940
|
+
elif isinstance(type_anno, intermediate.OurTypeAnnotation):
|
|
941
|
+
if isinstance(type_anno.our_type, intermediate.Enumeration):
|
|
942
|
+
return Stripped(f"{access_expression}.value")
|
|
943
|
+
|
|
944
|
+
elif isinstance(type_anno.our_type, intermediate.ConstrainedPrimitive):
|
|
945
|
+
raise AssertionError("This case should have been handled before.")
|
|
946
|
+
|
|
947
|
+
elif isinstance(
|
|
948
|
+
type_anno.our_type, (intermediate.AbstractClass, intermediate.ConcreteClass)
|
|
949
|
+
):
|
|
950
|
+
return Stripped(f"self.transform({access_expression})")
|
|
951
|
+
|
|
952
|
+
else:
|
|
953
|
+
assert_never(type_anno.our_type)
|
|
954
|
+
|
|
955
|
+
else:
|
|
956
|
+
assert_never(type_anno.our_type)
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
def _generate_transform(cls: intermediate.ConcreteClass) -> Stripped:
|
|
960
|
+
"""Generate the ``transform_X`` method to serialize an instance into a JSON-able."""
|
|
961
|
+
blocks = [
|
|
962
|
+
Stripped("jsonable: MutableMapping[str, MutableJsonable] = dict()")
|
|
963
|
+
] # type: List[Stripped]
|
|
964
|
+
|
|
965
|
+
for prop in cls.properties:
|
|
966
|
+
key_literal = python_common.string_literal(naming.json_property(prop.name))
|
|
967
|
+
prop_name = python_naming.property_name(prop.name)
|
|
968
|
+
|
|
969
|
+
type_anno = intermediate.beneath_optional(prop.type_annotation)
|
|
970
|
+
|
|
971
|
+
block: Stripped
|
|
972
|
+
|
|
973
|
+
if isinstance(
|
|
974
|
+
type_anno,
|
|
975
|
+
(intermediate.PrimitiveTypeAnnotation, intermediate.OurTypeAnnotation),
|
|
976
|
+
):
|
|
977
|
+
transformation_expression = _generate_transform_atomic_value(
|
|
978
|
+
access_expression=Stripped(f"that.{prop_name}"), type_anno=type_anno
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
block = Stripped(f"jsonable[{key_literal}] = {transformation_expression}")
|
|
982
|
+
|
|
983
|
+
# Rudimentary formatting heuristics
|
|
984
|
+
if len(block) > 70:
|
|
985
|
+
block = Stripped(
|
|
986
|
+
f"""\
|
|
987
|
+
jsonable[{key_literal}] = (
|
|
988
|
+
{I}{transformation_expression}
|
|
989
|
+
)"""
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
elif isinstance(type_anno, intermediate.ListTypeAnnotation):
|
|
993
|
+
assert isinstance(
|
|
994
|
+
type_anno.items,
|
|
995
|
+
(intermediate.PrimitiveTypeAnnotation, intermediate.OurTypeAnnotation),
|
|
996
|
+
), (
|
|
997
|
+
"We expect only lists of primitive and our types. Lists of optionals "
|
|
998
|
+
"and nested lists are not handled yet. Please contact the developers."
|
|
999
|
+
)
|
|
1000
|
+
|
|
1001
|
+
transformation_expression = _generate_transform_atomic_value(
|
|
1002
|
+
access_expression=Stripped("item"), type_anno=type_anno.items
|
|
1003
|
+
)
|
|
1004
|
+
|
|
1005
|
+
block = Stripped(
|
|
1006
|
+
f"""\
|
|
1007
|
+
jsonable[{key_literal}] = [
|
|
1008
|
+
{I}{transformation_expression}
|
|
1009
|
+
{I}for item in that.{prop_name}
|
|
1010
|
+
]"""
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
else:
|
|
1014
|
+
assert_never(type_anno)
|
|
1015
|
+
|
|
1016
|
+
if isinstance(prop.type_annotation, intermediate.OptionalTypeAnnotation):
|
|
1017
|
+
block = Stripped(
|
|
1018
|
+
f"""\
|
|
1019
|
+
if that.{prop_name} is not None:
|
|
1020
|
+
{I}{indent_but_first_line(block, I)}"""
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
blocks.append(block)
|
|
1024
|
+
|
|
1025
|
+
if cls.serialization.with_model_type:
|
|
1026
|
+
model_type_literal = python_common.string_literal(
|
|
1027
|
+
naming.json_model_type(cls.name)
|
|
1028
|
+
)
|
|
1029
|
+
blocks.append(Stripped(f"""jsonable["modelType"] = {model_type_literal}"""))
|
|
1030
|
+
|
|
1031
|
+
blocks.append(Stripped("return jsonable"))
|
|
1032
|
+
|
|
1033
|
+
method_name = python_naming.method_name(Identifier(f"transform_{cls.name}"))
|
|
1034
|
+
|
|
1035
|
+
cls_name = python_naming.class_name(cls.name)
|
|
1036
|
+
|
|
1037
|
+
writer = io.StringIO()
|
|
1038
|
+
|
|
1039
|
+
no_self_use = all(
|
|
1040
|
+
(
|
|
1041
|
+
some_type_anno := intermediate.beneath_optional(prop.type_annotation),
|
|
1042
|
+
intermediate.try_primitive_type(some_type_anno) is not None
|
|
1043
|
+
or (
|
|
1044
|
+
isinstance(some_type_anno, intermediate.OurTypeAnnotation)
|
|
1045
|
+
and isinstance(some_type_anno.our_type, intermediate.Enumeration)
|
|
1046
|
+
),
|
|
1047
|
+
)[1]
|
|
1048
|
+
for prop in cls.properties
|
|
1049
|
+
)
|
|
1050
|
+
|
|
1051
|
+
if no_self_use:
|
|
1052
|
+
writer.write("# noinspection PyMethodMayBeStatic\n")
|
|
1053
|
+
|
|
1054
|
+
writer.write(
|
|
1055
|
+
f"""\
|
|
1056
|
+
def {method_name}(
|
|
1057
|
+
{I}self,
|
|
1058
|
+
{I}that: aas_types.{cls_name}
|
|
1059
|
+
) -> MutableJsonable:
|
|
1060
|
+
{I}\"\"\"Serialize :paramref:`that` to a JSON-able representation.\"\"\""""
|
|
1061
|
+
)
|
|
1062
|
+
|
|
1063
|
+
if len(blocks) > 0:
|
|
1064
|
+
writer.write("\n")
|
|
1065
|
+
|
|
1066
|
+
for i, block in enumerate(blocks):
|
|
1067
|
+
if i > 0:
|
|
1068
|
+
writer.write("\n\n")
|
|
1069
|
+
writer.write(textwrap.indent(block, I))
|
|
1070
|
+
|
|
1071
|
+
return Stripped(writer.getvalue())
|
|
1072
|
+
|
|
1073
|
+
|
|
1074
|
+
def _generate_transformer(symbol_table: intermediate.SymbolTable) -> Stripped:
|
|
1075
|
+
methods = [] # type: List[Stripped]
|
|
1076
|
+
|
|
1077
|
+
for our_type in symbol_table.our_types:
|
|
1078
|
+
if isinstance(our_type, intermediate.ConcreteClass):
|
|
1079
|
+
methods.append(_generate_transform(our_type))
|
|
1080
|
+
|
|
1081
|
+
writer = io.StringIO()
|
|
1082
|
+
writer.write(
|
|
1083
|
+
f"""\
|
|
1084
|
+
class _Serializer(
|
|
1085
|
+
{II}aas_types.AbstractTransformer[MutableJsonable]
|
|
1086
|
+
):
|
|
1087
|
+
{I}\"\"\"Transform the instance to its JSON-able representation.\"\"\""""
|
|
1088
|
+
)
|
|
1089
|
+
|
|
1090
|
+
for method in methods:
|
|
1091
|
+
writer.write("\n\n")
|
|
1092
|
+
writer.write(textwrap.indent(method, I))
|
|
1093
|
+
|
|
1094
|
+
return Stripped(writer.getvalue())
|
|
1095
|
+
|
|
1096
|
+
|
|
1097
|
+
# endregion
|
|
1098
|
+
|
|
1099
|
+
# fmt: off
|
|
1100
|
+
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
|
|
1101
|
+
@ensure(
|
|
1102
|
+
lambda result:
|
|
1103
|
+
not (result[0] is not None) or result[0].endswith('\n'),
|
|
1104
|
+
"Trailing newline mandatory for valid end-of-files"
|
|
1105
|
+
)
|
|
1106
|
+
# fmt: on
|
|
1107
|
+
def generate(
|
|
1108
|
+
symbol_table: intermediate.SymbolTable,
|
|
1109
|
+
aas_module: python_common.QualifiedModuleName,
|
|
1110
|
+
spec_impls: specific_implementations.SpecificImplementations,
|
|
1111
|
+
) -> Tuple[Optional[str], Optional[List[Error]]]:
|
|
1112
|
+
"""
|
|
1113
|
+
Generate the Python code for the general serialization.
|
|
1114
|
+
|
|
1115
|
+
The ``aas_module`` indicates the fully-qualified name of the base module.
|
|
1116
|
+
"""
|
|
1117
|
+
blocks = [
|
|
1118
|
+
Stripped(
|
|
1119
|
+
"""\
|
|
1120
|
+
\"\"\"
|
|
1121
|
+
Provide de/serialization of AAS classes to/from JSON.
|
|
1122
|
+
|
|
1123
|
+
We can not use one-pass deserialization for JSON since the object
|
|
1124
|
+
properties do not have fixed order, and hence we can not read
|
|
1125
|
+
``modelType`` property ahead of the remaining properties.
|
|
1126
|
+
\"\"\""""
|
|
1127
|
+
),
|
|
1128
|
+
python_common.WARNING,
|
|
1129
|
+
Stripped(
|
|
1130
|
+
f"""\
|
|
1131
|
+
import base64
|
|
1132
|
+
import collections.abc
|
|
1133
|
+
import sys
|
|
1134
|
+
from typing import (
|
|
1135
|
+
{I}cast,
|
|
1136
|
+
{I}Any,
|
|
1137
|
+
{I}Callable,
|
|
1138
|
+
{I}Iterable,
|
|
1139
|
+
{I}List,
|
|
1140
|
+
{I}Mapping,
|
|
1141
|
+
{I}MutableMapping,
|
|
1142
|
+
{I}Optional,
|
|
1143
|
+
{I}Sequence,
|
|
1144
|
+
{I}Union,
|
|
1145
|
+
)
|
|
1146
|
+
|
|
1147
|
+
if sys.version_info >= (3, 8):
|
|
1148
|
+
{I}from typing import Final
|
|
1149
|
+
else:
|
|
1150
|
+
{I}from typing_extensions import Final
|
|
1151
|
+
|
|
1152
|
+
import {aas_module}.common as aas_common
|
|
1153
|
+
import {aas_module}.stringification as aas_stringification
|
|
1154
|
+
import {aas_module}.types as aas_types"""
|
|
1155
|
+
),
|
|
1156
|
+
Stripped(
|
|
1157
|
+
f"""\
|
|
1158
|
+
class PropertySegment:
|
|
1159
|
+
{I}\"\"\"Represent a property on a path to the erroneous value.\"\"\"
|
|
1160
|
+
|
|
1161
|
+
{I}#: Instance that contains the property
|
|
1162
|
+
{I}instance: Final[Mapping[str, Any]]
|
|
1163
|
+
|
|
1164
|
+
{I}#: Name of the property
|
|
1165
|
+
{I}name: Final[str]
|
|
1166
|
+
|
|
1167
|
+
{I}def __init__(
|
|
1168
|
+
{III}self,
|
|
1169
|
+
{III}instance: Mapping[str, Any],
|
|
1170
|
+
{III}name: str
|
|
1171
|
+
{I}) -> None:
|
|
1172
|
+
{II}\"\"\"Initialize with the given values.\"\"\"
|
|
1173
|
+
{II}self.instance = instance
|
|
1174
|
+
{II}self.name = name"""
|
|
1175
|
+
),
|
|
1176
|
+
Stripped(
|
|
1177
|
+
f"""\
|
|
1178
|
+
class IndexSegment:
|
|
1179
|
+
{I}\"\"\"Represent an index access on a path to the erroneous value.\"\"\"
|
|
1180
|
+
|
|
1181
|
+
{I}#: Container that contains the item
|
|
1182
|
+
{I}container: Final[Iterable[Any]]
|
|
1183
|
+
|
|
1184
|
+
{I}#: Index of the item
|
|
1185
|
+
{I}index: Final[int]
|
|
1186
|
+
|
|
1187
|
+
{I}def __init__(
|
|
1188
|
+
{III}self,
|
|
1189
|
+
{III}container: Iterable[Any],
|
|
1190
|
+
{III}index: int
|
|
1191
|
+
{I}) -> None:
|
|
1192
|
+
{II}\"\"\"Initialize with the given values.\"\"\"
|
|
1193
|
+
{II}self.container = container
|
|
1194
|
+
{II}self.index = index"""
|
|
1195
|
+
),
|
|
1196
|
+
Stripped(
|
|
1197
|
+
"""\
|
|
1198
|
+
Segment = Union[PropertySegment, IndexSegment]"""
|
|
1199
|
+
),
|
|
1200
|
+
Stripped(
|
|
1201
|
+
f"""\
|
|
1202
|
+
class Path:
|
|
1203
|
+
{I}\"\"\"Represent the relative path to the erroneous value.\"\"\"
|
|
1204
|
+
|
|
1205
|
+
{I}def __init__(self) -> None:
|
|
1206
|
+
{II}\"\"\"Initialize as an empty path.\"\"\"
|
|
1207
|
+
{II}self._segments = [] # type: List[Segment]
|
|
1208
|
+
|
|
1209
|
+
{I}@property
|
|
1210
|
+
{I}def segments(self) -> Sequence[Segment]:
|
|
1211
|
+
{II}\"\"\"Get the segments of the path.\"\"\"
|
|
1212
|
+
{II}return self._segments
|
|
1213
|
+
|
|
1214
|
+
{I}def _prepend(self, segment: Segment) -> None:
|
|
1215
|
+
{II}\"\"\"Insert the :paramref:`segment` in front of other segments.\"\"\"
|
|
1216
|
+
{II}self._segments.insert(0, segment)
|
|
1217
|
+
|
|
1218
|
+
{I}def __str__(self) -> str:
|
|
1219
|
+
{II}if len(self._segments) == 0:
|
|
1220
|
+
{III}return ""
|
|
1221
|
+
|
|
1222
|
+
{II}parts = [] # type: List[str]
|
|
1223
|
+
|
|
1224
|
+
{II}iterator = iter(self._segments)
|
|
1225
|
+
{II}first = next(iterator)
|
|
1226
|
+
{II}if isinstance(first, PropertySegment):
|
|
1227
|
+
{III}parts.append(f"{{first.name}}")
|
|
1228
|
+
{II}elif isinstance(first, IndexSegment):
|
|
1229
|
+
{III}parts.append(f"[{{first.index}}]")
|
|
1230
|
+
{II}else:
|
|
1231
|
+
{III}aas_common.assert_never(first)
|
|
1232
|
+
|
|
1233
|
+
{II}for segment in iterator:
|
|
1234
|
+
{III}if isinstance(segment, PropertySegment):
|
|
1235
|
+
{IIII}parts.append(f".{{segment.name}}")
|
|
1236
|
+
{III}elif isinstance(segment, IndexSegment):
|
|
1237
|
+
{IIII}parts.append(f"[{{segment.index}}]")
|
|
1238
|
+
{III}else:
|
|
1239
|
+
{IIII}aas_common.assert_never(segment)
|
|
1240
|
+
|
|
1241
|
+
{II}return "".join(parts)"""
|
|
1242
|
+
),
|
|
1243
|
+
Stripped(
|
|
1244
|
+
f"""\
|
|
1245
|
+
class DeserializationException(Exception):
|
|
1246
|
+
{I}\"\"\"Signal that the JSON de-serialization could not be performed.\"\"\"
|
|
1247
|
+
|
|
1248
|
+
{I}#: Human-readable explanation of the exception's cause
|
|
1249
|
+
{I}cause: Final[str]
|
|
1250
|
+
|
|
1251
|
+
{I}#: Relative path to the erroneous value
|
|
1252
|
+
{I}path: Final[Path]
|
|
1253
|
+
|
|
1254
|
+
{I}def __init__(
|
|
1255
|
+
{III}self,
|
|
1256
|
+
{III}cause: str
|
|
1257
|
+
{I}) -> None:
|
|
1258
|
+
{II}\"\"\"Initialize with the given :paramref:`cause` and an empty path.\"\"\"
|
|
1259
|
+
{II}self.cause = cause
|
|
1260
|
+
{II}self.path = Path()"""
|
|
1261
|
+
),
|
|
1262
|
+
Stripped(
|
|
1263
|
+
f"""\
|
|
1264
|
+
# NOTE (mristin, 2022-10-03):
|
|
1265
|
+
# Recursive definitions are not yet available in mypy
|
|
1266
|
+
# (see https://github.com/python/mypy/issues/731). We have to use ``Any``
|
|
1267
|
+
# here, instead of recursive type annotations.
|
|
1268
|
+
Jsonable = Union[
|
|
1269
|
+
{I}bool,
|
|
1270
|
+
{I}int,
|
|
1271
|
+
{I}float,
|
|
1272
|
+
{I}str,
|
|
1273
|
+
{I}Sequence[Any],
|
|
1274
|
+
{I}Mapping[str, Any]
|
|
1275
|
+
]"""
|
|
1276
|
+
),
|
|
1277
|
+
Stripped(
|
|
1278
|
+
f"""\
|
|
1279
|
+
MutableJsonable = Union[
|
|
1280
|
+
{I}bool,
|
|
1281
|
+
{I}int,
|
|
1282
|
+
{I}float,
|
|
1283
|
+
{I}str,
|
|
1284
|
+
{I}List[Any],
|
|
1285
|
+
{I}MutableMapping[str, Any]
|
|
1286
|
+
]"""
|
|
1287
|
+
),
|
|
1288
|
+
Stripped("# region De-serialization"),
|
|
1289
|
+
_generate_bool_from_jsonable(),
|
|
1290
|
+
_generate_int_from_jsonable(),
|
|
1291
|
+
_generate_float_from_jsonable(),
|
|
1292
|
+
_generate_str_from_jsonable(),
|
|
1293
|
+
_generate_bytes_from_jsonable(),
|
|
1294
|
+
_generate_is_array_like(),
|
|
1295
|
+
] # type: List[Stripped]
|
|
1296
|
+
|
|
1297
|
+
errors = [] # type: List[Error]
|
|
1298
|
+
|
|
1299
|
+
for our_type in symbol_table.our_types:
|
|
1300
|
+
if isinstance(our_type, intermediate.Enumeration):
|
|
1301
|
+
blocks.append(_generate_enumeration_from_jsonable(enumeration=our_type))
|
|
1302
|
+
elif isinstance(our_type, intermediate.ConstrainedPrimitive):
|
|
1303
|
+
pass
|
|
1304
|
+
elif isinstance(our_type, intermediate.AbstractClass):
|
|
1305
|
+
blocks.append(_generate_dispatch_from_jsonable(cls=our_type))
|
|
1306
|
+
elif isinstance(our_type, intermediate.ConcreteClass):
|
|
1307
|
+
if len(our_type.concrete_descendants) > 0:
|
|
1308
|
+
blocks.append(_generate_dispatch_from_jsonable(cls=our_type))
|
|
1309
|
+
|
|
1310
|
+
if our_type.is_implementation_specific:
|
|
1311
|
+
implementation_key = specific_implementations.ImplementationKey(
|
|
1312
|
+
f"Jsonization/{our_type.name}_from_jsonable.py"
|
|
1313
|
+
)
|
|
1314
|
+
|
|
1315
|
+
implementation = spec_impls.get(implementation_key, None)
|
|
1316
|
+
if implementation is None:
|
|
1317
|
+
errors.append(
|
|
1318
|
+
Error(
|
|
1319
|
+
our_type.parsed.node,
|
|
1320
|
+
f"The jsonization snippet is missing "
|
|
1321
|
+
f"for the implementation-specific "
|
|
1322
|
+
f"class {our_type.name}: {implementation_key}",
|
|
1323
|
+
)
|
|
1324
|
+
)
|
|
1325
|
+
continue
|
|
1326
|
+
else:
|
|
1327
|
+
blocks.append(_generate_setter(cls=our_type))
|
|
1328
|
+
|
|
1329
|
+
blocks.append(_generate_concrete_class_from_jsonable(cls=our_type))
|
|
1330
|
+
else:
|
|
1331
|
+
assert_never(our_type)
|
|
1332
|
+
|
|
1333
|
+
# NOTE (mristin, 2022-10-03):
|
|
1334
|
+
# We add all the dispatch mappings at the end as the functions might not have been
|
|
1335
|
+
# defined yet otherwise.
|
|
1336
|
+
for our_type in symbol_table.our_types:
|
|
1337
|
+
if isinstance(our_type, intermediate.AbstractClass):
|
|
1338
|
+
blocks.append(_generate_dispatch_map_for_abstract_class(cls=our_type))
|
|
1339
|
+
elif isinstance(our_type, intermediate.ConcreteClass):
|
|
1340
|
+
if len(our_type.concrete_descendants) > 0:
|
|
1341
|
+
blocks.append(_generate_dispatch_map_for_concrete_class(cls=our_type))
|
|
1342
|
+
|
|
1343
|
+
if not our_type.is_implementation_specific:
|
|
1344
|
+
blocks.append(_generate_setter_map(cls=our_type))
|
|
1345
|
+
|
|
1346
|
+
else:
|
|
1347
|
+
pass
|
|
1348
|
+
|
|
1349
|
+
blocks.append(Stripped("# endregion"))
|
|
1350
|
+
|
|
1351
|
+
blocks.append(Stripped("# region Serialization"))
|
|
1352
|
+
|
|
1353
|
+
blocks.append(_generate_bytes_to_base64_str())
|
|
1354
|
+
|
|
1355
|
+
blocks.append(_generate_transformer(symbol_table=symbol_table))
|
|
1356
|
+
|
|
1357
|
+
blocks.append(Stripped("_SERIALIZER = _Serializer()"))
|
|
1358
|
+
|
|
1359
|
+
blocks.append(
|
|
1360
|
+
Stripped(
|
|
1361
|
+
f"""\
|
|
1362
|
+
def to_jsonable(that: aas_types.Class) -> MutableJsonable:
|
|
1363
|
+
{I}\"\"\"
|
|
1364
|
+
{I}Convert :paramref:`that` to a JSON-able structure.
|
|
1365
|
+
|
|
1366
|
+
{I}:param that:
|
|
1367
|
+
{II}AAS data to be recursively converted to a JSON-able structure
|
|
1368
|
+
{I}:return:
|
|
1369
|
+
{II}JSON-able structure which can be further encoded with, *e.g.*, :py:mod:`json`
|
|
1370
|
+
{I}\"\"\"
|
|
1371
|
+
{I}return _SERIALIZER.transform(that)"""
|
|
1372
|
+
)
|
|
1373
|
+
)
|
|
1374
|
+
|
|
1375
|
+
blocks.append(Stripped("# endregion"))
|
|
1376
|
+
|
|
1377
|
+
if len(errors) > 0:
|
|
1378
|
+
return None, errors
|
|
1379
|
+
|
|
1380
|
+
blocks.append(python_common.WARNING)
|
|
1381
|
+
|
|
1382
|
+
writer = io.StringIO()
|
|
1383
|
+
for i, block in enumerate(blocks):
|
|
1384
|
+
if i > 0:
|
|
1385
|
+
writer.write("\n\n\n")
|
|
1386
|
+
|
|
1387
|
+
writer.write(block)
|
|
1388
|
+
|
|
1389
|
+
writer.write("\n")
|
|
1390
|
+
|
|
1391
|
+
return writer.getvalue(), None
|