labtasker 0.2.9__tar.gz → 0.2.11__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 (215) hide show
  1. {labtasker-0.2.9 → labtasker-0.2.11}/PKG-INFO +3 -2
  2. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/__init__.py +1 -1
  3. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/client_api.py +16 -12
  4. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/api.py +22 -4
  5. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/cli_utils.py +2 -2
  6. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/events.py +2 -2
  7. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/job_runner.py +18 -9
  8. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/query_transpiler.py +23 -4
  9. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/utils.py +27 -1
  10. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/constants.py +0 -1
  11. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/database.py +1 -0
  12. {labtasker-0.2.9 → labtasker-0.2.11}/pyproject.toml +4 -3
  13. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_job_runner.py +36 -0
  14. {labtasker-0.2.9 → labtasker-0.2.11}/LICENSE +0 -0
  15. {labtasker-0.2.9 → labtasker-0.2.11}/MANIFEST.in +0 -0
  16. {labtasker-0.2.9 → labtasker-0.2.11}/README.md +0 -0
  17. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/__main__.py +0 -0
  18. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/api_models.py +0 -0
  19. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/__init__.py +0 -0
  20. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/cli/__init__.py +0 -0
  21. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/cli/cli.py +0 -0
  22. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/cli/config.py +0 -0
  23. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/cli/event.py +0 -0
  24. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/cli/init.py +0 -0
  25. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/cli/loop.py +0 -0
  26. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/cli/queue.py +0 -0
  27. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/cli/task.py +0 -0
  28. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/cli/worker.py +0 -0
  29. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/__init__.py +0 -0
  30. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/cmd_parser/LabCmd.g4 +0 -0
  31. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/cmd_parser/LabCmdLexer.g4 +0 -0
  32. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/cmd_parser/__init__.py +0 -0
  33. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/cmd_parser/generated/LabCmd.py +0 -0
  34. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/cmd_parser/generated/LabCmdLexer.py +0 -0
  35. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/cmd_parser/generated/LabCmdListener.py +0 -0
  36. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/cmd_parser/generated/__init__.py +0 -0
  37. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/cmd_parser/parser.py +0 -0
  38. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/config.py +0 -0
  39. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/context.py +0 -0
  40. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/exceptions.py +0 -0
  41. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/heartbeat.py +0 -0
  42. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/logging.py +0 -0
  43. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/pager.py +0 -0
  44. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/paths.py +0 -0
  45. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/plugin_utils.py +0 -0
  46. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/resolver/__init__.py +0 -0
  47. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/resolver/models.py +0 -0
  48. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/resolver/utils.py +0 -0
  49. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/core/version_checker.py +0 -0
  50. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/templates/labtasker_root/.gitignore +0 -0
  51. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/templates/labtasker_root/client.toml +0 -0
  52. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/client/templates/labtasker_root/logs/.gitkeep +0 -0
  53. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/filtering.py +0 -0
  54. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/security.py +0 -0
  55. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/__init__.py +0 -0
  56. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/cli.py +0 -0
  57. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/config.py +0 -0
  58. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/db_utils.py +0 -0
  59. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/dependencies.py +0 -0
  60. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/embedded_db.py +0 -0
  61. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/endpoints.py +0 -0
  62. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/event_manager.py +0 -0
  63. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/fsm.py +0 -0
  64. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/server/logging.py +0 -0
  65. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/utils.py +0 -0
  66. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/README.txt +0 -0
  67. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/__init__.py +0 -0
  68. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/BufferedTokenStream.py +0 -0
  69. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/CommonTokenFactory.py +0 -0
  70. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/CommonTokenStream.py +0 -0
  71. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/FileStream.py +0 -0
  72. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/InputStream.py +0 -0
  73. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/IntervalSet.py +0 -0
  74. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/LL1Analyzer.py +0 -0
  75. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/Lexer.py +0 -0
  76. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/ListTokenSource.py +0 -0
  77. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/Parser.py +0 -0
  78. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/ParserInterpreter.py +0 -0
  79. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/ParserRuleContext.py +0 -0
  80. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/PredictionContext.py +0 -0
  81. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/Recognizer.py +0 -0
  82. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/RuleContext.py +0 -0
  83. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/StdinStream.py +0 -0
  84. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/Token.py +0 -0
  85. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/TokenStreamRewriter.py +0 -0
  86. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/Utils.py +0 -0
  87. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/__init__.py +0 -0
  88. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/_pygrun.py +0 -0
  89. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/ATN.py +0 -0
  90. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/ATNConfig.py +0 -0
  91. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/ATNConfigSet.py +0 -0
  92. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/ATNDeserializationOptions.py +0 -0
  93. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/ATNDeserializer.py +0 -0
  94. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/ATNSimulator.py +0 -0
  95. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/ATNState.py +0 -0
  96. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/ATNType.py +0 -0
  97. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/LexerATNSimulator.py +0 -0
  98. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/LexerAction.py +0 -0
  99. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/LexerActionExecutor.py +0 -0
  100. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/ParserATNSimulator.py +0 -0
  101. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/PredictionMode.py +0 -0
  102. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/SemanticContext.py +0 -0
  103. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/Transition.py +0 -0
  104. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/atn/__init__.py +0 -0
  105. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/dfa/DFA.py +0 -0
  106. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/dfa/DFASerializer.py +0 -0
  107. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/dfa/DFAState.py +0 -0
  108. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/dfa/__init__.py +0 -0
  109. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/error/DiagnosticErrorListener.py +0 -0
  110. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/error/ErrorListener.py +0 -0
  111. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/error/ErrorStrategy.py +0 -0
  112. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/error/Errors.py +0 -0
  113. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/error/__init__.py +0 -0
  114. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/tree/Chunk.py +0 -0
  115. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/tree/ParseTreeMatch.py +0 -0
  116. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/tree/ParseTreePattern.py +0 -0
  117. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/tree/ParseTreePatternMatcher.py +0 -0
  118. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/tree/RuleTagToken.py +0 -0
  119. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/tree/TokenTagToken.py +0 -0
  120. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/tree/Tree.py +0 -0
  121. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/tree/Trees.py +0 -0
  122. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/tree/__init__.py +0 -0
  123. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/xpath/XPath.py +0 -0
  124. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/xpath/XPathLexer.py +0 -0
  125. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/antlr4/xpath/__init__.py +0 -0
  126. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker/vendor/vendor.txt +0 -0
  127. {labtasker-0.2.9 → labtasker-0.2.11}/labtasker.egg-info/SOURCES.txt +0 -0
  128. {labtasker-0.2.9 → labtasker-0.2.11}/setup.cfg +0 -0
  129. {labtasker-0.2.9 → labtasker-0.2.11}/tests/__init__.py +0 -0
  130. {labtasker-0.2.9 → labtasker-0.2.11}/tests/conftest.py +0 -0
  131. {labtasker-0.2.9 → labtasker-0.2.11}/tests/demo_pager_iterator.py +0 -0
  132. {labtasker-0.2.9 → labtasker-0.2.11}/tests/dummy_jobs/job_1.py +0 -0
  133. {labtasker-0.2.9 → labtasker-0.2.11}/tests/fixtures/__init__.py +0 -0
  134. {labtasker-0.2.9 → labtasker-0.2.11}/tests/fixtures/database/__init__.py +0 -0
  135. {labtasker-0.2.9 → labtasker-0.2.11}/tests/fixtures/database/mock.py +0 -0
  136. {labtasker-0.2.9 → labtasker-0.2.11}/tests/fixtures/database/real.py +0 -0
  137. {labtasker-0.2.9 → labtasker-0.2.11}/tests/fixtures/logging.py +0 -0
  138. {labtasker-0.2.9 → labtasker-0.2.11}/tests/fixtures/mock_datetime_now.py +0 -0
  139. {labtasker-0.2.9 → labtasker-0.2.11}/tests/fixtures/server/__init__.py +0 -0
  140. {labtasker-0.2.9 → labtasker-0.2.11}/tests/fixtures/server/async_app.py +0 -0
  141. {labtasker-0.2.9 → labtasker-0.2.11}/tests/fixtures/server/sync_app.py +0 -0
  142. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_api_models.py +0 -0
  143. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/__init__.py +0 -0
  144. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/conftest.py +0 -0
  145. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_cli/__init__.py +0 -0
  146. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_cli/conftest.py +0 -0
  147. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_cli/test_basic.py +0 -0
  148. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_cli/test_config.py +0 -0
  149. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_cli/test_event.py +0 -0
  150. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_cli/test_init.py +0 -0
  151. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_cli/test_loop.py +0 -0
  152. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_cli/test_queue.py +0 -0
  153. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_cli/test_task.py +0 -0
  154. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_cli/test_worker.py +0 -0
  155. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/__init__.py +0 -0
  156. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_cli_utils.py +0 -0
  157. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_event/__init__.py +0 -0
  158. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_event/test_concurrency_job_flow_event.py +0 -0
  159. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_event/test_event_listener_basic.py +0 -0
  160. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_event/test_event_listener_entity_data.py +0 -0
  161. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_event/test_various_actions.py +0 -0
  162. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_event/utils.py +0 -0
  163. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_heartbeat.py +0 -0
  164. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_logging.py +0 -0
  165. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_loop_internal_error_handler.py +0 -0
  166. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_pager_iterator.py +0 -0
  167. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_parser.py +0 -0
  168. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_query_transpiler/__init__.py +0 -0
  169. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_query_transpiler/conftest.py +0 -0
  170. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_query_transpiler/test_behavior.py +0 -0
  171. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_query_transpiler/test_matching.py +0 -0
  172. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_query_transpiler/test_utils.py +0 -0
  173. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_query_transpiler/utils.py +0 -0
  174. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_resolver.py +0 -0
  175. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_runner_concurrency/__init__.py +0 -0
  176. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_runner_concurrency/run_concurrent.py +0 -0
  177. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_runner_concurrency/test_runner_concurrency_success_failure.py +0 -0
  178. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_runner_concurrency/test_runner_high_concurrency.py +0 -0
  179. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_runner_timeout/__init__.py +0 -0
  180. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_runner_timeout/conftest.py +0 -0
  181. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_runner_timeout/test_job_runner_timeout.py +0 -0
  182. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_runner_timeout/test_job_runner_with_resolver_timeout.py +0 -0
  183. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_runner_with_resolver.py +0 -0
  184. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_server_notification_and_client_version.py +0 -0
  185. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_client/test_core/test_version_checker.py +0 -0
  186. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_filtering/__init__.py +0 -0
  187. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_filtering/exception_utils.py +0 -0
  188. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_filtering/test_exception_filtering.py +0 -0
  189. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_mock_time.py +0 -0
  190. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_security.py +0 -0
  191. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/__init__.py +0 -0
  192. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/conftest.py +0 -0
  193. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_database/__init__.py +0 -0
  194. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_database/conftest.py +0 -0
  195. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_database/test_database_basic.py +0 -0
  196. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_database/test_fetch_extra_filter.py +0 -0
  197. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_database/test_query_dict_to_mongo_filter.py +0 -0
  198. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_database/test_required_field_fetching.py +0 -0
  199. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_db_utils/__init__.py +0 -0
  200. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_db_utils/test_arg_match.py +0 -0
  201. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_db_utils/test_keys_to_query_dict.py +0 -0
  202. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_db_utils/test_keys_to_query_dict_deepest.py +0 -0
  203. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_db_utils/test_keys_to_query_dict_topmost.py +0 -0
  204. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_embedded_db.py +0 -0
  205. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_endpoint/__init__.py +0 -0
  206. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_endpoint/test_event_basic.py +0 -0
  207. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_endpoint/test_server.py +0 -0
  208. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_endpoint/test_server_async.py +0 -0
  209. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_endpoint/test_server_async_ping.py +0 -0
  210. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_fsm.py +0 -0
  211. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_server/test_get_verified_queue_dependency.py +0 -0
  212. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_utils/__init__.py +0 -0
  213. {labtasker-0.2.9 → labtasker-0.2.11}/tests/test_utils/test_utils.py +0 -0
  214. {labtasker-0.2.9 → labtasker-0.2.11}/tests/utils.py +0 -0
  215. {labtasker-0.2.9 → labtasker-0.2.11}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labtasker
3
- Version: 0.2.9
3
+ Version: 0.2.11
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
@@ -32,7 +32,7 @@ Requires-Dist: pyyaml<7.0.0,>=6.0.0
32
32
  Requires-Dist: tomlkit<0.14.0,>=0.13.2
33
33
  Requires-Dist: importlib-metadata<9.0.0,>=8.5.0
34
34
  Requires-Dist: packaging<26.0,>=24.2
35
- Requires-Dist: sse-starlette<3.0.0,>=2.1.3
35
+ Requires-Dist: sse-starlette<4.0.0,>=2.1.3
36
36
  Requires-Dist: httpx-sse<0.5.0,>=0.4.0
37
37
  Requires-Dist: stamina<26.0.0,>=25.1.0
38
38
  Requires-Dist: noneprompt<0.2.0,>=0.1.9
@@ -57,6 +57,7 @@ Requires-Dist: asgi-lifespan<3.0.0,>=2.1.0; extra == "dev"
57
57
  Requires-Dist: tox<4.29.0,>=4.24.0; extra == "dev"
58
58
  Requires-Dist: pytest-dependency<0.7.0,>=0.6.0; extra == "dev"
59
59
  Requires-Dist: pytest-sugar<2.0.0,>=1.0.0; extra == "dev"
60
+ Requires-Dist: rust-just<2.0.0,>=1.42.4; extra == "dev"
60
61
  Provides-Extra: doc
61
62
  Requires-Dist: mkdocs-material<9.7.0,>=9.6.5; extra == "doc"
62
63
  Requires-Dist: mkdocs-glightbox<0.5.0,>=0.4.0; extra == "doc"
@@ -1,4 +1,4 @@
1
- __version__ = "0.2.9"
1
+ __version__ = "0.2.11"
2
2
 
3
3
  from labtasker.client.client_api import *
4
4
  from labtasker.client.core.config import get_client_config
@@ -42,31 +42,35 @@ __all__ = [
42
42
  "connect_events",
43
43
  "EventListener",
44
44
  # http api (you should be careful with these unless you know what you are doing)
45
+ "get_httpx_client",
45
46
  "close_httpx_client",
46
47
  "health_check",
47
- "submit_task",
48
- "delete_worker",
49
48
  "create_queue",
50
- "create_worker",
49
+ "get_queue",
51
50
  "delete_queue",
52
- "delete_task",
53
- "delete_worker",
51
+ "submit_task",
54
52
  "fetch_task",
55
- "get_queue",
56
- "health_check",
57
- "ls_tasks",
58
- "ls_workers",
59
- "refresh_task_heartbeat",
60
53
  "report_task_status",
54
+ "refresh_task_heartbeat",
55
+ "create_worker",
56
+ "ls_workers",
57
+ "report_worker_status",
58
+ "ls_tasks",
59
+ "update_tasks",
60
+ "delete_task",
61
+ "update_queue",
62
+ "delete_worker",
61
63
  # utilities
62
64
  "run_with_pty",
63
65
  "run_with_subprocess",
64
66
  ]
65
67
 
68
+ assert len(set(__all__)) == len(__all__), "Duplicated symbols in __all__"
69
+
66
70
 
67
71
  def loop(
68
72
  required_fields: Optional[List[str]] = None,
69
- extra_filter: Optional[Dict[str, Any]] = None,
73
+ extra_filter: Optional[Union[str, Dict[str, Any]]] = None,
70
74
  cmd: Optional[Union[str, List[str]]] = None,
71
75
  worker_id: Optional[str] = None,
72
76
  create_worker_kwargs: Optional[Dict[str, Any]] = None,
@@ -78,7 +82,7 @@ def loop(
78
82
 
79
83
  Args:
80
84
  required_fields: Fields (or extra fields other than specified using Required(...)) required for task execution in a dot-separated manner. E.g. ["arg1.arg11", "arg2.arg22"]
81
- extra_filter: Additional filtering criteria for tasks
85
+ extra_filter: Additional filtering criteria for tasks. Dict in MongoDB syntax or string in Python syntax is allowed.
82
86
  cmd: Command line arguments that runs current process. Default to sys.argv
83
87
  worker_id: Specific worker ID to use
84
88
  create_worker_kwargs: Arguments for default worker creation
@@ -35,6 +35,7 @@ from labtasker.client.core.utils import (
35
35
  cast_http_error,
36
36
  display_server_notifications,
37
37
  raise_for_status,
38
+ transpile_query_safe,
38
39
  )
39
40
  from labtasker.constants import Priority
40
41
  from labtasker.security import SecretStr, get_auth_headers
@@ -67,7 +68,13 @@ def _network_err_retry(func):
67
68
  @wraps(func)
68
69
  def wrapper(*args, **kwargs):
69
70
  return stamina.retry(
70
- on=httpx.TransportError, attempts=5, wait_initial=0.5, wait_max=10.0
71
+ on=httpx.TransportError,
72
+ attempts=10,
73
+ timeout=100.0,
74
+ wait_initial=0.5,
75
+ wait_max=16.0,
76
+ wait_jitter=1.0,
77
+ wait_exp_base=2.0,
71
78
  )(func)(*args, **kwargs)
72
79
 
73
80
  return wrapper
@@ -193,7 +200,7 @@ def fetch_task(
193
200
  heartbeat_timeout: Optional[float] = None,
194
201
  start_heartbeat: bool = True,
195
202
  required_fields: Optional[List[str]] = None,
196
- extra_filter: Optional[Dict[str, Any]] = None,
203
+ extra_filter: Optional[Union[str, Dict[str, Any]]] = None,
197
204
  client: Optional[httpx.Client] = None,
198
205
  cmd: Optional[Union[str, List[str]]] = None,
199
206
  ) -> TaskFetchResponse:
@@ -206,6 +213,9 @@ def fetch_task(
206
213
  "Either eta_max or start_heartbeat must be specified."
207
214
  )
208
215
 
216
+ if isinstance(extra_filter, str): # transpile to mongodb query
217
+ extra_filter = transpile_query_safe(query_str=extra_filter)
218
+
209
219
  payload = TaskFetchRequest(
210
220
  worker_id=worker_id,
211
221
  eta_max=eta_max,
@@ -303,7 +313,7 @@ def ls_workers(
303
313
  worker_id: Optional[str] = None,
304
314
  worker_name: Optional[str] = None,
305
315
  status: Optional[str] = None,
306
- extra_filter: Optional[Dict[str, Any]] = None,
316
+ extra_filter: Optional[Union[str, Dict[str, Any]]] = None,
307
317
  limit: int = 100,
308
318
  offset: int = 0,
309
319
  sort: Optional[List[Tuple[str, int]]] = None,
@@ -312,6 +322,10 @@ def ls_workers(
312
322
  """List workers."""
313
323
  if client is None:
314
324
  client = get_httpx_client()
325
+
326
+ if isinstance(extra_filter, str): # transpile to mongodb query
327
+ extra_filter = transpile_query_safe(query_str=extra_filter)
328
+
315
329
  payload = WorkerLsRequest(
316
330
  worker_id=worker_id,
317
331
  worker_name=worker_name,
@@ -364,7 +378,7 @@ def ls_tasks(
364
378
  task_id: Optional[str] = None,
365
379
  task_name: Optional[str] = None,
366
380
  status: Optional[str] = None,
367
- extra_filter: Optional[Dict[str, Any]] = None,
381
+ extra_filter: Optional[Union[str, Dict[str, Any]]] = None,
368
382
  limit: int = 100,
369
383
  offset: int = 0,
370
384
  sort: Optional[List[Tuple[str, int]]] = None,
@@ -373,6 +387,10 @@ def ls_tasks(
373
387
  """List tasks in a queue."""
374
388
  if client is None:
375
389
  client = get_httpx_client()
390
+
391
+ if isinstance(extra_filter, str): # transpile to mongodb query
392
+ extra_filter = transpile_query_safe(query_str=extra_filter)
393
+
376
394
  payload = TaskLsRequest(
377
395
  task_id=task_id,
378
396
  task_name=task_name,
@@ -34,7 +34,7 @@ from labtasker.client.core.exceptions import (
34
34
  QueryTranspilerError,
35
35
  )
36
36
  from labtasker.client.core.logging import stderr_console
37
- from labtasker.client.core.query_transpiler import transpile_query
37
+ from labtasker.client.core.utils import transpile_query_safe
38
38
  from labtasker.utils import parse_time_interval, unflatten_dict
39
39
 
40
40
  DT = TypeVar("DT")
@@ -84,7 +84,7 @@ def parse_filter(filter_str: Optional[str]) -> Optional[Dict[str, Any]]:
84
84
  return parse_dict(d_str=filter_str)
85
85
  except typer.BadParameter:
86
86
  try:
87
- return transpile_query(query_str=filter_str) # type: ignore[arg-type]
87
+ return transpile_query_safe(query_str=filter_str) # type: ignore[arg-type]
88
88
  except QueryTranspilerError as e:
89
89
  raise typer.BadParameter(f"Invalid filter str: {e}") from e
90
90
 
@@ -176,9 +176,9 @@ class EventListener:
176
176
  self._retry_context_iter = stamina.retry_context(
177
177
  on=httpx.TransportError,
178
178
  attempts=10,
179
- timeout=60,
179
+ timeout=100.0,
180
180
  wait_initial=0.5,
181
- wait_max=8.0,
181
+ wait_max=16.0,
182
182
  wait_jitter=1.0,
183
183
  wait_exp_base=2.0,
184
184
  ).__iter__()
@@ -12,6 +12,7 @@ import labtasker
12
12
  from labtasker.api_models import TaskUpdateRequest
13
13
  from labtasker.client.core.api import (
14
14
  create_worker,
15
+ delete_worker,
15
16
  fetch_task,
16
17
  get_queue,
17
18
  report_task_status,
@@ -37,6 +38,7 @@ from labtasker.client.core.exceptions import (
37
38
  from labtasker.client.core.heartbeat import end_heartbeat, start_heartbeat
38
39
  from labtasker.client.core.logging import log_to_file, logger, stderr_console
39
40
  from labtasker.client.core.paths import get_labtasker_log_dir, set_labtasker_log_dir
41
+ from labtasker.client.core.utils import transpile_query_safe
40
42
  from labtasker.utils import parse_time_interval
41
43
 
42
44
  __all__ = [
@@ -105,7 +107,7 @@ def dump_task_info():
105
107
 
106
108
  def loop_run(
107
109
  required_fields: List[str],
108
- extra_filter: Optional[Dict[str, Any]] = None,
110
+ extra_filter: Optional[Union[str, Dict[str, Any]]] = None,
109
111
  cmd: Optional[Union[str, List[str]]] = None,
110
112
  worker_id: Optional[str] = None,
111
113
  create_worker_kwargs: Optional[Dict[str, Any]] = None,
@@ -141,6 +143,9 @@ def loop_run(
141
143
  f"Invalid eta_max {eta_max}. ETA max must be a valid duration string (e.g. '1h', '1h30m', '50s')"
142
144
  )
143
145
 
146
+ if isinstance(extra_filter, str): # transpile to mongodb query
147
+ extra_filter = transpile_query_safe(query_str=extra_filter)
148
+
144
149
  # Check connection and authentication
145
150
  try:
146
151
  get_queue()
@@ -155,9 +160,12 @@ def loop_run(
155
160
  raise e
156
161
 
157
162
  # Create worker if not exists
163
+ auto_create_worker = False
164
+
158
165
  if current_worker_id() is None:
159
- new_worker_id = worker_id or create_worker(**(create_worker_kwargs or {}))
160
- set_current_worker_id(new_worker_id)
166
+ auto_create_worker = worker_id is None
167
+ worker_id = worker_id or create_worker(**(create_worker_kwargs or {}))
168
+ set_current_worker_id(worker_id)
161
169
 
162
170
  if cmd is None:
163
171
  cmd = sys.argv
@@ -270,12 +278,9 @@ def loop_run(
270
278
  resp = update_tasks(
271
279
  task_updates=[
272
280
  TaskUpdateRequest(
273
- **{
274
- "task_id": current_task_id(),
275
- "status": "pending", # running -> pending
276
- "retries": 0,
277
- "worker_id": None,
278
- }
281
+ task_id=current_task_id(), # noqa
282
+ status="pending", # running -> pending
283
+ retries=0,
279
284
  )
280
285
  ]
281
286
  )
@@ -336,6 +341,10 @@ def loop_run(
336
341
  finish(status="success")
337
342
  end_heartbeat()
338
343
  except _LabtaskerLoopExit:
344
+ # clean up the worker
345
+ if auto_create_worker: # worker is managed automatically
346
+ delete_worker(worker_id=current_worker_id())
347
+
339
348
  logger.info("Exiting task loop.")
340
349
  break
341
350
  except WorkerSuspended:
@@ -1,6 +1,7 @@
1
1
  import ast
2
+ import difflib
2
3
  from datetime import timezone
3
- from typing import Any, Dict, List, NoReturn, Type
4
+ from typing import Any, Dict, List, NoReturn, Optional, Type
4
5
 
5
6
  import dateparser
6
7
  from rich.console import Console
@@ -154,9 +155,10 @@ class QueryTranspiler(ast.NodeVisitor):
154
155
  ast.Or: "$or", # a or b -> {$or: [a, b]}
155
156
  }
156
157
 
157
- def __init__(self, query_str: str):
158
+ def __init__(self, query_str: str, allowed_fields: Optional[List[str]] = None):
158
159
  super().__init__()
159
160
  self.query_str = query_str
161
+ self.allowed_fields = allowed_fields
160
162
 
161
163
  def _report_error(
162
164
  self, node: ast.AST, msg: str, exception: Type[QueryTranspilerError]
@@ -701,6 +703,20 @@ class QueryTranspiler(ast.NodeVisitor):
701
703
  Python: field_name
702
704
  MongoDB: "field_name" (as a field reference)
703
705
  """
706
+ if self.allowed_fields and node.id not in self.allowed_fields:
707
+ suggestions = difflib.get_close_matches(
708
+ node.id, self.allowed_fields, n=1, cutoff=0.6
709
+ )
710
+ suggestion_msg = (
711
+ f" Maybe you meant '{suggestions[0]}'?" if suggestions else ""
712
+ )
713
+
714
+ self._report_error(
715
+ node=node,
716
+ msg=f"Field '{node.id}' is unknown or not allowed.{suggestion_msg}"
717
+ f"\nAllowed fields: {', '.join(sorted(self.allowed_fields))}",
718
+ exception=QueryTranspilerValueError,
719
+ )
704
720
  return node.id
705
721
 
706
722
  def visit_Attribute(self, node: ast.Attribute) -> str:
@@ -960,7 +976,9 @@ class QueryTranspiler(ast.NodeVisitor):
960
976
  )
961
977
 
962
978
 
963
- def transpile_query(query_str: str) -> Dict[str, Any]:
979
+ def transpile_query(
980
+ query_str: str, allowed_fields: Optional[List[str]] = None
981
+ ) -> Dict[str, Any]:
964
982
  """
965
983
  Transpile a Python-like query string and convert it to a MongoDB query object.
966
984
 
@@ -969,6 +987,7 @@ def transpile_query(query_str: str) -> Dict[str, Any]:
969
987
 
970
988
  Args:
971
989
  query_str: A string containing a Python-like expression to be converted
990
+ allowed_fields: A list of allowed fields (e.g. ast.Name.id). If None, allowed_fields will not be checked.
972
991
 
973
992
  Returns:
974
993
  A dictionary representing the equivalent MongoDB query
@@ -979,7 +998,7 @@ def transpile_query(query_str: str) -> Dict[str, Any]:
979
998
  try:
980
999
  query_str = query_str.strip()
981
1000
  parsed_ast = ast.parse(query_str)
982
- visitor = QueryTranspiler(query_str=query_str)
1001
+ visitor = QueryTranspiler(query_str=query_str, allowed_fields=allowed_fields)
983
1002
  result = visitor.visit(parsed_ast)
984
1003
 
985
1004
  if isinstance(result, bool) or isinstance(result, int):
@@ -3,7 +3,7 @@ import os
3
3
  import subprocess
4
4
  import sys
5
5
  import threading
6
- from functools import wraps
6
+ from functools import partial, wraps
7
7
  from typing import Any, Callable, Optional
8
8
 
9
9
  import httpx
@@ -20,6 +20,7 @@ from labtasker.client.core.exceptions import (
20
20
  )
21
21
  from labtasker.client.core.logging import stderr_console, stdout_console
22
22
  from labtasker.client.core.paths import get_labtasker_client_config_path
23
+ from labtasker.client.core.query_transpiler import transpile_query
23
24
 
24
25
  server_notification_prefix = {
25
26
  "info": "[bold dodger_blue1]INFO(notification):[/bold dodger_blue1] ",
@@ -33,6 +34,31 @@ server_notification_level = {
33
34
  "high": 2,
34
35
  }
35
36
 
37
+ transpile_query_safe = partial(
38
+ transpile_query,
39
+ allowed_fields=[
40
+ "task_id",
41
+ "queue_id",
42
+ "status",
43
+ "task_name",
44
+ "created_at",
45
+ "start_time",
46
+ "last_heartbeat",
47
+ "last_modified",
48
+ "heartbeat_timeout",
49
+ "task_timeout",
50
+ "max_retries",
51
+ "retries",
52
+ "priority",
53
+ "metadata",
54
+ "args",
55
+ "cmd",
56
+ "summary",
57
+ "worker_id",
58
+ "worker_name",
59
+ ],
60
+ )
61
+
36
62
 
37
63
  def json_serializer(obj: Any, **kwargs) -> str:
38
64
  return json.dumps(to_jsonable_python(obj), **kwargs)
@@ -7,5 +7,4 @@ class Priority(int, Enum):
7
7
  HIGH = 20
8
8
 
9
9
 
10
- KEY_PATTERN = r"^[a-zA-Z0-9_-]+$"
11
10
  DOT_SEPARATED_KEY_PATTERN = r"^[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)*$"
@@ -614,6 +614,7 @@ class DBService:
614
614
  "last_modified": now,
615
615
  "worker_id": worker_id,
616
616
  "cmd": cmd,
617
+ "summary": {}, # clear previous summary before fetched
617
618
  }
618
619
  }
619
620
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "labtasker"
7
- version = "0.2.9"
7
+ version = "0.2.11"
8
8
  description = "A task queue system for lab experiments"
9
9
  authors = [{ name = "Your Name", email = "your.email@example.com" }]
10
10
  license = { text = "Apache License 2.0" }
@@ -36,7 +36,7 @@ dependencies = [
36
36
  "tomlkit (>=0.13.2,<0.14.0)",
37
37
  "importlib-metadata (>=8.5.0,<9.0.0)",
38
38
  "packaging (>=24.2,<26.0)",
39
- "sse-starlette (>=2.1.3,<3.0.0)",
39
+ "sse-starlette (>=2.1.3,<4.0.0)",
40
40
  "httpx-sse (>=0.4.0,<0.5.0)",
41
41
  "stamina (>=25.1.0,<26.0.0)",
42
42
  "noneprompt (>=0.1.9,<0.2.0)",
@@ -64,6 +64,7 @@ dev = [
64
64
  "tox (>=4.24.0,<4.29.0)",
65
65
  "pytest-dependency (>=0.6.0,<0.7.0)",
66
66
  "pytest-sugar (>=1.0.0,<2.0.0)",
67
+ "rust-just (>=1.42.4,<2.0.0)",
67
68
  ]
68
69
  doc = [
69
70
  "mkdocs-material (>=9.6.5,<9.7.0)",
@@ -132,7 +133,7 @@ disable_error_code = [
132
133
  "no-redef",
133
134
  "import-untyped"
134
135
  ]
135
- python_version = "3.10"
136
+ python_version = "0.2.11"
136
137
  warn_unused_configs = true
137
138
  ignore_missing_imports = true
138
139
  show_error_codes = true
@@ -90,6 +90,42 @@ def test_job_success(setup_tasks):
90
90
  assert task.status == "success"
91
91
 
92
92
 
93
+ def test_job_str_filter(setup_tasks):
94
+ """Test if Pythonic query str as extra_filter works"""
95
+ tasks = ls_tasks()
96
+ assert tasks.found
97
+ assert len(tasks.content) == TOTAL_TASKS
98
+
99
+ idx = -1
100
+
101
+ @loop_run(
102
+ required_fields=["arg1", "arg2"],
103
+ eta_max="1h",
104
+ pass_args_dict=True,
105
+ extra_filter="args.arg2.arg3 < 2", # only run the first two tasks
106
+ )
107
+ def job(args):
108
+ nonlocal idx
109
+ idx += 1
110
+ task_name = task_info().task_name
111
+ assert task_name == f"test_task_{idx}"
112
+ assert args["arg1"] == idx
113
+ assert args["arg2"]["arg3"] == idx
114
+
115
+ time.sleep(0.5) # a tiny delay to ensure the tasks api request are processed
116
+
117
+ finish("success")
118
+
119
+ job()
120
+
121
+ assert idx + 1 == 2, idx
122
+
123
+ tasks = ls_tasks(extra_filter="args.arg2.arg3 < 2")
124
+ assert tasks.found
125
+ for task in tasks.content:
126
+ assert task.status == "success"
127
+
128
+
93
129
  def test_job_manual_failure(setup_tasks):
94
130
  cnt = 0
95
131
 
File without changes
File without changes
File without changes
File without changes