labtasker 0.2.2__tar.gz → 0.2.4__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.
- {labtasker-0.2.2 → labtasker-0.2.4}/.gitignore +2 -0
- labtasker-0.2.4/.gitmodules +8 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/.pre-commit-config.yaml +2 -2
- {labtasker-0.2.2 → labtasker-0.2.4}/Dockerfile +5 -2
- {labtasker-0.2.2 → labtasker-0.2.4}/MANIFEST.in +2 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/PKG-INFO +37 -15
- {labtasker-0.2.2 → labtasker-0.2.4}/README.md +29 -10
- {labtasker-0.2.2 → labtasker-0.2.4}/docker-compose.yml +2 -2
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/__init__.py +1 -1
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/api_models.py +2 -2
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/cli/init.py +5 -2
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/cli/loop.py +23 -15
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/cli/task.py +41 -8
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/cli/worker.py +11 -5
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/cli_utils.py +27 -21
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/cmd_parser/parser.py +16 -11
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/config.py +2 -2
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/exceptions.py +12 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/job_runner.py +49 -7
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/templates/labtasker_root/client.toml +1 -1
- labtasker-0.2.4/labtasker/server/cli.py +90 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/server/config.py +4 -3
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/server/database.py +69 -29
- labtasker-0.2.4/labtasker/server/embedded_db.py +420 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/server/endpoints.py +5 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker.egg-info/SOURCES.txt +3 -1
- {labtasker-0.2.2 → labtasker-0.2.4}/pyproject.toml +11 -7
- {labtasker-0.2.2 → labtasker-0.2.4}/server.example.env +1 -1
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/conftest.py +1 -1
- labtasker-0.2.4/tests/fixtures/database/__init__.py +4 -0
- labtasker-0.2.4/tests/fixtures/database/mock.py +19 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/conftest.py +10 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_cli/test_init.py +1 -1
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_cli/test_loop.py +22 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_cli/test_task.py +18 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_runner_with_resolver.py +16 -0
- labtasker-0.2.4/tests/test_server/test_embedded_db.py +41 -0
- labtasker-0.2.2/.gitmodules +0 -4
- labtasker-0.2.2/labtasker/server/run.py +0 -21
- labtasker-0.2.2/tests/fixtures/database/__init__.py +0 -2
- labtasker-0.2.2/tests/fixtures/database/mock.py +0 -61
- {labtasker-0.2.2 → labtasker-0.2.4}/.coveragerc +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/.dockerignore +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/.flake8 +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/.gitattributes +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/.pytest.ini +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/.python-version +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/LICENSE +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/PRIVACY.md +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/advanced/custom_resolver/submit_job.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/advanced/custom_resolver/w.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/advanced/custom_resolver/wo.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/advanced/event_system/email_on_task_failure.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/advanced/event_system/send_email.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/advanced/event_system/sim_unstable_job.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/advanced/event_system/submit.sh +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/basic/bash_demo/job_main.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/basic/bash_demo/run_job.sh +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/basic/bash_demo/submit_job.sh +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/basic/python_demo/run_job.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/demo/basic/python_demo/submit_job.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/docker/mongodb/init.d/init-keyfile.sh +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/docker/mongodb/post-init.d/init-mongo.sh +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/__main__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/cli/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/cli/cli.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/cli/config.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/cli/event.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/cli/queue.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/client_api.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/api.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/cmd_parser/LabCmd.g4 +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/cmd_parser/LabCmdLexer.g4 +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/cmd_parser/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/cmd_parser/generated/LabCmd.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/cmd_parser/generated/LabCmdLexer.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/cmd_parser/generated/LabCmdListener.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/cmd_parser/generated/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/context.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/events.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/heartbeat.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/logging.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/paths.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/plugin_utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/query_transpiler.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/resolver/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/resolver/models.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/resolver/utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/core/version_checker.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/templates/labtasker_root/.gitignore +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/client/templates/labtasker_root/logs/.gitkeep +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/constants.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/filtering.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/security.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/server/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/server/db_utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/server/dependencies.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/server/event_manager.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/server/fsm.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/server/logging.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/labtasker/utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/script_tests/test_ban_datetime_now/allowed.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/script_tests/test_ban_datetime_now/disallowed.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/script_tests/test_ban_datetime_now/test_ban.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/scripts/asciinema_adjust_timestamp.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/scripts/check_version.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/scripts/datetime_now_checker.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/setup.cfg +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/demo_pager_iterator.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/dummy_jobs/job_1.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/fixtures/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/fixtures/database/real.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/fixtures/logging.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/fixtures/mock_datetime_now.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/fixtures/server/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/fixtures/server/async_app.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/fixtures/server/sync_app.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_api_models.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_cli/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_cli/conftest.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_cli/test_basic.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_cli/test_config.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_cli/test_event.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_cli/test_queue.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_cli/test_worker.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_cli_utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_event/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_event/test_concurrency_job_flow_event.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_event/test_event_listener_basic.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_event/test_various_actions.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_event/utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_heartbeat.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_job_runner.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_logging.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_loop_internal_error_handler.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_pager_iterator.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_parser.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_query_transpiler/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_query_transpiler/conftest.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_query_transpiler/test_behavior.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_query_transpiler/test_matching.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_query_transpiler/test_utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_query_transpiler/utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_resolver.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_runner_concurrency/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_runner_concurrency/run_concurrent.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_runner_concurrency/test_runner_concurrency_success_failure.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_runner_concurrency/test_runner_high_concurrency.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_runner_timeout/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_runner_timeout/conftest.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_runner_timeout/test_job_runner_timeout.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_runner_timeout/test_job_runner_with_resolver_timeout.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_server_notification_and_client_version.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_client/test_core/test_version_checker.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_filtering/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_filtering/exception_utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_filtering/test_exception_filtering.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_mock_time.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_security.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/conftest.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_database/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_database/conftest.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_database/test_database_basic.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_database/test_fetch_extra_filter.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_database/test_query_dict_to_mongo_filter.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_database/test_required_field_fetching.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_db_utils/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_db_utils/test_arg_match.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_db_utils/test_keys_to_query_dict.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_db_utils/test_keys_to_query_dict_deepest.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_db_utils/test_keys_to_query_dict_topmost.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_endpoint/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_endpoint/test_event_basic.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_endpoint/test_server.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_endpoint/test_server_async.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_endpoint/test_server_async_ping.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_fsm.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_server/test_get_verified_queue_dependency.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_utils/__init__.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/test_utils/test_utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tests/utils.py +0 -0
- {labtasker-0.2.2 → labtasker-0.2.4}/tox.ini +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
[submodule "plugins/labtasker_plugin_task_count"]
|
|
2
|
+
path = plugins/labtasker_plugin_task_count
|
|
3
|
+
url = https://github.com/fkcptlst/labtasker-plugin-task-count.git
|
|
4
|
+
branch = main
|
|
5
|
+
[submodule "plugins/labtasker_plugin_script_generate"]
|
|
6
|
+
path = plugins/labtasker_plugin_script_generate
|
|
7
|
+
url = https://github.com/fkcptlst/labtasker-plugin-script-generate.git
|
|
8
|
+
branch = main
|
|
@@ -16,7 +16,10 @@ RUN pip install --no-cache-dir "."
|
|
|
16
16
|
|
|
17
17
|
# Health check
|
|
18
18
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
19
|
-
CMD curl -f http://localhost:${API_PORT:-
|
|
19
|
+
CMD curl -f http://localhost:${API_PORT:-9321}/health || exit 1
|
|
20
|
+
|
|
21
|
+
# Set DB_MODE to external
|
|
22
|
+
ENV DB_MODE=${DB_MODE:-external}
|
|
20
23
|
|
|
21
24
|
# Run the application
|
|
22
|
-
CMD ["
|
|
25
|
+
CMD ["labtasker-server", "serve"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: labtasker
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A task queue system for lab experiments
|
|
5
5
|
Author-email: Your Name <your.email@example.com>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -40,11 +40,12 @@ Requires-Dist: sse-starlette<3.0.0,>=2.1.3
|
|
|
40
40
|
Requires-Dist: httpx-sse<0.5.0,>=0.4.0
|
|
41
41
|
Requires-Dist: stamina<26.0.0,>=25.1.0
|
|
42
42
|
Requires-Dist: noneprompt<0.2.0,>=0.1.9
|
|
43
|
-
Requires-Dist:
|
|
43
|
+
Requires-Dist: mongomock<4.4.0,>=4.3.0
|
|
44
|
+
Requires-Dist: jsonpickle<5.0.0,>=4.0.2
|
|
45
|
+
Requires-Dist: mslex<2.0.0,>=1.3.0
|
|
44
46
|
Provides-Extra: dev
|
|
45
47
|
Requires-Dist: pytest<9.0.0,>=8.0.0; extra == "dev"
|
|
46
48
|
Requires-Dist: pytest-cov<7.0.0,>=5.0.0; extra == "dev"
|
|
47
|
-
Requires-Dist: mongomock<4.4.0,>=4.3.0; extra == "dev"
|
|
48
49
|
Requires-Dist: black<26.0.0,>=24.0.0; extra == "dev"
|
|
49
50
|
Requires-Dist: isort<7.0.0,>=5.13.0; extra == "dev"
|
|
50
51
|
Requires-Dist: mypy<2.0.0,>=1.14.0; extra == "dev"
|
|
@@ -52,21 +53,23 @@ Requires-Dist: flake8<8.0.0,>=7.0.0; extra == "dev"
|
|
|
52
53
|
Requires-Dist: pre-commit<5.0.0,>=3.0.0; extra == "dev"
|
|
53
54
|
Requires-Dist: freezegun<2.0.0,>=1.5.0; extra == "dev"
|
|
54
55
|
Requires-Dist: pytest-docker<4.0.0,>=3.0.0; extra == "dev"
|
|
55
|
-
Requires-Dist: pytest-asyncio<0.
|
|
56
|
+
Requires-Dist: pytest-asyncio<0.27.0,>=0.24.0; extra == "dev"
|
|
56
57
|
Requires-Dist: asgi-lifespan<3.0.0,>=2.1.0; extra == "dev"
|
|
57
|
-
Requires-Dist: tox<4.
|
|
58
|
+
Requires-Dist: tox<4.26.0,>=4.24.0; extra == "dev"
|
|
58
59
|
Requires-Dist: pytest-dependency<0.7.0,>=0.6.0; extra == "dev"
|
|
60
|
+
Requires-Dist: pytest-sugar<2.0.0,>=1.0.0; extra == "dev"
|
|
59
61
|
Provides-Extra: doc
|
|
60
62
|
Requires-Dist: mkdocs-material<9.7.0,>=9.6.5; extra == "doc"
|
|
61
63
|
Requires-Dist: mkdocs-glightbox<0.5.0,>=0.4.0; extra == "doc"
|
|
62
64
|
Requires-Dist: mike<2.2.0,>=2.1.3; extra == "doc"
|
|
63
65
|
Provides-Extra: plugins
|
|
64
66
|
Requires-Dist: labtasker-plugin-task-count; extra == "plugins"
|
|
67
|
+
Requires-Dist: labtasker-plugin-script-generate; extra == "plugins"
|
|
65
68
|
Dynamic: license-file
|
|
66
69
|
|
|
67
70
|
<p align="center"><em>Make your ML experiment wrapper scripts smarter with...</em></p>
|
|
68
71
|
<h1 align="center" style="font-size: 40px;"> <a href="">Labtasker</a></h1>
|
|
69
|
-
<p align="center"><a href="https://fkcptlst.github.io/labtasker/latest/install/install/">Install</a> • <a href="https://fkcptlst.github.io/labtasker/latest/guide/basic/">Tutorial / Demo</a> • <a href="https://fkcptlst.github.io/labtasker/latest/">Documentation</a> • <a href="https://fkcptlst.github.io/labtasker/
|
|
72
|
+
<p align="center"><a href="https://fkcptlst.github.io/labtasker/latest/install/install/">Install</a> • <a href="https://fkcptlst.github.io/labtasker/latest/guide/basic/">Tutorial / Demo</a> • <a href="https://fkcptlst.github.io/labtasker/latest/">Documentation</a> • <a href="https://fkcptlst.github.io/labtasker/latest/faq/">FAQs</a> • <a href="https://github.com/fkcptlst/labtasker/releases">Releases</a></p>
|
|
70
73
|
|
|
71
74
|
<p align="center">
|
|
72
75
|
<img src="https://github.com/fkcptlst/labtasker/actions/workflows/unit-test-matrix.yml/badge.svg" alt="unit-test-matrix" />
|
|
@@ -99,7 +102,8 @@ such as sending emails on task failure.
|
|
|
99
102
|
|
|
100
103
|

|
|
101
104
|
|
|
102
|
-
For more detailed steps, please refer to the content in
|
|
105
|
+
For more detailed steps, please refer to the content in
|
|
106
|
+
the [Tutorial / Demo](https://fkcptlst.github.io/labtasker/latest/guide/basic/).
|
|
103
107
|
|
|
104
108
|
## ⚡️ Features
|
|
105
109
|
|
|
@@ -111,23 +115,31 @@ For more detailed steps, please refer to the content in the [Tutorial / Demo](ht
|
|
|
111
115
|
## 🔮 Supercharge Your ML Experiments with Labtasker
|
|
112
116
|
|
|
113
117
|
- ⚡️ **Effortless Parallelization:** Distribute tasks across multiple GPU workers with just a few lines of code.
|
|
114
|
-
- 🛡️ **Intelligent Failure Management:** Automatically capture exceptions, retry failed tasks, and maintain detailed
|
|
115
|
-
|
|
116
|
-
-
|
|
117
|
-
|
|
118
|
-
-
|
|
119
|
-
|
|
118
|
+
- 🛡️ **Intelligent Failure Management:** Automatically capture exceptions, retry failed tasks, and maintain detailed
|
|
119
|
+
error logs.
|
|
120
|
+
- 🔄 **Seamless Recovery:** Resume failed experiments with a single command - no more scavenging through logs or
|
|
121
|
+
directories.
|
|
122
|
+
- 🎯 **Real-time Prioritization:** Changed your mind about experiment settings? Instantly cancel, add, or reschedule
|
|
123
|
+
tasks without disrupting existing ones.
|
|
124
|
+
- 🤖 **Workflow Automation:** Set up smart event triggers for email notifications or task workflow based on FSM
|
|
125
|
+
transition events.
|
|
126
|
+
- 📊 **Streamlined Logging:** All stdout/stderr automatically organized in `.labtasker/logs` - zero configuration
|
|
127
|
+
required.
|
|
128
|
+
- 🧩 **Extensible Plugin System:** Create custom command combinations or leverage community plugins to extend
|
|
129
|
+
functionality.
|
|
120
130
|
|
|
121
131
|
## 🛠️ Installation
|
|
122
132
|
|
|
123
133
|
> [!NOTE]
|
|
124
134
|
> You need a running Labtasker server to use the client tools.
|
|
135
|
+
> Simply use the installed Python CLI `labtasker-server serve` or use docker-compose to deploy the server.
|
|
125
136
|
> See [deployment instructions](https://fkcptlst.github.io/labtasker/latest/install/deployment/).
|
|
126
137
|
|
|
127
|
-
### 1. Install
|
|
138
|
+
### 1. Install via PyPI
|
|
128
139
|
|
|
129
140
|
```bash
|
|
130
|
-
|
|
141
|
+
# Install with optional bundled plugins
|
|
142
|
+
pip install 'labtasker[plugins]'
|
|
131
143
|
```
|
|
132
144
|
|
|
133
145
|
### 2. Install the Latest Version from GitHub
|
|
@@ -138,6 +150,12 @@ pip install git+https://github.com/fkcptlst/labtasker.git
|
|
|
138
150
|
|
|
139
151
|
## 🚀 Quick Start
|
|
140
152
|
|
|
153
|
+
Use the following command to launch a labtasker server in the background:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
labtasker-server serve &
|
|
157
|
+
```
|
|
158
|
+
|
|
141
159
|
Use the following command to quickly setup a labtasker queue for your project:
|
|
142
160
|
|
|
143
161
|
```bash
|
|
@@ -146,6 +164,10 @@ labtasker init
|
|
|
146
164
|
|
|
147
165
|
Then, use `labtasker submit` to submit tasks and use `labtasker loop` to run tasks across any number of workers.
|
|
148
166
|
|
|
167
|
+
> [!TIP]
|
|
168
|
+
> If you think manually writing 2 scripts for submit and run is laborious, you can checkout the [labtasker-plugin-script-generate](https://github.com/fkcptlst/labtasker-plugin-script-generate).
|
|
169
|
+
> It automatically generate 2 scripts based on the script you provided.
|
|
170
|
+
|
|
149
171
|
## 📚 Documentation
|
|
150
172
|
|
|
151
173
|
For detailed information on demo, tutorial, deployment, usage, please refer to
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<p align="center"><em>Make your ML experiment wrapper scripts smarter with...</em></p>
|
|
2
2
|
<h1 align="center" style="font-size: 40px;"> <a href="">Labtasker</a></h1>
|
|
3
|
-
<p align="center"><a href="https://fkcptlst.github.io/labtasker/latest/install/install/">Install</a> • <a href="https://fkcptlst.github.io/labtasker/latest/guide/basic/">Tutorial / Demo</a> • <a href="https://fkcptlst.github.io/labtasker/latest/">Documentation</a> • <a href="https://fkcptlst.github.io/labtasker/
|
|
3
|
+
<p align="center"><a href="https://fkcptlst.github.io/labtasker/latest/install/install/">Install</a> • <a href="https://fkcptlst.github.io/labtasker/latest/guide/basic/">Tutorial / Demo</a> • <a href="https://fkcptlst.github.io/labtasker/latest/">Documentation</a> • <a href="https://fkcptlst.github.io/labtasker/latest/faq/">FAQs</a> • <a href="https://github.com/fkcptlst/labtasker/releases">Releases</a></p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<img src="https://github.com/fkcptlst/labtasker/actions/workflows/unit-test-matrix.yml/badge.svg" alt="unit-test-matrix" />
|
|
@@ -33,7 +33,8 @@ such as sending emails on task failure.
|
|
|
33
33
|
|
|
34
34
|

|
|
35
35
|
|
|
36
|
-
For more detailed steps, please refer to the content in
|
|
36
|
+
For more detailed steps, please refer to the content in
|
|
37
|
+
the [Tutorial / Demo](https://fkcptlst.github.io/labtasker/latest/guide/basic/).
|
|
37
38
|
|
|
38
39
|
## ⚡️ Features
|
|
39
40
|
|
|
@@ -45,23 +46,31 @@ For more detailed steps, please refer to the content in the [Tutorial / Demo](ht
|
|
|
45
46
|
## 🔮 Supercharge Your ML Experiments with Labtasker
|
|
46
47
|
|
|
47
48
|
- ⚡️ **Effortless Parallelization:** Distribute tasks across multiple GPU workers with just a few lines of code.
|
|
48
|
-
- 🛡️ **Intelligent Failure Management:** Automatically capture exceptions, retry failed tasks, and maintain detailed
|
|
49
|
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
-
|
|
53
|
-
|
|
49
|
+
- 🛡️ **Intelligent Failure Management:** Automatically capture exceptions, retry failed tasks, and maintain detailed
|
|
50
|
+
error logs.
|
|
51
|
+
- 🔄 **Seamless Recovery:** Resume failed experiments with a single command - no more scavenging through logs or
|
|
52
|
+
directories.
|
|
53
|
+
- 🎯 **Real-time Prioritization:** Changed your mind about experiment settings? Instantly cancel, add, or reschedule
|
|
54
|
+
tasks without disrupting existing ones.
|
|
55
|
+
- 🤖 **Workflow Automation:** Set up smart event triggers for email notifications or task workflow based on FSM
|
|
56
|
+
transition events.
|
|
57
|
+
- 📊 **Streamlined Logging:** All stdout/stderr automatically organized in `.labtasker/logs` - zero configuration
|
|
58
|
+
required.
|
|
59
|
+
- 🧩 **Extensible Plugin System:** Create custom command combinations or leverage community plugins to extend
|
|
60
|
+
functionality.
|
|
54
61
|
|
|
55
62
|
## 🛠️ Installation
|
|
56
63
|
|
|
57
64
|
> [!NOTE]
|
|
58
65
|
> You need a running Labtasker server to use the client tools.
|
|
66
|
+
> Simply use the installed Python CLI `labtasker-server serve` or use docker-compose to deploy the server.
|
|
59
67
|
> See [deployment instructions](https://fkcptlst.github.io/labtasker/latest/install/deployment/).
|
|
60
68
|
|
|
61
|
-
### 1. Install
|
|
69
|
+
### 1. Install via PyPI
|
|
62
70
|
|
|
63
71
|
```bash
|
|
64
|
-
|
|
72
|
+
# Install with optional bundled plugins
|
|
73
|
+
pip install 'labtasker[plugins]'
|
|
65
74
|
```
|
|
66
75
|
|
|
67
76
|
### 2. Install the Latest Version from GitHub
|
|
@@ -72,6 +81,12 @@ pip install git+https://github.com/fkcptlst/labtasker.git
|
|
|
72
81
|
|
|
73
82
|
## 🚀 Quick Start
|
|
74
83
|
|
|
84
|
+
Use the following command to launch a labtasker server in the background:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
labtasker-server serve &
|
|
88
|
+
```
|
|
89
|
+
|
|
75
90
|
Use the following command to quickly setup a labtasker queue for your project:
|
|
76
91
|
|
|
77
92
|
```bash
|
|
@@ -80,6 +95,10 @@ labtasker init
|
|
|
80
95
|
|
|
81
96
|
Then, use `labtasker submit` to submit tasks and use `labtasker loop` to run tasks across any number of workers.
|
|
82
97
|
|
|
98
|
+
> [!TIP]
|
|
99
|
+
> If you think manually writing 2 scripts for submit and run is laborious, you can checkout the [labtasker-plugin-script-generate](https://github.com/fkcptlst/labtasker-plugin-script-generate).
|
|
100
|
+
> It automatically generate 2 scripts based on the script you provided.
|
|
101
|
+
|
|
83
102
|
## 📚 Documentation
|
|
84
103
|
|
|
85
104
|
For detailed information on demo, tutorial, deployment, usage, please refer to
|
|
@@ -67,10 +67,10 @@ services:
|
|
|
67
67
|
- DB_HOST=mongodb
|
|
68
68
|
- DB_PORT=${DB_PORT:-27017}
|
|
69
69
|
- API_HOST=${API_HOST:-0.0.0.0}
|
|
70
|
-
- API_PORT=${API_PORT:-
|
|
70
|
+
- API_PORT=${API_PORT:-9321}
|
|
71
71
|
- PERIODIC_TASK_INTERVAL=${PERIODIC_TASK_INTERVAL:-30}
|
|
72
72
|
ports:
|
|
73
|
-
- "${API_PORT:-
|
|
73
|
+
- "${API_PORT:-9321}:${API_PORT:-9321}"
|
|
74
74
|
depends_on:
|
|
75
75
|
mongodb:
|
|
76
76
|
condition: service_healthy
|
|
@@ -206,7 +206,7 @@ class TaskFetchResponse(BaseResponseModel):
|
|
|
206
206
|
|
|
207
207
|
class TaskLsRequest(BaseRequestModel):
|
|
208
208
|
offset: int = Field(0, ge=0)
|
|
209
|
-
limit: int = Field(100,
|
|
209
|
+
limit: int = Field(100, gt=0, le=1000)
|
|
210
210
|
task_id: Optional[str] = None
|
|
211
211
|
task_name: Optional[str] = None
|
|
212
212
|
status: Optional[str] = Field(
|
|
@@ -268,7 +268,7 @@ class WorkerStatusUpdateRequest(BaseRequestModel):
|
|
|
268
268
|
|
|
269
269
|
class WorkerLsRequest(BaseRequestModel):
|
|
270
270
|
offset: int = Field(0, ge=0)
|
|
271
|
-
limit: int = Field(100,
|
|
271
|
+
limit: int = Field(100, gt=0, le=1000)
|
|
272
272
|
worker_id: Optional[str] = None
|
|
273
273
|
worker_name: Optional[str] = None
|
|
274
274
|
status: Optional[str] = Field(None, pattern=r"^(active|suspended|crashed)$")
|
|
@@ -83,7 +83,8 @@ def setup_endpoint_url() -> Tuple[str, bool]:
|
|
|
83
83
|
|
|
84
84
|
# 1. Prompt
|
|
85
85
|
url = InputPrompt(
|
|
86
|
-
"URL of your Labtasker server
|
|
86
|
+
"URL of your Labtasker server: ",
|
|
87
|
+
default_text="http://localhost:9321",
|
|
87
88
|
validator=validator,
|
|
88
89
|
).prompt()
|
|
89
90
|
url = str(HttpUrl(url))
|
|
@@ -186,7 +187,9 @@ def setup_queue(base_url, base_url_verified) -> Tuple[str, str]:
|
|
|
186
187
|
except LabtaskerHTTPStatusError:
|
|
187
188
|
pass
|
|
188
189
|
|
|
189
|
-
yes = ConfirmPrompt(
|
|
190
|
+
yes = ConfirmPrompt(
|
|
191
|
+
question="Would you like to create this queue?", default_choice=True
|
|
192
|
+
).prompt()
|
|
190
193
|
if yes:
|
|
191
194
|
try:
|
|
192
195
|
resp = create_queue(queue_name, password, client=client)
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
"""Implements `labtasker loop xxx`"""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
import os
|
|
5
|
-
import shlex
|
|
6
4
|
import subprocess
|
|
5
|
+
import sys
|
|
7
6
|
from collections import defaultdict
|
|
8
7
|
from typing import List, Optional
|
|
9
8
|
|
|
10
9
|
import typer
|
|
11
10
|
from typing_extensions import Annotated
|
|
12
11
|
|
|
13
|
-
import labtasker
|
|
14
|
-
import labtasker.client.core.context
|
|
15
12
|
from labtasker.client.cli.cli import app
|
|
16
13
|
from labtasker.client.core.cli_utils import (
|
|
17
14
|
cli_utils_decorator,
|
|
@@ -20,8 +17,9 @@ from labtasker.client.core.cli_utils import (
|
|
|
20
17
|
)
|
|
21
18
|
from labtasker.client.core.cmd_parser import cmd_interpolate
|
|
22
19
|
from labtasker.client.core.config import get_client_config
|
|
23
|
-
from labtasker.client.core.
|
|
24
|
-
from labtasker.client.core.
|
|
20
|
+
from labtasker.client.core.context import task_info
|
|
21
|
+
from labtasker.client.core.exceptions import CmdParserError, _LabtaskerJobFailed
|
|
22
|
+
from labtasker.client.core.job_runner import loop_run
|
|
25
23
|
from labtasker.client.core.logging import (
|
|
26
24
|
logger,
|
|
27
25
|
set_verbose,
|
|
@@ -56,9 +54,10 @@ def loop(
|
|
|
56
54
|
] = None,
|
|
57
55
|
option_cmd: str = typer.Option(
|
|
58
56
|
None,
|
|
59
|
-
"--
|
|
57
|
+
"--command",
|
|
58
|
+
"--command",
|
|
60
59
|
"-c",
|
|
61
|
-
help="
|
|
60
|
+
help="Specify the command to run with shell=True. Supports the same argument interpolation the same way as the positional argument. Except you need to quote the entire command.",
|
|
62
61
|
),
|
|
63
62
|
extra_filter: Optional[str] = typer.Option(
|
|
64
63
|
None,
|
|
@@ -99,13 +98,17 @@ def loop(
|
|
|
99
98
|
"""
|
|
100
99
|
if cmd and option_cmd:
|
|
101
100
|
raise typer.BadParameter(
|
|
102
|
-
"Only one of [CMD] and [--
|
|
101
|
+
"Only one of [CMD] and [--command] can be specified. Please use one of them."
|
|
103
102
|
)
|
|
104
103
|
|
|
105
|
-
cmd = cmd if cmd else
|
|
104
|
+
cmd = cmd if cmd else option_cmd
|
|
105
|
+
if not cmd and not sys.stdin.isatty():
|
|
106
|
+
# try reading multi-line cmd from stdin if shell mode
|
|
107
|
+
cmd = sys.stdin.read()
|
|
108
|
+
|
|
106
109
|
if not cmd:
|
|
107
110
|
raise typer.BadParameter(
|
|
108
|
-
"Command cannot be empty. Either specify via positional argument [CMD] or `--
|
|
111
|
+
"Command cannot be empty. Either specify via positional argument [CMD] or `--command`."
|
|
109
112
|
)
|
|
110
113
|
|
|
111
114
|
parsed_filter = parse_filter(extra_filter)
|
|
@@ -145,11 +148,16 @@ def loop(
|
|
|
145
148
|
)
|
|
146
149
|
logger.info(f"Prepared to run interpolated command: {interpolated_cmd}")
|
|
147
150
|
|
|
151
|
+
shell = False
|
|
152
|
+
if isinstance(interpolated_cmd, str):
|
|
153
|
+
shell = True
|
|
154
|
+
|
|
148
155
|
with subprocess.Popen(
|
|
149
156
|
args=interpolated_cmd,
|
|
150
157
|
stdout=subprocess.PIPE,
|
|
151
158
|
stderr=subprocess.PIPE,
|
|
152
159
|
text=True,
|
|
160
|
+
shell=shell,
|
|
153
161
|
) as process:
|
|
154
162
|
while True:
|
|
155
163
|
output = process.stdout.readline()
|
|
@@ -166,11 +174,11 @@ def loop(
|
|
|
166
174
|
|
|
167
175
|
process.wait()
|
|
168
176
|
if process.returncode != 0:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
177
|
+
raise _LabtaskerJobFailed(
|
|
178
|
+
"Job process finished with non-zero exit code."
|
|
179
|
+
)
|
|
172
180
|
|
|
173
|
-
logger.info(f"Task {
|
|
181
|
+
logger.info(f"Task {task_info().task_id} ended.")
|
|
174
182
|
|
|
175
183
|
run_cmd()
|
|
176
184
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import io
|
|
4
4
|
import json
|
|
5
5
|
import os
|
|
6
|
+
import sys
|
|
6
7
|
import tempfile
|
|
7
8
|
from functools import partial
|
|
8
9
|
from pathlib import Path
|
|
@@ -51,6 +52,10 @@ from labtasker.constants import Priority
|
|
|
51
52
|
app = typer.Typer()
|
|
52
53
|
|
|
53
54
|
|
|
55
|
+
class _ReEdit(Exception):
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
54
59
|
def commented_seq_from_dict_list(
|
|
55
60
|
entries: List[Dict[str, Any]],
|
|
56
61
|
) -> ruamel.yaml.CommentedSeq:
|
|
@@ -373,6 +378,10 @@ def update(
|
|
|
373
378
|
help="Updated values of fields. Specify multiple options via repeating `-u`. "
|
|
374
379
|
"E.g. `labtasker task update -u args.arg1=foo -u metadata.tag=test`",
|
|
375
380
|
),
|
|
381
|
+
limit: int = typer.Option(
|
|
382
|
+
1000,
|
|
383
|
+
help="Limit the number of tasks returned.",
|
|
384
|
+
),
|
|
376
385
|
offset: int = typer.Option(
|
|
377
386
|
0,
|
|
378
387
|
help="Initial offset for pagination (In case there are too many items for update, only 1000 results starting from offset is displayed. "
|
|
@@ -441,7 +450,7 @@ def update(
|
|
|
441
450
|
task_name=task_name,
|
|
442
451
|
status=status,
|
|
443
452
|
extra_filter=extra_filter,
|
|
444
|
-
limit=
|
|
453
|
+
limit=limit,
|
|
445
454
|
offset=offset,
|
|
446
455
|
).content
|
|
447
456
|
use_editor = not updates
|
|
@@ -495,6 +504,22 @@ def handle_editor_mode(old_tasks, readonly_fields, editor):
|
|
|
495
504
|
with open(temp_file_path, "r", encoding="utf-8") as temp_file:
|
|
496
505
|
modified = yaml.safe_load(temp_file)
|
|
497
506
|
|
|
507
|
+
# check if length match
|
|
508
|
+
if len(modified) != len(old_tasks_primitive):
|
|
509
|
+
raise _ReEdit(
|
|
510
|
+
"Number of tasks in the file should match the number of tasks in the original list. "
|
|
511
|
+
f"Expected {len(old_tasks_primitive)}, got {len(modified)}. "
|
|
512
|
+
"If you want to add/delete tasks, you should use `labtasker task submit` or `labtasker task delete`."
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
# check if task order match
|
|
516
|
+
for mod, old in zip(modified, old_tasks_primitive):
|
|
517
|
+
if mod["task_id"] != old["task_id"]:
|
|
518
|
+
raise _ReEdit(
|
|
519
|
+
"Task IDs in the modified file should match the task IDs in the original list in the same order. "
|
|
520
|
+
f"Expected {old['task_id']}, got {mod['task_id']}."
|
|
521
|
+
)
|
|
522
|
+
|
|
498
523
|
# Calculate diffs and create task updates
|
|
499
524
|
update_dicts = diff(
|
|
500
525
|
old_tasks_primitive, modified, readonly_fields=list(readonly_fields)
|
|
@@ -538,8 +563,8 @@ def handle_editor_mode(old_tasks, readonly_fields, editor):
|
|
|
538
563
|
# No validation errors, we can break the loop
|
|
539
564
|
break
|
|
540
565
|
|
|
541
|
-
except yaml.error.YAMLError as e:
|
|
542
|
-
stderr_console.print(f"[bold red]
|
|
566
|
+
except (_ReEdit, yaml.error.YAMLError) as e:
|
|
567
|
+
stderr_console.print(f"[bold red]Error:[/bold red] {str(e)}")
|
|
543
568
|
if not typer.confirm("Continue to edit?", default=True):
|
|
544
569
|
raise typer.Abort()
|
|
545
570
|
# Continue the loop with the current file state
|
|
@@ -607,7 +632,9 @@ def display_updated_tasks(updated_tasks, update_dicts):
|
|
|
607
632
|
# If replace_fields is empty or not available, try to infer from the update
|
|
608
633
|
if not modified_fields:
|
|
609
634
|
modified_fields = [
|
|
610
|
-
k
|
|
635
|
+
k
|
|
636
|
+
for k in update_dict.keys()
|
|
637
|
+
if k not in ("_id", "task_id", "replace_fields")
|
|
611
638
|
]
|
|
612
639
|
|
|
613
640
|
# Add comment for each modified field
|
|
@@ -634,7 +661,10 @@ def display_updated_tasks(updated_tasks, update_dicts):
|
|
|
634
661
|
@app.command()
|
|
635
662
|
@cli_utils_decorator
|
|
636
663
|
def delete(
|
|
637
|
-
|
|
664
|
+
task_ids: List[str] = typer.Argument(
|
|
665
|
+
... if sys.stdin.isatty() else None,
|
|
666
|
+
help="IDs of the task to delete.",
|
|
667
|
+
),
|
|
638
668
|
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
|
|
639
669
|
):
|
|
640
670
|
"""
|
|
@@ -647,14 +677,17 @@ def delete(
|
|
|
647
677
|
labtasker task delete task-123
|
|
648
678
|
labtasker task delete task-123 --yes # Skip confirmation
|
|
649
679
|
"""
|
|
680
|
+
if task_ids is None: # read from stdin to support piping
|
|
681
|
+
task_ids = [line.strip() for line in sys.stdin.readlines() if line.strip()]
|
|
650
682
|
if not yes:
|
|
651
683
|
typer.confirm(
|
|
652
|
-
f"Are you sure you want to delete
|
|
684
|
+
f"Are you sure you want to delete tasks '{task_ids}'?",
|
|
653
685
|
abort=True,
|
|
654
686
|
)
|
|
655
687
|
try:
|
|
656
|
-
|
|
657
|
-
|
|
688
|
+
for task_id in task_ids:
|
|
689
|
+
delete_task(task_id=task_id)
|
|
690
|
+
stdout_console.print(f"Task {task_id} deleted.")
|
|
658
691
|
except LabtaskerHTTPStatusError as e:
|
|
659
692
|
if e.response.status_code == HTTP_404_NOT_FOUND:
|
|
660
693
|
raise typer.BadParameter("Task not found")
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Manage workers (CRUD operations)."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import sys
|
|
4
5
|
from functools import partial
|
|
5
|
-
from typing import Optional
|
|
6
|
+
from typing import List, Optional
|
|
6
7
|
|
|
7
8
|
import click
|
|
8
9
|
import typer
|
|
@@ -206,7 +207,9 @@ def report(
|
|
|
206
207
|
@app.command()
|
|
207
208
|
@cli_utils_decorator
|
|
208
209
|
def delete(
|
|
209
|
-
|
|
210
|
+
worker_ids: List[str] = typer.Argument(
|
|
211
|
+
... if sys.stdin.isatty() else None, help="IDs of the worker to delete."
|
|
212
|
+
),
|
|
210
213
|
cascade_update: bool = typer.Option(
|
|
211
214
|
True,
|
|
212
215
|
help="Whether to cascade set the worker id of relevant tasks to None",
|
|
@@ -216,14 +219,17 @@ def delete(
|
|
|
216
219
|
"""
|
|
217
220
|
Delete a worker by worker_id.
|
|
218
221
|
"""
|
|
222
|
+
if worker_ids is None: # read from stdin to support piping
|
|
223
|
+
worker_ids = [line.strip() for line in sys.stdin.readlines() if line.strip()]
|
|
219
224
|
if not yes:
|
|
220
225
|
typer.confirm(
|
|
221
|
-
f"Are you sure you want to delete worker '{
|
|
226
|
+
f"Are you sure you want to delete worker '{worker_ids}'?",
|
|
222
227
|
abort=True,
|
|
223
228
|
)
|
|
224
229
|
try:
|
|
225
|
-
|
|
226
|
-
|
|
230
|
+
for worker_id in worker_ids:
|
|
231
|
+
delete_worker(worker_id=worker_id, cascade_update=cascade_update)
|
|
232
|
+
stdout_console.print(f"Worker {worker_id} deleted.")
|
|
227
233
|
except LabtaskerHTTPStatusError as e:
|
|
228
234
|
if e.response.status_code == HTTP_404_NOT_FOUND:
|
|
229
235
|
raise typer.BadParameter("Worker not found")
|
|
@@ -12,7 +12,7 @@ import httpx
|
|
|
12
12
|
import pydantic
|
|
13
13
|
import typer
|
|
14
14
|
import yaml
|
|
15
|
-
from noneprompt import
|
|
15
|
+
from noneprompt import Choice, ListPrompt
|
|
16
16
|
from prompt_toolkit import Application
|
|
17
17
|
from prompt_toolkit.layout import Float, FloatContainer, Layout, Window
|
|
18
18
|
from prompt_toolkit.layout.controls import FormattedTextControl
|
|
@@ -543,11 +543,17 @@ class TimedListPrompt(ListPrompt):
|
|
|
543
543
|
"""A ListPrompt with countdown and timeout functionality."""
|
|
544
544
|
|
|
545
545
|
def __init__(
|
|
546
|
-
self,
|
|
546
|
+
self,
|
|
547
|
+
*args,
|
|
548
|
+
timeout: int = 10,
|
|
549
|
+
default: Optional[Choice] = None,
|
|
550
|
+
keyboard_interrupt_default: Optional[Choice] = None,
|
|
551
|
+
**kwargs,
|
|
547
552
|
):
|
|
548
553
|
super().__init__(*args, **kwargs)
|
|
549
554
|
self.timeout = timeout
|
|
550
555
|
self.default = default
|
|
556
|
+
self.keyboard_interrupt_default = keyboard_interrupt_default
|
|
551
557
|
self.countdown_text = [
|
|
552
558
|
("class:countdown", "TIMER: "),
|
|
553
559
|
("class:countdown-emphasis", f"{timeout}"),
|
|
@@ -604,32 +610,32 @@ class TimedListPrompt(ListPrompt):
|
|
|
604
610
|
app = self._build_application(no_ansi=False, style=Style([]))
|
|
605
611
|
countdown_task = asyncio.create_task(self._run_countdown(app))
|
|
606
612
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
except (asyncio.CancelledError, KeyboardInterrupt):
|
|
616
|
-
return None
|
|
613
|
+
result = await app.run_async()
|
|
614
|
+
countdown_task.cancel()
|
|
615
|
+
if result is None: # timed out
|
|
616
|
+
return self.default
|
|
617
|
+
elif isinstance(result, type(...)): # keyboard interrupt
|
|
618
|
+
return self.keyboard_interrupt_default
|
|
619
|
+
else:
|
|
620
|
+
return result
|
|
617
621
|
|
|
618
622
|
|
|
619
623
|
def make_a_choice(
|
|
620
|
-
question: str,
|
|
624
|
+
question: str,
|
|
625
|
+
options: List[Choice],
|
|
626
|
+
default: Choice,
|
|
627
|
+
keyboard_interrupt_default: Choice,
|
|
628
|
+
timeout: int = 10,
|
|
621
629
|
) -> Optional[Choice]:
|
|
622
630
|
"""Helper function to create and run a timed choice prompt."""
|
|
623
631
|
prompt = TimedListPrompt(
|
|
624
|
-
question,
|
|
632
|
+
question,
|
|
633
|
+
choices=options,
|
|
634
|
+
timeout=timeout,
|
|
635
|
+
default=default,
|
|
636
|
+
keyboard_interrupt_default=keyboard_interrupt_default,
|
|
625
637
|
)
|
|
626
|
-
|
|
627
|
-
result = asyncio.run(prompt.prompt_async())
|
|
628
|
-
if isinstance(result, type(...)):
|
|
629
|
-
return default
|
|
630
|
-
return result
|
|
631
|
-
except KeyboardInterrupt:
|
|
632
|
-
return default
|
|
638
|
+
return asyncio.run(prompt.prompt_async())
|
|
633
639
|
|
|
634
640
|
|
|
635
641
|
ls_format_iter = {
|