experimaestro 0.22.0__zip → 0.24.0__zip
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-0.22.0 → experimaestro-0.24.0}/CHANGELOG.md +21 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/PKG-INFO +22 -1
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/experiments/task.md +45 -7
- {experimaestro-0.22.0 → experimaestro-0.24.0}/pyproject.toml +1 -1
- {experimaestro-0.22.0 → experimaestro-0.24.0}/requirements.txt +6 -3
- {experimaestro-0.22.0 → experimaestro-0.24.0}/setup.cfg +1 -1
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/__init__.py +7 -5
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/__main__.py +3 -3
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/commandline.py +0 -8
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/objects.py +218 -164
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/objects.pyi +25 -11
- experimaestro-0.24.0/src/experimaestro/core/serializers.py +52 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/types.py +44 -2
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/generators.py +7 -6
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/huggingface.py +2 -2
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launchers/__init__.py +19 -7
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/base.py +21 -3
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/server/__init__.py +10 -2
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_identifier.py +33 -6
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_instance.py +18 -15
- experimaestro-0.24.0/src/experimaestro/tests/test_outputs.py +50 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_progress.py +7 -9
- experimaestro-0.24.0/src/experimaestro/tests/test_serializers.py +54 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/jobs.py +2 -2
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/version.py +2 -2
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/xpmutils.py +3 -3
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/PKG-INFO +22 -1
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/SOURCES.txt +2 -1
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/requires.txt +6 -2
- experimaestro-0.22.0/src/experimaestro/tests/test_outputs.py +0 -88
- experimaestro-0.22.0/src/experimaestro/tests/test_serialization.py +0 -45
- {experimaestro-0.22.0 → experimaestro-0.24.0}/.circleci/config.yml +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/.flake8 +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/.github/release.yaml +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/.github/workflows/pytest.yml +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/.github/workflows/python-publish.yml +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/.gitignore +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/.gitmodules +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/.pre-commit-config.yaml +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/.prettierignore +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/.readthedocs.yml +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/LICENSE +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/MANIFEST.in +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/README.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/.gitignore +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/.nolluprc.js +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/CHANGELOG.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/README.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/package-lock.json +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/package.json +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/postcss.config.js +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/public/favicon.ico +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/public/index.html +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/public/login.html +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/public/manifest.json +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/App.tsx +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/Experiments.tsx +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/Services.tsx +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/TaskDetail.tsx +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/TaskJobs.tsx +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/Tasks.tsx +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/client.ts +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/clipboard.ts +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/index.css +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/index.tsx +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/logo.png +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/logo.pxm +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/reducers.ts +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/store.ts +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/theme/_jobs.scss +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/theme/theme.scss +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/src/ui/messages.tsx +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/tsconfig.json +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/webpack.config.ts +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/app/xp/run.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/changelog.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/cli.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/concepts.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/configuration.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/connectors/index.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/documenting.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/experiments/config.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/experiments/overview.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/experiments/plan.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/faq.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/index.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/jupyter.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/launchers/index.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/requirements.txt +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/serialization.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/docs/tutorial.md +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/mkdocs.yml +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/pytest.ini +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/scripts/longtask.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/setup.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/annotations.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/checkers.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/click.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/compat.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/connectors/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/connectors/local.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/connectors/ssh.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/arguments.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/core/context.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/filter.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/ipc.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launcherfinder/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launcherfinder/base.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launcherfinder/parser.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launcherfinder/registry.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launcherfinder/specs.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launchers/direct.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launchers/oar.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/launchers/slurm.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/locking.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mkdocs/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mkdocs/annotations.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mkdocs/base.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mkdocs/metaloader.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mkdocs/style.css +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/mypy.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/notifications.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/rpyc.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/run.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/dependencies.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/environment.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/services.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scheduler/workspace.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/scriptbuilder.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/settings.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/sphinx/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/sphinx/static/experimaestro.css +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/taskglobals.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/conftest.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/connectors/bin/executable.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/connectors/test_local.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/connectors/utils.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/definitions_types.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/bin/sacct +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/bin/sbatch +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/bin/test.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/common.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/config_slurm/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/config_slurm/launchers.yaml +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/test_local.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/launchers/test_slurm.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/restart.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/restart_main.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/scripts/notifyandwait.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/scripts/waitforfile.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/task_tokens.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/tasks/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/tasks/all.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/tasks/foreign.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/tasks/subparams.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_checkers.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_findlauncher.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_forward.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_objects.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_param.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_snippets.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_ssh.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_tags.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_tasks.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_tokens.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_types.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/test_validation.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/token_reschedule.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tests/utils.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tokens.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tools/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tools/diff.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/tools/jobs.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/typingutils.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/__init__.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/asyncio.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/jupyter.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/resources.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/settings.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro/utils/yaml.py +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/dependency_links.txt +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/entry_points.txt +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/not-zip-safe +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/src/experimaestro.egg-info/top_level.txt +0 -0
- {experimaestro-0.22.0 → experimaestro-0.24.0}/tox.ini +0 -0
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from functools import cached_property
|
|
4
4
|
import json
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from types import NoneType
|
|
8
|
+
except Exception:
|
|
9
|
+
# compatibility: python-3.8
|
|
10
|
+
NoneType = type(None)
|
|
5
11
|
from termcolor import cprint
|
|
6
12
|
import os
|
|
7
13
|
from pathlib import Path
|
|
@@ -14,12 +20,12 @@ import inspect
|
|
|
14
20
|
import importlib
|
|
15
21
|
from typing import (
|
|
16
22
|
Any,
|
|
17
|
-
Callable,
|
|
18
23
|
ClassVar,
|
|
19
24
|
Dict,
|
|
20
25
|
List,
|
|
21
26
|
Optional,
|
|
22
27
|
Set,
|
|
28
|
+
Tuple,
|
|
23
29
|
Type,
|
|
24
30
|
TypeVar,
|
|
25
31
|
Union,
|
|
@@ -35,6 +41,7 @@ from experimaestro.core.types import DeprecatedAttribute, ObjectType
|
|
|
35
41
|
from .context import SerializationContext, SerializedPath, SerializedPathLoader
|
|
36
42
|
|
|
37
43
|
if TYPE_CHECKING:
|
|
44
|
+
from experimaestro.scheduler.base import Job
|
|
38
45
|
from experimaestro.scheduler.workspace import RunMode
|
|
39
46
|
from experimaestro.launchers import Launcher
|
|
40
47
|
from experimaestro.scheduler import Workspace
|
|
@@ -167,7 +174,7 @@ class HashComputer:
|
|
|
167
174
|
for key, value in items:
|
|
168
175
|
self.update(key, subparam=subparam)
|
|
169
176
|
self.update(value, subparam=subparam)
|
|
170
|
-
elif isinstance(value,
|
|
177
|
+
elif isinstance(value, ConfigWrapper):
|
|
171
178
|
# Add the task ID...
|
|
172
179
|
self.update(value.__xpm__.task, subparam=subparam)
|
|
173
180
|
|
|
@@ -276,8 +283,14 @@ def updatedependencies(
|
|
|
276
283
|
elif isinstance(value, (list, set)):
|
|
277
284
|
for el in value:
|
|
278
285
|
updatedependencies(dependencies, el, path, taskids)
|
|
279
|
-
elif isinstance(value,
|
|
280
|
-
|
|
286
|
+
elif isinstance(value, ConfigWrapper):
|
|
287
|
+
# Add the base value (if any)
|
|
288
|
+
if value.__xpm__.base is not None:
|
|
289
|
+
value.__xpm__.base.__xpm__.updatedependencies(dependencies, path, taskids)
|
|
290
|
+
|
|
291
|
+
# Add the task (if any)
|
|
292
|
+
if value.__xpm__.task is not None:
|
|
293
|
+
dependencies.add(value.__xpm__.task.__xpm__.dependency())
|
|
281
294
|
elif isinstance(value, (dict,)):
|
|
282
295
|
for key, val in value.items():
|
|
283
296
|
updatedependencies(dependencies, key, path, taskids)
|
|
@@ -307,7 +320,7 @@ def add_to_path(p):
|
|
|
307
320
|
sys.path = old_path
|
|
308
321
|
|
|
309
322
|
|
|
310
|
-
class
|
|
323
|
+
class ConfigWalkContext:
|
|
311
324
|
"""Context when generating values in configurations"""
|
|
312
325
|
|
|
313
326
|
@property
|
|
@@ -342,33 +355,38 @@ class GenerationContext:
|
|
|
342
355
|
NOT_SET = object()
|
|
343
356
|
|
|
344
357
|
|
|
345
|
-
class
|
|
358
|
+
class ConfigWalk:
|
|
346
359
|
"""Allows to perform an operation on all nested configurations"""
|
|
347
360
|
|
|
348
|
-
def __init__(self, recurse_task=False):
|
|
361
|
+
def __init__(self, context: ConfigWalkContext = None, recurse_task=False):
|
|
349
362
|
"""
|
|
350
363
|
|
|
351
|
-
|
|
352
|
-
|
|
364
|
+
:param recurse_task: Recurse into linked tasks
|
|
365
|
+
:param context: The context, by default only tracks the position in the
|
|
366
|
+
config tree
|
|
353
367
|
"""
|
|
354
368
|
self.recurse_task = recurse_task
|
|
369
|
+
self.context = ConfigWalkContext() if context is None else context
|
|
355
370
|
|
|
356
371
|
# Stores already visited nodes
|
|
357
372
|
self.visited = {}
|
|
358
373
|
|
|
359
|
-
def preprocess(self, config: "Config"):
|
|
374
|
+
def preprocess(self, config: "Config") -> Tuple[bool, Any]:
|
|
375
|
+
"""Returns a tuple boolean/value
|
|
376
|
+
|
|
377
|
+
The boolean value is used to stop the processing if False.
|
|
378
|
+
The value is returned
|
|
379
|
+
"""
|
|
360
380
|
return True, None
|
|
361
381
|
|
|
362
382
|
def postprocess(self, config: "Config", values: Dict[str, Any]):
|
|
363
383
|
return config
|
|
364
384
|
|
|
365
|
-
@contextmanager
|
|
366
385
|
def list(self, i: int):
|
|
367
|
-
|
|
386
|
+
return self.context.push(str(i))
|
|
368
387
|
|
|
369
|
-
@contextmanager
|
|
370
388
|
def map(self, k: str):
|
|
371
|
-
|
|
389
|
+
return self.context.push(k)
|
|
372
390
|
|
|
373
391
|
def __call__(self, x):
|
|
374
392
|
if isinstance(x, Config):
|
|
@@ -412,9 +430,13 @@ class ConfigProcessing:
|
|
|
412
430
|
result[key] = self(value)
|
|
413
431
|
return result
|
|
414
432
|
|
|
415
|
-
if isinstance(x,
|
|
433
|
+
if isinstance(x, ConfigWrapper):
|
|
416
434
|
# Process task if different
|
|
417
|
-
if
|
|
435
|
+
if (
|
|
436
|
+
x.__xpm__.task is not None
|
|
437
|
+
and self.recurse_task
|
|
438
|
+
and x.__xpm__.task is not x.__unwrap__()
|
|
439
|
+
):
|
|
418
440
|
self(x.__xpm__.task)
|
|
419
441
|
|
|
420
442
|
# Processed the wrapped config
|
|
@@ -423,19 +445,10 @@ class ConfigProcessing:
|
|
|
423
445
|
if isinstance(x, (float, int, str, Path, Enum)):
|
|
424
446
|
return x
|
|
425
447
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
class GenerationConfigProcessing(ConfigProcessing):
|
|
430
|
-
def __init__(self, context: GenerationContext, recurse_task=False):
|
|
431
|
-
super().__init__(recurse_task=recurse_task)
|
|
432
|
-
self.context = context
|
|
433
|
-
|
|
434
|
-
def list(self, i: int):
|
|
435
|
-
return self.context.push(str(i))
|
|
448
|
+
if isinstance(x, Proxy):
|
|
449
|
+
return self(x.__unwrap__())
|
|
436
450
|
|
|
437
|
-
|
|
438
|
-
return self.context.push(k)
|
|
451
|
+
raise NotImplementedError(f"Cannot handle a value of type {type(x)}")
|
|
439
452
|
|
|
440
453
|
|
|
441
454
|
def getqualattr(module, qualname):
|
|
@@ -453,7 +466,7 @@ class ConfigInformation:
|
|
|
453
466
|
"""Forces this configuration to be a meta-parameter"""
|
|
454
467
|
|
|
455
468
|
# Set to true when loading from JSON
|
|
456
|
-
LOADING = False
|
|
469
|
+
LOADING: ClassVar[bool] = False
|
|
457
470
|
|
|
458
471
|
def __init__(self, pyobject: "TypeConfig"):
|
|
459
472
|
# The underlying pyobject and XPM type
|
|
@@ -464,6 +477,7 @@ class ConfigInformation:
|
|
|
464
477
|
# Meta-informations
|
|
465
478
|
self._tags = {}
|
|
466
479
|
self._initinfo = ""
|
|
480
|
+
self.submit_hooks = set()
|
|
467
481
|
|
|
468
482
|
# Generated task
|
|
469
483
|
self._taskoutput = None
|
|
@@ -481,6 +495,7 @@ class ConfigInformation:
|
|
|
481
495
|
self._meta = None
|
|
482
496
|
|
|
483
497
|
def set_meta(self, value: Optional[bool]):
|
|
498
|
+
"""Sets the meta flag"""
|
|
484
499
|
assert not self._sealed, "Configuration is sealed"
|
|
485
500
|
self._meta = value
|
|
486
501
|
|
|
@@ -534,7 +549,7 @@ class ConfigInformation:
|
|
|
534
549
|
yield argument, self.values[argument.name]
|
|
535
550
|
|
|
536
551
|
def tags(self):
|
|
537
|
-
class TagFinder(
|
|
552
|
+
class TagFinder(ConfigWalk):
|
|
538
553
|
def __init__(self):
|
|
539
554
|
super().__init__(recurse_task=True)
|
|
540
555
|
self.tags = {}
|
|
@@ -578,7 +593,7 @@ class ConfigInformation:
|
|
|
578
593
|
)
|
|
579
594
|
raise
|
|
580
595
|
|
|
581
|
-
def seal(self, context:
|
|
596
|
+
def seal(self, context: ConfigWalkContext):
|
|
582
597
|
"""Seal the object, generating values when needed,
|
|
583
598
|
before scheduling the associated job(s)
|
|
584
599
|
|
|
@@ -586,7 +601,7 @@ class ConfigInformation:
|
|
|
586
601
|
- context: the generation context
|
|
587
602
|
"""
|
|
588
603
|
|
|
589
|
-
class Sealer(
|
|
604
|
+
class Sealer(ConfigWalk):
|
|
590
605
|
def preprocess(self, config: Config):
|
|
591
606
|
return not config.__xpm__._sealed, config
|
|
592
607
|
|
|
@@ -607,9 +622,9 @@ class ConfigInformation:
|
|
|
607
622
|
|
|
608
623
|
Internal API - do not use
|
|
609
624
|
"""
|
|
610
|
-
context =
|
|
625
|
+
context = ConfigWalkContext()
|
|
611
626
|
|
|
612
|
-
class Unsealer(
|
|
627
|
+
class Unsealer(ConfigWalk):
|
|
613
628
|
def preprocess(self, config: Config):
|
|
614
629
|
return config.__xpm__._sealed, config
|
|
615
630
|
|
|
@@ -658,13 +673,35 @@ class ConfigInformation:
|
|
|
658
673
|
logger.error("While setting %s", path + [argument.name])
|
|
659
674
|
raise
|
|
660
675
|
|
|
676
|
+
def apply_submit_hooks(self, job: "Job"):
|
|
677
|
+
"""Apply configuration hooks"""
|
|
678
|
+
context = ConfigWalkContext()
|
|
679
|
+
|
|
680
|
+
class HookGatherer(ConfigWalk):
|
|
681
|
+
def __init__(self, *args, **kwargs):
|
|
682
|
+
super().__init__(*args, **kwargs)
|
|
683
|
+
self.hooks = set()
|
|
684
|
+
|
|
685
|
+
def postprocess(self, config: "Config", values: Dict[str, Any]):
|
|
686
|
+
self.hooks.update(config.__xpm__.submit_hooks)
|
|
687
|
+
|
|
688
|
+
gatherer = HookGatherer(context, recurse_task=False)
|
|
689
|
+
gatherer(self.pyobject)
|
|
690
|
+
for hook in gatherer.hooks:
|
|
691
|
+
hook(job)
|
|
692
|
+
|
|
661
693
|
def submit(
|
|
662
|
-
self, workspace: "Workspace", launcher: "Launcher", run_mode=None
|
|
663
|
-
) -> "
|
|
694
|
+
self, workspace: "Workspace", launcher: "Launcher", *, run_mode=None, pre=None
|
|
695
|
+
) -> "ConfigWrapper":
|
|
664
696
|
from experimaestro.scheduler import experiment, JobContext
|
|
665
697
|
from experimaestro.scheduler.workspace import RunMode
|
|
666
698
|
|
|
699
|
+
# --- Handle default values
|
|
700
|
+
|
|
701
|
+
pre = pre or []
|
|
702
|
+
|
|
667
703
|
# --- Prepare the object
|
|
704
|
+
|
|
668
705
|
if self.job:
|
|
669
706
|
raise Exception("task %s was already submitted" % self)
|
|
670
707
|
if not self.xpmtype.task:
|
|
@@ -672,6 +709,7 @@ class ConfigInformation:
|
|
|
672
709
|
|
|
673
710
|
# --- Submit the job
|
|
674
711
|
|
|
712
|
+
# Creates a new job
|
|
675
713
|
self.job = self.xpmtype.task(
|
|
676
714
|
self.pyobject, launcher=launcher, workspace=workspace, run_mode=run_mode
|
|
677
715
|
)
|
|
@@ -696,7 +734,7 @@ class ConfigInformation:
|
|
|
696
734
|
experiment.CURRENT.workspace if experiment.CURRENT else None
|
|
697
735
|
)
|
|
698
736
|
|
|
699
|
-
# Call onSubmit
|
|
737
|
+
# Call onSubmit hooks
|
|
700
738
|
launcher = (
|
|
701
739
|
launcher
|
|
702
740
|
or (workspace and workspace.launcher)
|
|
@@ -705,6 +743,9 @@ class ConfigInformation:
|
|
|
705
743
|
if launcher:
|
|
706
744
|
launcher.onSubmit(self.job)
|
|
707
745
|
|
|
746
|
+
# Apply submit hooks
|
|
747
|
+
self.apply_submit_hooks(self.job)
|
|
748
|
+
|
|
708
749
|
# Add job dependencies
|
|
709
750
|
self.updatedependencies(self.job.dependencies, [], set([id(self.pyobject)]))
|
|
710
751
|
|
|
@@ -743,16 +784,20 @@ class ConfigInformation:
|
|
|
743
784
|
hints = get_type_hints(self.pyobject.config)
|
|
744
785
|
config = hints["return"](**config)
|
|
745
786
|
config.__xpm__.validate()
|
|
746
|
-
self._taskoutput =
|
|
787
|
+
self._taskoutput = ConfigWrapper.__create_taskoutput__(
|
|
788
|
+
config, self.pyobject
|
|
789
|
+
)
|
|
747
790
|
|
|
748
791
|
# New way to handle outputs
|
|
749
792
|
elif hasattr(self.pyobject, "taskoutputs"):
|
|
750
793
|
value = self.pyobject.taskoutputs()
|
|
751
|
-
self._taskoutput =
|
|
794
|
+
self._taskoutput = ConfigWrapper.__create_taskoutput__(value, self.pyobject)
|
|
752
795
|
|
|
753
796
|
# Otherwise, the output is just the config
|
|
754
797
|
else:
|
|
755
|
-
self._taskoutput =
|
|
798
|
+
self._taskoutput = ConfigWrapper.__create_taskoutput__(
|
|
799
|
+
self.pyobject, self.pyobject
|
|
800
|
+
)
|
|
756
801
|
|
|
757
802
|
return self._taskoutput
|
|
758
803
|
|
|
@@ -794,20 +839,13 @@ class ConfigInformation:
|
|
|
794
839
|
"value": value.name,
|
|
795
840
|
}
|
|
796
841
|
|
|
797
|
-
elif isinstance(value,
|
|
798
|
-
# Reference to a serialized object
|
|
799
|
-
return {
|
|
800
|
-
"type": "serialized",
|
|
801
|
-
"value": id(value.__xpm__.serialized.loader),
|
|
802
|
-
"path": [c.toJSON() for c in value.__xpm__.path],
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
elif isinstance(value, TaskOutput):
|
|
842
|
+
elif isinstance(value, ConfigWrapper):
|
|
806
843
|
return {
|
|
807
844
|
"type": "python",
|
|
808
845
|
"value": id(value.__unwrap__()),
|
|
809
846
|
# We add the task for identifier computation
|
|
810
847
|
"task": id(value.__xpm__.task),
|
|
848
|
+
"path": [c.toJSON() for c in value.__xpm__.path or []],
|
|
811
849
|
}
|
|
812
850
|
|
|
813
851
|
elif isinstance(value, Config):
|
|
@@ -876,25 +914,18 @@ class ConfigInformation:
|
|
|
876
914
|
def __collect_objects__(value, objects: List[Dict], context: SerializationContext):
|
|
877
915
|
"""Serialize all needed configuration objects, looking at sub
|
|
878
916
|
configurations if necessary"""
|
|
879
|
-
# objects
|
|
880
|
-
if isinstance(value, SerializedTaskOutput):
|
|
881
|
-
loader = value.__xpm__.serialized.loader
|
|
882
|
-
if id(loader) not in context.serialized:
|
|
883
|
-
objects.append(
|
|
884
|
-
{
|
|
885
|
-
"id": id(loader),
|
|
886
|
-
"serialized": True,
|
|
887
|
-
"module": loader.__class__.__module__,
|
|
888
|
-
"type": loader.__class__.__qualname__,
|
|
889
|
-
"value": loader.toJSON(),
|
|
890
|
-
}
|
|
891
|
-
)
|
|
892
|
-
return
|
|
893
|
-
|
|
894
917
|
# Unwrap if needed
|
|
895
|
-
if isinstance(value,
|
|
918
|
+
if isinstance(value, ConfigWrapper):
|
|
896
919
|
# We will need to output the task configuration objects
|
|
897
|
-
|
|
920
|
+
if value.__xpm__.task is not None:
|
|
921
|
+
ConfigInformation.__collect_objects__(
|
|
922
|
+
value.__xpm__.task, objects, context
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
if value.__xpm__.base is not None:
|
|
926
|
+
ConfigInformation.__collect_objects__(
|
|
927
|
+
value.__xpm__.base, objects, context
|
|
928
|
+
)
|
|
898
929
|
|
|
899
930
|
# Unwrap the value to output it
|
|
900
931
|
value = value.__unwrap__()
|
|
@@ -1024,7 +1055,7 @@ class ConfigInformation:
|
|
|
1024
1055
|
if not as_instance:
|
|
1025
1056
|
if task_id := value.get("task", None):
|
|
1026
1057
|
task = objects[task_id]
|
|
1027
|
-
return
|
|
1058
|
+
return ConfigWrapper.__create_taskoutput__(obj, task)
|
|
1028
1059
|
return obj
|
|
1029
1060
|
|
|
1030
1061
|
if value["type"] == "serialized":
|
|
@@ -1086,6 +1117,7 @@ class ConfigInformation:
|
|
|
1086
1117
|
o = None
|
|
1087
1118
|
objects = {}
|
|
1088
1119
|
import experimaestro.taskglobals as taskglobals
|
|
1120
|
+
from .serializers import SerializedConfig
|
|
1089
1121
|
|
|
1090
1122
|
for definition in definitions:
|
|
1091
1123
|
module_name = definition["module"]
|
|
@@ -1168,7 +1200,10 @@ class ConfigInformation:
|
|
|
1168
1200
|
assert isinstance(v, Path), "Excepted Path, got {type(v)}"
|
|
1169
1201
|
|
|
1170
1202
|
if as_instance:
|
|
1203
|
+
# Unwrap the value if needed
|
|
1204
|
+
v = unwrap(v)
|
|
1171
1205
|
setattr(o, name, v)
|
|
1206
|
+
|
|
1172
1207
|
assert (
|
|
1173
1208
|
getattr(o, name) is v
|
|
1174
1209
|
), f"Problem with deserialization {name} of {o.__class__}"
|
|
@@ -1178,6 +1213,8 @@ class ConfigInformation:
|
|
|
1178
1213
|
if as_instance:
|
|
1179
1214
|
# Calls post-init
|
|
1180
1215
|
o.__post_init__()
|
|
1216
|
+
if isinstance(o, SerializedConfig):
|
|
1217
|
+
o.initialize()
|
|
1181
1218
|
else:
|
|
1182
1219
|
# Seal and set the identifier
|
|
1183
1220
|
if not discard_id:
|
|
@@ -1194,8 +1231,8 @@ class ConfigInformation:
|
|
|
1194
1231
|
|
|
1195
1232
|
return o
|
|
1196
1233
|
|
|
1197
|
-
class FromPython(
|
|
1198
|
-
def __init__(self, context:
|
|
1234
|
+
class FromPython(ConfigWalk):
|
|
1235
|
+
def __init__(self, context: ConfigWalkContext):
|
|
1199
1236
|
super().__init__(context)
|
|
1200
1237
|
self.objects = {}
|
|
1201
1238
|
|
|
@@ -1231,9 +1268,16 @@ class ConfigInformation:
|
|
|
1231
1268
|
# Call __post_init__
|
|
1232
1269
|
o.__post_init__()
|
|
1233
1270
|
|
|
1271
|
+
# Process a serialized configuration
|
|
1272
|
+
from .serializers import SerializedConfig
|
|
1273
|
+
|
|
1274
|
+
if isinstance(o, SerializedConfig):
|
|
1275
|
+
o.initialize()
|
|
1276
|
+
o = o.__unwrap__()
|
|
1277
|
+
|
|
1234
1278
|
return o
|
|
1235
1279
|
|
|
1236
|
-
def fromConfig(self, context:
|
|
1280
|
+
def fromConfig(self, context: ConfigWalkContext):
|
|
1237
1281
|
"""Generate an instance given the current configuration"""
|
|
1238
1282
|
self.validate()
|
|
1239
1283
|
processor = ConfigInformation.FromPython(context)
|
|
@@ -1378,7 +1422,7 @@ class TypeConfig:
|
|
|
1378
1422
|
self.__xpm__.add_dependencies(*dependencies)
|
|
1379
1423
|
return self
|
|
1380
1424
|
|
|
1381
|
-
def instance(self, context:
|
|
1425
|
+
def instance(self, context: ConfigWalkContext = None) -> T:
|
|
1382
1426
|
"""Return an instance with the current values"""
|
|
1383
1427
|
if context is None:
|
|
1384
1428
|
from experimaestro.xpmutils import EmptyContext
|
|
@@ -1386,19 +1430,22 @@ class TypeConfig:
|
|
|
1386
1430
|
context = EmptyContext()
|
|
1387
1431
|
else:
|
|
1388
1432
|
assert isinstance(
|
|
1389
|
-
context,
|
|
1390
|
-
), f"{context.__class__} is not an instance of
|
|
1433
|
+
context, ConfigWalkContext
|
|
1434
|
+
), f"{context.__class__} is not an instance of ConfigWalkContext"
|
|
1391
1435
|
return self.__xpm__.fromConfig(context) # type: ignore
|
|
1392
1436
|
|
|
1393
|
-
def submit(
|
|
1437
|
+
def submit(
|
|
1438
|
+
self, *, workspace=None, launcher=None, run_mode: "RunMode" = None, pre=None
|
|
1439
|
+
):
|
|
1394
1440
|
"""Submit this task
|
|
1395
1441
|
|
|
1396
1442
|
:param workspace: the workspace, defaults to None
|
|
1397
1443
|
:param launcher: The launcher, defaults to None
|
|
1398
1444
|
:param run_mode: Run mode (if None, uses the workspace default)
|
|
1399
|
-
:
|
|
1445
|
+
:param pre: Pre-tasks to execute before
|
|
1446
|
+
:return: a :py:class:ConfigWrapper object
|
|
1400
1447
|
"""
|
|
1401
|
-
return self.__xpm__.submit(workspace, launcher, run_mode=run_mode)
|
|
1448
|
+
return self.__xpm__.submit(workspace, launcher, run_mode=run_mode, pre=pre)
|
|
1402
1449
|
|
|
1403
1450
|
def stdout(self):
|
|
1404
1451
|
return self.__xpm__.job.stdout
|
|
@@ -1434,7 +1481,7 @@ class Config:
|
|
|
1434
1481
|
configuration/task"""
|
|
1435
1482
|
|
|
1436
1483
|
@classmethod
|
|
1437
|
-
def __getxpmtype__(cls):
|
|
1484
|
+
def __getxpmtype__(cls) -> "ObjectType":
|
|
1438
1485
|
"""Get (and create if necessary) the Object type of this"""
|
|
1439
1486
|
xpmtype = cls.__dict__.get("__xpmtype__", None)
|
|
1440
1487
|
if xpmtype is None:
|
|
@@ -1499,9 +1546,6 @@ class Task(Config):
|
|
|
1499
1546
|
raise NotImplementedError()
|
|
1500
1547
|
|
|
1501
1548
|
|
|
1502
|
-
# --- Output proxy
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
1549
|
class Proxy:
|
|
1506
1550
|
"""A proxy for a value"""
|
|
1507
1551
|
|
|
@@ -1509,7 +1553,29 @@ class Proxy:
|
|
|
1509
1553
|
raise NotImplementedError()
|
|
1510
1554
|
|
|
1511
1555
|
|
|
1556
|
+
def unwrap(v: Any):
|
|
1557
|
+
"""Unwrap all proxies"""
|
|
1558
|
+
while isinstance(v, Proxy):
|
|
1559
|
+
v = v.__unwrap__()
|
|
1560
|
+
return v
|
|
1561
|
+
|
|
1562
|
+
|
|
1563
|
+
class AttrAccessor:
|
|
1564
|
+
"""Access an attribute"""
|
|
1565
|
+
|
|
1566
|
+
def __init__(self, key: str):
|
|
1567
|
+
self.key = key
|
|
1568
|
+
|
|
1569
|
+
def get(self, value):
|
|
1570
|
+
return getattr(value, self.key)
|
|
1571
|
+
|
|
1572
|
+
def toJSON(self):
|
|
1573
|
+
return {"type": "attr", "name": self.key}
|
|
1574
|
+
|
|
1575
|
+
|
|
1512
1576
|
class ItemAccessor:
|
|
1577
|
+
"""Access an array item"""
|
|
1578
|
+
|
|
1513
1579
|
def __init__(self, key: Any):
|
|
1514
1580
|
self.key = key
|
|
1515
1581
|
|
|
@@ -1520,49 +1586,65 @@ class ItemAccessor:
|
|
|
1520
1586
|
return value.__getitem__(self.key)
|
|
1521
1587
|
|
|
1522
1588
|
|
|
1523
|
-
class
|
|
1524
|
-
|
|
1525
|
-
self.key = key
|
|
1526
|
-
self.default = default
|
|
1527
|
-
|
|
1528
|
-
def get(self, value):
|
|
1529
|
-
return getattr(value, self.key, self.default)
|
|
1530
|
-
|
|
1531
|
-
def toJSON(self):
|
|
1532
|
-
return {"type": "attr", "name": self.key}
|
|
1589
|
+
class ConfigWrapperInfo:
|
|
1590
|
+
"""Global information about the Configuration wrapper"""
|
|
1533
1591
|
|
|
1592
|
+
def __init__(
|
|
1593
|
+
self,
|
|
1594
|
+
value: Any,
|
|
1595
|
+
*,
|
|
1596
|
+
task: Optional[Task] = None,
|
|
1597
|
+
parent: Optional["ConfigWrapperInfo"] = None,
|
|
1598
|
+
base: Optional[Config] = None,
|
|
1599
|
+
path: Optional[List[Path]] = None,
|
|
1600
|
+
):
|
|
1601
|
+
# Current value
|
|
1602
|
+
self.value = value
|
|
1603
|
+
self.parent = parent
|
|
1534
1604
|
|
|
1535
|
-
|
|
1536
|
-
|
|
1605
|
+
# The task
|
|
1606
|
+
if isinstance(value, Task):
|
|
1607
|
+
self.task = value
|
|
1608
|
+
else:
|
|
1609
|
+
self.task = task
|
|
1537
1610
|
|
|
1538
|
-
|
|
1539
|
-
|
|
1611
|
+
# Holds serialized config information
|
|
1612
|
+
from .serializers import SerializedConfig
|
|
1540
1613
|
|
|
1541
|
-
|
|
1542
|
-
|
|
1614
|
+
if isinstance(value, SerializedConfig):
|
|
1615
|
+
self.base = value
|
|
1616
|
+
self.value = value.config
|
|
1617
|
+
self.path = [AttrAccessor("config")]
|
|
1618
|
+
else:
|
|
1619
|
+
self.base = base
|
|
1620
|
+
self.path = path
|
|
1543
1621
|
|
|
1622
|
+
def __state_dict__(self):
|
|
1623
|
+
return {"task": self.task, "base": self.base, "path": self.path}
|
|
1544
1624
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1625
|
+
def __getitem__(self, key: Any):
|
|
1626
|
+
kwargs = self.__state_dict__()
|
|
1627
|
+
value = self.value.__getitem__(key)
|
|
1547
1628
|
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
"""
|
|
1629
|
+
if self.path:
|
|
1630
|
+
kwargs["path"].append(ItemAccessor(key))
|
|
1551
1631
|
|
|
1552
|
-
|
|
1553
|
-
"""The configuration that will be serialized"""
|
|
1632
|
+
return ConfigWrapperInfo(value, **kwargs)
|
|
1554
1633
|
|
|
1555
|
-
def
|
|
1556
|
-
|
|
1557
|
-
self.
|
|
1634
|
+
def __getattr__(self, key: str, *default) -> Any:
|
|
1635
|
+
kwargs = self.__state_dict__()
|
|
1636
|
+
value = getattr(self.value, key, *default)
|
|
1637
|
+
if self.path:
|
|
1638
|
+
kwargs["path"].append(AttrAccessor(key, *default))
|
|
1639
|
+
return ConfigWrapperInfo(value, parent=self, **kwargs)
|
|
1558
1640
|
|
|
1641
|
+
def wrap(self):
|
|
1642
|
+
"""Wrap a value if needed"""
|
|
1643
|
+
if isinstance(self.value, (str, int, float, Path, bool, NoneType)):
|
|
1644
|
+
return self.value
|
|
1559
1645
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
self.task = task
|
|
1563
|
-
self.value = None
|
|
1564
|
-
self.path = None
|
|
1565
|
-
self.serialized = None
|
|
1646
|
+
# Returns a new config wrapper
|
|
1647
|
+
return ConfigWrapper(self)
|
|
1566
1648
|
|
|
1567
1649
|
@property
|
|
1568
1650
|
def identifier(self):
|
|
@@ -1573,92 +1655,64 @@ class TaskOutputInfo:
|
|
|
1573
1655
|
return self.task.__xpm__.job
|
|
1574
1656
|
|
|
1575
1657
|
def tags(self):
|
|
1658
|
+
"""Returns the tags of the task"""
|
|
1576
1659
|
tags = self.task.__xpm__.tags()
|
|
1577
1660
|
return tags
|
|
1578
1661
|
|
|
1579
1662
|
def stdout(self):
|
|
1663
|
+
"""Returns the standard output of the associated task"""
|
|
1580
1664
|
return self.task.__xpm__.job.stdout
|
|
1581
1665
|
|
|
1582
1666
|
def stderr(self):
|
|
1667
|
+
"""Returns the standard error of the associated task"""
|
|
1583
1668
|
return self.task.__xpm__.job.stderr
|
|
1584
1669
|
|
|
1585
1670
|
def wait(self):
|
|
1671
|
+
"""Wait for the task to end
|
|
1672
|
+
|
|
1673
|
+
:return: True if the task completed without error
|
|
1674
|
+
"""
|
|
1586
1675
|
from experimaestro.scheduler import JobState
|
|
1587
1676
|
|
|
1588
1677
|
return self.task.__xpm__.job.wait() == JobState.DONE
|
|
1589
1678
|
|
|
1590
1679
|
|
|
1591
|
-
|
|
1680
|
+
# Cleanup into just one
|
|
1681
|
+
class ConfigWrapper(Proxy):
|
|
1592
1682
|
"""Task proxy
|
|
1593
1683
|
|
|
1594
1684
|
This is used when accessing properties *after* having submitted a task,
|
|
1595
|
-
to keep track of the dependencies
|
|
1685
|
+
to keep track of the dependencies, and/or as an accessor when dealing with
|
|
1686
|
+
a serialized config
|
|
1596
1687
|
"""
|
|
1597
1688
|
|
|
1598
|
-
def __init__(self,
|
|
1599
|
-
self.__xpm__ =
|
|
1600
|
-
task if isinstance(task, TaskOutputInfo) else TaskOutputInfo(task)
|
|
1601
|
-
)
|
|
1602
|
-
self.__xpm__.value = value
|
|
1603
|
-
|
|
1604
|
-
def _wrap(self, value):
|
|
1605
|
-
if isinstance(value, SerializedConfig):
|
|
1606
|
-
return SerializedTaskOutput(value.pyobject, value, self.__xpm__.task, [])
|
|
1607
|
-
|
|
1608
|
-
if isinstance(value, (str, int, float, Path, bool)):
|
|
1609
|
-
# No need to wrap if direct
|
|
1610
|
-
return value
|
|
1689
|
+
def __init__(self, info: ConfigWrapperInfo):
|
|
1690
|
+
self.__xpm__ = info
|
|
1611
1691
|
|
|
1612
|
-
|
|
1692
|
+
@staticmethod
|
|
1693
|
+
def __create_taskoutput__(value: Any, task: Task):
|
|
1694
|
+
return ConfigWrapperInfo(value, task=task).wrap()
|
|
1613
1695
|
|
|
1614
1696
|
def __getitem__(self, key: Any):
|
|
1615
|
-
return self.
|
|
1697
|
+
return self.__xpm__[key].wrap()
|
|
1616
1698
|
|
|
1617
|
-
def __getattr__(self, key: str, default
|
|
1618
|
-
return self.
|
|
1699
|
+
def __getattr__(self, key: str, *default) -> Any:
|
|
1700
|
+
return self.__xpm__.__getattr__(key, *default).wrap()
|
|
1619
1701
|
|
|
1620
1702
|
def __unwrap__(self):
|
|
1621
1703
|
return self.__xpm__.value
|
|
1622
1704
|
|
|
1623
1705
|
def __call__(self, *args, **kwargs):
|
|
1624
1706
|
assert callable(self.__xpm__.value), "Attribute is not a function"
|
|
1625
|
-
__self__ = TaskOutput(self.__xpm__.value.__self__, self.__xpm__.task)
|
|
1626
|
-
return self.__xpm__.value.__func__(__self__, *args, **kwargs)
|
|
1627
1707
|
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
"""Used when serializing a configuration
|
|
1631
|
-
|
|
1632
|
-
Here, we need to keep track of the path to the value we need
|
|
1633
|
-
"""
|
|
1634
|
-
|
|
1635
|
-
def __init__(
|
|
1636
|
-
self, value, serialized: SerializedConfig, task: Task, path: List[Any]
|
|
1637
|
-
):
|
|
1638
|
-
super().__init__(value, task)
|
|
1639
|
-
self.__xpm__.serialized = serialized
|
|
1640
|
-
self.__xpm__.path = path
|
|
1641
|
-
|
|
1642
|
-
def __getitem__(self, key: Any):
|
|
1643
|
-
value = self.__xpm__.value.__getitem__(key)
|
|
1644
|
-
return SerializedTaskOutput(
|
|
1645
|
-
value, self.serialized, self.__xpm__.task, self.path + [ItemAccessor(key)]
|
|
1646
|
-
)
|
|
1647
|
-
|
|
1648
|
-
def __getattr__(self, key: str, default=None) -> Any:
|
|
1649
|
-
value = getattr(self.__xpm__.value, key, default)
|
|
1650
|
-
return SerializedTaskOutput(
|
|
1651
|
-
value,
|
|
1652
|
-
self.__xpm__.serialized,
|
|
1653
|
-
self.__xpm__.task,
|
|
1654
|
-
self.__xpm__.path + [AttrAccessor(key, default)],
|
|
1655
|
-
)
|
|
1708
|
+
__self__ = self.__xpm__.parent.wrap()
|
|
1709
|
+
return self.__xpm__.value.__func__(__self__, *args, **kwargs)
|
|
1656
1710
|
|
|
1657
1711
|
|
|
1658
1712
|
# --- Utility functions
|
|
1659
1713
|
|
|
1660
1714
|
|
|
1661
|
-
def copyconfig(config_or_output: Union[Config,
|
|
1715
|
+
def copyconfig(config_or_output: Union[Config, ConfigWrapper], **kwargs):
|
|
1662
1716
|
"""Copy a configuration or task output
|
|
1663
1717
|
|
|
1664
1718
|
Useful to modify a configuration that can be potentially
|
|
@@ -1666,7 +1720,7 @@ def copyconfig(config_or_output: Union[Config, TaskOutput], **kwargs):
|
|
|
1666
1720
|
a task output).
|
|
1667
1721
|
"""
|
|
1668
1722
|
|
|
1669
|
-
if isinstance(config_or_output,
|
|
1723
|
+
if isinstance(config_or_output, ConfigWrapper):
|
|
1670
1724
|
output = config_or_output
|
|
1671
1725
|
config = config_or_output.__unwrap__()
|
|
1672
1726
|
assert isinstance(config, Config)
|
|
@@ -1686,7 +1740,7 @@ def copyconfig(config_or_output: Union[Config, TaskOutput], **kwargs):
|
|
|
1686
1740
|
return copy
|
|
1687
1741
|
|
|
1688
1742
|
# wrap in Task output
|
|
1689
|
-
return
|
|
1743
|
+
return ConfigWrapper(copy, output.__xpm__)
|
|
1690
1744
|
|
|
1691
1745
|
|
|
1692
1746
|
def setmeta(config: Config, flag: bool):
|