labtasker 0.2.4__tar.gz → 0.2.6__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 (250) hide show
  1. {labtasker-0.2.4 → labtasker-0.2.6}/.coveragerc +1 -0
  2. {labtasker-0.2.4 → labtasker-0.2.6}/.flake8 +2 -1
  3. {labtasker-0.2.4 → labtasker-0.2.6}/.gitmodules +2 -2
  4. {labtasker-0.2.4 → labtasker-0.2.6}/.pre-commit-config.yaml +2 -0
  5. {labtasker-0.2.4 → labtasker-0.2.6}/PKG-INFO +14 -15
  6. {labtasker-0.2.4 → labtasker-0.2.6}/README.md +9 -9
  7. {labtasker-0.2.4 → labtasker-0.2.6}/docker-compose.yml +1 -1
  8. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/__init__.py +1 -1
  9. labtasker-0.2.6/labtasker/client/cli/loop.py +266 -0
  10. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/cli_utils.py +5 -1
  11. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/cmd_parser/generated/LabCmd.py +2 -3
  12. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/cmd_parser/generated/LabCmdLexer.py +2 -3
  13. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/cmd_parser/generated/LabCmdListener.py +1 -1
  14. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/cmd_parser/parser.py +18 -12
  15. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/job_runner.py +10 -7
  16. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/logging.py +1 -1
  17. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/database.py +0 -1
  18. labtasker-0.2.6/labtasker/vendor/README.txt +8 -0
  19. labtasker-0.2.6/labtasker/vendor/antlr4/BufferedTokenStream.py +302 -0
  20. labtasker-0.2.6/labtasker/vendor/antlr4/CommonTokenFactory.py +61 -0
  21. labtasker-0.2.6/labtasker/vendor/antlr4/CommonTokenStream.py +87 -0
  22. labtasker-0.2.6/labtasker/vendor/antlr4/FileStream.py +27 -0
  23. labtasker-0.2.6/labtasker/vendor/antlr4/InputStream.py +87 -0
  24. labtasker-0.2.6/labtasker/vendor/antlr4/IntervalSet.py +180 -0
  25. labtasker-0.2.6/labtasker/vendor/antlr4/LL1Analyzer.py +173 -0
  26. labtasker-0.2.6/labtasker/vendor/antlr4/Lexer.py +329 -0
  27. labtasker-0.2.6/labtasker/vendor/antlr4/ListTokenSource.py +144 -0
  28. labtasker-0.2.6/labtasker/vendor/antlr4/Parser.py +580 -0
  29. labtasker-0.2.6/labtasker/vendor/antlr4/ParserInterpreter.py +170 -0
  30. labtasker-0.2.6/labtasker/vendor/antlr4/ParserRuleContext.py +186 -0
  31. labtasker-0.2.6/labtasker/vendor/antlr4/PredictionContext.py +629 -0
  32. labtasker-0.2.6/labtasker/vendor/antlr4/Recognizer.py +147 -0
  33. labtasker-0.2.6/labtasker/vendor/antlr4/RuleContext.py +227 -0
  34. labtasker-0.2.6/labtasker/vendor/antlr4/StdinStream.py +11 -0
  35. labtasker-0.2.6/labtasker/vendor/antlr4/Token.py +155 -0
  36. labtasker-0.2.6/labtasker/vendor/antlr4/TokenStreamRewriter.py +255 -0
  37. labtasker-0.2.6/labtasker/vendor/antlr4/Utils.py +33 -0
  38. labtasker-0.2.6/labtasker/vendor/antlr4/__init__.py +21 -0
  39. labtasker-0.2.6/labtasker/vendor/antlr4/_pygrun.py +171 -0
  40. labtasker-0.2.6/labtasker/vendor/antlr4/atn/ATN.py +132 -0
  41. labtasker-0.2.6/labtasker/vendor/antlr4/atn/ATNConfig.py +159 -0
  42. labtasker-0.2.6/labtasker/vendor/antlr4/atn/ATNConfigSet.py +212 -0
  43. labtasker-0.2.6/labtasker/vendor/antlr4/atn/ATNDeserializationOptions.py +24 -0
  44. labtasker-0.2.6/labtasker/vendor/antlr4/atn/ATNDeserializer.py +446 -0
  45. labtasker-0.2.6/labtasker/vendor/antlr4/atn/ATNSimulator.py +47 -0
  46. labtasker-0.2.6/labtasker/vendor/antlr4/atn/ATNState.py +264 -0
  47. labtasker-0.2.6/labtasker/vendor/antlr4/atn/ATNType.py +17 -0
  48. labtasker-0.2.6/labtasker/vendor/antlr4/atn/LexerATNSimulator.py +570 -0
  49. labtasker-0.2.6/labtasker/vendor/antlr4/atn/LexerAction.py +298 -0
  50. labtasker-0.2.6/labtasker/vendor/antlr4/atn/LexerActionExecutor.py +143 -0
  51. labtasker-0.2.6/labtasker/vendor/antlr4/atn/ParserATNSimulator.py +1661 -0
  52. labtasker-0.2.6/labtasker/vendor/antlr4/atn/PredictionMode.py +499 -0
  53. labtasker-0.2.6/labtasker/vendor/antlr4/atn/SemanticContext.py +330 -0
  54. labtasker-0.2.6/labtasker/vendor/antlr4/atn/Transition.py +268 -0
  55. labtasker-0.2.6/labtasker/vendor/antlr4/atn/__init__.py +1 -0
  56. labtasker-0.2.6/labtasker/vendor/antlr4/dfa/DFA.py +133 -0
  57. labtasker-0.2.6/labtasker/vendor/antlr4/dfa/DFASerializer.py +73 -0
  58. labtasker-0.2.6/labtasker/vendor/antlr4/dfa/DFAState.py +126 -0
  59. labtasker-0.2.6/labtasker/vendor/antlr4/dfa/__init__.py +1 -0
  60. labtasker-0.2.6/labtasker/vendor/antlr4/error/DiagnosticErrorListener.py +107 -0
  61. labtasker-0.2.6/labtasker/vendor/antlr4/error/ErrorListener.py +72 -0
  62. labtasker-0.2.6/labtasker/vendor/antlr4/error/ErrorStrategy.py +709 -0
  63. labtasker-0.2.6/labtasker/vendor/antlr4/error/Errors.py +173 -0
  64. labtasker-0.2.6/labtasker/vendor/antlr4/error/__init__.py +1 -0
  65. labtasker-0.2.6/labtasker/vendor/antlr4/tree/Chunk.py +30 -0
  66. labtasker-0.2.6/labtasker/vendor/antlr4/tree/ParseTreeMatch.py +118 -0
  67. labtasker-0.2.6/labtasker/vendor/antlr4/tree/ParseTreePattern.py +72 -0
  68. labtasker-0.2.6/labtasker/vendor/antlr4/tree/ParseTreePatternMatcher.py +374 -0
  69. labtasker-0.2.6/labtasker/vendor/antlr4/tree/RuleTagToken.py +50 -0
  70. labtasker-0.2.6/labtasker/vendor/antlr4/tree/TokenTagToken.py +47 -0
  71. labtasker-0.2.6/labtasker/vendor/antlr4/tree/Tree.py +191 -0
  72. labtasker-0.2.6/labtasker/vendor/antlr4/tree/Trees.py +111 -0
  73. labtasker-0.2.6/labtasker/vendor/antlr4/xpath/XPath.py +269 -0
  74. labtasker-0.2.6/labtasker/vendor/antlr4/xpath/XPathLexer.py +95 -0
  75. labtasker-0.2.6/labtasker/vendor/antlr4/xpath/__init__.py +1 -0
  76. labtasker-0.2.6/labtasker/vendor/vendor.txt +1 -0
  77. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker.egg-info/SOURCES.txt +63 -0
  78. {labtasker-0.2.4 → labtasker-0.2.6}/pyproject.toml +35 -8
  79. labtasker-0.2.6/scripts/get_vendored.py +137 -0
  80. labtasker-0.2.6/tests/dummy_jobs/job_1.py +39 -0
  81. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_cli/test_loop.py +51 -2
  82. labtasker-0.2.6/tests/test_server/test_db_utils/__init__.py +0 -0
  83. labtasker-0.2.6/tests/test_server/test_endpoint/__init__.py +0 -0
  84. labtasker-0.2.6/tests/test_utils/__init__.py +0 -0
  85. labtasker-0.2.4/labtasker/client/cli/loop.py +0 -185
  86. labtasker-0.2.4/tests/dummy_jobs/job_1.py +0 -27
  87. {labtasker-0.2.4 → labtasker-0.2.6}/.dockerignore +0 -0
  88. {labtasker-0.2.4 → labtasker-0.2.6}/.gitattributes +0 -0
  89. {labtasker-0.2.4 → labtasker-0.2.6}/.gitignore +0 -0
  90. {labtasker-0.2.4 → labtasker-0.2.6}/.pytest.ini +0 -0
  91. {labtasker-0.2.4 → labtasker-0.2.6}/.python-version +0 -0
  92. {labtasker-0.2.4 → labtasker-0.2.6}/Dockerfile +0 -0
  93. {labtasker-0.2.4 → labtasker-0.2.6}/LICENSE +0 -0
  94. {labtasker-0.2.4 → labtasker-0.2.6}/MANIFEST.in +0 -0
  95. {labtasker-0.2.4 → labtasker-0.2.6}/PRIVACY.md +0 -0
  96. {labtasker-0.2.4 → labtasker-0.2.6}/demo/advanced/custom_resolver/submit_job.py +0 -0
  97. {labtasker-0.2.4 → labtasker-0.2.6}/demo/advanced/custom_resolver/w.py +0 -0
  98. {labtasker-0.2.4 → labtasker-0.2.6}/demo/advanced/custom_resolver/wo.py +0 -0
  99. {labtasker-0.2.4 → labtasker-0.2.6}/demo/advanced/event_system/email_on_task_failure.py +0 -0
  100. {labtasker-0.2.4 → labtasker-0.2.6}/demo/advanced/event_system/send_email.py +0 -0
  101. {labtasker-0.2.4 → labtasker-0.2.6}/demo/advanced/event_system/sim_unstable_job.py +0 -0
  102. {labtasker-0.2.4 → labtasker-0.2.6}/demo/advanced/event_system/submit.sh +0 -0
  103. {labtasker-0.2.4 → labtasker-0.2.6}/demo/basic/bash_demo/job_main.py +0 -0
  104. {labtasker-0.2.4 → labtasker-0.2.6}/demo/basic/bash_demo/run_job.sh +0 -0
  105. {labtasker-0.2.4 → labtasker-0.2.6}/demo/basic/bash_demo/submit_job.sh +0 -0
  106. {labtasker-0.2.4 → labtasker-0.2.6}/demo/basic/python_demo/run_job.py +0 -0
  107. {labtasker-0.2.4 → labtasker-0.2.6}/demo/basic/python_demo/submit_job.py +0 -0
  108. {labtasker-0.2.4 → labtasker-0.2.6}/docker/mongodb/init.d/init-keyfile.sh +0 -0
  109. {labtasker-0.2.4 → labtasker-0.2.6}/docker/mongodb/post-init.d/init-mongo.sh +0 -0
  110. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/__main__.py +0 -0
  111. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/api_models.py +0 -0
  112. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/__init__.py +0 -0
  113. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/cli/__init__.py +0 -0
  114. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/cli/cli.py +0 -0
  115. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/cli/config.py +0 -0
  116. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/cli/event.py +0 -0
  117. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/cli/init.py +0 -0
  118. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/cli/queue.py +0 -0
  119. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/cli/task.py +0 -0
  120. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/cli/worker.py +0 -0
  121. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/client_api.py +0 -0
  122. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/__init__.py +0 -0
  123. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/api.py +0 -0
  124. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/cmd_parser/LabCmd.g4 +0 -0
  125. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/cmd_parser/LabCmdLexer.g4 +0 -0
  126. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/cmd_parser/__init__.py +0 -0
  127. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/cmd_parser/generated/__init__.py +0 -0
  128. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/config.py +0 -0
  129. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/context.py +0 -0
  130. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/events.py +0 -0
  131. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/exceptions.py +0 -0
  132. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/heartbeat.py +0 -0
  133. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/paths.py +0 -0
  134. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/plugin_utils.py +0 -0
  135. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/query_transpiler.py +0 -0
  136. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/resolver/__init__.py +0 -0
  137. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/resolver/models.py +0 -0
  138. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/resolver/utils.py +0 -0
  139. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/utils.py +0 -0
  140. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/core/version_checker.py +0 -0
  141. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/templates/labtasker_root/.gitignore +0 -0
  142. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/templates/labtasker_root/client.toml +0 -0
  143. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/client/templates/labtasker_root/logs/.gitkeep +0 -0
  144. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/constants.py +0 -0
  145. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/filtering.py +0 -0
  146. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/security.py +0 -0
  147. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/__init__.py +0 -0
  148. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/cli.py +0 -0
  149. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/config.py +0 -0
  150. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/db_utils.py +0 -0
  151. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/dependencies.py +0 -0
  152. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/embedded_db.py +0 -0
  153. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/endpoints.py +0 -0
  154. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/event_manager.py +0 -0
  155. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/fsm.py +0 -0
  156. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/server/logging.py +0 -0
  157. {labtasker-0.2.4 → labtasker-0.2.6}/labtasker/utils.py +0 -0
  158. {labtasker-0.2.4/tests → labtasker-0.2.6/labtasker/vendor}/__init__.py +0 -0
  159. {labtasker-0.2.4/tests/fixtures → labtasker-0.2.6/labtasker/vendor/antlr4/tree}/__init__.py +0 -0
  160. {labtasker-0.2.4 → labtasker-0.2.6}/script_tests/test_ban_datetime_now/allowed.py +0 -0
  161. {labtasker-0.2.4 → labtasker-0.2.6}/script_tests/test_ban_datetime_now/disallowed.py +0 -0
  162. {labtasker-0.2.4 → labtasker-0.2.6}/script_tests/test_ban_datetime_now/test_ban.py +0 -0
  163. {labtasker-0.2.4 → labtasker-0.2.6}/scripts/asciinema_adjust_timestamp.py +0 -0
  164. {labtasker-0.2.4 → labtasker-0.2.6}/scripts/check_version.py +0 -0
  165. {labtasker-0.2.4 → labtasker-0.2.6}/scripts/datetime_now_checker.py +0 -0
  166. {labtasker-0.2.4 → labtasker-0.2.6}/server.example.env +0 -0
  167. {labtasker-0.2.4 → labtasker-0.2.6}/setup.cfg +0 -0
  168. {labtasker-0.2.4/tests/test_client → labtasker-0.2.6/tests}/__init__.py +0 -0
  169. {labtasker-0.2.4 → labtasker-0.2.6}/tests/conftest.py +0 -0
  170. {labtasker-0.2.4 → labtasker-0.2.6}/tests/demo_pager_iterator.py +0 -0
  171. {labtasker-0.2.4/tests/test_client/test_cli → labtasker-0.2.6/tests/fixtures}/__init__.py +0 -0
  172. {labtasker-0.2.4 → labtasker-0.2.6}/tests/fixtures/database/__init__.py +0 -0
  173. {labtasker-0.2.4 → labtasker-0.2.6}/tests/fixtures/database/mock.py +0 -0
  174. {labtasker-0.2.4 → labtasker-0.2.6}/tests/fixtures/database/real.py +0 -0
  175. {labtasker-0.2.4 → labtasker-0.2.6}/tests/fixtures/logging.py +0 -0
  176. {labtasker-0.2.4 → labtasker-0.2.6}/tests/fixtures/mock_datetime_now.py +0 -0
  177. {labtasker-0.2.4 → labtasker-0.2.6}/tests/fixtures/server/__init__.py +0 -0
  178. {labtasker-0.2.4 → labtasker-0.2.6}/tests/fixtures/server/async_app.py +0 -0
  179. {labtasker-0.2.4 → labtasker-0.2.6}/tests/fixtures/server/sync_app.py +0 -0
  180. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_api_models.py +0 -0
  181. {labtasker-0.2.4/tests/test_client/test_core → labtasker-0.2.6/tests/test_client}/__init__.py +0 -0
  182. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/conftest.py +0 -0
  183. {labtasker-0.2.4/tests/test_client/test_core/test_event → labtasker-0.2.6/tests/test_client/test_cli}/__init__.py +0 -0
  184. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_cli/conftest.py +0 -0
  185. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_cli/test_basic.py +0 -0
  186. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_cli/test_config.py +0 -0
  187. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_cli/test_event.py +0 -0
  188. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_cli/test_init.py +0 -0
  189. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_cli/test_queue.py +0 -0
  190. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_cli/test_task.py +0 -0
  191. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_cli/test_worker.py +0 -0
  192. {labtasker-0.2.4/tests/test_client/test_core/test_query_transpiler → labtasker-0.2.6/tests/test_client/test_core}/__init__.py +0 -0
  193. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_cli_utils.py +0 -0
  194. {labtasker-0.2.4/tests/test_client/test_core/test_runner_concurrency → labtasker-0.2.6/tests/test_client/test_core/test_event}/__init__.py +0 -0
  195. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_event/test_concurrency_job_flow_event.py +0 -0
  196. /labtasker-0.2.4/tests/test_client/test_core/test_runner_timeout/__init__.py → /labtasker-0.2.6/tests/test_client/test_core/test_event/test_event_integration.py +0 -0
  197. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_event/test_event_listener_basic.py +0 -0
  198. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_event/test_various_actions.py +0 -0
  199. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_event/utils.py +0 -0
  200. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_heartbeat.py +0 -0
  201. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_job_runner.py +0 -0
  202. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_logging.py +0 -0
  203. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_loop_internal_error_handler.py +0 -0
  204. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_pager_iterator.py +0 -0
  205. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_parser.py +0 -0
  206. {labtasker-0.2.4/tests/test_filtering → labtasker-0.2.6/tests/test_client/test_core/test_query_transpiler}/__init__.py +0 -0
  207. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_query_transpiler/conftest.py +0 -0
  208. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_query_transpiler/test_behavior.py +0 -0
  209. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_query_transpiler/test_matching.py +0 -0
  210. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_query_transpiler/test_utils.py +0 -0
  211. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_query_transpiler/utils.py +0 -0
  212. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_resolver.py +0 -0
  213. {labtasker-0.2.4/tests/test_server → labtasker-0.2.6/tests/test_client/test_core/test_runner_concurrency}/__init__.py +0 -0
  214. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_runner_concurrency/run_concurrent.py +0 -0
  215. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_runner_concurrency/test_runner_concurrency_success_failure.py +0 -0
  216. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_runner_concurrency/test_runner_high_concurrency.py +0 -0
  217. {labtasker-0.2.4/tests/test_server/test_database → labtasker-0.2.6/tests/test_client/test_core/test_runner_timeout}/__init__.py +0 -0
  218. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_runner_timeout/conftest.py +0 -0
  219. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_runner_timeout/test_job_runner_timeout.py +0 -0
  220. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_runner_timeout/test_job_runner_with_resolver_timeout.py +0 -0
  221. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_runner_with_resolver.py +0 -0
  222. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_server_notification_and_client_version.py +0 -0
  223. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_client/test_core/test_version_checker.py +0 -0
  224. {labtasker-0.2.4/tests/test_server/test_db_utils → labtasker-0.2.6/tests/test_filtering}/__init__.py +0 -0
  225. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_filtering/exception_utils.py +0 -0
  226. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_filtering/test_exception_filtering.py +0 -0
  227. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_mock_time.py +0 -0
  228. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_security.py +0 -0
  229. {labtasker-0.2.4/tests/test_server/test_endpoint → labtasker-0.2.6/tests/test_server}/__init__.py +0 -0
  230. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/conftest.py +0 -0
  231. {labtasker-0.2.4/tests/test_utils → labtasker-0.2.6/tests/test_server/test_database}/__init__.py +0 -0
  232. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_database/conftest.py +0 -0
  233. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_database/test_database_basic.py +0 -0
  234. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_database/test_fetch_extra_filter.py +0 -0
  235. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_database/test_query_dict_to_mongo_filter.py +0 -0
  236. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_database/test_required_field_fetching.py +0 -0
  237. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_db_utils/test_arg_match.py +0 -0
  238. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_db_utils/test_keys_to_query_dict.py +0 -0
  239. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_db_utils/test_keys_to_query_dict_deepest.py +0 -0
  240. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_db_utils/test_keys_to_query_dict_topmost.py +0 -0
  241. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_embedded_db.py +0 -0
  242. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_endpoint/test_event_basic.py +0 -0
  243. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_endpoint/test_server.py +0 -0
  244. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_endpoint/test_server_async.py +0 -0
  245. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_endpoint/test_server_async_ping.py +0 -0
  246. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_fsm.py +0 -0
  247. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_server/test_get_verified_queue_dependency.py +0 -0
  248. {labtasker-0.2.4 → labtasker-0.2.6}/tests/test_utils/test_utils.py +0 -0
  249. {labtasker-0.2.4 → labtasker-0.2.6}/tests/utils.py +0 -0
  250. {labtasker-0.2.4 → labtasker-0.2.6}/tox.ini +0 -0
@@ -2,3 +2,4 @@
2
2
  omit =
3
3
  # skip those auto-generated codes by antlr
4
4
  labtasker/client/core/cmd_parser/generated
5
+ labtasker/vendor/*
@@ -11,7 +11,8 @@ exclude =
11
11
  .tox,
12
12
  .mypy_cache,
13
13
  .pytest_cache,
14
- labtasker/client/core/cmd_parser/generated
14
+ labtasker/client/core/cmd_parser/generated,
15
+ labtasker/vendor
15
16
  per-file-ignores =
16
17
  __init__.py:F401,F403
17
18
  tests/*:F841,F811,F401,S101
@@ -1,8 +1,8 @@
1
1
  [submodule "plugins/labtasker_plugin_task_count"]
2
2
  path = plugins/labtasker_plugin_task_count
3
- url = https://github.com/fkcptlst/labtasker-plugin-task-count.git
3
+ url = https://github.com/luocfprime/labtasker-plugin-task-count.git
4
4
  branch = main
5
5
  [submodule "plugins/labtasker_plugin_script_generate"]
6
6
  path = plugins/labtasker_plugin_script_generate
7
- url = https://github.com/fkcptlst/labtasker-plugin-script-generate.git
7
+ url = https://github.com/luocfprime/labtasker-plugin-script-generate.git
8
8
  branch = main
@@ -1,3 +1,5 @@
1
+ exclude: ^labtasker/vendor/|^labtasker/client/core/cmd_parser/generated
2
+
1
3
  repos:
2
4
  - repo: https://github.com/pre-commit/pre-commit-hooks
3
5
  rev: v5.0.0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labtasker
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: A task queue system for lab experiments
5
5
  Author-email: Your Name <your.email@example.com>
6
6
  License: Apache License 2.0
7
- Project-URL: Homepage, https://github.com/fkcptlst/labtasker
8
- Project-URL: Documentation, https://fkcptlst.github.io/labtasker
9
- Project-URL: Repository, https://github.com/fkcptlst/labtasker.git
7
+ Project-URL: Homepage, https://github.com/luocfprime/labtasker
8
+ Project-URL: Documentation, https://luocfprime.github.io/labtasker
9
+ Project-URL: Repository, https://github.com/luocfprime/labtasker.git
10
10
  Classifier: Intended Audience :: Science/Research
11
11
  Classifier: License :: OSI Approved :: Apache Software License
12
12
  Classifier: Operating System :: OS Independent
@@ -20,13 +20,11 @@ Classifier: Programming Language :: Python :: 3.13
20
20
  Requires-Python: <4.0,>=3.8.1
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
- Requires-Dist: python-dotenv<2.0.0,>=1.0.1
24
23
  Requires-Dist: pymongo<5.0.0,>=4.0.0
25
24
  Requires-Dist: fastapi<0.116.0,>=0.115.0
26
25
  Requires-Dist: uvicorn[standard]<0.35.0,>=0.15.0
27
26
  Requires-Dist: click<9.0.0,>=8.1.0
28
27
  Requires-Dist: passlib<2.0.0,>=1.7.0
29
- Requires-Dist: antlr4-python3-runtime<5.0.0,>=4.13.0
30
28
  Requires-Dist: pydantic-settings<3.0.0,>=2.8.0
31
29
  Requires-Dist: httpx[socks]<0.29.0,>=0.28.0
32
30
  Requires-Dist: typer<0.16.0,>=0.15.0
@@ -43,6 +41,7 @@ Requires-Dist: noneprompt<0.2.0,>=0.1.9
43
41
  Requires-Dist: mongomock<4.4.0,>=4.3.0
44
42
  Requires-Dist: jsonpickle<5.0.0,>=4.0.2
45
43
  Requires-Dist: mslex<2.0.0,>=1.3.0
44
+ Requires-Dist: pexpect<5.0.0,>=4.9.0
46
45
  Provides-Extra: dev
47
46
  Requires-Dist: pytest<9.0.0,>=8.0.0; extra == "dev"
48
47
  Requires-Dist: pytest-cov<7.0.0,>=5.0.0; extra == "dev"
@@ -69,11 +68,11 @@ Dynamic: license-file
69
68
 
70
69
  <p align="center"><em>Make your ML experiment wrapper scripts smarter with...</em></p>
71
70
  <h1 align="center" style="font-size: 40px;"> <a href="">Labtasker</a></h1>
72
- <p align="center"><a href="https://fkcptlst.github.io/labtasker/latest/install/install/">Install</a> • <a href="https://fkcptlst.github.io/labtasker/latest/guide/basic/">Tutorial / Demo</a> • <a href="https://fkcptlst.github.io/labtasker/latest/">Documentation</a> • <a href="https://fkcptlst.github.io/labtasker/latest/faq/">FAQs</a> • <a href="https://github.com/fkcptlst/labtasker/releases">Releases</a></p>
71
+ <p align="center"><a href="https://luocfprime.github.io/labtasker/latest/install/install/">Install</a> • <a href="https://luocfprime.github.io/labtasker/latest/guide/basic/">Tutorial / Demo</a> • <a href="https://luocfprime.github.io/labtasker/latest/">Documentation</a> • <a href="https://luocfprime.github.io/labtasker/latest/faq/">FAQs</a> • <a href="https://github.com/luocfprime/labtasker/releases">Releases</a></p>
73
72
 
74
73
  <p align="center">
75
- <img src="https://github.com/fkcptlst/labtasker/actions/workflows/unit-test-matrix.yml/badge.svg" alt="unit-test-matrix" />
76
- <a href="https://codecov.io/gh/fkcptlst/labtasker"><img src="https://codecov.io/gh/fkcptlst/labtasker/graph/badge.svg?token=KQFBV3QRPY" alt="codecov" /></a>
74
+ <img src="https://github.com/luocfprime/labtasker/actions/workflows/unit-test-matrix.yml/badge.svg" alt="unit-test-matrix" />
75
+ <a href="https://codecov.io/gh/luocfprime/labtasker"><img src="https://codecov.io/gh/luocfprime/labtasker/graph/badge.svg?token=KQFBV3QRPY" alt="codecov" /></a>
77
76
  <img src="https://img.shields.io/badge/Python-3.8%20|%203.9%20|%203.10%20|%203.11%20|%203.12%20|%203.13-blue" alt="Python version" />
78
77
  <a href="https://pypi.org/project/labtasker/"><img src="https://img.shields.io/pypi/v/labtasker" alt="PyPI" /></a>
79
78
  </p>
@@ -91,7 +90,7 @@ parallelization, dynamic task prioritization, failure handling, halfway resume,
91
90
 
92
91
  ![comparison](docs/docs/assets/comparison.png)
93
92
 
94
- 🐳 For detailed examples and concepts, check out the [documentation](https://fkcptlst.github.io/labtasker/).
93
+ 🐳 For detailed examples and concepts, check out the [documentation](https://luocfprime.github.io/labtasker/).
95
94
 
96
95
  ## 🧪️ A Quick Demo
97
96
 
@@ -103,7 +102,7 @@ such as sending emails on task failure.
103
102
  ![demo](docs/docs/assets/gifs/demo.gif)
104
103
 
105
104
  For more detailed steps, please refer to the content in
106
- the [Tutorial / Demo](https://fkcptlst.github.io/labtasker/latest/guide/basic/).
105
+ the [Tutorial / Demo](https://luocfprime.github.io/labtasker/latest/guide/basic/).
107
106
 
108
107
  ## ⚡️ Features
109
108
 
@@ -133,7 +132,7 @@ the [Tutorial / Demo](https://fkcptlst.github.io/labtasker/latest/guide/basic/).
133
132
  > [!NOTE]
134
133
  > You need a running Labtasker server to use the client tools.
135
134
  > Simply use the installed Python CLI `labtasker-server serve` or use docker-compose to deploy the server.
136
- > See [deployment instructions](https://fkcptlst.github.io/labtasker/latest/install/deployment/).
135
+ > See [deployment instructions](https://luocfprime.github.io/labtasker/latest/install/deployment/).
137
136
 
138
137
  ### 1. Install via PyPI
139
138
 
@@ -145,7 +144,7 @@ pip install 'labtasker[plugins]'
145
144
  ### 2. Install the Latest Version from GitHub
146
145
 
147
146
  ```bash
148
- pip install git+https://github.com/fkcptlst/labtasker.git
147
+ pip install git+https://github.com/luocfprime/labtasker.git
149
148
  ```
150
149
 
151
150
  ## 🚀 Quick Start
@@ -165,13 +164,13 @@ labtasker init
165
164
  Then, use `labtasker submit` to submit tasks and use `labtasker loop` to run tasks across any number of workers.
166
165
 
167
166
  > [!TIP]
168
- > If you think manually writing 2 scripts for submit and run is laborious, you can checkout the [labtasker-plugin-script-generate](https://github.com/fkcptlst/labtasker-plugin-script-generate).
167
+ > If you think manually writing 2 scripts for submit and run is laborious, you can checkout the [labtasker-plugin-script-generate](https://github.com/luocfprime/labtasker-plugin-script-generate).
169
168
  > It automatically generate 2 scripts based on the script you provided.
170
169
 
171
170
  ## 📚 Documentation
172
171
 
173
172
  For detailed information on demo, tutorial, deployment, usage, please refer to
174
- the [documentation](https://fkcptlst.github.io/labtasker/).
173
+ the [documentation](https://luocfprime.github.io/labtasker/).
175
174
 
176
175
  ## 🔒 License
177
176
 
@@ -1,10 +1,10 @@
1
1
  <p align="center"><em>Make your ML experiment wrapper scripts smarter with...</em></p>
2
2
  <h1 align="center" style="font-size: 40px;"> <a href="">Labtasker</a></h1>
3
- <p align="center"><a href="https://fkcptlst.github.io/labtasker/latest/install/install/">Install</a> • <a href="https://fkcptlst.github.io/labtasker/latest/guide/basic/">Tutorial / Demo</a> • <a href="https://fkcptlst.github.io/labtasker/latest/">Documentation</a> • <a href="https://fkcptlst.github.io/labtasker/latest/faq/">FAQs</a> • <a href="https://github.com/fkcptlst/labtasker/releases">Releases</a></p>
3
+ <p align="center"><a href="https://luocfprime.github.io/labtasker/latest/install/install/">Install</a> • <a href="https://luocfprime.github.io/labtasker/latest/guide/basic/">Tutorial / Demo</a> • <a href="https://luocfprime.github.io/labtasker/latest/">Documentation</a> • <a href="https://luocfprime.github.io/labtasker/latest/faq/">FAQs</a> • <a href="https://github.com/luocfprime/labtasker/releases">Releases</a></p>
4
4
 
5
5
  <p align="center">
6
- <img src="https://github.com/fkcptlst/labtasker/actions/workflows/unit-test-matrix.yml/badge.svg" alt="unit-test-matrix" />
7
- <a href="https://codecov.io/gh/fkcptlst/labtasker"><img src="https://codecov.io/gh/fkcptlst/labtasker/graph/badge.svg?token=KQFBV3QRPY" alt="codecov" /></a>
6
+ <img src="https://github.com/luocfprime/labtasker/actions/workflows/unit-test-matrix.yml/badge.svg" alt="unit-test-matrix" />
7
+ <a href="https://codecov.io/gh/luocfprime/labtasker"><img src="https://codecov.io/gh/luocfprime/labtasker/graph/badge.svg?token=KQFBV3QRPY" alt="codecov" /></a>
8
8
  <img src="https://img.shields.io/badge/Python-3.8%20|%203.9%20|%203.10%20|%203.11%20|%203.12%20|%203.13-blue" alt="Python version" />
9
9
  <a href="https://pypi.org/project/labtasker/"><img src="https://img.shields.io/pypi/v/labtasker" alt="PyPI" /></a>
10
10
  </p>
@@ -22,7 +22,7 @@ parallelization, dynamic task prioritization, failure handling, halfway resume,
22
22
 
23
23
  ![comparison](docs/docs/assets/comparison.png)
24
24
 
25
- 🐳 For detailed examples and concepts, check out the [documentation](https://fkcptlst.github.io/labtasker/).
25
+ 🐳 For detailed examples and concepts, check out the [documentation](https://luocfprime.github.io/labtasker/).
26
26
 
27
27
  ## 🧪️ A Quick Demo
28
28
 
@@ -34,7 +34,7 @@ such as sending emails on task failure.
34
34
  ![demo](docs/docs/assets/gifs/demo.gif)
35
35
 
36
36
  For more detailed steps, please refer to the content in
37
- the [Tutorial / Demo](https://fkcptlst.github.io/labtasker/latest/guide/basic/).
37
+ the [Tutorial / Demo](https://luocfprime.github.io/labtasker/latest/guide/basic/).
38
38
 
39
39
  ## ⚡️ Features
40
40
 
@@ -64,7 +64,7 @@ the [Tutorial / Demo](https://fkcptlst.github.io/labtasker/latest/guide/basic/).
64
64
  > [!NOTE]
65
65
  > You need a running Labtasker server to use the client tools.
66
66
  > Simply use the installed Python CLI `labtasker-server serve` or use docker-compose to deploy the server.
67
- > See [deployment instructions](https://fkcptlst.github.io/labtasker/latest/install/deployment/).
67
+ > See [deployment instructions](https://luocfprime.github.io/labtasker/latest/install/deployment/).
68
68
 
69
69
  ### 1. Install via PyPI
70
70
 
@@ -76,7 +76,7 @@ pip install 'labtasker[plugins]'
76
76
  ### 2. Install the Latest Version from GitHub
77
77
 
78
78
  ```bash
79
- pip install git+https://github.com/fkcptlst/labtasker.git
79
+ pip install git+https://github.com/luocfprime/labtasker.git
80
80
  ```
81
81
 
82
82
  ## 🚀 Quick Start
@@ -96,13 +96,13 @@ labtasker init
96
96
  Then, use `labtasker submit` to submit tasks and use `labtasker loop` to run tasks across any number of workers.
97
97
 
98
98
  > [!TIP]
99
- > If you think manually writing 2 scripts for submit and run is laborious, you can checkout the [labtasker-plugin-script-generate](https://github.com/fkcptlst/labtasker-plugin-script-generate).
99
+ > If you think manually writing 2 scripts for submit and run is laborious, you can checkout the [labtasker-plugin-script-generate](https://github.com/luocfprime/labtasker-plugin-script-generate).
100
100
  > It automatically generate 2 scripts based on the script you provided.
101
101
 
102
102
  ## 📚 Documentation
103
103
 
104
104
  For detailed information on demo, tutorial, deployment, usage, please refer to
105
- the [documentation](https://fkcptlst.github.io/labtasker/).
105
+ the [documentation](https://luocfprime.github.io/labtasker/).
106
106
 
107
107
  ## 🔒 License
108
108
 
@@ -58,7 +58,7 @@ services:
58
58
  context: .
59
59
  dockerfile: Dockerfile
60
60
  network: host
61
- image: ghcr.io/fkcptlst/labtasker-api
61
+ image: ghcr.io/luocfprime/labtasker-api
62
62
  container_name: labtasker-api
63
63
  environment:
64
64
  - DB_USER=${DB_USER}
@@ -1,4 +1,4 @@
1
- __version__ = "0.2.4"
1
+ __version__ = "0.2.6"
2
2
 
3
3
  from labtasker.client.client_api import *
4
4
  from labtasker.client.core.config import get_client_config
@@ -0,0 +1,266 @@
1
+ """Implements `labtasker loop xxx`"""
2
+
3
+ import json
4
+ import os
5
+ import subprocess
6
+ import sys
7
+ from collections import defaultdict
8
+ from pathlib import Path
9
+ from typing import List, Optional
10
+
11
+ import pexpect
12
+ import typer
13
+ from typing_extensions import Annotated
14
+
15
+ from labtasker.client.cli.cli import app
16
+ from labtasker.client.core.cli_utils import (
17
+ cli_utils_decorator,
18
+ eta_max_validation,
19
+ parse_filter,
20
+ )
21
+ from labtasker.client.core.cmd_parser import cmd_interpolate
22
+ from labtasker.client.core.config import get_client_config
23
+ from labtasker.client.core.context import task_info
24
+ from labtasker.client.core.exceptions import CmdParserError, _LabtaskerJobFailed
25
+ from labtasker.client.core.job_runner import loop_run
26
+ from labtasker.client.core.logging import (
27
+ logger,
28
+ set_verbose,
29
+ stderr_console,
30
+ verbose_print,
31
+ )
32
+
33
+
34
+ class InfiniteDefaultDict(defaultdict):
35
+
36
+ def __getitem__(self, key):
37
+ if key not in self:
38
+ self[key] = InfiniteDefaultDict()
39
+ return super().__getitem__(key)
40
+
41
+ def get(self, key, default=None):
42
+ if key not in self:
43
+ self[key] = InfiniteDefaultDict()
44
+ return super().get(key, default)
45
+
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
+
117
+ @app.command()
118
+ @cli_utils_decorator
119
+ def loop(
120
+ cmd: Annotated[
121
+ List[str],
122
+ typer.Argument(
123
+ ...,
124
+ help="Command to run. Supports argument interpolation using %(arg_name) syntax. Example: `python main.py '%(input_file)' '%(output_dir)'`",
125
+ ),
126
+ ] = None,
127
+ option_cmd: str = typer.Option(
128
+ None,
129
+ "--command",
130
+ "--cmd",
131
+ "-c",
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.",
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
+ ),
140
+ executable: Optional[str] = typer.Option(
141
+ None,
142
+ "--executable",
143
+ help="Specify the shell executable to run the command with.",
144
+ ),
145
+ extra_filter: Optional[str] = typer.Option(
146
+ None,
147
+ "--extra-filter",
148
+ "-f",
149
+ help='Filter tasks using MongoDB query syntax (e.g., \'{"metadata.tag": {"$in": ["a", "b"]}}\') or Python expression (e.g., \'metadata.tag in ["a", "b"] and priority == 10\').',
150
+ ),
151
+ worker_id: Optional[str] = typer.Option(
152
+ None,
153
+ help="Assign a specific worker ID to run the tasks under.",
154
+ ),
155
+ eta_max: Optional[str] = typer.Option(
156
+ None,
157
+ callback=eta_max_validation,
158
+ help="Maximum estimated time for task completion (e.g. '1h', '1h30m', '50s'). After which the task will be considered timed out.",
159
+ ),
160
+ heartbeat_timeout: Optional[float] = typer.Option(
161
+ None,
162
+ help="Time in seconds before a task is considered stalled if no heartbeat is received.",
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
+ ),
169
+ verbose: bool = typer.Option( # noqa
170
+ False,
171
+ "--verbose",
172
+ "-v",
173
+ help="Enable verbose output.",
174
+ callback=set_verbose,
175
+ is_eager=True,
176
+ ),
177
+ ):
178
+ """Process tasks from the queue by repeatedly running a command with task arguments.
179
+
180
+ The command uses template syntax to insert task arguments. For example:
181
+
182
+ labtasker loop -- python process.py --input '%(input_file)' --output '%(output_dir)'
183
+
184
+ This will fetch tasks with 'input_file' and 'output_dir' arguments and run the command
185
+ with those values substituted. Tasks are processed until the queue is empty.
186
+ """
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:
191
+ raise typer.BadParameter(
192
+ "Only one of [CMD], [--command], or [--script-path] can be specified. Choose one."
193
+ )
194
+
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()
202
+
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:
209
+ raise typer.BadParameter(
210
+ "Command cannot be empty. Specify via positional argument [CMD] or `--command` or `--script-path`."
211
+ )
212
+
213
+ parsed_filter = parse_filter(extra_filter)
214
+ verbose_print(f"Parsed filter: {json.dumps(parsed_filter, indent=4)}")
215
+
216
+ if heartbeat_timeout is None:
217
+ heartbeat_timeout = get_client_config().task.heartbeat_interval * 3
218
+
219
+ # Generate required fields dict
220
+ dummy_variable_table = InfiniteDefaultDict()
221
+ try:
222
+ _, queried_keys = cmd_interpolate(input_cmd, dummy_variable_table)
223
+ except (CmdParserError, KeyError, TypeError) as e:
224
+ raise typer.BadParameter(f"Command error with exception {e}")
225
+
226
+ required_fields = list(queried_keys)
227
+
228
+ logger.info(f"Got command: {input_cmd}")
229
+
230
+ @loop_run(
231
+ required_fields=required_fields,
232
+ extra_filter=parsed_filter,
233
+ worker_id=worker_id,
234
+ eta_max=eta_max,
235
+ heartbeat_timeout=heartbeat_timeout,
236
+ pass_args_dict=True,
237
+ )
238
+ def run_cmd(args):
239
+ interpolated_cmd, _ = cmd_interpolate(input_cmd, args)
240
+ logger.info(f"Prepared to run interpolated command: {interpolated_cmd}")
241
+
242
+ use_shell = isinstance(interpolated_cmd, str)
243
+
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
+ )
253
+
254
+ if exit_code != 0:
255
+ raise _LabtaskerJobFailed(
256
+ f"Job process finished with non-zero exit code: {exit_code}"
257
+ )
258
+
259
+ except Exception as e:
260
+ raise _LabtaskerJobFailed(f"Error running command: {str(e)}")
261
+
262
+ logger.info(f"Task {task_info().task_id} ended.")
263
+
264
+ run_cmd()
265
+
266
+ logger.info("Loop ended.")
@@ -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:
@@ -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],