labtasker 0.2.3__tar.gz → 0.2.5__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.5/.gitmodules +8 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/.pre-commit-config.yaml +2 -2
- {labtasker-0.2.3 → labtasker-0.2.5}/PKG-INFO +35 -21
- {labtasker-0.2.3 → labtasker-0.2.5}/README.md +27 -15
- {labtasker-0.2.3 → labtasker-0.2.5}/docker-compose.yml +1 -1
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/__init__.py +1 -1
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/api_models.py +2 -2
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/cli/loop.py +29 -15
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/cli/task.py +41 -8
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/cli/worker.py +11 -5
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/cli_utils.py +27 -21
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/cmd_parser/parser.py +16 -11
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/config.py +2 -2
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/exceptions.py +12 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/job_runner.py +49 -7
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/logging.py +1 -1
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/database.py +58 -24
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/endpoints.py +5 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/pyproject.toml +9 -7
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/conftest.py +10 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_cli/test_loop.py +22 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_cli/test_task.py +18 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_runner_with_resolver.py +16 -0
- labtasker-0.2.3/.gitmodules +0 -4
- {labtasker-0.2.3 → labtasker-0.2.5}/.coveragerc +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/.dockerignore +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/.flake8 +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/.gitattributes +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/.gitignore +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/.pytest.ini +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/.python-version +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/Dockerfile +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/LICENSE +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/MANIFEST.in +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/PRIVACY.md +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/advanced/custom_resolver/submit_job.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/advanced/custom_resolver/w.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/advanced/custom_resolver/wo.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/advanced/event_system/email_on_task_failure.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/advanced/event_system/send_email.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/advanced/event_system/sim_unstable_job.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/advanced/event_system/submit.sh +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/basic/bash_demo/job_main.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/basic/bash_demo/run_job.sh +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/basic/bash_demo/submit_job.sh +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/basic/python_demo/run_job.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/demo/basic/python_demo/submit_job.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/docker/mongodb/init.d/init-keyfile.sh +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/docker/mongodb/post-init.d/init-mongo.sh +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/__main__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/cli/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/cli/cli.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/cli/config.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/cli/event.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/cli/init.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/cli/queue.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/client_api.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/api.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/cmd_parser/LabCmd.g4 +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/cmd_parser/LabCmdLexer.g4 +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/cmd_parser/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/cmd_parser/generated/LabCmd.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/cmd_parser/generated/LabCmdLexer.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/cmd_parser/generated/LabCmdListener.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/cmd_parser/generated/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/context.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/events.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/heartbeat.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/paths.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/plugin_utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/query_transpiler.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/resolver/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/resolver/models.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/resolver/utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/core/version_checker.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/templates/labtasker_root/.gitignore +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/templates/labtasker_root/client.toml +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/client/templates/labtasker_root/logs/.gitkeep +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/constants.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/filtering.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/security.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/cli.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/config.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/db_utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/dependencies.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/embedded_db.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/event_manager.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/fsm.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/server/logging.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker/utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/labtasker.egg-info/SOURCES.txt +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/script_tests/test_ban_datetime_now/allowed.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/script_tests/test_ban_datetime_now/disallowed.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/script_tests/test_ban_datetime_now/test_ban.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/scripts/asciinema_adjust_timestamp.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/scripts/check_version.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/scripts/datetime_now_checker.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/server.example.env +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/setup.cfg +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/conftest.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/demo_pager_iterator.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/dummy_jobs/job_1.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/fixtures/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/fixtures/database/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/fixtures/database/mock.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/fixtures/database/real.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/fixtures/logging.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/fixtures/mock_datetime_now.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/fixtures/server/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/fixtures/server/async_app.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/fixtures/server/sync_app.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_api_models.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_cli/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_cli/conftest.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_cli/test_basic.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_cli/test_config.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_cli/test_event.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_cli/test_init.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_cli/test_queue.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_cli/test_worker.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_cli_utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_event/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_event/test_concurrency_job_flow_event.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_event/test_event_listener_basic.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_event/test_various_actions.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_event/utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_heartbeat.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_job_runner.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_logging.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_loop_internal_error_handler.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_pager_iterator.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_parser.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_query_transpiler/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_query_transpiler/conftest.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_query_transpiler/test_behavior.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_query_transpiler/test_matching.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_query_transpiler/test_utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_query_transpiler/utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_resolver.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_runner_concurrency/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_runner_concurrency/run_concurrent.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_runner_concurrency/test_runner_concurrency_success_failure.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_runner_concurrency/test_runner_high_concurrency.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_runner_timeout/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_runner_timeout/conftest.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_runner_timeout/test_job_runner_timeout.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_runner_timeout/test_job_runner_with_resolver_timeout.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_server_notification_and_client_version.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_client/test_core/test_version_checker.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_filtering/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_filtering/exception_utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_filtering/test_exception_filtering.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_mock_time.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_security.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/conftest.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_database/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_database/conftest.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_database/test_database_basic.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_database/test_fetch_extra_filter.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_database/test_query_dict_to_mongo_filter.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_database/test_required_field_fetching.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_db_utils/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_db_utils/test_arg_match.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_db_utils/test_keys_to_query_dict.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_db_utils/test_keys_to_query_dict_deepest.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_db_utils/test_keys_to_query_dict_topmost.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_embedded_db.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_endpoint/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_endpoint/test_event_basic.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_endpoint/test_server.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_endpoint/test_server_async.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_endpoint/test_server_async_ping.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_fsm.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_server/test_get_verified_queue_dependency.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_utils/__init__.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/test_utils/test_utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/tests/utils.py +0 -0
- {labtasker-0.2.3 → labtasker-0.2.5}/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/luocfprime/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/luocfprime/labtasker-plugin-script-generate.git
|
|
8
|
+
branch = main
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: labtasker
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
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
|
|
7
|
-
Project-URL: Homepage, https://github.com/
|
|
8
|
-
Project-URL: Documentation, https://
|
|
9
|
-
Project-URL: Repository, https://github.com/
|
|
7
|
+
Project-URL: Homepage, https://github.com/luocfprime/labtasker
|
|
8
|
+
Project-URL: Documentation, https://luocfprime.github.io/labtasker
|
|
9
|
+
Project-URL: Repository, https://github.com/luocfprime/labtasker.git
|
|
10
10
|
Classifier: Intended Audience :: Science/Research
|
|
11
11
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
@@ -42,6 +42,7 @@ Requires-Dist: stamina<26.0.0,>=25.1.0
|
|
|
42
42
|
Requires-Dist: noneprompt<0.2.0,>=0.1.9
|
|
43
43
|
Requires-Dist: mongomock<4.4.0,>=4.3.0
|
|
44
44
|
Requires-Dist: jsonpickle<5.0.0,>=4.0.2
|
|
45
|
+
Requires-Dist: mslex<2.0.0,>=1.3.0
|
|
45
46
|
Provides-Extra: dev
|
|
46
47
|
Requires-Dist: pytest<9.0.0,>=8.0.0; extra == "dev"
|
|
47
48
|
Requires-Dist: pytest-cov<7.0.0,>=5.0.0; extra == "dev"
|
|
@@ -52,9 +53,9 @@ 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"
|
|
59
60
|
Requires-Dist: pytest-sugar<2.0.0,>=1.0.0; extra == "dev"
|
|
60
61
|
Provides-Extra: doc
|
|
@@ -63,15 +64,16 @@ Requires-Dist: mkdocs-glightbox<0.5.0,>=0.4.0; extra == "doc"
|
|
|
63
64
|
Requires-Dist: mike<2.2.0,>=2.1.3; extra == "doc"
|
|
64
65
|
Provides-Extra: plugins
|
|
65
66
|
Requires-Dist: labtasker-plugin-task-count; extra == "plugins"
|
|
67
|
+
Requires-Dist: labtasker-plugin-script-generate; extra == "plugins"
|
|
66
68
|
Dynamic: license-file
|
|
67
69
|
|
|
68
70
|
<p align="center"><em>Make your ML experiment wrapper scripts smarter with...</em></p>
|
|
69
71
|
<h1 align="center" style="font-size: 40px;"> <a href="">Labtasker</a></h1>
|
|
70
|
-
<p align="center"><a href="https://
|
|
72
|
+
<p align="center"><a href="https://luocfprime.github.io/labtasker/latest/install/install/">Install</a> • <a href="https://luocfprime.github.io/labtasker/latest/guide/basic/">Tutorial / Demo</a> • <a href="https://luocfprime.github.io/labtasker/latest/">Documentation</a> • <a href="https://luocfprime.github.io/labtasker/latest/faq/">FAQs</a> • <a href="https://github.com/luocfprime/labtasker/releases">Releases</a></p>
|
|
71
73
|
|
|
72
74
|
<p align="center">
|
|
73
|
-
<img src="https://github.com/
|
|
74
|
-
<a href="https://codecov.io/gh/
|
|
75
|
+
<img src="https://github.com/luocfprime/labtasker/actions/workflows/unit-test-matrix.yml/badge.svg" alt="unit-test-matrix" />
|
|
76
|
+
<a href="https://codecov.io/gh/luocfprime/labtasker"><img src="https://codecov.io/gh/luocfprime/labtasker/graph/badge.svg?token=KQFBV3QRPY" alt="codecov" /></a>
|
|
75
77
|
<img src="https://img.shields.io/badge/Python-3.8%20|%203.9%20|%203.10%20|%203.11%20|%203.12%20|%203.13-blue" alt="Python version" />
|
|
76
78
|
<a href="https://pypi.org/project/labtasker/"><img src="https://img.shields.io/pypi/v/labtasker" alt="PyPI" /></a>
|
|
77
79
|
</p>
|
|
@@ -89,7 +91,7 @@ parallelization, dynamic task prioritization, failure handling, halfway resume,
|
|
|
89
91
|
|
|
90
92
|

|
|
91
93
|
|
|
92
|
-
🐳 For detailed examples and concepts, check out the [documentation](https://
|
|
94
|
+
🐳 For detailed examples and concepts, check out the [documentation](https://luocfprime.github.io/labtasker/).
|
|
93
95
|
|
|
94
96
|
## 🧪️ A Quick Demo
|
|
95
97
|
|
|
@@ -100,7 +102,8 @@ such as sending emails on task failure.
|
|
|
100
102
|
|
|
101
103
|

|
|
102
104
|
|
|
103
|
-
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://luocfprime.github.io/labtasker/latest/guide/basic/).
|
|
104
107
|
|
|
105
108
|
## ⚡️ Features
|
|
106
109
|
|
|
@@ -112,30 +115,37 @@ For more detailed steps, please refer to the content in the [Tutorial / Demo](ht
|
|
|
112
115
|
## 🔮 Supercharge Your ML Experiments with Labtasker
|
|
113
116
|
|
|
114
117
|
- ⚡️ **Effortless Parallelization:** Distribute tasks across multiple GPU workers with just a few lines of code.
|
|
115
|
-
- 🛡️ **Intelligent Failure Management:** Automatically capture exceptions, retry failed tasks, and maintain detailed
|
|
116
|
-
|
|
117
|
-
-
|
|
118
|
-
|
|
119
|
-
-
|
|
120
|
-
|
|
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.
|
|
121
130
|
|
|
122
131
|
## 🛠️ Installation
|
|
123
132
|
|
|
124
133
|
> [!NOTE]
|
|
125
134
|
> You need a running Labtasker server to use the client tools.
|
|
126
135
|
> Simply use the installed Python CLI `labtasker-server serve` or use docker-compose to deploy the server.
|
|
127
|
-
> See [deployment instructions](https://
|
|
136
|
+
> See [deployment instructions](https://luocfprime.github.io/labtasker/latest/install/deployment/).
|
|
128
137
|
|
|
129
138
|
### 1. Install via PyPI
|
|
130
139
|
|
|
131
140
|
```bash
|
|
132
|
-
|
|
141
|
+
# Install with optional bundled plugins
|
|
142
|
+
pip install 'labtasker[plugins]'
|
|
133
143
|
```
|
|
134
144
|
|
|
135
145
|
### 2. Install the Latest Version from GitHub
|
|
136
146
|
|
|
137
147
|
```bash
|
|
138
|
-
pip install git+https://github.com/
|
|
148
|
+
pip install git+https://github.com/luocfprime/labtasker.git
|
|
139
149
|
```
|
|
140
150
|
|
|
141
151
|
## 🚀 Quick Start
|
|
@@ -154,10 +164,14 @@ labtasker init
|
|
|
154
164
|
|
|
155
165
|
Then, use `labtasker submit` to submit tasks and use `labtasker loop` to run tasks across any number of workers.
|
|
156
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/luocfprime/labtasker-plugin-script-generate).
|
|
169
|
+
> It automatically generate 2 scripts based on the script you provided.
|
|
170
|
+
|
|
157
171
|
## 📚 Documentation
|
|
158
172
|
|
|
159
173
|
For detailed information on demo, tutorial, deployment, usage, please refer to
|
|
160
|
-
the [documentation](https://
|
|
174
|
+
the [documentation](https://luocfprime.github.io/labtasker/).
|
|
161
175
|
|
|
162
176
|
## 🔒 License
|
|
163
177
|
|
|
@@ -1,10 +1,10 @@
|
|
|
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://
|
|
3
|
+
<p align="center"><a href="https://luocfprime.github.io/labtasker/latest/install/install/">Install</a> • <a href="https://luocfprime.github.io/labtasker/latest/guide/basic/">Tutorial / Demo</a> • <a href="https://luocfprime.github.io/labtasker/latest/">Documentation</a> • <a href="https://luocfprime.github.io/labtasker/latest/faq/">FAQs</a> • <a href="https://github.com/luocfprime/labtasker/releases">Releases</a></p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<img src="https://github.com/
|
|
7
|
-
<a href="https://codecov.io/gh/
|
|
6
|
+
<img src="https://github.com/luocfprime/labtasker/actions/workflows/unit-test-matrix.yml/badge.svg" alt="unit-test-matrix" />
|
|
7
|
+
<a href="https://codecov.io/gh/luocfprime/labtasker"><img src="https://codecov.io/gh/luocfprime/labtasker/graph/badge.svg?token=KQFBV3QRPY" alt="codecov" /></a>
|
|
8
8
|
<img src="https://img.shields.io/badge/Python-3.8%20|%203.9%20|%203.10%20|%203.11%20|%203.12%20|%203.13-blue" alt="Python version" />
|
|
9
9
|
<a href="https://pypi.org/project/labtasker/"><img src="https://img.shields.io/pypi/v/labtasker" alt="PyPI" /></a>
|
|
10
10
|
</p>
|
|
@@ -22,7 +22,7 @@ parallelization, dynamic task prioritization, failure handling, halfway resume,
|
|
|
22
22
|
|
|
23
23
|

|
|
24
24
|
|
|
25
|
-
🐳 For detailed examples and concepts, check out the [documentation](https://
|
|
25
|
+
🐳 For detailed examples and concepts, check out the [documentation](https://luocfprime.github.io/labtasker/).
|
|
26
26
|
|
|
27
27
|
## 🧪️ A Quick Demo
|
|
28
28
|
|
|
@@ -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://luocfprime.github.io/labtasker/latest/guide/basic/).
|
|
37
38
|
|
|
38
39
|
## ⚡️ Features
|
|
39
40
|
|
|
@@ -45,30 +46,37 @@ 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.
|
|
59
66
|
> Simply use the installed Python CLI `labtasker-server serve` or use docker-compose to deploy the server.
|
|
60
|
-
> See [deployment instructions](https://
|
|
67
|
+
> See [deployment instructions](https://luocfprime.github.io/labtasker/latest/install/deployment/).
|
|
61
68
|
|
|
62
69
|
### 1. Install via PyPI
|
|
63
70
|
|
|
64
71
|
```bash
|
|
65
|
-
|
|
72
|
+
# Install with optional bundled plugins
|
|
73
|
+
pip install 'labtasker[plugins]'
|
|
66
74
|
```
|
|
67
75
|
|
|
68
76
|
### 2. Install the Latest Version from GitHub
|
|
69
77
|
|
|
70
78
|
```bash
|
|
71
|
-
pip install git+https://github.com/
|
|
79
|
+
pip install git+https://github.com/luocfprime/labtasker.git
|
|
72
80
|
```
|
|
73
81
|
|
|
74
82
|
## 🚀 Quick Start
|
|
@@ -87,10 +95,14 @@ labtasker init
|
|
|
87
95
|
|
|
88
96
|
Then, use `labtasker submit` to submit tasks and use `labtasker loop` to run tasks across any number of workers.
|
|
89
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/luocfprime/labtasker-plugin-script-generate).
|
|
100
|
+
> It automatically generate 2 scripts based on the script you provided.
|
|
101
|
+
|
|
90
102
|
## 📚 Documentation
|
|
91
103
|
|
|
92
104
|
For detailed information on demo, tutorial, deployment, usage, please refer to
|
|
93
|
-
the [documentation](https://
|
|
105
|
+
the [documentation](https://luocfprime.github.io/labtasker/).
|
|
94
106
|
|
|
95
107
|
## 🔒 License
|
|
96
108
|
|
|
@@ -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)$")
|
|
@@ -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,15 @@ 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.",
|
|
61
|
+
),
|
|
62
|
+
executable: Optional[str] = typer.Option(
|
|
63
|
+
None,
|
|
64
|
+
"--executable",
|
|
65
|
+
help="Specify the shell executable to run the command with.",
|
|
62
66
|
),
|
|
63
67
|
extra_filter: Optional[str] = typer.Option(
|
|
64
68
|
None,
|
|
@@ -99,13 +103,17 @@ def loop(
|
|
|
99
103
|
"""
|
|
100
104
|
if cmd and option_cmd:
|
|
101
105
|
raise typer.BadParameter(
|
|
102
|
-
"Only one of [CMD] and [--
|
|
106
|
+
"Only one of [CMD] and [--command] can be specified. Please use one of them."
|
|
103
107
|
)
|
|
104
108
|
|
|
105
|
-
cmd = cmd if cmd else
|
|
109
|
+
cmd = cmd if cmd else option_cmd
|
|
110
|
+
if not cmd and not sys.stdin.isatty():
|
|
111
|
+
# try reading multi-line cmd from stdin if shell mode
|
|
112
|
+
cmd = sys.stdin.read()
|
|
113
|
+
|
|
106
114
|
if not cmd:
|
|
107
115
|
raise typer.BadParameter(
|
|
108
|
-
"Command cannot be empty. Either specify via positional argument [CMD] or `--
|
|
116
|
+
"Command cannot be empty. Either specify via positional argument [CMD] or `--command`."
|
|
109
117
|
)
|
|
110
118
|
|
|
111
119
|
parsed_filter = parse_filter(extra_filter)
|
|
@@ -145,11 +153,17 @@ def loop(
|
|
|
145
153
|
)
|
|
146
154
|
logger.info(f"Prepared to run interpolated command: {interpolated_cmd}")
|
|
147
155
|
|
|
156
|
+
use_shell = False
|
|
157
|
+
if isinstance(interpolated_cmd, str):
|
|
158
|
+
use_shell = True
|
|
159
|
+
|
|
148
160
|
with subprocess.Popen(
|
|
149
161
|
args=interpolated_cmd,
|
|
150
162
|
stdout=subprocess.PIPE,
|
|
151
163
|
stderr=subprocess.PIPE,
|
|
152
164
|
text=True,
|
|
165
|
+
executable=executable,
|
|
166
|
+
shell=use_shell,
|
|
153
167
|
) as process:
|
|
154
168
|
while True:
|
|
155
169
|
output = process.stdout.readline()
|
|
@@ -166,11 +180,11 @@ def loop(
|
|
|
166
180
|
|
|
167
181
|
process.wait()
|
|
168
182
|
if process.returncode != 0:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
183
|
+
raise _LabtaskerJobFailed(
|
|
184
|
+
"Job process finished with non-zero exit code."
|
|
185
|
+
)
|
|
172
186
|
|
|
173
|
-
logger.info(f"Task {
|
|
187
|
+
logger.info(f"Task {task_info().task_id} ended.")
|
|
174
188
|
|
|
175
189
|
run_cmd()
|
|
176
190
|
|
|
@@ -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 = {
|