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.
- speedy_utils-1.1.43/.github/prompts/improveParallelErrorHandling.prompt.md +64 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/PKG-INFO +3 -2
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/pyproject.toml +3 -2
- speedy_utils-1.1.43/scripts/bug.py +34 -0
- speedy_utils-1.1.43/scripts/bug_simple.py +11 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/scripts/debug_import_time.py +80 -18
- speedy_utils-1.1.43/scripts/test.py +26 -0
- speedy_utils-1.1.43/scripts/test_both_backends.py +25 -0
- speedy_utils-1.1.43/scripts/test_error_handling.py +37 -0
- speedy_utils-1.1.43/scripts/test_locals.py +19 -0
- speedy_utils-1.1.43/scripts/test_ray_locals.py +11 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/__init__.py +7 -0
- speedy_utils-1.1.43/src/speedy_utils/multi_worker/process.py +1302 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/multi_worker/thread.py +202 -42
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/uv.lock +5247 -2937
- speedy_utils-1.1.42/src/speedy_utils/multi_worker/process.py +0 -699
- speedy_utils-1.1.42/test2.txt +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.githooks/pre-push +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/copilot-instructions.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/caching-utilities/SKILL.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/caching-utilities/examples/caching_example.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/io-utilities/SKILL.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/io-utilities/examples/io_example.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/llm-integration/SKILL.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/llm-integration/examples/llm_example.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/multi-threading-processing/SKILL.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/ray-distributed-computing/SKILL.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/skill-creation/SKILL.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/vision-utilities/SKILL.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/skills/vision-utilities/examples/vision_example.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.github/workflows/publish.yml +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.gitignore +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/.pre-commit-config.yaml +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/AGENTS.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/README.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/bumpversion.sh +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/debug_generate_response.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/debug_n_param.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/debug_n_structure.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/integration_test.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/test_decode_api.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/test_endpoints.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/test_generate.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/debug/test_generate_endpoint.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/GENERATE_QUICKREF.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/IMPLEMENTATION.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/QUICKSTART.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/TOKENIZATION.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/TOKENIZATION_IMPLEMENTATION.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/docs/zero_copy_sharing.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/generate_example.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/llm_ray_example.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/pytorch_large_model.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/shared_kwargs_example.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/temperature_range_example.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/test_parallel_gpu.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/test_share_ray.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/tokenization_example.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/examples/vision_utils_example.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/experiments/exp1/dockerfile +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/experiments/exp1/run_in_docker.sh +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/experiments/exp1/test.png +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/experiments/test_read_image.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/notebooks/README.ipynb +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/notebooks/llm_utils/llm_as_a_judge.ipynb +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/notebooks/parallel_gpu_pool.ipynb +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/notebooks/ray_tutorial.ipynb +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/notebooks/test_multi_thread.ipynb +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/ruff.toml +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/scripts/deploy.sh +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/scripts/imports.sh +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/scripts/test_import_time_vision.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/scripts/test_ray_mp.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/setup.cfg +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/datasets_utils/convert_to_arrow.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/__init__.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/chat_format/__init__.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/chat_format/display.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/chat_format/transform.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/chat_format/utils.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/group_messages.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/llm_ray.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/__init__.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/__init__.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/_utils.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/async_llm_task.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/async_lm.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/async_lm_base.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/async_lm/lm_specific.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/base_prompt_builder.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/llm.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/llm_signature.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/lm_base.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/mixins.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/openai_memoize.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/signature.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/lm/utils.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/scripts/README.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/scripts/fast_vllm.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/scripts/vllm_load_balancer.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/scripts/vllm_serve.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/vector_cache/__init__.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/vector_cache/cli.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/vector_cache/core.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/vector_cache/types.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/llm_utils/vector_cache/utils.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/__imports.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/__init__.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/clock.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/function_decorator.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/logger.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/notebook_utils.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/patcher.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/report_manager.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/utils_cache.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/utils_io.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/utils_misc.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/common/utils_print.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/multi_worker/__init__.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/multi_worker/dataset_ray.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/multi_worker/parallel_gpu_pool.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/multi_worker/progress.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/scripts/__init__.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/scripts/mpython.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/speedy_utils/scripts/openapi_client_codegen.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/vision_utils/README.md +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/vision_utils/__init__.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/vision_utils/io_utils.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/src/vision_utils/plot.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/test_s3.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/import_all.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/import_time_report.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/integration_test.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/llm_utils/test_llm_mixins.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/sample_objects.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_logger.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_logger_format.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_memoize_typing.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_mpython.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_multithread_error_trace.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_process.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_process_update.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_pytorch_sharing.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_shared_kwargs.py +0 -0
- {speedy_utils-1.1.42 → speedy_utils-1.1.43}/tests/test_thread.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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
|
+
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
|
+
|
|
@@ -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
|
|
31
|
-
as a
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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'
|
|
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,
|
|
91
|
-
|
|
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)
|
|
@@ -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
|
|