experimaestro 1.4.2__tar.gz → 1.5.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.
Potentially problematic release.
This version of experimaestro might be problematic. Click here for more details.
- {experimaestro-1.4.2 → experimaestro-1.5.0}/PKG-INFO +2 -3
- {experimaestro-1.4.2 → experimaestro-1.5.0}/pyproject.toml +4 -5
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/connectors/ssh.py +31 -11
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/core/objects.py +72 -27
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/core/objects.pyi +11 -5
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/core/types.py +49 -9
- experimaestro-1.5.0/src/experimaestro/exceptions.py +2 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/experiments/cli.py +32 -15
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/experiments/configuration.py +20 -5
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launcherfinder/registry.py +4 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launcherfinder/specs.py +7 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/scheduler/base.py +27 -8
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_objects.py +13 -6
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_param.py +2 -2
- experimaestro-1.5.0/src/experimaestro/typingutils.py +110 -0
- experimaestro-1.4.2/src/experimaestro/typingutils.py +0 -65
- {experimaestro-1.4.2 → experimaestro-1.5.0}/LICENSE +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/README.md +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/__main__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/annotations.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/checkers.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/click.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/commandline.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/compat.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/connectors/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/connectors/local.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/core/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/core/arguments.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/core/context.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/core/serialization.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/core/serializers.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/core/utils.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/experiments/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/filter.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/generators.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/huggingface.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/ipc.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launcherfinder/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launcherfinder/base.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launcherfinder/parser.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launchers/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launchers/direct.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launchers/oar.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launchers/slurm/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launchers/slurm/base.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launchers/slurm/cli.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/launchers/slurm/configuration.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/locking.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/mkdocs/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/mkdocs/annotations.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/mkdocs/base.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/mkdocs/metaloader.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/mkdocs/style.css +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/mypy.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/notifications.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/py.typed +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/rpyc.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/run.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/scheduler/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/scheduler/dependencies.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/scheduler/environment.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/scheduler/services.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/scheduler/workspace.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/scriptbuilder.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/016b4a6cdced82ab3aa1.ttf +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/50701fbb8177c2dde530.ttf +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/878f31251d960bd6266f.woff2 +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/b041b1fa4fe241b23445.woff2 +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/b6879d41b0852f01ed5b.woff2 +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/d75e3fd1eb12e9bd6655.ttf +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/favicon.ico +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/index.css +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/index.css.map +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/index.html +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/index.js +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/index.js.map +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/login.html +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/server/data/manifest.json +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/settings.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/sphinx/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/sphinx/static/experimaestro.css +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/taskglobals.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/conftest.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/connectors/bin/executable.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/connectors/test_local.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/connectors/utils.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/definitions_types.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/launchers/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/launchers/bin/sacct +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/launchers/bin/sbatch +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/launchers/bin/test.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/launchers/common.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/launchers/config_slurm/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/launchers/config_slurm/launchers.yaml +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/launchers/test_local.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/launchers/test_slurm.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/restart.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/restart_main.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/scripts/notifyandwait.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/scripts/waitforfile.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/task_tokens.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/tasks/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/tasks/all.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/tasks/foreign.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_checkers.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_dependencies.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_findlauncher.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_forward.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_identifier.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_instance.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_outputs.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_progress.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_serializers.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_snippets.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_ssh.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_tags.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_tasks.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_tokens.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_types.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/test_validation.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/token_reschedule.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tests/utils.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tokens.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tools/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tools/diff.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tools/documentation.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/tools/jobs.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/utils/__init__.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/utils/asyncio.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/utils/jobs.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/utils/jupyter.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/utils/resources.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/utils/settings.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/utils/yaml.py +0 -0
- {experimaestro-1.4.2 → experimaestro-1.5.0}/src/experimaestro/xpmutils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: experimaestro
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: "Experimaestro is a computer science experiment manager"
|
|
5
5
|
Home-page: https://github.com/experimaestro/experimaestro-python
|
|
6
6
|
License: GPL-3
|
|
@@ -23,7 +23,6 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
23
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
24
|
Requires-Dist: arpeggio (>=2,<3)
|
|
25
25
|
Requires-Dist: attrs (>=23.1.0,<24.0.0)
|
|
26
|
-
Requires-Dist: cached-property (>=1.5.2,<2.0.0) ; python_version < "3.9"
|
|
27
26
|
Requires-Dist: click (>=8)
|
|
28
27
|
Requires-Dist: decorator (>=5,<6)
|
|
29
28
|
Requires-Dist: docstring-parser (>=0.15,<0.16)
|
|
@@ -46,7 +45,7 @@ Requires-Dist: rpyc (>=5,<6)
|
|
|
46
45
|
Requires-Dist: sortedcontainers (>=2.4,<3.0)
|
|
47
46
|
Requires-Dist: termcolor (>=2.3)
|
|
48
47
|
Requires-Dist: tqdm (>=4.66.1,<5.0.0)
|
|
49
|
-
Requires-Dist: typing-extensions (>=4.2) ; python_version < "3.
|
|
48
|
+
Requires-Dist: typing-extensions (>=4.2) ; python_version < "3.12"
|
|
50
49
|
Requires-Dist: watchdog (>=2,<3)
|
|
51
50
|
Project-URL: Documentation, https://experimaestro-python.readthedocs.io/
|
|
52
51
|
Project-URL: Repository, https://github.com/experimaestro/experimaestro-python
|
|
@@ -19,7 +19,7 @@ include = [
|
|
|
19
19
|
"src/experimaestro/sphinx/static/experimaestro.css",
|
|
20
20
|
"src/experimaestro/mkdocs/style.css"
|
|
21
21
|
]
|
|
22
|
-
version = "1.
|
|
22
|
+
version = "1.5.0"
|
|
23
23
|
repository = "https://github.com/experimaestro/experimaestro-python"
|
|
24
24
|
documentation = "https://experimaestro-python.readthedocs.io/"
|
|
25
25
|
|
|
@@ -38,8 +38,7 @@ build-backend = "poetry_dynamic_versioning.backend"
|
|
|
38
38
|
python = "^3.8"
|
|
39
39
|
click = ">=8"
|
|
40
40
|
omegaconf = "^2.3"
|
|
41
|
-
typing-extensions = {version = ">=4.2", markers = "python_version < \"3.
|
|
42
|
-
cached-property = {markers = "python_version < \"3.9\"", version = "^1.5.2"}
|
|
41
|
+
typing-extensions = {version = ">=4.2", markers = "python_version < \"3.12\""}
|
|
43
42
|
attrs = "^23.1.0"
|
|
44
43
|
fasteners = "^0.19"
|
|
45
44
|
pyyaml = "^6.0.1"
|
|
@@ -119,12 +118,12 @@ max-line-length = "88"
|
|
|
119
118
|
extend-ignore = "E203"
|
|
120
119
|
|
|
121
120
|
[tool.mypy]
|
|
122
|
-
python_version = "3.
|
|
121
|
+
python_version = "3.9"
|
|
123
122
|
warn_unused_ignores = true
|
|
124
123
|
|
|
125
124
|
[tool.commitizen]
|
|
126
125
|
name = "cz_conventional_commits"
|
|
127
|
-
version = "1.
|
|
126
|
+
version = "1.5.0"
|
|
128
127
|
changelog_start_rev = "0.15.0"
|
|
129
128
|
tag_format = "v$version"
|
|
130
129
|
update_changelog_on_bump = true
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from pathlib import Path,
|
|
2
|
+
from pathlib import Path, _posix_flavour
|
|
3
3
|
import io
|
|
4
4
|
import os
|
|
5
|
+
import re
|
|
5
6
|
from experimaestro.launcherfinder import LauncherRegistry, YAMLDataClass
|
|
6
7
|
from fabric import Connection
|
|
7
8
|
from invoke import Promise
|
|
@@ -21,7 +22,7 @@ from experimaestro.tokens import Token
|
|
|
21
22
|
# Might be wise to switch to https://github.com/marian-code/ssh-utilities
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
class SshPath(Path
|
|
25
|
+
class SshPath(Path):
|
|
25
26
|
"""SSH path
|
|
26
27
|
|
|
27
28
|
Absolute:
|
|
@@ -31,16 +32,31 @@ class SshPath(Path, PurePosixPath):
|
|
|
31
32
|
ssh://[user@]host[:port]/relative/path
|
|
32
33
|
"""
|
|
33
34
|
|
|
35
|
+
_flavour = _posix_flavour
|
|
36
|
+
|
|
37
|
+
def __new__(cls, *args, **kwargs):
|
|
38
|
+
return object.__new__(cls)
|
|
39
|
+
|
|
40
|
+
def __init__(self, url: str):
|
|
41
|
+
parsed = urlparse(url)
|
|
42
|
+
assert parsed.scheme == "ssh"
|
|
43
|
+
self._host = parsed.hostname
|
|
44
|
+
|
|
45
|
+
self._parts = re.split(r"/+", parsed.path)
|
|
46
|
+
if parsed.path.startswith("//"):
|
|
47
|
+
self._parts[0] = "/"
|
|
48
|
+
else:
|
|
49
|
+
self._parts = self._parts[1:]
|
|
50
|
+
|
|
34
51
|
@property
|
|
35
52
|
def hostpath(self):
|
|
36
|
-
# path = "/" if self._parts[:0] + "/".join(self._parts[1:])
|
|
37
53
|
if self.is_absolute():
|
|
38
|
-
return "/" +
|
|
39
|
-
return
|
|
54
|
+
return "/" + "/".join(self._parts[1:])
|
|
55
|
+
return "/".join(self._parts)
|
|
40
56
|
|
|
41
57
|
@property
|
|
42
58
|
def host(self):
|
|
43
|
-
return self.
|
|
59
|
+
return self._host
|
|
44
60
|
|
|
45
61
|
def is_absolute(self):
|
|
46
62
|
return self._parts and self._parts[0] == "/"
|
|
@@ -66,11 +82,15 @@ class SshPath(Path, PurePosixPath):
|
|
|
66
82
|
|
|
67
83
|
def _make_child(self, args):
|
|
68
84
|
drv, root, parts = self._parse_args(args)
|
|
69
|
-
assert self.
|
|
85
|
+
assert self._host == drv or drv == "", f"{self._host} and {drv}"
|
|
70
86
|
drv, root, parts = self._flavour.join_parsed_parts(
|
|
71
|
-
"",
|
|
87
|
+
"", "", self._parts, "", root, parts
|
|
72
88
|
)
|
|
73
|
-
|
|
89
|
+
|
|
90
|
+
child = object.__new__(SshPath)
|
|
91
|
+
child._parts = parts
|
|
92
|
+
child._host = self._host
|
|
93
|
+
return child
|
|
74
94
|
|
|
75
95
|
def open(self, mode="r", buffering=-1, encoding=None, errors=None, newline=None):
|
|
76
96
|
# FIXME: should probably be wiser
|
|
@@ -105,10 +125,10 @@ class SshPath(Path, PurePosixPath):
|
|
|
105
125
|
return obj
|
|
106
126
|
|
|
107
127
|
def __repr__(self):
|
|
108
|
-
return "SshPath(%s,%s)" % (self.
|
|
128
|
+
return "SshPath(%s,%s)" % (self._host, self._flavour.join(self._parts[1:]))
|
|
109
129
|
|
|
110
130
|
def __str__(self):
|
|
111
|
-
return "ssh://%s/%s" % (self.
|
|
131
|
+
return "ssh://%s/%s" % (self._host, self._flavour.join(self._parts[1:]))
|
|
112
132
|
|
|
113
133
|
|
|
114
134
|
@dataclass
|
|
@@ -646,7 +646,7 @@ class ConfigInformation:
|
|
|
646
646
|
"Cannot set non existing attribute %s in %s" % (k, self.xpmtype)
|
|
647
647
|
)
|
|
648
648
|
except Exception:
|
|
649
|
-
logger.
|
|
649
|
+
logger.error("Error while setting value %s in %s", k, self.xpmtype)
|
|
650
650
|
raise
|
|
651
651
|
|
|
652
652
|
def addtag(self, name, value):
|
|
@@ -1093,7 +1093,7 @@ class ConfigInformation:
|
|
|
1093
1093
|
state_dict = {
|
|
1094
1094
|
"id": id(self.pyobject),
|
|
1095
1095
|
"module": self.xpmtype._module,
|
|
1096
|
-
"type": self.xpmtype.
|
|
1096
|
+
"type": self.xpmtype.basetype.__qualname__,
|
|
1097
1097
|
"typename": self.xpmtype.name(),
|
|
1098
1098
|
"identifier": self.identifier.state_dict(),
|
|
1099
1099
|
}
|
|
@@ -1340,7 +1340,10 @@ class ConfigInformation:
|
|
|
1340
1340
|
cls = getqualattr(mod, definition["type"])
|
|
1341
1341
|
|
|
1342
1342
|
# Creates an object (or a config)
|
|
1343
|
-
|
|
1343
|
+
if as_instance:
|
|
1344
|
+
o = cls.XPMValue.__new__(cls.XPMValue)
|
|
1345
|
+
else:
|
|
1346
|
+
o = cls.XPMConfig.__new__(cls.XPMConfig)
|
|
1344
1347
|
assert definition["id"] not in objects, "Duplicate id %s" % definition["id"]
|
|
1345
1348
|
objects[definition["id"]] = o
|
|
1346
1349
|
|
|
@@ -1377,6 +1380,7 @@ class ConfigInformation:
|
|
|
1377
1380
|
o.__xpm_stdout__ = basepath / f"{name.lower()}.out"
|
|
1378
1381
|
o.__xpm_stderr__ = basepath / f"{name.lower()}.err"
|
|
1379
1382
|
else:
|
|
1383
|
+
o.__init__()
|
|
1380
1384
|
xpminfo = o.__xpm__ # type: ConfigInformation
|
|
1381
1385
|
|
|
1382
1386
|
meta = definition.get("meta", None)
|
|
@@ -1497,12 +1501,7 @@ class ConfigInformation:
|
|
|
1497
1501
|
|
|
1498
1502
|
if o is None:
|
|
1499
1503
|
# Creates an object (and not a config)
|
|
1500
|
-
o = config.
|
|
1501
|
-
config.__xpmtype__.objecttype, __xpmobject__=True
|
|
1502
|
-
)
|
|
1503
|
-
|
|
1504
|
-
# And calls the parameter-less initialization
|
|
1505
|
-
o.__init__()
|
|
1504
|
+
o = config.XPMValue()
|
|
1506
1505
|
|
|
1507
1506
|
# Store in cache
|
|
1508
1507
|
self.objects.add_stub(id(config), o)
|
|
@@ -1650,8 +1649,8 @@ class TypeConfig:
|
|
|
1650
1649
|
[f"{key}={value}" for key, value in self.__xpm__.values.items()]
|
|
1651
1650
|
)
|
|
1652
1651
|
return (
|
|
1653
|
-
f"{self.__xpmtype__.
|
|
1654
|
-
f"{self.__xpmtype__.
|
|
1652
|
+
f"{self.__xpmtype__.basetype.__module__}."
|
|
1653
|
+
f"{self.__xpmtype__.basetype.__qualname__}({params})"
|
|
1655
1654
|
)
|
|
1656
1655
|
|
|
1657
1656
|
def tag(self, name, value):
|
|
@@ -1770,6 +1769,11 @@ class TypeConfig:
|
|
|
1770
1769
|
self.__xpm__.add_dependencies(*other.__xpm__.dependencies)
|
|
1771
1770
|
|
|
1772
1771
|
|
|
1772
|
+
class classproperty(property):
|
|
1773
|
+
def __get__(self, owner_self, owner_cls):
|
|
1774
|
+
return self.fget(owner_cls)
|
|
1775
|
+
|
|
1776
|
+
|
|
1773
1777
|
class Config:
|
|
1774
1778
|
"""Base type for all objects in python interface"""
|
|
1775
1779
|
|
|
@@ -1781,6 +1785,42 @@ class Config:
|
|
|
1781
1785
|
"""The __xpm__ object contains all instance specific information about a
|
|
1782
1786
|
configuration/task"""
|
|
1783
1787
|
|
|
1788
|
+
@classproperty
|
|
1789
|
+
def XPMConfig(cls):
|
|
1790
|
+
if issubclass(cls, TypeConfig):
|
|
1791
|
+
return cls
|
|
1792
|
+
return cls.__getxpmtype__().configtype
|
|
1793
|
+
|
|
1794
|
+
@classproperty
|
|
1795
|
+
def C(cls):
|
|
1796
|
+
return cls.XPMConfig
|
|
1797
|
+
|
|
1798
|
+
@classproperty
|
|
1799
|
+
def XPMValue(cls):
|
|
1800
|
+
"""Returns the value object for this configuration"""
|
|
1801
|
+
if issubclass(cls, TypeConfig):
|
|
1802
|
+
return cls.__xpmtype__.objecttype
|
|
1803
|
+
|
|
1804
|
+
if value_cls := cls.__dict__.get("__XPMValue__", None):
|
|
1805
|
+
pass
|
|
1806
|
+
else:
|
|
1807
|
+
from .types import XPMValue
|
|
1808
|
+
|
|
1809
|
+
__objectbases__ = tuple(
|
|
1810
|
+
s.XPMValue
|
|
1811
|
+
for s in cls.__bases__
|
|
1812
|
+
if issubclass(s, Config) and (s is not Config)
|
|
1813
|
+
) or (XPMValue,)
|
|
1814
|
+
|
|
1815
|
+
*tp_qual, tp_name = cls.__qualname__.split(".")
|
|
1816
|
+
value_cls = type(f"{tp_name}.XPMValue", (cls,) + __objectbases__, {})
|
|
1817
|
+
value_cls.__qualname__ = ".".join(tp_qual + [value_cls.__name__])
|
|
1818
|
+
value_cls.__module__ = cls.__module__
|
|
1819
|
+
|
|
1820
|
+
setattr(cls, "__XPMValue__", value_cls)
|
|
1821
|
+
|
|
1822
|
+
return value_cls
|
|
1823
|
+
|
|
1784
1824
|
@classmethod
|
|
1785
1825
|
def __getxpmtype__(cls) -> "ObjectType":
|
|
1786
1826
|
"""Get (and create if necessary) the Object type of this"""
|
|
@@ -1796,27 +1836,32 @@ class Config:
|
|
|
1796
1836
|
raise
|
|
1797
1837
|
return xpmtype
|
|
1798
1838
|
|
|
1799
|
-
def
|
|
1800
|
-
|
|
1801
|
-
|
|
1839
|
+
def __new__(cls: Type[T], *args, **kwargs) -> T:
|
|
1840
|
+
"""Returns an instance of a TypeConfig (for compatibility, use XPMConfig
|
|
1841
|
+
or C if possible)"""
|
|
1802
1842
|
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
"""Allows typing to process easily"""
|
|
1806
|
-
return cls.__new__(cls, **kwargs)
|
|
1843
|
+
# If this is an XPMValue, just return a new instance
|
|
1844
|
+
from experimaestro.core.types import XPMValue
|
|
1807
1845
|
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
and otherwise the real object
|
|
1811
|
-
"""
|
|
1846
|
+
if issubclass(cls, XPMValue):
|
|
1847
|
+
return object.__new__(cls)
|
|
1812
1848
|
|
|
1813
|
-
|
|
1814
|
-
|
|
1849
|
+
# If this is the XPMConfig, just return a new instance
|
|
1850
|
+
# __init__ will be called
|
|
1851
|
+
if issubclass(cls, TypeConfig):
|
|
1815
1852
|
return object.__new__(cls)
|
|
1816
1853
|
|
|
1817
|
-
#
|
|
1818
|
-
o = object.__new__(cls.__getxpmtype__().configtype)
|
|
1819
|
-
|
|
1854
|
+
# otherwise, we use the configuration type
|
|
1855
|
+
o: TypeConfig = object.__new__(cls.__getxpmtype__().configtype)
|
|
1856
|
+
try:
|
|
1857
|
+
o.__init__(*args, **kwargs)
|
|
1858
|
+
except Exception:
|
|
1859
|
+
caller = inspect.getframeinfo(inspect.stack()[1][0])
|
|
1860
|
+
logger.error(
|
|
1861
|
+
"Init error in %s:%s"
|
|
1862
|
+
% (str(Path(caller.filename).absolute()), caller.lineno)
|
|
1863
|
+
)
|
|
1864
|
+
raise
|
|
1820
1865
|
return o
|
|
1821
1866
|
|
|
1822
1867
|
def __validate__(self):
|
|
@@ -27,13 +27,16 @@ from typing import (
|
|
|
27
27
|
Callable,
|
|
28
28
|
ClassVar,
|
|
29
29
|
Dict,
|
|
30
|
+
Generic,
|
|
30
31
|
List,
|
|
31
32
|
Optional,
|
|
32
33
|
Set,
|
|
34
|
+
Type,
|
|
33
35
|
TypeVar,
|
|
34
36
|
Union,
|
|
35
37
|
overload,
|
|
36
38
|
)
|
|
39
|
+
from typing_extensions import Self
|
|
37
40
|
|
|
38
41
|
TConfig = TypeVar("TConfig", bound="Config")
|
|
39
42
|
|
|
@@ -205,12 +208,15 @@ class TypeConfig:
|
|
|
205
208
|
class Config:
|
|
206
209
|
__xpmtype__: ClassVar[ObjectType]
|
|
207
210
|
__xpm__: ConfigInformation
|
|
211
|
+
__use_xpmobject__: ClassVar[bool]
|
|
212
|
+
|
|
213
|
+
XPMValue: Type[Self]
|
|
214
|
+
XPMConfig: Union[Type[Self], Type[TypeConfig[Self]]]
|
|
215
|
+
C: Union[Type[Self], Type[TypeConfig[Self]]]
|
|
216
|
+
|
|
208
217
|
@classmethod
|
|
209
218
|
def __getxpmtype__(cls) -> ObjectType: ...
|
|
210
|
-
def
|
|
211
|
-
@classmethod
|
|
212
|
-
def c(cls, **kwargs) -> T: ...
|
|
213
|
-
def __new__(cls, *args, __xpmobject__: bool = ..., **kwargs) -> T: ...
|
|
219
|
+
def __new__(cls, *args, **kwargs) -> Self: ...
|
|
214
220
|
def __validate__(self) -> None: ...
|
|
215
221
|
def __post_init__(self) -> None: ...
|
|
216
222
|
def __json__(self): ...
|
|
@@ -240,6 +246,6 @@ class Task(LightweightTask):
|
|
|
240
246
|
def copyconfig(config_or_output: TConfig, **kwargs) -> TConfig: ...
|
|
241
247
|
def setmeta(config: TConfig, flag: bool) -> TConfig: ...
|
|
242
248
|
|
|
243
|
-
class TypeConfig:
|
|
249
|
+
class TypeConfig(Generic[T]):
|
|
244
250
|
def __validate__(self):
|
|
245
251
|
pass
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
import inspect
|
|
3
3
|
import sys
|
|
4
|
-
from typing import Set, Union, Dict, Iterator, List
|
|
4
|
+
from typing import Set, Union, Dict, Iterator, List, get_args, get_origin
|
|
5
5
|
from collections import ChainMap
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
import typing
|
|
@@ -126,6 +126,10 @@ class Type:
|
|
|
126
126
|
if t:
|
|
127
127
|
return DictType(Type.fromType(t[0]), Type.fromType(t[1]))
|
|
128
128
|
|
|
129
|
+
# Takes care of generics
|
|
130
|
+
if get_origin(key):
|
|
131
|
+
return GenericType(key)
|
|
132
|
+
|
|
129
133
|
raise Exception("No type found for %s", key)
|
|
130
134
|
|
|
131
135
|
|
|
@@ -179,6 +183,12 @@ class SubmitHook(ABC):
|
|
|
179
183
|
return hash((self.__class__, self.spec()))
|
|
180
184
|
|
|
181
185
|
|
|
186
|
+
class XPMValue:
|
|
187
|
+
"""Jut marks a XPMValue"""
|
|
188
|
+
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
|
|
182
192
|
class ObjectType(Type):
|
|
183
193
|
submit_hooks: Set[SubmitHook]
|
|
184
194
|
"""Hooks associated with this configuration"""
|
|
@@ -211,6 +221,8 @@ class ObjectType(Type):
|
|
|
211
221
|
# Get the identifier
|
|
212
222
|
if identifier is None and hasattr(tp, "__xpmid__"):
|
|
213
223
|
__xpmid__ = getattr(tp, "__xpmid__")
|
|
224
|
+
if isinstance(__xpmid__, Identifier):
|
|
225
|
+
identifier = __xpmid__
|
|
214
226
|
if inspect.ismethod(__xpmid__):
|
|
215
227
|
identifier = Identifier(__xpmid__())
|
|
216
228
|
elif "__xpmid__" in tp.__dict__:
|
|
@@ -247,7 +259,7 @@ class ObjectType(Type):
|
|
|
247
259
|
else:
|
|
248
260
|
self.basetype = tp
|
|
249
261
|
|
|
250
|
-
# Create the type-specific configuration class
|
|
262
|
+
# --- Create the type-specific configuration class (XPMConfig)
|
|
251
263
|
__configbases__ = tuple(
|
|
252
264
|
s.__getxpmtype__().configtype
|
|
253
265
|
for s in tp.__bases__
|
|
@@ -256,23 +268,18 @@ class ObjectType(Type):
|
|
|
256
268
|
|
|
257
269
|
*tp_qual, tp_name = self.basetype.__qualname__.split(".")
|
|
258
270
|
self.configtype = type(
|
|
259
|
-
f"{tp_name}
|
|
271
|
+
f"{tp_name}.XPMConfig", __configbases__ + (self.basetype,), {}
|
|
260
272
|
)
|
|
261
273
|
self.configtype.__qualname__ = ".".join(tp_qual + [self.configtype.__name__])
|
|
262
274
|
self.configtype.__module__ = tp.__module__
|
|
263
275
|
|
|
264
|
-
# Create the type-specific object class
|
|
265
|
-
# (now, the same as basetype - but in the future, remove references)
|
|
266
|
-
self.objecttype = self.basetype # type: type
|
|
267
|
-
self.basetype._ = self.configtype
|
|
268
|
-
|
|
269
276
|
# Return type is used by tasks to change the output
|
|
270
277
|
if hasattr(self.basetype, "task_outputs") or False:
|
|
271
278
|
self.returntype = get_type_hints(
|
|
272
279
|
getattr(self.basetype, "task_outputs")
|
|
273
280
|
).get("return", typing.Any)
|
|
274
281
|
else:
|
|
275
|
-
self.returntype = self.
|
|
282
|
+
self.returntype = self.basetype
|
|
276
283
|
|
|
277
284
|
# Registers ourselves
|
|
278
285
|
self.basetype.__xpmtype__ = self
|
|
@@ -284,6 +291,11 @@ class ObjectType(Type):
|
|
|
284
291
|
self.annotations = []
|
|
285
292
|
self._deprecated = False
|
|
286
293
|
|
|
294
|
+
@property
|
|
295
|
+
def objecttype(self):
|
|
296
|
+
"""Returns the object type"""
|
|
297
|
+
return self.basetype.XPMValue
|
|
298
|
+
|
|
287
299
|
def addAnnotation(self, annotation):
|
|
288
300
|
assert not self.__initialized__
|
|
289
301
|
self.annotations.append(annotation)
|
|
@@ -636,3 +648,31 @@ class DictType(Type):
|
|
|
636
648
|
|
|
637
649
|
def __repr__(self):
|
|
638
650
|
return str(self)
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
class GenericType(Type):
|
|
654
|
+
def __init__(self, type: typing.Type):
|
|
655
|
+
self.type = type
|
|
656
|
+
self.origin = get_origin(type)
|
|
657
|
+
|
|
658
|
+
self.args = get_args(type)
|
|
659
|
+
|
|
660
|
+
def name(self):
|
|
661
|
+
return str(self.type)
|
|
662
|
+
|
|
663
|
+
def __repr__(self):
|
|
664
|
+
return repr(self.type)
|
|
665
|
+
|
|
666
|
+
def validate(self, value):
|
|
667
|
+
# Now, let's check generics...
|
|
668
|
+
mros = typingutils.generic_mro(type(value))
|
|
669
|
+
matching = next(
|
|
670
|
+
(mro for mro in mros if (get_origin(mro) or mro) is self.origin), None
|
|
671
|
+
)
|
|
672
|
+
target = get_origin(self.type) or self.type
|
|
673
|
+
if matching is None:
|
|
674
|
+
raise ValueError(
|
|
675
|
+
f"{type(value)} is not of type {target} ({type(value).__mro__})"
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
return value
|
|
@@ -10,6 +10,7 @@ import omegaconf
|
|
|
10
10
|
import yaml
|
|
11
11
|
from experimaestro import LauncherRegistry, RunMode, experiment
|
|
12
12
|
from experimaestro.experiments.configuration import ConfigurationBase
|
|
13
|
+
from experimaestro.exceptions import HandledException
|
|
13
14
|
from experimaestro.settings import get_workspace
|
|
14
15
|
from omegaconf import OmegaConf, SCMode
|
|
15
16
|
from termcolor import cprint
|
|
@@ -63,7 +64,7 @@ def load(yaml_file: Path):
|
|
|
63
64
|
if parent := _data.get("parent", None):
|
|
64
65
|
data.extend(load(yaml_file.parent / parent))
|
|
65
66
|
|
|
66
|
-
return data
|
|
67
|
+
return data[::-1]
|
|
67
68
|
|
|
68
69
|
|
|
69
70
|
@click.option("--debug", is_flag=True, help="Print debug information")
|
|
@@ -110,10 +111,16 @@ def load(yaml_file: Path):
|
|
|
110
111
|
help="Working directory - if None, uses the default XPM " "working directory",
|
|
111
112
|
)
|
|
112
113
|
@click.option("--conf", "-c", "extra_conf", type=str, multiple=True)
|
|
114
|
+
@click.option(
|
|
115
|
+
"--pre-yaml", type=str, multiple=True, help="Add YAML file after the main one"
|
|
116
|
+
)
|
|
117
|
+
@click.option(
|
|
118
|
+
"--post-yaml", type=str, multiple=True, help="Add YAML file before the main one"
|
|
119
|
+
)
|
|
113
120
|
@click.argument("args", nargs=-1, type=click.UNPROCESSED)
|
|
114
121
|
@click.argument("yaml_file", metavar="YAML file", type=str)
|
|
115
122
|
@click.command()
|
|
116
|
-
def experiments_cli(
|
|
123
|
+
def experiments_cli( # noqa: C901
|
|
117
124
|
yaml_file: str,
|
|
118
125
|
xp_file: str,
|
|
119
126
|
host: str,
|
|
@@ -123,6 +130,8 @@ def experiments_cli(
|
|
|
123
130
|
env: List[Tuple[str, str]],
|
|
124
131
|
run_mode: RunMode,
|
|
125
132
|
extra_conf: List[str],
|
|
133
|
+
pre_yaml: List[str],
|
|
134
|
+
post_yaml: List[str],
|
|
126
135
|
args: List[str],
|
|
127
136
|
show: bool,
|
|
128
137
|
debug: bool,
|
|
@@ -133,13 +142,17 @@ def experiments_cli(
|
|
|
133
142
|
logging.getLogger("xpm.hash").setLevel(logging.INFO)
|
|
134
143
|
|
|
135
144
|
# --- Loads the YAML
|
|
136
|
-
yamls =
|
|
145
|
+
yamls = []
|
|
146
|
+
for y in pre_yaml:
|
|
147
|
+
yamls.extend(load(Path(y)))
|
|
148
|
+
yamls.extend(load(Path(yaml_file)))
|
|
149
|
+
for y in post_yaml:
|
|
150
|
+
yamls.extend(load(Path(y)))
|
|
137
151
|
|
|
138
152
|
# --- Get the XP file
|
|
139
153
|
if xp_file is None:
|
|
140
|
-
for data in yamls:
|
|
141
|
-
if xp_file := data.get("file"):
|
|
142
|
-
del data["file"]
|
|
154
|
+
for data in yamls[::-1]:
|
|
155
|
+
if xp_file := data.get("file", None):
|
|
143
156
|
break
|
|
144
157
|
|
|
145
158
|
if xp_file is None:
|
|
@@ -201,15 +214,15 @@ def experiments_cli(
|
|
|
201
214
|
cprint(f"Error in configuration:\n\n{e}", "red", file=sys.stderr)
|
|
202
215
|
sys.exit(1)
|
|
203
216
|
|
|
217
|
+
if show:
|
|
218
|
+
print(json.dumps(OmegaConf.to_container(configuration))) # noqa: T201
|
|
219
|
+
sys.exit(0)
|
|
220
|
+
|
|
204
221
|
# Move to an object container
|
|
205
222
|
configuration: schema = OmegaConf.to_container(
|
|
206
223
|
configuration, structured_config_mode=SCMode.INSTANTIATE
|
|
207
224
|
)
|
|
208
225
|
|
|
209
|
-
if show:
|
|
210
|
-
print(json.dumps(OmegaConf.to_container(configuration))) # noqa: T201
|
|
211
|
-
sys.exit(0)
|
|
212
|
-
|
|
213
226
|
# Get the working directory
|
|
214
227
|
if workdir is None or not Path(workdir).is_dir():
|
|
215
228
|
workdir = get_workspace(workdir).path.expanduser().resolve()
|
|
@@ -223,9 +236,13 @@ def experiments_cli(
|
|
|
223
236
|
for key, value in env:
|
|
224
237
|
xp.setenv(key, value)
|
|
225
238
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
239
|
+
try:
|
|
240
|
+
# Run the experiment
|
|
241
|
+
helper.xp = xp
|
|
242
|
+
helper.run(list(args), configuration)
|
|
243
|
+
|
|
244
|
+
# ... and wait
|
|
245
|
+
xp.wait()
|
|
229
246
|
|
|
230
|
-
|
|
231
|
-
|
|
247
|
+
except HandledException:
|
|
248
|
+
sys.exit(1)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from omegaconf import MISSING
|
|
1
2
|
from typing import Optional
|
|
2
3
|
import attr
|
|
3
4
|
|
|
@@ -19,14 +20,28 @@ def configuration(*args, **kwargs):
|
|
|
19
20
|
|
|
20
21
|
@configuration()
|
|
21
22
|
class ConfigurationBase:
|
|
22
|
-
|
|
23
|
-
"""ID of the experiment"""
|
|
23
|
+
"""Base configuration for any experiment"""
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
"""
|
|
25
|
+
id: str = MISSING
|
|
26
|
+
"""ID of the experiment
|
|
27
|
+
|
|
28
|
+
This ID is used by experimaestro when running as the experiment.
|
|
29
|
+
"""
|
|
27
30
|
|
|
28
31
|
file: str = "experiment"
|
|
29
|
-
"""
|
|
32
|
+
"""Relative path of the file containing a run function"""
|
|
30
33
|
|
|
31
34
|
parent: Optional[str] = None
|
|
32
35
|
"""Relative path of a YAML file that should be merged"""
|
|
36
|
+
|
|
37
|
+
title: str = ""
|
|
38
|
+
"""Short description of the experiment"""
|
|
39
|
+
|
|
40
|
+
subtitle: str = ""
|
|
41
|
+
"""Allows to give some more details about the experiment"""
|
|
42
|
+
|
|
43
|
+
paper: str = ""
|
|
44
|
+
"""Source paper for this experiment"""
|
|
45
|
+
|
|
46
|
+
description: str = ""
|
|
47
|
+
"""Description of the experiment"""
|
|
@@ -85,6 +85,10 @@ def load_yaml(loader_cls: Type[Loader], path: Path):
|
|
|
85
85
|
if not path.is_file():
|
|
86
86
|
return None
|
|
87
87
|
|
|
88
|
+
logger.warning(
|
|
89
|
+
"Using YAML file to configure launchers is deprecated. Please remove %s using launchers.py",
|
|
90
|
+
path,
|
|
91
|
+
)
|
|
88
92
|
logger.debug("Loading %s", path)
|
|
89
93
|
with path.open("rt") as fp:
|
|
90
94
|
loader = loader_cls(fp)
|
|
@@ -51,6 +51,13 @@ class CPUSpecification:
|
|
|
51
51
|
def __lt__(self, other: "CPUSpecification"):
|
|
52
52
|
return self.memory < other.memory and self.cores < other.cores
|
|
53
53
|
|
|
54
|
+
def total_memory(self, gpus: int = 0):
|
|
55
|
+
return max(
|
|
56
|
+
self.memory,
|
|
57
|
+
self.mem_per_cpu * self.cores,
|
|
58
|
+
self.cpu_per_gpu * self.mem_per_cpu * gpus,
|
|
59
|
+
)
|
|
60
|
+
|
|
54
61
|
|
|
55
62
|
@define(kw_only=True)
|
|
56
63
|
class HostSpecification:
|