truss 0.10.0rc1__py3-none-any.whl → 0.60.0__py3-none-any.whl

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.

Potentially problematic release.


This version of truss might be problematic. Click here for more details.

Files changed (362) hide show
  1. truss/__init__.py +10 -3
  2. truss/api/__init__.py +123 -0
  3. truss/api/definitions.py +51 -0
  4. truss/base/constants.py +116 -0
  5. truss/base/custom_types.py +29 -0
  6. truss/{errors.py → base/errors.py} +4 -0
  7. truss/base/trt_llm_config.py +310 -0
  8. truss/{truss_config.py → base/truss_config.py} +344 -31
  9. truss/{truss_spec.py → base/truss_spec.py} +20 -6
  10. truss/{validation.py → base/validation.py} +60 -11
  11. truss/cli/cli.py +841 -88
  12. truss/{remote → cli}/remote_cli.py +2 -7
  13. truss/contexts/docker_build_setup.py +67 -0
  14. truss/contexts/image_builder/cache_warmer.py +2 -8
  15. truss/contexts/image_builder/image_builder.py +1 -1
  16. truss/contexts/image_builder/serving_image_builder.py +292 -46
  17. truss/contexts/image_builder/util.py +1 -3
  18. truss/contexts/local_loader/docker_build_emulator.py +58 -0
  19. truss/contexts/local_loader/load_model_local.py +2 -2
  20. truss/contexts/local_loader/truss_module_loader.py +1 -1
  21. truss/contexts/local_loader/utils.py +1 -1
  22. truss/local/local_config.py +2 -6
  23. truss/local/local_config_handler.py +20 -5
  24. truss/patch/__init__.py +1 -0
  25. truss/patch/hash.py +4 -70
  26. truss/patch/signature.py +4 -16
  27. truss/patch/truss_dir_patch_applier.py +3 -78
  28. truss/remote/baseten/api.py +308 -23
  29. truss/remote/baseten/auth.py +3 -3
  30. truss/remote/baseten/core.py +257 -50
  31. truss/remote/baseten/custom_types.py +44 -0
  32. truss/remote/baseten/error.py +4 -0
  33. truss/remote/baseten/remote.py +369 -118
  34. truss/remote/baseten/service.py +118 -11
  35. truss/remote/baseten/utils/status.py +29 -0
  36. truss/remote/baseten/utils/tar.py +34 -22
  37. truss/remote/baseten/utils/transfer.py +36 -23
  38. truss/remote/remote_factory.py +14 -5
  39. truss/remote/truss_remote.py +72 -45
  40. truss/templates/base.Dockerfile.jinja +18 -16
  41. truss/templates/cache.Dockerfile.jinja +3 -3
  42. truss/{server → templates/control}/control/application.py +14 -35
  43. truss/{server → templates/control}/control/endpoints.py +39 -9
  44. truss/{server/control/patch/types.py → templates/control/control/helpers/custom_types.py} +13 -52
  45. truss/{server → templates/control}/control/helpers/inference_server_controller.py +4 -8
  46. truss/{server → templates/control}/control/helpers/inference_server_process_controller.py +2 -4
  47. truss/{server → templates/control}/control/helpers/inference_server_starter.py +5 -10
  48. truss/{server/control → templates/control/control/helpers}/truss_patch/model_code_patch_applier.py +8 -6
  49. truss/{server/control/patch → templates/control/control/helpers/truss_patch}/model_container_patch_applier.py +18 -26
  50. truss/templates/control/control/helpers/truss_patch/requirement_name_identifier.py +66 -0
  51. truss/{server → templates/control}/control/server.py +11 -6
  52. truss/templates/control/requirements.txt +9 -0
  53. truss/templates/custom_python_dx/my_model.py +28 -0
  54. truss/templates/docker_server/proxy.conf.jinja +42 -0
  55. truss/templates/docker_server/supervisord.conf.jinja +27 -0
  56. truss/templates/docker_server_requirements.txt +1 -0
  57. truss/templates/server/common/errors.py +231 -0
  58. truss/{server → templates/server}/common/patches/whisper/patch.py +1 -0
  59. truss/{server/common/patches/__init__.py → templates/server/common/patches.py} +1 -3
  60. truss/{server → templates/server}/common/retry.py +1 -0
  61. truss/{server → templates/server}/common/schema.py +11 -9
  62. truss/templates/server/common/tracing.py +157 -0
  63. truss/templates/server/main.py +9 -0
  64. truss/templates/server/model_wrapper.py +961 -0
  65. truss/templates/server/requirements.txt +21 -0
  66. truss/templates/server/truss_server.py +447 -0
  67. truss/templates/server.Dockerfile.jinja +62 -14
  68. truss/templates/shared/dynamic_config_resolver.py +28 -0
  69. truss/templates/shared/lazy_data_resolver.py +164 -0
  70. truss/templates/shared/log_config.py +125 -0
  71. truss/{server → templates}/shared/secrets_resolver.py +1 -2
  72. truss/{server → templates}/shared/serialization.py +31 -9
  73. truss/{server → templates}/shared/util.py +3 -13
  74. truss/templates/trtllm-audio/model/model.py +49 -0
  75. truss/templates/trtllm-audio/packages/sigint_patch.py +14 -0
  76. truss/templates/trtllm-audio/packages/whisper_trt/__init__.py +215 -0
  77. truss/templates/trtllm-audio/packages/whisper_trt/assets.py +25 -0
  78. truss/templates/trtllm-audio/packages/whisper_trt/batching.py +52 -0
  79. truss/templates/trtllm-audio/packages/whisper_trt/custom_types.py +26 -0
  80. truss/templates/trtllm-audio/packages/whisper_trt/modeling.py +184 -0
  81. truss/templates/trtllm-audio/packages/whisper_trt/tokenizer.py +185 -0
  82. truss/templates/trtllm-audio/packages/whisper_trt/utils.py +245 -0
  83. truss/templates/trtllm-briton/src/extension.py +64 -0
  84. truss/tests/conftest.py +302 -94
  85. truss/tests/contexts/image_builder/test_serving_image_builder.py +74 -31
  86. truss/tests/contexts/local_loader/test_load_local.py +2 -2
  87. truss/tests/contexts/local_loader/test_truss_module_finder.py +1 -1
  88. truss/tests/patch/test_calc_patch.py +439 -127
  89. truss/tests/patch/test_dir_signature.py +3 -12
  90. truss/tests/patch/test_hash.py +1 -1
  91. truss/tests/patch/test_signature.py +1 -1
  92. truss/tests/patch/test_truss_dir_patch_applier.py +23 -11
  93. truss/tests/patch/test_types.py +2 -2
  94. truss/tests/remote/baseten/test_api.py +153 -58
  95. truss/tests/remote/baseten/test_auth.py +2 -1
  96. truss/tests/remote/baseten/test_core.py +160 -12
  97. truss/tests/remote/baseten/test_remote.py +489 -77
  98. truss/tests/remote/baseten/test_service.py +55 -0
  99. truss/tests/remote/test_remote_factory.py +16 -18
  100. truss/tests/remote/test_truss_remote.py +26 -17
  101. truss/tests/templates/control/control/helpers/test_context_managers.py +11 -0
  102. truss/tests/templates/control/control/helpers/test_model_container_patch_applier.py +184 -0
  103. truss/tests/templates/control/control/helpers/test_requirement_name_identifier.py +89 -0
  104. truss/tests/{server → templates/control}/control/test_server.py +79 -24
  105. truss/tests/{server → templates/control}/control/test_server_integration.py +24 -16
  106. truss/tests/templates/core/server/test_dynamic_config_resolver.py +108 -0
  107. truss/tests/templates/core/server/test_lazy_data_resolver.py +329 -0
  108. truss/tests/templates/core/server/test_lazy_data_resolver_v2.py +79 -0
  109. truss/tests/{server → templates}/core/server/test_secrets_resolver.py +1 -1
  110. truss/tests/{server → templates/server}/common/test_retry.py +3 -3
  111. truss/tests/templates/server/test_model_wrapper.py +248 -0
  112. truss/tests/{server → templates/server}/test_schema.py +3 -5
  113. truss/tests/{server/core/server/common → templates/server}/test_truss_server.py +8 -5
  114. truss/tests/test_build.py +9 -52
  115. truss/tests/test_config.py +336 -77
  116. truss/tests/test_context_builder_image.py +3 -11
  117. truss/tests/test_control_truss_patching.py +7 -12
  118. truss/tests/test_custom_server.py +38 -0
  119. truss/tests/test_data/context_builder_image_test/test.py +3 -0
  120. truss/tests/test_data/happy.ipynb +56 -0
  121. truss/tests/test_data/model_load_failure_test/config.yaml +2 -0
  122. truss/tests/test_data/model_load_failure_test/model/__init__.py +0 -0
  123. truss/tests/test_data/patch_ping_test_server/__init__.py +0 -0
  124. truss/{test_data → tests/test_data}/patch_ping_test_server/app.py +3 -9
  125. truss/{test_data → tests/test_data}/server.Dockerfile +20 -21
  126. truss/tests/test_data/server_conformance_test_truss/__init__.py +0 -0
  127. truss/tests/test_data/server_conformance_test_truss/model/__init__.py +0 -0
  128. truss/{test_data → tests/test_data}/server_conformance_test_truss/model/model.py +1 -3
  129. truss/tests/test_data/test_async_truss/__init__.py +0 -0
  130. truss/tests/test_data/test_async_truss/model/__init__.py +0 -0
  131. truss/tests/test_data/test_basic_truss/__init__.py +0 -0
  132. truss/tests/test_data/test_basic_truss/config.yaml +16 -0
  133. truss/tests/test_data/test_basic_truss/model/__init__.py +0 -0
  134. truss/tests/test_data/test_build_commands/__init__.py +0 -0
  135. truss/tests/test_data/test_build_commands/config.yaml +13 -0
  136. truss/tests/test_data/test_build_commands/model/__init__.py +0 -0
  137. truss/{test_data/test_streaming_async_generator_truss → tests/test_data/test_build_commands}/model/model.py +2 -3
  138. truss/tests/test_data/test_build_commands_failure/__init__.py +0 -0
  139. truss/tests/test_data/test_build_commands_failure/config.yaml +14 -0
  140. truss/tests/test_data/test_build_commands_failure/model/__init__.py +0 -0
  141. truss/tests/test_data/test_build_commands_failure/model/model.py +17 -0
  142. truss/tests/test_data/test_concurrency_truss/__init__.py +0 -0
  143. truss/tests/test_data/test_concurrency_truss/config.yaml +4 -0
  144. truss/tests/test_data/test_concurrency_truss/model/__init__.py +0 -0
  145. truss/tests/test_data/test_custom_server_truss/__init__.py +0 -0
  146. truss/tests/test_data/test_custom_server_truss/config.yaml +20 -0
  147. truss/tests/test_data/test_custom_server_truss/test_docker_image/Dockerfile +17 -0
  148. truss/tests/test_data/test_custom_server_truss/test_docker_image/README.md +10 -0
  149. truss/tests/test_data/test_custom_server_truss/test_docker_image/VERSION +1 -0
  150. truss/tests/test_data/test_custom_server_truss/test_docker_image/__init__.py +0 -0
  151. truss/tests/test_data/test_custom_server_truss/test_docker_image/app.py +19 -0
  152. truss/tests/test_data/test_custom_server_truss/test_docker_image/build_upload_new_image.sh +6 -0
  153. truss/tests/test_data/test_openai/__init__.py +0 -0
  154. truss/{test_data/test_basic_truss → tests/test_data/test_openai}/config.yaml +1 -2
  155. truss/tests/test_data/test_openai/model/__init__.py +0 -0
  156. truss/tests/test_data/test_openai/model/model.py +15 -0
  157. truss/tests/test_data/test_pyantic_v1/__init__.py +0 -0
  158. truss/tests/test_data/test_pyantic_v1/model/__init__.py +0 -0
  159. truss/tests/test_data/test_pyantic_v1/model/model.py +28 -0
  160. truss/tests/test_data/test_pyantic_v1/requirements.txt +1 -0
  161. truss/tests/test_data/test_pyantic_v2/__init__.py +0 -0
  162. truss/tests/test_data/test_pyantic_v2/config.yaml +13 -0
  163. truss/tests/test_data/test_pyantic_v2/model/__init__.py +0 -0
  164. truss/tests/test_data/test_pyantic_v2/model/model.py +30 -0
  165. truss/tests/test_data/test_pyantic_v2/requirements.txt +1 -0
  166. truss/tests/test_data/test_requirements_file_truss/__init__.py +0 -0
  167. truss/tests/test_data/test_requirements_file_truss/config.yaml +13 -0
  168. truss/tests/test_data/test_requirements_file_truss/model/__init__.py +0 -0
  169. truss/{test_data → tests/test_data}/test_requirements_file_truss/model/model.py +1 -0
  170. truss/tests/test_data/test_streaming_async_generator_truss/__init__.py +0 -0
  171. truss/tests/test_data/test_streaming_async_generator_truss/config.yaml +4 -0
  172. truss/tests/test_data/test_streaming_async_generator_truss/model/__init__.py +0 -0
  173. truss/tests/test_data/test_streaming_async_generator_truss/model/model.py +7 -0
  174. truss/tests/test_data/test_streaming_read_timeout/__init__.py +0 -0
  175. truss/tests/test_data/test_streaming_read_timeout/model/__init__.py +0 -0
  176. truss/tests/test_data/test_streaming_truss/__init__.py +0 -0
  177. truss/tests/test_data/test_streaming_truss/config.yaml +4 -0
  178. truss/tests/test_data/test_streaming_truss/model/__init__.py +0 -0
  179. truss/tests/test_data/test_streaming_truss_with_error/__init__.py +0 -0
  180. truss/tests/test_data/test_streaming_truss_with_error/model/__init__.py +0 -0
  181. truss/{test_data → tests/test_data}/test_streaming_truss_with_error/model/model.py +3 -11
  182. truss/tests/test_data/test_streaming_truss_with_error/packages/__init__.py +0 -0
  183. truss/tests/test_data/test_streaming_truss_with_error/packages/helpers_1.py +5 -0
  184. truss/tests/test_data/test_streaming_truss_with_error/packages/helpers_2.py +2 -0
  185. truss/tests/test_data/test_streaming_truss_with_tracing/__init__.py +0 -0
  186. truss/tests/test_data/test_streaming_truss_with_tracing/config.yaml +43 -0
  187. truss/tests/test_data/test_streaming_truss_with_tracing/model/__init__.py +0 -0
  188. truss/tests/test_data/test_streaming_truss_with_tracing/model/model.py +65 -0
  189. truss/tests/test_data/test_trt_llm_truss/__init__.py +0 -0
  190. truss/tests/test_data/test_trt_llm_truss/config.yaml +15 -0
  191. truss/tests/test_data/test_trt_llm_truss/model/__init__.py +0 -0
  192. truss/tests/test_data/test_trt_llm_truss/model/model.py +15 -0
  193. truss/tests/test_data/test_truss/__init__.py +0 -0
  194. truss/tests/test_data/test_truss/config.yaml +4 -0
  195. truss/tests/test_data/test_truss/model/__init__.py +0 -0
  196. truss/tests/test_data/test_truss/model/dummy +0 -0
  197. truss/tests/test_data/test_truss/packages/__init__.py +0 -0
  198. truss/tests/test_data/test_truss/packages/test_package/__init__.py +0 -0
  199. truss/tests/test_data/test_truss_server_caching_truss/__init__.py +0 -0
  200. truss/tests/test_data/test_truss_server_caching_truss/model/__init__.py +0 -0
  201. truss/tests/test_data/test_truss_with_error/__init__.py +0 -0
  202. truss/tests/test_data/test_truss_with_error/config.yaml +4 -0
  203. truss/tests/test_data/test_truss_with_error/model/__init__.py +0 -0
  204. truss/tests/test_data/test_truss_with_error/model/model.py +8 -0
  205. truss/tests/test_data/test_truss_with_error/packages/__init__.py +0 -0
  206. truss/tests/test_data/test_truss_with_error/packages/helpers_1.py +5 -0
  207. truss/tests/test_data/test_truss_with_error/packages/helpers_2.py +2 -0
  208. truss/tests/test_docker.py +2 -1
  209. truss/tests/test_model_inference.py +1340 -292
  210. truss/tests/test_model_schema.py +33 -26
  211. truss/tests/test_testing_utilities_for_other_tests.py +50 -5
  212. truss/tests/test_truss_gatherer.py +3 -5
  213. truss/tests/test_truss_handle.py +62 -59
  214. truss/tests/test_util.py +2 -1
  215. truss/tests/test_validation.py +15 -13
  216. truss/tests/trt_llm/test_trt_llm_config.py +41 -0
  217. truss/tests/trt_llm/test_validation.py +91 -0
  218. truss/tests/util/test_config_checks.py +40 -0
  219. truss/tests/util/test_env_vars.py +14 -0
  220. truss/tests/util/test_path.py +10 -23
  221. truss/trt_llm/config_checks.py +43 -0
  222. truss/trt_llm/validation.py +42 -0
  223. truss/truss_handle/__init__.py +0 -0
  224. truss/truss_handle/build.py +122 -0
  225. truss/{decorators.py → truss_handle/decorators.py} +1 -1
  226. truss/truss_handle/patch/__init__.py +0 -0
  227. truss/{patch → truss_handle/patch}/calc_patch.py +146 -92
  228. truss/{types.py → truss_handle/patch/custom_types.py} +35 -27
  229. truss/{patch → truss_handle/patch}/dir_signature.py +1 -1
  230. truss/truss_handle/patch/hash.py +71 -0
  231. truss/{patch → truss_handle/patch}/local_truss_patch_applier.py +6 -4
  232. truss/truss_handle/patch/signature.py +22 -0
  233. truss/truss_handle/patch/truss_dir_patch_applier.py +87 -0
  234. truss/{readme_generator.py → truss_handle/readme_generator.py} +3 -2
  235. truss/{truss_gatherer.py → truss_handle/truss_gatherer.py} +3 -2
  236. truss/{truss_handle.py → truss_handle/truss_handle.py} +174 -78
  237. truss/util/.truss_ignore +3 -0
  238. truss/{docker.py → util/docker.py} +6 -2
  239. truss/util/download.py +6 -15
  240. truss/util/env_vars.py +41 -0
  241. truss/util/log_utils.py +52 -0
  242. truss/util/path.py +20 -20
  243. truss/util/requirements.py +11 -0
  244. {truss-0.10.0rc1.dist-info → truss-0.60.0.dist-info}/METADATA +18 -16
  245. truss-0.60.0.dist-info/RECORD +324 -0
  246. {truss-0.10.0rc1.dist-info → truss-0.60.0.dist-info}/WHEEL +1 -1
  247. truss-0.60.0.dist-info/entry_points.txt +4 -0
  248. truss_chains/__init__.py +71 -0
  249. truss_chains/definitions.py +756 -0
  250. truss_chains/deployment/__init__.py +0 -0
  251. truss_chains/deployment/code_gen.py +816 -0
  252. truss_chains/deployment/deployment_client.py +871 -0
  253. truss_chains/framework.py +1480 -0
  254. truss_chains/public_api.py +231 -0
  255. truss_chains/py.typed +0 -0
  256. truss_chains/pydantic_numpy.py +131 -0
  257. truss_chains/reference_code/reference_chainlet.py +34 -0
  258. truss_chains/reference_code/reference_model.py +10 -0
  259. truss_chains/remote_chainlet/__init__.py +0 -0
  260. truss_chains/remote_chainlet/model_skeleton.py +60 -0
  261. truss_chains/remote_chainlet/stub.py +380 -0
  262. truss_chains/remote_chainlet/utils.py +332 -0
  263. truss_chains/streaming.py +378 -0
  264. truss_chains/utils.py +178 -0
  265. CODE_OF_CONDUCT.md +0 -131
  266. CONTRIBUTING.md +0 -48
  267. README.md +0 -137
  268. context_builder.Dockerfile +0 -24
  269. truss/blob/blob_backend.py +0 -10
  270. truss/blob/blob_backend_registry.py +0 -23
  271. truss/blob/http_public_blob_backend.py +0 -23
  272. truss/build/__init__.py +0 -2
  273. truss/build/build.py +0 -143
  274. truss/build/configure.py +0 -63
  275. truss/cli/__init__.py +0 -2
  276. truss/cli/console.py +0 -5
  277. truss/cli/create.py +0 -5
  278. truss/config/trt_llm.py +0 -81
  279. truss/constants.py +0 -61
  280. truss/model_inference.py +0 -123
  281. truss/patch/types.py +0 -30
  282. truss/pytest.ini +0 -7
  283. truss/server/common/errors.py +0 -100
  284. truss/server/common/termination_handler_middleware.py +0 -64
  285. truss/server/common/truss_server.py +0 -389
  286. truss/server/control/patch/model_code_patch_applier.py +0 -46
  287. truss/server/control/patch/requirement_name_identifier.py +0 -17
  288. truss/server/inference_server.py +0 -29
  289. truss/server/model_wrapper.py +0 -434
  290. truss/server/shared/logging.py +0 -81
  291. truss/templates/trtllm/model/model.py +0 -97
  292. truss/templates/trtllm/packages/build_engine_utils.py +0 -34
  293. truss/templates/trtllm/packages/constants.py +0 -11
  294. truss/templates/trtllm/packages/schema.py +0 -216
  295. truss/templates/trtllm/packages/tensorrt_llm_model_repository/ensemble/config.pbtxt +0 -246
  296. truss/templates/trtllm/packages/tensorrt_llm_model_repository/postprocessing/1/model.py +0 -181
  297. truss/templates/trtllm/packages/tensorrt_llm_model_repository/postprocessing/config.pbtxt +0 -64
  298. truss/templates/trtllm/packages/tensorrt_llm_model_repository/preprocessing/1/model.py +0 -260
  299. truss/templates/trtllm/packages/tensorrt_llm_model_repository/preprocessing/config.pbtxt +0 -99
  300. truss/templates/trtllm/packages/tensorrt_llm_model_repository/tensorrt_llm/config.pbtxt +0 -208
  301. truss/templates/trtllm/packages/triton_client.py +0 -150
  302. truss/templates/trtllm/packages/utils.py +0 -43
  303. truss/test_data/context_builder_image_test/test.py +0 -4
  304. truss/test_data/happy.ipynb +0 -54
  305. truss/test_data/model_load_failure_test/config.yaml +0 -2
  306. truss/test_data/test_concurrency_truss/config.yaml +0 -2
  307. truss/test_data/test_streaming_async_generator_truss/config.yaml +0 -2
  308. truss/test_data/test_streaming_truss/config.yaml +0 -3
  309. truss/test_data/test_truss/config.yaml +0 -2
  310. truss/tests/server/common/test_termination_handler_middleware.py +0 -93
  311. truss/tests/server/control/test_model_container_patch_applier.py +0 -203
  312. truss/tests/server/core/server/common/test_util.py +0 -19
  313. truss/tests/server/test_model_wrapper.py +0 -87
  314. truss/util/data_structures.py +0 -16
  315. truss-0.10.0rc1.dist-info/RECORD +0 -216
  316. truss-0.10.0rc1.dist-info/entry_points.txt +0 -3
  317. truss/{server/shared → base}/__init__.py +0 -0
  318. truss/{server → templates/control}/control/helpers/context_managers.py +0 -0
  319. truss/{server/control → templates/control/control/helpers}/errors.py +0 -0
  320. truss/{server/control/patch → templates/control/control/helpers/truss_patch}/__init__.py +0 -0
  321. truss/{server/control/patch → templates/control/control/helpers/truss_patch}/system_packages.py +0 -0
  322. truss/{test_data/annotated_types_truss/model → templates/server}/__init__.py +0 -0
  323. truss/{server → templates/server}/common/__init__.py +0 -0
  324. truss/{test_data/gcs_fix/model → templates/shared}/__init__.py +0 -0
  325. truss/templates/{trtllm → trtllm-briton}/README.md +0 -0
  326. truss/{test_data/server_conformance_test_truss/model → tests/test_data}/__init__.py +0 -0
  327. truss/{test_data/test_basic_truss/model → tests/test_data/annotated_types_truss}/__init__.py +0 -0
  328. truss/{test_data → tests/test_data}/annotated_types_truss/config.yaml +0 -0
  329. truss/{test_data/test_requirements_file_truss → tests/test_data/annotated_types_truss}/model/__init__.py +0 -0
  330. truss/{test_data → tests/test_data}/annotated_types_truss/model/model.py +0 -0
  331. truss/{test_data → tests/test_data}/auto-mpg.data +0 -0
  332. truss/{test_data → tests/test_data}/context_builder_image_test/Dockerfile +0 -0
  333. truss/{test_data/test_truss/model → tests/test_data/context_builder_image_test}/__init__.py +0 -0
  334. truss/{test_data/test_truss_server_caching_truss/model → tests/test_data/gcs_fix}/__init__.py +0 -0
  335. truss/{test_data → tests/test_data}/gcs_fix/config.yaml +0 -0
  336. truss/tests/{local → test_data/gcs_fix/model}/__init__.py +0 -0
  337. truss/{test_data → tests/test_data}/gcs_fix/model/model.py +0 -0
  338. truss/{test_data/test_truss/model/dummy → tests/test_data/model_load_failure_test/__init__.py} +0 -0
  339. truss/{test_data → tests/test_data}/model_load_failure_test/model/model.py +0 -0
  340. truss/{test_data → tests/test_data}/pima-indians-diabetes.csv +0 -0
  341. truss/{test_data → tests/test_data}/readme_int_example.md +0 -0
  342. truss/{test_data → tests/test_data}/readme_no_example.md +0 -0
  343. truss/{test_data → tests/test_data}/readme_str_example.md +0 -0
  344. truss/{test_data → tests/test_data}/server_conformance_test_truss/config.yaml +0 -0
  345. truss/{test_data → tests/test_data}/test_async_truss/config.yaml +0 -0
  346. truss/{test_data → tests/test_data}/test_async_truss/model/model.py +3 -3
  347. /truss/{test_data → tests/test_data}/test_basic_truss/model/model.py +0 -0
  348. /truss/{test_data → tests/test_data}/test_concurrency_truss/model/model.py +0 -0
  349. /truss/{test_data/test_requirements_file_truss → tests/test_data/test_pyantic_v1}/config.yaml +0 -0
  350. /truss/{test_data → tests/test_data}/test_requirements_file_truss/requirements.txt +0 -0
  351. /truss/{test_data → tests/test_data}/test_streaming_read_timeout/config.yaml +0 -0
  352. /truss/{test_data → tests/test_data}/test_streaming_read_timeout/model/model.py +0 -0
  353. /truss/{test_data → tests/test_data}/test_streaming_truss/model/model.py +0 -0
  354. /truss/{test_data → tests/test_data}/test_streaming_truss_with_error/config.yaml +0 -0
  355. /truss/{test_data → tests/test_data}/test_truss/examples.yaml +0 -0
  356. /truss/{test_data → tests/test_data}/test_truss/model/model.py +0 -0
  357. /truss/{test_data → tests/test_data}/test_truss/packages/test_package/test.py +0 -0
  358. /truss/{test_data → tests/test_data}/test_truss_server_caching_truss/config.yaml +0 -0
  359. /truss/{test_data → tests/test_data}/test_truss_server_caching_truss/model/model.py +0 -0
  360. /truss/{patch → truss_handle/patch}/constants.py +0 -0
  361. /truss/{notebook.py → util/notebook.py} +0 -0
  362. {truss-0.10.0rc1.dist-info → truss-0.60.0.dist-info}/LICENSE +0 -0
@@ -1,18 +1,38 @@
1
1
  import os
2
+ import sys
2
3
  from contextlib import contextmanager
4
+ from pathlib import Path
3
5
  from typing import Dict, List
6
+ from unittest.mock import AsyncMock, patch
4
7
 
8
+ import httpx
5
9
  import pytest
6
- from httpx import AsyncClient
7
- from truss.server.control.application import create_app
8
- from truss.server.control.patch.types import (
10
+ from tenacity import RetryError
11
+ from truss.truss_handle.patch.custom_types import PatchRequest
12
+
13
+ # Needed to simulate the set up on the model docker container
14
+ sys.path.append(
15
+ str(
16
+ Path(__file__).parent.parent.parent.parent.parent
17
+ / "templates"
18
+ / "control"
19
+ / "control"
20
+ )
21
+ )
22
+
23
+ sys.path.append(str(Path(__file__).parent.parent.parent.parent.parent / "templates"))
24
+ sys.path.append(
25
+ str(Path(__file__).parent.parent.parent.parent.parent / "templates" / "shared")
26
+ )
27
+
28
+ from truss.templates.control.control.application import create_app # noqa
29
+ from truss.templates.control.control.helpers.custom_types import ( # noqa
9
30
  Action,
10
31
  ModelCodePatch,
11
32
  Patch,
12
33
  PatchType,
13
34
  PythonRequirementPatch,
14
35
  )
15
- from truss.types import PatchRequest
16
36
 
17
37
 
18
38
  @pytest.fixture
@@ -21,17 +41,13 @@ def truss_original_hash():
21
41
 
22
42
 
23
43
  @pytest.fixture
24
- def app(tmp_truss_dir, truss_original_hash):
44
+ def app(truss_container_fs, truss_original_hash):
25
45
  with _env_var({"HASH_TRUSS": truss_original_hash}):
26
- inf_serv_home = tmp_truss_dir
46
+ inf_serv_home = truss_container_fs / "app"
27
47
  control_app = create_app(
28
48
  {
29
49
  "inference_server_home": inf_serv_home,
30
- "inference_server_process_args": [
31
- "python",
32
- "-m",
33
- "truss.server.inference_server",
34
- ],
50
+ "inference_server_process_args": ["python", "main.py"],
35
51
  "control_server_host": "*",
36
52
  "control_server_port": 8081,
37
53
  "inference_server_port": 8082,
@@ -59,7 +75,10 @@ def anyio_backend(request):
59
75
 
60
76
  @pytest.fixture()
61
77
  async def client(app):
62
- async with AsyncClient(app=app, base_url="http://localhost:8080") as async_client:
78
+ transport = httpx.ASGITransport(app=app)
79
+ async with httpx.AsyncClient(
80
+ transport=transport, base_url="http://localhost:8080"
81
+ ) as async_client:
63
82
  yield async_client
64
83
 
65
84
 
@@ -87,9 +106,7 @@ class Model:
87
106
  patch = Patch(
88
107
  type=PatchType.MODEL_CODE,
89
108
  body=ModelCodePatch(
90
- action=Action.UPDATE,
91
- path="model.py",
92
- content=mock_model_file_content,
109
+ action=Action.UPDATE, path="model.py", content=mock_model_file_content
93
110
  ),
94
111
  )
95
112
  await _verify_apply_patch_success(client, patch)
@@ -112,9 +129,7 @@ class Model:
112
129
  patch = Patch(
113
130
  type=PatchType.MODEL_CODE,
114
131
  body=ModelCodePatch(
115
- action=Action.UPDATE,
116
- path="model.py",
117
- content=mock_model_file_content,
132
+ action=Action.UPDATE, path="model.py", content=mock_model_file_content
118
133
  ),
119
134
  )
120
135
  await _verify_apply_patch_success(client, patch)
@@ -129,9 +144,7 @@ async def test_patch_model_code_create_new(app, client):
129
144
  patch = Patch(
130
145
  type=PatchType.MODEL_CODE,
131
146
  body=ModelCodePatch(
132
- action=Action.UPDATE,
133
- path="touched",
134
- content=empty_content,
147
+ action=Action.UPDATE, path="touched", content=empty_content
135
148
  ),
136
149
  )
137
150
  await _verify_apply_patch_success(client, patch)
@@ -144,9 +157,7 @@ async def test_patch_model_code_create_in_new_dir(app, client):
144
157
  patch = Patch(
145
158
  type=PatchType.MODEL_CODE,
146
159
  body=ModelCodePatch(
147
- action=Action.UPDATE,
148
- path="new_directory/touched",
149
- content=empty_content,
160
+ action=Action.UPDATE, path="new_directory/touched", content=empty_content
150
161
  ),
151
162
  )
152
163
  await _verify_apply_patch_success(client, patch)
@@ -203,6 +214,50 @@ async def test_patch_failed_unrecoverable(client):
203
214
  assert resp.json()["error"]["type"] == "patch_failed_unrecoverable"
204
215
 
205
216
 
217
+ @pytest.mark.anyio
218
+ async def test_health_check(client):
219
+ resp = await client.get("/v1/models/model")
220
+ assert resp.status_code == 200
221
+ assert resp.json() == {}
222
+
223
+
224
+ @pytest.mark.anyio
225
+ async def test_health_check_retries(client, app):
226
+ async def mock_send(*args, **kwargs):
227
+ return httpx.Response(
228
+ status_code=503, json={"error": "Model with name model is not ready."}
229
+ )
230
+
231
+ app.state.proxy_client.send = AsyncMock(side_effect=mock_send)
232
+
233
+ with pytest.raises(RetryError):
234
+ await client.get("/v1/models/model")
235
+
236
+ # Health check was retried 10 times
237
+ assert app.state.proxy_client.send.call_count == 10
238
+
239
+
240
+ @pytest.mark.anyio
241
+ async def test_retries(client, app):
242
+ app.state.proxy_client.send = AsyncMock(
243
+ side_effect=[
244
+ httpx.ConnectTimeout("Connect timeout"),
245
+ httpx.ReadTimeout("Read timeout"),
246
+ httpx.ReadError("Read error"),
247
+ httpx.ConnectError("Connect error"),
248
+ httpx.RemoteProtocolError("Remote protocol error"),
249
+ ]
250
+ )
251
+
252
+ with patch("endpoints.INFERENCE_SERVER_START_WAIT_SECS", new=4), pytest.raises(
253
+ RetryError
254
+ ):
255
+ await client.get("/v1/models/model")
256
+
257
+ # We should have made 5 attempts
258
+ assert app.state.proxy_client.send.call_count == 5
259
+
260
+
206
261
  async def _verify_apply_patch_success(client, patch: Patch):
207
262
  resp = await client.get("/control/truss_hash")
208
263
  original_hash = resp.json()["result"]
@@ -15,7 +15,6 @@ from typing import Callable
15
15
  import psutil
16
16
  import pytest
17
17
  import requests
18
- from truss.server.control.server import ControlServer
19
18
 
20
19
  PATCH_PING_MAX_DELAY_SECS = 3
21
20
 
@@ -28,8 +27,8 @@ class ControlServerDetails:
28
27
 
29
28
 
30
29
  @pytest.fixture
31
- def control_server(tmp_truss_control_dir):
32
- with _configured_control_server(tmp_truss_control_dir) as server:
30
+ def control_server(truss_control_container_fs):
31
+ with _configured_control_server(truss_control_container_fs) as server:
33
32
  yield server
34
33
 
35
34
 
@@ -63,10 +62,7 @@ class Model:
63
62
 
64
63
  def predict(inp):
65
64
  time.sleep(random.uniform(0, 0.5))
66
- resp = requests.post(
67
- f"{ctrl_url}/v1/models/model:predict",
68
- json=inp,
69
- )
65
+ resp = requests.post(f"{ctrl_url}/v1/models/model:predict", json=inp)
70
66
  return resp.json()
71
67
 
72
68
  with ThreadPool(10) as p:
@@ -96,11 +92,18 @@ class Model:
96
92
 
97
93
 
98
94
  @pytest.mark.integration
99
- def test_truss_control_server_patch_ping_delays(tmp_truss_control_dir: Path):
95
+ def test_truss_control_server_health_check(control_server: ControlServerDetails):
96
+ ctrl_url = f"http://localhost:{control_server.control_server_port}"
97
+ resp = requests.get(f"{ctrl_url}/v1/models/model")
98
+ assert resp.status_code == 200
99
+ assert resp.json() == {}
100
+
101
+
102
+ @pytest.mark.integration
103
+ def test_truss_control_server_patch_ping_delays(truss_control_container_fs: Path):
100
104
  for _ in range(10):
101
105
  with _configured_control_server(
102
- tmp_truss_control_dir,
103
- with_patch_ping_flow=True,
106
+ truss_control_container_fs, with_patch_ping_flow=True
104
107
  ) as control_server:
105
108
  # Account for patch ping delays
106
109
  time.sleep(PATCH_PING_MAX_DELAY_SECS)
@@ -172,8 +175,7 @@ def _process_tree_is_dead(pid: int):
172
175
 
173
176
  @contextmanager
174
177
  def _configured_control_server(
175
- truss_control_container_fs: Path,
176
- with_patch_ping_flow: bool = False,
178
+ truss_control_container_fs: Path, with_patch_ping_flow: bool = False
177
179
  ):
178
180
  # Pick random ports to reduce reuse, port release may take time
179
181
  # which can interfere with tests
@@ -183,14 +185,20 @@ def _configured_control_server(
183
185
 
184
186
  def start_truss_server(stdout_capture_file_path):
185
187
  if with_patch_ping_flow:
186
- os.environ[
187
- "PATCH_PING_URL_TRUSS"
188
- ] = f"http://localhost:{patch_ping_server_port}"
188
+ os.environ["PATCH_PING_URL_TRUSS"] = (
189
+ f"http://localhost:{patch_ping_server_port}"
190
+ )
189
191
  sys.stdout = open(stdout_capture_file_path, "w")
192
+ app_path = truss_control_container_fs / "app"
193
+ sys.path.append(str(app_path))
194
+ control_path = truss_control_container_fs / "control" / "control"
195
+ sys.path.append(str(control_path))
196
+
197
+ from server import ControlServer
190
198
 
191
199
  control_server = ControlServer(
192
200
  python_executable_path=sys.executable,
193
- inf_serv_home=str(truss_control_container_fs),
201
+ inf_serv_home=str(app_path),
194
202
  control_server_port=ctrl_port,
195
203
  inference_server_port=inf_port,
196
204
  )
@@ -0,0 +1,108 @@
1
+ import json
2
+
3
+ import aiofiles
4
+ import pytest
5
+ from truss.templates.shared import dynamic_config_resolver
6
+
7
+ from truss_chains import definitions
8
+
9
+
10
+ @pytest.mark.parametrize(
11
+ "config",
12
+ [
13
+ {
14
+ "RandInt": {
15
+ "predict_url": "https://model-id.api.baseten.co/deployment/deployment-id/predict"
16
+ }
17
+ },
18
+ {},
19
+ "",
20
+ ],
21
+ )
22
+ def test_get_dynamic_chainlet_config_value_sync(
23
+ config, tmp_path, dynamic_config_mount_dir
24
+ ):
25
+ with (tmp_path / definitions.DYNAMIC_CHAINLET_CONFIG_KEY).open("w") as f:
26
+ f.write(json.dumps(config))
27
+ chainlet_service_config = dynamic_config_resolver.get_dynamic_config_value_sync(
28
+ definitions.DYNAMIC_CHAINLET_CONFIG_KEY
29
+ )
30
+ assert json.loads(chainlet_service_config) == config
31
+
32
+
33
+ @pytest.mark.parametrize(
34
+ "config", [{"environment_name": "production", "foo": "bar"}, {}, "", None]
35
+ )
36
+ def test_get_dynamic_config_environment_value_sync(
37
+ config, tmp_path, dynamic_config_mount_dir
38
+ ):
39
+ with (tmp_path / dynamic_config_resolver.ENVIRONMENT_DYNAMIC_CONFIG_KEY).open(
40
+ "w"
41
+ ) as f:
42
+ f.write(json.dumps(config))
43
+ environment_str = dynamic_config_resolver.get_dynamic_config_value_sync(
44
+ dynamic_config_resolver.ENVIRONMENT_DYNAMIC_CONFIG_KEY
45
+ )
46
+ assert json.loads(environment_str) == config
47
+
48
+
49
+ def test_get_missing_config_value_sync(dynamic_config_mount_dir):
50
+ chainlet_service_config = dynamic_config_resolver.get_dynamic_config_value_sync(
51
+ definitions.DYNAMIC_CHAINLET_CONFIG_KEY
52
+ )
53
+ assert not chainlet_service_config
54
+
55
+
56
+ @pytest.mark.asyncio
57
+ @pytest.mark.parametrize(
58
+ "config",
59
+ [
60
+ {
61
+ "RandInt": {
62
+ "predict_url": "https://model-id.api.baseten.co/deployment/deployment-id/predict"
63
+ }
64
+ },
65
+ {},
66
+ "",
67
+ ],
68
+ )
69
+ async def test_get_dynamic_chainlet_config_value_async(
70
+ config, tmp_path, dynamic_config_mount_dir
71
+ ):
72
+ async with aiofiles.open(
73
+ tmp_path / definitions.DYNAMIC_CHAINLET_CONFIG_KEY, "w"
74
+ ) as f:
75
+ await f.write(json.dumps(config))
76
+ chainlet_service_config = (
77
+ await dynamic_config_resolver.get_dynamic_config_value_async(
78
+ definitions.DYNAMIC_CHAINLET_CONFIG_KEY
79
+ )
80
+ )
81
+ assert json.loads(chainlet_service_config) == config
82
+
83
+
84
+ @pytest.mark.asyncio
85
+ @pytest.mark.parametrize(
86
+ "config", [{"environment_name": "production", "foo": "bar"}, {}, "", None]
87
+ )
88
+ async def test_get_dynamic_config_environment_value_async(
89
+ config, tmp_path, dynamic_config_mount_dir
90
+ ):
91
+ async with aiofiles.open(
92
+ tmp_path / dynamic_config_resolver.ENVIRONMENT_DYNAMIC_CONFIG_KEY, "w"
93
+ ) as f:
94
+ await f.write(json.dumps(config))
95
+ environment_str = await dynamic_config_resolver.get_dynamic_config_value_async(
96
+ dynamic_config_resolver.ENVIRONMENT_DYNAMIC_CONFIG_KEY
97
+ )
98
+ assert json.loads(environment_str) == config
99
+
100
+
101
+ @pytest.mark.asyncio
102
+ async def test_get_missing_config_value_async(dynamic_config_mount_dir):
103
+ chainlet_service_config = (
104
+ await dynamic_config_resolver.get_dynamic_config_value_async(
105
+ definitions.DYNAMIC_CHAINLET_CONFIG_KEY
106
+ )
107
+ )
108
+ assert not chainlet_service_config
@@ -0,0 +1,329 @@
1
+ import datetime
2
+ import json
3
+ import os
4
+ from contextlib import nullcontext
5
+ from pathlib import Path
6
+ from typing import Callable
7
+ from unittest.mock import patch
8
+
9
+ import pytest
10
+ import requests_mock
11
+ from truss.templates.shared.lazy_data_resolver import (
12
+ BASETEN_FS_ENABLED_ENV_VAR,
13
+ LAZY_DATA_RESOLVER_PATH,
14
+ LazyDataResolver,
15
+ )
16
+
17
+
18
+ @pytest.fixture
19
+ def baseten_pointer_manifest_mock() -> Callable:
20
+ def _baseten_pointer_manifest_mock(
21
+ foo_expiry_timestamp: int, bar_expiry_timestamp: int
22
+ ):
23
+ return f"""
24
+ pointers:
25
+ - uid: foo
26
+ file_name: foo-name
27
+ hashtype: hash-type
28
+ hash: foo-hash
29
+ size: 100
30
+ resolution:
31
+ url: https://foo-rl
32
+ expiration_timestamp: {foo_expiry_timestamp}
33
+ - uid: bar
34
+ file_name: bar-name
35
+ hashtype: hash-type
36
+ hash: bar-hash
37
+ size: 1000
38
+ resolution:
39
+ url: https://bar-rl
40
+ expiration_timestamp: {bar_expiry_timestamp}
41
+ """
42
+
43
+ return _baseten_pointer_manifest_mock
44
+
45
+
46
+ def test_lazy_data_resolution_not_found():
47
+ ldr = LazyDataResolver(Path("foo"))
48
+ assert not LAZY_DATA_RESOLVER_PATH.exists()
49
+ assert ldr._bptr_resolution == {}
50
+
51
+
52
+ @pytest.mark.parametrize(
53
+ "foo_expiry,bar_expiry,expectation",
54
+ [
55
+ (
56
+ int(
57
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
58
+ ),
59
+ int(
60
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
61
+ ),
62
+ nullcontext(),
63
+ ),
64
+ (
65
+ int(
66
+ datetime.datetime(2020, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
67
+ ),
68
+ int(
69
+ datetime.datetime(2020, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
70
+ ),
71
+ pytest.raises(RuntimeError),
72
+ ),
73
+ ],
74
+ )
75
+ def test_lazy_data_resolution(
76
+ baseten_pointer_manifest_mock, foo_expiry, bar_expiry, expectation, tmp_path
77
+ ):
78
+ baseten_pointer_manifest_mock = baseten_pointer_manifest_mock(
79
+ foo_expiry, bar_expiry
80
+ )
81
+ manifest_path = tmp_path / "bptr" / "bptr-manifest"
82
+ manifest_path.parent.mkdir()
83
+ manifest_path.touch()
84
+ manifest_path.write_text(baseten_pointer_manifest_mock)
85
+ with patch(
86
+ "truss.templates.shared.lazy_data_resolver.LAZY_DATA_RESOLVER_PATH",
87
+ manifest_path,
88
+ ):
89
+ with expectation:
90
+ ldr = LazyDataResolver(Path("foo"))
91
+ assert ldr._bptr_resolution == {
92
+ "foo-name": ("https://foo-rl", "foo-hash", 100),
93
+ "bar-name": ("https://bar-rl", "bar-hash", 1000),
94
+ }
95
+
96
+
97
+ @pytest.mark.parametrize(
98
+ "foo_expiry,bar_expiry",
99
+ [
100
+ (
101
+ int(
102
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
103
+ ),
104
+ int(
105
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
106
+ ),
107
+ )
108
+ ],
109
+ )
110
+ def test_lazy_data_fetch(
111
+ baseten_pointer_manifest_mock, foo_expiry, bar_expiry, tmp_path
112
+ ):
113
+ baseten_pointer_manifest_mock = baseten_pointer_manifest_mock(
114
+ foo_expiry, bar_expiry
115
+ )
116
+ manifest_path = tmp_path / "bptr" / "bptr-manifest"
117
+ manifest_path.parent.mkdir()
118
+ manifest_path.touch()
119
+ manifest_path.write_text(baseten_pointer_manifest_mock)
120
+ with patch(
121
+ "truss.templates.shared.lazy_data_resolver.LAZY_DATA_RESOLVER_PATH",
122
+ manifest_path,
123
+ ):
124
+ data_dir = Path(tmp_path)
125
+ ldr = LazyDataResolver(data_dir)
126
+ with requests_mock.Mocker() as m:
127
+ for file_name, (url, _, _) in ldr._bptr_resolution.items():
128
+ resp = {"file_name": file_name, "url": url}
129
+ m.get(url, json=resp)
130
+ ldr.fetch()
131
+ for file_name, (url, _, _) in ldr._bptr_resolution.items():
132
+ assert (ldr._data_dir / file_name).read_text() == json.dumps(
133
+ {"file_name": file_name, "url": url}
134
+ )
135
+
136
+
137
+ @pytest.mark.parametrize(
138
+ "foo_expiry,bar_expiry",
139
+ [
140
+ (
141
+ int(
142
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
143
+ ),
144
+ int(
145
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
146
+ ),
147
+ )
148
+ ],
149
+ )
150
+ def test_lazy_data_fetch_to_cache_non_200_status(
151
+ baseten_pointer_manifest_mock, foo_expiry, bar_expiry, tmp_path, monkeypatch
152
+ ):
153
+ monkeypatch.setenv(BASETEN_FS_ENABLED_ENV_VAR, "True")
154
+ baseten_pointer_manifest_mock = baseten_pointer_manifest_mock(
155
+ foo_expiry, bar_expiry
156
+ )
157
+ manifest_path = tmp_path / "bptr" / "bptr-manifest"
158
+ manifest_path.parent.mkdir()
159
+ manifest_path.touch()
160
+ manifest_path.write_text(baseten_pointer_manifest_mock)
161
+ cache_dir = tmp_path / "cache" / "org" / "artifacts"
162
+ cache_dir.mkdir(parents=True, exist_ok=True)
163
+ cache_dir.touch()
164
+ with patch(
165
+ "truss.templates.shared.lazy_data_resolver.LAZY_DATA_RESOLVER_PATH",
166
+ manifest_path,
167
+ ) as _, patch(
168
+ "truss.templates.shared.lazy_data_resolver.CACHE_DIR", cache_dir
169
+ ) as _:
170
+ data_dir = Path(tmp_path)
171
+ ldr = LazyDataResolver(data_dir)
172
+ assert ldr._uses_b10_cache
173
+ with requests_mock.Mocker() as m:
174
+ for _, (url, _, _) in ldr._bptr_resolution.items():
175
+ m.get(url, status_code=500)
176
+ with pytest.raises(RuntimeError):
177
+ ldr.fetch()
178
+
179
+
180
+ @pytest.mark.parametrize(
181
+ "foo_expiry,bar_expiry",
182
+ [
183
+ (
184
+ int(
185
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
186
+ ),
187
+ int(
188
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
189
+ ),
190
+ )
191
+ ],
192
+ )
193
+ def test_lazy_data_fetch_to_cache(
194
+ baseten_pointer_manifest_mock, foo_expiry, bar_expiry, tmp_path, monkeypatch
195
+ ):
196
+ monkeypatch.setenv(BASETEN_FS_ENABLED_ENV_VAR, "True")
197
+ baseten_pointer_manifest_mock = baseten_pointer_manifest_mock(
198
+ foo_expiry, bar_expiry
199
+ )
200
+ manifest_path = tmp_path / "bptr" / "bptr-manifest"
201
+ manifest_path.parent.mkdir()
202
+ manifest_path.touch()
203
+ manifest_path.write_text(baseten_pointer_manifest_mock)
204
+ cache_dir = tmp_path / "cache" / "org" / "artifacts"
205
+ cache_dir.mkdir(parents=True, exist_ok=True)
206
+ cache_dir.touch()
207
+ with patch(
208
+ "truss.templates.shared.lazy_data_resolver.LAZY_DATA_RESOLVER_PATH",
209
+ manifest_path,
210
+ ) as _, patch(
211
+ "truss.templates.shared.lazy_data_resolver.CACHE_DIR", cache_dir
212
+ ) as CACHE_DIR:
213
+ data_dir = Path(tmp_path)
214
+ ldr = LazyDataResolver(data_dir)
215
+ assert ldr._uses_b10_cache
216
+ with requests_mock.Mocker() as m:
217
+ for file_name, (url, hash, _) in ldr._bptr_resolution.items():
218
+ resp = {"file_name": file_name, "url": url}
219
+ m.get(url, json=resp)
220
+ ldr.fetch()
221
+ for file_name, (url, hash, _) in ldr._bptr_resolution.items():
222
+ assert (CACHE_DIR / hash).read_text() == json.dumps(
223
+ {"file_name": file_name, "url": url}
224
+ )
225
+ assert os.path.islink(ldr._data_dir / file_name)
226
+ assert os.readlink(ldr._data_dir / file_name) == str(CACHE_DIR / hash)
227
+
228
+
229
+ @pytest.mark.parametrize(
230
+ "foo_expiry,bar_expiry",
231
+ [
232
+ (
233
+ int(
234
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
235
+ ),
236
+ int(
237
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
238
+ ),
239
+ )
240
+ ],
241
+ )
242
+ def test_lazy_data_fetch_to_cache_fallback_if_no_space(
243
+ baseten_pointer_manifest_mock, foo_expiry, bar_expiry, tmp_path, monkeypatch
244
+ ):
245
+ monkeypatch.setenv(BASETEN_FS_ENABLED_ENV_VAR, "True")
246
+ baseten_pointer_manifest_mock = baseten_pointer_manifest_mock(
247
+ foo_expiry, bar_expiry
248
+ )
249
+ manifest_path = tmp_path / "bptr" / "bptr-manifest"
250
+ manifest_path.parent.mkdir()
251
+ manifest_path.touch()
252
+ manifest_path.write_text(baseten_pointer_manifest_mock)
253
+ cache_dir = tmp_path / "cache" / "org" / "artifacts"
254
+ cache_dir.mkdir(parents=True, exist_ok=True)
255
+ cache_dir.touch()
256
+ with patch(
257
+ "truss.templates.shared.lazy_data_resolver.LAZY_DATA_RESOLVER_PATH",
258
+ manifest_path,
259
+ ) as _, patch(
260
+ "truss.templates.shared.lazy_data_resolver.CACHE_DIR", cache_dir
261
+ ) as _, patch(
262
+ "truss.templates.shared.lazy_data_resolver.shutil.disk_usage"
263
+ ) as mock_disk_usage:
264
+ data_dir = Path(tmp_path)
265
+ ldr = LazyDataResolver(data_dir)
266
+ assert ldr._uses_b10_cache
267
+
268
+ mock_disk_usage.return_value.free = 1
269
+ with requests_mock.Mocker() as m:
270
+ for file_name, (url, _, _) in ldr._bptr_resolution.items():
271
+ resp = {"file_name": file_name, "url": url}
272
+ m.get(url, json=resp)
273
+ ldr.fetch()
274
+
275
+ for file_name, (url, _, _) in ldr._bptr_resolution.items():
276
+ assert (ldr._data_dir / file_name).read_text() == json.dumps(
277
+ {"file_name": file_name, "url": url}
278
+ )
279
+
280
+
281
+ @pytest.mark.parametrize(
282
+ "foo_expiry,bar_expiry",
283
+ [
284
+ (
285
+ int(
286
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
287
+ ),
288
+ int(
289
+ datetime.datetime(3000, 1, 1, tzinfo=datetime.timezone.utc).timestamp()
290
+ ),
291
+ )
292
+ ],
293
+ )
294
+ def test_lazy_data_fetch_cached(
295
+ baseten_pointer_manifest_mock, foo_expiry, bar_expiry, tmp_path, monkeypatch
296
+ ):
297
+ monkeypatch.setenv(BASETEN_FS_ENABLED_ENV_VAR, "True")
298
+ baseten_pointer_manifest_mock = baseten_pointer_manifest_mock(
299
+ foo_expiry, bar_expiry
300
+ )
301
+ manifest_path = tmp_path / "bptr" / "bptr-manifest"
302
+ manifest_path.parent.mkdir()
303
+ manifest_path.touch()
304
+ manifest_path.write_text(baseten_pointer_manifest_mock)
305
+ cache_dir = tmp_path / "cache" / "org" / "artifacts"
306
+ cache_dir.mkdir(parents=True, exist_ok=True)
307
+ cache_dir.touch()
308
+ with patch(
309
+ "truss.templates.shared.lazy_data_resolver.LAZY_DATA_RESOLVER_PATH",
310
+ manifest_path,
311
+ ) as _, patch(
312
+ "truss.templates.shared.lazy_data_resolver.CACHE_DIR", cache_dir
313
+ ) as CACHE_DIR:
314
+ data_dir = Path(tmp_path)
315
+ ldr = LazyDataResolver(data_dir)
316
+ assert ldr._uses_b10_cache
317
+ with requests_mock.Mocker() as m:
318
+ for file_name, (url, hash, _) in ldr._bptr_resolution.items():
319
+ resp = {"file_name": file_name, "url": url}
320
+ (CACHE_DIR / hash).write_text(json.dumps(resp))
321
+ m.get(url, json=resp)
322
+ ldr.fetch()
323
+ for file_name, (url, hash, _) in ldr._bptr_resolution.items():
324
+ assert (CACHE_DIR / hash).read_text() == json.dumps(
325
+ {"file_name": file_name, "url": url}
326
+ )
327
+ assert not m.called
328
+ assert os.path.islink(ldr._data_dir / file_name)
329
+ assert os.readlink(ldr._data_dir / file_name) == str(CACHE_DIR / hash)