labtasker 0.2.5__tar.gz → 0.2.7__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 → labtasker-0.2.7}/PKG-INFO +10 -13
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/__init__.py +1 -1
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/config.py +4 -3
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/loop.py +122 -47
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cli_utils.py +5 -1
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/generated/LabCmd.py +2 -3
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/generated/LabCmdLexer.py +2 -3
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/generated/LabCmdListener.py +1 -1
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/parser.py +18 -12
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/config.py +1 -1
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/job_runner.py +10 -7
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/query_transpiler.py +9 -9
- labtasker-0.2.7/labtasker/vendor/README.txt +8 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/BufferedTokenStream.py +302 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/CommonTokenFactory.py +61 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/CommonTokenStream.py +87 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/FileStream.py +27 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/InputStream.py +87 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/IntervalSet.py +180 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/LL1Analyzer.py +173 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/Lexer.py +329 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/ListTokenSource.py +144 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/Parser.py +580 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/ParserInterpreter.py +170 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/ParserRuleContext.py +186 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/PredictionContext.py +629 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/Recognizer.py +147 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/RuleContext.py +227 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/StdinStream.py +11 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/Token.py +155 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/TokenStreamRewriter.py +255 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/Utils.py +33 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/__init__.py +21 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/_pygrun.py +171 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATN.py +132 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNConfig.py +159 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNConfigSet.py +212 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNDeserializationOptions.py +24 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNDeserializer.py +446 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNSimulator.py +47 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNState.py +264 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNType.py +17 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/LexerATNSimulator.py +570 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/LexerAction.py +298 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/LexerActionExecutor.py +143 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/ParserATNSimulator.py +1661 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/PredictionMode.py +499 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/SemanticContext.py +330 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/Transition.py +268 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/atn/__init__.py +1 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/dfa/DFA.py +133 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/dfa/DFASerializer.py +73 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/dfa/DFAState.py +126 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/dfa/__init__.py +1 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/error/DiagnosticErrorListener.py +107 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/error/ErrorListener.py +72 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/error/ErrorStrategy.py +709 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/error/Errors.py +173 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/error/__init__.py +1 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/tree/Chunk.py +30 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/tree/ParseTreeMatch.py +118 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/tree/ParseTreePattern.py +72 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/tree/ParseTreePatternMatcher.py +374 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/tree/RuleTagToken.py +50 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/tree/TokenTagToken.py +47 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/tree/Tree.py +191 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/tree/Trees.py +111 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/xpath/XPath.py +269 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/xpath/XPathLexer.py +95 -0
- labtasker-0.2.7/labtasker/vendor/antlr4/xpath/__init__.py +1 -0
- labtasker-0.2.7/labtasker/vendor/vendor.txt +1 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker.egg-info/SOURCES.txt +62 -33
- {labtasker-0.2.5 → labtasker-0.2.7}/pyproject.toml +42 -17
- labtasker-0.2.7/tests/dummy_jobs/job_1.py +39 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_loop.py +51 -2
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_task.py +1 -3
- labtasker-0.2.7/tests/test_server/test_db_utils/__init__.py +0 -0
- labtasker-0.2.7/tests/test_server/test_endpoint/__init__.py +0 -0
- labtasker-0.2.7/tests/test_utils/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tox.ini +1 -1
- labtasker-0.2.5/.coveragerc +0 -4
- labtasker-0.2.5/.dockerignore +0 -11
- labtasker-0.2.5/.flake8 +0 -19
- labtasker-0.2.5/.gitattributes +0 -217
- labtasker-0.2.5/.gitignore +0 -189
- labtasker-0.2.5/.gitmodules +0 -8
- labtasker-0.2.5/.pre-commit-config.yaml +0 -22
- labtasker-0.2.5/.pytest.ini +0 -9
- labtasker-0.2.5/.python-version +0 -6
- labtasker-0.2.5/Dockerfile +0 -25
- labtasker-0.2.5/PRIVACY.md +0 -1
- labtasker-0.2.5/demo/advanced/custom_resolver/submit_job.py +0 -11
- labtasker-0.2.5/demo/advanced/custom_resolver/w.py +0 -38
- labtasker-0.2.5/demo/advanced/custom_resolver/wo.py +0 -29
- labtasker-0.2.5/demo/advanced/event_system/email_on_task_failure.py +0 -67
- labtasker-0.2.5/demo/advanced/event_system/send_email.py +0 -46
- labtasker-0.2.5/demo/advanced/event_system/sim_unstable_job.py +0 -27
- labtasker-0.2.5/demo/advanced/event_system/submit.sh +0 -10
- labtasker-0.2.5/demo/basic/bash_demo/job_main.py +0 -23
- labtasker-0.2.5/demo/basic/bash_demo/run_job.sh +0 -8
- labtasker-0.2.5/demo/basic/bash_demo/submit_job.sh +0 -13
- labtasker-0.2.5/demo/basic/python_demo/run_job.py +0 -25
- labtasker-0.2.5/demo/basic/python_demo/submit_job.py +0 -9
- labtasker-0.2.5/docker/mongodb/init.d/init-keyfile.sh +0 -29
- labtasker-0.2.5/docker/mongodb/post-init.d/init-mongo.sh +0 -53
- labtasker-0.2.5/docker-compose.yml +0 -90
- labtasker-0.2.5/script_tests/test_ban_datetime_now/allowed.py +0 -80
- labtasker-0.2.5/script_tests/test_ban_datetime_now/disallowed.py +0 -129
- labtasker-0.2.5/script_tests/test_ban_datetime_now/test_ban.py +0 -138
- labtasker-0.2.5/scripts/asciinema_adjust_timestamp.py +0 -38
- labtasker-0.2.5/scripts/check_version.py +0 -59
- labtasker-0.2.5/scripts/datetime_now_checker.py +0 -268
- labtasker-0.2.5/server.example.env +0 -24
- labtasker-0.2.5/tests/dummy_jobs/job_1.py +0 -27
- {labtasker-0.2.5 → labtasker-0.2.7}/LICENSE +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/MANIFEST.in +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/README.md +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/__main__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/api_models.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/cli.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/event.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/init.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/queue.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/task.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/worker.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/client_api.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/api.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/LabCmd.g4 +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/LabCmdLexer.g4 +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/generated/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/context.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/events.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/exceptions.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/heartbeat.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/logging.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/paths.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/plugin_utils.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/resolver/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/resolver/models.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/resolver/utils.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/utils.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/version_checker.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/templates/labtasker_root/.gitignore +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/templates/labtasker_root/client.toml +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/templates/labtasker_root/logs/.gitkeep +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/constants.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/filtering.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/security.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/cli.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/config.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/database.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/db_utils.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/dependencies.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/embedded_db.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/endpoints.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/event_manager.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/fsm.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/logging.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/utils.py +0 -0
- {labtasker-0.2.5/tests → labtasker-0.2.7/labtasker/vendor}/__init__.py +0 -0
- {labtasker-0.2.5/tests/fixtures → labtasker-0.2.7/labtasker/vendor/antlr4/tree}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/setup.cfg +0 -0
- {labtasker-0.2.5/tests/test_client → labtasker-0.2.7/tests}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/conftest.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/demo_pager_iterator.py +0 -0
- {labtasker-0.2.5/tests/test_client/test_cli → labtasker-0.2.7/tests/fixtures}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/database/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/database/mock.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/database/real.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/logging.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/mock_datetime_now.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/server/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/server/async_app.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/server/sync_app.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_api_models.py +0 -0
- {labtasker-0.2.5/tests/test_client/test_core → labtasker-0.2.7/tests/test_client}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/conftest.py +0 -0
- {labtasker-0.2.5/tests/test_client/test_core/test_event → labtasker-0.2.7/tests/test_client/test_cli}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/conftest.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_basic.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_config.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_event.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_init.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_queue.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_worker.py +0 -0
- {labtasker-0.2.5/tests/test_client/test_core/test_query_transpiler → labtasker-0.2.7/tests/test_client/test_core}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_cli_utils.py +0 -0
- {labtasker-0.2.5/tests/test_client/test_core/test_runner_concurrency → labtasker-0.2.7/tests/test_client/test_core/test_event}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_event/test_concurrency_job_flow_event.py +0 -0
- /labtasker-0.2.5/tests/test_client/test_core/test_runner_timeout/__init__.py → /labtasker-0.2.7/tests/test_client/test_core/test_event/test_event_integration.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_event/test_event_listener_basic.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_event/test_various_actions.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_event/utils.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_heartbeat.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_job_runner.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_logging.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_loop_internal_error_handler.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_pager_iterator.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_parser.py +0 -0
- {labtasker-0.2.5/tests/test_filtering → labtasker-0.2.7/tests/test_client/test_core/test_query_transpiler}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_query_transpiler/conftest.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_query_transpiler/test_behavior.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_query_transpiler/test_matching.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_query_transpiler/test_utils.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_query_transpiler/utils.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_resolver.py +0 -0
- {labtasker-0.2.5/tests/test_server → labtasker-0.2.7/tests/test_client/test_core/test_runner_concurrency}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_concurrency/run_concurrent.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_concurrency/test_runner_concurrency_success_failure.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_concurrency/test_runner_high_concurrency.py +0 -0
- {labtasker-0.2.5/tests/test_server/test_database → labtasker-0.2.7/tests/test_client/test_core/test_runner_timeout}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_timeout/conftest.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_timeout/test_job_runner_timeout.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_timeout/test_job_runner_with_resolver_timeout.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_with_resolver.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_server_notification_and_client_version.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_version_checker.py +0 -0
- {labtasker-0.2.5/tests/test_server/test_db_utils → labtasker-0.2.7/tests/test_filtering}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_filtering/exception_utils.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_filtering/test_exception_filtering.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_mock_time.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_security.py +0 -0
- {labtasker-0.2.5/tests/test_server/test_endpoint → labtasker-0.2.7/tests/test_server}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/conftest.py +0 -0
- {labtasker-0.2.5/tests/test_utils → labtasker-0.2.7/tests/test_server/test_database}/__init__.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_database/conftest.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_database/test_database_basic.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_database/test_fetch_extra_filter.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_database/test_query_dict_to_mongo_filter.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_database/test_required_field_fetching.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_db_utils/test_arg_match.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_db_utils/test_keys_to_query_dict.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_db_utils/test_keys_to_query_dict_deepest.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_db_utils/test_keys_to_query_dict_topmost.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_embedded_db.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_endpoint/test_event_basic.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_endpoint/test_server.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_endpoint/test_server_async.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_endpoint/test_server_async_ping.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_fsm.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_get_verified_queue_dependency.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_utils/test_utils.py +0 -0
- {labtasker-0.2.5 → labtasker-0.2.7}/tests/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: labtasker
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
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
|
|
@@ -11,31 +11,27 @@ Classifier: Intended Audience :: Science/Research
|
|
|
11
11
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
16
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
-
Requires-Python: <4.0,>=3.
|
|
18
|
+
Requires-Python: <4.0,>=3.10
|
|
21
19
|
Description-Content-Type: text/markdown
|
|
22
20
|
License-File: LICENSE
|
|
23
|
-
Requires-Dist: python-dotenv<2.0.0,>=1.0.1
|
|
24
21
|
Requires-Dist: pymongo<5.0.0,>=4.0.0
|
|
25
|
-
Requires-Dist: fastapi<0.
|
|
26
|
-
Requires-Dist: uvicorn[standard]<0.
|
|
27
|
-
Requires-Dist: click<9.0.0,>=8.
|
|
22
|
+
Requires-Dist: fastapi<0.117.0,>=0.115.0
|
|
23
|
+
Requires-Dist: uvicorn[standard]<0.36.0,>=0.15.0
|
|
24
|
+
Requires-Dist: click<9.0.0,>=8.2.0
|
|
28
25
|
Requires-Dist: passlib<2.0.0,>=1.7.0
|
|
29
|
-
Requires-Dist: antlr4-python3-runtime<5.0.0,>=4.13.0
|
|
30
26
|
Requires-Dist: pydantic-settings<3.0.0,>=2.8.0
|
|
31
27
|
Requires-Dist: httpx[socks]<0.29.0,>=0.28.0
|
|
32
|
-
Requires-Dist: typer<0.
|
|
28
|
+
Requires-Dist: typer<0.17.0,>=0.16.0
|
|
33
29
|
Requires-Dist: loguru<0.8.0,>=0.7.0
|
|
34
30
|
Requires-Dist: ruamel-yaml<0.19.0,>=0.18.10
|
|
35
31
|
Requires-Dist: pyyaml<7.0.0,>=6.0.0
|
|
36
32
|
Requires-Dist: tomlkit<0.14.0,>=0.13.2
|
|
37
33
|
Requires-Dist: importlib-metadata<9.0.0,>=8.5.0
|
|
38
|
-
Requires-Dist: packaging<
|
|
34
|
+
Requires-Dist: packaging<26.0,>=24.2
|
|
39
35
|
Requires-Dist: sse-starlette<3.0.0,>=2.1.3
|
|
40
36
|
Requires-Dist: httpx-sse<0.5.0,>=0.4.0
|
|
41
37
|
Requires-Dist: stamina<26.0.0,>=25.1.0
|
|
@@ -43,6 +39,7 @@ Requires-Dist: noneprompt<0.2.0,>=0.1.9
|
|
|
43
39
|
Requires-Dist: mongomock<4.4.0,>=4.3.0
|
|
44
40
|
Requires-Dist: jsonpickle<5.0.0,>=4.0.2
|
|
45
41
|
Requires-Dist: mslex<2.0.0,>=1.3.0
|
|
42
|
+
Requires-Dist: pexpect<5.0.0,>=4.9.0
|
|
46
43
|
Provides-Extra: dev
|
|
47
44
|
Requires-Dist: pytest<9.0.0,>=8.0.0; extra == "dev"
|
|
48
45
|
Requires-Dist: pytest-cov<7.0.0,>=5.0.0; extra == "dev"
|
|
@@ -53,9 +50,9 @@ Requires-Dist: flake8<8.0.0,>=7.0.0; extra == "dev"
|
|
|
53
50
|
Requires-Dist: pre-commit<5.0.0,>=3.0.0; extra == "dev"
|
|
54
51
|
Requires-Dist: freezegun<2.0.0,>=1.5.0; extra == "dev"
|
|
55
52
|
Requires-Dist: pytest-docker<4.0.0,>=3.0.0; extra == "dev"
|
|
56
|
-
Requires-Dist: pytest-asyncio<
|
|
53
|
+
Requires-Dist: pytest-asyncio<1.1.0,>=0.24.0; extra == "dev"
|
|
57
54
|
Requires-Dist: asgi-lifespan<3.0.0,>=2.1.0; extra == "dev"
|
|
58
|
-
Requires-Dist: tox<4.
|
|
55
|
+
Requires-Dist: tox<4.28.0,>=4.24.0; extra == "dev"
|
|
59
56
|
Requires-Dist: pytest-dependency<0.7.0,>=0.6.0; extra == "dev"
|
|
60
57
|
Requires-Dist: pytest-sugar<2.0.0,>=1.0.0; extra == "dev"
|
|
61
58
|
Provides-Extra: doc
|
|
@@ -48,9 +48,10 @@ def config(
|
|
|
48
48
|
# 1.1 Copy existing configuration to the temporary file (if it exists)
|
|
49
49
|
config_path = get_labtasker_client_config_path()
|
|
50
50
|
if config_path.exists():
|
|
51
|
-
with
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
with (
|
|
52
|
+
open(config_path, "rb") as existing_config,
|
|
53
|
+
open(temp_file_path, "wb") as temp_file,
|
|
54
|
+
):
|
|
54
55
|
temp_file.write(existing_config.read())
|
|
55
56
|
|
|
56
57
|
# 1.2 Open the temporary file in the editor and validate changes
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"""Implements `labtasker loop xxx`"""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import os
|
|
4
5
|
import subprocess
|
|
5
6
|
import sys
|
|
6
7
|
from collections import defaultdict
|
|
8
|
+
from pathlib import Path
|
|
7
9
|
from typing import List, Optional
|
|
8
10
|
|
|
11
|
+
import pexpect
|
|
9
12
|
import typer
|
|
10
13
|
from typing_extensions import Annotated
|
|
11
14
|
|
|
@@ -24,7 +27,6 @@ from labtasker.client.core.logging import (
|
|
|
24
27
|
logger,
|
|
25
28
|
set_verbose,
|
|
26
29
|
stderr_console,
|
|
27
|
-
stdout_console,
|
|
28
30
|
verbose_print,
|
|
29
31
|
)
|
|
30
32
|
|
|
@@ -42,6 +44,76 @@ class InfiniteDefaultDict(defaultdict):
|
|
|
42
44
|
return super().get(key, default)
|
|
43
45
|
|
|
44
46
|
|
|
47
|
+
def _check_pty_available(opt: bool) -> bool:
|
|
48
|
+
if opt and os.name == "nt":
|
|
49
|
+
stderr_console.print(
|
|
50
|
+
"[bold orange1]Warning:[/bold orange1] PTY is not available on Windows. "
|
|
51
|
+
"Disabling PTY support."
|
|
52
|
+
)
|
|
53
|
+
return False
|
|
54
|
+
return opt
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _stream_child_output(child) -> None:
|
|
58
|
+
"""Stream the output of a pexpect child in real-time, supporting progress bars."""
|
|
59
|
+
try:
|
|
60
|
+
while True:
|
|
61
|
+
try:
|
|
62
|
+
output = child.read_nonblocking(size=1024, timeout=0.1)
|
|
63
|
+
if output:
|
|
64
|
+
# keep \r
|
|
65
|
+
sys.stdout.write(output)
|
|
66
|
+
sys.stdout.flush()
|
|
67
|
+
except pexpect.TIMEOUT:
|
|
68
|
+
continue
|
|
69
|
+
except pexpect.EOF:
|
|
70
|
+
break
|
|
71
|
+
finally:
|
|
72
|
+
child.close()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _run_with_pty(cmd, shell_exec=None, use_shell=False):
|
|
76
|
+
"""Run a command with PTY support for interactive programs."""
|
|
77
|
+
if use_shell:
|
|
78
|
+
shell_exec = shell_exec or "/bin/sh"
|
|
79
|
+
child = pexpect.spawn(shell_exec, ["-c", cmd], encoding="utf-8")
|
|
80
|
+
else:
|
|
81
|
+
child = pexpect.spawn(cmd[0], cmd[1:], encoding="utf-8")
|
|
82
|
+
|
|
83
|
+
_stream_child_output(child)
|
|
84
|
+
|
|
85
|
+
return child.exitstatus
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _run_with_subprocess(cmd, shell_exec=None, use_shell=False):
|
|
89
|
+
"""Run a command using standard subprocess approach."""
|
|
90
|
+
with subprocess.Popen(
|
|
91
|
+
args=cmd,
|
|
92
|
+
stdin=subprocess.PIPE,
|
|
93
|
+
stdout=subprocess.PIPE,
|
|
94
|
+
stderr=subprocess.PIPE,
|
|
95
|
+
text=True,
|
|
96
|
+
executable=shell_exec,
|
|
97
|
+
shell=use_shell,
|
|
98
|
+
) as process:
|
|
99
|
+
while True:
|
|
100
|
+
output = process.stdout.readline()
|
|
101
|
+
error = process.stderr.readline()
|
|
102
|
+
|
|
103
|
+
if output:
|
|
104
|
+
sys.stdout.write(output.strip())
|
|
105
|
+
sys.stdout.flush()
|
|
106
|
+
if error:
|
|
107
|
+
sys.stderr.write(error.strip())
|
|
108
|
+
sys.stderr.flush()
|
|
109
|
+
|
|
110
|
+
# Break when process completes and streams are empty
|
|
111
|
+
if process.poll() is not None and not output and not error:
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
return process.returncode
|
|
115
|
+
|
|
116
|
+
|
|
45
117
|
@app.command()
|
|
46
118
|
@cli_utils_decorator
|
|
47
119
|
def loop(
|
|
@@ -55,10 +127,16 @@ def loop(
|
|
|
55
127
|
option_cmd: str = typer.Option(
|
|
56
128
|
None,
|
|
57
129
|
"--command",
|
|
58
|
-
"--
|
|
130
|
+
"--cmd",
|
|
59
131
|
"-c",
|
|
60
132
|
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
133
|
),
|
|
134
|
+
script_path: Optional[Path] = typer.Option(
|
|
135
|
+
None,
|
|
136
|
+
exists=True,
|
|
137
|
+
readable=True,
|
|
138
|
+
help="Path to a script file that contains the command to execute. The script will run with '%(...)' placeholders replaced by the retrieved task arguments.",
|
|
139
|
+
),
|
|
62
140
|
executable: Optional[str] = typer.Option(
|
|
63
141
|
None,
|
|
64
142
|
"--executable",
|
|
@@ -83,6 +161,11 @@ def loop(
|
|
|
83
161
|
None,
|
|
84
162
|
help="Time in seconds before a task is considered stalled if no heartbeat is received.",
|
|
85
163
|
),
|
|
164
|
+
use_pty: bool = typer.Option(
|
|
165
|
+
os.name == "posix", # enabled by default on POSIX systems
|
|
166
|
+
callback=_check_pty_available,
|
|
167
|
+
help="Use pseudo terminal on POSIX systems for better interactive program support.",
|
|
168
|
+
),
|
|
86
169
|
verbose: bool = typer.Option( # noqa
|
|
87
170
|
False,
|
|
88
171
|
"--verbose",
|
|
@@ -101,19 +184,30 @@ def loop(
|
|
|
101
184
|
This will fetch tasks with 'input_file' and 'output_dir' arguments and run the command
|
|
102
185
|
with those values substituted. Tasks are processed until the queue is empty.
|
|
103
186
|
"""
|
|
104
|
-
|
|
187
|
+
# Ensure only one of [CMD], [--command], or [--script-path] is specified
|
|
188
|
+
cmd_sources = [cmd, option_cmd, script_path]
|
|
189
|
+
cmd_sources = list(filter(None, cmd_sources)) # Remove None values dynamically
|
|
190
|
+
if len(cmd_sources) > 1:
|
|
105
191
|
raise typer.BadParameter(
|
|
106
|
-
"Only one of [CMD]
|
|
192
|
+
"Only one of [CMD], [--command], or [--script-path] can be specified. Choose one."
|
|
107
193
|
)
|
|
108
194
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
195
|
+
# 1. Assign from cmd or option_cmd
|
|
196
|
+
input_cmd = cmd or option_cmd
|
|
197
|
+
|
|
198
|
+
# 2. Assign from script_path if provided
|
|
199
|
+
if script_path and input_cmd is None:
|
|
200
|
+
with open(script_path, "r") as f:
|
|
201
|
+
input_cmd = f.read().strip()
|
|
113
202
|
|
|
114
|
-
if
|
|
203
|
+
# 3. Try reading from stdin if shell mode is enabled
|
|
204
|
+
if not input_cmd and not sys.stdin.isatty():
|
|
205
|
+
input_cmd = sys.stdin.read().strip()
|
|
206
|
+
|
|
207
|
+
# Final validation: ensure a command is present
|
|
208
|
+
if not input_cmd:
|
|
115
209
|
raise typer.BadParameter(
|
|
116
|
-
"Command cannot be empty.
|
|
210
|
+
"Command cannot be empty. Specify via positional argument [CMD] or `--command` or `--script-path`."
|
|
117
211
|
)
|
|
118
212
|
|
|
119
213
|
parsed_filter = parse_filter(extra_filter)
|
|
@@ -125,13 +219,13 @@ def loop(
|
|
|
125
219
|
# Generate required fields dict
|
|
126
220
|
dummy_variable_table = InfiniteDefaultDict()
|
|
127
221
|
try:
|
|
128
|
-
_, queried_keys = cmd_interpolate(
|
|
222
|
+
_, queried_keys = cmd_interpolate(input_cmd, dummy_variable_table)
|
|
129
223
|
except (CmdParserError, KeyError, TypeError) as e:
|
|
130
224
|
raise typer.BadParameter(f"Command error with exception {e}")
|
|
131
225
|
|
|
132
226
|
required_fields = list(queried_keys)
|
|
133
227
|
|
|
134
|
-
logger.info(f"Got command: {
|
|
228
|
+
logger.info(f"Got command: {input_cmd}")
|
|
135
229
|
|
|
136
230
|
@loop_run(
|
|
137
231
|
required_fields=required_fields,
|
|
@@ -142,48 +236,29 @@ def loop(
|
|
|
142
236
|
pass_args_dict=True,
|
|
143
237
|
)
|
|
144
238
|
def run_cmd(args):
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
(
|
|
148
|
-
interpolated_cmd,
|
|
149
|
-
_,
|
|
150
|
-
) = cmd_interpolate(
|
|
151
|
-
cmd,
|
|
152
|
-
args,
|
|
153
|
-
)
|
|
239
|
+
interpolated_cmd, _ = cmd_interpolate(input_cmd, args)
|
|
154
240
|
logger.info(f"Prepared to run interpolated command: {interpolated_cmd}")
|
|
155
241
|
|
|
156
|
-
use_shell =
|
|
157
|
-
if isinstance(interpolated_cmd, str):
|
|
158
|
-
use_shell = True
|
|
159
|
-
|
|
160
|
-
with subprocess.Popen(
|
|
161
|
-
args=interpolated_cmd,
|
|
162
|
-
stdout=subprocess.PIPE,
|
|
163
|
-
stderr=subprocess.PIPE,
|
|
164
|
-
text=True,
|
|
165
|
-
executable=executable,
|
|
166
|
-
shell=use_shell,
|
|
167
|
-
) as process:
|
|
168
|
-
while True:
|
|
169
|
-
output = process.stdout.readline()
|
|
170
|
-
error = process.stderr.readline()
|
|
171
|
-
|
|
172
|
-
if output:
|
|
173
|
-
stdout_console.print(output.strip())
|
|
174
|
-
if error:
|
|
175
|
-
stderr_console.print(error.strip())
|
|
242
|
+
use_shell = isinstance(interpolated_cmd, str)
|
|
176
243
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
244
|
+
try:
|
|
245
|
+
if use_pty:
|
|
246
|
+
# Use pexpect with PTY on POSIX systems
|
|
247
|
+
exit_code = _run_with_pty(interpolated_cmd, executable, use_shell)
|
|
248
|
+
else:
|
|
249
|
+
# Standard subprocess approach for non-PTY execution
|
|
250
|
+
exit_code = _run_with_subprocess(
|
|
251
|
+
interpolated_cmd, executable, use_shell
|
|
252
|
+
)
|
|
180
253
|
|
|
181
|
-
|
|
182
|
-
if process.returncode != 0:
|
|
254
|
+
if exit_code != 0:
|
|
183
255
|
raise _LabtaskerJobFailed(
|
|
184
|
-
"Job process finished with non-zero exit code
|
|
256
|
+
f"Job process finished with non-zero exit code: {exit_code}"
|
|
185
257
|
)
|
|
186
258
|
|
|
259
|
+
except Exception as e:
|
|
260
|
+
raise _LabtaskerJobFailed(f"Error running command: {str(e)}")
|
|
261
|
+
|
|
187
262
|
logger.info(f"Task {task_info().task_id} ended.")
|
|
188
263
|
|
|
189
264
|
run_cmd()
|
|
@@ -635,7 +635,11 @@ def make_a_choice(
|
|
|
635
635
|
default=default,
|
|
636
636
|
keyboard_interrupt_default=keyboard_interrupt_default,
|
|
637
637
|
)
|
|
638
|
-
|
|
638
|
+
|
|
639
|
+
try:
|
|
640
|
+
return asyncio.run(prompt.prompt_async())
|
|
641
|
+
except Exception: # EOFError: # e.g. closed connection under special circumstances
|
|
642
|
+
return default
|
|
639
643
|
|
|
640
644
|
|
|
641
645
|
ls_format_iter = {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# Generated from labtasker/client/core/cmd_parser/generated/LabCmd.g4 by ANTLR 4.13.2
|
|
2
2
|
# encoding: utf-8
|
|
3
|
-
import
|
|
3
|
+
from labtasker.vendor.antlr4 import *
|
|
4
4
|
from io import StringIO
|
|
5
|
-
|
|
6
|
-
from antlr4 import *
|
|
5
|
+
import sys
|
|
7
6
|
|
|
8
7
|
if sys.version_info[1] > 5:
|
|
9
8
|
from typing import TextIO
|
{labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/generated/LabCmdLexer.py
RENAMED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Generated from labtasker/client/core/cmd_parser/generated/LabCmdLexer.g4 by ANTLR 4.13.2
|
|
2
|
-
import
|
|
2
|
+
from labtasker.vendor.antlr4 import *
|
|
3
3
|
from io import StringIO
|
|
4
|
-
|
|
5
|
-
from antlr4 import *
|
|
4
|
+
import sys
|
|
6
5
|
|
|
7
6
|
if sys.version_info[1] > 5:
|
|
8
7
|
from typing import TextIO
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from typing import Any, Dict, List, Set, Tuple, Union
|
|
3
3
|
|
|
4
|
-
from antlr4 import CommonTokenStream, InputStream, ParserRuleContext, ParseTreeWalker
|
|
5
|
-
from antlr4.error.ErrorListener import ErrorListener
|
|
6
4
|
from rich import print
|
|
7
5
|
from rich.console import Console
|
|
8
6
|
from rich.text import Text
|
|
@@ -17,6 +15,13 @@ from labtasker.client.core.exceptions import (
|
|
|
17
15
|
CmdTypeError,
|
|
18
16
|
)
|
|
19
17
|
from labtasker.client.core.logging import stderr_console
|
|
18
|
+
from labtasker.vendor.antlr4 import (
|
|
19
|
+
CommonTokenStream,
|
|
20
|
+
InputStream,
|
|
21
|
+
ParserRuleContext,
|
|
22
|
+
ParseTreeWalker,
|
|
23
|
+
)
|
|
24
|
+
from labtasker.vendor.antlr4.error.ErrorListener import ErrorListener
|
|
20
25
|
|
|
21
26
|
# Posix quote and windows quote
|
|
22
27
|
if os.name == "nt":
|
|
@@ -158,10 +163,10 @@ def format_print_error(
|
|
|
158
163
|
|
|
159
164
|
|
|
160
165
|
class CmdListener(LabCmdListener):
|
|
161
|
-
def __init__(self, variable_table,
|
|
166
|
+
def __init__(self, variable_table, use_quote: bool):
|
|
162
167
|
super().__init__()
|
|
163
168
|
self.variable_table = variable_table
|
|
164
|
-
self.
|
|
169
|
+
self.use_quote = use_quote
|
|
165
170
|
|
|
166
171
|
self.result_str = ""
|
|
167
172
|
self.args = set()
|
|
@@ -191,12 +196,15 @@ class CmdListener(LabCmdListener):
|
|
|
191
196
|
|
|
192
197
|
if isinstance(self.variable, dict):
|
|
193
198
|
# convert dict into bash string
|
|
194
|
-
|
|
199
|
+
# reverse quotes simplifies the conventional Python string representation
|
|
200
|
+
if self.use_quote:
|
|
195
201
|
self.result_str += quote(reverse_quotes(str(self.variable)))
|
|
196
202
|
else:
|
|
197
203
|
self.result_str += reverse_quotes(str(self.variable))
|
|
198
204
|
else:
|
|
199
|
-
self.result_str +=
|
|
205
|
+
self.result_str += (
|
|
206
|
+
quote(str(self.variable)) if self.use_quote else str(self.variable)
|
|
207
|
+
)
|
|
200
208
|
|
|
201
209
|
self.variable = None
|
|
202
210
|
|
|
@@ -290,9 +298,7 @@ def cmd_interpolate(
|
|
|
290
298
|
interpolated_cmd = []
|
|
291
299
|
involved_keys = set()
|
|
292
300
|
for c in cmd:
|
|
293
|
-
interpolated_str, keys = interpolate_str(
|
|
294
|
-
c, variable_table, quote_dict=False
|
|
295
|
-
)
|
|
301
|
+
interpolated_str, keys = interpolate_str(c, variable_table, use_quote=False)
|
|
296
302
|
interpolated_cmd.append(interpolated_str)
|
|
297
303
|
involved_keys.update(keys)
|
|
298
304
|
|
|
@@ -300,14 +306,14 @@ def cmd_interpolate(
|
|
|
300
306
|
|
|
301
307
|
|
|
302
308
|
def interpolate_str(
|
|
303
|
-
input_str: str, variable_table: Dict[str, Any],
|
|
309
|
+
input_str: str, variable_table: Dict[str, Any], use_quote: bool = True
|
|
304
310
|
) -> Tuple[str, Set[str]]:
|
|
305
311
|
"""
|
|
306
312
|
|
|
307
313
|
Args:
|
|
308
314
|
input_str:
|
|
309
315
|
variable_table:
|
|
310
|
-
|
|
316
|
+
use_quote: quote dict or strings using shlex.quote
|
|
311
317
|
|
|
312
318
|
Returns:
|
|
313
319
|
interpolated str, involved keys
|
|
@@ -325,7 +331,7 @@ def interpolate_str(
|
|
|
325
331
|
try:
|
|
326
332
|
tree = parser.command()
|
|
327
333
|
# Walk the parse tree with the custom listener
|
|
328
|
-
listener = CmdListener(variable_table=variable_table,
|
|
334
|
+
listener = CmdListener(variable_table=variable_table, use_quote=use_quote)
|
|
329
335
|
walker = ParseTreeWalker()
|
|
330
336
|
walker.walk(listener, tree)
|
|
331
337
|
except CmdParserError as e:
|
|
@@ -99,7 +99,7 @@ def requires_client_config(
|
|
|
99
99
|
if not _config and not get_labtasker_client_config_path().exists():
|
|
100
100
|
stderr_console.print(
|
|
101
101
|
"[bold red]Error:[/bold red] Configuration not initialized. "
|
|
102
|
-
"Run [orange1]`labtasker
|
|
102
|
+
"Run [orange1]`labtasker init`[/orange1] to initialize configuration."
|
|
103
103
|
)
|
|
104
104
|
raise typer.Exit(-1)
|
|
105
105
|
|
|
@@ -233,12 +233,15 @@ def loop_run(
|
|
|
233
233
|
# B. Ignore: Reset task to back to PENDING with retries count set to 0, as if this crashed run never happened
|
|
234
234
|
|
|
235
235
|
# 1. log exception
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
236
|
+
if isinstance(e, KeyboardInterrupt):
|
|
237
|
+
logger.warning("KeyboardInterrupt detected")
|
|
238
|
+
else:
|
|
239
|
+
logger.error(f"Task {current_task_id()} failed")
|
|
240
|
+
if not isinstance(e, _LabtaskerJobFailed):
|
|
241
|
+
stderr_console.print_exception(
|
|
242
|
+
# hide traceback from internals
|
|
243
|
+
suppress=[labtasker]
|
|
244
|
+
)
|
|
242
245
|
|
|
243
246
|
# 2. ask the user
|
|
244
247
|
_next_action = "report" # one of ["report", "ignore"]
|
|
@@ -256,7 +259,7 @@ def loop_run(
|
|
|
256
259
|
),
|
|
257
260
|
]
|
|
258
261
|
choice = make_a_choice(
|
|
259
|
-
question="Task failed with the above
|
|
262
|
+
question="Task interrupted or failed with the above info. You have 10 seconds to make a choice:",
|
|
260
263
|
options=choices,
|
|
261
264
|
# if timed out while waiting for user input, we assume the user is not present and report this crash by default
|
|
262
265
|
default=choices[0],
|
|
@@ -706,8 +706,8 @@ class QueryTranspiler(ast.NodeVisitor):
|
|
|
706
706
|
# Python 3.8 and earlier uses an Index node
|
|
707
707
|
if isinstance(node.slice, ast.Index):
|
|
708
708
|
# Extract the value from the Index node
|
|
709
|
-
if isinstance(node.slice.value, ast.Constant):
|
|
710
|
-
index = node.slice.value.value
|
|
709
|
+
if isinstance(node.slice.value, ast.Constant): # type: ignore[attr-defined]
|
|
710
|
+
index = node.slice.value.value # type: ignore[attr-defined]
|
|
711
711
|
# Type check: only allow string and integer subscripts
|
|
712
712
|
if isinstance(index, (str, int)):
|
|
713
713
|
return f"{value}.{index}"
|
|
@@ -719,16 +719,16 @@ class QueryTranspiler(ast.NodeVisitor):
|
|
|
719
719
|
exception=QueryTranspilerValueError,
|
|
720
720
|
)
|
|
721
721
|
# Handle negative indexing with unary operations
|
|
722
|
-
elif isinstance(node.slice.value, ast.UnaryOp) and isinstance(
|
|
723
|
-
node.slice.value.op, ast.USub
|
|
722
|
+
elif isinstance(node.slice.value, ast.UnaryOp) and isinstance( # type: ignore[attr-defined]
|
|
723
|
+
node.slice.value.op, ast.USub # type: ignore[attr-defined]
|
|
724
724
|
):
|
|
725
725
|
# Get the operand value if possible
|
|
726
726
|
operand_info = ""
|
|
727
727
|
try:
|
|
728
|
-
if isinstance(node.slice.value.operand, ast.Constant):
|
|
729
|
-
operand_info = f" (value: -{node.slice.value.operand.value})"
|
|
730
|
-
elif isinstance(node.slice.value.operand, ast.Name):
|
|
731
|
-
operand_info = f" (variable: -{node.slice.value.operand.id})"
|
|
728
|
+
if isinstance(node.slice.value.operand, ast.Constant): # type: ignore[attr-defined]
|
|
729
|
+
operand_info = f" (value: -{node.slice.value.operand.value})" # type: ignore[attr-defined]
|
|
730
|
+
elif isinstance(node.slice.value.operand, ast.Name): # type: ignore[attr-defined]
|
|
731
|
+
operand_info = f" (variable: -{node.slice.value.operand.id})" # type: ignore[attr-defined]
|
|
732
732
|
except AttributeError:
|
|
733
733
|
pass
|
|
734
734
|
|
|
@@ -742,7 +742,7 @@ class QueryTranspiler(ast.NodeVisitor):
|
|
|
742
742
|
self._report_error(
|
|
743
743
|
# More specific node location
|
|
744
744
|
node=node.slice.value, # type: ignore
|
|
745
|
-
msg=f"Unsupported index value type: {type(node.slice.value).__name__}",
|
|
745
|
+
msg=f"Unsupported index value type: {type(node.slice.value).__name__}", # type: ignore[attr-defined]
|
|
746
746
|
exception=QueryTranspilerValueError,
|
|
747
747
|
)
|
|
748
748
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Vendored dependencies
|
|
2
|
+
|
|
3
|
+
This folder contains libraries that are vendored from third party packages.
|
|
4
|
+
To add or modify a vendored library, just add or edit the corresponding dependency
|
|
5
|
+
in `vendor.txt`, and then run the `scripts/get_vendored.py` script.
|
|
6
|
+
|
|
7
|
+
**NOTE** all files in this folder apart from `__init__.py`, `vendor.txt` and `README.txt` are
|
|
8
|
+
dynamically generated; any manual modifications will be lost when running the `get_vendored` script
|