py-modelrunner 0.17.0__tar.gz → 0.18.0__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.17.0 → py_modelrunner-0.18.0}/PKG-INFO +5 -5
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/README.md +4 -4
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/source/conf.py +0 -1
- py_modelrunner-0.18.0/docs/source/sphinx_simplify_typehints.py +74 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/source/user_manual.rst +3 -3
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/model_storage_output.py +2 -2
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/__init__.py +2 -11
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/_version.py +2 -2
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/config.py +1 -1
- py_modelrunner-0.18.0/modelrunner/model/__init__.py +19 -0
- py_modelrunner-0.18.0/modelrunner/model/base.py +247 -0
- py_modelrunner-0.18.0/modelrunner/model/factory.py +173 -0
- {py-modelrunner-0.17.0/modelrunner → py_modelrunner-0.18.0/modelrunner/model}/parameters.py +1 -1
- py_modelrunner-0.18.0/modelrunner/run/__init__.py +19 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/run/__main__.py +7 -0
- {py-modelrunner-0.17.0/modelrunner → py_modelrunner-0.18.0/modelrunner/run}/compatibility/triage.py +47 -16
- {py-modelrunner-0.17.0/modelrunner → py_modelrunner-0.18.0/modelrunner/run}/compatibility/version0.py +1 -1
- {py-modelrunner-0.17.0/modelrunner → py_modelrunner-0.18.0/modelrunner/run}/compatibility/version1.py +2 -13
- py_modelrunner-0.18.0/modelrunner/run/compatibility/version2.py +45 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/run/job.py +1 -1
- py_modelrunner-0.18.0/modelrunner/run/launch.py +139 -0
- {py-modelrunner-0.17.0/modelrunner → py_modelrunner-0.18.0/modelrunner/run}/results.py +54 -20
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/__init__.py +13 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/base.py +1 -1
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/group.py +20 -2
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/py_modelrunner.egg-info/PKG-INFO +5 -5
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/py_modelrunner.egg-info/SOURCES.txt +61 -17
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/scripts/create_compatibility_files.py +29 -5
- py_modelrunner-0.18.0/tests/compatibility/3/array.hdf +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/array.json +1 -0
- py_modelrunner-0.18.0/tests/compatibility/3/array.pkl +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/array.yaml +19 -0
- py_modelrunner-0.18.0/tests/compatibility/3/array.zip +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/array_col.hdf +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/array_col.json +1 -0
- py_modelrunner-0.18.0/tests/compatibility/3/array_col.pkl +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/array_col.yaml +20 -0
- py_modelrunner-0.18.0/tests/compatibility/3/array_col.zip +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/dict.hdf +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/dict.json +1 -0
- py_modelrunner-0.18.0/tests/compatibility/3/dict.pkl +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/dict.yaml +21 -0
- py_modelrunner-0.18.0/tests/compatibility/3/dict.zip +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_array.hdf +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_array.json +1 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_array.pkl +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_array.yaml +33 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_array.zip +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_array_col.hdf +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_array_col.json +1 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_array_col.pkl +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_array_col.yaml +30 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_array_col.zip +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_dict.hdf +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_dict.json +1 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_dict.pkl +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_dict.yaml +32 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_dict.zip +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_nested.hdf +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_nested.json +1 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_nested.pkl +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_nested.yaml +28 -0
- py_modelrunner-0.18.0/tests/compatibility/3/model_nested.zip +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/nested.hdf +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/nested.json +1 -0
- py_modelrunner-0.18.0/tests/compatibility/3/nested.pkl +0 -0
- py_modelrunner-0.18.0/tests/compatibility/3/nested.yaml +19 -0
- py_modelrunner-0.18.0/tests/compatibility/3/nested.zip +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/test_compatibility.py +3 -3
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/helpers/asserts.py +16 -2
- {py-modelrunner-0.17.0/tests → py_modelrunner-0.18.0/tests/model}/test_model.py +11 -5
- {py-modelrunner-0.17.0/tests → py_modelrunner-0.18.0/tests/model}/test_parameters.py +1 -1
- {py-modelrunner-0.17.0/tests → py_modelrunner-0.18.0/tests/run}/test_results.py +1 -1
- py_modelrunner-0.18.0/tests/run/test_run_main.py +79 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/storage/test_storage_generic.py +85 -1
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/test_config.py +1 -1
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/test_main.py +1 -1
- py-modelrunner-0.17.0/docs/source/sphinx_simplify_typehints.py +0 -132
- py-modelrunner-0.17.0/modelrunner/model.py +0 -543
- py-modelrunner-0.17.0/modelrunner/run/__init__.py +0 -1
- py-modelrunner-0.17.0/tests/run/test_run_main.py +0 -26
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/.github/workflows/build.yml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/.github/workflows/check_docs.yml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/.github/workflows/coverage_report.yml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/.github/workflows/release_pip.yml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/.gitignore +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/.readthedocs.yaml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/LICENSE +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/Makefile +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/build_all.sh +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/requirements.txt +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/source/.gitignore +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/source/examples.rst +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/source/index.rst +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/source/installation.rst +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/source/parse_examples.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/docs/source/run_autodoc.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/io_hdf.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/io_json.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/io_yaml.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/model_class.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/model_decorator.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/model_function.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/read_many.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/script_custom_func.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/script_function.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/script_many_classes.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/script_minimal.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/script_model_class.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/submit_function.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/submit_many_func.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/submit_many_iter.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/examples/submit_shebang.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/.gitignore +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/__main__.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/py.typed +0 -0
- {py-modelrunner-0.17.0/modelrunner → py_modelrunner-0.18.0/modelrunner/run}/compatibility/__init__.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/run/templates/background.jinja +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/run/templates/foreground.jinja +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/run/templates/qsub.jinja +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/run/templates/srun.jinja +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/access_modes.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/attributes.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/backend/__init__.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/backend/hdf.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/backend/json.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/backend/memory.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/backend/text_base.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/backend/utils.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/backend/yaml.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/backend/zarr.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/tools.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/trajectory.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/storage/utils.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/modelrunner/utils.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/py_modelrunner.egg-info/dependency_links.txt +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/py_modelrunner.egg-info/not-zip-safe +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/py_modelrunner.egg-info/requires.txt +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/py_modelrunner.egg-info/top_level.txt +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/pyproject.toml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/requirements.txt +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/requirements_full.txt +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/scripts/format_code.sh +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/scripts/performance_hdf5_zarr.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/scripts/run_tests.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/scripts/tests_all.sh +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/scripts/tests_codestyle.sh +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/scripts/tests_coverage.sh +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/scripts/tests_parallel.sh +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/scripts/tests_run.sh +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/scripts/tests_types.sh +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/setup.cfg +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/0/result.hdf +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/0/result.json +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/0/result.pkl +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/0/result.yaml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/array.json +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/array.pkl +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/array.yaml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/array.zarr +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/array_col.json +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/array_col.pkl +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/array_col.yaml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/array_col.zarr +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/dict.json +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/dict.pkl +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/dict.yaml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/dict.zarr +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/object.json +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/object.pkl +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/object.yaml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/1/object.zarr +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/array.hdf +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/array.json +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/array.pkl +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/array.yaml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/array.zip +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/array_col.hdf +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/array_col.json +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/array_col.pkl +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/array_col.yaml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/array_col.zip +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/dict.hdf +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/dict.json +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/dict.pkl +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/dict.yaml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/dict.zip +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/nested.hdf +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/nested.json +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/nested.pkl +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/nested.yaml +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/compatibility/2/nested.zip +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/conftest.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/helpers/__init__.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/helpers/storage.py +0 -0
- {py-modelrunner-0.17.0/tests → py_modelrunner-0.18.0/tests/model}/scripts/empty.py +0 -0
- {py-modelrunner-0.17.0/tests → py_modelrunner-0.18.0/tests/model}/scripts/function.py +0 -0
- {py-modelrunner-0.17.0/tests → py_modelrunner-0.18.0/tests/model}/scripts/function_main.py +0 -0
- {py-modelrunner-0.17.0/tests → py_modelrunner-0.18.0/tests/model}/scripts/function_marked.py +0 -0
- {py-modelrunner-0.17.0/tests → py_modelrunner-0.18.0/tests/model}/scripts/make_model.py +0 -0
- {py-modelrunner-0.17.0/tests → py_modelrunner-0.18.0/tests/model}/scripts/make_model_class.py +0 -0
- {py-modelrunner-0.17.0/tests → py_modelrunner-0.18.0/tests/model}/scripts/make_model_marked.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/requirements.txt +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/run/scripts/fail.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/run/scripts/function.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/run/scripts/print.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/run/scripts/script.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/run/test_job.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/storage/test_group.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/storage/test_storage_special.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/storage/test_trajectory.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/storage/test_utils.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/test_examples.py +0 -0
- {py-modelrunner-0.17.0 → py_modelrunner-0.18.0}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: py-modelrunner
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.0
|
|
4
4
|
Summary: Python classes for organizing (HPC) simulations
|
|
5
5
|
Author-email: David Zwicker <david.zwicker@ds.mpg.de>
|
|
6
6
|
License: MIT
|
|
@@ -226,7 +226,7 @@ def multiply(a=1, b=2):
|
|
|
226
226
|
return a * b
|
|
227
227
|
|
|
228
228
|
if __name__ == "__main__":
|
|
229
|
-
submit_job(__file__, parameters={
|
|
229
|
+
submit_job(__file__, parameters={"a": 2}, output="data.hdf5", method="foreground")
|
|
230
230
|
```
|
|
231
231
|
Here, the `output` argument specifies a file to which the results are written, while
|
|
232
232
|
`method` chooses how the script is submitted.
|
|
@@ -243,7 +243,7 @@ def multiply(a=1, b=2):
|
|
|
243
243
|
|
|
244
244
|
if __name__ == "__main__":
|
|
245
245
|
for a in range(5):
|
|
246
|
-
submit_job(__file__, parameters={
|
|
246
|
+
submit_job(__file__, parameters={"a": a}, output=f"data_{a}.hdf5", method="foreground")
|
|
247
247
|
```
|
|
248
248
|
|
|
249
249
|
Note that the safe-guard `if __name__ == "__main__"` is absolutely crucial to ensure that
|
|
@@ -261,7 +261,7 @@ def multiply(a=1, b=2):
|
|
|
261
261
|
return a * b
|
|
262
262
|
|
|
263
263
|
if __name__ == "__main__":
|
|
264
|
-
submit_jobs(__file__, parameters={
|
|
264
|
+
submit_jobs(__file__, parameters={"a": range(5)}, output_folder="data", method="foreground")
|
|
265
265
|
```
|
|
266
266
|
|
|
267
267
|
Finally, the packages also offers a method to submit a model script to the cluster using
|
|
@@ -301,7 +301,7 @@ For instance, the data from the multiple jobs ran above can be collected using
|
|
|
301
301
|
from modelrunner import ResultCollection
|
|
302
302
|
|
|
303
303
|
results = ResultCollection.from_folder(".", pattern="data_*.hdf5")
|
|
304
|
-
print(results.
|
|
304
|
+
print(results.to_dataframe())
|
|
305
305
|
```
|
|
306
306
|
|
|
307
307
|
This example should print all results using a pandas Dataframe, where each row
|
|
@@ -184,7 +184,7 @@ def multiply(a=1, b=2):
|
|
|
184
184
|
return a * b
|
|
185
185
|
|
|
186
186
|
if __name__ == "__main__":
|
|
187
|
-
submit_job(__file__, parameters={
|
|
187
|
+
submit_job(__file__, parameters={"a": 2}, output="data.hdf5", method="foreground")
|
|
188
188
|
```
|
|
189
189
|
Here, the `output` argument specifies a file to which the results are written, while
|
|
190
190
|
`method` chooses how the script is submitted.
|
|
@@ -201,7 +201,7 @@ def multiply(a=1, b=2):
|
|
|
201
201
|
|
|
202
202
|
if __name__ == "__main__":
|
|
203
203
|
for a in range(5):
|
|
204
|
-
submit_job(__file__, parameters={
|
|
204
|
+
submit_job(__file__, parameters={"a": a}, output=f"data_{a}.hdf5", method="foreground")
|
|
205
205
|
```
|
|
206
206
|
|
|
207
207
|
Note that the safe-guard `if __name__ == "__main__"` is absolutely crucial to ensure that
|
|
@@ -219,7 +219,7 @@ def multiply(a=1, b=2):
|
|
|
219
219
|
return a * b
|
|
220
220
|
|
|
221
221
|
if __name__ == "__main__":
|
|
222
|
-
submit_jobs(__file__, parameters={
|
|
222
|
+
submit_jobs(__file__, parameters={"a": range(5)}, output_folder="data", method="foreground")
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
Finally, the packages also offers a method to submit a model script to the cluster using
|
|
@@ -259,7 +259,7 @@ For instance, the data from the multiple jobs ran above can be collected using
|
|
|
259
259
|
from modelrunner import ResultCollection
|
|
260
260
|
|
|
261
261
|
results = ResultCollection.from_folder(".", pattern="data_*.hdf5")
|
|
262
|
-
print(results.
|
|
262
|
+
print(results.to_dataframe())
|
|
263
263
|
```
|
|
264
264
|
|
|
265
265
|
This example should print all results using a pandas Dataframe, where each row
|
|
@@ -240,7 +240,6 @@ napoleon_custom_sections = None
|
|
|
240
240
|
intersphinx_mapping = {
|
|
241
241
|
"h5py": ("https://docs.h5py.org/en/latest", None),
|
|
242
242
|
"matplotlib": ("https://matplotlib.org/stable", None),
|
|
243
|
-
"modelrunner": ("https://py-modelrunner.readthedocs.io/en/latest", None),
|
|
244
243
|
"napari": ("https://napari.org/", None),
|
|
245
244
|
"numba": ("https://numba.pydata.org/numba-doc/latest/", None),
|
|
246
245
|
"numpy": ("https://numpy.org/doc/stable", None),
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simple sphinx plug-in that simplifies type information in function signatures
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import collections
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from modelrunner import Parameterized
|
|
9
|
+
|
|
10
|
+
# simple (literal) replacement rules
|
|
11
|
+
REPLACEMENTS = collections.OrderedDict(
|
|
12
|
+
[
|
|
13
|
+
# Remove some unnecessary information in favor of a more compact style
|
|
14
|
+
("Dict[KT, VT]", "dict"),
|
|
15
|
+
("Dict[str, Any]", "dict"),
|
|
16
|
+
("Optional[str]", "str"),
|
|
17
|
+
("Optional[float]", "float"),
|
|
18
|
+
("Optional[int]", "int"),
|
|
19
|
+
("Optional[dict]", "dict"),
|
|
20
|
+
("Optional[Dict[str, Any]]", "dict"),
|
|
21
|
+
]
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# replacement rules based on regular expressions
|
|
26
|
+
REPLACEMENTS_REGEX = {
|
|
27
|
+
# remove full package path and only leave the module/class identifier
|
|
28
|
+
r"modelrunner\.(\w+\.)*": "",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def process_signature(
|
|
33
|
+
app, what: str, name: str, obj, options, signature, return_annotation
|
|
34
|
+
):
|
|
35
|
+
"""Process signature by applying replacement rules"""
|
|
36
|
+
if signature is not None:
|
|
37
|
+
for key, value in REPLACEMENTS.items():
|
|
38
|
+
signature = signature.replace(key, value)
|
|
39
|
+
for key, value in REPLACEMENTS_REGEX.items():
|
|
40
|
+
signature = re.sub(key, value, signature)
|
|
41
|
+
return signature, return_annotation
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def sphinx_display_parameters(app, what, name, obj, options, lines):
|
|
45
|
+
"""helper function to display parameters in sphinx documentation
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
This function should be connected to the 'autodoc-process-docstring'
|
|
49
|
+
event like so:
|
|
50
|
+
|
|
51
|
+
app.connect('autodoc-process-docstring', sphinx_display_parameters)
|
|
52
|
+
"""
|
|
53
|
+
if what == "class" and issubclass(obj, Parameterized):
|
|
54
|
+
if any(":param parameters:" in line for line in lines):
|
|
55
|
+
# parse parameters
|
|
56
|
+
parameters = obj.get_parameters(sort=False)
|
|
57
|
+
if parameters:
|
|
58
|
+
lines.append(".. admonition::")
|
|
59
|
+
lines.append(f" Parameters of {obj.__name__}:")
|
|
60
|
+
lines.append(" ")
|
|
61
|
+
for p in parameters.values():
|
|
62
|
+
lines.append(f" {p.name}")
|
|
63
|
+
text = p.description.splitlines()
|
|
64
|
+
text.append(f"(Default value: :code:`{p.default_value!r}`)")
|
|
65
|
+
text = [" " + t for t in text]
|
|
66
|
+
lines.extend(text)
|
|
67
|
+
lines.append("")
|
|
68
|
+
lines.append("")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def setup(app):
|
|
72
|
+
"""set up hooks for this sphinx plugin"""
|
|
73
|
+
app.connect("autodoc-process-signature", process_signature)
|
|
74
|
+
app.connect("autodoc-process-docstring", sphinx_display_parameters)
|
|
@@ -12,9 +12,9 @@ Hierarchical input/output using :mod:`~modelrunner.storage`:
|
|
|
12
12
|
Storages are conveniently created by :func:`~modelrunner.storage.tools.open_storage`,
|
|
13
13
|
which returns a storage object that offers a dict-like interface.
|
|
14
14
|
|
|
15
|
-
Defining parameters using :mod:`~modelrunner.parameters`:
|
|
16
|
-
The module provides the :class:`~modelrunner.parameters.Parameter` class, describing a single parameter.
|
|
17
|
-
This can be used together with the mixin :class:`~modelrunner.parameters.
|
|
15
|
+
Defining parameters using :mod:`~modelrunner.model.parameters`:
|
|
16
|
+
The module provides the :class:`~modelrunner.model.parameters.Parameter` class, describing a single parameter.
|
|
17
|
+
This can be used together with the mixin :class:`~modelrunner.model.parameters.Parameterized`,
|
|
18
18
|
which allows to equip classes with default parameters and some convenience methods.
|
|
19
19
|
|
|
20
20
|
Defining models using :mod:`~modelrunner.model`:
|
|
@@ -23,5 +23,5 @@ with tempfile.NamedTemporaryFile(suffix=".yaml") as fp:
|
|
|
23
23
|
|
|
24
24
|
# read the file and check whether all the data is there
|
|
25
25
|
with open_storage(fp.name) as storage:
|
|
26
|
-
print("Stored data:", storage["data"])
|
|
27
|
-
print("Model result:", storage["result"]
|
|
26
|
+
print("Stored data:", storage["storage/data"])
|
|
27
|
+
print("Model result:", storage["result"])
|
|
@@ -18,15 +18,6 @@ except ImportError:
|
|
|
18
18
|
del PackageNotFoundError, version # clean name space
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
from .model import
|
|
22
|
-
|
|
23
|
-
make_model,
|
|
24
|
-
make_model_class,
|
|
25
|
-
run_function_with_cmd_args,
|
|
26
|
-
run_script,
|
|
27
|
-
set_default,
|
|
28
|
-
)
|
|
29
|
-
from .parameters import HideParameter, Parameter, Parameterized
|
|
30
|
-
from .results import Result, ResultCollection
|
|
31
|
-
from .run import submit_job, submit_jobs
|
|
21
|
+
from .model import *
|
|
22
|
+
from .run import *
|
|
32
23
|
from .storage import open_storage
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Defines models that handle a simulation and its inputs
|
|
3
|
+
|
|
4
|
+
.. autosummary::
|
|
5
|
+
:nosignatures:
|
|
6
|
+
|
|
7
|
+
~modelrunner.model.base.ModelBase
|
|
8
|
+
~modelrunner.model.factory.make_model
|
|
9
|
+
~modelrunner.model.factory.make_model_class
|
|
10
|
+
~modelrunner.model.factory.set_default
|
|
11
|
+
~modelrunner.model.parameters.Parameter
|
|
12
|
+
~modelrunner.model.parameters.Parameterized
|
|
13
|
+
|
|
14
|
+
.. codeauthor:: David Zwicker <david.zwicker@ds.mpg.de>
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from .base import ModelBase
|
|
18
|
+
from .factory import cleared_default_model, make_model, make_model_class, set_default
|
|
19
|
+
from .parameters import HideParameter, Parameter, Parameterized
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base class describing a model
|
|
3
|
+
|
|
4
|
+
.. codeauthor:: David Zwicker <david.zwicker@ds.mpg.de>
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
from abc import ABCMeta, abstractmethod
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from typing import TYPE_CHECKING, Any, Sequence
|
|
15
|
+
|
|
16
|
+
from ..storage import ModeType, StorageGroup, open_storage
|
|
17
|
+
from .parameters import DeprecatedParameter, HideParameter, Parameterized
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from ..run.results import Result # @UnusedImport
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ModelBase(Parameterized, metaclass=ABCMeta):
|
|
24
|
+
"""base class for describing models"""
|
|
25
|
+
|
|
26
|
+
name: str | None = None
|
|
27
|
+
"""str: the name of the model"""
|
|
28
|
+
description: str | None = None
|
|
29
|
+
"""str: a longer description of the model"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
parameters: dict[str, Any] | None = None,
|
|
34
|
+
output: str | None = None,
|
|
35
|
+
*,
|
|
36
|
+
mode: ModeType = "insert",
|
|
37
|
+
strict: bool = False,
|
|
38
|
+
):
|
|
39
|
+
"""initialize the parameters of the object
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
parameters (dict):
|
|
43
|
+
A dictionary of parameters to change the defaults of this model. The
|
|
44
|
+
allowed parameters can be obtained from
|
|
45
|
+
:meth:`~Parameterized.get_parameters` or displayed by calling
|
|
46
|
+
:meth:`~Parameterized.show_parameters`.
|
|
47
|
+
output (str):
|
|
48
|
+
Path where the output file will be written. The output will be written
|
|
49
|
+
using :mod:`~modelrunner.storage` and might contain two groups: `result`
|
|
50
|
+
to which the final result of the model is written, and `data`, which
|
|
51
|
+
can contain extra information that is written using
|
|
52
|
+
:attr:`~ModelBase.storage`.
|
|
53
|
+
mode (str or :class:`~modelrunner.storage.access_modes.ModeType`):
|
|
54
|
+
The file mode with which the storage is accessed, which determines the
|
|
55
|
+
allowed operations. Common options are "read", "full", "append", and
|
|
56
|
+
"truncate".
|
|
57
|
+
strict (bool):
|
|
58
|
+
Flag indicating whether parameters are strictly interpreted. If `True`,
|
|
59
|
+
only parameters listed in `parameters_default` can be set and their type
|
|
60
|
+
will be enforced.
|
|
61
|
+
"""
|
|
62
|
+
super().__init__(parameters, strict=strict)
|
|
63
|
+
self.output = output # TODO: also allow already opened storages
|
|
64
|
+
self.mode = mode
|
|
65
|
+
self._storage: open_storage | None = None
|
|
66
|
+
self._logger = logging.getLogger(self.__class__.__name__)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def storage(self) -> StorageGroup:
|
|
70
|
+
""":class:`StorageGroup`: Storage to which data can be written"""
|
|
71
|
+
if self._storage is None:
|
|
72
|
+
if self.output is None:
|
|
73
|
+
raise RuntimeError("Output file needs to be specified")
|
|
74
|
+
self._storage = open_storage(self.output, mode=self.mode)
|
|
75
|
+
return self._storage
|
|
76
|
+
|
|
77
|
+
def close(self) -> None:
|
|
78
|
+
"""close any opened storages"""
|
|
79
|
+
if self._storage is not None:
|
|
80
|
+
self._storage.close()
|
|
81
|
+
self._storage = None
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def __call__(self):
|
|
85
|
+
"""main method calculating the result. Needs to be specified by sub-class"""
|
|
86
|
+
|
|
87
|
+
def get_result(self, data: Any = None) -> Result:
|
|
88
|
+
"""get the result as a :class:`~model.Result` object
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
data:
|
|
92
|
+
The result data. If omitted, the model is run to obtain results
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
:class:`Result`: The result after the model is run
|
|
96
|
+
"""
|
|
97
|
+
from ..run.results import Result # @Reimport
|
|
98
|
+
|
|
99
|
+
if data is None:
|
|
100
|
+
data = self()
|
|
101
|
+
|
|
102
|
+
info = {"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
|
|
103
|
+
return Result(self, data, info=info)
|
|
104
|
+
|
|
105
|
+
def write_result(self, result: Result | None = None) -> None:
|
|
106
|
+
"""write the result to the output file
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
result:
|
|
110
|
+
The result data. If omitted, the model is run to obtain results
|
|
111
|
+
"""
|
|
112
|
+
from ..run.results import Result # @Reimport
|
|
113
|
+
|
|
114
|
+
if self.output is None:
|
|
115
|
+
raise RuntimeError("Output file needs to be specified")
|
|
116
|
+
|
|
117
|
+
if result is None:
|
|
118
|
+
result = self.get_result()
|
|
119
|
+
elif not isinstance(result, Result):
|
|
120
|
+
raise TypeError(f"result has type {result.__class__} instead of `Result`")
|
|
121
|
+
|
|
122
|
+
if self._storage is not None:
|
|
123
|
+
# reuse the opened storage
|
|
124
|
+
result.to_file(self._storage)
|
|
125
|
+
else:
|
|
126
|
+
result.to_file(self.output, mode=self.mode)
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def _prepare_argparser(cls, name: str | None = None) -> argparse.ArgumentParser:
|
|
130
|
+
"""create argument parser for setting parameters of this model
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
name (str):
|
|
134
|
+
Name of the program, which will be shown in the command line help
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
:class:`~argparse.ArgumentParser`
|
|
138
|
+
"""
|
|
139
|
+
parser = argparse.ArgumentParser(
|
|
140
|
+
prog=name,
|
|
141
|
+
description=cls.description,
|
|
142
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
group = parser.add_argument_group()
|
|
146
|
+
seen = set()
|
|
147
|
+
# iterate over all parent classes
|
|
148
|
+
for cls1 in cls.__mro__:
|
|
149
|
+
if hasattr(cls1, "parameters_default"):
|
|
150
|
+
# add all parameters of this class
|
|
151
|
+
for p in cls1.parameters_default:
|
|
152
|
+
if p.name not in seen:
|
|
153
|
+
if not isinstance(p, (HideParameter, DeprecatedParameter)):
|
|
154
|
+
p._argparser_add(group)
|
|
155
|
+
seen.add(p.name)
|
|
156
|
+
|
|
157
|
+
# add special parameters
|
|
158
|
+
parser.add_argument(
|
|
159
|
+
"--json",
|
|
160
|
+
metavar="JSON",
|
|
161
|
+
help="JSON-encoded parameter values. Overwrites other parameters.",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return parser
|
|
165
|
+
|
|
166
|
+
@classmethod
|
|
167
|
+
def from_command_line(
|
|
168
|
+
cls, args: Sequence[str] | None = None, name: str | None = None
|
|
169
|
+
) -> ModelBase:
|
|
170
|
+
"""create model from command line parameters
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
args (list):
|
|
174
|
+
Sequence of strings corresponding to the command line arguments
|
|
175
|
+
name (str):
|
|
176
|
+
Name of the program, which will be shown in the command line help
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
:class:`ModelBase`: An instance of this model with appropriate parameters
|
|
180
|
+
"""
|
|
181
|
+
if args is None:
|
|
182
|
+
args = []
|
|
183
|
+
|
|
184
|
+
# read the command line arguments
|
|
185
|
+
parser = cls._prepare_argparser(name)
|
|
186
|
+
|
|
187
|
+
# add special parameters to determine the output file
|
|
188
|
+
parser.add_argument(
|
|
189
|
+
"-o",
|
|
190
|
+
"--output",
|
|
191
|
+
metavar="PATH",
|
|
192
|
+
help="Path to output file. If omitted, no output file is created.",
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
parameters = vars(parser.parse_args(args))
|
|
196
|
+
output = parameters.pop("output")
|
|
197
|
+
parameters_json = parameters.pop("json")
|
|
198
|
+
|
|
199
|
+
# update parameters with data from the json argument
|
|
200
|
+
if parameters_json:
|
|
201
|
+
parameters.update(json.loads(parameters_json))
|
|
202
|
+
|
|
203
|
+
# create the model
|
|
204
|
+
return cls(parameters, output=output)
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def run_from_command_line(
|
|
208
|
+
cls, args: Sequence[str] | None = None, name: str | None = None
|
|
209
|
+
) -> Result:
|
|
210
|
+
"""run model using command line parameters
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
args (list):
|
|
214
|
+
Sequence of strings corresponding to the command line arguments
|
|
215
|
+
name (str):
|
|
216
|
+
Name of the program, which will be shown in the command line help
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
:class:`Result`: The result of running the model
|
|
220
|
+
"""
|
|
221
|
+
# create model from command line parameters
|
|
222
|
+
mdl = cls.from_command_line(args, name)
|
|
223
|
+
|
|
224
|
+
# run the model
|
|
225
|
+
result = mdl.get_result()
|
|
226
|
+
if mdl.output:
|
|
227
|
+
# write the results to a file
|
|
228
|
+
mdl.write_result(result=result)
|
|
229
|
+
# else:
|
|
230
|
+
# # display the results on stdout
|
|
231
|
+
# storage = MemoryStorage()
|
|
232
|
+
# result.to_file(storage)
|
|
233
|
+
|
|
234
|
+
# close the output file
|
|
235
|
+
mdl.close()
|
|
236
|
+
|
|
237
|
+
return result
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def _state_attributes(self) -> dict[str, Any]:
|
|
241
|
+
"""dict: information about the element state, which does not change in time"""
|
|
242
|
+
return {
|
|
243
|
+
"class": self.__class__.__name__,
|
|
244
|
+
"name": self.name,
|
|
245
|
+
"description": self.description,
|
|
246
|
+
"parameters": self.parameters,
|
|
247
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Functions for creating models and model classes from other input
|
|
3
|
+
|
|
4
|
+
.. codeauthor:: David Zwicker <david.zwicker@ds.mpg.de>
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import functools
|
|
10
|
+
import inspect
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, get_args, get_origin
|
|
12
|
+
|
|
13
|
+
from ..storage import ModeType
|
|
14
|
+
from .base import ModelBase
|
|
15
|
+
from .parameters import NoValue, Parameter
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from ..run.results import Result # @UnusedImport
|
|
19
|
+
|
|
20
|
+
_DEFAULT_MODEL: Callable | ModelBase | None = None
|
|
21
|
+
"""stores the default model that will be used automatically"""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
TModel = TypeVar("TModel", Callable, ModelBase, None)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def set_default(func_or_model: TModel) -> TModel:
|
|
28
|
+
"""sets the function or model as the default model
|
|
29
|
+
|
|
30
|
+
The last model that received this flag will be run automatically. This only affects
|
|
31
|
+
the behavior when the script is run using `modelrunner` from the command line, e.g.,
|
|
32
|
+
using :code:`python -m modelrunner script.py`.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
func_or_model (callabel or :class:`ModelBase`, optional):
|
|
36
|
+
The function or model that should be called when the script is run.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
`func_or_model`, so the function can be used as a decorator
|
|
40
|
+
"""
|
|
41
|
+
global _DEFAULT_MODEL
|
|
42
|
+
_DEFAULT_MODEL = func_or_model
|
|
43
|
+
return func_or_model
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
TFunc = TypeVar("TFunc", bound=Any)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def cleared_default_model(func: TFunc) -> TFunc:
|
|
50
|
+
"""run the function with a cleared _DEFAULT_MODEL and restore it afterwards"""
|
|
51
|
+
|
|
52
|
+
@functools.wraps(func)
|
|
53
|
+
def inner(*args, **kwargs):
|
|
54
|
+
global _DEFAULT_MODEL
|
|
55
|
+
_old_model = _DEFAULT_MODEL
|
|
56
|
+
_DEFAULT_MODEL = None
|
|
57
|
+
try:
|
|
58
|
+
return func(*args, **kwargs)
|
|
59
|
+
finally:
|
|
60
|
+
_DEFAULT_MODEL = _old_model
|
|
61
|
+
|
|
62
|
+
return inner # type: ignore
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def make_model_class(func: Callable, *, default: bool = False) -> type[ModelBase]:
|
|
66
|
+
"""create a model from a function by interpreting its signature
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
func (callable):
|
|
70
|
+
The function that will be turned into a Model
|
|
71
|
+
default (bool):
|
|
72
|
+
If True, set this model as the default one for the current script
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
:class:`ModelBase`: A subclass of ModelBase, which encompasses `func`
|
|
76
|
+
"""
|
|
77
|
+
# determine the parameters of the function
|
|
78
|
+
provide_storage = False
|
|
79
|
+
parameters_default = []
|
|
80
|
+
for name, param in inspect.signature(func).parameters.items():
|
|
81
|
+
if name == "storage":
|
|
82
|
+
# treat this parameter specially and provide access to a storage object
|
|
83
|
+
provide_storage = True
|
|
84
|
+
else:
|
|
85
|
+
# all remaining parameters are treated as model parameters
|
|
86
|
+
if param.annotation is param.empty:
|
|
87
|
+
cls = object
|
|
88
|
+
choices = None
|
|
89
|
+
elif get_origin(param.annotation) is Literal:
|
|
90
|
+
cls = object
|
|
91
|
+
choices = get_args(param.annotation)
|
|
92
|
+
else:
|
|
93
|
+
cls = param.annotation
|
|
94
|
+
choices = None
|
|
95
|
+
if param.default is param.empty:
|
|
96
|
+
default_value = NoValue
|
|
97
|
+
else:
|
|
98
|
+
default_value = param.default
|
|
99
|
+
|
|
100
|
+
parameter = Parameter(
|
|
101
|
+
name, default_value=default_value, cls=cls, choices=choices
|
|
102
|
+
)
|
|
103
|
+
parameters_default.append(parameter)
|
|
104
|
+
|
|
105
|
+
def __call__(self, *args, **kwargs):
|
|
106
|
+
"""call the function preserving the original signature"""
|
|
107
|
+
parameters = {}
|
|
108
|
+
for i, (name, value) in enumerate(self.parameters.items()):
|
|
109
|
+
if len(args) > i:
|
|
110
|
+
if name in kwargs:
|
|
111
|
+
raise ValueError(f"{name} also given as positional argument")
|
|
112
|
+
param_value = args[i]
|
|
113
|
+
elif name in kwargs:
|
|
114
|
+
param_value = kwargs[name]
|
|
115
|
+
else:
|
|
116
|
+
param_value = value
|
|
117
|
+
|
|
118
|
+
if param_value is NoValue:
|
|
119
|
+
raise TypeError(f"Model missing required argument: '{name}'")
|
|
120
|
+
parameters[name] = param_value
|
|
121
|
+
|
|
122
|
+
if provide_storage:
|
|
123
|
+
if "storage" in self.storage:
|
|
124
|
+
self._logger.info("Open storage group `storage`")
|
|
125
|
+
parameters["storage"] = self.storage.open_group("storage")
|
|
126
|
+
else:
|
|
127
|
+
self._logger.info("Create storage group `storage`")
|
|
128
|
+
parameters["storage"] = self.storage.create_group("storage")
|
|
129
|
+
|
|
130
|
+
return func(**parameters)
|
|
131
|
+
|
|
132
|
+
args = {
|
|
133
|
+
"name": func.__name__,
|
|
134
|
+
"description": func.__doc__,
|
|
135
|
+
"parameters_default": parameters_default,
|
|
136
|
+
"__doc__": func.__doc__,
|
|
137
|
+
"__call__": __call__,
|
|
138
|
+
}
|
|
139
|
+
newclass = type(func.__name__, (ModelBase,), args)
|
|
140
|
+
if default:
|
|
141
|
+
set_default(newclass)
|
|
142
|
+
return newclass
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def make_model(
|
|
146
|
+
func: Callable,
|
|
147
|
+
parameters: dict[str, Any] | None = None,
|
|
148
|
+
output: str | None = None,
|
|
149
|
+
*,
|
|
150
|
+
mode: ModeType = "insert",
|
|
151
|
+
default: bool = False,
|
|
152
|
+
) -> ModelBase:
|
|
153
|
+
"""create model from a function and a dictionary of parameters
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
func (callable):
|
|
157
|
+
The function that will be turned into a Model
|
|
158
|
+
parameters (dict):
|
|
159
|
+
Paramter values with which the model is initialized
|
|
160
|
+
output (str):
|
|
161
|
+
Path where the output file will be written.
|
|
162
|
+
mode (str or :class:`~modelrunner.storage.access_modes.ModeType`):
|
|
163
|
+
The file mode with which the storage is accessed, which determines the
|
|
164
|
+
allowed operations. Common options are "read", "full", "append", and
|
|
165
|
+
"truncate".
|
|
166
|
+
default (bool):
|
|
167
|
+
If True, set this model as the default one for the current script
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
:class:`ModelBase`: An instance of a subclass of ModelBase encompassing `func`
|
|
171
|
+
"""
|
|
172
|
+
model_class = make_model_class(func, default=default)
|
|
173
|
+
return model_class(parameters, output=output, mode=mode)
|