speedy-utils 1.1.42__tar.gz → 1.1.43__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 (147) hide show
  1. speedy_utils-1.1.43/.github/prompts/improveParallelErrorHandling.prompt.md +64 -0
  2. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/PKG-INFO +3 -2
  3. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/pyproject.toml +3 -2
  4. speedy_utils-1.1.43/scripts/bug.py +34 -0
  5. speedy_utils-1.1.43/scripts/bug_simple.py +11 -0
  6. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/scripts/debug_import_time.py +80 -18
  7. speedy_utils-1.1.43/scripts/test.py +26 -0
  8. speedy_utils-1.1.43/scripts/test_both_backends.py +25 -0
  9. speedy_utils-1.1.43/scripts/test_error_handling.py +37 -0
  10. speedy_utils-1.1.43/scripts/test_locals.py +19 -0
  11. speedy_utils-1.1.43/scripts/test_ray_locals.py +11 -0
  12. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/__init__.py +7 -0
  13. speedy_utils-1.1.43/src/speedy_utils/multi_worker/process.py +1302 -0
  14. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/multi_worker/thread.py +202 -42
  15. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/uv.lock +5247 -2937
  16. speedy_utils-1.1.42/src/speedy_utils/multi_worker/process.py +0 -699
  17. speedy_utils-1.1.42/test2.txt +0 -0
  18. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.githooks/pre-push +0 -0
  19. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/copilot-instructions.md +0 -0
  20. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/caching-utilities/SKILL.md +0 -0
  21. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/caching-utilities/examples/caching_example.py +0 -0
  22. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/io-utilities/SKILL.md +0 -0
  23. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/io-utilities/examples/io_example.py +0 -0
  24. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/llm-integration/SKILL.md +0 -0
  25. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/llm-integration/examples/llm_example.py +0 -0
  26. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/multi-threading-processing/SKILL.md +0 -0
  27. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/ray-distributed-computing/SKILL.md +0 -0
  28. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/skill-creation/SKILL.md +0 -0
  29. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/vision-utilities/SKILL.md +0 -0
  30. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/vision-utilities/examples/vision_example.py +0 -0
  31. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/workflows/publish.yml +0 -0
  32. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.gitignore +0 -0
  33. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.pre-commit-config.yaml +0 -0
  34. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/AGENTS.md +0 -0
  35. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/README.md +0 -0
  36. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/bumpversion.sh +0 -0
  37. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/debug_generate_response.py +0 -0
  38. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/debug_n_param.py +0 -0
  39. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/debug_n_structure.py +0 -0
  40. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/integration_test.py +0 -0
  41. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/test_decode_api.py +0 -0
  42. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/test_endpoints.py +0 -0
  43. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/test_generate.py +0 -0
  44. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/test_generate_endpoint.py +0 -0
  45. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/GENERATE_QUICKREF.md +0 -0
  46. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/IMPLEMENTATION.md +0 -0
  47. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/QUICKSTART.md +0 -0
  48. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/TOKENIZATION.md +0 -0
  49. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/TOKENIZATION_IMPLEMENTATION.md +0 -0
  50. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/zero_copy_sharing.md +0 -0
  51. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/generate_example.py +0 -0
  52. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/llm_ray_example.py +0 -0
  53. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/pytorch_large_model.py +0 -0
  54. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/shared_kwargs_example.py +0 -0
  55. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/temperature_range_example.py +0 -0
  56. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/test_parallel_gpu.py +0 -0
  57. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/test_share_ray.py +0 -0
  58. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/tokenization_example.py +0 -0
  59. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/vision_utils_example.py +0 -0
  60. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/experiments/exp1/dockerfile +0 -0
  61. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/experiments/exp1/run_in_docker.sh +0 -0
  62. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/experiments/exp1/test.png +0 -0
  63. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/experiments/test_read_image.py +0 -0
  64. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/notebooks/README.ipynb +0 -0
  65. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/notebooks/llm_utils/llm_as_a_judge.ipynb +0 -0
  66. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/notebooks/parallel_gpu_pool.ipynb +0 -0
  67. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/notebooks/ray_tutorial.ipynb +0 -0
  68. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/notebooks/test_multi_thread.ipynb +0 -0
  69. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/ruff.toml +0 -0
  70. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/scripts/deploy.sh +0 -0
  71. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/scripts/imports.sh +0 -0
  72. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/scripts/test_import_time_vision.py +0 -0
  73. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/scripts/test_ray_mp.py +0 -0
  74. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/setup.cfg +0 -0
  75. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/datasets_utils/convert_to_arrow.py +0 -0
  76. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/__init__.py +0 -0
  77. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/chat_format/__init__.py +0 -0
  78. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/chat_format/display.py +0 -0
  79. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/chat_format/transform.py +0 -0
  80. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/chat_format/utils.py +0 -0
  81. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/group_messages.py +0 -0
  82. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/llm_ray.py +0 -0
  83. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/__init__.py +0 -0
  84. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/__init__.py +0 -0
  85. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/_utils.py +0 -0
  86. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/async_llm_task.py +0 -0
  87. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/async_lm.py +0 -0
  88. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/async_lm_base.py +0 -0
  89. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/lm_specific.py +0 -0
  90. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/base_prompt_builder.py +0 -0
  91. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/llm.py +0 -0
  92. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/llm_signature.py +0 -0
  93. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/lm_base.py +0 -0
  94. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/mixins.py +0 -0
  95. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/openai_memoize.py +0 -0
  96. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/signature.py +0 -0
  97. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/utils.py +0 -0
  98. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/scripts/README.md +0 -0
  99. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/scripts/fast_vllm.py +0 -0
  100. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/scripts/vllm_load_balancer.py +0 -0
  101. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/scripts/vllm_serve.py +0 -0
  102. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/vector_cache/__init__.py +0 -0
  103. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/vector_cache/cli.py +0 -0
  104. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/vector_cache/core.py +0 -0
  105. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/vector_cache/types.py +0 -0
  106. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/vector_cache/utils.py +0 -0
  107. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/__imports.py +0 -0
  108. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/__init__.py +0 -0
  109. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/clock.py +0 -0
  110. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/function_decorator.py +0 -0
  111. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/logger.py +0 -0
  112. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/notebook_utils.py +0 -0
  113. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/patcher.py +0 -0
  114. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/report_manager.py +0 -0
  115. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/utils_cache.py +0 -0
  116. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/utils_io.py +0 -0
  117. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/utils_misc.py +0 -0
  118. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/utils_print.py +0 -0
  119. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/multi_worker/__init__.py +0 -0
  120. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/multi_worker/dataset_ray.py +0 -0
  121. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/multi_worker/parallel_gpu_pool.py +0 -0
  122. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/multi_worker/progress.py +0 -0
  123. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/scripts/__init__.py +0 -0
  124. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/scripts/mpython.py +0 -0
  125. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/scripts/openapi_client_codegen.py +0 -0
  126. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/vision_utils/README.md +0 -0
  127. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/vision_utils/__init__.py +0 -0
  128. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/vision_utils/io_utils.py +0 -0
  129. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/vision_utils/plot.py +0 -0
  130. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/test_s3.py +0 -0
  131. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/import_all.py +0 -0
  132. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/import_time_report.py +0 -0
  133. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/integration_test.py +0 -0
  134. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/llm_utils/test_llm_mixins.py +0 -0
  135. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/sample_objects.py +0 -0
  136. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test.py +0 -0
  137. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_logger.py +0 -0
  138. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_logger_format.py +0 -0
  139. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_memoize_typing.py +0 -0
  140. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_mpython.py +0 -0
  141. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_multithread_error_trace.py +0 -0
  142. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_process.py +0 -0
  143. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_process_update.py +0 -0
  144. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_pytorch_sharing.py +0 -0
  145. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_shared_kwargs.py +0 -0
  146. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_thread.py +0 -0
  147. {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_tokenization.py +0 -0
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: improveParallelErrorHandling
3
+ description: Enhance error tracebacks in parallel execution with rich formatting and context
4
+ argument-hint: the parallel execution function and backend type
5
+ ---
6
+
7
+ Improve error handling for the specified parallel execution function to provide clean, user-focused tracebacks similar to direct function calls.
8
+
9
+ ## Requirements
10
+
11
+ 1. **Filter Internal Frames**: Remove framework/library internal frames from tracebacks, showing only user code
12
+ 2. **Add Context Lines**: Display 3 lines before and after each error location with line numbers
13
+ 3. **Include Caller Frame**: Show where the parallel execution function was called, not just where the error occurred
14
+ 4. **Rich Formatting**: Use rich library's Panel/formatting for clean, readable output
15
+ 5. **Suppress Noise**: Set environment variables or flags to suppress verbose framework error logs
16
+
17
+ ## Implementation Steps
18
+
19
+ 1. **Capture Caller Context**: Use `inspect.currentframe().f_back` to capture where the parallel function was called (filename, line number, function name)
20
+
21
+ 2. **Wrap Error Handling**: Catch framework-specific exceptions (e.g., `RayTaskError`, thread exceptions) in the execution loop
22
+
23
+ 3. **Parse/Extract Original Exception**: Get the underlying user exception from the framework wrapper
24
+ - Extract exception type, message, and traceback information
25
+ - Parse from string representation if traceback objects aren't preserved
26
+
27
+ 4. **Filter Frames**: Skip frames matching internal paths:
28
+ - Framework internals (e.g., `ray/_private`, `concurrent/futures`)
29
+ - Library worker implementations (e.g., `speedy_utils/multi_worker`)
30
+ - Site-packages for the framework
31
+
32
+ 5. **Format with Context**:
33
+ - For each user frame, show: `filepath:lineno in function_name`
34
+ - Use `linecache.getline()` to retrieve surrounding lines
35
+ - Highlight the error line with `❱` marker
36
+ - Number all lines (e.g., ` 4 │ code here` or ` 5 ❱ error here`)
37
+
38
+ 6. **Display Caller Frame First**: Show where the parallel function was invoked before showing the actual error location
39
+
40
+ 7. **Clean Exit**: Flush output streams before exiting to ensure traceback displays
41
+
42
+ ## Example Output Format
43
+
44
+ ```
45
+ ╭─────────────── Traceback (most recent call last) ───────────────╮
46
+ │ /path/to/user/script.py:42 in main │
47
+ │ │
48
+ │ 40 │ data = load_data() │
49
+ │ 41 │ # Process in parallel │
50
+ │ 42 ❱ results = multi_process(process_item, data, workers=8) │
51
+ │ 43 │ │
52
+ │ │
53
+ │ /path/to/user/module.py:15 in process_item │
54
+ │ │
55
+ │ 12 │ def process_item(item): │
56
+ │ 13 │ value = item['key'] │
57
+ │ 14 │ denominator = value - 100 │
58
+ │ 15 ❱ return 1 / denominator │
59
+ │ 16 │ │
60
+ ╰──────────────────────────────────────────────────────────────────╯
61
+ ZeroDivisionError: division by zero
62
+ ```
63
+
64
+ Apply these improvements to the specified parallel execution function, ensuring error messages are as clear as direct function calls while maintaining all performance benefits of parallel execution.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: speedy-utils
3
- Version: 1.1.42
3
+ Version: 1.1.43
4
4
  Summary: Fast and easy-to-use package for data science
5
5
  Project-URL: Homepage, https://github.com/anhvth/speedy
6
6
  Project-URL: Repository, https://github.com/anhvth/speedy
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Programming Language :: Python :: 3.14
20
- Requires-Python: >=3.8
20
+ Requires-Python: >=3.9
21
21
  Requires-Dist: aiohttp
22
22
  Requires-Dist: bump2version
23
23
  Requires-Dist: cachetools
@@ -39,6 +39,7 @@ Requires-Dist: pydantic
39
39
  Requires-Dist: pytest
40
40
  Requires-Dist: ray
41
41
  Requires-Dist: requests
42
+ Requires-Dist: rich>=14.3.1
42
43
  Requires-Dist: ruff
43
44
  Requires-Dist: scikit-learn
44
45
  Requires-Dist: tabulate
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "speedy-utils"
3
- version = "1.1.42"
3
+ version = "1.1.43"
4
4
  description = "Fast and easy-to-use package for data science"
5
5
  authors = [{ name = "AnhVTH", email = "anhvth.226@gmail.com" }]
6
6
  readme = "README.md"
7
7
  license = { text = "MIT" }
8
- requires-python = ">=3.8"
8
+ requires-python = ">=3.9"
9
9
  dependencies = [
10
10
  "numpy",
11
11
  "requests",
@@ -33,6 +33,7 @@ dependencies = [
33
33
  "ray",
34
34
  "aiohttp",
35
35
  "pytest",
36
+ "rich>=14.3.1",
36
37
  ]
37
38
  classifiers = [
38
39
  "Development Status :: 4 - Beta",
@@ -0,0 +1,34 @@
1
+ # type: ignore
2
+ from speedy_utils import multi_process, multi_thread
3
+
4
+
5
+ def do_something(x):
6
+ if x % 3 == 0:
7
+ raise ValueError(f'Error at index {x}')
8
+ return x * 2
9
+
10
+
11
+ inputs = range(10)
12
+
13
+
14
+ if __name__ == '__main__':
15
+ print('Testing error_handler="log" with mp backend:')
16
+ results = multi_process(
17
+ do_something,
18
+ inputs,
19
+ backend='mp',
20
+ error_handler='log',
21
+ max_error_files=5,
22
+ )
23
+ print(f'Results: {results}')
24
+ print()
25
+
26
+ # print('Testing error_handler="log" with multi_thread:')
27
+ # results = multi_thread(
28
+ # do_something,
29
+ # inputs,
30
+ # error_handler='log',
31
+ # max_error_files=5,
32
+ # )
33
+ # print(f'Results: {results}')
34
+
@@ -0,0 +1,11 @@
1
+ from speedy_utils import *
2
+
3
+
4
+ def do_something(x):
5
+ x = 10
6
+ y = 0
7
+ x/y
8
+
9
+
10
+ do_something(1)
11
+
@@ -18,21 +18,20 @@ import json
18
18
  import re
19
19
  import subprocess
20
20
  import sys
21
- from typing import Dict, List, Tuple
21
+ from typing import Dict, List, Optional, Tuple
22
22
 
23
23
 
24
24
  DEFAULT_MODULES = ['speedy_utils', 'llm_utils', 'vision_utils']
25
25
 
26
26
 
27
- def parse_x_importtime(stderr: str) -> List[Tuple[str, float]]:
27
+ def parse_x_importtime(stderr: str) -> List[Tuple[str, float, float]]:
28
28
  """Parse -X importtime stderr into per-top-level module seconds.
29
29
 
30
- We use the first column (self-time) aggregated per top-level module
31
- as a good approximation of which third-party or heavy packages cost
32
- time during import.
30
+ We aggregate both self-time and cumulative-time per top-level module
31
+ as a quick proxy for heavy imports.
33
32
  """
34
33
 
35
- times: Dict[str, float] = {}
34
+ times: Dict[str, Tuple[float, float]] = {}
36
35
  pattern = re.compile(r'^\s*import time:\s*(\d+)\s*\|\s*(\d+)\s*\|\s*(.+)$')
37
36
  for line in stderr.splitlines():
38
37
  match = pattern.match(line)
@@ -40,27 +39,43 @@ def parse_x_importtime(stderr: str) -> List[Tuple[str, float]]:
40
39
  continue
41
40
  try:
42
41
  self_us = int(match.group(1))
42
+ cum_us = int(match.group(2))
43
43
  mod_name = match.group(3).strip()
44
44
  except Exception:
45
45
  continue
46
46
 
47
47
  top = mod_name.split('.', 1)[0]
48
- times[top] = times.get(top, 0.0) + (self_us / 1_000_000.0)
48
+ self_sec = self_us / 1_000_000.0
49
+ cum_sec = cum_us / 1_000_000.0
50
+ prev_self, prev_cum = times.get(top, (0.0, 0.0))
51
+ times[top] = (prev_self + self_sec, prev_cum + cum_sec)
49
52
 
50
53
  # return sorted list (desc)
51
- return sorted(times.items(), key=lambda it: it[1], reverse=True)
54
+ return sorted(
55
+ [(name, vals[0], vals[1]) for name, vals in times.items()],
56
+ key=lambda it: it[1],
57
+ reverse=True,
58
+ )
52
59
 
53
60
 
54
- def run_importtime(module: str) -> Tuple[bool, str]:
61
+ def run_importtime(module: str, star_import: bool) -> Tuple[bool, str]:
55
62
  exe = sys.executable
56
- cmd = [exe, '-X', 'importtime', '-c', f'from {module} import *']
63
+ if star_import:
64
+ code = f'from {module} import *'
65
+ else:
66
+ code = f'import {module}'
67
+ cmd = [exe, '-X', 'importtime', '-c', code]
57
68
  p = subprocess.run(cmd, capture_output=True, text=True, check=False)
58
69
  ok = p.returncode == 0 and bool(p.stderr.strip())
59
70
  out = p.stderr if p.stderr else p.stdout
60
71
  return ok, out
61
72
 
62
73
 
63
- def run_timed_import(module: str) -> Tuple[bool, str]:
74
+ def run_timed_import(module: str, star_import: bool) -> Tuple[bool, str]:
75
+ if star_import:
76
+ import_stmt = f'from {module} import *'
77
+ else:
78
+ import_stmt = f'import {module}'
64
79
  code = (
65
80
  'import builtins, time, json\n'
66
81
  'orig = builtins.__import__\n'
@@ -74,7 +89,7 @@ def run_timed_import(module: str) -> Tuple[bool, str]:
74
89
  " key = name.split('.',1)[0]\n"
75
90
  ' times[key] = times.get(key, 0.0) + elapsed\n'
76
91
  'builtins.__import__ = timed\n'
77
- f'from {module} import *\n'
92
+ f'{import_stmt}\n'
78
93
  'builtins.__import__ = orig\n'
79
94
  'print(json.dumps(sorted(times.items(), key=lambda it: it[1], reverse=True)))\n'
80
95
  )
@@ -86,9 +101,30 @@ def run_timed_import(module: str) -> Tuple[bool, str]:
86
101
  return True, p.stdout.strip()
87
102
 
88
103
 
89
- def pretty_print_list(items: List[Tuple[str, float]]) -> None:
90
- for name, sec in items:
91
- print(f'{sec:6.3f}s {name}')
104
+ def pretty_print_list(items: List[Tuple[str, float, Optional[float]]]) -> None:
105
+ for name, self_sec, cum_sec in items:
106
+ if cum_sec is None:
107
+ print(f'{self_sec:6.3f}s {name}')
108
+ else:
109
+ print(f'{self_sec:6.3f}s {cum_sec:6.3f}s {name}')
110
+
111
+
112
+ def is_stdlib(name: str) -> bool:
113
+ if name == 'builtins':
114
+ return True
115
+ stdlib_names = getattr(sys, 'stdlib_module_names', None)
116
+ if stdlib_names is None:
117
+ return False
118
+ return name in stdlib_names
119
+
120
+
121
+ def filter_stdlib(
122
+ items: List[Tuple[str, float, Optional[float]]],
123
+ show_stdlib: Optional[bool],
124
+ ) -> List[Tuple[str, float, Optional[float]]]:
125
+ if show_stdlib is None:
126
+ return items
127
+ return [it for it in items if is_stdlib(it[0]) is show_stdlib]
92
128
 
93
129
 
94
130
  def main(argv: List[str] | None = None) -> int:
@@ -98,11 +134,28 @@ def main(argv: List[str] | None = None) -> int:
98
134
  '--min-sec', type=float, default=0.2, help='Minimum seconds to show'
99
135
  )
100
136
  parser.add_argument('--no-x', action='store_true', help="Don't try -X importtime")
137
+ parser.add_argument(
138
+ '--star-import',
139
+ action='store_true',
140
+ help='Use "from module import *" instead of plain import',
141
+ )
101
142
  parser.add_argument(
102
143
  '--raw', action='store_true', help='Show raw -X output in addition'
103
144
  )
104
145
  parser.add_argument('-n', '--top', type=int, default=20)
146
+ stdlib_group = parser.add_mutually_exclusive_group()
147
+ stdlib_group.add_argument(
148
+ '--stdlib',
149
+ action='store_true',
150
+ help='Show only standard library modules',
151
+ )
152
+ stdlib_group.add_argument(
153
+ '--no-stdlib',
154
+ action='store_true',
155
+ help='Exclude standard library modules',
156
+ )
105
157
  args = parser.parse_args(argv)
158
+ show_stdlib = True if args.stdlib else False if args.no_stdlib else None
106
159
 
107
160
  for module in args.modules:
108
161
  print('=' * 60)
@@ -110,12 +163,16 @@ def main(argv: List[str] | None = None) -> int:
110
163
  print('=' * 60)
111
164
 
112
165
  if not args.no_x:
113
- ok, out = run_importtime(module)
166
+ ok, out = run_importtime(module, args.star_import)
114
167
  if ok:
115
168
  parsed = parse_x_importtime(out)
116
169
  filtered = [it for it in parsed if it[1] >= args.min_sec]
170
+ filtered = filter_stdlib(
171
+ [(n, s, c) for n, s, c in filtered], show_stdlib
172
+ )
117
173
  if filtered:
118
174
  print('Top heavy imports (from -X importtime):')
175
+ print(' self cum module')
119
176
  pretty_print_list(filtered[: args.top])
120
177
  else:
121
178
  print(
@@ -125,15 +182,20 @@ def main(argv: List[str] | None = None) -> int:
125
182
  print('\nRaw -X importtime output:\n')
126
183
  print(out)
127
184
  continue
185
+ print(
186
+ f'Failed to measure imports for {module!r} with -X importtime. '
187
+ 'Retry with --raw for details.'
188
+ )
128
189
 
129
190
  # Fallback instrumentation
130
- ok, out = run_timed_import(module)
191
+ ok, out = run_timed_import(module, args.star_import)
131
192
  if not ok:
132
- print('Failed to measure imports:\n', out)
193
+ print(f'Failed to measure imports for {module!r}:\n', out)
133
194
  continue
134
195
 
135
196
  items = json.loads(out)
136
197
  filtered = [it for it in items if it[1] >= args.min_sec]
198
+ filtered = filter_stdlib([(n, s, None) for n, s in filtered], show_stdlib)
137
199
  if not filtered:
138
200
  print(f'No imports >= {args.min_sec:.3f}s (fallback)')
139
201
  continue
@@ -0,0 +1,26 @@
1
+ from speedy_utils import multi_thread
2
+
3
+
4
+ def demo_1_simple_error():
5
+ """Demo 1: Simple function error."""
6
+ print('\n' + '=' * 70)
7
+ print('DEMO 1: Simple TypeError in user function')
8
+ print('=' * 70)
9
+
10
+ def process_item(x):
11
+ # Intentional error - calling a list
12
+ my_list = [1, 2, 3]
13
+ return my_list(x)
14
+
15
+ # try:
16
+ multi_thread(process_item, range(300), workers=100, progress=True)
17
+ # except TypeError as e:
18
+ # pass
19
+ # import traceback; traceback.print_exc()
20
+ # print(f'\nCaught: {type(e).__name__}')
21
+ # print('\nError message focuses on YOUR code:')
22
+ # print(f'{e}')
23
+
24
+
25
+
26
+ demo_1_simple_error()
@@ -0,0 +1,25 @@
1
+ from speedy_utils import *
2
+
3
+ def do_something(x):
4
+ x = 10
5
+ y = 0
6
+ x/y
7
+
8
+ if __name__ == '__main__':
9
+ inputs = range(10)
10
+
11
+ print("=" * 80)
12
+ print("Testing MP backend:")
13
+ print("=" * 80)
14
+ try:
15
+ multi_process(do_something, inputs, backend='mp')
16
+ except SystemExit:
17
+ pass
18
+
19
+ print("\n" + "=" * 80)
20
+ print("Testing RAY backend:")
21
+ print("=" * 80)
22
+ try:
23
+ multi_process(do_something, inputs, backend='ray')
24
+ except SystemExit:
25
+ pass
@@ -0,0 +1,37 @@
1
+ """Test improved error handling in multi_thread."""
2
+ from speedy_utils import multi_thread
3
+
4
+
5
+ def process_data(item):
6
+ """Process a data item - may raise errors."""
7
+ # Simulate some processing logic
8
+ value = item['value']
9
+ threshold = item.get('threshold', 100)
10
+
11
+ # This will error when value == threshold
12
+ result = 1 / (value - threshold)
13
+ return result * 2
14
+
15
+
16
+ def main():
17
+ """Run test cases for error handling."""
18
+ # Test case 1: Error in the middle of processing
19
+ print("Test 1: Error with division by zero")
20
+ print("-" * 60)
21
+
22
+ data = [
23
+ {'value': 50, 'threshold': 100},
24
+ {'value': 75, 'threshold': 100},
25
+ {'value': 100, 'threshold': 100}, # This will error
26
+ {'value': 125, 'threshold': 100},
27
+ ]
28
+
29
+ try:
30
+ results = multi_thread(process_data, data, workers=4)
31
+ print(f"Results: {results}")
32
+ except SystemExit:
33
+ print("\nTest completed - error was properly reported")
34
+
35
+
36
+ if __name__ == '__main__':
37
+ main()
@@ -0,0 +1,19 @@
1
+ from speedy_utils import *
2
+ import json
3
+ import sys
4
+
5
+ def process_data(item):
6
+ # Mix of user variables and imported modules
7
+ data = {'value': item}
8
+ multiplier = 2
9
+ result_list = [1, 2, 3]
10
+
11
+ # This will cause an error
12
+ denominator = 0
13
+ final = data['value'] * multiplier / denominator
14
+ return final
15
+
16
+ if __name__ == '__main__':
17
+ inputs = range(5)
18
+ ret = multi_process(process_data, inputs, backend='mp', error_handler='log')
19
+ print(ret)
@@ -0,0 +1,11 @@
1
+ from speedy_utils import *
2
+
3
+ def do_something(x):
4
+ x = 10
5
+ y = 0
6
+ # Intentionally cause division by zero error for testing
7
+ _ = x/y
8
+
9
+ if __name__ == '__main__':
10
+ inputs = range(10)
11
+ multi_process(do_something, inputs, backend='ray')
@@ -7,6 +7,13 @@ t = time.time()
7
7
 
8
8
  from .__imports import *
9
9
 
10
+ # Install rich traceback for better error messages
11
+ try:
12
+ from rich.traceback import install
13
+ install(show_locals=os.getenv('TRACEBACK_SHOW_LOCALS', 'True') == 'True')
14
+ except ImportError:
15
+ pass # rich is optional
16
+
10
17
  # Clock module
11
18
  from .common.clock import Clock, speedy_timer, timef
12
19