# core


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

Sometimes when building FastHTML apps we run into performance
bottlenecks. Figuring out what is slow can be challenging, especially
when building apps with async components. That’s where profiling tools
like pyinstrument can help. Profilers are tools that show exactly how
long each component of a project takes to run. Identifying slow parts of
an app is the first step in figuring out how to make things run faster.

``` python
from starlette.testclient import TestClient
from fastcore.all import *
from functools import partialmethod
from anyio import from_thread
```

------------------------------------------------------------------------

### get_trigger_name

``` python

def get_trigger_name(
    
):

```

*Call self as a function.*

------------------------------------------------------------------------

### ProfileMiddleware

``` python

def ProfileMiddleware(
    app, save_dir:NoneType=None
):

```

*Add the power of pyinstrument to potentially every request.*

------------------------------------------------------------------------

### profiling

``` python

def profiling(
    
):

```

*Call self as a function.*

------------------------------------------------------------------------

### instrument

``` python

def instrument(
    route_handler
):

```

*Replaces the route handler’s output with pyinstrument results.*

``` python
def slow(): time.sleep(0.01)

@instrument
def my_route(): slow()

res = await my_route()
assert 'slow' in res and 'pyinstrument' in res.lower()
res[:50]
```

    '<!DOCTYPE html>\n            <html>\n            <he'

------------------------------------------------------------------------

### load_session

``` python

def load_session(
    path
):

```

*Load a saved pyinstrument session and return a renderer-ready object.*

``` python
app, rt = fast_app()
app.add_middleware(ProfileMiddleware, save_dir='/tmp/profiles')
client = TestClient(app)

@rt
def index(): return Titled('Hello, profiler')
```

``` python
'pyinstrumentHTMLRenderer' in client.get('/?profile=1').text
```

    True

------------------------------------------------------------------------

### Session.flat

``` python

def flat(
    paths:NoneType=None, n:int=20
):

```

*Aggregate self-time by function, optionally filtered by file path
substrings.*

`flat` walks the frame tree and sums `total_self_time` per unique
(function, file, line) tuple. Filter with `paths` to focus on specific
codebases. Returns a sorted list of `ProfileEntry` named tuples.

``` python
sess = load_session(sorted(Path('/tmp/profiles').glob('*.pkl'))[-1])
for e in sess.flat(n=5): print(f'{e.time*1000:7.1f}ms  {e.func}  {e.file}:{e.line}')
```

<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">    <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">1.</span>7ms  run_sync_in_worker_thread  
<span style="color: #800080; text-decoration-color: #800080">/Users/jhoward/aai-ws/.venv/lib/python3.12/site-packages/anyio/_backends/</span><span style="color: #ff00ff; text-decoration-color: #ff00ff">_asyncio.py</span>:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">2459</span>
</pre>

<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">    <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">1.</span>7ms    :<span style="color: #800080; text-decoration-color: #800080; font-style: italic">None</span>
</pre>

<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">    <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">1.</span>0ms  getcoroutinestate  
<span style="color: #800080; text-decoration-color: #800080">/Users/jhoward/.local/share/uv/python/cpython-3.12.0-macos-aarch64-none/lib/python3.12/</span><span style="color: #ff00ff; text-decoration-color: #ff00ff">inspect.py</span>:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">1919</span>
</pre>

<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">    <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">1.</span>0ms    :<span style="color: #800080; text-decoration-color: #800080; font-style: italic">None</span>
</pre>

------------------------------------------------------------------------

### Session.callers

``` python

def callers(
    func_name, paths:NoneType=None, n:int=10
):

```

*Find which functions call `func_name` and how much time they
contribute.*

`callers` answers “who is calling this hot function?”. For each
occurrence of `func_name` with self-time, it attributes that time to the
immediate parent frame.

``` python
for e in sess.callers('getattr', n=5): print(f'{e.time*1000:7.1f}ms  {e.func}  {e.file}:{e.line}')
```

------------------------------------------------------------------------

### Session.callees

``` python

def callees(
    func_name, paths:NoneType=None, n:int=10
):

```

*Find what `func_name` spends its time calling.*

`callees` answers “what does this function spend its time on?”. It walks
descendants of matching frames and aggregates their self-time.

``` python
for e in sess.callees('getattr', n=5): print(f'{e.time*1000:7.1f}ms  {e.func}  {e.file}:{e.line}')
```

------------------------------------------------------------------------

### Session.hot_paths

``` python

def hot_paths(
    paths:NoneType=None, n:int=10, depth:int=8
):

```

*Top call stacks by cumulative time, filtered to frames matching
`paths`.*

`hot_paths` shows the most expensive call stacks, collapsed to only
frames matching `paths`. The `depth` parameter limits stack depth to
keep output readable.

``` python
ps = ['solveit/', 'fasthtml/', 'fastcore/']
for t,s in sess.hot_paths(paths=ps, n=5): print(f'{t*1000:7.1f}ms  {s}')
```

<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">    <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">3.</span>4ms  _f core.py:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">669</span> → _wrap_call core.py:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">478</span> → _handle core.py:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">269</span>
</pre>

------------------------------------------------------------------------

### render_session

``` python

def render_session(
    sess, text:bool=True, show_all:bool=False, short_mode:bool=True
):

```

*Render a saved session as text or html.*

## Tests

First, confirm that the view works normally

``` python
assert 'Hello, profiler' in client.get('/').text
```

Now lets profile it! Or rather, check that it works.

``` python
'pyinstrumentHTMLRenderer' in client.get('/?profile=1').text
```

    True

Let’s print to the terminal

``` python
client.get('/?profile=1&term=1')
```

<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">
  _     ._   __/__   _ _  _  _ _/_   Recorded: <span style="color: #00ff00; text-decoration-color: #00ff00; font-weight: bold">13:41:32</span>  Samples:  <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">1</span>
 <span style="color: #800080; text-decoration-color: #800080">/_//_///</span> <span style="color: #800080; text-decoration-color: #800080">/</span><span style="color: #ff00ff; text-decoration-color: #ff00ff">_</span>\ <span style="color: #800080; text-decoration-color: #800080">/</span> <span style="color: #800080; text-decoration-color: #800080">//_//</span> <span style="color: #800080; text-decoration-color: #800080">/</span> <span style="color: #800080; text-decoration-color: #800080">//</span><span style="color: #ff00ff; text-decoration-color: #ff00ff">_</span>'<span style="color: #800080; text-decoration-color: #800080">/</span> <span style="color: #800080; text-decoration-color: #800080">//</span>     Duration: <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span>     CPU time: <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span>
<span style="color: #800080; text-decoration-color: #800080">/</span>   _/                      v5.<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">1.2</span>
&#10;Profile at <span style="color: #800080; text-decoration-color: #800080">/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/ipykernel_92920/</span><span style="color: #ff00ff; text-decoration-color: #ff00ff">363051617.py</span>:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">18</span>
&#10;<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span> Handle._run  asyncio/events.py:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">82</span>
`- <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span> coro  starlette/middleware/base.py:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">139</span>
      <span style="font-weight: bold">[</span><span style="color: #008080; text-decoration-color: #008080; font-weight: bold">9</span> frames hidden<span style="font-weight: bold">]</span>  starlette
         <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span> app  starlette/routing.py:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">60</span>
         `- <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span> _f  ..<span style="color: #800080; text-decoration-color: #800080">/fasthtml/fasthtml/</span><span style="color: #ff00ff; text-decoration-color: #ff00ff">core.py</span>:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">669</span>
            `- <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span> _wrap_call  ..<span style="color: #800080; text-decoration-color: #800080">/fasthtml/fasthtml/</span><span style="color: #ff00ff; text-decoration-color: #ff00ff">core.py</span>:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">478</span>
               `- <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span> _handle  ..<span style="color: #800080; text-decoration-color: #800080">/fasthtml/fasthtml/</span><span style="color: #ff00ff; text-decoration-color: #ff00ff">core.py</span>:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">269</span>
                  `- <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span> run_in_threadpool  starlette/concurrency.py:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">30</span>
                     `- <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span> run_sync  anyio/to_thread.py:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">25</span>
                        `- <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span> AsyncIOBackend.run_sync_in_worker_thread  anyio/_backends/_asyncio.py:<span style="color: #008080; text-decoration-color: #008080; font-weight: bold">2459</span>
                           `- <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">0.001</span>   anyio/_backends/_asyncio.py
&#10;
</pre>

    <Response [200 OK]>

``` python
@rt
@instrument
def saxaphone(): return Titled('Play that sweet horn')
assert 'pyinstrumentHTMLRenderer' in client.get('/saxaphone').text
```

``` python
@rt
@instrument
async def trombone(): return Titled('Async horn')
assert 'pyinstrumentHTMLRenderer' in client.get('/trombone').text
```
