mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-02 07:18:25 +01:00
Use task status to replace collected.
fix: #8088 1. Use status to replace collected. 2. Make status show more accurate. Fix lint
This commit is contained in:
@@ -73,6 +73,7 @@ class TaskStatus(IntEnum):
|
|||||||
SUCCESS = 3
|
SUCCESS = 3
|
||||||
FAILED = 4
|
FAILED = 4
|
||||||
REVOKED = 5
|
REVOKED = 5
|
||||||
|
FINISHED = 6
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -84,7 +85,6 @@ class ExecutorInfo:
|
|||||||
dvc_dir: str
|
dvc_dir: str
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
wdir: Optional[str] = None
|
wdir: Optional[str] = None
|
||||||
collected: bool = False
|
|
||||||
result_hash: Optional[str] = None
|
result_hash: Optional[str] = None
|
||||||
result_ref: Optional[str] = None
|
result_ref: Optional[str] = None
|
||||||
result_force: bool = False
|
result_force: bool = False
|
||||||
@@ -431,12 +431,9 @@ class BaseExecutor(ABC):
|
|||||||
exp_ref: Optional["ExpRefInfo"] = None
|
exp_ref: Optional["ExpRefInfo"] = None
|
||||||
repro_force: bool = False
|
repro_force: bool = False
|
||||||
|
|
||||||
info.status = TaskStatus.PREPARING
|
|
||||||
if infofile is not None:
|
|
||||||
info.dump_json(infofile)
|
|
||||||
|
|
||||||
with cls._repro_dvc(
|
with cls._repro_dvc(
|
||||||
info,
|
info,
|
||||||
|
infofile,
|
||||||
log_errors=log_errors,
|
log_errors=log_errors,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) as dvc:
|
) as dvc:
|
||||||
@@ -488,9 +485,6 @@ class BaseExecutor(ABC):
|
|||||||
info.name,
|
info.name,
|
||||||
repro_force or checkpoint_reset,
|
repro_force or checkpoint_reset,
|
||||||
)
|
)
|
||||||
info.status = TaskStatus.RUNNING
|
|
||||||
if infofile is not None:
|
|
||||||
info.dump_json(infofile)
|
|
||||||
stages = dvc_reproduce(
|
stages = dvc_reproduce(
|
||||||
dvc,
|
dvc,
|
||||||
*args,
|
*args,
|
||||||
@@ -514,10 +508,6 @@ class BaseExecutor(ABC):
|
|||||||
info.result_hash = exp_hash
|
info.result_hash = exp_hash
|
||||||
info.result_ref = ref
|
info.result_ref = ref
|
||||||
info.result_force = repro_force
|
info.result_force = repro_force
|
||||||
info.status = TaskStatus.SUCCESS
|
|
||||||
|
|
||||||
if infofile is not None:
|
|
||||||
info.dump_json(infofile)
|
|
||||||
|
|
||||||
# ideally we would return stages here like a normal repro() call, but
|
# ideally we would return stages here like a normal repro() call, but
|
||||||
# stages is not currently picklable and cannot be returned across
|
# stages is not currently picklable and cannot be returned across
|
||||||
@@ -575,6 +565,7 @@ class BaseExecutor(ABC):
|
|||||||
def _repro_dvc(
|
def _repro_dvc(
|
||||||
cls,
|
cls,
|
||||||
info: "ExecutorInfo",
|
info: "ExecutorInfo",
|
||||||
|
infofile: str = None,
|
||||||
log_errors: bool = True,
|
log_errors: bool = True,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
@@ -582,6 +573,9 @@ class BaseExecutor(ABC):
|
|||||||
from dvc.stage.monitor import CheckpointKilledError
|
from dvc.stage.monitor import CheckpointKilledError
|
||||||
|
|
||||||
dvc = Repo(os.path.join(info.root_dir, info.dvc_dir))
|
dvc = Repo(os.path.join(info.root_dir, info.dvc_dir))
|
||||||
|
info.status = TaskStatus.RUNNING
|
||||||
|
if infofile is not None:
|
||||||
|
info.dump_json(infofile)
|
||||||
if cls.QUIET:
|
if cls.QUIET:
|
||||||
dvc.scm_context.quiet = cls.QUIET
|
dvc.scm_context.quiet = cls.QUIET
|
||||||
old_cwd = os.getcwd()
|
old_cwd = os.getcwd()
|
||||||
@@ -593,15 +587,28 @@ class BaseExecutor(ABC):
|
|||||||
try:
|
try:
|
||||||
logger.debug("Running repro in '%s'", os.getcwd())
|
logger.debug("Running repro in '%s'", os.getcwd())
|
||||||
yield dvc
|
yield dvc
|
||||||
|
info.status = TaskStatus.SUCCESS
|
||||||
|
if infofile is not None:
|
||||||
|
info.dump_json(infofile)
|
||||||
|
|
||||||
except CheckpointKilledError:
|
except CheckpointKilledError:
|
||||||
|
info.status = TaskStatus.FAILED
|
||||||
|
if infofile is not None:
|
||||||
|
info.dump_json(infofile)
|
||||||
raise
|
raise
|
||||||
except DvcException:
|
except DvcException:
|
||||||
if log_errors:
|
if log_errors:
|
||||||
logger.exception("")
|
logger.exception("")
|
||||||
|
info.status = TaskStatus.FAILED
|
||||||
|
if infofile is not None:
|
||||||
|
info.dump_json(infofile)
|
||||||
raise
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
if log_errors:
|
if log_errors:
|
||||||
logger.exception("unexpected error")
|
logger.exception("unexpected error")
|
||||||
|
info.status = TaskStatus.FAILED
|
||||||
|
if infofile is not None:
|
||||||
|
info.dump_json(infofile)
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
dvc.close()
|
dvc.close()
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ from ..executor.base import (
|
|||||||
EXEC_TMP_DIR,
|
EXEC_TMP_DIR,
|
||||||
BaseExecutor,
|
BaseExecutor,
|
||||||
ExecutorResult,
|
ExecutorResult,
|
||||||
|
TaskStatus,
|
||||||
)
|
)
|
||||||
from ..executor.local import WorkspaceExecutor
|
from ..executor.local import WorkspaceExecutor
|
||||||
from ..refs import ExpRefInfo
|
from ..refs import ExpRefInfo
|
||||||
@@ -607,6 +608,7 @@ class BaseStashQueue(ABC):
|
|||||||
exp: "Experiments",
|
exp: "Experiments",
|
||||||
executor: BaseExecutor,
|
executor: BaseExecutor,
|
||||||
exec_result: ExecutorResult,
|
exec_result: ExecutorResult,
|
||||||
|
infofile: str,
|
||||||
) -> Dict[str, str]:
|
) -> Dict[str, str]:
|
||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
@@ -630,6 +632,10 @@ class BaseStashQueue(ABC):
|
|||||||
if exec_result.ref_info is not None:
|
if exec_result.ref_info is not None:
|
||||||
executor.collect_cache(exp.repo, exec_result.ref_info)
|
executor.collect_cache(exp.repo, exec_result.ref_info)
|
||||||
|
|
||||||
|
executor.status = TaskStatus.FINISHED
|
||||||
|
if infofile is not None:
|
||||||
|
executor.info.dump_json(infofile)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def match_queue_entry_by_name(
|
def match_queue_entry_by_name(
|
||||||
|
|||||||
@@ -23,7 +23,12 @@ from dvc.exceptions import DvcException
|
|||||||
from dvc.ui import ui
|
from dvc.ui import ui
|
||||||
|
|
||||||
from ..exceptions import UnresolvedQueueExpNamesError
|
from ..exceptions import UnresolvedQueueExpNamesError
|
||||||
from ..executor.base import EXEC_TMP_DIR, ExecutorInfo, ExecutorResult
|
from ..executor.base import (
|
||||||
|
EXEC_TMP_DIR,
|
||||||
|
ExecutorInfo,
|
||||||
|
ExecutorResult,
|
||||||
|
TaskStatus,
|
||||||
|
)
|
||||||
from .base import BaseStashQueue, QueueDoneResult, QueueEntry, QueueGetResult
|
from .base import BaseStashQueue, QueueDoneResult, QueueEntry, QueueGetResult
|
||||||
from .tasks import run_exp
|
from .tasks import run_exp
|
||||||
|
|
||||||
@@ -248,7 +253,8 @@ class LocalCeleryQueue(BaseStashQueue):
|
|||||||
|
|
||||||
def _load_collected(rev: str) -> Optional[ExecutorResult]:
|
def _load_collected(rev: str) -> Optional[ExecutorResult]:
|
||||||
executor_info = _load_info(rev)
|
executor_info = _load_info(rev)
|
||||||
if executor_info.collected:
|
print("executor_info is", executor_info.status)
|
||||||
|
if executor_info.status > TaskStatus.SUCCESS:
|
||||||
return executor_info.result
|
return executor_info.result
|
||||||
raise FileNotFoundError
|
raise FileNotFoundError
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ def collect_exp(
|
|||||||
try:
|
try:
|
||||||
if exec_result is not None:
|
if exec_result is not None:
|
||||||
BaseStashQueue.collect_executor(
|
BaseStashQueue.collect_executor(
|
||||||
repo.experiments, executor, exec_result
|
repo.experiments, executor, exec_result, infofile
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug("Experiment failed (Exec result was None)")
|
logger.debug("Experiment failed (Exec result was None)")
|
||||||
@@ -91,15 +91,6 @@ def cleanup_exp( # pylint: disable=unused-argument
|
|||||||
remove(tmp_dir)
|
remove(tmp_dir)
|
||||||
|
|
||||||
|
|
||||||
def _set_collected(infofile: str):
|
|
||||||
try:
|
|
||||||
executor_info = ExecutorInfo.load_json(infofile)
|
|
||||||
executor_info.collected = True
|
|
||||||
executor_info.dump_json(infofile)
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def run_exp(entry_dict: Dict[str, Any]) -> None:
|
def run_exp(entry_dict: Dict[str, Any]) -> None:
|
||||||
"""Run a full experiment.
|
"""Run a full experiment.
|
||||||
@@ -122,4 +113,3 @@ def run_exp(entry_dict: Dict[str, Any]) -> None:
|
|||||||
collect_exp.s(proc_dict, entry_dict)()
|
collect_exp.s(proc_dict, entry_dict)()
|
||||||
finally:
|
finally:
|
||||||
cleanup_exp.s(root_dir, entry_dict)()
|
cleanup_exp.s(root_dir, entry_dict)()
|
||||||
_set_collected(infofile)
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from ..executor.base import (
|
|||||||
BaseExecutor,
|
BaseExecutor,
|
||||||
ExecutorInfo,
|
ExecutorInfo,
|
||||||
ExecutorResult,
|
ExecutorResult,
|
||||||
|
TaskStatus,
|
||||||
)
|
)
|
||||||
from ..executor.local import TempDirExecutor
|
from ..executor.local import TempDirExecutor
|
||||||
from .base import BaseStashQueue, QueueEntry, QueueGetResult
|
from .base import BaseStashQueue, QueueEntry, QueueGetResult
|
||||||
@@ -75,8 +76,9 @@ class TempDirQueue(WorkspaceQueue):
|
|||||||
for stash_rev in self.proc:
|
for stash_rev in self.proc:
|
||||||
infofile = self.get_infofile_path(stash_rev)
|
infofile = self.get_infofile_path(stash_rev)
|
||||||
executor_info = ExecutorInfo.load_json(infofile)
|
executor_info = ExecutorInfo.load_json(infofile)
|
||||||
if not executor_info.collected and os.path.exists(
|
if (
|
||||||
executor_info.root_dir
|
not executor_info.status <= TaskStatus.SUCCESS
|
||||||
|
and os.path.exists(executor_info.root_dir)
|
||||||
):
|
):
|
||||||
yield QueueEntry(
|
yield QueueEntry(
|
||||||
self.repo.root_dir,
|
self.repo.root_dir,
|
||||||
@@ -94,5 +96,8 @@ class TempDirQueue(WorkspaceQueue):
|
|||||||
exp: "Experiments",
|
exp: "Experiments",
|
||||||
executor: BaseExecutor,
|
executor: BaseExecutor,
|
||||||
exec_result: ExecutorResult,
|
exec_result: ExecutorResult,
|
||||||
|
infofile: str,
|
||||||
) -> Dict[str, str]:
|
) -> Dict[str, str]:
|
||||||
return BaseStashQueue.collect_executor(exp, executor, exec_result)
|
return BaseStashQueue.collect_executor(
|
||||||
|
exp, executor, exec_result, infofile
|
||||||
|
)
|
||||||
|
|||||||
@@ -105,18 +105,15 @@ class WorkspaceQueue(BaseStashQueue):
|
|||||||
if exec_result.ref_info:
|
if exec_result.ref_info:
|
||||||
results[rev].update(
|
results[rev].update(
|
||||||
self.collect_executor(
|
self.collect_executor(
|
||||||
self.repo.experiments, executor, exec_result
|
self.repo.experiments, executor, exec_result, infofile
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except CheckpointKilledError:
|
except CheckpointKilledError:
|
||||||
# Checkpoint errors have already been logged
|
# Checkpoint errors have already been logged
|
||||||
executor.status = TaskStatus.FAILED
|
|
||||||
return {}
|
return {}
|
||||||
except DvcException:
|
except DvcException:
|
||||||
executor.status = TaskStatus.FAILED
|
|
||||||
raise
|
raise
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
executor.status = TaskStatus.FAILED
|
|
||||||
raise DvcException(
|
raise DvcException(
|
||||||
f"Failed to reproduce experiment '{rev[:7]}'"
|
f"Failed to reproduce experiment '{rev[:7]}'"
|
||||||
) from exc
|
) from exc
|
||||||
@@ -131,6 +128,7 @@ class WorkspaceQueue(BaseStashQueue):
|
|||||||
exp: "Experiments",
|
exp: "Experiments",
|
||||||
executor: BaseExecutor,
|
executor: BaseExecutor,
|
||||||
exec_result: ExecutorResult,
|
exec_result: ExecutorResult,
|
||||||
|
infofile: str,
|
||||||
) -> Dict[str, str]:
|
) -> Dict[str, str]:
|
||||||
results: Dict[str, str] = {}
|
results: Dict[str, str] = {}
|
||||||
exp_rev = exp.scm.get_ref(EXEC_BRANCH)
|
exp_rev = exp.scm.get_ref(EXEC_BRANCH)
|
||||||
@@ -138,6 +136,11 @@ class WorkspaceQueue(BaseStashQueue):
|
|||||||
assert exec_result.exp_hash
|
assert exec_result.exp_hash
|
||||||
logger.debug("Collected experiment '%s'.", exp_rev[:7])
|
logger.debug("Collected experiment '%s'.", exp_rev[:7])
|
||||||
results[exp_rev] = exec_result.exp_hash
|
results[exp_rev] = exec_result.exp_hash
|
||||||
|
|
||||||
|
executor.status = TaskStatus.FINISHED
|
||||||
|
if infofile is not None:
|
||||||
|
executor.info.dump_json(infofile)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def get_result(self, entry: QueueEntry) -> Optional[ExecutorResult]:
|
def get_result(self, entry: QueueEntry) -> Optional[ExecutorResult]:
|
||||||
|
|||||||
@@ -38,14 +38,14 @@ def test_init_from_stash(tmp_dir, scm, dvc, machine_instance, mocker):
|
|||||||
|
|
||||||
@pytest.mark.needs_internet
|
@pytest.mark.needs_internet
|
||||||
@pytest.mark.parametrize("cloud", [pytest.lazy_fixture("git_ssh")])
|
@pytest.mark.parametrize("cloud", [pytest.lazy_fixture("git_ssh")])
|
||||||
def test_init_git(tmp_dir, scm, cloud):
|
def test_init_git(tmp_dir, scm, cloud, mocker):
|
||||||
tmp_dir.scm_gen({"foo": "foo", "dir": {"bar": "bar"}}, commit="init")
|
tmp_dir.scm_gen({"foo": "foo", "dir": {"bar": "bar"}}, commit="init")
|
||||||
baseline_rev = scm.get_rev()
|
baseline_rev = scm.get_rev()
|
||||||
scm.set_ref(EXEC_HEAD, baseline_rev)
|
|
||||||
tmp_dir.gen("foo", "stashed")
|
tmp_dir.gen("foo", "stashed")
|
||||||
scm.gitpython.git.stash()
|
scm.gitpython.git.stash()
|
||||||
rev = scm.resolve_rev("stash@{0}")
|
rev = scm.resolve_rev("stash@{0}")
|
||||||
scm.set_ref(EXEC_MERGE, rev)
|
|
||||||
|
mock = mocker.Mock(baseline_rev=baseline_rev, head_rev=baseline_rev)
|
||||||
|
|
||||||
root_url = cloud / SSHExecutor.gen_dirname()
|
root_url = cloud / SSHExecutor.gen_dirname()
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ def test_init_git(tmp_dir, scm, cloud):
|
|||||||
username=TEST_SSH_USER,
|
username=TEST_SSH_USER,
|
||||||
fs_factory=partial(_ssh_factory, cloud),
|
fs_factory=partial(_ssh_factory, cloud),
|
||||||
)
|
)
|
||||||
executor.init_git(scm)
|
executor.init_git(scm, rev, mock)
|
||||||
assert root_url.path == executor._repo_abspath
|
assert root_url.path == executor._repo_abspath
|
||||||
|
|
||||||
fs = cloud._ssh
|
fs = cloud._ssh
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from dvc.repo.experiments.executor.base import (
|
|||||||
EXEC_TMP_DIR,
|
EXEC_TMP_DIR,
|
||||||
BaseExecutor,
|
BaseExecutor,
|
||||||
ExecutorInfo,
|
ExecutorInfo,
|
||||||
|
TaskStatus,
|
||||||
)
|
)
|
||||||
from dvc.repo.experiments.queue.base import QueueEntry
|
from dvc.repo.experiments.queue.base import QueueEntry
|
||||||
from dvc.repo.experiments.refs import CELERY_STASH, ExpRefInfo
|
from dvc.repo.experiments.refs import CELERY_STASH, ExpRefInfo
|
||||||
@@ -338,9 +339,16 @@ def test_show_sort(tmp_dir, scm, dvc, exp_stage, caplog):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.vscode
|
@pytest.mark.vscode
|
||||||
def test_show_running_workspace(tmp_dir, scm, dvc, exp_stage, capsys):
|
@pytest.mark.parametrize(
|
||||||
|
"status, running", [(TaskStatus.RUNNING, True), (TaskStatus.FAILED, False)]
|
||||||
|
)
|
||||||
|
def test_show_running_workspace(
|
||||||
|
tmp_dir, scm, dvc, exp_stage, capsys, status, running
|
||||||
|
):
|
||||||
pid_dir = os.path.join(dvc.tmp_dir, EXEC_TMP_DIR, EXEC_PID_DIR)
|
pid_dir = os.path.join(dvc.tmp_dir, EXEC_TMP_DIR, EXEC_PID_DIR)
|
||||||
info = make_executor_info(location=BaseExecutor.DEFAULT_LOCATION)
|
info = make_executor_info(
|
||||||
|
location=BaseExecutor.DEFAULT_LOCATION, status=status
|
||||||
|
)
|
||||||
pidfile = os.path.join(
|
pidfile = os.path.join(
|
||||||
pid_dir,
|
pid_dir,
|
||||||
"workspace",
|
"workspace",
|
||||||
@@ -349,7 +357,8 @@ def test_show_running_workspace(tmp_dir, scm, dvc, exp_stage, capsys):
|
|||||||
makedirs(os.path.dirname(pidfile), True)
|
makedirs(os.path.dirname(pidfile), True)
|
||||||
(tmp_dir / pidfile).dump_json(info.asdict())
|
(tmp_dir / pidfile).dump_json(info.asdict())
|
||||||
|
|
||||||
assert dvc.experiments.show()["workspace"] == {
|
print(dvc.experiments.show())
|
||||||
|
assert dvc.experiments.show().get("workspace") == {
|
||||||
"baseline": {
|
"baseline": {
|
||||||
"data": {
|
"data": {
|
||||||
"deps": {
|
"deps": {
|
||||||
@@ -363,18 +372,18 @@ def test_show_running_workspace(tmp_dir, scm, dvc, exp_stage, capsys):
|
|||||||
"params": {"params.yaml": {"data": {"foo": 1}}},
|
"params": {"params.yaml": {"data": {"foo": 1}}},
|
||||||
"outs": {},
|
"outs": {},
|
||||||
"queued": False,
|
"queued": False,
|
||||||
"running": True,
|
"running": True if running else False,
|
||||||
"executor": info.location,
|
"executor": info.location if running else None,
|
||||||
"timestamp": None,
|
"timestamp": None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
capsys.readouterr()
|
capsys.readouterr()
|
||||||
assert main(["exp", "show", "--csv"]) == 0
|
assert main(["exp", "show", "--csv"]) == 0
|
||||||
cap = capsys.readouterr()
|
cap = capsys.readouterr()
|
||||||
assert "Running" in cap.out
|
if running:
|
||||||
assert info.location in cap.out
|
assert "Running" in cap.out
|
||||||
|
assert info.location in cap.out
|
||||||
|
|
||||||
|
|
||||||
def test_show_running_tempdir(tmp_dir, scm, dvc, exp_stage, mocker):
|
def test_show_running_tempdir(tmp_dir, scm, dvc, exp_stage, mocker):
|
||||||
|
|||||||
Reference in New Issue
Block a user