py-modelrunner 0.18.0__tar.gz → 0.18.1__tar.gz
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.
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/PKG-INFO +1 -1
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/source/user_manual.rst +13 -13
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/_version.py +2 -2
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/job.py +6 -3
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/results.py +1 -1
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/backend/text_base.py +1 -1
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/utils.py +17 -5
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/py_modelrunner.egg-info/PKG-INFO +1 -1
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/py_modelrunner.egg-info/SOURCES.txt +3 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/scripts/run_tests.py +16 -0
- py_modelrunner-0.18.1/scripts/tests_debug.sh +16 -0
- py_modelrunner-0.18.1/tests/run/scripts/custom.jinja +19 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/run/test_job.py +16 -2
- py_modelrunner-0.18.1/tests/storage/test_register_action.py +155 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/storage/test_storage_generic.py +1 -85
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/.github/workflows/build.yml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/.github/workflows/check_docs.yml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/.github/workflows/coverage_report.yml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/.github/workflows/release_pip.yml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/.gitignore +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/.readthedocs.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/LICENSE +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/README.md +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/Makefile +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/build_all.sh +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/requirements.txt +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/source/.gitignore +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/source/conf.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/source/examples.rst +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/source/index.rst +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/source/installation.rst +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/source/parse_examples.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/source/run_autodoc.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/docs/source/sphinx_simplify_typehints.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/io_hdf.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/io_json.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/io_yaml.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/model_class.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/model_decorator.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/model_function.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/model_storage_output.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/read_many.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/script_custom_func.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/script_function.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/script_many_classes.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/script_minimal.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/script_model_class.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/submit_function.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/submit_many_func.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/submit_many_iter.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/examples/submit_shebang.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/.gitignore +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/__init__.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/__main__.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/config.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/model/__init__.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/model/base.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/model/factory.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/model/parameters.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/py.typed +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/__init__.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/__main__.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/compatibility/__init__.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/compatibility/triage.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/compatibility/version0.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/compatibility/version1.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/compatibility/version2.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/launch.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/templates/background.jinja +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/templates/foreground.jinja +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/templates/qsub.jinja +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/run/templates/srun.jinja +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/__init__.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/access_modes.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/attributes.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/backend/__init__.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/backend/hdf.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/backend/json.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/backend/memory.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/backend/utils.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/backend/yaml.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/backend/zarr.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/base.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/group.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/tools.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/storage/trajectory.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/modelrunner/utils.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/py_modelrunner.egg-info/dependency_links.txt +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/py_modelrunner.egg-info/not-zip-safe +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/py_modelrunner.egg-info/requires.txt +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/py_modelrunner.egg-info/top_level.txt +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/pyproject.toml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/requirements.txt +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/requirements_full.txt +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/scripts/create_compatibility_files.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/scripts/format_code.sh +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/scripts/performance_hdf5_zarr.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/scripts/tests_all.sh +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/scripts/tests_codestyle.sh +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/scripts/tests_coverage.sh +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/scripts/tests_parallel.sh +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/scripts/tests_run.sh +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/scripts/tests_types.sh +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/setup.cfg +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/0/result.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/0/result.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/0/result.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/0/result.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/array.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/array.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/array.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/array.zarr +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/array_col.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/array_col.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/array_col.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/array_col.zarr +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/dict.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/dict.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/dict.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/dict.zarr +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/object.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/object.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/object.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/1/object.zarr +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/array.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/array.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/array.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/array.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/array.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/array_col.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/array_col.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/array_col.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/array_col.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/array_col.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/dict.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/dict.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/dict.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/dict.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/dict.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/nested.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/nested.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/nested.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/nested.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/2/nested.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/array.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/array.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/array.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/array.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/array.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/array_col.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/array_col.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/array_col.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/array_col.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/array_col.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/dict.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/dict.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/dict.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/dict.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/dict.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_array.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_array.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_array.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_array.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_array.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_array_col.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_array_col.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_array_col.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_array_col.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_array_col.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_dict.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_dict.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_dict.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_dict.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_dict.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_nested.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_nested.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_nested.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_nested.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/model_nested.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/nested.hdf +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/nested.json +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/nested.pkl +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/nested.yaml +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/3/nested.zip +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/compatibility/test_compatibility.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/conftest.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/helpers/__init__.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/helpers/asserts.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/helpers/storage.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/model/scripts/empty.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/model/scripts/function.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/model/scripts/function_main.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/model/scripts/function_marked.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/model/scripts/make_model.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/model/scripts/make_model_class.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/model/scripts/make_model_marked.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/model/test_model.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/model/test_parameters.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/requirements.txt +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/run/scripts/fail.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/run/scripts/function.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/run/scripts/print.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/run/scripts/script.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/run/test_results.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/run/test_run_main.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/storage/test_group.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/storage/test_storage_special.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/storage/test_trajectory.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/storage/test_utils.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/test_config.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/test_examples.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/test_main.py +0 -0
- {py_modelrunner-0.18.0 → py_modelrunner-0.18.1}/tests/test_version.py +0 -0
|
@@ -20,33 +20,33 @@ Defining parameters using :mod:`~modelrunner.model.parameters`:
|
|
|
20
20
|
Defining models using :mod:`~modelrunner.model`:
|
|
21
21
|
Models are augmented functions, which define input, calculations, and output.
|
|
22
22
|
Models can be conveniently created by decorating a function or by subclassing
|
|
23
|
-
:class:`~modelrunner.model.ModelBase`, which is built on the parameter classes.
|
|
23
|
+
:class:`~modelrunner.model.base.ModelBase`, which is built on the parameter classes.
|
|
24
24
|
|
|
25
|
-
Model results are captured by :mod:`~modelrunner.results`:
|
|
26
|
-
Results are returned as the special :class:`~modelrunner.results.Result`, which
|
|
25
|
+
Model results are captured by :mod:`~modelrunner.run.results`:
|
|
26
|
+
Results are returned as the special :class:`~modelrunner.run.results.Result`, which
|
|
27
27
|
keeps track of the input parameters and the data calculated by the model.
|
|
28
|
-
:class:`~modelrunner.results.ResultCollection` describes a collections of the same
|
|
28
|
+
:class:`~modelrunner.run.results.ResultCollection` describes a collections of the same
|
|
29
29
|
model evoked with different parameter values.
|
|
30
30
|
|
|
31
31
|
Submitting models to HPC using :mod:`~modelrunner.run`:
|
|
32
32
|
A single model can be submitted to a compute node using :func:`~modelrunner.run.job.submit_job`,
|
|
33
33
|
e.g., to run the computation on a high performance compute cluster.
|
|
34
34
|
A parameter study using multiple jobs can be conveniently submitted using :func:`~modelrunner.run.job.submit_jobs`.
|
|
35
|
-
The results written to one directory can then be conveniently analyzed using :
|
|
35
|
+
The results written to one directory can then be conveniently analyzed using :func:`~modelrunner.run.results.ResultCollection.from_folder`.
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
Design philosophy
|
|
39
39
|
-----------------
|
|
40
40
|
|
|
41
|
-
The main requirements for the
|
|
41
|
+
The main requirements for the package can be summarized as follows:
|
|
42
42
|
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
43
|
+
- **Usability**: The user should not need to think about how data is stored in different files. The :class:`~modelrunner.run.results.Result` class should simply work.
|
|
44
|
+
- **Flexibility**: :mod:`~modelrunner.storage` should provide a unified interface to write data in multiple file formats (JSON, YAML, HDF, zarr, ...)
|
|
45
|
+
- **Stability**: Future versions of the package should be able to read older files even when the internal definitions of file formats change
|
|
46
|
+
- **Modularity**: Different parts of the package (like :mod:`~modelrunner.storage`, :mod:`~modelrunner.model.parameters`, and :mod:`~modelrunner.run`) should be rather independent of each other, so they can be used in isolation
|
|
47
|
+
- **Extensibility**: Models inherting from :class:`~modelrunner.model.base.ModelBase` should be easy to subclass to implement more complicated requirements (e.g., additional parameters)
|
|
48
|
+
- **Self-explainability**: The files should in principle contain all information to reconstruct the data, even if the :mod:`modelrunner` package is no longer available.
|
|
49
|
+
- **Efficiency**: The files should only store necessary information.
|
|
50
50
|
|
|
51
51
|
The last point results in particular constraints if we want to store temporal simulation results.
|
|
52
52
|
In most cases, there are are some data that are kept fixed for the simulation (describing physical parameters) and others that evolve with time.
|
|
@@ -219,10 +219,13 @@ def submit_job(
|
|
|
219
219
|
job_args = []
|
|
220
220
|
if parameters is not None and len(parameters) > 0:
|
|
221
221
|
if isinstance(parameters, dict):
|
|
222
|
-
|
|
223
|
-
elif
|
|
222
|
+
parameters_json = json.dumps(parameters)
|
|
223
|
+
elif isinstance(parameters, str):
|
|
224
|
+
parameters_json = parameters
|
|
225
|
+
else:
|
|
224
226
|
raise TypeError("Parameters need to be given as a string or a dict")
|
|
225
|
-
job_args.append(f"--json {escape_string(
|
|
227
|
+
job_args.append(f"--json {escape_string(parameters_json)}")
|
|
228
|
+
script_args["PARAMETERS"] = parameters # allow using parameters in job script
|
|
226
229
|
|
|
227
230
|
logger.debug("Job arguments: `%s`", job_args)
|
|
228
231
|
|
|
@@ -55,7 +55,7 @@ class Result:
|
|
|
55
55
|
The result is represented as a hierarchical structure when safed using the
|
|
56
56
|
:mod:`~modelrunner.storage`. The actual result is stored in the `result` group,
|
|
57
57
|
whereas the model information can be found in `_model` group. Additional
|
|
58
|
-
information is stored in the
|
|
58
|
+
information is stored in the `storage` group. Thus, the full :class:`Result` can
|
|
59
59
|
be read using :code:`storage[loc]`, where `loc` denotes the result location. If
|
|
60
60
|
only the actual result is needed, :code:`storage[loc + "/result"]` can be read.
|
|
61
61
|
"""
|
|
@@ -80,7 +80,7 @@ class TextStorageBase(MemoryStorage, metaclass=ABCMeta):
|
|
|
80
80
|
elif self._modified:
|
|
81
81
|
# The storage was modified, but it cannot be written to the file. This
|
|
82
82
|
# should not happen, but it's better to throw an explicit error
|
|
83
|
-
raise AccessError("Cannot write to file")
|
|
83
|
+
raise AccessError("Cannot write modifications to file opened read-only")
|
|
84
84
|
|
|
85
85
|
def close(self) -> None:
|
|
86
86
|
"""close the file and write the data to the file"""
|
|
@@ -159,13 +159,20 @@ class _StorageRegistry:
|
|
|
159
159
|
allowed_actions = set(ActionType.__args__) # type: ignore
|
|
160
160
|
"""set: all actions that can be registered"""
|
|
161
161
|
|
|
162
|
-
_hooks: dict[type, dict[str, Callable]]
|
|
162
|
+
_hooks: dict[type, dict[str, tuple[Callable, bool]]]
|
|
163
163
|
"""dict: register for all defined hooks"""
|
|
164
164
|
|
|
165
165
|
def __init__(self):
|
|
166
166
|
self._hooks = defaultdict(dict)
|
|
167
167
|
|
|
168
|
-
def register(
|
|
168
|
+
def register(
|
|
169
|
+
self,
|
|
170
|
+
action: ActionType,
|
|
171
|
+
cls: type,
|
|
172
|
+
method_or_func: Callable,
|
|
173
|
+
*,
|
|
174
|
+
inherit: bool = True,
|
|
175
|
+
) -> None:
|
|
169
176
|
"""register an action for the given class
|
|
170
177
|
|
|
171
178
|
Example:
|
|
@@ -182,6 +189,9 @@ class _StorageRegistry:
|
|
|
182
189
|
The class this action is associated with
|
|
183
190
|
method_or_func (callable):
|
|
184
191
|
The function/method that is called for the action
|
|
192
|
+
inherit (bool):
|
|
193
|
+
Determines whether child classes of `cls` inherit this action and will
|
|
194
|
+
be able to use it.
|
|
185
195
|
"""
|
|
186
196
|
if action not in self.allowed_actions:
|
|
187
197
|
raise ValueError(f"Unknown action `{action}` ")
|
|
@@ -192,9 +202,9 @@ class _StorageRegistry:
|
|
|
192
202
|
"""helper function to call the classmethod"""
|
|
193
203
|
return method_or_func(cls, *args, **kwargs)
|
|
194
204
|
|
|
195
|
-
self._hooks[cls][action] = _call_classmethod
|
|
205
|
+
self._hooks[cls][action] = (_call_classmethod, inherit)
|
|
196
206
|
elif callable(method_or_func):
|
|
197
|
-
self._hooks[cls][action] = method_or_func
|
|
207
|
+
self._hooks[cls][action] = (method_or_func, inherit)
|
|
198
208
|
else:
|
|
199
209
|
raise TypeError("`method_or_func` must be method or function")
|
|
200
210
|
|
|
@@ -214,7 +224,9 @@ class _StorageRegistry:
|
|
|
214
224
|
classes = inspect.getmro(cls)[:-1]
|
|
215
225
|
for c in classes:
|
|
216
226
|
if c in self._hooks and action in self._hooks[c]:
|
|
217
|
-
|
|
227
|
+
func, inherit = self._hooks[c][action]
|
|
228
|
+
if inherit or c is cls:
|
|
229
|
+
return func
|
|
218
230
|
|
|
219
231
|
raise RuntimeError(f"No action `{action}` for `{cls.__name__}`")
|
|
220
232
|
|
|
@@ -92,6 +92,7 @@ scripts/run_tests.py
|
|
|
92
92
|
scripts/tests_all.sh
|
|
93
93
|
scripts/tests_codestyle.sh
|
|
94
94
|
scripts/tests_coverage.sh
|
|
95
|
+
scripts/tests_debug.sh
|
|
95
96
|
scripts/tests_parallel.sh
|
|
96
97
|
scripts/tests_run.sh
|
|
97
98
|
scripts/tests_types.sh
|
|
@@ -197,11 +198,13 @@ tests/model/scripts/make_model_marked.py
|
|
|
197
198
|
tests/run/test_job.py
|
|
198
199
|
tests/run/test_results.py
|
|
199
200
|
tests/run/test_run_main.py
|
|
201
|
+
tests/run/scripts/custom.jinja
|
|
200
202
|
tests/run/scripts/fail.py
|
|
201
203
|
tests/run/scripts/function.py
|
|
202
204
|
tests/run/scripts/print.py
|
|
203
205
|
tests/run/scripts/script.py
|
|
204
206
|
tests/storage/test_group.py
|
|
207
|
+
tests/storage/test_register_action.py
|
|
205
208
|
tests/storage/test_storage_generic.py
|
|
206
209
|
tests/storage/test_storage_special.py
|
|
207
210
|
tests/storage/test_trajectory.py
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import argparse
|
|
4
6
|
import os
|
|
5
7
|
import subprocess as sp
|
|
@@ -66,6 +68,7 @@ def run_unit_tests(
|
|
|
66
68
|
coverage: bool = False,
|
|
67
69
|
no_numba: bool = False,
|
|
68
70
|
pattern: str = None,
|
|
71
|
+
pytest_args: list[str] = [],
|
|
69
72
|
) -> int:
|
|
70
73
|
"""run the unit tests
|
|
71
74
|
|
|
@@ -74,6 +77,9 @@ def run_unit_tests(
|
|
|
74
77
|
coverage (bool): Whether to determine the test coverage
|
|
75
78
|
no_numba (bool): Whether to disable numba jit compilation
|
|
76
79
|
pattern (str): A pattern that determines which tests are ran
|
|
80
|
+
pytest_args (list of str):
|
|
81
|
+
Additional arguments forwarded to py.test. For instance ["--maxfail=1"]
|
|
82
|
+
fails tests early.
|
|
77
83
|
|
|
78
84
|
Returns:
|
|
79
85
|
int: The return code indicating success or failure
|
|
@@ -117,6 +123,8 @@ def run_unit_tests(
|
|
|
117
123
|
]
|
|
118
124
|
)
|
|
119
125
|
|
|
126
|
+
args.extend(pytest_args)
|
|
127
|
+
|
|
120
128
|
# specify the package to run
|
|
121
129
|
args.append("tests")
|
|
122
130
|
|
|
@@ -191,6 +199,13 @@ def main():
|
|
|
191
199
|
help="Write a report of the results",
|
|
192
200
|
)
|
|
193
201
|
|
|
202
|
+
# set py.test arguments
|
|
203
|
+
group = parser.add_argument_group(
|
|
204
|
+
"py.test arguments",
|
|
205
|
+
description="Additional arguments separated by `--` are forward to py.test",
|
|
206
|
+
)
|
|
207
|
+
group.add_argument("pytest_args", nargs="*", help=argparse.SUPPRESS)
|
|
208
|
+
|
|
194
209
|
# parse the command line arguments
|
|
195
210
|
args = parser.parse_args()
|
|
196
211
|
run_all = not (args.style or args.types or args.unit)
|
|
@@ -206,6 +221,7 @@ def main():
|
|
|
206
221
|
parallel=args.parallel,
|
|
207
222
|
no_numba=args.no_numba,
|
|
208
223
|
pattern=args.pattern,
|
|
224
|
+
pytest_args=args.pytest_args,
|
|
209
225
|
)
|
|
210
226
|
|
|
211
227
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
export PYTHONPATH=../py-modelrunner # likely path of package, relative to current base path
|
|
4
|
+
|
|
5
|
+
if [ ! -z $1 ]
|
|
6
|
+
then
|
|
7
|
+
# test pattern was specified
|
|
8
|
+
echo 'Run unittests with pattern '$1':'
|
|
9
|
+
./run_tests.py --unit --pattern "$1" -- \
|
|
10
|
+
-o log_cli=true --log-cli-level=debug -vv
|
|
11
|
+
else
|
|
12
|
+
# test pattern was not specified
|
|
13
|
+
echo 'Run all unittests:'
|
|
14
|
+
./run_tests.py --unit -- \
|
|
15
|
+
-o log_cli=true --log-cli-level=debug -vv
|
|
16
|
+
fi
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/bin/bash -l
|
|
2
|
+
|
|
3
|
+
export PYTHONPATH={{ PACKAGE_PATH }}:$PYTHONPATH
|
|
4
|
+
|
|
5
|
+
{% if CONFIG.num_threads is number %}
|
|
6
|
+
# set the number of threads to use
|
|
7
|
+
export MKL_NUM_THREADS={{ CONFIG.num_threads }}
|
|
8
|
+
export NUMBA_NUM_THREADS={{ CONFIG.num_threads }}
|
|
9
|
+
export NUMEXPR_NUM_THREADS={{ CONFIG.num_threads }}
|
|
10
|
+
export OMP_NUM_THREADS={{ CONFIG.num_threads }}
|
|
11
|
+
export OPENBLAS_NUM_THREADS={{ CONFIG.num_threads }}
|
|
12
|
+
{% endif %}
|
|
13
|
+
|
|
14
|
+
{% if OUTPUT_FOLDER is defined and OUTPUT_FOLDER %}
|
|
15
|
+
mkdir -p {{ OUTPUT_FOLDER }}
|
|
16
|
+
{% endif %}
|
|
17
|
+
|
|
18
|
+
# Run the program
|
|
19
|
+
{{ CONFIG.python_bin }} -m modelrunner {{ MODEL_FILE }} --a {{ PARAMETERS.a }}
|
|
@@ -57,8 +57,8 @@ def test_submit_job_stdout(tmp_path, method):
|
|
|
57
57
|
overwrite_strategy="silent_overwrite",
|
|
58
58
|
)
|
|
59
59
|
|
|
60
|
-
assert outs == "3.0\n"
|
|
61
60
|
assert errs == ""
|
|
61
|
+
assert outs == "3.0\n"
|
|
62
62
|
assert Result.from_file(output).result is None
|
|
63
63
|
|
|
64
64
|
|
|
@@ -69,8 +69,8 @@ def test_submit_job_no_output():
|
|
|
69
69
|
method="foreground",
|
|
70
70
|
overwrite_strategy="silent_overwrite",
|
|
71
71
|
)
|
|
72
|
-
assert outs == "3.0\n"
|
|
73
72
|
assert errs == ""
|
|
73
|
+
assert outs == "3.0\n"
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
def test_submit_jobs(tmp_path):
|
|
@@ -137,3 +137,17 @@ def test_submit_job_no_modelrunner(tmp_path):
|
|
|
137
137
|
|
|
138
138
|
assert run() == ("", "")
|
|
139
139
|
assert run(a=1) == ('--json{"a": 1}', "")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_submit_job_own_template(tmp_path):
|
|
143
|
+
"""test the submit_job function with a custom template"""
|
|
144
|
+
outs, errs = submit_job(
|
|
145
|
+
SCRIPT_PATH / "print.py",
|
|
146
|
+
method="foreground",
|
|
147
|
+
parameters={"a": 5, "b": 10}, # b is not used by template
|
|
148
|
+
template=SCRIPT_PATH / "custom.jinja",
|
|
149
|
+
overwrite_strategy="silent_overwrite",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
assert errs == ""
|
|
153
|
+
assert outs == "7.0\n" # a + 2 (default value of b)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""
|
|
2
|
+
.. codeauthor:: David Zwicker <david.zwicker@ds.mpg.de>
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from helpers import STORAGE_EXT, STORAGE_OBJECTS, assert_data_equals
|
|
8
|
+
from modelrunner.storage import StorageGroup, open_storage, storage_actions
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class A:
|
|
12
|
+
|
|
13
|
+
def __init__(self, value):
|
|
14
|
+
self.value = value
|
|
15
|
+
self.loaded = False
|
|
16
|
+
|
|
17
|
+
def save(self, storage, loc):
|
|
18
|
+
storage.write_object(loc, self.value, cls=self.__class__)
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def load(cls, storage, loc):
|
|
22
|
+
obj = cls(storage.read_object(loc))
|
|
23
|
+
obj.loaded = True
|
|
24
|
+
return obj
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def save_A(storage, loc, obj):
|
|
28
|
+
obj.save(storage, loc)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
storage_actions.register("read_item", A, A.load, inherit=True)
|
|
32
|
+
storage_actions.register("write_item", A, save_A, inherit=True)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class A1(A): ...
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class A2(A):
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def load(cls, storage, loc):
|
|
42
|
+
obj = cls(storage.read_object(loc))
|
|
43
|
+
obj.loaded = "yes"
|
|
44
|
+
return obj
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
storage_actions.register("read_item", A2, A2.load, inherit=False)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class B:
|
|
51
|
+
|
|
52
|
+
def __init__(self, a, b):
|
|
53
|
+
self.a = a
|
|
54
|
+
self.b = b
|
|
55
|
+
self.loaded = False
|
|
56
|
+
|
|
57
|
+
def save(self, storage, loc):
|
|
58
|
+
group = storage.create_group(loc, cls=self.__class__)
|
|
59
|
+
self.a.save(group, "a")
|
|
60
|
+
group.write_object("b", self.b)
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def load(cls, storage, loc):
|
|
64
|
+
group = StorageGroup(storage, loc)
|
|
65
|
+
obj = cls(a=group["a"], b=group.read_object("b"))
|
|
66
|
+
obj.loaded = True
|
|
67
|
+
return obj
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
storage_actions.register("read_item", B, B.load, inherit=False)
|
|
71
|
+
storage_actions.register("write_item", B, lambda s, l, o: o.save(s, l), inherit=False)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class B1(B): ...
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.mark.parametrize("ext", STORAGE_EXT)
|
|
78
|
+
def test_register_class_action(ext, tmp_path):
|
|
79
|
+
"""test loading custom classes"""
|
|
80
|
+
|
|
81
|
+
a = A({"a": 1})
|
|
82
|
+
assert not a.loaded
|
|
83
|
+
|
|
84
|
+
with open_storage(tmp_path / f"file{ext}", mode="truncate") as store:
|
|
85
|
+
store["a"] = a
|
|
86
|
+
|
|
87
|
+
with open_storage(tmp_path / f"file{ext}", mode="read") as store:
|
|
88
|
+
b = store["a"]
|
|
89
|
+
|
|
90
|
+
assert b.loaded
|
|
91
|
+
assert b.value == {"a": 1}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@pytest.mark.parametrize("ext", STORAGE_EXT)
|
|
95
|
+
def test_register_class_action_nested(tmp_path, ext):
|
|
96
|
+
"""test loading custom nested classes"""
|
|
97
|
+
|
|
98
|
+
a = A({"a": 1})
|
|
99
|
+
b = B(a, "str")
|
|
100
|
+
assert not a.loaded and not b.loaded
|
|
101
|
+
|
|
102
|
+
with open_storage(tmp_path / f"file{ext}", mode="truncate") as store:
|
|
103
|
+
store["data"] = b
|
|
104
|
+
|
|
105
|
+
with open_storage(tmp_path / f"file{ext}", mode="read") as store:
|
|
106
|
+
res = store["data"]
|
|
107
|
+
|
|
108
|
+
assert res.loaded and res.a.loaded
|
|
109
|
+
assert res.a.value == {"a": 1}
|
|
110
|
+
assert res.b == "str"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@pytest.mark.parametrize("ext", STORAGE_EXT)
|
|
114
|
+
def test_register_class_action_subclass(ext, tmp_path):
|
|
115
|
+
"""test loading custom classes and subclasses"""
|
|
116
|
+
|
|
117
|
+
a = A({"a": 1})
|
|
118
|
+
a1 = A1({"b": 2}) # has not defined hooks itself
|
|
119
|
+
a2 = A2({"c": 3}) # has own loading hook
|
|
120
|
+
assert not a.loaded and not a1.loaded and not a2.loaded
|
|
121
|
+
|
|
122
|
+
with open_storage(tmp_path / f"file{ext}", mode="truncate") as store:
|
|
123
|
+
store["a"] = a
|
|
124
|
+
store["a1"] = a1
|
|
125
|
+
store["a2"] = a2
|
|
126
|
+
|
|
127
|
+
with open_storage(tmp_path / f"file{ext}", mode="read") as store:
|
|
128
|
+
b = store["a"]
|
|
129
|
+
b1 = store["a1"]
|
|
130
|
+
b2 = store["a2"]
|
|
131
|
+
|
|
132
|
+
assert b.loaded and b1.loaded and b2.loaded == "yes"
|
|
133
|
+
assert b.value == {"a": 1} and b1.value == {"b": 2} and b2.value == {"c": 3}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@pytest.mark.parametrize("ext", STORAGE_EXT)
|
|
137
|
+
def test_register_class_action_no_inhert(ext, tmp_path):
|
|
138
|
+
"""test loading custom classes and subclasses"""
|
|
139
|
+
|
|
140
|
+
a = B(A({"a": 1}), 2) # does not use hooks that inherit
|
|
141
|
+
a1 = B1(A({"a": 2}), 4) # has not defined hooks itself
|
|
142
|
+
assert not a.loaded and not a1.loaded
|
|
143
|
+
|
|
144
|
+
with open_storage(tmp_path / f"file{ext}", mode="truncate") as store:
|
|
145
|
+
store["b"] = a
|
|
146
|
+
store["b1"] = a1
|
|
147
|
+
|
|
148
|
+
with open_storage(tmp_path / f"file{ext}", mode="read") as store:
|
|
149
|
+
b = store["b"]
|
|
150
|
+
b1 = store["b1"]
|
|
151
|
+
|
|
152
|
+
assert b.loaded # B uses the hook class
|
|
153
|
+
assert not b1.loaded # B2 does not yse the hook classes
|
|
154
|
+
assert b.a.value == {"a": 1} and b.b == 2
|
|
155
|
+
assert b1.a.value == {"a": 2} and b1.b == 4
|
|
@@ -6,7 +6,7 @@ import numpy as np
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
8
|
from helpers import STORAGE_EXT, STORAGE_OBJECTS, assert_data_equals
|
|
9
|
-
from modelrunner.storage import AccessError,
|
|
9
|
+
from modelrunner.storage import AccessError, open_storage
|
|
10
10
|
|
|
11
11
|
OBJ = {"a": 1, "b": np.arange(5)}
|
|
12
12
|
ARRAY_EXAMPLES = [
|
|
@@ -328,87 +328,3 @@ def test_storage_close(ext, tmp_path):
|
|
|
328
328
|
assert reader["obj"] == 1
|
|
329
329
|
reader.close()
|
|
330
330
|
assert reader.closed
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
class A:
|
|
334
|
-
|
|
335
|
-
def __init__(self, value):
|
|
336
|
-
self.value = value
|
|
337
|
-
self.loaded = False
|
|
338
|
-
|
|
339
|
-
def save(self, storage, loc):
|
|
340
|
-
storage.write_object(loc, self.value, cls=self.__class__)
|
|
341
|
-
|
|
342
|
-
@classmethod
|
|
343
|
-
def load(cls, storage, loc):
|
|
344
|
-
obj = cls(storage.read_object(loc))
|
|
345
|
-
obj.loaded = True
|
|
346
|
-
return obj
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
def save_A(storage, loc, obj):
|
|
350
|
-
obj.save(storage, loc)
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
storage_actions.register("read_item", A, A.load)
|
|
354
|
-
storage_actions.register("write_item", A, save_A)
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
class B:
|
|
358
|
-
|
|
359
|
-
def __init__(self, a, b):
|
|
360
|
-
self.a = a
|
|
361
|
-
self.b = b
|
|
362
|
-
self.loaded = False
|
|
363
|
-
|
|
364
|
-
def save(self, storage, loc):
|
|
365
|
-
group = storage.create_group(loc, cls=self.__class__)
|
|
366
|
-
self.a.save(group, "a")
|
|
367
|
-
group.write_object("b", self.b)
|
|
368
|
-
|
|
369
|
-
@classmethod
|
|
370
|
-
def load(cls, storage, loc):
|
|
371
|
-
group = StorageGroup(storage, loc)
|
|
372
|
-
obj = cls(a=group["a"], b=group.read_object("b"))
|
|
373
|
-
obj.loaded = True
|
|
374
|
-
return obj
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
storage_actions.register("read_item", B, B.load)
|
|
378
|
-
storage_actions.register("write_item", B, lambda s, l, o: o.save(s, l))
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
@pytest.mark.parametrize("ext", STORAGE_EXT)
|
|
382
|
-
def test_register_class_action(ext, tmp_path):
|
|
383
|
-
"""test loading custom classes"""
|
|
384
|
-
|
|
385
|
-
a = A({"a": 1})
|
|
386
|
-
assert not a.loaded
|
|
387
|
-
|
|
388
|
-
with open_storage(tmp_path / f"file{ext}", mode="truncate") as store:
|
|
389
|
-
store["a"] = a
|
|
390
|
-
|
|
391
|
-
with open_storage(tmp_path / f"file{ext}", mode="read") as store:
|
|
392
|
-
b = store["a"]
|
|
393
|
-
|
|
394
|
-
assert b.loaded
|
|
395
|
-
assert b.value == {"a": 1}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
@pytest.mark.parametrize("ext", STORAGE_EXT)
|
|
399
|
-
def test_register_class_action_nested(tmp_path, ext):
|
|
400
|
-
"""test loading custom nested classes"""
|
|
401
|
-
|
|
402
|
-
a = A({"a": 1})
|
|
403
|
-
b = B(a, "str")
|
|
404
|
-
assert not a.loaded and not b.loaded
|
|
405
|
-
|
|
406
|
-
with open_storage(tmp_path / f"file{ext}", mode="truncate") as store:
|
|
407
|
-
store["data"] = b
|
|
408
|
-
|
|
409
|
-
with open_storage(tmp_path / f"file{ext}", mode="read") as store:
|
|
410
|
-
res = store["data"]
|
|
411
|
-
|
|
412
|
-
assert res.loaded and res.a.loaded
|
|
413
|
-
assert res.a.value == {"a": 1}
|
|
414
|
-
assert res.b == "str"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|