taskito 0.4.0__tar.gz → 0.5.0__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 (168) hide show
  1. {taskito-0.4.0 → taskito-0.5.0}/Cargo.lock +20 -8
  2. {taskito-0.4.0 → taskito-0.5.0}/PKG-INFO +6 -3
  3. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/Cargo.toml +1 -1
  4. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/mod.rs +31 -7
  5. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/models.rs +6 -0
  6. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/mod.rs +14 -0
  7. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/workers.rs +17 -3
  8. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/workers.rs +26 -10
  9. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/schema.rs +3 -0
  10. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/mod.rs +11 -0
  11. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/workers.rs +17 -3
  12. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/traits.rs +10 -2
  13. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/tests/rust/storage_tests.rs +11 -2
  14. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-python/Cargo.toml +3 -1
  15. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-python/src/lib.rs +3 -0
  16. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-python/src/py_queue/inspection.rs +3 -0
  17. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-python/src/py_queue/worker.rs +79 -23
  18. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/__init__.py +23 -1
  19. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/_taskito.pyi +39 -0
  20. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/app.py +462 -80
  21. taskito-0.5.0/py_src/taskito/async_support/__init__.py +25 -0
  22. taskito-0.5.0/py_src/taskito/async_support/context.py +32 -0
  23. taskito-0.5.0/py_src/taskito/async_support/executor.py +218 -0
  24. taskito-0.5.0/py_src/taskito/async_support/helpers.py +17 -0
  25. taskito-0.5.0/py_src/taskito/async_support/locks.py +95 -0
  26. taskito-0.5.0/py_src/taskito/async_support/mixins.py +183 -0
  27. taskito-0.5.0/py_src/taskito/async_support/result.py +61 -0
  28. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/cli.py +111 -0
  29. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/context.py +10 -3
  30. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/fastapi.py +5 -0
  31. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/prometheus.py +126 -3
  32. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/dashboard.py +82 -29
  33. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/exceptions.py +28 -0
  34. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/health.py +16 -0
  35. taskito-0.5.0/py_src/taskito/inject.py +42 -0
  36. taskito-0.5.0/py_src/taskito/interception/__init__.py +25 -0
  37. taskito-0.5.0/py_src/taskito/interception/built_in.py +363 -0
  38. taskito-0.5.0/py_src/taskito/interception/converters.py +243 -0
  39. taskito-0.5.0/py_src/taskito/interception/errors.py +45 -0
  40. taskito-0.5.0/py_src/taskito/interception/interceptor.py +207 -0
  41. taskito-0.5.0/py_src/taskito/interception/metrics.py +46 -0
  42. taskito-0.5.0/py_src/taskito/interception/reconstruct.py +69 -0
  43. taskito-0.5.0/py_src/taskito/interception/registry.py +86 -0
  44. taskito-0.5.0/py_src/taskito/interception/strategy.py +13 -0
  45. taskito-0.5.0/py_src/taskito/interception/walker.py +266 -0
  46. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/locks.py +6 -80
  47. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/mixins.py +3 -95
  48. taskito-0.5.0/py_src/taskito/proxies/__init__.py +14 -0
  49. taskito-0.5.0/py_src/taskito/proxies/built_in.py +77 -0
  50. taskito-0.5.0/py_src/taskito/proxies/handler.py +30 -0
  51. taskito-0.5.0/py_src/taskito/proxies/handlers/__init__.py +9 -0
  52. taskito-0.5.0/py_src/taskito/proxies/handlers/boto3_client.py +54 -0
  53. taskito-0.5.0/py_src/taskito/proxies/handlers/file.py +78 -0
  54. taskito-0.5.0/py_src/taskito/proxies/handlers/gcs_client.py +75 -0
  55. taskito-0.5.0/py_src/taskito/proxies/handlers/httpx_client.py +61 -0
  56. taskito-0.5.0/py_src/taskito/proxies/handlers/logger.py +32 -0
  57. taskito-0.5.0/py_src/taskito/proxies/handlers/requests_session.py +49 -0
  58. taskito-0.5.0/py_src/taskito/proxies/metrics.py +86 -0
  59. taskito-0.5.0/py_src/taskito/proxies/no_proxy.py +22 -0
  60. taskito-0.5.0/py_src/taskito/proxies/reconstruct.py +203 -0
  61. taskito-0.5.0/py_src/taskito/proxies/registry.py +32 -0
  62. taskito-0.5.0/py_src/taskito/proxies/schema.py +50 -0
  63. taskito-0.5.0/py_src/taskito/proxies/signing.py +35 -0
  64. taskito-0.5.0/py_src/taskito/resources/__init__.py +22 -0
  65. taskito-0.5.0/py_src/taskito/resources/definition.py +39 -0
  66. taskito-0.5.0/py_src/taskito/resources/frozen.py +40 -0
  67. taskito-0.5.0/py_src/taskito/resources/graph.py +76 -0
  68. taskito-0.5.0/py_src/taskito/resources/health.py +105 -0
  69. taskito-0.5.0/py_src/taskito/resources/pool.py +144 -0
  70. taskito-0.5.0/py_src/taskito/resources/runtime.py +266 -0
  71. taskito-0.5.0/py_src/taskito/resources/thread_local.py +61 -0
  72. taskito-0.5.0/py_src/taskito/resources/toml_config.py +117 -0
  73. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/result.py +3 -47
  74. taskito-0.5.0/py_src/taskito/scaler.py +121 -0
  75. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/task.py +21 -0
  76. taskito-0.5.0/py_src/taskito/templates/dashboard.css +368 -0
  77. taskito-0.5.0/py_src/taskito/templates/dashboard.html +40 -0
  78. taskito-0.5.0/py_src/taskito/templates/js/actions.js +34 -0
  79. taskito-0.5.0/py_src/taskito/templates/js/app.js +93 -0
  80. taskito-0.5.0/py_src/taskito/templates/js/chart.js +150 -0
  81. taskito-0.5.0/py_src/taskito/templates/js/components.js +58 -0
  82. taskito-0.5.0/py_src/taskito/templates/js/utils.js +19 -0
  83. taskito-0.5.0/py_src/taskito/templates/js/views.js +348 -0
  84. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/testing.py +73 -1
  85. {taskito-0.4.0 → taskito-0.5.0}/pyproject.toml +5 -4
  86. taskito-0.4.0/py_src/taskito/templates/dashboard.html +0 -1226
  87. {taskito-0.4.0 → taskito-0.5.0}/Cargo.toml +0 -0
  88. {taskito-0.4.0 → taskito-0.5.0}/LICENSE +0 -0
  89. {taskito-0.4.0 → taskito-0.5.0}/README.md +0 -0
  90. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/error.rs +0 -0
  91. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/job.rs +0 -0
  92. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/lib.rs +0 -0
  93. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/periodic.rs +0 -0
  94. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/resilience/circuit_breaker.rs +0 -0
  95. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/resilience/dlq.rs +0 -0
  96. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/resilience/mod.rs +0 -0
  97. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/resilience/rate_limiter.rs +0 -0
  98. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/resilience/retry.rs +0 -0
  99. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/scheduler/maintenance.rs +0 -0
  100. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/scheduler/mod.rs +0 -0
  101. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/scheduler/poller.rs +0 -0
  102. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/scheduler/result_handler.rs +0 -0
  103. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/diesel_common/jobs.rs +0 -0
  104. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/diesel_common/locks.rs +0 -0
  105. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/diesel_common/logs.rs +0 -0
  106. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/diesel_common/metrics.rs +0 -0
  107. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/diesel_common/mod.rs +0 -0
  108. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/archival.rs +0 -0
  109. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/circuit_breakers.rs +0 -0
  110. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/dead_letter.rs +0 -0
  111. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/jobs.rs +0 -0
  112. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/locks.rs +0 -0
  113. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/logs.rs +0 -0
  114. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/metrics.rs +0 -0
  115. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/periodic.rs +0 -0
  116. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/queue_state.rs +0 -0
  117. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/rate_limits.rs +0 -0
  118. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/postgres/trait_impl.rs +0 -0
  119. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/archival.rs +0 -0
  120. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/circuit_breakers.rs +0 -0
  121. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/dead_letter.rs +0 -0
  122. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/jobs.rs +0 -0
  123. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/locks.rs +0 -0
  124. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/logs.rs +0 -0
  125. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/metrics.rs +0 -0
  126. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/mod.rs +0 -0
  127. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/periodic.rs +0 -0
  128. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/queue_state.rs +0 -0
  129. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/rate_limits.rs +0 -0
  130. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/redis_backend/trait_impl.rs +0 -0
  131. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/archival.rs +0 -0
  132. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/circuit_breakers.rs +0 -0
  133. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/dead_letter.rs +0 -0
  134. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/jobs.rs +0 -0
  135. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/locks.rs +0 -0
  136. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/logs.rs +0 -0
  137. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/metrics.rs +0 -0
  138. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/periodic.rs +0 -0
  139. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/queue_state.rs +0 -0
  140. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/rate_limits.rs +0 -0
  141. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/tests.rs +0 -0
  142. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/storage/sqlite/trait_impl.rs +0 -0
  143. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/src/worker.rs +0 -0
  144. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-core/tests/rust.rs +0 -0
  145. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-python/src/async_worker.rs +0 -0
  146. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-python/src/py_config.rs +0 -0
  147. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-python/src/py_job.rs +0 -0
  148. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-python/src/py_queue/mod.rs +0 -0
  149. {taskito-0.4.0 → taskito-0.5.0}/crates/taskito-python/src/py_worker.rs +0 -0
  150. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/canvas.py +0 -0
  151. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/__init__.py +0 -0
  152. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/django/__init__.py +0 -0
  153. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/django/admin.py +0 -0
  154. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/django/apps.py +0 -0
  155. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/django/management/__init__.py +0 -0
  156. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/django/management/commands/__init__.py +0 -0
  157. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/django/management/commands/taskito_dashboard.py +0 -0
  158. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/django/management/commands/taskito_info.py +0 -0
  159. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/django/management/commands/taskito_worker.py +0 -0
  160. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/django/settings.py +0 -0
  161. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/flask.py +0 -0
  162. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/otel.py +0 -0
  163. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/contrib/sentry.py +0 -0
  164. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/events.py +0 -0
  165. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/middleware.py +0 -0
  166. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/py.typed +0 -0
  167. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/serializers.py +0 -0
  168. {taskito-0.4.0 → taskito-0.5.0}/py_src/taskito/webhooks.py +0 -0
@@ -194,9 +194,9 @@ dependencies = [
194
194
 
195
195
  [[package]]
196
196
  name = "diesel"
197
- version = "2.3.6"
197
+ version = "2.3.7"
198
198
  source = "registry+https://github.com/rust-lang/crates.io-index"
199
- checksum = "d9b6c2fc184a6fb6ebcf5f9a5e3bbfa84d8fd268cdfcce4ed508979a6259494d"
199
+ checksum = "f4ae09a41a4b89f94ec1e053623da8340d996bc32c6517d325a9daad9b239358"
200
200
  dependencies = [
201
201
  "bitflags",
202
202
  "byteorder",
@@ -693,9 +693,9 @@ dependencies = [
693
693
 
694
694
  [[package]]
695
695
  name = "once_cell"
696
- version = "1.21.3"
696
+ version = "1.21.4"
697
697
  source = "registry+https://github.com/rust-lang/crates.io-index"
698
- checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
698
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
699
699
 
700
700
  [[package]]
701
701
  name = "openssl-src"
@@ -708,9 +708,9 @@ dependencies = [
708
708
 
709
709
  [[package]]
710
710
  name = "openssl-sys"
711
- version = "0.9.111"
711
+ version = "0.9.112"
712
712
  source = "registry+https://github.com/rust-lang/crates.io-index"
713
- checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
713
+ checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb"
714
714
  dependencies = [
715
715
  "cc",
716
716
  "libc",
@@ -1199,9 +1199,20 @@ version = "0.12.16"
1199
1199
  source = "registry+https://github.com/rust-lang/crates.io-index"
1200
1200
  checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
1201
1201
 
1202
+ [[package]]
1203
+ name = "taskito-async"
1204
+ version = "0.5.0"
1205
+ dependencies = [
1206
+ "async-trait",
1207
+ "crossbeam-channel",
1208
+ "pyo3",
1209
+ "taskito-core",
1210
+ "tokio",
1211
+ ]
1212
+
1202
1213
  [[package]]
1203
1214
  name = "taskito-core"
1204
- version = "0.4.0"
1215
+ version = "0.5.0"
1205
1216
  dependencies = [
1206
1217
  "async-trait",
1207
1218
  "chrono",
@@ -1225,11 +1236,12 @@ dependencies = [
1225
1236
 
1226
1237
  [[package]]
1227
1238
  name = "taskito-python"
1228
- version = "0.4.0"
1239
+ version = "0.5.0"
1229
1240
  dependencies = [
1230
1241
  "async-trait",
1231
1242
  "crossbeam-channel",
1232
1243
  "pyo3",
1244
+ "taskito-async",
1233
1245
  "taskito-core",
1234
1246
  "tokio",
1235
1247
  "uuid",
@@ -1,11 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskito
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
7
7
  Classifier: Programming Language :: Python :: 3
8
- Classifier: Programming Language :: Python :: 3.9
9
8
  Classifier: Programming Language :: Python :: 3.10
10
9
  Classifier: Programming Language :: Python :: 3.11
11
10
  Classifier: Programming Language :: Python :: 3.12
@@ -14,6 +13,7 @@ Classifier: Programming Language :: Rust
14
13
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
14
  Classifier: Topic :: System :: Distributed Computing
16
15
  Requires-Dist: cloudpickle>=3.0
16
+ Requires-Dist: boto3>=1.20 ; extra == 'aws'
17
17
  Requires-Dist: pytest>=7.0 ; extra == 'dev'
18
18
  Requires-Dist: pytest-asyncio>=0.21 ; extra == 'dev'
19
19
  Requires-Dist: pytest-cov>=4.0 ; extra == 'dev'
@@ -25,17 +25,20 @@ Requires-Dist: cryptography ; extra == 'encryption'
25
25
  Requires-Dist: fastapi>=0.100.0 ; extra == 'fastapi'
26
26
  Requires-Dist: pydantic>=2.0 ; extra == 'fastapi'
27
27
  Requires-Dist: flask>=2.0 ; extra == 'flask'
28
+ Requires-Dist: google-cloud-storage>=2.0 ; extra == 'gcs'
28
29
  Requires-Dist: msgpack ; extra == 'msgpack'
29
30
  Requires-Dist: opentelemetry-api ; extra == 'otel'
30
31
  Requires-Dist: opentelemetry-sdk ; extra == 'otel'
31
32
  Requires-Dist: prometheus-client ; extra == 'prometheus'
32
33
  Requires-Dist: sentry-sdk ; extra == 'sentry'
34
+ Provides-Extra: aws
33
35
  Provides-Extra: dev
34
36
  Provides-Extra: django
35
37
  Provides-Extra: docs
36
38
  Provides-Extra: encryption
37
39
  Provides-Extra: fastapi
38
40
  Provides-Extra: flask
41
+ Provides-Extra: gcs
39
42
  Provides-Extra: msgpack
40
43
  Provides-Extra: otel
41
44
  Provides-Extra: postgres
@@ -44,7 +47,7 @@ Provides-Extra: redis
44
47
  Provides-Extra: sentry
45
48
  License-File: LICENSE
46
49
  Summary: Rust-powered task queue for Python. No broker required.
47
- Requires-Python: >=3.9
50
+ Requires-Python: >=3.10
48
51
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
49
52
  Project-URL: Changelog, https://github.com/pratyush618/taskito/blob/master/docs/changelog.md
50
53
  Project-URL: Documentation, https://taskito.grigori.in
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "taskito-core"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  edition = "2021"
5
5
 
6
6
  [features]
@@ -344,11 +344,18 @@ macro_rules! impl_storage {
344
344
  worker_id: &str,
345
345
  queues: &str,
346
346
  tags: Option<&str>,
347
+ resources: Option<&str>,
348
+ resource_health: Option<&str>,
349
+ threads: i32,
347
350
  ) -> $crate::error::Result<()> {
348
- self.register_worker(worker_id, queues, tags)
351
+ self.register_worker(worker_id, queues, tags, resources, resource_health, threads)
349
352
  }
350
- fn heartbeat(&self, worker_id: &str) -> $crate::error::Result<()> {
351
- self.heartbeat(worker_id)
353
+ fn heartbeat(
354
+ &self,
355
+ worker_id: &str,
356
+ resource_health: Option<&str>,
357
+ ) -> $crate::error::Result<()> {
358
+ self.heartbeat(worker_id, resource_health)
352
359
  }
353
360
  fn list_workers(
354
361
  &self,
@@ -705,11 +712,28 @@ impl Storage for StorageBackend {
705
712
  fn list_circuit_breakers(&self) -> Result<Vec<models::CircuitBreakerRow>> {
706
713
  delegate!(self, list_circuit_breakers)
707
714
  }
708
- fn register_worker(&self, worker_id: &str, queues: &str, tags: Option<&str>) -> Result<()> {
709
- delegate!(self, register_worker, worker_id, queues, tags)
715
+ fn register_worker(
716
+ &self,
717
+ worker_id: &str,
718
+ queues: &str,
719
+ tags: Option<&str>,
720
+ resources: Option<&str>,
721
+ resource_health: Option<&str>,
722
+ threads: i32,
723
+ ) -> Result<()> {
724
+ delegate!(
725
+ self,
726
+ register_worker,
727
+ worker_id,
728
+ queues,
729
+ tags,
730
+ resources,
731
+ resource_health,
732
+ threads
733
+ )
710
734
  }
711
- fn heartbeat(&self, worker_id: &str) -> Result<()> {
712
- delegate!(self, heartbeat, worker_id)
735
+ fn heartbeat(&self, worker_id: &str, resource_health: Option<&str>) -> Result<()> {
736
+ delegate!(self, heartbeat, worker_id, resource_health)
713
737
  }
714
738
  fn list_workers(&self) -> Result<Vec<models::WorkerRow>> {
715
739
  delegate!(self, list_workers)
@@ -284,6 +284,9 @@ pub struct WorkerRow {
284
284
  pub queues: String,
285
285
  pub status: String,
286
286
  pub tags: Option<String>,
287
+ pub resources: Option<String>,
288
+ pub resource_health: Option<String>,
289
+ pub threads: i32,
287
290
  }
288
291
 
289
292
  #[derive(Insertable, AsChangeset, Debug)]
@@ -294,6 +297,9 @@ pub struct NewWorkerRow<'a> {
294
297
  pub queues: &'a str,
295
298
  pub status: &'a str,
296
299
  pub tags: Option<&'a str>,
300
+ pub resources: Option<&'a str>,
301
+ pub resource_health: Option<&'a str>,
302
+ pub threads: i32,
297
303
  }
298
304
 
299
305
  // ── Queue State ─────────────────────────────────────────────────
@@ -340,6 +340,20 @@ impl PostgresStorage {
340
340
  "ALTER TABLE workers ADD COLUMN IF NOT EXISTS tags TEXT",
341
341
  );
342
342
 
343
+ // Migration: add resource advertisement columns to workers
344
+ migration_alter(
345
+ &mut conn,
346
+ "ALTER TABLE workers ADD COLUMN IF NOT EXISTS resources TEXT",
347
+ );
348
+ migration_alter(
349
+ &mut conn,
350
+ "ALTER TABLE workers ADD COLUMN IF NOT EXISTS resource_health TEXT",
351
+ );
352
+ migration_alter(
353
+ &mut conn,
354
+ "ALTER TABLE workers ADD COLUMN IF NOT EXISTS threads INTEGER NOT NULL DEFAULT 0",
355
+ );
356
+
343
357
  diesel::sql_query(
344
358
  "CREATE TABLE IF NOT EXISTS queue_state (
345
359
  queue_name TEXT PRIMARY KEY,
@@ -11,7 +11,15 @@ const DEAD_WORKER_THRESHOLD_MS: i64 = 30_000;
11
11
 
12
12
  impl PostgresStorage {
13
13
  /// Register a new worker or update an existing one.
14
- pub fn register_worker(&self, worker_id: &str, queues: &str, tags: Option<&str>) -> Result<()> {
14
+ pub fn register_worker(
15
+ &self,
16
+ worker_id: &str,
17
+ queues: &str,
18
+ tags: Option<&str>,
19
+ resources: Option<&str>,
20
+ resource_health: Option<&str>,
21
+ threads: i32,
22
+ ) -> Result<()> {
15
23
  let mut conn = self.conn()?;
16
24
  let now = now_millis();
17
25
 
@@ -21,6 +29,9 @@ impl PostgresStorage {
21
29
  queues,
22
30
  status: "active",
23
31
  tags,
32
+ resources,
33
+ resource_health,
34
+ threads,
24
35
  };
25
36
 
26
37
  diesel::insert_into(workers::table)
@@ -34,13 +45,16 @@ impl PostgresStorage {
34
45
  }
35
46
 
36
47
  /// Update the heartbeat timestamp for a worker.
37
- pub fn heartbeat(&self, worker_id: &str) -> Result<()> {
48
+ pub fn heartbeat(&self, worker_id: &str, resource_health: Option<&str>) -> Result<()> {
38
49
  let mut conn = self.conn()?;
39
50
  let now = now_millis();
40
51
 
41
52
  diesel::update(workers::table)
42
53
  .filter(workers::worker_id.eq(worker_id))
43
- .set(workers::last_heartbeat.eq(now))
54
+ .set((
55
+ workers::last_heartbeat.eq(now),
56
+ workers::resource_health.eq(resource_health),
57
+ ))
44
58
  .execute(&mut conn)?;
45
59
 
46
60
  Ok(())
@@ -8,7 +8,15 @@ use crate::storage::models::WorkerRow;
8
8
  const DEAD_WORKER_THRESHOLD_MS: i64 = 30_000;
9
9
 
10
10
  impl RedisStorage {
11
- pub fn register_worker(&self, worker_id: &str, queues: &str, tags: Option<&str>) -> Result<()> {
11
+ pub fn register_worker(
12
+ &self,
13
+ worker_id: &str,
14
+ queues: &str,
15
+ tags: Option<&str>,
16
+ resources: Option<&str>,
17
+ resource_health: Option<&str>,
18
+ threads: i32,
19
+ ) -> Result<()> {
12
20
  let mut conn = self.conn()?;
13
21
  let now = now_millis();
14
22
  let wkey = self.key(&["worker", worker_id]);
@@ -19,19 +27,24 @@ impl RedisStorage {
19
27
  pipe.hset(&wkey, "queues", queues);
20
28
  pipe.hset(&wkey, "status", "active");
21
29
  pipe.hset(&wkey, "tags", tags.unwrap_or(""));
30
+ pipe.hset(&wkey, "resources", resources.unwrap_or(""));
31
+ pipe.hset(&wkey, "resource_health", resource_health.unwrap_or(""));
32
+ pipe.hset(&wkey, "threads", threads);
22
33
  pipe.sadd(&wall, worker_id);
23
34
  pipe.query::<()>(&mut conn).map_err(map_err)?;
24
35
 
25
36
  Ok(())
26
37
  }
27
38
 
28
- pub fn heartbeat(&self, worker_id: &str) -> Result<()> {
39
+ pub fn heartbeat(&self, worker_id: &str, resource_health: Option<&str>) -> Result<()> {
29
40
  let mut conn = self.conn()?;
30
41
  let now = now_millis();
31
42
  let wkey = self.key(&["worker", worker_id]);
32
43
 
33
- conn.hset::<_, _, _, ()>(&wkey, "last_heartbeat", now)
34
- .map_err(map_err)?;
44
+ let pipe = &mut redis::pipe();
45
+ pipe.hset(&wkey, "last_heartbeat", now);
46
+ pipe.hset(&wkey, "resource_health", resource_health.unwrap_or(""));
47
+ pipe.query::<()>(&mut conn).map_err(map_err)?;
35
48
 
36
49
  Ok(())
37
50
  }
@@ -52,7 +65,8 @@ impl RedisStorage {
52
65
  continue;
53
66
  }
54
67
 
55
- let tags_val = data.get("tags").map(|s| s.as_str()).unwrap_or("");
68
+ let to_opt =
69
+ |key: &str| -> Option<String> { data.get(key).filter(|s| !s.is_empty()).cloned() };
56
70
 
57
71
  rows.push(WorkerRow {
58
72
  worker_id: wid,
@@ -68,11 +82,13 @@ impl RedisStorage {
68
82
  .get("status")
69
83
  .cloned()
70
84
  .unwrap_or_else(|| "active".to_string()),
71
- tags: if tags_val.is_empty() {
72
- None
73
- } else {
74
- Some(tags_val.to_string())
75
- },
85
+ tags: to_opt("tags"),
86
+ resources: to_opt("resources"),
87
+ resource_health: to_opt("resource_health"),
88
+ threads: data
89
+ .get("threads")
90
+ .and_then(|s| s.parse().ok())
91
+ .unwrap_or(0),
76
92
  });
77
93
  }
78
94
 
@@ -144,6 +144,9 @@ diesel::table! {
144
144
  queues -> Text,
145
145
  status -> Text,
146
146
  tags -> Nullable<Text>,
147
+ resources -> Nullable<Text>,
148
+ resource_health -> Nullable<Text>,
149
+ threads -> Integer,
147
150
  }
148
151
  }
149
152
 
@@ -340,6 +340,17 @@ impl SqliteStorage {
340
340
  // Migration: add tags column to workers
341
341
  migration_alter(&mut conn, "ALTER TABLE workers ADD COLUMN tags TEXT");
342
342
 
343
+ // Migration: add resource advertisement columns to workers
344
+ migration_alter(&mut conn, "ALTER TABLE workers ADD COLUMN resources TEXT");
345
+ migration_alter(
346
+ &mut conn,
347
+ "ALTER TABLE workers ADD COLUMN resource_health TEXT",
348
+ );
349
+ migration_alter(
350
+ &mut conn,
351
+ "ALTER TABLE workers ADD COLUMN threads INTEGER NOT NULL DEFAULT 0",
352
+ );
353
+
343
354
  // ── Queue State ──────────────────────────────────
344
355
  diesel::sql_query(
345
356
  "CREATE TABLE IF NOT EXISTS queue_state (
@@ -11,7 +11,15 @@ const DEAD_WORKER_THRESHOLD_MS: i64 = 30_000;
11
11
 
12
12
  impl SqliteStorage {
13
13
  /// Register a new worker or update an existing one.
14
- pub fn register_worker(&self, worker_id: &str, queues: &str, tags: Option<&str>) -> Result<()> {
14
+ pub fn register_worker(
15
+ &self,
16
+ worker_id: &str,
17
+ queues: &str,
18
+ tags: Option<&str>,
19
+ resources: Option<&str>,
20
+ resource_health: Option<&str>,
21
+ threads: i32,
22
+ ) -> Result<()> {
15
23
  let mut conn = self.conn()?;
16
24
  let now = now_millis();
17
25
 
@@ -21,6 +29,9 @@ impl SqliteStorage {
21
29
  queues,
22
30
  status: "active",
23
31
  tags,
32
+ resources,
33
+ resource_health,
34
+ threads,
24
35
  };
25
36
 
26
37
  diesel::replace_into(workers::table)
@@ -31,13 +42,16 @@ impl SqliteStorage {
31
42
  }
32
43
 
33
44
  /// Update the heartbeat timestamp for a worker.
34
- pub fn heartbeat(&self, worker_id: &str) -> Result<()> {
45
+ pub fn heartbeat(&self, worker_id: &str, resource_health: Option<&str>) -> Result<()> {
35
46
  let mut conn = self.conn()?;
36
47
  let now = now_millis();
37
48
 
38
49
  diesel::update(workers::table)
39
50
  .filter(workers::worker_id.eq(worker_id))
40
- .set(workers::last_heartbeat.eq(now))
51
+ .set((
52
+ workers::last_heartbeat.eq(now),
53
+ workers::resource_health.eq(resource_health),
54
+ ))
41
55
  .execute(&mut conn)?;
42
56
 
43
57
  Ok(())
@@ -113,8 +113,16 @@ pub trait Storage: Send + Sync + Clone {
113
113
 
114
114
  // ── Worker operations ───────────────────────────────────────────
115
115
 
116
- fn register_worker(&self, worker_id: &str, queues: &str, tags: Option<&str>) -> Result<()>;
117
- fn heartbeat(&self, worker_id: &str) -> Result<()>;
116
+ fn register_worker(
117
+ &self,
118
+ worker_id: &str,
119
+ queues: &str,
120
+ tags: Option<&str>,
121
+ resources: Option<&str>,
122
+ resource_health: Option<&str>,
123
+ threads: i32,
124
+ ) -> Result<()>;
125
+ fn heartbeat(&self, worker_id: &str, resource_health: Option<&str>) -> Result<()>;
118
126
  fn list_workers(&self) -> Result<Vec<WorkerRow>>;
119
127
  fn reap_dead_workers(&self) -> Result<u64>;
120
128
  fn unregister_worker(&self, worker_id: &str) -> Result<()>;
@@ -162,11 +162,20 @@ fn test_record_and_get_errors(s: &impl Storage) {
162
162
  }
163
163
 
164
164
  fn test_workers(s: &impl Storage) {
165
- s.register_worker("w-test-1", "q-workers", None).unwrap();
166
- s.heartbeat("w-test-1").unwrap();
165
+ let resources = Some(r#"["db","redis"]"#);
166
+ let health = Some(r#"{"db":"healthy","redis":"healthy"}"#);
167
+
168
+ s.register_worker("w-test-1", "q-workers", None, resources, health, 4)
169
+ .unwrap();
170
+ s.heartbeat("w-test-1", Some(r#"{"db":"unhealthy","redis":"healthy"}"#))
171
+ .unwrap();
167
172
 
168
173
  let workers = s.list_workers().unwrap();
169
174
  assert!(!workers.is_empty());
175
+ let w = workers.iter().find(|w| w.worker_id == "w-test-1").unwrap();
176
+ assert_eq!(w.threads, 4);
177
+ assert!(w.resources.as_deref().unwrap().contains("db"));
178
+ assert!(w.resource_health.as_deref().unwrap().contains("unhealthy"));
170
179
 
171
180
  s.unregister_worker("w-test-1").unwrap();
172
181
  }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "taskito-python"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  edition = "2021"
5
5
 
6
6
  [features]
@@ -8,6 +8,7 @@ default = []
8
8
  extension-module = ["pyo3/extension-module"]
9
9
  postgres = ["taskito-core/postgres"]
10
10
  redis = ["taskito-core/redis"]
11
+ native-async = ["dep:taskito-async"]
11
12
 
12
13
  [lib]
13
14
  name = "_taskito"
@@ -20,3 +21,4 @@ tokio = { workspace = true }
20
21
  crossbeam-channel = { workspace = true }
21
22
  uuid = { workspace = true }
22
23
  async-trait = { workspace = true }
24
+ taskito-async = { path = "../taskito-async", optional = true }
@@ -1,5 +1,6 @@
1
1
  use pyo3::prelude::*;
2
2
 
3
+ #[cfg(not(feature = "native-async"))]
3
4
  mod async_worker;
4
5
  mod py_config;
5
6
  mod py_job;
@@ -15,5 +16,7 @@ fn _taskito(m: &Bound<'_, PyModule>) -> PyResult<()> {
15
16
  m.add_class::<PyQueue>()?;
16
17
  m.add_class::<PyJob>()?;
17
18
  m.add_class::<PyTaskConfig>()?;
19
+ #[cfg(feature = "native-async")]
20
+ m.add_class::<taskito_async::PyResultSender>()?;
18
21
  Ok(())
19
22
  }
@@ -371,6 +371,9 @@ impl PyQueue {
371
371
  dict.set_item("queues", r.queues)?;
372
372
  dict.set_item("status", r.status)?;
373
373
  dict.set_item("tags", r.tags)?;
374
+ dict.set_item("resources", r.resources)?;
375
+ dict.set_item("resource_health", r.resource_health)?;
376
+ dict.set_item("threads", r.threads)?;
374
377
  result.push(dict.into());
375
378
  }
376
379
  Ok(result)
@@ -11,6 +11,7 @@ use taskito_core::scheduler::{JobResult, Scheduler, SchedulerConfig, TaskConfig}
11
11
  use taskito_core::storage::Storage;
12
12
 
13
13
  use super::PyQueue;
14
+ #[cfg(not(feature = "native-async"))]
14
15
  use crate::async_worker::AsyncWorkerPool;
15
16
  use crate::py_config::PyTaskConfig;
16
17
 
@@ -18,7 +19,21 @@ use crate::py_config::PyTaskConfig;
18
19
  #[allow(clippy::useless_conversion)]
19
20
  impl PyQueue {
20
21
  /// Run the worker loop. This blocks until interrupted.
21
- #[pyo3(signature = (task_registry, task_configs, queues=None, drain_timeout_secs=None, tags=None))]
22
+ ///
23
+ /// The heartbeat is now driven from Python (see `worker_heartbeat`),
24
+ /// so the internal Rust heartbeat thread is removed.
25
+ #[pyo3(signature = (
26
+ task_registry,
27
+ task_configs,
28
+ queues=None,
29
+ drain_timeout_secs=None,
30
+ tags=None,
31
+ worker_id=None,
32
+ resources=None,
33
+ threads=1,
34
+ async_concurrency=100,
35
+ ))]
36
+ #[allow(clippy::too_many_arguments)]
22
37
  pub fn run_worker(
23
38
  &self,
24
39
  py: Python<'_>,
@@ -27,6 +42,10 @@ impl PyQueue {
27
42
  queues: Option<Vec<String>>,
28
43
  drain_timeout_secs: Option<u64>,
29
44
  tags: Option<String>,
45
+ worker_id: Option<String>,
46
+ resources: Option<String>,
47
+ threads: i32,
48
+ #[allow(unused_variables)] async_concurrency: i32,
30
49
  ) -> PyResult<()> {
31
50
  // Reset shutdown flag for this run
32
51
  self.shutdown_flag.store(false, Ordering::SeqCst);
@@ -113,25 +132,39 @@ impl PyQueue {
113
132
  let scheduler_arc = Arc::new(scheduler);
114
133
  let scheduler_for_dispatch = scheduler_arc.clone();
115
134
 
116
- // Generate a unique worker ID and register
117
- let worker_id = uuid::Uuid::now_v7().to_string();
118
- let _ = self
119
- .storage
120
- .register_worker(&worker_id, &queues_str, tags.as_deref());
121
-
122
- // Start heartbeat thread
123
- let heartbeat_storage = self.storage.clone();
124
- let heartbeat_worker_id = worker_id.clone();
125
- let heartbeat_flag = self.shutdown_flag.clone();
126
- let heartbeat_handle = std::thread::spawn(move || {
127
- while !heartbeat_flag.load(Ordering::SeqCst) {
128
- let _ = heartbeat_storage.heartbeat(&heartbeat_worker_id);
129
- let _ = heartbeat_storage.reap_dead_workers();
130
- std::thread::sleep(std::time::Duration::from_secs(5));
131
- }
132
- });
135
+ // Generate or use the provided worker ID and register
136
+ let worker_id = worker_id.unwrap_or_else(|| uuid::Uuid::now_v7().to_string());
137
+ let _ = self.storage.register_worker(
138
+ &worker_id,
139
+ &queues_str,
140
+ tags.as_deref(),
141
+ resources.as_deref(),
142
+ None,
143
+ threads,
144
+ );
145
+
146
+ // Create the async executor for native async tasks (if feature enabled)
147
+ #[cfg(feature = "native-async")]
148
+ let async_executor = {
149
+ let sender = taskito_async::PyResultSender::new(result_tx.clone());
150
+ Python::with_gil(|py| -> PyResult<Arc<PyObject>> {
151
+ let sender_obj = pyo3::Py::new(py, sender)?;
152
+ let mod_ = py.import_bound("taskito.async_support.executor")?;
153
+ let cls = mod_.getattr("AsyncTaskExecutor")?;
154
+ let context_mod = py.import_bound("taskito.context")?;
155
+ let queue_ref = context_mod.getattr("_queue_ref")?;
156
+ let executor = cls.call1((
157
+ sender_obj,
158
+ registry_arc.clone_ref(py),
159
+ queue_ref,
160
+ async_concurrency,
161
+ ))?;
162
+ executor.call_method0("start")?;
163
+ Ok(Arc::new(executor.into_py(py)))
164
+ })?
165
+ };
133
166
 
134
- // Create multi-threaded tokio runtime for scheduler + async worker pool
167
+ // Create multi-threaded tokio runtime for scheduler + worker pool
135
168
  let num_workers = self.num_workers;
136
169
  // Move result_tx into the runtime — don't keep a copy in the main thread
137
170
  // so result_rx disconnects when all workers are done.
@@ -149,15 +182,29 @@ impl PyQueue {
149
182
  };
150
183
 
151
184
  rt.block_on(async {
152
- let pool = AsyncWorkerPool::new(num_workers, registry_arc, filters_arc);
153
-
154
185
  let scheduler_task = tokio::spawn(async move {
155
186
  scheduler_for_dispatch.run(job_tx).await;
156
187
  });
157
188
 
158
189
  let worker_task = tokio::spawn(async move {
159
190
  use taskito_core::worker::WorkerDispatcher;
160
- pool.run(job_rx, result_tx).await;
191
+
192
+ #[cfg(feature = "native-async")]
193
+ {
194
+ let pool = taskito_async::NativeAsyncPool::new(
195
+ num_workers,
196
+ registry_arc,
197
+ filters_arc,
198
+ async_executor,
199
+ );
200
+ pool.run(job_rx, result_tx).await;
201
+ }
202
+
203
+ #[cfg(not(feature = "native-async"))]
204
+ {
205
+ let pool = AsyncWorkerPool::new(num_workers, registry_arc, filters_arc);
206
+ pool.run(job_rx, result_tx).await;
207
+ }
161
208
  });
162
209
 
163
210
  let _ = tokio::join!(scheduler_task, worker_task);
@@ -245,11 +292,20 @@ impl PyQueue {
245
292
  }
246
293
 
247
294
  let _ = runtime_handle.join();
248
- let _ = heartbeat_handle.join();
249
295
 
250
296
  // Unregister worker on shutdown
251
297
  let _ = self.storage.unregister_worker(&worker_id);
252
298
 
253
299
  Ok(())
254
300
  }
301
+
302
+ /// Update the heartbeat for a running worker. Called from Python every 5s.
303
+ #[pyo3(signature = (worker_id, resource_health=None))]
304
+ pub fn worker_heartbeat(&self, worker_id: &str, resource_health: Option<&str>) -> PyResult<()> {
305
+ self.storage
306
+ .heartbeat(worker_id, resource_health)
307
+ .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
308
+ let _ = self.storage.reap_dead_workers();
309
+ Ok(())
310
+ }
255
311
  }