# scope_profiler.ipython_magics

*module*

Jupyter/IPython magics for scope-profiler.

Load with::

    %load_ext scope_profiler.ipython_magics

This is a thin adapter over the same API the rest of scope-profiler uses --
``ProfileManager.session``/``profile_region`` for measuring, ``ProfilingResults
.print_summary`` for the table, and :mod:`scope_profiler.diff` for comparisons.
Nothing about timing, aggregation, or comparing runs is reimplemented here.

Importing this module requires IPython. It is not a dependency of the base
package (``pip install scope-profiler`` alone does not pull it in) - install
it with the ``notebook`` extra: ``pip install "scope-profiler[notebook]"``.

Eleven magics are registered:

``%%scope [name] [-q] [-p] [--include PATTERN]``
    Cell magic. Runs the cell inside a profiling region named ``name``
    (default ``"cell"``) and prints its summary table. The result is kept
    in memory under ``name`` for ``%scope_last``/``%scope_compare``.
``%scope_timeit [-n N] [-q] statement``
    Line magic. Runs ``statement`` ``N`` times (default 7) inside a
    profiling region named ``"timeit"`` and prints per-call timing
    statistics - like ``%timeit``, but backed by scope-profiler's region
    timer and table instead of the ``timeit`` module.
``%%scope_line [name] [-q] [-Q]``
    Cell magic. Runs the cell with ``use_line_profiler=True`` and prints
    per-line timing for whatever the cell itself profiles - a
    ``@ProfileManager.profile``-decorated function or a
    ``with ProfileManager.profile_region(...):`` block written in the cell,
    exactly as in a script.
``%%scope_recursive [name] [-q] [--include PATTERN]``
    Cell magic. Records every Python call the cell makes as its own region,
    with no decorators or ``profile_region`` blocks - ``recursive_profile
    =True``, as ``scope-profiler run`` applies it to a script.
``%%scope_agg [name] [-q] [--include PATTERN]``
    Cell magic. Runs the cell with ``aggregation_mode=True``: counts,
    totals and min/max per region, no per-call timeline, for cells that
    enter a region far too often to store every event.
``%scope_load filepath [-n name] [-q]``
    Load an HDF5 run produced elsewhere (an MPI job, ``scope-profiler
    run``) into the same registry, so the other magics can compare against
    it.
``%scope_df [-n name] [--events] [--per-rank] [--include PATTERN]``
    Return a recorded run as a pandas DataFrame, for analysis in the
    notebook rather than a printed table.
``%scope_last [name] [-p] [--include PATTERN]``
    Reprint the summary table for a previous ``%%scope``/``%scope_timeit``
    run. Defaults to the most recently recorded one.
``%scope_compare [name_a name_b] [--metric METRIC] [--sort KEY]``
    Compare two previous runs region-by-region with
    ``scope_profiler.diff.diff_rows``/``print_diff_table``. With no names,
    compares the two most recently recorded runs.
``%scope_export filepath [-n name] [--format prof|speedscope]``
    Export a previous run to a ``.prof`` (pstats/snakeviz) or speedscope
    JSON file via ``scope_profiler.prof_export``/``speedscope_export``.
    Format defaults to whatever ``filepath``'s extension implies.
    ``--no-call-paths`` makes the ``.prof`` one entry per region instead of
    one per ``parent > child`` call path.
``%scope_reset [name]``
    Drop one recorded run, or with no argument, every run recorded so far.

Every run uses ``deactivate_file_output=True``: nothing is written to disk,
results live only in the kernel's memory for the duration of the session.

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

## scope_profiler.ipython_magics.ScopeMagics

*class*

```python
class ScopeMagics(Magics)
```

Bases: `Magics`

IPython magics wrapping :class:`~scope_profiler.ProfileManager`.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_agg_cell

*method*

```python
def scope_agg_cell(line, cell)
```

Profile a cell in aggregation mode: counts and totals, no timeline.

``aggregation_mode=True`` keeps only each region's count, inclusive
total, min/max and exclusive total instead of every call's
timestamps, so a cell that enters a region millions of times stays
cheap and bounded in memory. The per-call event data the Gantt
chart, ``%scope_df --events`` and the timeline exports need is not
recorded; the summary table and ``%scope_compare`` work as usual.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_cell

*method*

```python
def scope_cell(line, cell)
```

Profile a cell as one region and print its summary table.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_compare

*method*

```python
def scope_compare(line)
```

Compare two previous runs region-by-region.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_df

*method*

```python
def scope_df(line)
```

Return a recorded run as a pandas DataFrame.

The frame is the magic's return value, so it renders as a table on
its own and can be captured for further analysis::

    df = %scope_df
    df.nlargest(5, "total_duration")

Needs pandas (the ``pproc`` extra). ``--events`` needs per-call event
data, which an aggregation-mode run (``%%scope_agg``) does not record
- that comes back as an empty frame, with a note saying why.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_export

*method*

```python
def scope_export(line)
```

Export a previous run to a .prof or speedscope JSON file.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_last

*method*

```python
def scope_last(line)
```

Reprint the summary table for a previous %%scope/%scope_timeit run.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_line_cell

*method*

```python
def scope_line_cell(line, cell)
```

Profile a cell with line_profiler enabled and print per-line stats.

Only what the cell itself hands to line_profiler gets a line-by-line
breakdown - exactly as in a script (see
``examples/ex_line_profiling.py``). A cell with neither still gets
the usual region summary table.

Prefer decorating the function you care about with
``@ProfileManager.profile``: it registers cleanly regardless of
where it is called from. A bare ``with ProfileManager.profile_region
(...):`` at the cell's top level also works, but line_profiler's own
handling of a module-level code object containing a nested ``def``
can cut the printed table off at that ``def`` - the same limitation
a plain script hits, not something specific to the magic.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_load

*method*

```python
def scope_load(line)
```

Load a profiling file into the notebook's recorded runs.

Puts a run produced outside this notebook - an MPI job, a
``scope-profiler run``, a colleague's file - under the same name
registry as ``%%scope``, so ``%scope_last``, ``%scope_compare``,
``%scope_df`` and ``%scope_export`` treat it like any cell's run.
That makes "is my notebook version faster than the cluster run?" a
one-liner.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_recursive_cell

*method*

```python
def scope_recursive_cell(line, cell)
```

Profile every Python call the cell makes, with no instrumentation.

The cell needs no decorators and no ``profile_region`` blocks: each
function called while it runs becomes its own region, named
``<module>.<qualname>`` - what ``recursive_profile=True`` does for a
decorated function, and what ``scope-profiler run`` does for a
script. Use it to find *where* a cell's time goes before deciding
what to instrument properly.

As in a script, tracing is not limited to your own functions:
library internals called from the cell are recorded too, which is
thorough but noisy and adds per-call overhead. Keep the cell small,
and use ``--include`` to filter the table.

The cell runs via ``exec`` rather than IPython's own execution, so a
trailing expression is not echoed as ``Out[n]``; assignments land in
the notebook namespace as usual.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_reset

*method*

```python
def scope_reset(line)
```

Drop one recorded run, or every recorded run with no argument.

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

### scope_profiler.ipython_magics.ScopeMagics.scope_timeit

*method*

```python
def scope_timeit(line)
```

Run a statement N times inside a profiling region and print timing stats.

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

## scope_profiler.ipython_magics.load_ipython_extension

*function*

```python
def load_ipython_extension(ipython) -> None
```

Register the magics: ``%load_ext scope_profiler.ipython_magics``.

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