python-corekit 0.4.1__tar.gz → 0.6.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 (251) hide show
  1. {python_corekit-0.4.1/python_corekit.egg-info → python_corekit-0.6.0}/PKG-INFO +10 -8
  2. {python_corekit-0.4.1 → python_corekit-0.6.0}/README.md +7 -6
  3. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/connection.py +6 -2
  4. python_corekit-0.6.0/corekit/docker/__init__.py +9 -0
  5. python_corekit-0.6.0/corekit/docker/client.py +48 -0
  6. python_corekit-0.6.0/corekit/docker/constants.py +19 -0
  7. python_corekit-0.6.0/corekit/docker/disk/__init__.py +8 -0
  8. python_corekit-0.6.0/corekit/docker/disk/constants.py +14 -0
  9. python_corekit-0.6.0/corekit/docker/disk/inspector.py +217 -0
  10. python_corekit-0.6.0/corekit/docker/disk/models.py +44 -0
  11. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/docker/watchdog.py +10 -8
  12. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptions/__init__.py +2 -0
  13. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptions/base.py +19 -0
  14. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/files/__init__.py +3 -0
  15. python_corekit-0.6.0/corekit/files/disk.py +123 -0
  16. python_corekit-0.6.0/corekit/html/__init__.py +22 -0
  17. python_corekit-0.6.0/corekit/html/client.py +22 -0
  18. python_corekit-0.6.0/corekit/html/models.py +20 -0
  19. python_corekit-0.6.0/corekit/html/parser.py +272 -0
  20. python_corekit-0.6.0/corekit/html/scraper.py +127 -0
  21. python_corekit-0.6.0/corekit/html/settings.py +197 -0
  22. python_corekit-0.6.0/corekit/html/state.py +56 -0
  23. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/http/__init__.py +2 -1
  24. python_corekit-0.6.0/corekit/http/api.py +92 -0
  25. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/http/client.py +24 -54
  26. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/http/response.py +38 -5
  27. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/client.py +3 -2
  28. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/tools/loop.py +5 -2
  29. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/wire.py +11 -5
  30. python_corekit-0.6.0/corekit/process/__init__.py +7 -0
  31. python_corekit-0.6.0/corekit/process/runner.py +47 -0
  32. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/schemas/__init__.py +10 -1
  33. python_corekit-0.6.0/corekit/schemas/models/arbitrary.py +55 -0
  34. python_corekit-0.6.0/corekit/schemas/size.py +85 -0
  35. python_corekit-0.6.0/corekit/utils/__init__.py +116 -0
  36. python_corekit-0.6.0/corekit/utils/coercion.py +104 -0
  37. python_corekit-0.6.0/corekit/utils/collections.py +377 -0
  38. python_corekit-0.6.0/corekit/utils/parsers.py +205 -0
  39. {python_corekit-0.4.1 → python_corekit-0.6.0}/pyproject.toml +3 -2
  40. {python_corekit-0.4.1 → python_corekit-0.6.0/python_corekit.egg-info}/PKG-INFO +10 -8
  41. {python_corekit-0.4.1 → python_corekit-0.6.0}/python_corekit.egg-info/SOURCES.txt +23 -0
  42. {python_corekit-0.4.1 → python_corekit-0.6.0}/python_corekit.egg-info/requires.txt +1 -0
  43. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_architecture.py +2 -0
  44. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_docker.py +141 -1
  45. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_files.py +64 -0
  46. python_corekit-0.6.0/tests/test_html.py +214 -0
  47. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_llm.py +48 -0
  48. python_corekit-0.6.0/tests/test_process.py +36 -0
  49. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_requests.py +19 -3
  50. python_corekit-0.6.0/tests/test_schemas_arbitrary.py +55 -0
  51. python_corekit-0.6.0/tests/test_schemas_size.py +56 -0
  52. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_sql.py +2 -1
  53. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_utils_coercion.py +80 -1
  54. python_corekit-0.6.0/tests/test_utils_collections.py +339 -0
  55. python_corekit-0.6.0/tests/test_utils_parsers.py +238 -0
  56. python_corekit-0.4.1/corekit/docker/__init__.py +0 -7
  57. python_corekit-0.4.1/corekit/http/api.py +0 -24
  58. python_corekit-0.4.1/corekit/schemas/models/arbitrary.py +0 -11
  59. python_corekit-0.4.1/corekit/utils/__init__.py +0 -61
  60. python_corekit-0.4.1/corekit/utils/coercion.py +0 -118
  61. python_corekit-0.4.1/corekit/utils/collections.py +0 -139
  62. python_corekit-0.4.1/tests/test_utils_collections.py +0 -139
  63. {python_corekit-0.4.1 → python_corekit-0.6.0}/LICENSE +0 -0
  64. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/__init__.py +0 -0
  65. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/api/__init__.py +0 -0
  66. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/api/application.py +0 -0
  67. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/api/handler.py +0 -0
  68. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/api/lifespan.py +0 -0
  69. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/api/middleware.py +0 -0
  70. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/api/responses.py +0 -0
  71. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/api/routers.py +0 -0
  72. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/concurrency/__init__.py +0 -0
  73. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/concurrency/decorators.py +0 -0
  74. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/concurrency/thread_local.py +0 -0
  75. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/concurrency/worker.py +0 -0
  76. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/config/__init__.py +0 -0
  77. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/config/loader.py +0 -0
  78. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/config/settings.py +0 -0
  79. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/config/sources.py +0 -0
  80. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/__init__.py +0 -0
  81. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/connectable.py +0 -0
  82. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/decorators.py +0 -0
  83. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/redis/__init__.py +0 -0
  84. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/redis/connection.py +0 -0
  85. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/registry.py +0 -0
  86. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/__init__.py +0 -0
  87. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/fields/__init__.py +0 -0
  88. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/fields/jsonb.py +0 -0
  89. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/migration/__init__.py +0 -0
  90. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/migration/base.py +0 -0
  91. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/migration/operations.py +0 -0
  92. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/migration/registry.py +0 -0
  93. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/migration/table.py +0 -0
  94. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/operations/__init__.py +0 -0
  95. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/operations/base.py +0 -0
  96. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/operations/statements.py +0 -0
  97. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/query.py +0 -0
  98. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/connections/sql/table.py +0 -0
  99. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/crypto/__init__.py +0 -0
  100. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/crypto/constants.py +0 -0
  101. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/crypto/enum.py +0 -0
  102. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/crypto/hasher.py +0 -0
  103. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/data/__init__.py +0 -0
  104. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/data/dataset.py +0 -0
  105. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/data/expressions/__init__.py +0 -0
  106. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/data/expressions/comparison.py +0 -0
  107. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/data/expressions/expression.py +0 -0
  108. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/data/expressions/operator.py +0 -0
  109. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/data/expressions/target.py +0 -0
  110. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/data/record.py +0 -0
  111. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/data/stats.py +0 -0
  112. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/decorators/__init__.py +0 -0
  113. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/decorators/exception_handling.py +0 -0
  114. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/decorators/warnings.py +0 -0
  115. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/__init__.py +0 -0
  116. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/connection.py +0 -0
  117. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/extract/__init__.py +0 -0
  118. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/extract/extractor.py +0 -0
  119. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/extract/schemas.py +0 -0
  120. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/load/__init__.py +0 -0
  121. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/load/loader.py +0 -0
  122. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/load/schemas.py +0 -0
  123. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/orchestrator.py +0 -0
  124. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/schemas.py +0 -0
  125. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/transform/__init__.py +0 -0
  126. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/transform/schemas.py +0 -0
  127. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/etl/transform/transformer.py +0 -0
  128. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/events/__init__.py +0 -0
  129. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/events/enum.py +0 -0
  130. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/events/frames.py +0 -0
  131. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/events/models.py +0 -0
  132. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/events/publisher.py +0 -0
  133. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/events/reader.py +0 -0
  134. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/events/sse.py +0 -0
  135. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/events/websocket.py +0 -0
  136. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptionator/__init__.py +0 -0
  137. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptionator/actions.py +0 -0
  138. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptionator/asgi.py +0 -0
  139. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptionator/constants.py +0 -0
  140. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptionator/exceptionator.py +0 -0
  141. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptionator/fingerprint.py +0 -0
  142. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptionator/guard.py +0 -0
  143. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptionator/jobs.py +0 -0
  144. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptionator/models.py +0 -0
  145. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptions/custom/__init__.py +0 -0
  146. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptions/enum.py +0 -0
  147. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptions/http/__init__.py +0 -0
  148. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/exceptions/types.py +0 -0
  149. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/files/base.py +0 -0
  150. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/files/enum.py +0 -0
  151. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/files/json.py +0 -0
  152. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/files/toml.py +0 -0
  153. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/http/exceptions.py +0 -0
  154. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/http/exponential_backoff.py +0 -0
  155. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/http/status.py +0 -0
  156. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/http/stream.py +0 -0
  157. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/jobs/__init__.py +0 -0
  158. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/jobs/registry.py +0 -0
  159. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/jobs/runner.py +0 -0
  160. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/jobs/task.py +0 -0
  161. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/__init__.py +0 -0
  162. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/enum.py +0 -0
  163. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/events.py +0 -0
  164. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/messages.py +0 -0
  165. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/prompts/__init__.py +0 -0
  166. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/prompts/enum.py +0 -0
  167. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/prompts/exceptions.py +0 -0
  168. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/prompts/loader.py +0 -0
  169. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/prompts/template.py +0 -0
  170. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/protocols.py +0 -0
  171. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/streaming.py +0 -0
  172. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/tools/__init__.py +0 -0
  173. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/tools/base.py +0 -0
  174. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/tools/detection.py +0 -0
  175. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/llm/tools/registry.py +0 -0
  176. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/log_monitor/__init__.py +0 -0
  177. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/log_monitor/constants.py +0 -0
  178. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/log_monitor/models.py +0 -0
  179. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/log_monitor/service.py +0 -0
  180. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/notifications/__init__.py +0 -0
  181. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/notifications/base.py +0 -0
  182. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/notifications/models.py +0 -0
  183. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/observability/__init__.py +0 -0
  184. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/observability/benchmarkable.py +0 -0
  185. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/observability/loggable.py +0 -0
  186. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/observability/request_context.py +0 -0
  187. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/observability/timing/__init__.py +0 -0
  188. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/observability/timing/constants.py +0 -0
  189. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/observability/timing/split.py +0 -0
  190. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/observability/timing/timer.py +0 -0
  191. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/py.typed +0 -0
  192. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/registry/__init__.py +0 -0
  193. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/registry/ordered.py +0 -0
  194. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/registry/registry.py +0 -0
  195. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/schemas/dataclasses/__init__.py +0 -0
  196. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/schemas/enum.py +0 -0
  197. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/schemas/models/__init__.py +0 -0
  198. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/schemas/models/date_models.py +0 -0
  199. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/schemas/pydantic/__init__.py +0 -0
  200. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/schemas/pydantic/fields.py +0 -0
  201. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/schemas/types.py +0 -0
  202. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/schemas/version.py +0 -0
  203. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/serialization/__init__.py +0 -0
  204. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/serialization/enum.py +0 -0
  205. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/serialization/pickle_file.py +0 -0
  206. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/serialization/serializable.py +0 -0
  207. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/serialization/serializer.py +0 -0
  208. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/utils/ids.py +0 -0
  209. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/utils/payload.py +0 -0
  210. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/utils/raise_exc.py +0 -0
  211. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/utils/text.py +0 -0
  212. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/utils/time.py +0 -0
  213. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/utils/validators.py +0 -0
  214. {python_corekit-0.4.1 → python_corekit-0.6.0}/corekit/utils/void.py +0 -0
  215. {python_corekit-0.4.1 → python_corekit-0.6.0}/python_corekit.egg-info/dependency_links.txt +0 -0
  216. {python_corekit-0.4.1 → python_corekit-0.6.0}/python_corekit.egg-info/top_level.txt +0 -0
  217. {python_corekit-0.4.1 → python_corekit-0.6.0}/setup.cfg +0 -0
  218. {python_corekit-0.4.1 → python_corekit-0.6.0}/setup.py +0 -0
  219. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_api.py +0 -0
  220. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_application.py +0 -0
  221. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_concurrency.py +0 -0
  222. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_config.py +0 -0
  223. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_connections.py +0 -0
  224. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_data.py +0 -0
  225. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_etl.py +0 -0
  226. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_events.py +0 -0
  227. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_exceptionator.py +0 -0
  228. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_exceptions.py +0 -0
  229. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_expressions.py +0 -0
  230. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_expressions_sqlalchemy.py +0 -0
  231. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_imports.py +0 -0
  232. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_imports_are_top_level.py +0 -0
  233. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_jobs.py +0 -0
  234. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_lifespan.py +0 -0
  235. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_log_monitor.py +0 -0
  236. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_middleware_stack.py +0 -0
  237. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_migration.py +0 -0
  238. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_notifications.py +0 -0
  239. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_observability.py +0 -0
  240. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_operations.py +0 -0
  241. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_ordered_registry.py +0 -0
  242. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_redis.py +0 -0
  243. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_registry.py +0 -0
  244. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_request_context.py +0 -0
  245. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_schemas_enum.py +0 -0
  246. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_serialization.py +0 -0
  247. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_utils.py +0 -0
  248. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_utils_ids.py +0 -0
  249. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_utils_payload.py +0 -0
  250. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_utils_text.py +0 -0
  251. {python_corekit-0.4.1 → python_corekit-0.6.0}/tests/test_utils_time.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-corekit
3
- Version: 0.4.1
4
- Summary: Shared foundations for Python projects: logging, benchmarking, registries, FastAPI application and routers, SQL statements and migrations, background tasks, ETL, and LLM chat/tool primitives
3
+ Version: 0.6.0
4
+ Summary: Shared foundations for Python projects: logging, benchmarking, registries, FastAPI application and routers, SQL statements and migrations, background tasks, ETL, LLM chat/tool primitives, and HTML scraping
5
5
  Author: Steven Jacobsen
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/stevejaker/corekit
@@ -23,6 +23,7 @@ Requires-Dist: sqlmodel<0.1,>=0.0.16
23
23
  Requires-Dist: SQLAlchemy<3,>=2.0
24
24
  Requires-Dist: redis<7,>=5.0
25
25
  Requires-Dist: httpx<1,>=0.27
26
+ Requires-Dist: selectolax<1,>=0.3
26
27
  Requires-Dist: docker<8,>=7.0
27
28
  Requires-Dist: PyYAML<7,>=6.0
28
29
  Requires-Dist: dill<0.5,>=0.3.8
@@ -347,13 +348,13 @@ class GithubClient(BaseApiClient):
347
348
  return "https://api.github.com"
348
349
 
349
350
  response = GithubClient().get("/users/octocat")
350
- response.data["login"]
351
+ response.data.login
351
352
  ```
352
353
 
353
- Retries 429 and 5xx with exponential backoff. Every response is a
354
- `BaseApiResponse`, so a non-JSON error page leaves `data` empty rather than
355
- raising. The same methods are awaitable inside a running event loop:
356
- `await client.get(...)`.
354
+ Retries 429 and 5xx with exponential backoff. ``BaseHttpClient`` returns a
355
+ ``BaseHttpResponse`` (text + metadata); ``BaseApiClient`` adds structured
356
+ ``data`` from JSON (``None`` when the body is not JSON). The same methods are
357
+ awaitable inside a running event loop: `await client.get(...)`.
357
358
 
358
359
  ## Serialization
359
360
 
@@ -432,6 +433,7 @@ corekit/
432
433
 
433
434
  observability/ Loggable, Benchmarkable, Timer
434
435
  registry/ schemas/ utils/ registries, enums and fields, helpers
436
+ process/ CommandRunner (external commands)
435
437
  data/ Dataset and its filter expressions
436
438
  jobs/ queue-independent background tasks
437
439
  crypto/ files/ serialization/
@@ -444,7 +446,7 @@ corekit/
444
446
  http/ BaseHttpClient, BaseApiClient, retries, responses
445
447
 
446
448
  api/ Application, lifespan, middleware, routers
447
- docker/ notifications/ etl/
449
+ docker/ notifications/ etl/ Watchdog, DockerDiskInspector, client provider
448
450
  llm/ messages, tools, agentic loop (no GenerationClient)
449
451
 
450
452
  events/ log_monitor/ built on the capabilities above
@@ -311,13 +311,13 @@ class GithubClient(BaseApiClient):
311
311
  return "https://api.github.com"
312
312
 
313
313
  response = GithubClient().get("/users/octocat")
314
- response.data["login"]
314
+ response.data.login
315
315
  ```
316
316
 
317
- Retries 429 and 5xx with exponential backoff. Every response is a
318
- `BaseApiResponse`, so a non-JSON error page leaves `data` empty rather than
319
- raising. The same methods are awaitable inside a running event loop:
320
- `await client.get(...)`.
317
+ Retries 429 and 5xx with exponential backoff. ``BaseHttpClient`` returns a
318
+ ``BaseHttpResponse`` (text + metadata); ``BaseApiClient`` adds structured
319
+ ``data`` from JSON (``None`` when the body is not JSON). The same methods are
320
+ awaitable inside a running event loop: `await client.get(...)`.
321
321
 
322
322
  ## Serialization
323
323
 
@@ -396,6 +396,7 @@ corekit/
396
396
 
397
397
  observability/ Loggable, Benchmarkable, Timer
398
398
  registry/ schemas/ utils/ registries, enums and fields, helpers
399
+ process/ CommandRunner (external commands)
399
400
  data/ Dataset and its filter expressions
400
401
  jobs/ queue-independent background tasks
401
402
  crypto/ files/ serialization/
@@ -408,7 +409,7 @@ corekit/
408
409
  http/ BaseHttpClient, BaseApiClient, retries, responses
409
410
 
410
411
  api/ Application, lifespan, middleware, routers
411
- docker/ notifications/ etl/
412
+ docker/ notifications/ etl/ Watchdog, DockerDiskInspector, client provider
412
413
  llm/ messages, tools, agentic loop (no GenerationClient)
413
414
 
414
415
  events/ log_monitor/ built on the capabilities above
@@ -330,15 +330,19 @@ class SQLConnection(Connectable):
330
330
  """
331
331
  return list(self._exec(query.build()))
332
332
 
333
- def delete_many_by_query(self, query: Query) -> None:
333
+ def delete_many_by_query(self, query: Query) -> int:
334
334
  """
335
335
  Delete every row matching a Query.
336
+
337
+ Returns the number of rows deleted.
336
338
  """
337
339
  self.require_writable()
338
340
  self._start_session()
339
- for item in self.fetch_many_by_query(query):
341
+ items = self.fetch_many_by_query(query)
342
+ for item in items:
340
343
  self.session.delete(item)
341
344
  self.session.commit()
345
+ return len(items)
342
346
 
343
347
  def execute(self, operation: Operation) -> Any:
344
348
  """
@@ -0,0 +1,9 @@
1
+ """
2
+ Docker container control and disk inspection.
3
+ """
4
+
5
+ from corekit.docker.client import DockerClientProvider
6
+ from corekit.docker.disk import ContainerLog, DfCategory, DockerDiskInspector, ImageSize
7
+ from corekit.docker.watchdog import Watchdog
8
+
9
+ __all__ = ["ContainerLog", "DfCategory", "DockerClientProvider", "DockerDiskInspector", "ImageSize", "Watchdog"]
@@ -0,0 +1,48 @@
1
+ """
2
+ Getting a Docker SDK client without assuming where the daemon lives.
3
+
4
+ ``DockerClientProvider`` is the one place that resolves the endpoint:
5
+ an explicit host wins, then ``$DOCKER_HOST``, then the first socket that
6
+ actually exists among the usual locations (including snap's), and only
7
+ then the classic default. ``Watchdog`` and ``DockerDiskInspector`` both
8
+ build their clients through here, so a daemon that moves (classic install
9
+ to snap, say) is found by both.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+
16
+ from docker import DockerClient
17
+
18
+ from corekit.docker.constants import DEFAULT_DOCKER_HOST, DOCKER_SOCKET_CANDIDATES
19
+
20
+ __all__ = ["DockerClientProvider"]
21
+
22
+
23
+ class DockerClientProvider:
24
+ """
25
+ Resolves Docker endpoints and builds SDK clients for them.
26
+ """
27
+
28
+ @staticmethod
29
+ def resolve_host(docker_host: str | None = None) -> str:
30
+ """
31
+ Resolve the endpoint to use, without connecting to it.
32
+ """
33
+ if docker_host:
34
+ return docker_host
35
+ from_env = os.environ.get("DOCKER_HOST")
36
+ if from_env:
37
+ return from_env
38
+ for candidate in DOCKER_SOCKET_CANDIDATES:
39
+ if candidate.exists():
40
+ return f"unix://{candidate}"
41
+ return DEFAULT_DOCKER_HOST
42
+
43
+ @staticmethod
44
+ def create_client(docker_host: str | None = None) -> DockerClient:
45
+ """
46
+ Build an SDK client for the resolved endpoint.
47
+ """
48
+ return DockerClient(base_url=DockerClientProvider.resolve_host(docker_host))
@@ -0,0 +1,19 @@
1
+ """
2
+ Shared Docker daemon constants.
3
+ """
4
+
5
+ from pathlib import Path
6
+
7
+ DEFAULT_DOCKER_HOST = "unix:///var/run/docker.sock"
8
+
9
+ # Candidate sockets in preference order. Snap Docker does not use
10
+ # /var/run/docker.sock, which is the single most common reason an SDK
11
+ # client "cannot connect" on an Ubuntu host.
12
+ DOCKER_SOCKET_CANDIDATES: tuple[Path, ...] = (
13
+ Path("/var/run/docker.sock"),
14
+ Path("/run/docker.sock"),
15
+ Path("/var/snap/docker/current/run/docker.sock"),
16
+ )
17
+
18
+ WATCHDOG_LABEL = "watchdog"
19
+ WATCHDOG_MANAGED = "true"
@@ -0,0 +1,8 @@
1
+ """
2
+ Docker disk inspection: what is using the space under the Docker root.
3
+ """
4
+
5
+ from corekit.docker.disk.inspector import DockerDiskInspector
6
+ from corekit.docker.disk.models import ContainerLog, DfCategory, ImageSize
7
+
8
+ __all__ = ["ContainerLog", "DfCategory", "DockerDiskInspector", "ImageSize"]
@@ -0,0 +1,14 @@
1
+ """
2
+ Constants for Docker disk inspection.
3
+ """
4
+
5
+ # (display name, key in the daemon's df report, dot-path to the entry's size)
6
+ # Volumes nest their size under UsageData; everything else is a top-level field.
7
+ DF_CATEGORIES: tuple[tuple[str, str, str], ...] = (
8
+ ("Images", "Images", "Size"),
9
+ ("Containers", "Containers", "SizeRw"),
10
+ ("Volumes", "Volumes", "UsageData.Size"),
11
+ ("Build Cache", "BuildCache", "Size"),
12
+ )
13
+
14
+ UNREFERENCED_IMAGE = "<none>:<none>"
@@ -0,0 +1,217 @@
1
+ """
2
+ Where Docker's disk went.
3
+
4
+ ``DockerDiskInspector`` is the read-side counterpart to ``Watchdog``:
5
+ Watchdog changes container state, the inspector measures it -- Docker root
6
+ usage, per-container log sizes, largest volumes and images, and a
7
+ ``docker system df``-style summary. It can also truncate container logs,
8
+ which is a measurement remedy rather than lifecycle control, and safe:
9
+ truncating in place reclaims space immediately, deleting would not.
10
+
11
+ Daemon data comes from the Docker SDK (built by ``DockerClientProvider``,
12
+ so snap and classic installs are both found); on-disk sizes come from
13
+ ``corekit.files.DiskScanner``, because the daemon does not report log-file
14
+ sizes and its volume accounting does not match ``du``. Filesystem access
15
+ under the Docker root typically needs elevation, which the scanner handles.
16
+
17
+ Daemon payloads are untrusted-shaped: every field is extracted through the
18
+ ``parse_*``/``safe_*`` utilities and ``getval`` rather than raw indexing.
19
+
20
+ inspector = DockerDiskInspector()
21
+ inspector.root_dir() # snap-aware: the socket is discovered
22
+ inspector.container_logs() # every log, biggest first
23
+ inspector.truncate_log("api") # after you have looked at it
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ from pathlib import Path
29
+
30
+ from docker import DockerClient
31
+ from docker.errors import APIError, NotFound
32
+
33
+ from corekit.docker.client import DockerClientProvider
34
+ from corekit.docker.disk import constants as disk_constants
35
+ from corekit.docker.disk.models import ContainerLog, DfCategory, ImageSize
36
+ from corekit.files.disk import DiskScanner, PathSize
37
+ from corekit.observability.benchmarkable import Benchmarkable
38
+ from corekit.utils import first_string, getval, parse_dict, safe_dict, safe_int, safe_list, safe_string
39
+
40
+ __all__ = ["DockerDiskInspector"]
41
+
42
+
43
+ class DockerDiskInspector(Benchmarkable):
44
+ """
45
+ Measures Docker disk usage via the SDK plus filesystem scans.
46
+
47
+ Unlike Watchdog there is no label enforcement: measuring is read-only,
48
+ and the one mutating operation (log truncation) is addressed by exact
49
+ container name or ID, never by pattern.
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ docker_host: str | None = None,
55
+ scanner: DiskScanner | None = None,
56
+ ) -> None:
57
+ """
58
+ :param docker_host: Docker endpoint; resolved by
59
+ ``DockerClientProvider`` (``$DOCKER_HOST``, existing sockets
60
+ including snap's, then the classic default) when omitted.
61
+ :param scanner: filesystem scanner for on-disk sizes; one with
62
+ elevation enabled by default.
63
+ """
64
+ super().__init__()
65
+ self.docker_host = DockerClientProvider.resolve_host(docker_host)
66
+ self._scanner = scanner or DiskScanner()
67
+ self._client_instance: DockerClient | None = None
68
+
69
+ @property
70
+ def _client(self) -> DockerClient:
71
+ """
72
+ The Docker client, connected on first use.
73
+ """
74
+ if self._client_instance is None:
75
+ self._client_instance = DockerClientProvider.create_client(self.docker_host)
76
+ return self._client_instance
77
+
78
+ # =============
79
+ # Daemon-level queries
80
+ # =============
81
+
82
+ def root_dir(self) -> Path:
83
+ """
84
+ The daemon's DockerRootDir (e.g. /var/snap/docker/common/var-lib-docker).
85
+ """
86
+ root = safe_string(getval(safe_dict(self._client.info()), "DockerRootDir"))
87
+ if not root:
88
+ raise RuntimeError("Daemon did not report a DockerRootDir (is docker running?)")
89
+ return Path(root)
90
+
91
+ def df_summary(self) -> list[DfCategory]:
92
+ """
93
+ Aggregate the daemon's df report into per-category counts and sizes.
94
+ """
95
+ report = parse_dict(self._client.df()).value
96
+ categories: list[DfCategory] = []
97
+ for display_name, report_key, size_path in disk_constants.DF_CATEGORIES:
98
+ entries = safe_list(getval(report, report_key, default=[]))
99
+ total = sum(safe_int(getval(entry, size_path, default=0)) for entry in entries)
100
+ categories.append(DfCategory(name=display_name, count=len(entries), size_bytes=total))
101
+ return categories
102
+
103
+ def images(self) -> list[ImageSize]:
104
+ """
105
+ Every image with its daemon-reported size, biggest first.
106
+ """
107
+ images = [
108
+ ImageSize(
109
+ reference=first_string(safe_list(image.tags), disk_constants.UNREFERENCED_IMAGE),
110
+ image_id=safe_string(image.short_id),
111
+ size_bytes=safe_int(getval(parse_dict(image.attrs).value, "Size")),
112
+ )
113
+ for image in self._client.images.list()
114
+ ]
115
+ return sorted(images, key=lambda img: img.size_bytes, reverse=True)
116
+
117
+ # =============
118
+ # Filesystem-backed queries
119
+ # =============
120
+
121
+ def root_children(self, root: Path | None = None) -> list[PathSize]:
122
+ """
123
+ Largest directories directly under the Docker root.
124
+ """
125
+ return self._scanner.child_sizes(root or self.root_dir())
126
+
127
+ def volumes(self, root: Path | None = None) -> list[PathSize]:
128
+ """
129
+ Largest Docker volumes (by on-disk size), biggest first.
130
+ """
131
+ return self._scanner.child_sizes((root or self.root_dir()) / "volumes")
132
+
133
+ def container_logs(self) -> list[ContainerLog]:
134
+ """
135
+ Every container's log with its size, biggest first.
136
+
137
+ Containers without a log path (non json-file drivers) or with an
138
+ unmeasurable log file are skipped.
139
+ """
140
+ logs: list[ContainerLog] = []
141
+ try:
142
+ containers = self._client.containers.list(all=True)
143
+ except APIError as exc:
144
+ self.error(f"Docker API error listing containers: {exc}")
145
+ return []
146
+ for container in containers:
147
+ log = self._measure_container_log(container)
148
+ if log is not None:
149
+ logs.append(log)
150
+ return sorted(logs, key=lambda x: x.size_bytes, reverse=True)
151
+
152
+ def logs_above(self, threshold_bytes: int) -> list[ContainerLog]:
153
+ """
154
+ Container logs strictly larger than the threshold, biggest first.
155
+ """
156
+ return [log for log in self.container_logs() if log.size_bytes > threshold_bytes]
157
+
158
+ def log_for(self, identifier: str) -> ContainerLog:
159
+ """
160
+ Resolve one container by name or ID and measure its log.
161
+
162
+ Raises LookupError if the container does not exist or has no log.
163
+ """
164
+ try:
165
+ container = self._client.containers.get(identifier)
166
+ except NotFound as exc:
167
+ raise LookupError(f"Container not found: {identifier}") from exc
168
+
169
+ log = self._measure_container_log(container)
170
+ if log is None:
171
+ raise LookupError(f"Container has no measurable log file (non json-file driver?): {identifier}")
172
+ return log
173
+
174
+ # =============
175
+ # Truncation
176
+ # =============
177
+
178
+ def truncate_log(self, identifier: str) -> ContainerLog:
179
+ """
180
+ Truncate one container's log; returns its pre-truncate measurement.
181
+ """
182
+ log = self.log_for(identifier)
183
+ self._scanner.truncate(log.log_path)
184
+ return log
185
+
186
+ def truncate_logs(self, logs: list[ContainerLog]) -> None:
187
+ """
188
+ Truncate each given container log.
189
+ """
190
+ for log in logs:
191
+ self._scanner.truncate(log.log_path)
192
+
193
+ # =============
194
+ # Private Methods
195
+ # =============
196
+
197
+ def _measure_container_log(self, container: object) -> ContainerLog | None:
198
+ """
199
+ Build a ContainerLog for one SDK container, or None when it has no
200
+ log path or the file cannot be measured.
201
+ """
202
+
203
+ log_path_text = safe_string(getval(container, "attrs.LogPath", default=""))
204
+ if not log_path_text:
205
+ return None
206
+
207
+ log_path = Path(log_path_text)
208
+ size = self._scanner.file_size(log_path)
209
+ if size is None:
210
+ return None
211
+
212
+ return ContainerLog(
213
+ container_id=safe_string(getval(container, "id")),
214
+ name=safe_string(getval(container, "name")).lstrip("/"),
215
+ log_path=log_path,
216
+ size_bytes=size,
217
+ )
@@ -0,0 +1,44 @@
1
+ """
2
+ Data shapes returned by DockerDiskInspector.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+
10
+ __all__ = ["ContainerLog", "DfCategory", "ImageSize"]
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class ContainerLog:
15
+ """
16
+ One container's json-file log and its measured size.
17
+ """
18
+
19
+ container_id: str
20
+ name: str
21
+ log_path: Path
22
+ size_bytes: int
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class ImageSize:
27
+ """
28
+ One image and its size as reported by the daemon.
29
+ """
30
+
31
+ reference: str
32
+ image_id: str
33
+ size_bytes: int
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class DfCategory:
38
+ """
39
+ One row of a ``docker system df``-style summary.
40
+ """
41
+
42
+ name: str
43
+ count: int
44
+ size_bytes: int
@@ -18,13 +18,13 @@ from docker import DockerClient
18
18
  from docker.errors import APIError, NotFound
19
19
  from docker.models.containers import Container
20
20
 
21
+ from corekit.docker.client import DockerClientProvider
22
+ from corekit.docker.constants import DEFAULT_DOCKER_HOST, WATCHDOG_LABEL, WATCHDOG_MANAGED
21
23
  from corekit.observability.benchmarkable import Benchmarkable
22
24
 
23
- __all__ = ["Watchdog"]
24
-
25
- DEFAULT_DOCKER_HOST = "unix:///var/run/docker.sock"
26
- WATCHDOG_LABEL = "watchdog"
27
- WATCHDOG_MANAGED = "true"
25
+ # DEFAULT_DOCKER_HOST / WATCHDOG_* are re-exported for backwards
26
+ # compatibility; the canonical definitions live in corekit.docker.constants.
27
+ __all__ = ["DEFAULT_DOCKER_HOST", "WATCHDOG_LABEL", "WATCHDOG_MANAGED", "Watchdog"]
28
28
 
29
29
 
30
30
  def _accept_all(*args: Any, **kwargs: Any) -> bool:
@@ -44,13 +44,15 @@ class Watchdog(Benchmarkable):
44
44
 
45
45
  def __init__(self, docker_host: str | None = None, enforce_label: bool = True) -> None:
46
46
  """
47
- :param docker_host: Docker endpoint. Defaults to the local socket.
47
+ :param docker_host: Docker endpoint. Defaults to the endpoint
48
+ resolved by ``DockerClientProvider`` (``$DOCKER_HOST``, then an
49
+ existing socket -- snap included -- then the classic default).
48
50
  :param enforce_label: restrict actions to containers carrying the
49
51
  managed label. On by default; turn it off only when this watchdog
50
52
  is meant to control every container on the host.
51
53
  """
52
54
  super().__init__()
53
- self.docker_host = docker_host or DEFAULT_DOCKER_HOST
55
+ self.docker_host = DockerClientProvider.resolve_host(docker_host)
54
56
  self.enforce_label = enforce_label
55
57
  self._client_instance: DockerClient | None = None
56
58
 
@@ -63,7 +65,7 @@ class Watchdog(Benchmarkable):
63
65
  and inspected on a machine where Docker is not running.
64
66
  """
65
67
  if self._client_instance is None:
66
- self._client_instance = DockerClient(base_url=self.docker_host)
68
+ self._client_instance = DockerClientProvider.create_client(self.docker_host)
67
69
  return self._client_instance
68
70
 
69
71
  @staticmethod
@@ -7,6 +7,7 @@ a user. ``CoreHTTPException`` is the FastAPI-facing public exception.
7
7
  """
8
8
 
9
9
  from corekit.exceptions.base import (
10
+ CommandFailedError,
10
11
  CoreException,
11
12
  CoreHTTPException,
12
13
  ExponentialBackoffTimeoutException,
@@ -20,6 +21,7 @@ from corekit.exceptions.types import ArbitraryCoreExceptionType, CoreExceptionTy
20
21
 
21
22
  __all__ = [
22
23
  "ArbitraryCoreExceptionType",
24
+ "CommandFailedError",
23
25
  "CoreException",
24
26
  "CoreExceptionType",
25
27
  "CoreHTTPException",
@@ -27,6 +27,7 @@ from fastapi import HTTPException
27
27
  from corekit.exceptions.enum import Retryability
28
28
 
29
29
  __all__ = [
30
+ "CommandFailedError",
30
31
  "CoreException",
31
32
  "CoreHTTPException",
32
33
  "RetryableCoreHTTPException",
@@ -112,6 +113,24 @@ class ExponentialBackoffTimeoutException(InternalCoreException):
112
113
  self.attempts = attempts
113
114
 
114
115
 
116
+ class CommandFailedError(InternalCoreException):
117
+ """
118
+ An external command exited non-zero, timed out, or could not be started.
119
+
120
+ ``command`` is the argv that was run; ``detail`` (also carried as the
121
+ shared ``error`` field) is stderr text or the OS-level reason.
122
+ """
123
+
124
+ def __init__(self, command: list[str], detail: str) -> None:
125
+ super().__init__(
126
+ message=f"Command failed: {' '.join(command)}",
127
+ retryable=Retryability.NON_RETRYABLE,
128
+ error=detail,
129
+ )
130
+ self.command = command
131
+ self.detail = detail
132
+
133
+
115
134
  class CoreHTTPException(HTTPException, PublicCoreException):
116
135
  """
117
136
  FastAPI HTTPException that is also a PublicCoreException.
@@ -9,15 +9,18 @@ File reading and writing, with format handled by the manager.
9
9
  """
10
10
 
11
11
  from corekit.files.base import FileContent, FileError, FileManager
12
+ from corekit.files.disk import DiskScanner, PathSize
12
13
  from corekit.files.enum import FileMode
13
14
  from corekit.files.json import JsonFileManager
14
15
  from corekit.files.toml import TomlFileManager
15
16
 
16
17
  __all__ = [
18
+ "DiskScanner",
17
19
  "FileContent",
18
20
  "FileError",
19
21
  "FileManager",
20
22
  "FileMode",
21
23
  "JsonFileManager",
24
+ "PathSize",
22
25
  "TomlFileManager",
23
26
  ]