scope-profiler 0.2__tar.gz → 0.2.2__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 (42) hide show
  1. {scope_profiler-0.2 → scope_profiler-0.2.2}/PKG-INFO +162 -19
  2. {scope_profiler-0.2 → scope_profiler-0.2.2}/README.md +159 -18
  3. {scope_profiler-0.2 → scope_profiler-0.2.2}/pyproject.toml +3 -1
  4. scope_profiler-0.2.2/src/scope_profiler/__init__.py +49 -0
  5. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/__main__.py +20 -3
  6. scope_profiler-0.2.2/src/scope_profiler/h5reader.py +380 -0
  7. scope_profiler-0.2.2/src/scope_profiler/inspection.py +373 -0
  8. scope_profiler-0.2.2/src/scope_profiler/metadata.py +200 -0
  9. scope_profiler-0.2.2/src/scope_profiler/mpi_region.py +289 -0
  10. scope_profiler-0.2.2/src/scope_profiler/plotting_scripts.py +1157 -0
  11. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/post_processing.py +122 -5
  12. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/profile_config.py +16 -3
  13. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/profile_manager.py +59 -73
  14. scope_profiler-0.2.2/src/scope_profiler/region.py +159 -0
  15. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/region_profiler.py +102 -192
  16. scope_profiler-0.2.2/src/scope_profiler/summary.py +196 -0
  17. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/test_app.py +156 -13
  18. scope_profiler-0.2.2/src/scope_profiler/tests/test_buffer_growth.py +126 -0
  19. scope_profiler-0.2.2/src/scope_profiler/tests/test_inspection.py +318 -0
  20. scope_profiler-0.2.2/src/scope_profiler/tests/test_metadata.py +175 -0
  21. scope_profiler-0.2.2/src/scope_profiler/tests/test_post_processing.py +516 -0
  22. scope_profiler-0.2.2/src/scope_profiler/tests/test_reader_api.py +204 -0
  23. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/PKG-INFO +162 -19
  24. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/SOURCES.txt +7 -0
  25. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/requires.txt +2 -0
  26. scope_profiler-0.2/src/scope_profiler/__init__.py +0 -7
  27. scope_profiler-0.2/src/scope_profiler/h5reader.py +0 -189
  28. scope_profiler-0.2/src/scope_profiler/mpi_region.py +0 -117
  29. scope_profiler-0.2/src/scope_profiler/plotting_scripts.py +0 -850
  30. scope_profiler-0.2/src/scope_profiler/region.py +0 -112
  31. scope_profiler-0.2/src/scope_profiler/tests/test_post_processing.py +0 -221
  32. {scope_profiler-0.2 → scope_profiler-0.2.2}/setup.cfg +0 -0
  33. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/__init__.py +0 -0
  34. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/examples.py +0 -0
  35. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/examples_pylikwid.py +0 -0
  36. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/pylikwid_readme.py +0 -0
  37. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/test_mpi.py +0 -0
  38. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/test_overhead.py +0 -0
  39. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler/tests/test_readme.py +0 -0
  40. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/dependency_links.txt +0 -0
  41. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/entry_points.txt +0 -0
  42. {scope_profiler-0.2 → scope_profiler-0.2.2}/src/scope_profiler.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scope-profiler
3
- Version: 0.2
3
+ Version: 0.2.2
4
4
  Summary: Profile code regions in python, optionally with LIKWID markers.
5
5
  Author: Max
6
6
  Project-URL: Source, https://github.com/max-models/scope-profiler
@@ -23,6 +23,8 @@ Provides-Extra: pproc
23
23
  Requires-Dist: ipykernel; extra == "pproc"
24
24
  Requires-Dist: jupyterlab; extra == "pproc"
25
25
  Requires-Dist: matplotlib; extra == "pproc"
26
+ Requires-Dist: maxplotlibx>=0.1.5; extra == "pproc"
27
+ Requires-Dist: pandas; extra == "pproc"
26
28
  Provides-Extra: dev
27
29
  Requires-Dist: black[jupyter]; extra == "dev"
28
30
  Requires-Dist: isort; extra == "dev"
@@ -97,22 +99,81 @@ Execution:
97
99
 
98
100
  ```bash
99
101
  ❯ python test.py
100
- Region: main
101
- Total Calls : 1
102
- Total Time : 0.001503709 s
103
- Avg Time : 0.001503709 s
104
- Min Time : 0.001503709 s
105
- Max Time : 0.001503709 s
106
- Std Dev : 0.0 s
107
- ----------------------------------------
108
- Region: iteration
109
- Total Calls : 10
110
- Total Time : 3.832e-06 s
111
- Avg Time : 3.832e-07 s
112
- Min Time : 2.08e-07 s
113
- Max Time : 8.75e-07 s
114
- Std Dev : 2.2431888016838885e-07 s
115
- ----------------------------------------
102
+ profiling_data.h5 (1 rank(s))
103
+ region ranks calls total [s] avg [s] min [s] max [s] std [s]
104
+ ----------------------------------------------------------------------------------------
105
+ main 1 1 0.00150371 0.00150371 0.00150371 0.00150371 0
106
+ iteration 1 10 3.832e-06 3.832e-07 2.08e-07 8.75e-07 2.24319e-07
107
+ ----------------------------------------------------------------------------------------
108
+ TOTAL 11 0.00150754
109
+
110
+ Regions may nest, so the summed total can exceed the wall-clock time.
111
+ ```
112
+
113
+ `finalize()` prints the same table as `scope-profiler inspect` and
114
+ `ProfilingH5Reader.print_summary()`. Pass `verbose=False` to suppress it.
115
+
116
+ ## Inspecting a profiling file
117
+
118
+ `scope-profiler inspect` prints what is inside an HDF5 profiling file: the
119
+ full run metadata (host, CPU, loaded modules, Slurm job, environment) and one
120
+ statistics line per region, with no plotting dependencies needed.
121
+
122
+ ```bash
123
+ scope-profiler inspect profiling_data.h5
124
+ ```
125
+
126
+ ```text
127
+ ==============================================================================
128
+ profiling_data.h5
129
+ 2 rank(s), 4 region(s), 0.18 MiB, 0.0951538 s wall clock
130
+ ==============================================================================
131
+
132
+ Metadata
133
+ Run
134
+ timestamp : 2026-07-26T18:57:49
135
+ user : mlindqvi
136
+ hostname : lrdn1234
137
+ System
138
+ chip_information : AMD EPYC 9654 96-Core Processor
139
+ Parallelism
140
+ mpi_size : 2
141
+ omp_num_threads : 8
142
+ total_cores : 16
143
+ Slurm
144
+ SLURM_JOB_ID : 9988776
145
+ Modules (4)
146
+ profile/base
147
+ gcc/12.3.0
148
+ openmpi/4.1.6--gcc--12.3.0
149
+ python/3.11.7
150
+
151
+ Regions (4)
152
+ region ranks calls total [s] avg [s] min [s] max [s] std [s]
153
+ --------------------------------------------------------------------------------
154
+ timestep 2 8 0.139235 0.0174044 0.0165256 0.0176325 0.000338551
155
+ solve 2 8 0.0991292 0.0123911 0.0115046 0.0125382 0.000335212
156
+ setup 2 2 0.0473326 0.0236663 0.0222938 0.0250388 0.00137254
157
+ assemble 2 8 0.0399496 0.0049937 0.00484729 0.0050345 5.5882e-05
158
+ --------------------------------------------------------------------------------
159
+ TOTAL 26 0.325647
160
+ ```
161
+
162
+ Long values such as `PATH` are clipped unless `--full` is passed, regions can
163
+ be filtered with `--include`/`--exclude`/`--ranks`, reordered with `--sort`,
164
+ and either section shown alone with `--metadata-only` / `--regions-only`.
165
+
166
+ The metadata can also be exported to JSON, with one entry per inspected file
167
+ and no clipping:
168
+
169
+ ```bash
170
+ scope-profiler inspect profiling_data.h5 --export-metadata metadata.json --quiet
171
+ ```
172
+
173
+ ```python
174
+ from scope_profiler.inspection import write_metadata_json
175
+
176
+ write_metadata_json("profiling_data.h5", "metadata.json")
116
177
  ```
117
178
 
118
179
  ## Example plots
@@ -245,6 +306,48 @@ ProfileManager.finalize()
245
306
  Both `fibonacci` and `fibonacci_ctx` will report one call per recursive
246
307
  invocation, each with correct, non-overlapping timing data.
247
308
 
309
+ ## Analysing results in Python
310
+
311
+ `ProfilingH5Reader` loads a merged profiling file and behaves like an ordered
312
+ mapping of region name to region. Every duration and timestamp it reports is in
313
+ **seconds**:
314
+
315
+ ```python
316
+ from scope_profiler import ProfilingH5Reader
317
+
318
+ reader = ProfilingH5Reader("profiling_data.h5")
319
+ reader.print_summary()
320
+
321
+ # region calls total [s] avg [s] min [s] max [s]
322
+ # ---------------------------------------------------------------------------
323
+ # setup 1 0.02401 0.02401 0.02401 0.02401
324
+ # timestep 5 0.062835 0.012567 0.0087755 0.0187844
325
+
326
+ solve = reader["solve"] # an MPIRegion: the region across all ranks
327
+ solve.num_calls # summed over ranks
328
+ solve.total_duration # seconds
329
+ solve.average_durations() # {rank: seconds}, for load imbalance
330
+ solve[0].durations # every call on rank 0, as a numpy array
331
+ ```
332
+
333
+ `summary()` returns the same table as a list of dicts, and `to_dataframe()`
334
+ returns it as a pandas DataFrame (one row per region, or per region and rank
335
+ with `per_rank=True`):
336
+
337
+ ```python
338
+ frame = reader.to_dataframe().sort_values("total_duration", ascending=False)
339
+ per_rank = reader.to_dataframe(per_rank=True)
340
+ ```
341
+
342
+ `include` / `exclude` regexes select regions in `get_regions()`, `summary()`,
343
+ `to_dataframe()` and every `plot_*` function.
344
+
345
+ The [tutorial notebooks](tutorials/) cover this in depth:
346
+ [getting started](tutorials/01_getting_started.ipynb),
347
+ [post-processing](tutorials/02_postprocessing.ipynb),
348
+ [visualization](tutorials/03_visualization.ipynb) and
349
+ [profiling modes](tutorials/04_profiling_modes.ipynb).
350
+
248
351
  ## Flame graphs
249
352
 
250
353
  Because each call - including recursive re-entries of the same region -
@@ -265,8 +368,7 @@ scope-profiler pproc profiling_data.h5 --show -o figures
265
368
  Or programmatically:
266
369
 
267
370
  ```python
268
- from scope_profiler.h5reader import ProfilingH5Reader
269
- from scope_profiler.plotting_scripts import plot_flame
371
+ from scope_profiler import ProfilingH5Reader, plot_flame
270
372
 
271
373
  reader = ProfilingH5Reader("profiling_data.h5")
272
374
  plot_flame(reader, filepath="flame_plot.png")
@@ -284,3 +386,44 @@ scope-profiler pproc profiling_data.h5 --cmap viridis -o figures
284
386
  By default the flame graph covers rank 0, since it represents a single
285
387
  execution's call stack; pass `ranks=[...]` to render one flame graph per
286
388
  requested rank.
389
+
390
+ ## Exporting plot data
391
+
392
+ Every `plot_*` function accepts a `data_filepath` argument that writes the
393
+ exact data behind the chart to a file, so it can be re-parsed and re-plotted
394
+ later without the original HDF5 file. `data_format` selects `"csv"` (default)
395
+ or `"json"`:
396
+
397
+ ```python
398
+ plot_gantt(reader, filepath="gantt_plot.png", data_filepath="gantt_data.csv")
399
+ plot_gantt(
400
+ reader,
401
+ filepath="gantt_plot.png",
402
+ data_filepath="gantt_data.json",
403
+ data_format="json",
404
+ )
405
+ ```
406
+
407
+ The JSON payload additionally includes a `colors` map (region or file label
408
+ to `#rrggbb`) matching the colors used in the matplotlib plot, so a
409
+ JavaScript charting library like Plotly can reproduce the same look.
410
+
411
+ `scope-profiler pproc --export-data` does the same for every plot in one
412
+ run, writing `gantt_data`, `flame_data`, `durations_data`, and (for multiple
413
+ input files) `speedup_data` alongside the PNGs. Pass `--export-data-format
414
+ json` to get `.json` files instead of the default `.csv`:
415
+
416
+ ```bash
417
+ scope-profiler pproc profiling_data.h5 -o figures --export-data
418
+ scope-profiler pproc profiling_data.h5 -o figures --export-data --export-data-format json
419
+ ```
420
+
421
+ Pass `--skip-plot-images` (requires `--export-data`) to skip rendering the
422
+ PNGs entirely and only write the exported data plus `region_statistics.json`
423
+ — useful when a website renders charts client-side (e.g. with Plotly)
424
+ straight from the JSON:
425
+
426
+ ```bash
427
+ scope-profiler pproc profiling_data.h5 -o figures \
428
+ --export-data --export-data-format json --skip-plot-images
429
+ ```
@@ -54,22 +54,81 @@ Execution:
54
54
 
55
55
  ```bash
56
56
  ❯ python test.py
57
- Region: main
58
- Total Calls : 1
59
- Total Time : 0.001503709 s
60
- Avg Time : 0.001503709 s
61
- Min Time : 0.001503709 s
62
- Max Time : 0.001503709 s
63
- Std Dev : 0.0 s
64
- ----------------------------------------
65
- Region: iteration
66
- Total Calls : 10
67
- Total Time : 3.832e-06 s
68
- Avg Time : 3.832e-07 s
69
- Min Time : 2.08e-07 s
70
- Max Time : 8.75e-07 s
71
- Std Dev : 2.2431888016838885e-07 s
72
- ----------------------------------------
57
+ profiling_data.h5 (1 rank(s))
58
+ region ranks calls total [s] avg [s] min [s] max [s] std [s]
59
+ ----------------------------------------------------------------------------------------
60
+ main 1 1 0.00150371 0.00150371 0.00150371 0.00150371 0
61
+ iteration 1 10 3.832e-06 3.832e-07 2.08e-07 8.75e-07 2.24319e-07
62
+ ----------------------------------------------------------------------------------------
63
+ TOTAL 11 0.00150754
64
+
65
+ Regions may nest, so the summed total can exceed the wall-clock time.
66
+ ```
67
+
68
+ `finalize()` prints the same table as `scope-profiler inspect` and
69
+ `ProfilingH5Reader.print_summary()`. Pass `verbose=False` to suppress it.
70
+
71
+ ## Inspecting a profiling file
72
+
73
+ `scope-profiler inspect` prints what is inside an HDF5 profiling file: the
74
+ full run metadata (host, CPU, loaded modules, Slurm job, environment) and one
75
+ statistics line per region, with no plotting dependencies needed.
76
+
77
+ ```bash
78
+ scope-profiler inspect profiling_data.h5
79
+ ```
80
+
81
+ ```text
82
+ ==============================================================================
83
+ profiling_data.h5
84
+ 2 rank(s), 4 region(s), 0.18 MiB, 0.0951538 s wall clock
85
+ ==============================================================================
86
+
87
+ Metadata
88
+ Run
89
+ timestamp : 2026-07-26T18:57:49
90
+ user : mlindqvi
91
+ hostname : lrdn1234
92
+ System
93
+ chip_information : AMD EPYC 9654 96-Core Processor
94
+ Parallelism
95
+ mpi_size : 2
96
+ omp_num_threads : 8
97
+ total_cores : 16
98
+ Slurm
99
+ SLURM_JOB_ID : 9988776
100
+ Modules (4)
101
+ profile/base
102
+ gcc/12.3.0
103
+ openmpi/4.1.6--gcc--12.3.0
104
+ python/3.11.7
105
+
106
+ Regions (4)
107
+ region ranks calls total [s] avg [s] min [s] max [s] std [s]
108
+ --------------------------------------------------------------------------------
109
+ timestep 2 8 0.139235 0.0174044 0.0165256 0.0176325 0.000338551
110
+ solve 2 8 0.0991292 0.0123911 0.0115046 0.0125382 0.000335212
111
+ setup 2 2 0.0473326 0.0236663 0.0222938 0.0250388 0.00137254
112
+ assemble 2 8 0.0399496 0.0049937 0.00484729 0.0050345 5.5882e-05
113
+ --------------------------------------------------------------------------------
114
+ TOTAL 26 0.325647
115
+ ```
116
+
117
+ Long values such as `PATH` are clipped unless `--full` is passed, regions can
118
+ be filtered with `--include`/`--exclude`/`--ranks`, reordered with `--sort`,
119
+ and either section shown alone with `--metadata-only` / `--regions-only`.
120
+
121
+ The metadata can also be exported to JSON, with one entry per inspected file
122
+ and no clipping:
123
+
124
+ ```bash
125
+ scope-profiler inspect profiling_data.h5 --export-metadata metadata.json --quiet
126
+ ```
127
+
128
+ ```python
129
+ from scope_profiler.inspection import write_metadata_json
130
+
131
+ write_metadata_json("profiling_data.h5", "metadata.json")
73
132
  ```
74
133
 
75
134
  ## Example plots
@@ -202,6 +261,48 @@ ProfileManager.finalize()
202
261
  Both `fibonacci` and `fibonacci_ctx` will report one call per recursive
203
262
  invocation, each with correct, non-overlapping timing data.
204
263
 
264
+ ## Analysing results in Python
265
+
266
+ `ProfilingH5Reader` loads a merged profiling file and behaves like an ordered
267
+ mapping of region name to region. Every duration and timestamp it reports is in
268
+ **seconds**:
269
+
270
+ ```python
271
+ from scope_profiler import ProfilingH5Reader
272
+
273
+ reader = ProfilingH5Reader("profiling_data.h5")
274
+ reader.print_summary()
275
+
276
+ # region calls total [s] avg [s] min [s] max [s]
277
+ # ---------------------------------------------------------------------------
278
+ # setup 1 0.02401 0.02401 0.02401 0.02401
279
+ # timestep 5 0.062835 0.012567 0.0087755 0.0187844
280
+
281
+ solve = reader["solve"] # an MPIRegion: the region across all ranks
282
+ solve.num_calls # summed over ranks
283
+ solve.total_duration # seconds
284
+ solve.average_durations() # {rank: seconds}, for load imbalance
285
+ solve[0].durations # every call on rank 0, as a numpy array
286
+ ```
287
+
288
+ `summary()` returns the same table as a list of dicts, and `to_dataframe()`
289
+ returns it as a pandas DataFrame (one row per region, or per region and rank
290
+ with `per_rank=True`):
291
+
292
+ ```python
293
+ frame = reader.to_dataframe().sort_values("total_duration", ascending=False)
294
+ per_rank = reader.to_dataframe(per_rank=True)
295
+ ```
296
+
297
+ `include` / `exclude` regexes select regions in `get_regions()`, `summary()`,
298
+ `to_dataframe()` and every `plot_*` function.
299
+
300
+ The [tutorial notebooks](tutorials/) cover this in depth:
301
+ [getting started](tutorials/01_getting_started.ipynb),
302
+ [post-processing](tutorials/02_postprocessing.ipynb),
303
+ [visualization](tutorials/03_visualization.ipynb) and
304
+ [profiling modes](tutorials/04_profiling_modes.ipynb).
305
+
205
306
  ## Flame graphs
206
307
 
207
308
  Because each call - including recursive re-entries of the same region -
@@ -222,8 +323,7 @@ scope-profiler pproc profiling_data.h5 --show -o figures
222
323
  Or programmatically:
223
324
 
224
325
  ```python
225
- from scope_profiler.h5reader import ProfilingH5Reader
226
- from scope_profiler.plotting_scripts import plot_flame
326
+ from scope_profiler import ProfilingH5Reader, plot_flame
227
327
 
228
328
  reader = ProfilingH5Reader("profiling_data.h5")
229
329
  plot_flame(reader, filepath="flame_plot.png")
@@ -241,3 +341,44 @@ scope-profiler pproc profiling_data.h5 --cmap viridis -o figures
241
341
  By default the flame graph covers rank 0, since it represents a single
242
342
  execution's call stack; pass `ranks=[...]` to render one flame graph per
243
343
  requested rank.
344
+
345
+ ## Exporting plot data
346
+
347
+ Every `plot_*` function accepts a `data_filepath` argument that writes the
348
+ exact data behind the chart to a file, so it can be re-parsed and re-plotted
349
+ later without the original HDF5 file. `data_format` selects `"csv"` (default)
350
+ or `"json"`:
351
+
352
+ ```python
353
+ plot_gantt(reader, filepath="gantt_plot.png", data_filepath="gantt_data.csv")
354
+ plot_gantt(
355
+ reader,
356
+ filepath="gantt_plot.png",
357
+ data_filepath="gantt_data.json",
358
+ data_format="json",
359
+ )
360
+ ```
361
+
362
+ The JSON payload additionally includes a `colors` map (region or file label
363
+ to `#rrggbb`) matching the colors used in the matplotlib plot, so a
364
+ JavaScript charting library like Plotly can reproduce the same look.
365
+
366
+ `scope-profiler pproc --export-data` does the same for every plot in one
367
+ run, writing `gantt_data`, `flame_data`, `durations_data`, and (for multiple
368
+ input files) `speedup_data` alongside the PNGs. Pass `--export-data-format
369
+ json` to get `.json` files instead of the default `.csv`:
370
+
371
+ ```bash
372
+ scope-profiler pproc profiling_data.h5 -o figures --export-data
373
+ scope-profiler pproc profiling_data.h5 -o figures --export-data --export-data-format json
374
+ ```
375
+
376
+ Pass `--skip-plot-images` (requires `--export-data`) to skip rendering the
377
+ PNGs entirely and only write the exported data plus `region_statistics.json`
378
+ — useful when a website renders charts client-side (e.g. with Plotly)
379
+ straight from the JSON:
380
+
381
+ ```bash
382
+ scope-profiler pproc profiling_data.h5 -o figures \
383
+ --export-data --export-data-format json --skip-plot-images
384
+ ```
@@ -5,7 +5,7 @@ requires = [ "setuptools", "wheel" ]
5
5
 
6
6
  [project]
7
7
  name = "scope-profiler"
8
- version = "0.2"
8
+ version = "0.2.2"
9
9
  description = "Profile code regions in python, optionally with LIKWID markers."
10
10
  readme = "README.md"
11
11
  keywords = [ "python" ]
@@ -35,6 +35,8 @@ optional-dependencies.pproc = [
35
35
  "ipykernel",
36
36
  "jupyterlab",
37
37
  "matplotlib",
38
+ "maxplotlibx >= 0.1.5",
39
+ "pandas",
38
40
  ]
39
41
 
40
42
  optional-dependencies.dev = [
@@ -0,0 +1,49 @@
1
+ """scope-profiler: lightweight region-based profiling for Python and HPC applications."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ from scope_profiler.h5reader import ProfilingH5Reader
6
+ from scope_profiler.mpi_region import MPIRegion
7
+ from scope_profiler.profile_manager import ProfileManager
8
+ from scope_profiler.region import Region
9
+
10
+ try:
11
+ __version__ = version("scope-profiler")
12
+ except PackageNotFoundError:
13
+ __version__ = "unknown"
14
+
15
+ # Plotting pulls in the optional maxplotlib stack, so these are resolved on
16
+ # first access (PEP 562) rather than at import time, keeping
17
+ # `import scope_profiler` cheap inside the applications being profiled.
18
+ _LAZY_PLOTTING = frozenset(
19
+ {
20
+ "collect_region_statistics",
21
+ "plot_durations",
22
+ "plot_flame",
23
+ "plot_gantt",
24
+ "plot_speedup",
25
+ "write_region_statistics_json",
26
+ }
27
+ )
28
+
29
+ __all__ = [
30
+ "MPIRegion",
31
+ "ProfileManager",
32
+ "ProfilingH5Reader",
33
+ "Region",
34
+ *sorted(_LAZY_PLOTTING),
35
+ ]
36
+
37
+
38
+ def __getattr__(name: str):
39
+ """Resolve the plotting helpers lazily; see ``_LAZY_PLOTTING``."""
40
+ if name in _LAZY_PLOTTING:
41
+ from scope_profiler import plotting_scripts
42
+
43
+ return getattr(plotting_scripts, name)
44
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
45
+
46
+
47
+ def __dir__() -> list:
48
+ """Include the lazily-resolved plotting helpers in ``dir()``."""
49
+ return sorted(set(globals()) | _LAZY_PLOTTING)
@@ -2,7 +2,7 @@
2
2
 
3
3
  Also runnable as ``python -m scope_profiler <command> ...``.
4
4
 
5
- Two subcommands:
5
+ Three subcommands:
6
6
 
7
7
  - ``scope-profiler run script.py [args...]`` -- profiles a script's function
8
8
  calls without requiring any decorators or context managers in the script
@@ -12,6 +12,9 @@ Two subcommands:
12
12
  - ``scope-profiler pproc file.h5 [...]`` -- reads merged HDF5 profiling
13
13
  output and renders Gantt/flame/duration/speedup charts. See
14
14
  ``scope_profiler.post_processing`` for its full set of options.
15
+ - ``scope-profiler inspect file.h5 [...]`` -- prints the run metadata and a
16
+ per-region statistics table for merged HDF5 profiling output, without
17
+ producing any plots. See ``scope_profiler.inspection``.
15
18
  """
16
19
 
17
20
  import argparse
@@ -47,8 +50,8 @@ def _parse_run_args(argv):
47
50
  parser.add_argument(
48
51
  "--buffer-limit",
49
52
  type=int,
50
- default=100_000,
51
- help="Max buffered calls per region before flushing to disk (default: 100000)",
53
+ default=1024,
54
+ help="Initial buffer capacity per region; grows as needed (default: 1024)",
52
55
  )
53
56
  parser.add_argument("script", help="Script to run and profile")
54
57
  parser.add_argument(
@@ -97,9 +100,17 @@ def _pproc(argv):
97
100
  return pproc_main(argv)
98
101
 
99
102
 
103
+ def _inspect(argv):
104
+ """Handle ``scope-profiler inspect``: delegate to the inspection CLI."""
105
+ from scope_profiler.inspection import main as inspect_main
106
+
107
+ return inspect_main(argv)
108
+
109
+
100
110
  _COMMANDS = {
101
111
  "run": _run,
102
112
  "pproc": _pproc,
113
+ "inspect": _inspect,
103
114
  }
104
115
 
105
116
 
@@ -123,6 +134,12 @@ def main(argv=None):
123
134
  help="Post-process and plot HDF5 profiling data "
124
135
  "(see `scope-profiler pproc --help`)",
125
136
  )
137
+ subparsers.add_parser(
138
+ "inspect",
139
+ add_help=False,
140
+ help="Print metadata and region statistics of HDF5 profiling data "
141
+ "(see `scope-profiler inspect --help`)",
142
+ )
126
143
 
127
144
  if not argv:
128
145
  parser.print_help()