experimaestro 1.11.1__tar.gz → 2.0.0a3__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.11.1 → experimaestro-2.0.0a3}/PKG-INFO +1 -1
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/pyproject.toml +9 -3
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/annotations.py +1 -1
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/cli/__init__.py +10 -11
- experimaestro-2.0.0a3/src/experimaestro/cli/progress.py +269 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/arguments.py +20 -1
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/identifier.py +11 -2
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/objects/config.py +119 -97
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/objects/config_walk.py +3 -1
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/types.py +35 -57
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launcherfinder/registry.py +3 -3
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mkdocs/base.py +6 -8
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/notifications.py +12 -3
- experimaestro-2.0.0a3/src/experimaestro/progress.py +406 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/settings.py +4 -2
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/common.py +2 -2
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/restart.py +1 -1
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_checkers.py +2 -2
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_dependencies.py +12 -12
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_experiment.py +3 -3
- experimaestro-2.0.0a3/src/experimaestro/tests/test_file_progress.py +425 -0
- experimaestro-2.0.0a3/src/experimaestro/tests/test_file_progress_integration.py +477 -0
- experimaestro-2.0.0a3/src/experimaestro/tests/test_generators.py +68 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_identifier.py +90 -81
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_instance.py +16 -9
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_objects.py +9 -32
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_outputs.py +6 -6
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_param.py +14 -14
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_progress.py +4 -4
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_serializers.py +5 -5
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_tags.py +15 -15
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_tasks.py +40 -36
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_tokens.py +8 -6
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_types.py +10 -10
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_validation.py +19 -19
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/token_reschedule.py +1 -1
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/LICENSE +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/README.md +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/__main__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/checkers.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/cli/filter.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/cli/jobs.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/click.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/commandline.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/compat.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/connectors/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/connectors/local.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/connectors/ssh.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/callbacks.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/context.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/objects/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/objects/config_utils.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/objects.pyi +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/serialization.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/serializers.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/core/utils.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/exceptions.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/experiments/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/experiments/cli.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/experiments/configuration.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/generators.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/huggingface.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/ipc.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launcherfinder/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launcherfinder/base.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launcherfinder/parser.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launcherfinder/specs.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launchers/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launchers/direct.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launchers/oar.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launchers/slurm/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/launchers/slurm/base.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/locking.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mkdocs/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mkdocs/annotations.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mkdocs/metaloader.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mkdocs/style.css +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/mypy.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/py.typed +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/rpyc.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/run.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/base.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/dependencies.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/dynamic_outputs.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/services.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/state.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scheduler/workspace.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/scriptbuilder.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/0c35d18bf06992036b69.woff2 +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/1815e00441357e01619e.ttf +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/219aa9140e099e6c72ed.woff2 +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/2463b90d9a316e4e5294.woff2 +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/2582b0e4bcf85eceead0.ttf +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/3a4004a46a653d4b2166.woff +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/3baa5b8f3469222b822d.woff +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/4d73cb90e394b34b7670.woff +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/4ef4218c522f1eb6b5b1.woff2 +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/5d681e2edae8c60630db.woff +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/6f420cf17cc0d7676fad.woff2 +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/89999bdf5d835c012025.woff2 +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/914997e1bdfc990d0897.ttf +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/c210719e60948b211a12.woff2 +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/c380809fd3677d7d6903.woff2 +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/f882956fd323fd322f31.woff +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/favicon.ico +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/index.css +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/index.css.map +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/index.html +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/index.js +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/index.js.map +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/login.html +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/server/data/manifest.json +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/sphinx/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/sphinx/static/experimaestro.css +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/taskglobals.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/conftest.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/connectors/bin/executable.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/connectors/test_local.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/connectors/utils.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/core/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/core/test_generics.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/definitions_types.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/bin/sacct +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/bin/sbatch +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/bin/srun +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/bin/test.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/config_slurm/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/config_slurm/launchers.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/test_local.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/launchers/test_slurm.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/restart_main.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/scripts/notifyandwait.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/scripts/waitforfile.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/task_tokens.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/tasks/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/tasks/all.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/tasks/foreign.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_findlauncher.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_forward.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_snippets.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/test_ssh.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tests/utils.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tokens.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tools/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tools/diff.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tools/documentation.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/tools/jobs.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/typingutils.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/__init__.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/asyncio.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/jobs.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/jupyter.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/multiprocessing.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/resources.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/utils/settings.py +0 -0
- {experimaestro-1.11.1 → experimaestro-2.0.0a3}/src/experimaestro/xpmutils.py +0 -0
|
@@ -48,7 +48,7 @@ dependencies = [
|
|
|
48
48
|
"typing-extensions >=4.2; python_version < \"3.12\"",
|
|
49
49
|
"watchdog >=2"
|
|
50
50
|
]
|
|
51
|
-
version = "
|
|
51
|
+
version = "2.0.0-a3"
|
|
52
52
|
|
|
53
53
|
[tool.poetry-dynamic-versioning]
|
|
54
54
|
enable = false
|
|
@@ -56,7 +56,7 @@ vcs = "git"
|
|
|
56
56
|
style = "semver"
|
|
57
57
|
dirty = true
|
|
58
58
|
format-jinja = """{%- set pre = [] -%}{%- set metadata = [] -%}
|
|
59
|
-
{%- if revision is not none -%}{{ pre.append(
|
|
59
|
+
{%- if revision is not none -%}{{ pre.append(stage|string + revision|string) or "" }}{%- endif -%}
|
|
60
60
|
{%- if distance > 0 -%}{{ metadata.append(distance|string) or "" }}{%- endif -%}
|
|
61
61
|
{{ serialize_semver(base, pre, metadata)}}"""
|
|
62
62
|
|
|
@@ -83,6 +83,12 @@ poetry-dynamic-versioning = { version = ">=1.0.0,<2.0.0", extras = ["plugin"] }
|
|
|
83
83
|
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
|
|
84
84
|
build-backend = "poetry_dynamic_versioning.backend"
|
|
85
85
|
|
|
86
|
+
[dependency-groups]
|
|
87
|
+
dev = [
|
|
88
|
+
"pytest>=8.4.1",
|
|
89
|
+
"pytest-timeout>=2.4.0",
|
|
90
|
+
]
|
|
91
|
+
|
|
86
92
|
[tool.poetry.group.ssh]
|
|
87
93
|
optional = true
|
|
88
94
|
|
|
@@ -140,7 +146,7 @@ warn_unused_ignores = true
|
|
|
140
146
|
|
|
141
147
|
[tool.commitizen]
|
|
142
148
|
name = "cz_conventional_commits"
|
|
143
|
-
version = "
|
|
149
|
+
version = "2.0.0a3"
|
|
144
150
|
changelog_start_rev = "v1.0.0"
|
|
145
151
|
tag_format = "v$major.$minor.$patch$prerelease"
|
|
146
152
|
# update_changelog_on_bump = true
|
|
@@ -71,7 +71,7 @@ class config:
|
|
|
71
71
|
assert inspect.isclass(tp), f"{tp} is not a class"
|
|
72
72
|
|
|
73
73
|
# Adds to xpminfo for on demand creation of information
|
|
74
|
-
return ObjectType(tp, identifier=self.identifier).
|
|
74
|
+
return ObjectType(tp, identifier=self.identifier).valuetype
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
class Array(TypeProxy):
|
|
@@ -20,16 +20,6 @@ from experimaestro.settings import find_workspace
|
|
|
20
20
|
logging.basicConfig(level=logging.INFO)
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def pass_cfg(f):
|
|
24
|
-
"""Pass configuration information"""
|
|
25
|
-
|
|
26
|
-
@click.pass_context
|
|
27
|
-
def new_func(ctx, *args, **kwargs):
|
|
28
|
-
return ctx.invoke(f, ctx.obj, *args, **kwargs)
|
|
29
|
-
|
|
30
|
-
return update_wrapper(new_func, f)
|
|
31
|
-
|
|
32
|
-
|
|
33
23
|
def check_xp_path(ctx, self, path: Path):
|
|
34
24
|
if not (path / ".__experimaestro__").is_file():
|
|
35
25
|
cprint(f"{path} is not an experimaestro working directory", "red")
|
|
@@ -142,7 +132,6 @@ def diff(path: Path):
|
|
|
142
132
|
"""Show the reason of the identifier change for a job"""
|
|
143
133
|
from experimaestro.tools.jobs import load_job
|
|
144
134
|
from experimaestro import Config
|
|
145
|
-
from experimaestro.core.objects import ConfigWalkContext
|
|
146
135
|
|
|
147
136
|
_, job = load_job(path / "params.json", discard_id=False)
|
|
148
137
|
_, new_job = load_job(path / "params.json")
|
|
@@ -290,6 +279,16 @@ cli.add_command(Launchers("launchers", help="Launcher specific commands"))
|
|
|
290
279
|
cli.add_command(Launchers("connectors", help="Connector specific commands"))
|
|
291
280
|
cli.add_command(Launchers("tokens", help="Token specific commands"))
|
|
292
281
|
|
|
282
|
+
# Import and add progress commands
|
|
283
|
+
from .progress import progress as progress_cli
|
|
284
|
+
|
|
285
|
+
cli.add_command(progress_cli)
|
|
286
|
+
|
|
287
|
+
# Import and add jobs commands
|
|
288
|
+
from .jobs import jobs as jobs_cli
|
|
289
|
+
|
|
290
|
+
cli.add_command(jobs_cli)
|
|
291
|
+
|
|
293
292
|
|
|
294
293
|
@cli.group()
|
|
295
294
|
@click.option("--workdir", type=Path, default=None)
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""Simplified CLI commands for managing and viewing progress files"""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional, Dict
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from termcolor import colored
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from tqdm import tqdm
|
|
13
|
+
|
|
14
|
+
TQDM_AVAILABLE = True
|
|
15
|
+
except ImportError:
|
|
16
|
+
TQDM_AVAILABLE = False
|
|
17
|
+
|
|
18
|
+
from experimaestro.progress import ProgressEntry, ProgressFileReader
|
|
19
|
+
from experimaestro.settings import find_workspace
|
|
20
|
+
from . import cli
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@click.option("--workspace", default="", help="Experimaestro workspace")
|
|
24
|
+
@click.option("--workdir", type=Path, default=None)
|
|
25
|
+
@cli.group()
|
|
26
|
+
@click.pass_context
|
|
27
|
+
def progress(
|
|
28
|
+
ctx,
|
|
29
|
+
workdir: Optional[Path],
|
|
30
|
+
workspace: Optional[str],
|
|
31
|
+
):
|
|
32
|
+
"""Progress tracking commands"""
|
|
33
|
+
ctx.obj.workspace = find_workspace(workdir=workdir, workspace=workspace)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def format_timestamp(timestamp: float) -> str:
|
|
37
|
+
"""Format timestamp for display"""
|
|
38
|
+
dt = datetime.fromtimestamp(timestamp)
|
|
39
|
+
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@click.argument("jobid", type=str)
|
|
43
|
+
@progress.command()
|
|
44
|
+
@click.pass_context
|
|
45
|
+
def show(ctx, jobid: str):
|
|
46
|
+
"""Show current progress state (default command)
|
|
47
|
+
|
|
48
|
+
JOBID format: task_name/task_hash
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
task_name, task_hash = jobid.split("/")
|
|
52
|
+
except ValueError:
|
|
53
|
+
raise click.ClickException("JOBID must be in format task_name/task_hash")
|
|
54
|
+
|
|
55
|
+
workspace = ctx.obj.workspace
|
|
56
|
+
task_path = workspace.path / "jobs" / task_name / task_hash
|
|
57
|
+
|
|
58
|
+
if not task_path.exists():
|
|
59
|
+
raise click.ClickException(f"Job directory not found: {task_path}")
|
|
60
|
+
|
|
61
|
+
reader = ProgressFileReader(task_path)
|
|
62
|
+
current_progress = reader.get_current_progress()
|
|
63
|
+
|
|
64
|
+
if not current_progress:
|
|
65
|
+
click.echo("No progress information available")
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# Filter out EOJ markers
|
|
69
|
+
current_progress = {k: v for k, v in current_progress.items() if k != -1}
|
|
70
|
+
|
|
71
|
+
if not current_progress:
|
|
72
|
+
click.echo("No progress information available")
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
click.echo(f"Progress for job {jobid}")
|
|
76
|
+
click.echo("=" * 80)
|
|
77
|
+
|
|
78
|
+
# Show simple text-based progress for each level
|
|
79
|
+
for level in sorted(current_progress.keys()):
|
|
80
|
+
entry = current_progress[level]
|
|
81
|
+
indent = " " * level
|
|
82
|
+
progress_pct = f"{entry.progress * 100:5.1f}%"
|
|
83
|
+
desc = entry.desc or f"Level {level}"
|
|
84
|
+
timestamp = format_timestamp(entry.timestamp)
|
|
85
|
+
|
|
86
|
+
color = "green" if entry.progress >= 1.0 else "yellow"
|
|
87
|
+
click.echo(colored(f"{indent}L{level}: {progress_pct} - {desc}", color))
|
|
88
|
+
click.echo(colored(f"{indent} Last updated: {timestamp}", "cyan"))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def create_progress_bar(
|
|
92
|
+
level: int,
|
|
93
|
+
desc: str,
|
|
94
|
+
progress: float = 0.0,
|
|
95
|
+
) -> tqdm:
|
|
96
|
+
"""Create a properly aligned progress bar like dashboard style"""
|
|
97
|
+
if level > 0:
|
|
98
|
+
indent = " " * (level - 1) + "└─ "
|
|
99
|
+
else:
|
|
100
|
+
indent = ""
|
|
101
|
+
label = f"{indent}L{level}"
|
|
102
|
+
|
|
103
|
+
colors = ["blue", "yellow", "magenta", "cyan", "white"]
|
|
104
|
+
bar_color = colors[level % len(colors)]
|
|
105
|
+
|
|
106
|
+
unit = desc[:50] if desc else f"Level {level}"
|
|
107
|
+
ncols = 100
|
|
108
|
+
wbar = 50
|
|
109
|
+
|
|
110
|
+
return tqdm(
|
|
111
|
+
total=100,
|
|
112
|
+
desc=label,
|
|
113
|
+
position=level,
|
|
114
|
+
leave=True,
|
|
115
|
+
bar_format=f"{{desc}}: {{percentage:3.0f}}%|{{bar:{wbar - len(indent)}}}| {{unit}}", # noqa: F541
|
|
116
|
+
ncols=ncols, # Adjust width based on level
|
|
117
|
+
unit=unit,
|
|
118
|
+
colour=bar_color,
|
|
119
|
+
initial=progress * 100,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _update_progress_display(
|
|
124
|
+
reader: ProgressFileReader, progress_bars: Dict[int, tqdm]
|
|
125
|
+
) -> bool:
|
|
126
|
+
"""Update the tqdm progress bars in dashboard style"""
|
|
127
|
+
current_state: Dict[int, ProgressEntry] = {
|
|
128
|
+
k: v for k, v in reader.get_current_state().items() if k != -1
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if not current_state:
|
|
132
|
+
click.echo("No progress information available yet...")
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
# Update existing bars and create new ones
|
|
136
|
+
for _level, entry in current_state.items():
|
|
137
|
+
progress_val = entry.progress * 100
|
|
138
|
+
desc = entry.desc or f"Level {entry.level}"
|
|
139
|
+
|
|
140
|
+
if entry.level not in progress_bars:
|
|
141
|
+
progress_bars[entry.level] = create_progress_bar(
|
|
142
|
+
entry.level, desc, progress_val
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
bar = progress_bars[entry.level]
|
|
146
|
+
bar.unit = desc[:50]
|
|
147
|
+
bar.n = progress_val
|
|
148
|
+
|
|
149
|
+
bar.refresh()
|
|
150
|
+
|
|
151
|
+
# Remove bars for levels that no longer exist
|
|
152
|
+
levels_to_remove = set(progress_bars.keys()) - set(current_state.keys())
|
|
153
|
+
for level in levels_to_remove:
|
|
154
|
+
progress_bars[level].close()
|
|
155
|
+
del progress_bars[level]
|
|
156
|
+
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@click.argument("jobid", type=str)
|
|
161
|
+
@click.option("--refresh-rate", "-r", default=0.5, help="Refresh rate in seconds")
|
|
162
|
+
@progress.command()
|
|
163
|
+
@click.pass_context
|
|
164
|
+
def live(ctx, jobid: str, refresh_rate: float):
|
|
165
|
+
"""Show live progress with tqdm-style bars
|
|
166
|
+
|
|
167
|
+
JOBID format: task_name/task_hash
|
|
168
|
+
"""
|
|
169
|
+
if not TQDM_AVAILABLE:
|
|
170
|
+
click.echo("tqdm is not available. Install with: pip install tqdm")
|
|
171
|
+
click.echo("Falling back to basic display...")
|
|
172
|
+
ctx.invoke(show, jobid=jobid)
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
task_name, task_hash = jobid.split("/")
|
|
177
|
+
except ValueError:
|
|
178
|
+
raise click.ClickException("JOBID must be in format task_name/task_hash")
|
|
179
|
+
|
|
180
|
+
workspace = ctx.obj.workspace
|
|
181
|
+
task_path = workspace.path / "jobs" / task_name / task_hash
|
|
182
|
+
|
|
183
|
+
if not task_path.exists():
|
|
184
|
+
raise click.ClickException(f"Job directory not found: {task_path}")
|
|
185
|
+
|
|
186
|
+
reader = ProgressFileReader(task_path)
|
|
187
|
+
progress_bars: Dict[int, tqdm] = {}
|
|
188
|
+
|
|
189
|
+
def cleanup_bars():
|
|
190
|
+
"""Clean up all progress bars"""
|
|
191
|
+
for bar in progress_bars.values():
|
|
192
|
+
bar.close()
|
|
193
|
+
progress_bars.clear()
|
|
194
|
+
|
|
195
|
+
click.echo(f"Live progress for job {jobid}")
|
|
196
|
+
click.echo("Press Ctrl+C to stop")
|
|
197
|
+
click.echo("=" * 80)
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
if not _update_progress_display(reader, progress_bars):
|
|
201
|
+
click.echo("No progress information available yet...")
|
|
202
|
+
|
|
203
|
+
while True:
|
|
204
|
+
time.sleep(refresh_rate)
|
|
205
|
+
|
|
206
|
+
if not _update_progress_display(reader, progress_bars):
|
|
207
|
+
# Check if job is complete
|
|
208
|
+
if reader.is_done():
|
|
209
|
+
click.echo("\nJob completed!")
|
|
210
|
+
break
|
|
211
|
+
|
|
212
|
+
# Check if all progress bars are at 100%
|
|
213
|
+
if progress_bars and all(bar.n >= 100 for bar in progress_bars.values()):
|
|
214
|
+
cleanup_bars()
|
|
215
|
+
click.echo("\nAll progress completed!")
|
|
216
|
+
break
|
|
217
|
+
|
|
218
|
+
except KeyboardInterrupt:
|
|
219
|
+
click.echo("\nStopped monitoring progress")
|
|
220
|
+
finally:
|
|
221
|
+
cleanup_bars()
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@progress.command(name="list")
|
|
225
|
+
@click.pass_context
|
|
226
|
+
def list_jobs(ctx):
|
|
227
|
+
"""List all jobs with progress information"""
|
|
228
|
+
ws = ctx.obj.workspace
|
|
229
|
+
jobs_path = ws.path / "jobs"
|
|
230
|
+
|
|
231
|
+
if not jobs_path.exists():
|
|
232
|
+
click.echo("No jobs directory found")
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
for task_dir in jobs_path.iterdir():
|
|
236
|
+
if not task_dir.is_dir():
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
for job_dir in task_dir.iterdir():
|
|
240
|
+
if not job_dir.is_dir():
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
progress_dir = job_dir / ".experimaestro"
|
|
244
|
+
if not progress_dir.exists():
|
|
245
|
+
continue
|
|
246
|
+
|
|
247
|
+
# Check if there are progress files
|
|
248
|
+
progress_files = list(progress_dir.glob("progress-*.jsonl"))
|
|
249
|
+
if not progress_files:
|
|
250
|
+
continue
|
|
251
|
+
|
|
252
|
+
job_id = f"{task_dir.name}/{job_dir.name}"
|
|
253
|
+
reader = ProgressFileReader(job_dir)
|
|
254
|
+
current_state = reader.get_current_state()
|
|
255
|
+
|
|
256
|
+
# if current_progress:
|
|
257
|
+
if current_state:
|
|
258
|
+
# Get overall progress (level 0)
|
|
259
|
+
level_0 = current_state.get(0)
|
|
260
|
+
if level_0:
|
|
261
|
+
color = "green" if level_0.progress >= 1.0 else "yellow"
|
|
262
|
+
desc = f"{level_0.desc}" if level_0.desc else ""
|
|
263
|
+
progress_pct = f"{level_0.progress * 100:5.1f}%"
|
|
264
|
+
click.echo(colored(f"{job_id:50} - {progress_pct} - {desc}", color))
|
|
265
|
+
|
|
266
|
+
else:
|
|
267
|
+
click.echo(f"{job_id:50} No level 0 progress")
|
|
268
|
+
else:
|
|
269
|
+
click.echo(f"{job_id:50} No progress data")
|
|
@@ -80,10 +80,13 @@ class Argument:
|
|
|
80
80
|
|
|
81
81
|
self.generator = generator
|
|
82
82
|
self.default = None
|
|
83
|
+
self.ignore_generated = False
|
|
83
84
|
|
|
84
85
|
if default is not None:
|
|
85
86
|
assert self.generator is None, "generator and default are exclusive options"
|
|
86
87
|
if isinstance(default, field):
|
|
88
|
+
self.ignore_generated = default.ignore_generated
|
|
89
|
+
|
|
87
90
|
if default.default is not None:
|
|
88
91
|
self.default = default.default
|
|
89
92
|
elif default.default_factory is not None:
|
|
@@ -184,13 +187,29 @@ DataPath = Annotated[Path, dataHint]
|
|
|
184
187
|
class field:
|
|
185
188
|
"""Extra information for a given experimaestro field (param or meta)"""
|
|
186
189
|
|
|
187
|
-
def __init__(
|
|
190
|
+
def __init__(
|
|
191
|
+
self,
|
|
192
|
+
*,
|
|
193
|
+
default: Any = None,
|
|
194
|
+
default_factory: Callable = None,
|
|
195
|
+
ignore_generated=False,
|
|
196
|
+
):
|
|
197
|
+
"""Gives some extra per-field information
|
|
198
|
+
|
|
199
|
+
:param default: a default value, defaults to None
|
|
200
|
+
:param default_factory: a default factory for values, defaults to None
|
|
201
|
+
:param ignore_generated: True if the value is hidden – it won't be accessible in
|
|
202
|
+
tasks, defaults to False. The interest of hidden is to add a
|
|
203
|
+
configuration field that changes the identifier, but will not be
|
|
204
|
+
used.
|
|
205
|
+
"""
|
|
188
206
|
assert not (
|
|
189
207
|
(default is not None) and (default_factory is not None)
|
|
190
208
|
), "default and default_factory are mutually exclusive options"
|
|
191
209
|
|
|
192
210
|
self.default_factory = default_factory
|
|
193
211
|
self.default = default
|
|
212
|
+
self.ignore_generated = ignore_generated
|
|
194
213
|
|
|
195
214
|
|
|
196
215
|
class help(TypeAnnotation):
|
|
@@ -170,7 +170,7 @@ class IdentifierComputer:
|
|
|
170
170
|
self._hashupdate(IdentifierComputer.ENUM_ID)
|
|
171
171
|
k = value.__class__
|
|
172
172
|
self._hashupdate(
|
|
173
|
-
f"{k.__module__}.{k.__qualname__
|
|
173
|
+
f"{k.__module__}.{k.__qualname__}:{value.name}".encode("utf-8"),
|
|
174
174
|
)
|
|
175
175
|
elif isinstance(value, dict):
|
|
176
176
|
self._hashupdate(IdentifierComputer.DICT_ID)
|
|
@@ -235,7 +235,16 @@ class IdentifierComputer:
|
|
|
235
235
|
# Skip if the argument is not a constant, and
|
|
236
236
|
# - optional argument: both value and default are None
|
|
237
237
|
# - the argument value is equal to the default value
|
|
238
|
-
|
|
238
|
+
try:
|
|
239
|
+
argvalue = getattr(value, argument.name, None)
|
|
240
|
+
except KeyError:
|
|
241
|
+
logging.warning(
|
|
242
|
+
"Parameter %s has not been set in %s created at %s",
|
|
243
|
+
argument.name,
|
|
244
|
+
value,
|
|
245
|
+
value.__xpm__._initinfo,
|
|
246
|
+
)
|
|
247
|
+
raise
|
|
239
248
|
if not argument.constant and (
|
|
240
249
|
(
|
|
241
250
|
not argument.required
|