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.
Files changed (248) hide show
  1. {labtasker-0.2.5 → labtasker-0.2.7}/PKG-INFO +10 -13
  2. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/__init__.py +1 -1
  3. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/config.py +4 -3
  4. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/loop.py +122 -47
  5. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cli_utils.py +5 -1
  6. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/generated/LabCmd.py +2 -3
  7. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/generated/LabCmdLexer.py +2 -3
  8. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/generated/LabCmdListener.py +1 -1
  9. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/parser.py +18 -12
  10. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/config.py +1 -1
  11. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/job_runner.py +10 -7
  12. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/query_transpiler.py +9 -9
  13. labtasker-0.2.7/labtasker/vendor/README.txt +8 -0
  14. labtasker-0.2.7/labtasker/vendor/antlr4/BufferedTokenStream.py +302 -0
  15. labtasker-0.2.7/labtasker/vendor/antlr4/CommonTokenFactory.py +61 -0
  16. labtasker-0.2.7/labtasker/vendor/antlr4/CommonTokenStream.py +87 -0
  17. labtasker-0.2.7/labtasker/vendor/antlr4/FileStream.py +27 -0
  18. labtasker-0.2.7/labtasker/vendor/antlr4/InputStream.py +87 -0
  19. labtasker-0.2.7/labtasker/vendor/antlr4/IntervalSet.py +180 -0
  20. labtasker-0.2.7/labtasker/vendor/antlr4/LL1Analyzer.py +173 -0
  21. labtasker-0.2.7/labtasker/vendor/antlr4/Lexer.py +329 -0
  22. labtasker-0.2.7/labtasker/vendor/antlr4/ListTokenSource.py +144 -0
  23. labtasker-0.2.7/labtasker/vendor/antlr4/Parser.py +580 -0
  24. labtasker-0.2.7/labtasker/vendor/antlr4/ParserInterpreter.py +170 -0
  25. labtasker-0.2.7/labtasker/vendor/antlr4/ParserRuleContext.py +186 -0
  26. labtasker-0.2.7/labtasker/vendor/antlr4/PredictionContext.py +629 -0
  27. labtasker-0.2.7/labtasker/vendor/antlr4/Recognizer.py +147 -0
  28. labtasker-0.2.7/labtasker/vendor/antlr4/RuleContext.py +227 -0
  29. labtasker-0.2.7/labtasker/vendor/antlr4/StdinStream.py +11 -0
  30. labtasker-0.2.7/labtasker/vendor/antlr4/Token.py +155 -0
  31. labtasker-0.2.7/labtasker/vendor/antlr4/TokenStreamRewriter.py +255 -0
  32. labtasker-0.2.7/labtasker/vendor/antlr4/Utils.py +33 -0
  33. labtasker-0.2.7/labtasker/vendor/antlr4/__init__.py +21 -0
  34. labtasker-0.2.7/labtasker/vendor/antlr4/_pygrun.py +171 -0
  35. labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATN.py +132 -0
  36. labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNConfig.py +159 -0
  37. labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNConfigSet.py +212 -0
  38. labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNDeserializationOptions.py +24 -0
  39. labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNDeserializer.py +446 -0
  40. labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNSimulator.py +47 -0
  41. labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNState.py +264 -0
  42. labtasker-0.2.7/labtasker/vendor/antlr4/atn/ATNType.py +17 -0
  43. labtasker-0.2.7/labtasker/vendor/antlr4/atn/LexerATNSimulator.py +570 -0
  44. labtasker-0.2.7/labtasker/vendor/antlr4/atn/LexerAction.py +298 -0
  45. labtasker-0.2.7/labtasker/vendor/antlr4/atn/LexerActionExecutor.py +143 -0
  46. labtasker-0.2.7/labtasker/vendor/antlr4/atn/ParserATNSimulator.py +1661 -0
  47. labtasker-0.2.7/labtasker/vendor/antlr4/atn/PredictionMode.py +499 -0
  48. labtasker-0.2.7/labtasker/vendor/antlr4/atn/SemanticContext.py +330 -0
  49. labtasker-0.2.7/labtasker/vendor/antlr4/atn/Transition.py +268 -0
  50. labtasker-0.2.7/labtasker/vendor/antlr4/atn/__init__.py +1 -0
  51. labtasker-0.2.7/labtasker/vendor/antlr4/dfa/DFA.py +133 -0
  52. labtasker-0.2.7/labtasker/vendor/antlr4/dfa/DFASerializer.py +73 -0
  53. labtasker-0.2.7/labtasker/vendor/antlr4/dfa/DFAState.py +126 -0
  54. labtasker-0.2.7/labtasker/vendor/antlr4/dfa/__init__.py +1 -0
  55. labtasker-0.2.7/labtasker/vendor/antlr4/error/DiagnosticErrorListener.py +107 -0
  56. labtasker-0.2.7/labtasker/vendor/antlr4/error/ErrorListener.py +72 -0
  57. labtasker-0.2.7/labtasker/vendor/antlr4/error/ErrorStrategy.py +709 -0
  58. labtasker-0.2.7/labtasker/vendor/antlr4/error/Errors.py +173 -0
  59. labtasker-0.2.7/labtasker/vendor/antlr4/error/__init__.py +1 -0
  60. labtasker-0.2.7/labtasker/vendor/antlr4/tree/Chunk.py +30 -0
  61. labtasker-0.2.7/labtasker/vendor/antlr4/tree/ParseTreeMatch.py +118 -0
  62. labtasker-0.2.7/labtasker/vendor/antlr4/tree/ParseTreePattern.py +72 -0
  63. labtasker-0.2.7/labtasker/vendor/antlr4/tree/ParseTreePatternMatcher.py +374 -0
  64. labtasker-0.2.7/labtasker/vendor/antlr4/tree/RuleTagToken.py +50 -0
  65. labtasker-0.2.7/labtasker/vendor/antlr4/tree/TokenTagToken.py +47 -0
  66. labtasker-0.2.7/labtasker/vendor/antlr4/tree/Tree.py +191 -0
  67. labtasker-0.2.7/labtasker/vendor/antlr4/tree/Trees.py +111 -0
  68. labtasker-0.2.7/labtasker/vendor/antlr4/xpath/XPath.py +269 -0
  69. labtasker-0.2.7/labtasker/vendor/antlr4/xpath/XPathLexer.py +95 -0
  70. labtasker-0.2.7/labtasker/vendor/antlr4/xpath/__init__.py +1 -0
  71. labtasker-0.2.7/labtasker/vendor/vendor.txt +1 -0
  72. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker.egg-info/SOURCES.txt +62 -33
  73. {labtasker-0.2.5 → labtasker-0.2.7}/pyproject.toml +42 -17
  74. labtasker-0.2.7/tests/dummy_jobs/job_1.py +39 -0
  75. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_loop.py +51 -2
  76. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_task.py +1 -3
  77. labtasker-0.2.7/tests/test_server/test_db_utils/__init__.py +0 -0
  78. labtasker-0.2.7/tests/test_server/test_endpoint/__init__.py +0 -0
  79. labtasker-0.2.7/tests/test_utils/__init__.py +0 -0
  80. {labtasker-0.2.5 → labtasker-0.2.7}/tox.ini +1 -1
  81. labtasker-0.2.5/.coveragerc +0 -4
  82. labtasker-0.2.5/.dockerignore +0 -11
  83. labtasker-0.2.5/.flake8 +0 -19
  84. labtasker-0.2.5/.gitattributes +0 -217
  85. labtasker-0.2.5/.gitignore +0 -189
  86. labtasker-0.2.5/.gitmodules +0 -8
  87. labtasker-0.2.5/.pre-commit-config.yaml +0 -22
  88. labtasker-0.2.5/.pytest.ini +0 -9
  89. labtasker-0.2.5/.python-version +0 -6
  90. labtasker-0.2.5/Dockerfile +0 -25
  91. labtasker-0.2.5/PRIVACY.md +0 -1
  92. labtasker-0.2.5/demo/advanced/custom_resolver/submit_job.py +0 -11
  93. labtasker-0.2.5/demo/advanced/custom_resolver/w.py +0 -38
  94. labtasker-0.2.5/demo/advanced/custom_resolver/wo.py +0 -29
  95. labtasker-0.2.5/demo/advanced/event_system/email_on_task_failure.py +0 -67
  96. labtasker-0.2.5/demo/advanced/event_system/send_email.py +0 -46
  97. labtasker-0.2.5/demo/advanced/event_system/sim_unstable_job.py +0 -27
  98. labtasker-0.2.5/demo/advanced/event_system/submit.sh +0 -10
  99. labtasker-0.2.5/demo/basic/bash_demo/job_main.py +0 -23
  100. labtasker-0.2.5/demo/basic/bash_demo/run_job.sh +0 -8
  101. labtasker-0.2.5/demo/basic/bash_demo/submit_job.sh +0 -13
  102. labtasker-0.2.5/demo/basic/python_demo/run_job.py +0 -25
  103. labtasker-0.2.5/demo/basic/python_demo/submit_job.py +0 -9
  104. labtasker-0.2.5/docker/mongodb/init.d/init-keyfile.sh +0 -29
  105. labtasker-0.2.5/docker/mongodb/post-init.d/init-mongo.sh +0 -53
  106. labtasker-0.2.5/docker-compose.yml +0 -90
  107. labtasker-0.2.5/script_tests/test_ban_datetime_now/allowed.py +0 -80
  108. labtasker-0.2.5/script_tests/test_ban_datetime_now/disallowed.py +0 -129
  109. labtasker-0.2.5/script_tests/test_ban_datetime_now/test_ban.py +0 -138
  110. labtasker-0.2.5/scripts/asciinema_adjust_timestamp.py +0 -38
  111. labtasker-0.2.5/scripts/check_version.py +0 -59
  112. labtasker-0.2.5/scripts/datetime_now_checker.py +0 -268
  113. labtasker-0.2.5/server.example.env +0 -24
  114. labtasker-0.2.5/tests/dummy_jobs/job_1.py +0 -27
  115. {labtasker-0.2.5 → labtasker-0.2.7}/LICENSE +0 -0
  116. {labtasker-0.2.5 → labtasker-0.2.7}/MANIFEST.in +0 -0
  117. {labtasker-0.2.5 → labtasker-0.2.7}/README.md +0 -0
  118. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/__main__.py +0 -0
  119. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/api_models.py +0 -0
  120. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/__init__.py +0 -0
  121. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/__init__.py +0 -0
  122. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/cli.py +0 -0
  123. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/event.py +0 -0
  124. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/init.py +0 -0
  125. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/queue.py +0 -0
  126. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/task.py +0 -0
  127. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/cli/worker.py +0 -0
  128. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/client_api.py +0 -0
  129. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/__init__.py +0 -0
  130. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/api.py +0 -0
  131. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/LabCmd.g4 +0 -0
  132. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/LabCmdLexer.g4 +0 -0
  133. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/__init__.py +0 -0
  134. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/cmd_parser/generated/__init__.py +0 -0
  135. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/context.py +0 -0
  136. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/events.py +0 -0
  137. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/exceptions.py +0 -0
  138. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/heartbeat.py +0 -0
  139. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/logging.py +0 -0
  140. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/paths.py +0 -0
  141. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/plugin_utils.py +0 -0
  142. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/resolver/__init__.py +0 -0
  143. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/resolver/models.py +0 -0
  144. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/resolver/utils.py +0 -0
  145. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/utils.py +0 -0
  146. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/core/version_checker.py +0 -0
  147. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/templates/labtasker_root/.gitignore +0 -0
  148. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/templates/labtasker_root/client.toml +0 -0
  149. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/client/templates/labtasker_root/logs/.gitkeep +0 -0
  150. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/constants.py +0 -0
  151. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/filtering.py +0 -0
  152. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/security.py +0 -0
  153. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/__init__.py +0 -0
  154. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/cli.py +0 -0
  155. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/config.py +0 -0
  156. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/database.py +0 -0
  157. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/db_utils.py +0 -0
  158. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/dependencies.py +0 -0
  159. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/embedded_db.py +0 -0
  160. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/endpoints.py +0 -0
  161. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/event_manager.py +0 -0
  162. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/fsm.py +0 -0
  163. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/server/logging.py +0 -0
  164. {labtasker-0.2.5 → labtasker-0.2.7}/labtasker/utils.py +0 -0
  165. {labtasker-0.2.5/tests → labtasker-0.2.7/labtasker/vendor}/__init__.py +0 -0
  166. {labtasker-0.2.5/tests/fixtures → labtasker-0.2.7/labtasker/vendor/antlr4/tree}/__init__.py +0 -0
  167. {labtasker-0.2.5 → labtasker-0.2.7}/setup.cfg +0 -0
  168. {labtasker-0.2.5/tests/test_client → labtasker-0.2.7/tests}/__init__.py +0 -0
  169. {labtasker-0.2.5 → labtasker-0.2.7}/tests/conftest.py +0 -0
  170. {labtasker-0.2.5 → labtasker-0.2.7}/tests/demo_pager_iterator.py +0 -0
  171. {labtasker-0.2.5/tests/test_client/test_cli → labtasker-0.2.7/tests/fixtures}/__init__.py +0 -0
  172. {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/database/__init__.py +0 -0
  173. {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/database/mock.py +0 -0
  174. {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/database/real.py +0 -0
  175. {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/logging.py +0 -0
  176. {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/mock_datetime_now.py +0 -0
  177. {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/server/__init__.py +0 -0
  178. {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/server/async_app.py +0 -0
  179. {labtasker-0.2.5 → labtasker-0.2.7}/tests/fixtures/server/sync_app.py +0 -0
  180. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_api_models.py +0 -0
  181. {labtasker-0.2.5/tests/test_client/test_core → labtasker-0.2.7/tests/test_client}/__init__.py +0 -0
  182. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/conftest.py +0 -0
  183. {labtasker-0.2.5/tests/test_client/test_core/test_event → labtasker-0.2.7/tests/test_client/test_cli}/__init__.py +0 -0
  184. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/conftest.py +0 -0
  185. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_basic.py +0 -0
  186. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_config.py +0 -0
  187. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_event.py +0 -0
  188. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_init.py +0 -0
  189. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_queue.py +0 -0
  190. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_cli/test_worker.py +0 -0
  191. {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
  192. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_cli_utils.py +0 -0
  193. {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
  194. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_event/test_concurrency_job_flow_event.py +0 -0
  195. /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
  196. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_event/test_event_listener_basic.py +0 -0
  197. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_event/test_various_actions.py +0 -0
  198. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_event/utils.py +0 -0
  199. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_heartbeat.py +0 -0
  200. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_job_runner.py +0 -0
  201. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_logging.py +0 -0
  202. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_loop_internal_error_handler.py +0 -0
  203. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_pager_iterator.py +0 -0
  204. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_parser.py +0 -0
  205. {labtasker-0.2.5/tests/test_filtering → labtasker-0.2.7/tests/test_client/test_core/test_query_transpiler}/__init__.py +0 -0
  206. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_query_transpiler/conftest.py +0 -0
  207. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_query_transpiler/test_behavior.py +0 -0
  208. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_query_transpiler/test_matching.py +0 -0
  209. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_query_transpiler/test_utils.py +0 -0
  210. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_query_transpiler/utils.py +0 -0
  211. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_resolver.py +0 -0
  212. {labtasker-0.2.5/tests/test_server → labtasker-0.2.7/tests/test_client/test_core/test_runner_concurrency}/__init__.py +0 -0
  213. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_concurrency/run_concurrent.py +0 -0
  214. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_concurrency/test_runner_concurrency_success_failure.py +0 -0
  215. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_concurrency/test_runner_high_concurrency.py +0 -0
  216. {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
  217. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_timeout/conftest.py +0 -0
  218. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_timeout/test_job_runner_timeout.py +0 -0
  219. {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
  220. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_runner_with_resolver.py +0 -0
  221. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_server_notification_and_client_version.py +0 -0
  222. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_client/test_core/test_version_checker.py +0 -0
  223. {labtasker-0.2.5/tests/test_server/test_db_utils → labtasker-0.2.7/tests/test_filtering}/__init__.py +0 -0
  224. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_filtering/exception_utils.py +0 -0
  225. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_filtering/test_exception_filtering.py +0 -0
  226. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_mock_time.py +0 -0
  227. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_security.py +0 -0
  228. {labtasker-0.2.5/tests/test_server/test_endpoint → labtasker-0.2.7/tests/test_server}/__init__.py +0 -0
  229. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/conftest.py +0 -0
  230. {labtasker-0.2.5/tests/test_utils → labtasker-0.2.7/tests/test_server/test_database}/__init__.py +0 -0
  231. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_database/conftest.py +0 -0
  232. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_database/test_database_basic.py +0 -0
  233. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_database/test_fetch_extra_filter.py +0 -0
  234. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_database/test_query_dict_to_mongo_filter.py +0 -0
  235. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_database/test_required_field_fetching.py +0 -0
  236. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_db_utils/test_arg_match.py +0 -0
  237. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_db_utils/test_keys_to_query_dict.py +0 -0
  238. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_db_utils/test_keys_to_query_dict_deepest.py +0 -0
  239. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_db_utils/test_keys_to_query_dict_topmost.py +0 -0
  240. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_embedded_db.py +0 -0
  241. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_endpoint/test_event_basic.py +0 -0
  242. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_endpoint/test_server.py +0 -0
  243. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_endpoint/test_server_async.py +0 -0
  244. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_endpoint/test_server_async_ping.py +0 -0
  245. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_fsm.py +0 -0
  246. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_server/test_get_verified_queue_dependency.py +0 -0
  247. {labtasker-0.2.5 → labtasker-0.2.7}/tests/test_utils/test_utils.py +0 -0
  248. {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.5
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.8.1
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.116.0,>=0.115.0
26
- Requires-Dist: uvicorn[standard]<0.35.0,>=0.15.0
27
- Requires-Dist: click<9.0.0,>=8.1.0
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.16.0,>=0.15.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<25.0,>=24.2
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<0.27.0,>=0.24.0; extra == "dev"
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.26.0,>=4.24.0; extra == "dev"
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
@@ -1,4 +1,4 @@
1
- __version__ = "0.2.5"
1
+ __version__ = "0.2.7"
2
2
 
3
3
  from labtasker.client.client_api import *
4
4
  from labtasker.client.core.config import get_client_config
@@ -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 open(config_path, "rb") as existing_config, open(
52
- temp_file_path, "wb"
53
- ) as temp_file:
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
- "--command",
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
- if cmd and option_cmd:
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] and [--command] can be specified. Please use one of them."
192
+ "Only one of [CMD], [--command], or [--script-path] can be specified. Choose one."
107
193
  )
108
194
 
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()
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 not cmd:
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. Either specify via positional argument [CMD] or `--command`."
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(cmd, dummy_variable_table)
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: {cmd}")
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
- # Interpolate command
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 = False
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
- # Break loop when process completes and streams are empty
178
- if process.poll() is not None and not output and not error:
179
- break
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
- process.wait()
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
- return asyncio.run(prompt.prompt_async())
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 sys
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
@@ -1,8 +1,7 @@
1
1
  # Generated from labtasker/client/core/cmd_parser/generated/LabCmdLexer.g4 by ANTLR 4.13.2
2
- import sys
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,5 +1,5 @@
1
1
  # Generated from labtasker/client/core/cmd_parser/generated/LabCmd.g4 by ANTLR 4.13.2
2
- from antlr4 import *
2
+ from labtasker.vendor.antlr4 import *
3
3
 
4
4
  if "." in __name__:
5
5
  from .LabCmd import LabCmd
@@ -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, quote_dict: bool):
166
+ def __init__(self, variable_table, use_quote: bool):
162
167
  super().__init__()
163
168
  self.variable_table = variable_table
164
- self.quote_dict = quote_dict
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
- if self.quote_dict:
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 += str(self.variable)
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], quote_dict: bool = True
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
- quote_dict: quote dict string using shlex.quote
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, quote_dict=quote_dict)
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 config`[/orange1] to initialize configuration."
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
- logger.error(f"Task {current_task_id()} failed")
237
- if not isinstance(e, _LabtaskerJobFailed):
238
- stderr_console.print_exception(
239
- # hide traceback from internals
240
- suppress=[labtasker]
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 exception. You have 10 seconds to make a choice:",
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