hpcflow 0.1.9__py3-none-any.whl → 0.2.0a271__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.
- hpcflow/__init__.py +2 -11
- hpcflow/__pyinstaller/__init__.py +5 -0
- hpcflow/__pyinstaller/hook-hpcflow.py +40 -0
- hpcflow/_version.py +1 -1
- hpcflow/app.py +43 -0
- hpcflow/cli.py +2 -462
- hpcflow/data/demo_data_manifest/__init__.py +3 -0
- hpcflow/data/demo_data_manifest/demo_data_manifest.json +6 -0
- hpcflow/data/jinja_templates/test/test_template.txt +8 -0
- hpcflow/data/programs/hello_world/README.md +1 -0
- hpcflow/data/programs/hello_world/hello_world.c +87 -0
- hpcflow/data/programs/hello_world/linux/hello_world +0 -0
- hpcflow/data/programs/hello_world/macos/hello_world +0 -0
- hpcflow/data/programs/hello_world/win/hello_world.exe +0 -0
- hpcflow/data/scripts/__init__.py +1 -0
- hpcflow/data/scripts/bad_script.py +2 -0
- hpcflow/data/scripts/demo_task_1_generate_t1_infile_1.py +8 -0
- hpcflow/data/scripts/demo_task_1_generate_t1_infile_2.py +8 -0
- hpcflow/data/scripts/demo_task_1_parse_p3.py +7 -0
- hpcflow/data/scripts/do_nothing.py +2 -0
- hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
- hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/generate_t1_file_01.py +7 -0
- hpcflow/data/scripts/import_future_script.py +7 -0
- hpcflow/data/scripts/input_file_generator_basic.py +3 -0
- hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
- hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_all_iters_test.py +15 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_env_spec.py +7 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_labels.py +8 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_sub_param_in_direct_out.py +6 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_group.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +11 -0
- hpcflow/data/scripts/main_script_test_json_and_direct_in_json_out.py +14 -0
- hpcflow/data/scripts/main_script_test_json_in_json_and_direct_out.py +17 -0
- hpcflow/data/scripts/main_script_test_json_in_json_out.py +14 -0
- hpcflow/data/scripts/main_script_test_json_in_json_out_labels.py +16 -0
- hpcflow/data/scripts/main_script_test_json_in_obj.py +12 -0
- hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
- hpcflow/data/scripts/main_script_test_json_out_obj.py +10 -0
- hpcflow/data/scripts/main_script_test_json_sub_param_in_json_out_labels.py +16 -0
- hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
- hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
- hpcflow/data/scripts/output_file_parser_basic.py +3 -0
- hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
- hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/parse_t1_file_01.py +4 -0
- hpcflow/data/scripts/script_exit_test.py +5 -0
- hpcflow/data/template_components/__init__.py +1 -0
- hpcflow/data/template_components/command_files.yaml +26 -0
- hpcflow/data/template_components/environments.yaml +13 -0
- hpcflow/data/template_components/parameters.yaml +14 -0
- hpcflow/data/template_components/task_schemas.yaml +139 -0
- hpcflow/data/workflows/workflow_1.yaml +5 -0
- hpcflow/examples.ipynb +1037 -0
- hpcflow/sdk/__init__.py +149 -0
- hpcflow/sdk/app.py +4266 -0
- hpcflow/sdk/cli.py +1479 -0
- hpcflow/sdk/cli_common.py +385 -0
- hpcflow/sdk/config/__init__.py +5 -0
- hpcflow/sdk/config/callbacks.py +246 -0
- hpcflow/sdk/config/cli.py +388 -0
- hpcflow/sdk/config/config.py +1410 -0
- hpcflow/sdk/config/config_file.py +501 -0
- hpcflow/sdk/config/errors.py +272 -0
- hpcflow/sdk/config/types.py +150 -0
- hpcflow/sdk/core/__init__.py +38 -0
- hpcflow/sdk/core/actions.py +3857 -0
- hpcflow/sdk/core/app_aware.py +25 -0
- hpcflow/sdk/core/cache.py +224 -0
- hpcflow/sdk/core/command_files.py +814 -0
- hpcflow/sdk/core/commands.py +424 -0
- hpcflow/sdk/core/element.py +2071 -0
- hpcflow/sdk/core/enums.py +221 -0
- hpcflow/sdk/core/environment.py +256 -0
- hpcflow/sdk/core/errors.py +1043 -0
- hpcflow/sdk/core/execute.py +207 -0
- hpcflow/sdk/core/json_like.py +809 -0
- hpcflow/sdk/core/loop.py +1320 -0
- hpcflow/sdk/core/loop_cache.py +282 -0
- hpcflow/sdk/core/object_list.py +933 -0
- hpcflow/sdk/core/parameters.py +3371 -0
- hpcflow/sdk/core/rule.py +196 -0
- hpcflow/sdk/core/run_dir_files.py +57 -0
- hpcflow/sdk/core/skip_reason.py +7 -0
- hpcflow/sdk/core/task.py +3792 -0
- hpcflow/sdk/core/task_schema.py +993 -0
- hpcflow/sdk/core/test_utils.py +538 -0
- hpcflow/sdk/core/types.py +447 -0
- hpcflow/sdk/core/utils.py +1207 -0
- hpcflow/sdk/core/validation.py +87 -0
- hpcflow/sdk/core/values.py +477 -0
- hpcflow/sdk/core/workflow.py +4820 -0
- hpcflow/sdk/core/zarr_io.py +206 -0
- hpcflow/sdk/data/__init__.py +13 -0
- hpcflow/sdk/data/config_file_schema.yaml +34 -0
- hpcflow/sdk/data/config_schema.yaml +260 -0
- hpcflow/sdk/data/environments_spec_schema.yaml +21 -0
- hpcflow/sdk/data/files_spec_schema.yaml +5 -0
- hpcflow/sdk/data/parameters_spec_schema.yaml +7 -0
- hpcflow/sdk/data/task_schema_spec_schema.yaml +3 -0
- hpcflow/sdk/data/workflow_spec_schema.yaml +22 -0
- hpcflow/sdk/demo/__init__.py +3 -0
- hpcflow/sdk/demo/cli.py +242 -0
- hpcflow/sdk/helper/__init__.py +3 -0
- hpcflow/sdk/helper/cli.py +137 -0
- hpcflow/sdk/helper/helper.py +300 -0
- hpcflow/sdk/helper/watcher.py +192 -0
- hpcflow/sdk/log.py +288 -0
- hpcflow/sdk/persistence/__init__.py +18 -0
- hpcflow/sdk/persistence/base.py +2817 -0
- hpcflow/sdk/persistence/defaults.py +6 -0
- hpcflow/sdk/persistence/discovery.py +39 -0
- hpcflow/sdk/persistence/json.py +954 -0
- hpcflow/sdk/persistence/pending.py +948 -0
- hpcflow/sdk/persistence/store_resource.py +203 -0
- hpcflow/sdk/persistence/types.py +309 -0
- hpcflow/sdk/persistence/utils.py +73 -0
- hpcflow/sdk/persistence/zarr.py +2388 -0
- hpcflow/sdk/runtime.py +320 -0
- hpcflow/sdk/submission/__init__.py +3 -0
- hpcflow/sdk/submission/enums.py +70 -0
- hpcflow/sdk/submission/jobscript.py +2379 -0
- hpcflow/sdk/submission/schedulers/__init__.py +281 -0
- hpcflow/sdk/submission/schedulers/direct.py +233 -0
- hpcflow/sdk/submission/schedulers/sge.py +376 -0
- hpcflow/sdk/submission/schedulers/slurm.py +598 -0
- hpcflow/sdk/submission/schedulers/utils.py +25 -0
- hpcflow/sdk/submission/shells/__init__.py +52 -0
- hpcflow/sdk/submission/shells/base.py +229 -0
- hpcflow/sdk/submission/shells/bash.py +504 -0
- hpcflow/sdk/submission/shells/os_version.py +115 -0
- hpcflow/sdk/submission/shells/powershell.py +352 -0
- hpcflow/sdk/submission/submission.py +1402 -0
- hpcflow/sdk/submission/types.py +140 -0
- hpcflow/sdk/typing.py +194 -0
- hpcflow/sdk/utils/arrays.py +69 -0
- hpcflow/sdk/utils/deferred_file.py +55 -0
- hpcflow/sdk/utils/hashing.py +16 -0
- hpcflow/sdk/utils/patches.py +31 -0
- hpcflow/sdk/utils/strings.py +69 -0
- hpcflow/tests/api/test_api.py +32 -0
- hpcflow/tests/conftest.py +123 -0
- hpcflow/tests/data/__init__.py +0 -0
- hpcflow/tests/data/benchmark_N_elements.yaml +6 -0
- hpcflow/tests/data/benchmark_script_runner.yaml +26 -0
- hpcflow/tests/data/multi_path_sequences.yaml +29 -0
- hpcflow/tests/data/workflow_1.json +10 -0
- hpcflow/tests/data/workflow_1.yaml +5 -0
- hpcflow/tests/data/workflow_1_slurm.yaml +8 -0
- hpcflow/tests/data/workflow_1_wsl.yaml +8 -0
- hpcflow/tests/data/workflow_test_run_abort.yaml +42 -0
- hpcflow/tests/jinja_templates/test_jinja_templates.py +161 -0
- hpcflow/tests/programs/test_programs.py +180 -0
- hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py +12 -0
- hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
- hpcflow/tests/schedulers/slurm/test_slurm_submission.py +14 -0
- hpcflow/tests/scripts/test_input_file_generators.py +282 -0
- hpcflow/tests/scripts/test_main_scripts.py +1361 -0
- hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
- hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
- hpcflow/tests/shells/wsl/test_wsl_submission.py +14 -0
- hpcflow/tests/unit/test_action.py +1066 -0
- hpcflow/tests/unit/test_action_rule.py +24 -0
- hpcflow/tests/unit/test_app.py +132 -0
- hpcflow/tests/unit/test_cache.py +46 -0
- hpcflow/tests/unit/test_cli.py +172 -0
- hpcflow/tests/unit/test_command.py +377 -0
- hpcflow/tests/unit/test_config.py +195 -0
- hpcflow/tests/unit/test_config_file.py +162 -0
- hpcflow/tests/unit/test_element.py +666 -0
- hpcflow/tests/unit/test_element_iteration.py +88 -0
- hpcflow/tests/unit/test_element_set.py +158 -0
- hpcflow/tests/unit/test_group.py +115 -0
- hpcflow/tests/unit/test_input_source.py +1479 -0
- hpcflow/tests/unit/test_input_value.py +398 -0
- hpcflow/tests/unit/test_jobscript_unit.py +757 -0
- hpcflow/tests/unit/test_json_like.py +1247 -0
- hpcflow/tests/unit/test_loop.py +2674 -0
- hpcflow/tests/unit/test_meta_task.py +325 -0
- hpcflow/tests/unit/test_multi_path_sequences.py +259 -0
- hpcflow/tests/unit/test_object_list.py +116 -0
- hpcflow/tests/unit/test_parameter.py +243 -0
- hpcflow/tests/unit/test_persistence.py +664 -0
- hpcflow/tests/unit/test_resources.py +243 -0
- hpcflow/tests/unit/test_run.py +286 -0
- hpcflow/tests/unit/test_run_directories.py +29 -0
- hpcflow/tests/unit/test_runtime.py +9 -0
- hpcflow/tests/unit/test_schema_input.py +372 -0
- hpcflow/tests/unit/test_shell.py +129 -0
- hpcflow/tests/unit/test_slurm.py +39 -0
- hpcflow/tests/unit/test_submission.py +502 -0
- hpcflow/tests/unit/test_task.py +2560 -0
- hpcflow/tests/unit/test_task_schema.py +182 -0
- hpcflow/tests/unit/test_utils.py +616 -0
- hpcflow/tests/unit/test_value_sequence.py +549 -0
- hpcflow/tests/unit/test_values.py +91 -0
- hpcflow/tests/unit/test_workflow.py +827 -0
- hpcflow/tests/unit/test_workflow_template.py +186 -0
- hpcflow/tests/unit/utils/test_arrays.py +40 -0
- hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
- hpcflow/tests/unit/utils/test_hashing.py +65 -0
- hpcflow/tests/unit/utils/test_patches.py +5 -0
- hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
- hpcflow/tests/unit/utils/test_strings.py +97 -0
- hpcflow/tests/workflows/__init__.py +0 -0
- hpcflow/tests/workflows/test_directory_structure.py +31 -0
- hpcflow/tests/workflows/test_jobscript.py +355 -0
- hpcflow/tests/workflows/test_run_status.py +198 -0
- hpcflow/tests/workflows/test_skip_downstream.py +696 -0
- hpcflow/tests/workflows/test_submission.py +140 -0
- hpcflow/tests/workflows/test_workflows.py +564 -0
- hpcflow/tests/workflows/test_zip.py +18 -0
- hpcflow/viz_demo.ipynb +6794 -0
- hpcflow-0.2.0a271.dist-info/LICENSE +375 -0
- hpcflow-0.2.0a271.dist-info/METADATA +65 -0
- hpcflow-0.2.0a271.dist-info/RECORD +237 -0
- {hpcflow-0.1.9.dist-info → hpcflow-0.2.0a271.dist-info}/WHEEL +4 -5
- hpcflow-0.2.0a271.dist-info/entry_points.txt +6 -0
- hpcflow/api.py +0 -458
- hpcflow/archive/archive.py +0 -308
- hpcflow/archive/cloud/cloud.py +0 -47
- hpcflow/archive/cloud/errors.py +0 -9
- hpcflow/archive/cloud/providers/dropbox.py +0 -432
- hpcflow/archive/errors.py +0 -5
- hpcflow/base_db.py +0 -4
- hpcflow/config.py +0 -232
- hpcflow/copytree.py +0 -66
- hpcflow/data/examples/_config.yml +0 -14
- hpcflow/data/examples/damask/demo/1.run.yml +0 -4
- hpcflow/data/examples/damask/demo/2.process.yml +0 -29
- hpcflow/data/examples/damask/demo/geom.geom +0 -2052
- hpcflow/data/examples/damask/demo/load.load +0 -1
- hpcflow/data/examples/damask/demo/material.config +0 -185
- hpcflow/data/examples/damask/inputs/geom.geom +0 -2052
- hpcflow/data/examples/damask/inputs/load.load +0 -1
- hpcflow/data/examples/damask/inputs/material.config +0 -185
- hpcflow/data/examples/damask/profiles/_variable_lookup.yml +0 -21
- hpcflow/data/examples/damask/profiles/damask.yml +0 -4
- hpcflow/data/examples/damask/profiles/damask_process.yml +0 -8
- hpcflow/data/examples/damask/profiles/damask_run.yml +0 -5
- hpcflow/data/examples/damask/profiles/default.yml +0 -6
- hpcflow/data/examples/thinking.yml +0 -177
- hpcflow/errors.py +0 -2
- hpcflow/init_db.py +0 -37
- hpcflow/models.py +0 -2549
- hpcflow/nesting.py +0 -9
- hpcflow/profiles.py +0 -455
- hpcflow/project.py +0 -81
- hpcflow/scheduler.py +0 -323
- hpcflow/utils.py +0 -103
- hpcflow/validation.py +0 -167
- hpcflow/variables.py +0 -544
- hpcflow-0.1.9.dist-info/METADATA +0 -168
- hpcflow-0.1.9.dist-info/RECORD +0 -45
- hpcflow-0.1.9.dist-info/entry_points.txt +0 -8
- hpcflow-0.1.9.dist-info/top_level.txt +0 -1
- /hpcflow/{archive → data/jinja_templates}/__init__.py +0 -0
- /hpcflow/{archive/cloud → data/programs}/__init__.py +0 -0
- /hpcflow/{archive/cloud/providers → data/workflows}/__init__.py +0 -0
|
@@ -0,0 +1,1247 @@
|
|
|
1
|
+
# mypy: disable-error-code="annotation-unchecked"
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
import enum
|
|
5
|
+
from types import SimpleNamespace
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from hpcflow.app import app as hf
|
|
11
|
+
from hpcflow.sdk.core.json_like import BaseJSONLike, ChildObjectSpec
|
|
12
|
+
from hpcflow.sdk.core.object_list import ObjectList
|
|
13
|
+
|
|
14
|
+
# BE AWARE THAT MYPY CANNOT CORRECTLY TYPE-CHECK THIS FILE AT ALL.
|
|
15
|
+
# It fails massively due to all the classes inside functions being passed to other functions.
|
|
16
|
+
# Omitting the types makes it ignore them all, which us for the best.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def null_config(tmp_path):
|
|
21
|
+
if not hf.is_config_loaded:
|
|
22
|
+
hf.load_config(config_dir=tmp_path)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_json_like_name_is_name(null_config):
|
|
26
|
+
spec = ChildObjectSpec(name="a")
|
|
27
|
+
assert spec.json_like_name == "a"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.fixture
|
|
31
|
+
def obj_and_json_like_1(null_config):
|
|
32
|
+
@dataclass
|
|
33
|
+
class ObjA(BaseJSONLike):
|
|
34
|
+
a: int
|
|
35
|
+
b: float
|
|
36
|
+
|
|
37
|
+
js_1 = {
|
|
38
|
+
"a": 1,
|
|
39
|
+
"b": 2.1,
|
|
40
|
+
}
|
|
41
|
+
return ObjA, js_1
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_from_json_like_expected_obj_simple(obj_and_json_like_1):
|
|
45
|
+
ObjA, js_1 = obj_and_json_like_1
|
|
46
|
+
assert ObjA.from_json_like(js_1) == ObjA(**js_1)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_to_json_like_expected_json_like_simple(obj_and_json_like_1):
|
|
50
|
+
ObjA, js_1 = obj_and_json_like_1
|
|
51
|
+
js_2, _ = ObjA(**js_1).to_json_like()
|
|
52
|
+
assert js_2 == js_1
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_json_like_round_trip_obj_simple(obj_and_json_like_1):
|
|
56
|
+
ObjA, js_1 = obj_and_json_like_1
|
|
57
|
+
obj1 = ObjA(**js_1)
|
|
58
|
+
js_2, _ = obj1.to_json_like()
|
|
59
|
+
obj2 = ObjA.from_json_like(js_2)
|
|
60
|
+
assert obj1 == obj2
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@pytest.fixture
|
|
64
|
+
def BaseJSONLikeSubClass(null_config):
|
|
65
|
+
return type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_BaseJSONLike_child_object_class_namespace_via_obj(null_config):
|
|
69
|
+
"""Child object class passed directly as a class object."""
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class ObjB(BaseJSONLike):
|
|
73
|
+
c: int
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class ObjA(BaseJSONLike):
|
|
77
|
+
_child_objects = (
|
|
78
|
+
ChildObjectSpec(
|
|
79
|
+
name="b",
|
|
80
|
+
class_obj=ObjB,
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
a: int
|
|
84
|
+
b: float
|
|
85
|
+
|
|
86
|
+
js_1 = {
|
|
87
|
+
"a": 1,
|
|
88
|
+
"b": {
|
|
89
|
+
"c": 8,
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
assert ObjA.from_json_like(js_1) == ObjA(a=1, b=ObjB(c=8))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_BaseJSONLike_child_object_class_namespace_via_name_and_dict_namespace(
|
|
96
|
+
BaseJSONLikeSubClass: type[BaseJSONLike],
|
|
97
|
+
):
|
|
98
|
+
"""Child object class passed as a name and namespace passed as a dict."""
|
|
99
|
+
T: type = BaseJSONLikeSubClass # Workaround for python/mypy#14458
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
class ObjB(T):
|
|
103
|
+
c: int
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class ObjA(T):
|
|
107
|
+
_child_objects = (
|
|
108
|
+
ChildObjectSpec(
|
|
109
|
+
name="b",
|
|
110
|
+
class_name="ObjB",
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
a: int
|
|
114
|
+
b: float
|
|
115
|
+
|
|
116
|
+
BaseJSONLikeSubClass._set_class_namespace({"ObjB": ObjB}, is_dict=True)
|
|
117
|
+
|
|
118
|
+
js_1 = {
|
|
119
|
+
"a": 1,
|
|
120
|
+
"b": {
|
|
121
|
+
"c": 8,
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
assert ObjA.from_json_like(js_1) == ObjA(a=1, b=ObjB(c=8))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_BaseJSONLike_child_object_class_namespace_via_name_and_func_locals(
|
|
128
|
+
BaseJSONLikeSubClass: type[BaseJSONLike],
|
|
129
|
+
):
|
|
130
|
+
"""Child object class passed as a name and namespace passed as function locals."""
|
|
131
|
+
T: type = BaseJSONLikeSubClass # Workaround for python/mypy#14458
|
|
132
|
+
|
|
133
|
+
@dataclass
|
|
134
|
+
class ObjB(T):
|
|
135
|
+
c: int
|
|
136
|
+
|
|
137
|
+
@dataclass
|
|
138
|
+
class ObjA(T):
|
|
139
|
+
_child_objects = (
|
|
140
|
+
ChildObjectSpec(
|
|
141
|
+
name="b",
|
|
142
|
+
class_name="ObjB",
|
|
143
|
+
),
|
|
144
|
+
)
|
|
145
|
+
a: int
|
|
146
|
+
b: float
|
|
147
|
+
|
|
148
|
+
BaseJSONLikeSubClass._set_class_namespace(locals(), is_dict=True)
|
|
149
|
+
|
|
150
|
+
js_1 = {
|
|
151
|
+
"a": 1,
|
|
152
|
+
"b": {
|
|
153
|
+
"c": 8,
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
assert ObjA.from_json_like(js_1) == ObjA(a=1, b=ObjB(c=8))
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_BaseJSONLike_child_object_class_namespace_via_name_and_SimpleNamespace(
|
|
160
|
+
BaseJSONLikeSubClass: type[BaseJSONLike],
|
|
161
|
+
):
|
|
162
|
+
"""Child object class passed as a name and namespace passed as a SimpleNamespace."""
|
|
163
|
+
T: type = BaseJSONLikeSubClass # Workaround for python/mypy#14458
|
|
164
|
+
|
|
165
|
+
@dataclass
|
|
166
|
+
class ObjB(T):
|
|
167
|
+
c: int
|
|
168
|
+
|
|
169
|
+
@dataclass
|
|
170
|
+
class ObjA(T):
|
|
171
|
+
_child_objects = (
|
|
172
|
+
ChildObjectSpec(
|
|
173
|
+
name="b",
|
|
174
|
+
class_name="ObjB",
|
|
175
|
+
),
|
|
176
|
+
)
|
|
177
|
+
a: int
|
|
178
|
+
b: float
|
|
179
|
+
|
|
180
|
+
BaseJSONLikeSubClass._set_class_namespace(SimpleNamespace(ObjB=ObjB))
|
|
181
|
+
|
|
182
|
+
js_1 = {
|
|
183
|
+
"a": 1,
|
|
184
|
+
"b": {
|
|
185
|
+
"c": 8,
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
assert ObjA.from_json_like(js_1) == ObjA(a=1, b=ObjB(c=8))
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@pytest.fixture
|
|
192
|
+
def obj_and_child_obj_and_json_like_1(null_config):
|
|
193
|
+
@dataclass
|
|
194
|
+
class ObjB(BaseJSONLike):
|
|
195
|
+
c: int
|
|
196
|
+
|
|
197
|
+
@dataclass
|
|
198
|
+
class ObjA(BaseJSONLike):
|
|
199
|
+
_child_objects = (
|
|
200
|
+
ChildObjectSpec(
|
|
201
|
+
name="b",
|
|
202
|
+
class_obj=ObjB,
|
|
203
|
+
),
|
|
204
|
+
)
|
|
205
|
+
a: int
|
|
206
|
+
b: float
|
|
207
|
+
|
|
208
|
+
js_1 = {
|
|
209
|
+
"a": 1,
|
|
210
|
+
"b": {
|
|
211
|
+
"c": 8,
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return ObjA, ObjB, js_1
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_from_json_like_expected_obj_with_child_obj(obj_and_child_obj_and_json_like_1):
|
|
219
|
+
ObjA, ObjB, js_1 = obj_and_child_obj_and_json_like_1
|
|
220
|
+
assert ObjA.from_json_like(js_1) == ObjA(a=1, b=ObjB(c=8))
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def test_to_json_like_expected_json_like_with_child_obj(
|
|
224
|
+
obj_and_child_obj_and_json_like_1,
|
|
225
|
+
):
|
|
226
|
+
ObjA, ObjB, js_1 = obj_and_child_obj_and_json_like_1
|
|
227
|
+
obj = ObjA(a=1, b=ObjB(c=js_1["b"]["c"]))
|
|
228
|
+
js, _ = obj.to_json_like()
|
|
229
|
+
assert js == js_1
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_json_like_round_trip_with_child_obj(obj_and_child_obj_and_json_like_1):
|
|
233
|
+
ObjA, ObjB, _ = obj_and_child_obj_and_json_like_1
|
|
234
|
+
obj1 = ObjA(a=1, b=ObjB(c=4))
|
|
235
|
+
js, _ = obj1.to_json_like()
|
|
236
|
+
obj2 = ObjA.from_json_like(js)
|
|
237
|
+
assert obj1 == obj2
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@pytest.fixture
|
|
241
|
+
def obj_and_child_obj_with_json_like_name_and_json_like(null_config):
|
|
242
|
+
@dataclass
|
|
243
|
+
class ObjB(BaseJSONLike):
|
|
244
|
+
c: int
|
|
245
|
+
|
|
246
|
+
@dataclass
|
|
247
|
+
class ObjA(BaseJSONLike):
|
|
248
|
+
_child_objects = (
|
|
249
|
+
ChildObjectSpec(
|
|
250
|
+
name="b",
|
|
251
|
+
json_like_name="json_b",
|
|
252
|
+
class_obj=ObjB,
|
|
253
|
+
),
|
|
254
|
+
)
|
|
255
|
+
a: int
|
|
256
|
+
b: float
|
|
257
|
+
|
|
258
|
+
js_1 = {
|
|
259
|
+
"a": 1,
|
|
260
|
+
"json_b": {
|
|
261
|
+
"c": 8,
|
|
262
|
+
},
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return ObjA, ObjB, js_1
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def test_from_json_like_expected_obj_with_child_obj_with_json_like_name(
|
|
269
|
+
obj_and_child_obj_with_json_like_name_and_json_like,
|
|
270
|
+
):
|
|
271
|
+
ObjA, ObjB, js_1 = obj_and_child_obj_with_json_like_name_and_json_like
|
|
272
|
+
assert ObjA.from_json_like(js_1) == ObjA(a=1, b=ObjB(c=8))
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def test_to_json_like_expected_json_like_with_child_obj_with_json_like_name(
|
|
276
|
+
obj_and_child_obj_with_json_like_name_and_json_like,
|
|
277
|
+
):
|
|
278
|
+
ObjA, ObjB, js_1 = obj_and_child_obj_with_json_like_name_and_json_like
|
|
279
|
+
obj = ObjA(a=1, b=ObjB(c=js_1["json_b"]["c"]))
|
|
280
|
+
js, _ = obj.to_json_like()
|
|
281
|
+
assert js == js_1
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def test_json_like_round_trip_with_child_obj_with_json_like_name(
|
|
285
|
+
obj_and_child_obj_with_json_like_name_and_json_like,
|
|
286
|
+
):
|
|
287
|
+
ObjA, ObjB, _ = obj_and_child_obj_with_json_like_name_and_json_like
|
|
288
|
+
obj1 = ObjA(a=1, b=ObjB(c=4))
|
|
289
|
+
obj2 = ObjA.from_json_like(obj1.to_json_like()[0])
|
|
290
|
+
assert obj1 == obj2
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@pytest.fixture
|
|
294
|
+
def obj_and_child_obj_with_list_and_json_like(null_config):
|
|
295
|
+
@dataclass
|
|
296
|
+
class ObjB(BaseJSONLike):
|
|
297
|
+
c: int
|
|
298
|
+
|
|
299
|
+
@dataclass
|
|
300
|
+
class ObjA(BaseJSONLike):
|
|
301
|
+
_child_objects = (
|
|
302
|
+
ChildObjectSpec(
|
|
303
|
+
name="b",
|
|
304
|
+
class_obj=ObjB,
|
|
305
|
+
is_multiple=True,
|
|
306
|
+
),
|
|
307
|
+
)
|
|
308
|
+
a: int
|
|
309
|
+
b: float
|
|
310
|
+
|
|
311
|
+
js_1 = {
|
|
312
|
+
"a": 1,
|
|
313
|
+
"b": [{"c": 8}, {"c": 9}],
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return ObjA, ObjB, js_1
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def test_from_json_like_expected_obj_with_child_obj_list(
|
|
320
|
+
obj_and_child_obj_with_list_and_json_like,
|
|
321
|
+
):
|
|
322
|
+
ObjA, ObjB, js_1 = obj_and_child_obj_with_list_and_json_like
|
|
323
|
+
assert ObjA.from_json_like(js_1) == ObjA(a=1, b=[ObjB(c=8), ObjB(c=9)])
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def test_to_json_like_expected_json_like_with_child_obj_list(
|
|
327
|
+
obj_and_child_obj_with_list_and_json_like,
|
|
328
|
+
):
|
|
329
|
+
ObjA, ObjB, js_1 = obj_and_child_obj_with_list_and_json_like
|
|
330
|
+
obj = ObjA(a=1, b=[ObjB(c=js_1["b"][0]["c"]), ObjB(c=js_1["b"][1]["c"])])
|
|
331
|
+
js, _ = obj.to_json_like()
|
|
332
|
+
assert js == js_1
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def test_json_like_round_trip_with_child_obj_list(
|
|
336
|
+
obj_and_child_obj_with_list_and_json_like,
|
|
337
|
+
):
|
|
338
|
+
ObjA, ObjB, _ = obj_and_child_obj_with_list_and_json_like
|
|
339
|
+
obj1 = ObjA(a=1, b=[ObjB(c=4), ObjB(c=5)])
|
|
340
|
+
obj2 = ObjA.from_json_like(obj1.to_json_like()[0])
|
|
341
|
+
assert obj1 == obj2
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@pytest.fixture
|
|
345
|
+
def obj_and_child_obj_with_dict_key_only_and_json_like_and_json_like_normed(null_config):
|
|
346
|
+
@dataclass
|
|
347
|
+
class ObjB(BaseJSONLike):
|
|
348
|
+
name: str
|
|
349
|
+
c: int
|
|
350
|
+
|
|
351
|
+
@dataclass
|
|
352
|
+
class ObjA(BaseJSONLike):
|
|
353
|
+
_child_objects = (
|
|
354
|
+
ChildObjectSpec(
|
|
355
|
+
name="b", class_obj=ObjB, is_multiple=True, dict_key_attr="name"
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
a: int
|
|
359
|
+
b: float
|
|
360
|
+
|
|
361
|
+
js_1 = {
|
|
362
|
+
"a": 1,
|
|
363
|
+
"b": {
|
|
364
|
+
"c1": {"c": 8},
|
|
365
|
+
"c2": {"c": 9},
|
|
366
|
+
},
|
|
367
|
+
}
|
|
368
|
+
js_1_normed = {
|
|
369
|
+
"a": 1,
|
|
370
|
+
"b": [
|
|
371
|
+
{"name": "c1", "c": 8},
|
|
372
|
+
{"name": "c2", "c": 9},
|
|
373
|
+
],
|
|
374
|
+
}
|
|
375
|
+
return ObjA, ObjB, js_1, js_1_normed
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def test_from_json_like_expected_obj_with_child_obj_dict_key_only(
|
|
379
|
+
obj_and_child_obj_with_dict_key_only_and_json_like_and_json_like_normed,
|
|
380
|
+
):
|
|
381
|
+
(
|
|
382
|
+
ObjA,
|
|
383
|
+
ObjB,
|
|
384
|
+
js_1,
|
|
385
|
+
_,
|
|
386
|
+
) = obj_and_child_obj_with_dict_key_only_and_json_like_and_json_like_normed
|
|
387
|
+
assert ObjA.from_json_like(js_1) == ObjA(
|
|
388
|
+
a=1,
|
|
389
|
+
b=[ObjB(name="c1", c=8), ObjB(name="c2", c=9)],
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def test_to_json_like_expected_json_like_with_child_obj_dict_key_only(
|
|
394
|
+
obj_and_child_obj_with_dict_key_only_and_json_like_and_json_like_normed,
|
|
395
|
+
):
|
|
396
|
+
(
|
|
397
|
+
ObjA,
|
|
398
|
+
ObjB,
|
|
399
|
+
js_1,
|
|
400
|
+
js_1_normed,
|
|
401
|
+
) = obj_and_child_obj_with_dict_key_only_and_json_like_and_json_like_normed
|
|
402
|
+
obj = ObjA(
|
|
403
|
+
a=1,
|
|
404
|
+
b=[
|
|
405
|
+
ObjB(name="c1", c=js_1["b"]["c1"]["c"]),
|
|
406
|
+
ObjB(name="c2", c=js_1["b"]["c2"]["c"]),
|
|
407
|
+
],
|
|
408
|
+
)
|
|
409
|
+
js, _ = obj.to_json_like()
|
|
410
|
+
assert js == js_1_normed
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
@pytest.fixture
|
|
414
|
+
def obj_and_child_obj_with_dict_key_val_and_json_like_and_json_like_normed(null_config):
|
|
415
|
+
@dataclass
|
|
416
|
+
class ObjB(BaseJSONLike):
|
|
417
|
+
name: str
|
|
418
|
+
c: int
|
|
419
|
+
|
|
420
|
+
@dataclass
|
|
421
|
+
class ObjA(BaseJSONLike):
|
|
422
|
+
_child_objects = (
|
|
423
|
+
ChildObjectSpec(
|
|
424
|
+
name="b",
|
|
425
|
+
class_obj=ObjB,
|
|
426
|
+
is_multiple=True,
|
|
427
|
+
dict_key_attr="name",
|
|
428
|
+
dict_val_attr="c",
|
|
429
|
+
),
|
|
430
|
+
)
|
|
431
|
+
a: int
|
|
432
|
+
b: float
|
|
433
|
+
|
|
434
|
+
js_1 = {
|
|
435
|
+
"a": 1,
|
|
436
|
+
"b": {
|
|
437
|
+
"c1": 8,
|
|
438
|
+
"c2": 9,
|
|
439
|
+
},
|
|
440
|
+
}
|
|
441
|
+
js_1_normed = {
|
|
442
|
+
"a": 1,
|
|
443
|
+
"b": [
|
|
444
|
+
{"name": "c1", "c": 8},
|
|
445
|
+
{"name": "c2", "c": 9},
|
|
446
|
+
],
|
|
447
|
+
}
|
|
448
|
+
return ObjA, ObjB, js_1, js_1_normed
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def test_from_json_like_expected_obj_with_child_obj_dict_key_dict_val(
|
|
452
|
+
obj_and_child_obj_with_dict_key_val_and_json_like_and_json_like_normed,
|
|
453
|
+
):
|
|
454
|
+
(
|
|
455
|
+
ObjA,
|
|
456
|
+
ObjB,
|
|
457
|
+
js_1,
|
|
458
|
+
_,
|
|
459
|
+
) = obj_and_child_obj_with_dict_key_val_and_json_like_and_json_like_normed
|
|
460
|
+
assert ObjA.from_json_like(js_1) == ObjA(
|
|
461
|
+
a=1,
|
|
462
|
+
b=[ObjB(name="c1", c=8), ObjB(name="c2", c=9)],
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def test_to_json_like_expected_json_like_with_child_obj_dict_key_dict_val(
|
|
467
|
+
obj_and_child_obj_with_dict_key_val_and_json_like_and_json_like_normed,
|
|
468
|
+
):
|
|
469
|
+
(
|
|
470
|
+
ObjA,
|
|
471
|
+
ObjB,
|
|
472
|
+
js_1,
|
|
473
|
+
js_1_normed,
|
|
474
|
+
) = obj_and_child_obj_with_dict_key_val_and_json_like_and_json_like_normed
|
|
475
|
+
obj = ObjA(
|
|
476
|
+
a=1,
|
|
477
|
+
b=[
|
|
478
|
+
ObjB(name="c1", c=js_1["b"]["c1"]),
|
|
479
|
+
ObjB(name="c2", c=js_1["b"]["c2"]),
|
|
480
|
+
],
|
|
481
|
+
)
|
|
482
|
+
js, _ = obj.to_json_like()
|
|
483
|
+
assert js == js_1_normed
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def test_from_json_like_raise_on_is_multiple_with_dict_but_no_dict_key_attr(null_config):
|
|
487
|
+
@dataclass
|
|
488
|
+
class ObjA(BaseJSONLike):
|
|
489
|
+
_child_objects = (
|
|
490
|
+
ChildObjectSpec(
|
|
491
|
+
name="b",
|
|
492
|
+
is_multiple=True,
|
|
493
|
+
),
|
|
494
|
+
)
|
|
495
|
+
a: int
|
|
496
|
+
b: float
|
|
497
|
+
|
|
498
|
+
js_1 = {
|
|
499
|
+
"a": 1,
|
|
500
|
+
"b": {
|
|
501
|
+
"c1": 8,
|
|
502
|
+
"c2": 9,
|
|
503
|
+
},
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
with pytest.raises(ValueError):
|
|
507
|
+
ObjA.from_json_like(js_1)
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def test_from_json_like_raise_on_is_multiple_with_dict_key_no_dict_val_but_non_dict_vals(
|
|
511
|
+
null_config,
|
|
512
|
+
):
|
|
513
|
+
@dataclass
|
|
514
|
+
class ObjA(BaseJSONLike):
|
|
515
|
+
_child_objects = (
|
|
516
|
+
ChildObjectSpec(
|
|
517
|
+
name="b",
|
|
518
|
+
is_multiple=True,
|
|
519
|
+
dict_key_attr="name",
|
|
520
|
+
),
|
|
521
|
+
)
|
|
522
|
+
a: int
|
|
523
|
+
b: float
|
|
524
|
+
|
|
525
|
+
js_1 = {
|
|
526
|
+
"a": 1,
|
|
527
|
+
"b": {
|
|
528
|
+
"c1": 8,
|
|
529
|
+
"c2": 9,
|
|
530
|
+
},
|
|
531
|
+
}
|
|
532
|
+
with pytest.raises(TypeError):
|
|
533
|
+
ObjA.from_json_like(js_1)
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
def test_from_json_like_raise_on_is_multiple_not_list_or_dict(null_config):
|
|
537
|
+
@dataclass
|
|
538
|
+
class ObjA(BaseJSONLike):
|
|
539
|
+
_child_objects = (
|
|
540
|
+
ChildObjectSpec(
|
|
541
|
+
name="b",
|
|
542
|
+
is_multiple=True,
|
|
543
|
+
),
|
|
544
|
+
)
|
|
545
|
+
a: int
|
|
546
|
+
b: float
|
|
547
|
+
|
|
548
|
+
js_1 = {
|
|
549
|
+
"a": 1,
|
|
550
|
+
"b": 2,
|
|
551
|
+
}
|
|
552
|
+
with pytest.raises(TypeError):
|
|
553
|
+
ObjA.from_json_like(js_1)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def test_from_json_like_with_parent_ref(null_config):
|
|
557
|
+
@dataclass
|
|
558
|
+
class ObjB(BaseJSONLike):
|
|
559
|
+
name: str
|
|
560
|
+
c: int
|
|
561
|
+
obj_A: Any = None
|
|
562
|
+
|
|
563
|
+
def __eq__(self, other):
|
|
564
|
+
if not isinstance(other, self.__class__):
|
|
565
|
+
return False
|
|
566
|
+
return self.name == other.name and self.c == other.c
|
|
567
|
+
|
|
568
|
+
@dataclass
|
|
569
|
+
class ObjA(BaseJSONLike):
|
|
570
|
+
_child_objects = (
|
|
571
|
+
ChildObjectSpec(
|
|
572
|
+
name="b",
|
|
573
|
+
class_obj=ObjB,
|
|
574
|
+
parent_ref="obj_A",
|
|
575
|
+
),
|
|
576
|
+
)
|
|
577
|
+
a: int
|
|
578
|
+
b: float
|
|
579
|
+
|
|
580
|
+
def __post_init__(self):
|
|
581
|
+
self._set_parent_refs()
|
|
582
|
+
|
|
583
|
+
def __eq__(self, other):
|
|
584
|
+
if not isinstance(other, self.__class__):
|
|
585
|
+
return False
|
|
586
|
+
return (
|
|
587
|
+
self.a == other.a
|
|
588
|
+
and self.b == other.b
|
|
589
|
+
and self.b.obj_A is self
|
|
590
|
+
and other.b.obj_A is other
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
js_1 = {
|
|
594
|
+
"a": 1,
|
|
595
|
+
"b": {
|
|
596
|
+
"name": "c1",
|
|
597
|
+
"c": 8,
|
|
598
|
+
},
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
objA = ObjA(a=1, b=ObjB(name=js_1["b"]["name"], c=js_1["b"]["c"]))
|
|
602
|
+
objA.b.obj_A = objA
|
|
603
|
+
|
|
604
|
+
assert ObjA.from_json_like(js_1) == objA
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def test_json_like_round_trip_with_parent_ref(null_config):
|
|
608
|
+
@dataclass
|
|
609
|
+
class ObjB(BaseJSONLike):
|
|
610
|
+
name: str
|
|
611
|
+
c: int
|
|
612
|
+
obj_A: Any = None
|
|
613
|
+
|
|
614
|
+
def __eq__(self, other):
|
|
615
|
+
if not isinstance(other, self.__class__):
|
|
616
|
+
return False
|
|
617
|
+
return self.name == other.name and self.c == other.c
|
|
618
|
+
|
|
619
|
+
@dataclass
|
|
620
|
+
class ObjA(BaseJSONLike):
|
|
621
|
+
_child_objects = (
|
|
622
|
+
ChildObjectSpec(
|
|
623
|
+
name="b",
|
|
624
|
+
class_obj=ObjB,
|
|
625
|
+
parent_ref="obj_A",
|
|
626
|
+
),
|
|
627
|
+
)
|
|
628
|
+
a: int
|
|
629
|
+
b: float
|
|
630
|
+
|
|
631
|
+
def __eq__(self, other):
|
|
632
|
+
if not isinstance(other, self.__class__):
|
|
633
|
+
return False
|
|
634
|
+
return (
|
|
635
|
+
self.a == other.a
|
|
636
|
+
and self.b == other.b
|
|
637
|
+
and self.b.obj_A is self
|
|
638
|
+
and other.b.obj_A is other
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
js_1 = {
|
|
642
|
+
"a": 1,
|
|
643
|
+
"b": {
|
|
644
|
+
"name": "c1",
|
|
645
|
+
"c": 8,
|
|
646
|
+
},
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
objA = ObjA.from_json_like(js_1)
|
|
650
|
+
assert objA.to_json_like()[0] == js_1
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def test_from_json_like_optional_attr(null_config):
|
|
654
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
655
|
+
|
|
656
|
+
@dataclass
|
|
657
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
658
|
+
name: str
|
|
659
|
+
c: int
|
|
660
|
+
|
|
661
|
+
@dataclass
|
|
662
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
663
|
+
_child_objects = (
|
|
664
|
+
ChildObjectSpec(
|
|
665
|
+
name="b",
|
|
666
|
+
class_obj=ObjB,
|
|
667
|
+
),
|
|
668
|
+
)
|
|
669
|
+
a: int
|
|
670
|
+
b: Any = None
|
|
671
|
+
|
|
672
|
+
js_in = {"a": 9, "b": None}
|
|
673
|
+
objA = ObjA.from_json_like(js_in)
|
|
674
|
+
assert objA == ObjA(a=9)
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def test_from_json_like_optional_attr_with_is_multiple_both_none(null_config):
|
|
678
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
679
|
+
|
|
680
|
+
@dataclass
|
|
681
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
682
|
+
name: str
|
|
683
|
+
c: int
|
|
684
|
+
|
|
685
|
+
@dataclass
|
|
686
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
687
|
+
_child_objects = (
|
|
688
|
+
ChildObjectSpec(
|
|
689
|
+
name="b",
|
|
690
|
+
is_multiple=True,
|
|
691
|
+
class_obj=ObjB,
|
|
692
|
+
),
|
|
693
|
+
)
|
|
694
|
+
a: int
|
|
695
|
+
b: Any = None
|
|
696
|
+
|
|
697
|
+
js_in = {
|
|
698
|
+
"a": 9,
|
|
699
|
+
"b": [None, None],
|
|
700
|
+
}
|
|
701
|
+
objA = ObjA.from_json_like(js_in)
|
|
702
|
+
assert objA == ObjA(a=9, b=[None, None])
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
def test_from_json_like_optional_attr_with_is_multiple_one_none(null_config):
|
|
706
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
707
|
+
|
|
708
|
+
@dataclass
|
|
709
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
710
|
+
name: str
|
|
711
|
+
c: int
|
|
712
|
+
|
|
713
|
+
@dataclass
|
|
714
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
715
|
+
_child_objects = (
|
|
716
|
+
ChildObjectSpec(
|
|
717
|
+
name="b",
|
|
718
|
+
is_multiple=True,
|
|
719
|
+
class_obj=ObjB,
|
|
720
|
+
),
|
|
721
|
+
)
|
|
722
|
+
a: int
|
|
723
|
+
b: Any = None
|
|
724
|
+
|
|
725
|
+
js_in = {
|
|
726
|
+
"a": 9,
|
|
727
|
+
"b": [None, {"name": "c1", "c": 2}],
|
|
728
|
+
}
|
|
729
|
+
objA = ObjA.from_json_like(js_in)
|
|
730
|
+
assert objA == ObjA(a=9, b=[None, ObjB(name="c1", c=2)])
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def test_from_json_like_optional_attr_with_is_multiple_one_none_and_shared_data_name(
|
|
734
|
+
null_config,
|
|
735
|
+
):
|
|
736
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
737
|
+
|
|
738
|
+
@dataclass
|
|
739
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
740
|
+
name: str
|
|
741
|
+
c: int
|
|
742
|
+
|
|
743
|
+
@dataclass
|
|
744
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
745
|
+
_child_objects = (
|
|
746
|
+
ChildObjectSpec(
|
|
747
|
+
name="b",
|
|
748
|
+
is_multiple=True,
|
|
749
|
+
class_obj=ObjB,
|
|
750
|
+
shared_data_name="bees",
|
|
751
|
+
shared_data_primary_key="name",
|
|
752
|
+
),
|
|
753
|
+
)
|
|
754
|
+
a: int
|
|
755
|
+
b: Any = None
|
|
756
|
+
|
|
757
|
+
dcts = [
|
|
758
|
+
{"name": "c1", "c": 2},
|
|
759
|
+
{"name": "c2", "c": 3},
|
|
760
|
+
]
|
|
761
|
+
|
|
762
|
+
obj_lst = ObjectList([ObjB(**i) for i in dcts])
|
|
763
|
+
|
|
764
|
+
js_in = {
|
|
765
|
+
"a": 9,
|
|
766
|
+
"b": [None, "c1"],
|
|
767
|
+
}
|
|
768
|
+
objA = ObjA.from_json_like(js_in, shared_data={"bees": obj_lst})
|
|
769
|
+
assert objA == ObjA(a=9, b=[None, ObjB(name="c1", c=2)])
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def test_from_json_like_optional_attr_with_is_multiple_all_none_and_shared_data_name(
|
|
773
|
+
null_config,
|
|
774
|
+
):
|
|
775
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
776
|
+
|
|
777
|
+
@dataclass
|
|
778
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
779
|
+
name: str
|
|
780
|
+
c: int
|
|
781
|
+
|
|
782
|
+
@dataclass
|
|
783
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
784
|
+
_child_objects = (
|
|
785
|
+
ChildObjectSpec(
|
|
786
|
+
name="b",
|
|
787
|
+
is_multiple=True,
|
|
788
|
+
class_obj=ObjB,
|
|
789
|
+
shared_data_name="bees",
|
|
790
|
+
shared_data_primary_key="name",
|
|
791
|
+
),
|
|
792
|
+
)
|
|
793
|
+
a: int
|
|
794
|
+
b: Any = None
|
|
795
|
+
|
|
796
|
+
dcts = [
|
|
797
|
+
{"name": "c1", "c": 2},
|
|
798
|
+
{"name": "c2", "c": 3},
|
|
799
|
+
]
|
|
800
|
+
|
|
801
|
+
obj_lst = ObjectList([ObjB(**i) for i in dcts])
|
|
802
|
+
|
|
803
|
+
js_in = {
|
|
804
|
+
"a": 9,
|
|
805
|
+
"b": [None, None],
|
|
806
|
+
}
|
|
807
|
+
objA = ObjA.from_json_like(js_in, shared_data={"bees": obj_lst})
|
|
808
|
+
assert objA == ObjA(a=9, b=[None, None])
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
def test_from_json_like_optional_attr_with_shared_data_name(null_config):
|
|
812
|
+
# test optional attribute with shared_data_name
|
|
813
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
814
|
+
|
|
815
|
+
@dataclass
|
|
816
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
817
|
+
name: str
|
|
818
|
+
c: int
|
|
819
|
+
|
|
820
|
+
@dataclass
|
|
821
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
822
|
+
_child_objects = (
|
|
823
|
+
ChildObjectSpec(
|
|
824
|
+
name="b",
|
|
825
|
+
class_obj=ObjB,
|
|
826
|
+
shared_data_name="bees",
|
|
827
|
+
shared_data_primary_key="name",
|
|
828
|
+
),
|
|
829
|
+
)
|
|
830
|
+
a: int
|
|
831
|
+
b: Any = None
|
|
832
|
+
|
|
833
|
+
dcts = [
|
|
834
|
+
{"name": "c1", "c": 2},
|
|
835
|
+
{"name": "c2", "c": 3},
|
|
836
|
+
]
|
|
837
|
+
|
|
838
|
+
obj_lst = ObjectList([ObjB(**i) for i in dcts])
|
|
839
|
+
|
|
840
|
+
js_in = {
|
|
841
|
+
"a": 9,
|
|
842
|
+
"b": None,
|
|
843
|
+
}
|
|
844
|
+
objA = ObjA.from_json_like(js_in, shared_data={"bees": obj_lst})
|
|
845
|
+
assert objA == ObjA(a=9, b=None)
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
def test_from_json_like_optional_attr_with_enum(null_config):
|
|
849
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
850
|
+
|
|
851
|
+
class MyEnum(enum.Enum):
|
|
852
|
+
A = 0
|
|
853
|
+
B = 1
|
|
854
|
+
C = 2
|
|
855
|
+
|
|
856
|
+
@dataclass
|
|
857
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
858
|
+
_child_objects = (ChildObjectSpec(name="b", class_obj=MyEnum, is_enum=True),)
|
|
859
|
+
a: int
|
|
860
|
+
b: Any = None
|
|
861
|
+
|
|
862
|
+
js_in = {
|
|
863
|
+
"a": 9,
|
|
864
|
+
"b": None,
|
|
865
|
+
}
|
|
866
|
+
objA = ObjA.from_json_like(js_in)
|
|
867
|
+
assert objA == ObjA(a=9, b=None)
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def test_from_json_like_with_is_multiple(null_config):
|
|
871
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
872
|
+
|
|
873
|
+
@dataclass
|
|
874
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
875
|
+
name: str
|
|
876
|
+
c: int
|
|
877
|
+
|
|
878
|
+
@dataclass
|
|
879
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
880
|
+
_child_objects = (ChildObjectSpec(name="b", class_obj=ObjB, is_multiple=True),)
|
|
881
|
+
a: int
|
|
882
|
+
b: Any
|
|
883
|
+
|
|
884
|
+
# e.g. from data files:
|
|
885
|
+
js_in = {
|
|
886
|
+
"a": 9,
|
|
887
|
+
"b": [{"name": "c1", "c": 2}, {"name": "c2", "c": 3}], # multiple
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
objA = ObjA.from_json_like(js_in)
|
|
891
|
+
assert objA == ObjA(a=9, b=[ObjB(name="c1", c=2), ObjB(name="c2", c=3)])
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
def test_from_json_like_with_is_multiple_dict_values(null_config):
|
|
895
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
896
|
+
|
|
897
|
+
@dataclass
|
|
898
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
899
|
+
name: str
|
|
900
|
+
c: int
|
|
901
|
+
|
|
902
|
+
@dataclass
|
|
903
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
904
|
+
_child_objects = (
|
|
905
|
+
ChildObjectSpec(
|
|
906
|
+
name="b",
|
|
907
|
+
class_obj=ObjB,
|
|
908
|
+
is_multiple=True,
|
|
909
|
+
is_dict_values=True,
|
|
910
|
+
),
|
|
911
|
+
)
|
|
912
|
+
a: int
|
|
913
|
+
b: Any
|
|
914
|
+
|
|
915
|
+
# e.g. from data files:
|
|
916
|
+
js_in = {
|
|
917
|
+
"a": 9,
|
|
918
|
+
"b": {
|
|
919
|
+
"key1": {"name": "c1", "c": 2},
|
|
920
|
+
"key2": {"name": "c2", "c": 3},
|
|
921
|
+
}, # multiple dict values, arbitrary keys, dict structure will be maintained
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
objA = ObjA.from_json_like(js_in)
|
|
925
|
+
assert objA == ObjA(
|
|
926
|
+
a=9,
|
|
927
|
+
b={"key1": ObjB(name="c1", c=2), "key2": ObjB(name="c2", c=3)},
|
|
928
|
+
)
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def test_from_json_like_with_is_multiple_dict_values_ensure_list(null_config):
|
|
932
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
933
|
+
|
|
934
|
+
@dataclass
|
|
935
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
936
|
+
name: str
|
|
937
|
+
c: int
|
|
938
|
+
|
|
939
|
+
@dataclass
|
|
940
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
941
|
+
_child_objects = (
|
|
942
|
+
ChildObjectSpec(
|
|
943
|
+
name="b",
|
|
944
|
+
class_obj=ObjB,
|
|
945
|
+
is_multiple=True,
|
|
946
|
+
is_dict_values=True,
|
|
947
|
+
is_dict_values_ensure_list=True,
|
|
948
|
+
),
|
|
949
|
+
)
|
|
950
|
+
a: int
|
|
951
|
+
b: Any
|
|
952
|
+
|
|
953
|
+
# e.g. from data files:
|
|
954
|
+
js_in = {
|
|
955
|
+
"a": 9,
|
|
956
|
+
"b": {
|
|
957
|
+
"key1": {"name": "c1", "c": 2},
|
|
958
|
+
"key2": [{"name": "c2", "c": 3}, {"name": "c3", "c": 4}],
|
|
959
|
+
},
|
|
960
|
+
# multiple dict values (and multiple items for each), arbitrary keys, dict
|
|
961
|
+
# structure will be maintained
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
objA = ObjA.from_json_like(js_in)
|
|
965
|
+
assert objA == ObjA(
|
|
966
|
+
a=9,
|
|
967
|
+
b={
|
|
968
|
+
"key1": [ObjB(name="c1", c=2)],
|
|
969
|
+
"key2": [ObjB(name="c2", c=3), ObjB(name="c3", c=4)],
|
|
970
|
+
},
|
|
971
|
+
)
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def test_from_json_like_round_trip_with_is_multiple_dict_values(null_config):
|
|
975
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
976
|
+
|
|
977
|
+
@dataclass
|
|
978
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
979
|
+
name: str
|
|
980
|
+
c: int
|
|
981
|
+
|
|
982
|
+
@dataclass
|
|
983
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
984
|
+
_child_objects = (
|
|
985
|
+
ChildObjectSpec(
|
|
986
|
+
name="b",
|
|
987
|
+
class_obj=ObjB,
|
|
988
|
+
is_multiple=True,
|
|
989
|
+
is_dict_values=True,
|
|
990
|
+
),
|
|
991
|
+
)
|
|
992
|
+
a: int
|
|
993
|
+
b: Any
|
|
994
|
+
|
|
995
|
+
# e.g. from data files:
|
|
996
|
+
js_in = {
|
|
997
|
+
"a": 9,
|
|
998
|
+
"b": {
|
|
999
|
+
"key1": {"name": "c1", "c": 2},
|
|
1000
|
+
"key2": {"name": "c2", "c": 3},
|
|
1001
|
+
}, # multiple dict values, arbitrary keys, dict structure will be maintained
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
objA = ObjA.from_json_like(js_in)
|
|
1005
|
+
assert objA.to_json_like()[0] == js_in
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
def test_from_json_like_round_trip_with_is_multiple_dict_values_ensure_list(null_config):
|
|
1009
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
1010
|
+
|
|
1011
|
+
@dataclass
|
|
1012
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
1013
|
+
name: str
|
|
1014
|
+
c: int
|
|
1015
|
+
|
|
1016
|
+
@dataclass
|
|
1017
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
1018
|
+
_child_objects = (
|
|
1019
|
+
ChildObjectSpec(
|
|
1020
|
+
name="b",
|
|
1021
|
+
class_obj=ObjB,
|
|
1022
|
+
is_multiple=True,
|
|
1023
|
+
is_dict_values=True,
|
|
1024
|
+
is_dict_values_ensure_list=True,
|
|
1025
|
+
),
|
|
1026
|
+
)
|
|
1027
|
+
a: int
|
|
1028
|
+
b: Any
|
|
1029
|
+
|
|
1030
|
+
# e.g. from data files:
|
|
1031
|
+
js_in = {
|
|
1032
|
+
"a": 9,
|
|
1033
|
+
"b": {
|
|
1034
|
+
"key1": [{"name": "c1", "c": 2}],
|
|
1035
|
+
"key2": [{"name": "c2", "c": 3}, {"name": "c3", "c": 4}],
|
|
1036
|
+
},
|
|
1037
|
+
# multiple dict values (and multiple items for each), arbitrary keys, dict
|
|
1038
|
+
# structure will be maintained
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
objA = ObjA.from_json_like(js_in)
|
|
1042
|
+
assert objA.to_json_like()[0] == js_in
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
def test_from_json_like_with_is_multiple_and_shared_data(null_config):
|
|
1046
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
1047
|
+
|
|
1048
|
+
@dataclass
|
|
1049
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
1050
|
+
name: str
|
|
1051
|
+
c: int
|
|
1052
|
+
|
|
1053
|
+
@dataclass
|
|
1054
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
1055
|
+
_child_objects = (
|
|
1056
|
+
ChildObjectSpec(
|
|
1057
|
+
name="b",
|
|
1058
|
+
class_obj=ObjB,
|
|
1059
|
+
is_multiple=True,
|
|
1060
|
+
shared_data_name="bees",
|
|
1061
|
+
shared_data_primary_key="name",
|
|
1062
|
+
),
|
|
1063
|
+
)
|
|
1064
|
+
a: int
|
|
1065
|
+
b: Any
|
|
1066
|
+
|
|
1067
|
+
dcts = [
|
|
1068
|
+
{"name": "c1", "c": 2},
|
|
1069
|
+
{"name": "c2", "c": 3},
|
|
1070
|
+
]
|
|
1071
|
+
|
|
1072
|
+
obj_lst = ObjectList([ObjB(**i) for i in dcts])
|
|
1073
|
+
|
|
1074
|
+
# e.g. from data files:
|
|
1075
|
+
js_in = {
|
|
1076
|
+
"a": 9,
|
|
1077
|
+
"b": ["c1", "c2"], # multiple from shared
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
objA = ObjA.from_json_like(js_in, shared_data={"bees": obj_lst})
|
|
1081
|
+
assert objA == ObjA(a=9, b=[ObjB(name="c1", c=2), ObjB(name="c2", c=3)])
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
def test_from_json_like_with_is_multiple_and_shared_data_dict_lookup(null_config):
|
|
1085
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
1086
|
+
|
|
1087
|
+
@dataclass
|
|
1088
|
+
class ObjB(BaseJSONLikeSubClass):
|
|
1089
|
+
name: str
|
|
1090
|
+
c: int
|
|
1091
|
+
_hash_value: str | None = None
|
|
1092
|
+
|
|
1093
|
+
@dataclass
|
|
1094
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
1095
|
+
_child_objects = (
|
|
1096
|
+
ChildObjectSpec(
|
|
1097
|
+
name="b",
|
|
1098
|
+
class_obj=ObjB,
|
|
1099
|
+
is_multiple=True,
|
|
1100
|
+
dict_key_attr="name",
|
|
1101
|
+
shared_data_name="bees",
|
|
1102
|
+
shared_data_primary_key="name",
|
|
1103
|
+
),
|
|
1104
|
+
)
|
|
1105
|
+
a: int
|
|
1106
|
+
b: Any
|
|
1107
|
+
|
|
1108
|
+
dcts = [
|
|
1109
|
+
{"name": "c1", "c": 2},
|
|
1110
|
+
{"name": "c2", "c": 3},
|
|
1111
|
+
]
|
|
1112
|
+
|
|
1113
|
+
obj_lst = ObjectList([ObjB(**i) for i in dcts])
|
|
1114
|
+
|
|
1115
|
+
# e.g. from data files:
|
|
1116
|
+
js_in = {
|
|
1117
|
+
"a": 9,
|
|
1118
|
+
"b": {
|
|
1119
|
+
"c1": {"c": 2},
|
|
1120
|
+
"c2": {"c": 3},
|
|
1121
|
+
}, # multiple from shared as dict with lookup kwargs
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
objA = ObjA.from_json_like(js_in, shared_data={"bees": obj_lst})
|
|
1125
|
+
assert objA == ObjA(a=9, b=[ObjB(name="c1", c=2), ObjB(name="c2", c=3)])
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
def test_from_json_like_enum(null_config):
|
|
1129
|
+
# test enum from_json_like
|
|
1130
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
1131
|
+
|
|
1132
|
+
class MyEnum(enum.Enum):
|
|
1133
|
+
A = 0
|
|
1134
|
+
B = 1
|
|
1135
|
+
C = 2
|
|
1136
|
+
|
|
1137
|
+
@dataclass
|
|
1138
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
1139
|
+
_child_objects = (ChildObjectSpec(name="b", class_obj=MyEnum, is_enum=True),)
|
|
1140
|
+
a: int
|
|
1141
|
+
b: Any
|
|
1142
|
+
|
|
1143
|
+
js_in = {
|
|
1144
|
+
"a": 9,
|
|
1145
|
+
"b": "A",
|
|
1146
|
+
}
|
|
1147
|
+
objA = ObjA.from_json_like(js_in)
|
|
1148
|
+
assert objA == ObjA(a=9, b=MyEnum.A)
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
def test_from_to_json_round_trip_enum(null_config):
|
|
1152
|
+
# test enum round trip
|
|
1153
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
1154
|
+
|
|
1155
|
+
class MyEnum(enum.Enum):
|
|
1156
|
+
A = 0
|
|
1157
|
+
B = 1
|
|
1158
|
+
C = 2
|
|
1159
|
+
|
|
1160
|
+
@dataclass
|
|
1161
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
1162
|
+
_child_objects = (ChildObjectSpec(name="b", class_obj=MyEnum, is_enum=True),)
|
|
1163
|
+
a: int
|
|
1164
|
+
b: Any
|
|
1165
|
+
|
|
1166
|
+
js_in = {
|
|
1167
|
+
"a": 9,
|
|
1168
|
+
"b": "A",
|
|
1169
|
+
}
|
|
1170
|
+
objA = ObjA.from_json_like(js_in)
|
|
1171
|
+
assert objA.to_json_like()[0] == js_in
|
|
1172
|
+
|
|
1173
|
+
|
|
1174
|
+
def test_from_json_like_round_trip_enum_case_insensitivity(null_config):
|
|
1175
|
+
# test enum from_json_like case-insensitivity
|
|
1176
|
+
BaseJSONLikeSubClass = type("MyBaseJSONLike", (BaseJSONLike,), {})
|
|
1177
|
+
|
|
1178
|
+
class MyEnum(enum.Enum):
|
|
1179
|
+
A = 0
|
|
1180
|
+
B = 1
|
|
1181
|
+
C = 2
|
|
1182
|
+
|
|
1183
|
+
@dataclass
|
|
1184
|
+
class ObjA(BaseJSONLikeSubClass):
|
|
1185
|
+
_child_objects = (ChildObjectSpec(name="b", class_obj=MyEnum, is_enum=True),)
|
|
1186
|
+
a: int
|
|
1187
|
+
b: Any
|
|
1188
|
+
|
|
1189
|
+
js_in_1 = {
|
|
1190
|
+
"a": 9,
|
|
1191
|
+
"b": "a",
|
|
1192
|
+
}
|
|
1193
|
+
js_in_2 = {
|
|
1194
|
+
"a": 9,
|
|
1195
|
+
"b": "A",
|
|
1196
|
+
}
|
|
1197
|
+
objA_1 = ObjA.from_json_like(js_in_1)
|
|
1198
|
+
objA_2 = ObjA.from_json_like(js_in_2)
|
|
1199
|
+
assert objA_1 == objA_2
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
@pytest.mark.skip(
|
|
1203
|
+
reason=(
|
|
1204
|
+
"We currently cull parent references in `JSONLike.to_dict`. This should ideally "
|
|
1205
|
+
"be in BaseJSONLike.to_dict, which would allow this test to pass. However, "
|
|
1206
|
+
"culling involves looping over app._core_classes, which we cannot access from "
|
|
1207
|
+
"this class."
|
|
1208
|
+
)
|
|
1209
|
+
)
|
|
1210
|
+
def test_to_json_like_with_child_ref(null_config):
|
|
1211
|
+
"""i.e. check parent references are not included in child item to_json_like output:"""
|
|
1212
|
+
|
|
1213
|
+
@dataclass
|
|
1214
|
+
class ObjB(BaseJSONLike):
|
|
1215
|
+
name: str
|
|
1216
|
+
c: int
|
|
1217
|
+
obj_A: Any = None
|
|
1218
|
+
|
|
1219
|
+
@dataclass
|
|
1220
|
+
class ObjA(BaseJSONLike):
|
|
1221
|
+
_child_objects = (
|
|
1222
|
+
ChildObjectSpec(
|
|
1223
|
+
name="b",
|
|
1224
|
+
class_obj=ObjB,
|
|
1225
|
+
parent_ref="obj_A",
|
|
1226
|
+
),
|
|
1227
|
+
)
|
|
1228
|
+
a: int
|
|
1229
|
+
b: float
|
|
1230
|
+
|
|
1231
|
+
def __post_init__(self):
|
|
1232
|
+
self._set_parent_refs()
|
|
1233
|
+
|
|
1234
|
+
objB = ObjB(name="c1", c=8)
|
|
1235
|
+
objA = ObjA(a=1, b=objB)
|
|
1236
|
+
|
|
1237
|
+
js_1 = {
|
|
1238
|
+
"a": 1,
|
|
1239
|
+
"b": {
|
|
1240
|
+
"name": "c1",
|
|
1241
|
+
"c": 8,
|
|
1242
|
+
},
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
objA = ObjA.from_json_like(js_1)
|
|
1246
|
+
|
|
1247
|
+
assert objA.b.obj_A == objA and objA.b.to_json_like()[0] == js_1["b"]
|