labtasker 0.2.11__tar.gz → 0.2.12__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.11 → labtasker-0.2.12}/PKG-INFO +1 -1
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/__init__.py +1 -1
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/cli/worker.py +7 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/api.py +9 -2
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/heartbeat.py +16 -5
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/job_runner.py +3 -1
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/database.py +41 -11
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/endpoints.py +5 -7
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/fsm.py +40 -3
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker.egg-info/SOURCES.txt +2 -1
- {labtasker-0.2.11 → labtasker-0.2.12}/pyproject.toml +1 -2
- labtasker-0.2.12/tests/test_client/test_core/test_heartbeat_e2e.py +73 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_fsm.py +1 -1
- {labtasker-0.2.11 → labtasker-0.2.12}/LICENSE +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/MANIFEST.in +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/README.md +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/__main__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/api_models.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/cli/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/cli/cli.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/cli/config.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/cli/event.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/cli/init.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/cli/loop.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/cli/queue.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/cli/task.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/client_api.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cli_utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/LabCmd.g4 +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/LabCmdLexer.g4 +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/generated/LabCmd.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/generated/LabCmdLexer.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/generated/LabCmdListener.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/generated/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/parser.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/config.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/context.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/events.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/exceptions.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/logging.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/pager.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/paths.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/plugin_utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/query_transpiler.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/resolver/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/resolver/models.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/resolver/utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/version_checker.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/templates/labtasker_root/.gitignore +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/templates/labtasker_root/client.toml +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/templates/labtasker_root/logs/.gitkeep +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/constants.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/filtering.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/security.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/cli.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/config.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/db_utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/dependencies.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/embedded_db.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/event_manager.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/server/logging.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/README.txt +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/BufferedTokenStream.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/CommonTokenFactory.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/CommonTokenStream.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/FileStream.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/InputStream.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/IntervalSet.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/LL1Analyzer.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/Lexer.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/ListTokenSource.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/Parser.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/ParserInterpreter.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/ParserRuleContext.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/PredictionContext.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/Recognizer.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/RuleContext.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/StdinStream.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/Token.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/TokenStreamRewriter.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/Utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/_pygrun.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/ATN.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/ATNConfig.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/ATNConfigSet.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/ATNDeserializationOptions.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/ATNDeserializer.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/ATNSimulator.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/ATNState.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/ATNType.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/LexerATNSimulator.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/LexerAction.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/LexerActionExecutor.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/ParserATNSimulator.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/PredictionMode.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/SemanticContext.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/Transition.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/atn/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/dfa/DFA.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/dfa/DFASerializer.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/dfa/DFAState.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/dfa/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/error/DiagnosticErrorListener.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/error/ErrorListener.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/error/ErrorStrategy.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/error/Errors.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/error/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/tree/Chunk.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/tree/ParseTreeMatch.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/tree/ParseTreePattern.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/tree/ParseTreePatternMatcher.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/tree/RuleTagToken.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/tree/TokenTagToken.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/tree/Tree.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/tree/Trees.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/tree/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/xpath/XPath.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/xpath/XPathLexer.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/antlr4/xpath/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/labtasker/vendor/vendor.txt +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/setup.cfg +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/conftest.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/demo_pager_iterator.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/dummy_jobs/job_1.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/fixtures/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/fixtures/database/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/fixtures/database/mock.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/fixtures/database/real.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/fixtures/logging.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/fixtures/mock_datetime_now.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/fixtures/server/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/fixtures/server/async_app.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/fixtures/server/sync_app.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_api_models.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/conftest.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_cli/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_cli/conftest.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_cli/test_basic.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_cli/test_config.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_cli/test_event.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_cli/test_init.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_cli/test_loop.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_cli/test_queue.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_cli/test_task.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_cli/test_worker.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_cli_utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_event/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_event/test_concurrency_job_flow_event.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_event/test_event_listener_basic.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_event/test_event_listener_entity_data.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_event/test_various_actions.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_event/utils.py +0 -0
- /labtasker-0.2.11/tests/test_client/test_core/test_heartbeat.py → /labtasker-0.2.12/tests/test_client/test_core/test_heartbeat_unit.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_job_runner.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_logging.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_loop_internal_error_handler.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_pager_iterator.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_parser.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_query_transpiler/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_query_transpiler/conftest.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_query_transpiler/test_behavior.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_query_transpiler/test_matching.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_query_transpiler/test_utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_query_transpiler/utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_resolver.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_runner_concurrency/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_runner_concurrency/run_concurrent.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_runner_concurrency/test_runner_concurrency_success_failure.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_runner_concurrency/test_runner_high_concurrency.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_runner_timeout/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_runner_timeout/conftest.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_runner_timeout/test_job_runner_timeout.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_runner_timeout/test_job_runner_with_resolver_timeout.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_runner_with_resolver.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_server_notification_and_client_version.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_client/test_core/test_version_checker.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_filtering/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_filtering/exception_utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_filtering/test_exception_filtering.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_mock_time.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_security.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/conftest.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_database/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_database/conftest.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_database/test_database_basic.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_database/test_fetch_extra_filter.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_database/test_query_dict_to_mongo_filter.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_database/test_required_field_fetching.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_db_utils/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_db_utils/test_arg_match.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_db_utils/test_keys_to_query_dict.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_db_utils/test_keys_to_query_dict_deepest.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_db_utils/test_keys_to_query_dict_topmost.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_embedded_db.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_endpoint/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_endpoint/test_event_basic.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_endpoint/test_server.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_endpoint/test_server_async.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_endpoint/test_server_async_ping.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_server/test_get_verified_queue_dependency.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_utils/__init__.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/test_utils/test_utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tests/utils.py +0 -0
- {labtasker-0.2.11 → labtasker-0.2.12}/tox.ini +0 -0
|
@@ -102,6 +102,12 @@ def ls(
|
|
|
102
102
|
"--name",
|
|
103
103
|
help="Filter by worker name.",
|
|
104
104
|
),
|
|
105
|
+
status: Optional[str] = typer.Option(
|
|
106
|
+
None,
|
|
107
|
+
"--status",
|
|
108
|
+
"-s",
|
|
109
|
+
help="Filter by worker status. One of `active`, `suspended`, `crashed`.",
|
|
110
|
+
),
|
|
105
111
|
extra_filter: Optional[str] = typer.Option(
|
|
106
112
|
None,
|
|
107
113
|
"--extra-filter",
|
|
@@ -164,6 +170,7 @@ def ls(
|
|
|
164
170
|
ls_workers,
|
|
165
171
|
worker_id=worker_id,
|
|
166
172
|
worker_name=worker_name,
|
|
173
|
+
status=status,
|
|
167
174
|
extra_filter=extra_filter,
|
|
168
175
|
),
|
|
169
176
|
offset=offset,
|
|
@@ -64,11 +64,15 @@ __all__ = [
|
|
|
64
64
|
]
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
def _is_network_transient_error(exception):
|
|
68
|
+
return isinstance(exception, (httpx.TransportError, ConnectionError, TimeoutError))
|
|
69
|
+
|
|
70
|
+
|
|
67
71
|
def _network_err_retry(func):
|
|
68
72
|
@wraps(func)
|
|
69
73
|
def wrapper(*args, **kwargs):
|
|
70
74
|
return stamina.retry(
|
|
71
|
-
on=
|
|
75
|
+
on=_is_network_transient_error,
|
|
72
76
|
attempts=10,
|
|
73
77
|
timeout=100.0,
|
|
74
78
|
wait_initial=0.5,
|
|
@@ -278,12 +282,15 @@ def report_task_status(
|
|
|
278
282
|
@_network_err_retry
|
|
279
283
|
def refresh_task_heartbeat(
|
|
280
284
|
task_id: str,
|
|
285
|
+
worker_id: Optional[str] = None,
|
|
281
286
|
client: Optional[httpx.Client] = None,
|
|
282
287
|
) -> None:
|
|
283
288
|
"""Refresh the heartbeat of a task."""
|
|
284
289
|
if client is None:
|
|
285
290
|
client = get_httpx_client()
|
|
286
|
-
response = client.post(
|
|
291
|
+
response = client.post(
|
|
292
|
+
f"/api/v1/queues/me/tasks/{task_id}/heartbeat", params={"worker_id": worker_id}
|
|
293
|
+
)
|
|
287
294
|
raise_for_status(response)
|
|
288
295
|
|
|
289
296
|
|
|
@@ -18,8 +18,9 @@ __all__ = [
|
|
|
18
18
|
|
|
19
19
|
class Heartbeat:
|
|
20
20
|
|
|
21
|
-
def __init__(self, task_id, heartbeat_interval):
|
|
21
|
+
def __init__(self, task_id, worker_id, heartbeat_interval):
|
|
22
22
|
self.task_id = task_id
|
|
23
|
+
self.worker_id = worker_id
|
|
23
24
|
self.heartbeat_interval = heartbeat_interval
|
|
24
25
|
|
|
25
26
|
self._thread = None
|
|
@@ -39,6 +40,7 @@ class Heartbeat:
|
|
|
39
40
|
self._thread.start()
|
|
40
41
|
|
|
41
42
|
def delay(self, interval: float) -> bool:
|
|
43
|
+
"""Returns False if it should exit."""
|
|
42
44
|
slice_t = 0.05 # check for stop event
|
|
43
45
|
start_time = time.perf_counter()
|
|
44
46
|
while True:
|
|
@@ -67,11 +69,12 @@ class Heartbeat:
|
|
|
67
69
|
"""Refresh heartbeat periodically"""
|
|
68
70
|
while True:
|
|
69
71
|
try:
|
|
70
|
-
refresh_task_heartbeat(task_id=self.task_id)
|
|
72
|
+
refresh_task_heartbeat(task_id=self.task_id, worker_id=self.worker_id)
|
|
71
73
|
except Exception as e:
|
|
72
|
-
logger.error(f"
|
|
74
|
+
logger.error(f"Failed to refresh heartbeat: {str(e)}")
|
|
75
|
+
raise
|
|
73
76
|
|
|
74
|
-
#
|
|
77
|
+
# Check if heartbeat should stop
|
|
75
78
|
if not self.delay(self.heartbeat_interval):
|
|
76
79
|
break
|
|
77
80
|
|
|
@@ -85,6 +88,9 @@ class Heartbeat:
|
|
|
85
88
|
except FileNotFoundError:
|
|
86
89
|
pass
|
|
87
90
|
|
|
91
|
+
def is_alive(self):
|
|
92
|
+
return self._thread and self._thread.is_alive()
|
|
93
|
+
|
|
88
94
|
|
|
89
95
|
_current_heartbeat: ContextVar[Optional[Heartbeat]] = ContextVar(
|
|
90
96
|
"heartbeat", default=None
|
|
@@ -92,7 +98,10 @@ _current_heartbeat: ContextVar[Optional[Heartbeat]] = ContextVar(
|
|
|
92
98
|
|
|
93
99
|
|
|
94
100
|
def start_heartbeat(
|
|
95
|
-
task_id,
|
|
101
|
+
task_id,
|
|
102
|
+
worker_id: Optional[str] = None,
|
|
103
|
+
heartbeat_interval: Optional[float] = None,
|
|
104
|
+
raise_error=True,
|
|
96
105
|
):
|
|
97
106
|
logger.debug("Try starting heartbeat.")
|
|
98
107
|
if _current_heartbeat.get() is not None:
|
|
@@ -102,12 +111,14 @@ def start_heartbeat(
|
|
|
102
111
|
|
|
103
112
|
heartbeat_manager = Heartbeat(
|
|
104
113
|
task_id=task_id,
|
|
114
|
+
worker_id=worker_id,
|
|
105
115
|
heartbeat_interval=heartbeat_interval
|
|
106
116
|
or get_client_config().task.heartbeat_interval,
|
|
107
117
|
)
|
|
108
118
|
heartbeat_manager.start()
|
|
109
119
|
_current_heartbeat.set(heartbeat_manager)
|
|
110
120
|
logger.debug("Heartbeat started.")
|
|
121
|
+
return heartbeat_manager
|
|
111
122
|
|
|
112
123
|
|
|
113
124
|
def end_heartbeat(raise_error=True):
|
|
@@ -220,7 +220,9 @@ def loop_run(
|
|
|
220
220
|
dump_task_info()
|
|
221
221
|
|
|
222
222
|
with log_to_file(file_path=get_labtasker_log_dir() / "run.log"):
|
|
223
|
-
start_heartbeat(
|
|
223
|
+
start_heartbeat(
|
|
224
|
+
task_id=current_task_id(), worker_id=current_worker_id()
|
|
225
|
+
)
|
|
224
226
|
success_flag = False
|
|
225
227
|
try:
|
|
226
228
|
func_args = (task.args, *args) if pass_args_dict else args
|
|
@@ -674,22 +674,52 @@ class DBService:
|
|
|
674
674
|
@retry_on_transient
|
|
675
675
|
@validate_arg
|
|
676
676
|
def refresh_task_heartbeat(
|
|
677
|
-
self,
|
|
678
|
-
|
|
679
|
-
task_id: str,
|
|
680
|
-
) -> bool:
|
|
677
|
+
self, queue_id: str, task_id: str, worker_id: Optional[str] = None
|
|
678
|
+
):
|
|
681
679
|
"""Update task heartbeat timestamp."""
|
|
680
|
+
query = {"_id": task_id, "queue_id": queue_id, "status": "running"}
|
|
681
|
+
|
|
682
682
|
with self._client.start_session() as session:
|
|
683
683
|
with session.start_transaction():
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
684
|
+
# Find the task in a single query
|
|
685
|
+
task = self._tasks.find_one(query)
|
|
686
|
+
if not task:
|
|
687
|
+
raise HTTPException(
|
|
688
|
+
status_code=HTTP_404_NOT_FOUND,
|
|
689
|
+
detail=f"Task '{task_id}' not found in queue '{queue_id}' or not in 'running' state",
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
# Validate worker if provided
|
|
693
|
+
if worker_id:
|
|
694
|
+
if task["worker_id"] != worker_id:
|
|
695
|
+
raise HTTPException(
|
|
696
|
+
status_code=HTTP_403_FORBIDDEN,
|
|
697
|
+
detail=f"Task '{task_id}' is assigned to worker '{task['worker_id']}', not '{worker_id}'",
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
# Check worker status in a single query
|
|
701
|
+
worker = self._workers.find_one(
|
|
702
|
+
{"_id": worker_id, "status": WorkerState.ACTIVE}
|
|
703
|
+
)
|
|
704
|
+
if not worker:
|
|
705
|
+
raise HTTPException(
|
|
706
|
+
status_code=HTTP_404_NOT_FOUND,
|
|
707
|
+
detail=f"Worker '{worker_id}' not found or not active",
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
# Update the task heartbeat
|
|
711
|
+
result = self._tasks.update_one(
|
|
712
|
+
query,
|
|
713
|
+
{"$set": {"last_heartbeat": get_current_time()}},
|
|
714
|
+
session=session,
|
|
691
715
|
)
|
|
692
716
|
|
|
717
|
+
if result.modified_count == 0:
|
|
718
|
+
raise HTTPException(
|
|
719
|
+
status_code=HTTP_404_NOT_FOUND,
|
|
720
|
+
detail=f"Failed to update heartbeat for task '{task_id}' - it may have changed state during the operation",
|
|
721
|
+
)
|
|
722
|
+
|
|
693
723
|
@retry_on_transient
|
|
694
724
|
@validate_arg
|
|
695
725
|
def worker_report_task_status(
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import uuid
|
|
3
3
|
from contextlib import asynccontextmanager
|
|
4
|
-
from typing import Any, Dict, List
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
5
|
|
|
6
|
-
from fastapi import Depends, FastAPI, HTTPException, Request
|
|
6
|
+
from fastapi import Depends, FastAPI, HTTPException, Query, Request
|
|
7
7
|
from sse_starlette.sse import EventSourceResponse
|
|
8
8
|
from starlette.status import (
|
|
9
9
|
HTTP_201_CREATED,
|
|
@@ -309,16 +309,14 @@ def report_task_status(
|
|
|
309
309
|
)
|
|
310
310
|
def refresh_task_heartbeat(
|
|
311
311
|
task_id: str,
|
|
312
|
+
worker_id: Optional[str] = Query(None), # use query param
|
|
312
313
|
queue: Dict[str, Any] = Depends(get_verified_queue_dependency),
|
|
313
314
|
db: DBService = Depends(get_db),
|
|
314
315
|
):
|
|
315
316
|
"""Update task heartbeat timestamp."""
|
|
316
|
-
|
|
317
|
-
queue_id=queue["_id"],
|
|
318
|
-
task_id=task_id,
|
|
317
|
+
db.refresh_task_heartbeat(
|
|
318
|
+
queue_id=queue["_id"], task_id=task_id, worker_id=worker_id
|
|
319
319
|
)
|
|
320
|
-
if not done:
|
|
321
|
-
raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Task not found.")
|
|
322
320
|
|
|
323
321
|
|
|
324
322
|
@app.get(
|
|
@@ -43,7 +43,12 @@ class StateTransitionEventHandle:
|
|
|
43
43
|
self.commit()
|
|
44
44
|
|
|
45
45
|
def commit(self):
|
|
46
|
-
event_data =
|
|
46
|
+
event_data = self._create_event_data()
|
|
47
|
+
self._publish_event(event_data)
|
|
48
|
+
self._entity_data = None
|
|
49
|
+
|
|
50
|
+
def _create_event_data(self):
|
|
51
|
+
return StateTransitionEvent(
|
|
47
52
|
entity_type=self.entity_type,
|
|
48
53
|
queue_id=self.queue_id,
|
|
49
54
|
entity_id=self.entity_id,
|
|
@@ -54,9 +59,17 @@ class StateTransitionEventHandle:
|
|
|
54
59
|
entity_data=self._entity_data,
|
|
55
60
|
)
|
|
56
61
|
|
|
62
|
+
def _publish_event(self, event_data):
|
|
57
63
|
# Use fully synchronous event publishing
|
|
58
64
|
event_manager.publish_event(self.queue_id, event_data)
|
|
59
|
-
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class NullEventHandle(StateTransitionEventHandle):
|
|
68
|
+
"""A placeholder that does nothing. (Used for cases where triggering event publishing is undesired)"""
|
|
69
|
+
|
|
70
|
+
def _publish_event(self, event_data):
|
|
71
|
+
# Override to do nothing
|
|
72
|
+
pass
|
|
60
73
|
|
|
61
74
|
|
|
62
75
|
class State(str, Enum):
|
|
@@ -140,6 +153,18 @@ class BaseFSM:
|
|
|
140
153
|
def state(self):
|
|
141
154
|
return self._state
|
|
142
155
|
|
|
156
|
+
def null_transition(self) -> NullEventHandle:
|
|
157
|
+
"""Perform a null transition and return a handle"""
|
|
158
|
+
return NullEventHandle(
|
|
159
|
+
entity_type=self.ENTITY_TYPE,
|
|
160
|
+
entity_id=self.entity_id,
|
|
161
|
+
queue_id=self.queue_id,
|
|
162
|
+
old_state=str(self._state),
|
|
163
|
+
new_state=str(self._state),
|
|
164
|
+
transition_time=get_current_time(),
|
|
165
|
+
metadata=self.metadata,
|
|
166
|
+
)
|
|
167
|
+
|
|
143
168
|
def transition_to(self, new_state: State) -> StateTransitionEventHandle:
|
|
144
169
|
"""Perform state transition and return a handle"""
|
|
145
170
|
old_state = self._state
|
|
@@ -190,6 +215,7 @@ class TaskFSM(BaseFSM):
|
|
|
190
215
|
TaskState.FAILED: {
|
|
191
216
|
TaskState.PENDING,
|
|
192
217
|
TaskState.CANCELLED,
|
|
218
|
+
TaskState.FAILED, # null transition (for more tolerance)
|
|
193
219
|
}, # Can be reset and requeued
|
|
194
220
|
TaskState.CANCELLED: {
|
|
195
221
|
TaskState.PENDING,
|
|
@@ -280,11 +306,15 @@ class TaskFSM(BaseFSM):
|
|
|
280
306
|
Transitions:
|
|
281
307
|
- RUNNING -> PENDING (if retries < max_retries)
|
|
282
308
|
- RUNNING -> FAILED (if retries >= max_retries)
|
|
309
|
+
- FAILED -> FAILED (null transition, does nothing)
|
|
283
310
|
- Others -> InvalidStateTransition (invalid)
|
|
284
311
|
|
|
285
312
|
Note: FAILED state can transition back to PENDING for retries
|
|
286
313
|
until max_retries is reached.
|
|
287
314
|
"""
|
|
315
|
+
if self.state == TaskState.FAILED:
|
|
316
|
+
return self.null_transition()
|
|
317
|
+
|
|
288
318
|
if self.state != TaskState.RUNNING:
|
|
289
319
|
raise InvalidStateTransition(f"Cannot fail task in {self.state} state")
|
|
290
320
|
|
|
@@ -305,7 +335,10 @@ class WorkerFSM(BaseFSM):
|
|
|
305
335
|
WorkerState.CRASHED,
|
|
306
336
|
},
|
|
307
337
|
WorkerState.SUSPENDED: {WorkerState.ACTIVE}, # Manual transition
|
|
308
|
-
WorkerState.CRASHED: {
|
|
338
|
+
WorkerState.CRASHED: {
|
|
339
|
+
WorkerState.ACTIVE, # Manual transition
|
|
340
|
+
WorkerState.CRASHED, # null transition (for more tolerance)
|
|
341
|
+
},
|
|
309
342
|
}
|
|
310
343
|
|
|
311
344
|
def __init__(
|
|
@@ -366,7 +399,11 @@ class WorkerFSM(BaseFSM):
|
|
|
366
399
|
Transitions:
|
|
367
400
|
- ACTIVE -> ACTIVE
|
|
368
401
|
- ACTIVE -> CRASHED (retries >= max_retries)
|
|
402
|
+
- CRASHED -> CRASHED (null transition, does nothing)
|
|
369
403
|
"""
|
|
404
|
+
if self.state == WorkerState.CRASHED:
|
|
405
|
+
return self.null_transition()
|
|
406
|
+
|
|
370
407
|
if self.state != WorkerState.ACTIVE:
|
|
371
408
|
raise InvalidStateTransition(f"Cannot fail worker in {self.state} state")
|
|
372
409
|
|
|
@@ -154,7 +154,8 @@ tests/test_client/test_cli/test_task.py
|
|
|
154
154
|
tests/test_client/test_cli/test_worker.py
|
|
155
155
|
tests/test_client/test_core/__init__.py
|
|
156
156
|
tests/test_client/test_core/test_cli_utils.py
|
|
157
|
-
tests/test_client/test_core/
|
|
157
|
+
tests/test_client/test_core/test_heartbeat_e2e.py
|
|
158
|
+
tests/test_client/test_core/test_heartbeat_unit.py
|
|
158
159
|
tests/test_client/test_core/test_job_runner.py
|
|
159
160
|
tests/test_client/test_core/test_logging.py
|
|
160
161
|
tests/test_client/test_core/test_loop_internal_error_handler.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "labtasker"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.12"
|
|
8
8
|
description = "A task queue system for lab experiments"
|
|
9
9
|
authors = [{ name = "Your Name", email = "your.email@example.com" }]
|
|
10
10
|
license = { text = "Apache License 2.0" }
|
|
@@ -133,7 +133,6 @@ disable_error_code = [
|
|
|
133
133
|
"no-redef",
|
|
134
134
|
"import-untyped"
|
|
135
135
|
]
|
|
136
|
-
python_version = "0.2.11"
|
|
137
136
|
warn_unused_configs = true
|
|
138
137
|
ignore_missing_imports = true
|
|
139
138
|
show_error_codes = true
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from labtasker.client.core.api import (
|
|
6
|
+
create_queue,
|
|
7
|
+
create_worker,
|
|
8
|
+
fetch_task,
|
|
9
|
+
submit_task,
|
|
10
|
+
)
|
|
11
|
+
from labtasker.client.core.heartbeat import start_heartbeat
|
|
12
|
+
from labtasker.client.core.paths import set_labtasker_log_dir
|
|
13
|
+
from tests.fixtures.logging import silence_logger
|
|
14
|
+
|
|
15
|
+
pytestmark = [
|
|
16
|
+
pytest.mark.e2e,
|
|
17
|
+
pytest.mark.usefixtures(
|
|
18
|
+
"silence_logger"
|
|
19
|
+
), # silence logger in testcases of this module
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture(autouse=True)
|
|
24
|
+
def setup_queue(client_config):
|
|
25
|
+
return create_queue(
|
|
26
|
+
queue_name=client_config.queue.queue_name,
|
|
27
|
+
password=client_config.queue.password.get_secret_value(),
|
|
28
|
+
metadata={"tag": "test"},
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.fixture(autouse=True)
|
|
33
|
+
def setup_workers():
|
|
34
|
+
id1 = create_worker(worker_name="worker_1")
|
|
35
|
+
id2 = create_worker(worker_name="worker_2")
|
|
36
|
+
|
|
37
|
+
return id1, id2
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@pytest.fixture(autouse=True)
|
|
41
|
+
def setup_task(db_fixture):
|
|
42
|
+
# relies on db_fixture to clear db after each test case
|
|
43
|
+
|
|
44
|
+
fetch_task()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_heartbeat_worker_mismatch():
|
|
48
|
+
# Test if heartbeat fails when worker_id does not match
|
|
49
|
+
|
|
50
|
+
# setup
|
|
51
|
+
# 1. create tasks
|
|
52
|
+
submit_task(task_name="test_task", args={"arg1": 0})
|
|
53
|
+
# 2. create workers
|
|
54
|
+
worker_id1 = create_worker(worker_name="worker_1")
|
|
55
|
+
worker_id2 = create_worker(worker_name="worker_2")
|
|
56
|
+
# 3. fetch task
|
|
57
|
+
task_resp = fetch_task(worker_id=worker_id1, heartbeat_timeout=99999.0)
|
|
58
|
+
|
|
59
|
+
# 4. set local dir
|
|
60
|
+
task_id = task_resp.task.task_id
|
|
61
|
+
set_labtasker_log_dir(
|
|
62
|
+
task_id=task_id,
|
|
63
|
+
task_name=task_resp.task.task_name,
|
|
64
|
+
set_env=True,
|
|
65
|
+
overwrite=True,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# use a different worker_id to start heartbeat
|
|
69
|
+
heartbeat_manager = start_heartbeat(
|
|
70
|
+
task_id=task_id, worker_id=worker_id2, heartbeat_interval=0.2
|
|
71
|
+
)
|
|
72
|
+
time.sleep(1.0)
|
|
73
|
+
assert not heartbeat_manager.is_alive()
|
|
@@ -174,7 +174,7 @@ class TestWorkerFSM:
|
|
|
174
174
|
|
|
175
175
|
def test_fail_from_invalid_state(self, worker_db_entry):
|
|
176
176
|
"""Test failing worker from invalid state."""
|
|
177
|
-
invalid_states = [WorkerState.SUSPENDED
|
|
177
|
+
invalid_states = [WorkerState.SUSPENDED]
|
|
178
178
|
for state in invalid_states:
|
|
179
179
|
worker_db_entry["status"] = state
|
|
180
180
|
fsm = WorkerFSM.from_db_entry(worker_db_entry)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/generated/LabCmdLexer.py
RENAMED
|
File without changes
|
{labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/generated/LabCmdListener.py
RENAMED
|
File without changes
|
{labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/core/cmd_parser/generated/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labtasker-0.2.11 → labtasker-0.2.12}/labtasker/client/templates/labtasker_root/logs/.gitkeep
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|