# scope_profiler.prof_export

*module*

Export merged HDF5 profiling data to the cProfile ``.prof`` (pstats) format.

A ``.prof`` file is nothing more than a :mod:`marshal` dump of the dict that
``cProfile.Profile.dump_stats`` writes: it maps a ``(filename, lineno,
funcname)`` key to ``(cc, nc, tt, ct, callers)``, where ``callers`` maps a
caller key to its own ``(cc, nc, tt, ct)`` sub-tuple. Writing that dict is
enough for :mod:`pstats`, ``snakeviz`` and friends to read the data.

Regions carry no call graph of their own, so the caller/callee relations are
reconstructed from timestamp containment
(:func:`~scope_profiler.call_stack.build_call_arrays`). The reconstruction is
consumed as columns rather than as one dict per call: a long run can export
millions of events while the pstats dictionary holds one entry per distinct
call path (or, on request, per region name).

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L1-L1)

## scope_profiler.prof_export.NS_PER_SECOND

*attribute* · *module attribute*

```python
NS_PER_SECOND = 1000000000.0
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L44-L44)

## scope_profiler.prof_export.PSEUDO_FILENAME

*attribute* · *module attribute*

```python
PSEUDO_FILENAME = '~'
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L41-L41)

## scope_profiler.prof_export.PSEUDO_LINENO

*attribute* · *module attribute*

```python
PSEUDO_LINENO = 0
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L42-L42)

## scope_profiler.prof_export.ScopeProfile

*class*

```python
class ScopeProfile(cProfile.Profile)
```

Bases: `cProfile.Profile`

A :class:`cProfile.Profile` carrying reconstructed region stats.

``pstats.Stats`` only accepts a filename or an object with a
``create_stats()`` method that leaves the finished pstats dict in
``.stats`` (see ``pstats.Stats.load_stats``) - ``cProfile.Profile`` is
the real class satisfying that contract. Subclassing it, instead of
merely duck-typing it, lets :func:`build_pstats` hand ``pstats.Stats`` a
type it actually declares support for, and skips ``Profile.__init__``
(which would start a C-level profiler this class never uses).

Inherited ``Profile`` methods that only need ``.stats`` work as usual, so
``ScopeProfile(stats).dump_stats(path)`` writes the same ``.prof`` file
:func:`write_prof_file` does. Like a real ``Profile``, an instance is
consumed by ``pstats.Stats``: it takes ``.stats`` and leaves the profile
empty, so build a new one per ``Stats``.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L304-L326)

### scope_profiler.prof_export.ScopeProfile.stats

*attribute* · *instance attribute*

```python
stats = stats
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L323-L323)

### scope_profiler.prof_export.ScopeProfile.create_stats

*method*

```python
def create_stats() -> None
```

No-op: ``self.stats`` is already the finished pstats dict.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L325-L326)

## scope_profiler.prof_export.build_pstats

*function*

```python
def build_pstats(calls: CallArrays, root_name: str | None = None, call_paths: bool = True) -> pstats.Stats
```

Build reconstructed calls straight into a :class:`pstats.Stats`.

The in-memory twin of :func:`build_pstats_dict`, for callers that want to
``sort_stats``/``print_stats`` immediately instead of writing a ``.prof``
file first.  A run with no calls yields an empty ``Stats``, which
``pstats`` would otherwise refuse to construct from an empty dict.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L329-L344)

## scope_profiler.prof_export.build_pstats_dict

*function*

```python
def build_pstats_dict(calls: CallArrays, root_name: str | None = None, call_paths: bool = True) -> dict[tuple[str, int, str], tuple]
```

Turn reconstructed calls into a pstats-format statistics dict.

Parameters
----------
calls : CallArrays
    Calls as returned by
    :func:`~scope_profiler.call_stack.build_call_arrays`.
root_name : str, optional
    When given, a synthetic frame of this name is added as the caller of
    every top-level call, so viewers that draw a single tree (snakeviz)
    show the whole run instead of only its largest region.
call_paths : bool, optional
    Keep a separate pstats entry for each parent/child path, naming it
    ``"parent > child"``. This makes tree viewers such as SnakeViz show
    distinct uses of a region instead of merging every call named
    ``"child"``. Set this to ``False`` for a compact, name-aggregated
    export.

Returns
-------
dict
    Maps ``(filename, lineno, funcname)`` to
    ``(cc, nc, tt, ct, callers)`` with times in seconds.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L181-L289)

## scope_profiler.prof_export.export_flamegraph_svg

*function*

```python
def export_flamegraph_svg(profiling_data: ProfilingResults | Sequence[ProfilingResults], filepath: str | Path, ranks: list[int] | int | None = None, include: list[str] | str | None = None, exclude: list[str] | str | None = None, call_paths: bool = True, width: int = 1200, threshold: float = 0.001, scalable: bool = True, verbose: bool = True) -> list[Path]
```

Render per-rank flame graphs to standalone SVG files with flameprof.

The aggregated, call-stack view of the same reconstruction
:func:`export_prof` writes: every call of a region below one parent is
one frame, sized by cumulative time. :func:`~scope_profiler.plot_flame`
draws the same thing through the plotting stack; this goes through
``pstats`` instead, so the result is a self-contained SVG with no
JavaScript, which is what a static site or an emailed report wants.

Requires ``flameprof`` (``pip install "scope-profiler[pproc]"``).

Parameters
----------
filepath : str | Path
    Base output path, e.g. ``figures/flamegraph.svg``; a ``_rank<N>``
    suffix is appended per rank, as for :func:`export_prof`.
width : int
    Width of the rendered document in pixels. With ``scalable`` this only
    sets the aspect ratio and the point at which frame labels are elided.
threshold : float
    Smallest fraction of total time a frame must take to be drawn
    (flameprof's own default, 0.001, is a tenth of a percent).
scalable : bool
    Replace the fixed pixel size with a ``viewBox`` so the graph scales to
    whatever element it is placed in, and drop the XML prolog so it can be
    inlined in an HTML page. Set ``False`` for a standalone SVG file.

Returns
-------
list[Path]
    The files written, in the order they were written.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L465-L554)

## scope_profiler.prof_export.export_prof

*function*

```python
def export_prof(profiling_data: ProfilingResults | Sequence[ProfilingResults], filepath: str | Path, ranks: list[int] | int | None = None, include: list[str] | str | None = None, exclude: list[str] | str | None = None, call_paths: bool = True, verbose: bool = True) -> list[Path]
```

Write per-rank ``.prof`` files readable by ``pstats``/``snakeviz``.

Parameters
----------
profiling_data : ProfilingResults | Sequence[ProfilingResults]
    The run(s) to export: file runs, in-memory results from
    ``ProfileManager.finalize(return_results=True)``, or a mix.
filepath : str | Path
    Base output path, e.g. ``figures/profile.prof``. A ``_rank<N>`` suffix
    is appended per rank (and the input file's stem too, when more than one
    file is exported), since ``.prof`` has no notion of ranks or runs.
ranks : list[int] | int, optional
    Ranks to export (default: rank 0 only).
include, exclude : list[str] | str, optional
    Region name filters, as for the plotting functions.
call_paths : bool, optional
    Preserve each reconstructed parent/child path as a separate node in
    the exported pstats tree.  This makes SnakeViz distinguish same-named
    regions called below different parents. Set to ``False`` for a
    compact, name-aggregated export.
verbose : bool, optional
    Print each file written, with the ``snakeviz`` command that opens it.

Returns
-------
list[Path]
    The files written, in the order they were written.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L557-L617)

## scope_profiler.prof_export.load_prof

*function*

```python
def load_prof(filepath: str | Path) -> pstats.Stats
```

Read a ``.prof`` file written by :func:`export_prof` back as a Stats.

The ``.prof`` twin of :func:`~scope_profiler.json_export.read_json`.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L347-L352)

## scope_profiler.prof_export.to_pstats

*function*

```python
def to_pstats(profiling_data: ProfilingResults | Sequence[ProfilingResults], ranks: list[int] | int | None = None, include: list[str] | str | None = None, exclude: list[str] | str | None = None, call_paths: bool = True) -> dict[tuple[str, int], pstats.Stats]
```

Build a :class:`pstats.Stats` per (run label, rank), without disk I/O.

The in-memory twin of :func:`export_prof`: same call reconstruction and
filtering, but returns real ``pstats.Stats`` objects keyed by
``(run_label, rank)`` instead of writing ``.prof`` files.

Parameters
----------
profiling_data : ProfilingResults | Sequence[ProfilingResults]
    The run(s) to export: file runs, in-memory results from
    ``ProfileManager.finalize(return_results=True)``, or a mix.
ranks : list[int] | int, optional
    Ranks to export (default: rank 0 only).
include, exclude : list[str] | str, optional
    Region name filters, as for the plotting functions.
call_paths : bool, optional
    Preserve each reconstructed parent/child path as a separate node in
    the resulting tree; see :func:`build_pstats_dict`.

Returns
-------
dict[tuple[str, int], pstats.Stats]
    Maps ``(run_label, rank)`` to its ``pstats.Stats``, empty on ranks
    that own no files.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L393-L433)

## scope_profiler.prof_export.write_prof_file

*function*

```python
def write_prof_file(filepath: str | Path, stats: dict[tuple[str, int, str], tuple]) -> Path
```

Marshal a pstats dict to ``filepath``, as ``cProfile`` would.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/prof_export.py#L292-L301)
