experimaestro 1.6.2__tar.gz → 1.7.0rc1__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.
Potentially problematic release.
This version of experimaestro might be problematic. Click here for more details.
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/PKG-INFO +3 -4
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/pyproject.toml +10 -7
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/__init__.py +3 -1
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/annotations.py +13 -3
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/cli/filter.py +3 -3
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/cli/jobs.py +1 -1
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/connectors/__init__.py +17 -8
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/connectors/local.py +8 -3
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/core/arguments.py +26 -3
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/core/objects.py +90 -6
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/core/objects.pyi +7 -1
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/core/types.py +33 -2
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/generators.py +6 -1
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/ipc.py +4 -1
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/launcherfinder/registry.py +18 -4
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/notifications.py +1 -1
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/scheduler/base.py +94 -10
- experimaestro-1.7.0rc1/src/experimaestro/scheduler/dynamic_outputs.py +184 -0
- experimaestro-1.7.0rc1/src/experimaestro/server/data/1815e00441357e01619e.ttf +0 -0
- experimaestro-1.7.0rc1/src/experimaestro/server/data/2463b90d9a316e4e5294.woff2 +0 -0
- experimaestro-1.7.0rc1/src/experimaestro/server/data/2582b0e4bcf85eceead0.ttf +0 -0
- experimaestro-1.7.0rc1/src/experimaestro/server/data/89999bdf5d835c012025.woff2 +0 -0
- experimaestro-1.7.0rc1/src/experimaestro/server/data/914997e1bdfc990d0897.ttf +0 -0
- experimaestro-1.7.0rc1/src/experimaestro/server/data/c210719e60948b211a12.woff2 +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/index.css +5187 -5068
- experimaestro-1.7.0rc1/src/experimaestro/server/data/index.css.map +1 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/index.js +68887 -68064
- experimaestro-1.7.0rc1/src/experimaestro/server/data/index.js.map +1 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/taskglobals.py +7 -2
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/definitions_types.py +5 -3
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/launchers/bin/sbatch +18 -5
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/launchers/common.py +11 -3
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/restart.py +6 -3
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/tasks/all.py +16 -10
- experimaestro-1.7.0rc1/src/experimaestro/tests/tasks/foreign.py +6 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_forward.py +5 -5
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_identifier.py +61 -66
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_instance.py +3 -6
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_param.py +40 -22
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_tags.py +5 -11
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_tokens.py +3 -2
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_types.py +17 -14
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_validation.py +48 -91
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tokens.py +16 -5
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/typingutils.py +7 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/utils/asyncio.py +6 -2
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/utils/resources.py +7 -3
- experimaestro-1.6.2/src/experimaestro/server/data/index.css.map +0 -1
- experimaestro-1.6.2/src/experimaestro/server/data/index.js.map +0 -1
- experimaestro-1.6.2/src/experimaestro/tests/tasks/foreign.py +0 -8
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/LICENSE +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/README.md +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/__main__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/checkers.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/cli/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/click.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/commandline.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/compat.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/connectors/ssh.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/core/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/core/context.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/core/serialization.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/core/serializers.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/core/utils.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/exceptions.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/experiments/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/experiments/cli.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/experiments/configuration.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/huggingface.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/launcherfinder/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/launcherfinder/base.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/launcherfinder/parser.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/launcherfinder/specs.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/launchers/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/launchers/direct.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/launchers/oar.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/launchers/slurm/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/launchers/slurm/base.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/locking.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/mkdocs/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/mkdocs/annotations.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/mkdocs/base.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/mkdocs/metaloader.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/mkdocs/style.css +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/mypy.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/py.typed +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/rpyc.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/run.py +1 -1
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/scheduler/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/scheduler/dependencies.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/scheduler/services.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/scheduler/workspace.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/scriptbuilder.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/016b4a6cdced82ab3aa1.ttf +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/50701fbb8177c2dde530.ttf +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/878f31251d960bd6266f.woff2 +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/b041b1fa4fe241b23445.woff2 +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/b6879d41b0852f01ed5b.woff2 +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/d75e3fd1eb12e9bd6655.ttf +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/favicon.ico +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/index.html +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/login.html +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/server/data/manifest.json +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/settings.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/sphinx/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/sphinx/static/experimaestro.css +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/conftest.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/connectors/bin/executable.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/connectors/test_local.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/connectors/utils.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/launchers/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/launchers/bin/sacct +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/launchers/bin/test.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/launchers/config_slurm/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/launchers/config_slurm/launchers.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/launchers/test_local.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/launchers/test_slurm.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/restart_main.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/scripts/notifyandwait.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/scripts/waitforfile.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/task_tokens.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/tasks/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_checkers.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_dependencies.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_findlauncher.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_objects.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_outputs.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_progress.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_serializers.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_snippets.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_ssh.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/test_tasks.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/token_reschedule.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tests/utils.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tools/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tools/diff.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tools/documentation.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/tools/jobs.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/utils/__init__.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/utils/jobs.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/utils/jupyter.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/utils/settings.py +0 -0
- {experimaestro-1.6.2 → experimaestro-1.7.0rc1}/src/experimaestro/xpmutils.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: experimaestro
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0rc1
|
|
4
4
|
Summary: "Experimaestro is a computer science experiment manager"
|
|
5
5
|
License: GPL-3
|
|
6
6
|
Keywords: experiment manager
|
|
7
7
|
Author: Benjamin Piwowarski
|
|
8
8
|
Author-email: benjamin@piwowarski.fr
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.9,<4.0
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Science/Research
|
|
12
12
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
@@ -14,7 +14,6 @@ Classifier: License :: Other/Proprietary License
|
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -35,7 +34,7 @@ Requires-Dist: huggingface-hub (>0.17)
|
|
|
35
34
|
Requires-Dist: humanfriendly (>=10,<11)
|
|
36
35
|
Requires-Dist: marshmallow (>=3.20,<4.0)
|
|
37
36
|
Requires-Dist: omegaconf (>=2.3,<3.0)
|
|
38
|
-
Requires-Dist: psutil (>=
|
|
37
|
+
Requires-Dist: psutil (>=7)
|
|
39
38
|
Requires-Dist: pyparsing (>=3.1,<4.0)
|
|
40
39
|
Requires-Dist: pytools (>=2023.1.1,<2024.0.0)
|
|
41
40
|
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
@@ -17,9 +17,10 @@ classifiers = [
|
|
|
17
17
|
include = [
|
|
18
18
|
"src/experimaestro/server/data/*",
|
|
19
19
|
"src/experimaestro/sphinx/static/experimaestro.css",
|
|
20
|
-
"src/experimaestro/mkdocs/style.css"
|
|
20
|
+
"src/experimaestro/mkdocs/style.css",
|
|
21
|
+
{ path="src/experimaestro/server/data/*", format=['sdist', 'wheel']}
|
|
21
22
|
]
|
|
22
|
-
version = "1.
|
|
23
|
+
version = "1.7.0-rc1"
|
|
23
24
|
repository = "https://github.com/experimaestro/experimaestro-python"
|
|
24
25
|
documentation = "https://experimaestro-python.readthedocs.io/"
|
|
25
26
|
|
|
@@ -28,21 +29,23 @@ enable = false
|
|
|
28
29
|
vcs = "git"
|
|
29
30
|
style = "semver"
|
|
30
31
|
dirty = true
|
|
31
|
-
format-jinja = "{
|
|
32
|
-
|
|
32
|
+
format-jinja = """{%- set pre = [] -%}{%- set metadata = [] -%}
|
|
33
|
+
{%- if revision is not none -%}{{ pre.append("rc" + revision|string) or "" }}{%- endif -%}
|
|
34
|
+
{%- if distance > 0 -%}{{ metadata.append(distance|string) or "" }}{%- endif -%}
|
|
35
|
+
{{ serialize_semver(base, pre, metadata)}}"""
|
|
33
36
|
[build-system]
|
|
34
37
|
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
|
|
35
38
|
build-backend = "poetry_dynamic_versioning.backend"
|
|
36
39
|
|
|
37
40
|
[tool.poetry.dependencies]
|
|
38
|
-
python = "^3.
|
|
41
|
+
python = "^3.9"
|
|
39
42
|
click = ">=8"
|
|
40
43
|
omegaconf = "^2.3"
|
|
41
44
|
typing-extensions = {version = ">=4.2", markers = "python_version < \"3.12\""}
|
|
42
45
|
attrs = "^23.1.0"
|
|
43
46
|
fasteners = "^0.19"
|
|
44
47
|
pyyaml = "^6.0.1"
|
|
45
|
-
psutil = "
|
|
48
|
+
psutil = ">=7"
|
|
46
49
|
pytools = "^2023.1.1"
|
|
47
50
|
tqdm = "^4.66.1"
|
|
48
51
|
docstring-parser = "^0.15"
|
|
@@ -119,7 +122,7 @@ warn_unused_ignores = true
|
|
|
119
122
|
|
|
120
123
|
[tool.commitizen]
|
|
121
124
|
name = "cz_conventional_commits"
|
|
122
|
-
version = "1.
|
|
125
|
+
version = "1.7.0rc0"
|
|
123
126
|
changelog_start_rev = "0.15.0"
|
|
124
127
|
tag_format = "v$version"
|
|
125
128
|
update_changelog_on_bump = true
|
|
@@ -36,15 +36,17 @@ from .core.arguments import (
|
|
|
36
36
|
DataPath,
|
|
37
37
|
Annotated,
|
|
38
38
|
Constant,
|
|
39
|
+
field,
|
|
39
40
|
# Annotations helpers
|
|
40
41
|
help,
|
|
41
42
|
default,
|
|
42
43
|
)
|
|
43
|
-
from .generators import pathgenerator
|
|
44
|
+
from .generators import pathgenerator, PathGenerator
|
|
44
45
|
from .core.objects import (
|
|
45
46
|
Config,
|
|
46
47
|
copyconfig,
|
|
47
48
|
setmeta,
|
|
49
|
+
DependentMarker,
|
|
48
50
|
Task,
|
|
49
51
|
LightweightTask,
|
|
50
52
|
ObjectStore,
|
|
@@ -8,7 +8,7 @@ import experimaestro.core.objects as objects
|
|
|
8
8
|
import experimaestro.core.types as types
|
|
9
9
|
from experimaestro.generators import PathGenerator
|
|
10
10
|
|
|
11
|
-
from .core.arguments import Argument as CoreArgument
|
|
11
|
+
from .core.arguments import Argument as CoreArgument, field
|
|
12
12
|
from .core.objects import Config
|
|
13
13
|
from .core.types import Any, Identifier, TypeProxy, Type, ObjectType
|
|
14
14
|
from .utils import logger
|
|
@@ -134,12 +134,22 @@ class param:
|
|
|
134
134
|
self.type = Type.fromType(type) if type else None
|
|
135
135
|
self.help = help
|
|
136
136
|
self.ignored = ignored
|
|
137
|
-
self.default = default
|
|
138
137
|
self.required = required
|
|
139
|
-
self.generator = None
|
|
140
138
|
self.checker = checker
|
|
141
139
|
self.constant = constant
|
|
142
140
|
|
|
141
|
+
self.generator = None
|
|
142
|
+
self.default = None
|
|
143
|
+
|
|
144
|
+
# Set default or generator
|
|
145
|
+
if isinstance(default, field):
|
|
146
|
+
if default.default is not None:
|
|
147
|
+
self.default = default
|
|
148
|
+
elif default.default_factory is not None:
|
|
149
|
+
self.generator = default.default_factory
|
|
150
|
+
else:
|
|
151
|
+
self.default = default
|
|
152
|
+
|
|
143
153
|
def __call__(self, tp):
|
|
144
154
|
# Don't annotate in task mode
|
|
145
155
|
tp.__getxpmtype__().addAnnotation(self)
|
|
@@ -3,7 +3,7 @@ import pyparsing as pp
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
import json
|
|
5
5
|
from experimaestro.compat import cached_property
|
|
6
|
-
import
|
|
6
|
+
import re
|
|
7
7
|
from experimaestro.scheduler import JobState
|
|
8
8
|
|
|
9
9
|
|
|
@@ -87,7 +87,7 @@ class NotInExpr(BaseInExpr):
|
|
|
87
87
|
class RegexExpr:
|
|
88
88
|
def __init__(self, tokens):
|
|
89
89
|
self.var, expr = tokens
|
|
90
|
-
self.regex =
|
|
90
|
+
self.regex = re.compile(expr)
|
|
91
91
|
|
|
92
92
|
def __repr__(self):
|
|
93
93
|
return f"""REGEX[{self.varname}, {self.value}]"""
|
|
@@ -103,7 +103,7 @@ class RegexExpr:
|
|
|
103
103
|
if not value:
|
|
104
104
|
return False
|
|
105
105
|
|
|
106
|
-
return self.
|
|
106
|
+
return self.re.match(value)
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
class ConstantString:
|
|
@@ -69,7 +69,7 @@ def process(
|
|
|
69
69
|
if (p / "jobs.bak").is_dir():
|
|
70
70
|
cprint(f" Experiment {p.name} has not finished yet", "red")
|
|
71
71
|
if (not perform) and (kill or clean):
|
|
72
|
-
cprint(" Preventing kill/clean (use --
|
|
72
|
+
cprint(" Preventing kill/clean (use --perform if you want to)", "yellow")
|
|
73
73
|
kill = False
|
|
74
74
|
clean = False
|
|
75
75
|
|
|
@@ -9,7 +9,8 @@ This module contains :
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import enum
|
|
12
|
-
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Any, Dict, Mapping, Type, Union, Optional
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from experimaestro.utils import logger
|
|
15
16
|
from experimaestro.locking import Lock
|
|
@@ -86,12 +87,12 @@ class Process:
|
|
|
86
87
|
@staticmethod
|
|
87
88
|
def fromDefinition(connector: "Connector", definition: Dict[str, Any]) -> "Process":
|
|
88
89
|
"""Retrieves a process from a serialized definition"""
|
|
89
|
-
|
|
90
|
+
handler_type = definition["type"]
|
|
91
|
+
handler = Process.handler(handler_type)
|
|
92
|
+
assert handler is not None, f"No handler of type {handler_type}"
|
|
90
93
|
try:
|
|
91
94
|
return handler.fromspec(connector, definition)
|
|
92
95
|
except Exception as e:
|
|
93
|
-
import logging
|
|
94
|
-
|
|
95
96
|
logging.exception("Could not retrieve job from specification")
|
|
96
97
|
raise e
|
|
97
98
|
|
|
@@ -101,7 +102,11 @@ class Process:
|
|
|
101
102
|
if Process.HANDLERS is None:
|
|
102
103
|
Process.HANDLERS = {}
|
|
103
104
|
for ep in pkg_resources.iter_entry_points(group="experimaestro.process"):
|
|
104
|
-
|
|
105
|
+
logging.debug("Adding process handler for type %s", ep.name)
|
|
106
|
+
handler = ep.load()
|
|
107
|
+
Process.HANDLERS[ep.name] = handler
|
|
108
|
+
if handler is None:
|
|
109
|
+
logging.error("Handler of type %s is null", ep.name)
|
|
105
110
|
|
|
106
111
|
return Process.HANDLERS.get(key, None)
|
|
107
112
|
|
|
@@ -117,10 +122,14 @@ class Process:
|
|
|
117
122
|
"""True is the process is truly running (I/O)"""
|
|
118
123
|
return (await self.aio_state()).running
|
|
119
124
|
|
|
120
|
-
async def aio_code(self):
|
|
121
|
-
"""Returns a future containing the returned code
|
|
125
|
+
async def aio_code(self) -> Optional[int]:
|
|
126
|
+
"""Returns a future containing the returned code
|
|
127
|
+
|
|
128
|
+
Returns None if the process has already finished – and no information is
|
|
129
|
+
known about the process.
|
|
130
|
+
"""
|
|
122
131
|
code = await asyncThreadcheck("aio_code", self.wait)
|
|
123
|
-
logger.debug("Got
|
|
132
|
+
logger.debug("Got return code %s for %s", code, self)
|
|
124
133
|
return code
|
|
125
134
|
|
|
126
135
|
def kill(self):
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"""
|
|
3
3
|
|
|
4
4
|
import subprocess
|
|
5
|
+
from typing import Optional
|
|
5
6
|
from pathlib import Path, WindowsPath, PosixPath
|
|
6
7
|
import os
|
|
7
8
|
import threading
|
|
@@ -29,11 +30,13 @@ class PsutilProcess(Process):
|
|
|
29
30
|
def __init__(self, pid: int):
|
|
30
31
|
self._process = psutil.Process(pid)
|
|
31
32
|
|
|
32
|
-
def wait(self) -> int:
|
|
33
|
+
def wait(self) -> Optional[int]:
|
|
33
34
|
logger.debug("Waiting (psutil) for process with PID %s", self._process.pid)
|
|
34
35
|
code = self._process.wait()
|
|
35
36
|
logger.debug(
|
|
36
|
-
"Finished to wait (psutil) for process with PID %s",
|
|
37
|
+
"Finished to wait (psutil) for process with PID %s: code %s",
|
|
38
|
+
self._process.pid,
|
|
39
|
+
code,
|
|
37
40
|
)
|
|
38
41
|
return code
|
|
39
42
|
|
|
@@ -57,7 +60,9 @@ class LocalProcess(Process):
|
|
|
57
60
|
logger.debug("Waiting (python) for process with PID %s", self._process.pid)
|
|
58
61
|
code = self._process.wait()
|
|
59
62
|
logger.debug(
|
|
60
|
-
"Finished to wait (python) for process with PID %s",
|
|
63
|
+
"Finished to wait (python) for process with PID %s: %s",
|
|
64
|
+
self._process.pid,
|
|
65
|
+
code,
|
|
61
66
|
)
|
|
62
67
|
return code
|
|
63
68
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Management of the arguments (params, options, etc) associated with the XPM objects"""
|
|
2
2
|
|
|
3
|
-
from typing import Optional, TypeVar, TYPE_CHECKING
|
|
3
|
+
from typing import Optional, TypeVar, TYPE_CHECKING, Callable, Any
|
|
4
4
|
from experimaestro.typingutils import get_optional
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
import sys
|
|
@@ -75,11 +75,22 @@ class Argument:
|
|
|
75
75
|
self.constant = constant
|
|
76
76
|
self.ignored = self.type.ignore if ignored is None else ignored
|
|
77
77
|
self.required = required
|
|
78
|
-
self.default = default
|
|
79
|
-
self.generator = generator
|
|
80
78
|
self.objecttype = None
|
|
81
79
|
self.is_data = is_data
|
|
82
80
|
|
|
81
|
+
self.generator = generator
|
|
82
|
+
self.default = None
|
|
83
|
+
|
|
84
|
+
if default is not None:
|
|
85
|
+
assert self.generator is None, "generator and default are exclusive options"
|
|
86
|
+
if isinstance(default, field):
|
|
87
|
+
if default.default is not None:
|
|
88
|
+
self.default = default.default
|
|
89
|
+
elif default.default_factory is not None:
|
|
90
|
+
self.generator = default.default_factory
|
|
91
|
+
else:
|
|
92
|
+
self.default = default
|
|
93
|
+
|
|
83
94
|
assert (
|
|
84
95
|
not self.constant or self.default is not None
|
|
85
96
|
), "Cannot be constant without default"
|
|
@@ -170,6 +181,18 @@ DataPath = Annotated[Path, dataHint]
|
|
|
170
181
|
"""Annotates a path that should be kept to restore an object to its state"""
|
|
171
182
|
|
|
172
183
|
|
|
184
|
+
class field:
|
|
185
|
+
"""Extra information for a given experimaestro field (param or meta)"""
|
|
186
|
+
|
|
187
|
+
def __init__(self, *, default: Any = None, default_factory: Callable = None):
|
|
188
|
+
assert not (
|
|
189
|
+
(default is not None) and (default_factory is not None)
|
|
190
|
+
), "default and default_factory are mutually exclusive options"
|
|
191
|
+
|
|
192
|
+
self.default_factory = default_factory
|
|
193
|
+
self.default = default
|
|
194
|
+
|
|
195
|
+
|
|
173
196
|
class help(TypeAnnotation):
|
|
174
197
|
def __init__(self, text: str):
|
|
175
198
|
self.text = text
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
from functools import cached_property
|
|
4
4
|
import json
|
|
5
5
|
|
|
6
|
+
from attr import define
|
|
7
|
+
|
|
8
|
+
from experimaestro import taskglobals
|
|
9
|
+
|
|
6
10
|
try:
|
|
7
11
|
from types import NoneType
|
|
8
12
|
except Exception:
|
|
@@ -21,6 +25,7 @@ import inspect
|
|
|
21
25
|
import importlib
|
|
22
26
|
from typing import (
|
|
23
27
|
Any,
|
|
28
|
+
Callable,
|
|
24
29
|
ClassVar,
|
|
25
30
|
Dict,
|
|
26
31
|
Iterator,
|
|
@@ -136,6 +141,8 @@ class ConfigPath:
|
|
|
136
141
|
|
|
137
142
|
hash_logger = logging.getLogger("xpm.hash")
|
|
138
143
|
|
|
144
|
+
DependentMarker = Callable[["Config"], None]
|
|
145
|
+
|
|
139
146
|
|
|
140
147
|
class HashComputer:
|
|
141
148
|
"""This class is in charge of computing a config/task identifier"""
|
|
@@ -555,6 +562,24 @@ class ObjectStore:
|
|
|
555
562
|
self.store[identifier] = stub
|
|
556
563
|
|
|
557
564
|
|
|
565
|
+
@define()
|
|
566
|
+
class WatchedOutput:
|
|
567
|
+
#: The enclosing job
|
|
568
|
+
job: "Job"
|
|
569
|
+
|
|
570
|
+
#: The configuration containing the watched output
|
|
571
|
+
config: "ConfigInformation"
|
|
572
|
+
|
|
573
|
+
#: The watched output (name)
|
|
574
|
+
method_name: str
|
|
575
|
+
|
|
576
|
+
#: The watched output method (called with the JSON event)
|
|
577
|
+
method: Callable
|
|
578
|
+
|
|
579
|
+
#: The callback to call (with the output of the previous method)
|
|
580
|
+
callback: Callable
|
|
581
|
+
|
|
582
|
+
|
|
558
583
|
class ConfigInformation:
|
|
559
584
|
"""Holds experimaestro information for a config (or task) instance"""
|
|
560
585
|
|
|
@@ -595,6 +620,9 @@ class ConfigInformation:
|
|
|
595
620
|
# Initialization tasks
|
|
596
621
|
self.init_tasks: List["LightweightTask"] = []
|
|
597
622
|
|
|
623
|
+
# Watched outputs
|
|
624
|
+
self.watched_outputs: List[WatchedOutput] = []
|
|
625
|
+
|
|
598
626
|
# Cached information
|
|
599
627
|
|
|
600
628
|
self._full_identifier = None
|
|
@@ -724,9 +752,16 @@ class ConfigInformation:
|
|
|
724
752
|
# Generate values
|
|
725
753
|
for k, argument in config.__xpmtype__.arguments.items():
|
|
726
754
|
if argument.generator:
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
755
|
+
sig = inspect.signature(argument.generator)
|
|
756
|
+
if len(sig.parameters) == 2:
|
|
757
|
+
value = argument.generator(self.context, config)
|
|
758
|
+
elif len(sig.parameters) == 0:
|
|
759
|
+
value = argument.generator()
|
|
760
|
+
else:
|
|
761
|
+
assert (
|
|
762
|
+
False
|
|
763
|
+
), "generator has either two parameters (context and config), or none"
|
|
764
|
+
config.__xpm__.set(k, value, bypass=True)
|
|
730
765
|
|
|
731
766
|
config.__xpm__._sealed = True
|
|
732
767
|
|
|
@@ -899,6 +934,19 @@ class ConfigInformation:
|
|
|
899
934
|
# Now, seal the object
|
|
900
935
|
self.seal(context)
|
|
901
936
|
|
|
937
|
+
def watch_output(self, method, callback):
|
|
938
|
+
"""Watch the task output linked with a given method
|
|
939
|
+
|
|
940
|
+
:param method: The method to watch
|
|
941
|
+
:param callback: The callback
|
|
942
|
+
"""
|
|
943
|
+
watched = WatchedOutput(
|
|
944
|
+
self, method.__self__, method.__name__, method, callback
|
|
945
|
+
)
|
|
946
|
+
self.watched_outputs.append(watched)
|
|
947
|
+
if self.job:
|
|
948
|
+
self.job.watch_output(watched)
|
|
949
|
+
|
|
902
950
|
def submit(
|
|
903
951
|
self,
|
|
904
952
|
workspace: "Workspace",
|
|
@@ -1000,7 +1048,7 @@ class ConfigInformation:
|
|
|
1000
1048
|
|
|
1001
1049
|
print(file=sys.stderr) # noqa: T201
|
|
1002
1050
|
|
|
1003
|
-
# Handle an output configuration
|
|
1051
|
+
# Handle an output configuration # FIXME: remove
|
|
1004
1052
|
def mark_output(config: "Config"):
|
|
1005
1053
|
"""Sets a dependency on the job"""
|
|
1006
1054
|
assert not isinstance(config, Task), "Cannot set a dependency on a task"
|
|
@@ -1011,12 +1059,18 @@ class ConfigInformation:
|
|
|
1011
1059
|
self.task = self.pyobject
|
|
1012
1060
|
|
|
1013
1061
|
if hasattr(self.pyobject, "task_outputs"):
|
|
1014
|
-
self._taskoutput = self.pyobject.task_outputs(mark_output)
|
|
1062
|
+
self._taskoutput = self.pyobject.task_outputs(self.mark_output)
|
|
1015
1063
|
else:
|
|
1016
1064
|
self._taskoutput = self.task = self.pyobject
|
|
1017
1065
|
|
|
1018
1066
|
return self._taskoutput
|
|
1019
1067
|
|
|
1068
|
+
def mark_output(self, config: "Config"):
|
|
1069
|
+
"""Sets a dependency on the job"""
|
|
1070
|
+
assert not isinstance(config, Task), "Cannot set a dependency on a task"
|
|
1071
|
+
config.__xpm__.task = self.pyobject
|
|
1072
|
+
return config
|
|
1073
|
+
|
|
1020
1074
|
# --- Serialization
|
|
1021
1075
|
|
|
1022
1076
|
@staticmethod
|
|
@@ -1345,7 +1399,9 @@ class ConfigInformation:
|
|
|
1345
1399
|
mod = importlib.import_module(module_name)
|
|
1346
1400
|
except ModuleNotFoundError:
|
|
1347
1401
|
# More hints on the nature of the error
|
|
1348
|
-
logging.warning(
|
|
1402
|
+
logging.warning(
|
|
1403
|
+
"(1) Either the python path is wrong – %s", ":".join(sys.path)
|
|
1404
|
+
)
|
|
1349
1405
|
logging.warning("(2) There is not __init__.py in your module")
|
|
1350
1406
|
raise
|
|
1351
1407
|
|
|
@@ -1790,6 +1846,10 @@ class classproperty(property):
|
|
|
1790
1846
|
class Config:
|
|
1791
1847
|
"""Base type for all objects in python interface"""
|
|
1792
1848
|
|
|
1849
|
+
__xpmid__: ClassVar[Optional[str]]
|
|
1850
|
+
"""Optional configuration ID, mostly useful when moving a class to another
|
|
1851
|
+
package to avoid changes in computed task identifiers"""
|
|
1852
|
+
|
|
1793
1853
|
__xpmtype__: ClassVar[ObjectType]
|
|
1794
1854
|
"""The object type holds all the information about a specific subclass
|
|
1795
1855
|
experimaestro metadata"""
|
|
@@ -1914,6 +1974,22 @@ class Config:
|
|
|
1914
1974
|
"""Access pre-tasks"""
|
|
1915
1975
|
raise AssertionError("Pre-tasks can be accessed only during configuration")
|
|
1916
1976
|
|
|
1977
|
+
def register_task_output(self, method, *args, **kwargs):
|
|
1978
|
+
# Determine the path for this...
|
|
1979
|
+
path = taskglobals.Env.instance().xpm_path / "task-outputs.jsonl"
|
|
1980
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
1981
|
+
|
|
1982
|
+
data = json.dumps(
|
|
1983
|
+
{
|
|
1984
|
+
"key": f"{self.__xpmidentifier__}/{method.__name__}",
|
|
1985
|
+
"args": args,
|
|
1986
|
+
"kwargs": kwargs,
|
|
1987
|
+
}
|
|
1988
|
+
)
|
|
1989
|
+
with path.open("at") as fp:
|
|
1990
|
+
fp.writelines([data, "\n"])
|
|
1991
|
+
fp.flush()
|
|
1992
|
+
|
|
1917
1993
|
|
|
1918
1994
|
class LightweightTask(Config):
|
|
1919
1995
|
"""A task that can be run before or after a real task to modify its behaviour"""
|
|
@@ -1931,6 +2007,14 @@ class Task(LightweightTask):
|
|
|
1931
2007
|
def submit(self):
|
|
1932
2008
|
raise AssertionError("This method can only be used during configuration")
|
|
1933
2009
|
|
|
2010
|
+
def watch_output(self, method, callback):
|
|
2011
|
+
"""Sets up a callback
|
|
2012
|
+
|
|
2013
|
+
:param method: a method within a configuration
|
|
2014
|
+
:param callback: the callback
|
|
2015
|
+
"""
|
|
2016
|
+
self.__xpm__.watch_output(method, callback)
|
|
2017
|
+
|
|
1934
2018
|
|
|
1935
2019
|
# --- Utility functions
|
|
1936
2020
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import ABC
|
|
2
|
+
from attrs import define
|
|
2
3
|
import typing_extensions
|
|
3
4
|
|
|
4
5
|
from experimaestro.core.types import ObjectType
|
|
@@ -108,6 +109,10 @@ class ConfigWalk(ConfigProcessing):
|
|
|
108
109
|
def map(self, k: str): ...
|
|
109
110
|
|
|
110
111
|
def getqualattr(module, qualname): ...
|
|
112
|
+
@define(frozen=True)
|
|
113
|
+
class WatchedOutput:
|
|
114
|
+
config: "Config"
|
|
115
|
+
method_name: str
|
|
111
116
|
|
|
112
117
|
class ConfigInformation:
|
|
113
118
|
LOADING: bool
|
|
@@ -116,6 +121,7 @@ class ConfigInformation:
|
|
|
116
121
|
values: Dict[str, Any]
|
|
117
122
|
job: Job
|
|
118
123
|
dependencies: Incomplete
|
|
124
|
+
watched_outputs: List[WatchedOutput]
|
|
119
125
|
def __init__(self, pyobject: TypeConfig) -> None: ...
|
|
120
126
|
def set_meta(self, value: Optional[bool]): ...
|
|
121
127
|
@property
|
|
@@ -231,7 +237,7 @@ class LightweightTask(Config):
|
|
|
231
237
|
def execute(self) -> None: ...
|
|
232
238
|
|
|
233
239
|
class Task(LightweightTask):
|
|
234
|
-
__tags__: Dict[str, str]
|
|
240
|
+
# __tags__: Dict[str, str]
|
|
235
241
|
|
|
236
242
|
def submit(
|
|
237
243
|
self,
|
|
@@ -26,6 +26,7 @@ if typing.TYPE_CHECKING:
|
|
|
26
26
|
|
|
27
27
|
class Identifier:
|
|
28
28
|
def __init__(self, name: str):
|
|
29
|
+
assert isinstance(name, str)
|
|
29
30
|
self.name = name
|
|
30
31
|
|
|
31
32
|
def __hash__(self):
|
|
@@ -126,6 +127,9 @@ class Type:
|
|
|
126
127
|
if t:
|
|
127
128
|
return DictType(Type.fromType(t[0]), Type.fromType(t[1]))
|
|
128
129
|
|
|
130
|
+
if union_t := typingutils.get_union(key):
|
|
131
|
+
return UnionType([Type.fromType(t) for t in union_t])
|
|
132
|
+
|
|
129
133
|
# Takes care of generics
|
|
130
134
|
if get_origin(key):
|
|
131
135
|
return GenericType(key)
|
|
@@ -223,7 +227,7 @@ class ObjectType(Type):
|
|
|
223
227
|
__xpmid__ = getattr(tp, "__xpmid__")
|
|
224
228
|
if isinstance(__xpmid__, Identifier):
|
|
225
229
|
identifier = __xpmid__
|
|
226
|
-
|
|
230
|
+
elif inspect.ismethod(__xpmid__):
|
|
227
231
|
identifier = Identifier(__xpmid__())
|
|
228
232
|
elif "__xpmid__" in tp.__dict__:
|
|
229
233
|
identifier = Identifier(__xpmid__)
|
|
@@ -576,7 +580,7 @@ class PathType(Type):
|
|
|
576
580
|
return Path(value.get("$value"))
|
|
577
581
|
|
|
578
582
|
if not isinstance(value, (str, Path)):
|
|
579
|
-
raise TypeError("value is not a pathlike value")
|
|
583
|
+
raise TypeError(f"value is not a pathlike value ({type(value)})")
|
|
580
584
|
return Path(value)
|
|
581
585
|
|
|
582
586
|
@property
|
|
@@ -631,6 +635,32 @@ class EnumType(Type):
|
|
|
631
635
|
return f"Enum({self.type})"
|
|
632
636
|
|
|
633
637
|
|
|
638
|
+
class UnionType(Type):
|
|
639
|
+
def __init__(self, types: List[Type]):
|
|
640
|
+
self.types = types
|
|
641
|
+
|
|
642
|
+
def name(self):
|
|
643
|
+
return "Union[" + ", ".join(t.name() for t in self.types) + "]"
|
|
644
|
+
|
|
645
|
+
def __str__(self):
|
|
646
|
+
return "[" + " | ".join(t.name() for t in self.types) + " ]"
|
|
647
|
+
|
|
648
|
+
def __repr__(self):
|
|
649
|
+
return str(self)
|
|
650
|
+
|
|
651
|
+
def validate(self, value):
|
|
652
|
+
for subtype in self.types:
|
|
653
|
+
try:
|
|
654
|
+
return subtype.validate(value)
|
|
655
|
+
except ValueError:
|
|
656
|
+
pass
|
|
657
|
+
except TypeError:
|
|
658
|
+
pass
|
|
659
|
+
|
|
660
|
+
if not isinstance(value, dict):
|
|
661
|
+
raise ValueError(f"value is not within the types {self}")
|
|
662
|
+
|
|
663
|
+
|
|
634
664
|
class DictType(Type):
|
|
635
665
|
def __init__(self, keytype: Type, valuetype: Type):
|
|
636
666
|
self.keytype = keytype
|
|
@@ -675,6 +705,7 @@ class GenericType(Type):
|
|
|
675
705
|
(mro for mro in mros if (get_origin(mro) or mro) is self.origin), None
|
|
676
706
|
)
|
|
677
707
|
target = get_origin(self.type) or self.type
|
|
708
|
+
|
|
678
709
|
if matching is None:
|
|
679
710
|
raise ValueError(
|
|
680
711
|
f"{type(value)} is not of type {target} ({type(value).__mro__})"
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
3
4
|
from typing import Callable, Union
|
|
4
5
|
from experimaestro.core.arguments import ArgumentOptions, TypeAnnotation
|
|
5
6
|
from experimaestro.core.objects import ConfigWalkContext, Config
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
class Generator:
|
|
9
|
+
class Generator(ABC):
|
|
9
10
|
"""Base class for all generators"""
|
|
10
11
|
|
|
11
12
|
def isoutput(self):
|
|
@@ -13,6 +14,10 @@ class Generator:
|
|
|
13
14
|
path within the job folder)"""
|
|
14
15
|
return False
|
|
15
16
|
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def __call__(self, context: ConfigWalkContext, config: Config):
|
|
19
|
+
...
|
|
20
|
+
|
|
16
21
|
|
|
17
22
|
class PathGenerator(Generator):
|
|
18
23
|
"""Generates a path"""
|
|
@@ -7,6 +7,7 @@ import sys
|
|
|
7
7
|
import logging
|
|
8
8
|
from .utils import logger
|
|
9
9
|
from watchdog.observers import Observer
|
|
10
|
+
from watchdog.observers.api import ObservedWatch
|
|
10
11
|
from watchdog.events import FileSystemEventHandler
|
|
11
12
|
|
|
12
13
|
|
|
@@ -20,7 +21,9 @@ class IPCom:
|
|
|
20
21
|
self.observer.start()
|
|
21
22
|
self.pid = os.getpid()
|
|
22
23
|
|
|
23
|
-
def fswatch(
|
|
24
|
+
def fswatch(
|
|
25
|
+
self, watcher: FileSystemEventHandler, path: Path, recursive=False
|
|
26
|
+
) -> ObservedWatch:
|
|
24
27
|
if not self.observer.is_alive():
|
|
25
28
|
logging.error("Observer is not alive")
|
|
26
29
|
|