# pool


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

Keep one kernel per key and enforce a shared kernel limit.

The host-prefixed `KERNEL_IDLE` environment variable overrides
`IDLE_SECONDS`.

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

<a
href="https://github.com/vedicreader/kunda/blob/main/kunda/pool.py#L21"
target="_blank" style="float:right; font-size:smaller">source</a>

### KernelLimit

``` python
def KernelLimit(
    msg, candidate:NoneType=None, limit:int=0
):
```

*The ceiling is reached. `candidate` is a kernel that can be offered to
close, or None while all are busy.*

[`KernelLimit`](https://vedicreader.github.io/kunda/pool.html#kernellimit)
reports the configured limit and an optional kernel that could be
closed.

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

<a
href="https://github.com/vedicreader/kunda/blob/main/kunda/pool.py#L28"
target="_blank" style="float:right; font-size:smaller">source</a>

### RuntimeBroker

``` python
def RuntimeBroker(
    max_kernels:NoneType=None, auto_manage:NoneType=None
):
```

*A ceiling across every pool in this process. Never evicts a namespace
somebody is using.*

[`RuntimeBroker`](https://vedicreader.github.io/kunda/pool.html#runtimebroker)
counts live kernels across registered pools. `admit` raises
[`KernelLimit`](https://vedicreader.github.io/kunda/pool.html#kernellimit)
when the next kernel would exceed `max_kernels`.

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

<a
href="https://github.com/vedicreader/kunda/blob/main/kunda/pool.py#L75"
target="_blank" style="float:right; font-size:smaller">source</a>

### KernelPool

``` python
def KernelPool(
    port:int=8000, default_kernel:str='ipykernel', default_python:NoneType=None, broker:NoneType=None,
    transport:str='direct', gateway:NoneType=None, idle:NoneType=None, runner_for:NoneType=None,
    known_kernels:NoneType=None, install_hints:NoneType=None
):
```

*Live kernels, keyed by an id the host assigns: usually a tab, a
notebook or a folder.*

[`KernelPool.get`](https://vedicreader.github.io/kunda/pool.html#kernelpool.get)
starts at most one kernel for each key. Concurrent callers wait for the
same start. Failed starts are not stored.

``` python
p = KernelPool()
p.choose(kernel='ipymini', python='/proj/.venv/bin/python')
```

    {'kernel': 'ipymini', 'python': '/proj/.venv/bin/python'}

`_class_for` selects the runner for a language and transport. The
gateway transport supports Python only.

``` python
gw = KernelPool(transport='gateway')
(gw._class_for({'lang': 'python'}).__name__, gw._class_for({'lang': 'rust'}).__name__,
 KernelPool(transport='carrier-pigeon').transport)
```

    ('GatewayKernel', 'Kernel', 'direct')

`reap` closes kernels idle for at least `idle` seconds. Busy kernels are
kept. `idle=0` disables reaping.

``` python
pool = KernelPool(idle=60)
pool.kernels.update(stale=Fake('/proj/stale', idle_for=90), fresh=Fake('/proj/fresh', idle_for=5),
                    working=Fake('/proj/working', idle_for=900, busy=True))
await pool.reap(), list(pool.kernels)
```

    (['stale'], ['fresh', 'working'])

The examples use stand-in kernels and do not start child processes.

``` python
b = RuntimeBroker(max_kernels=2, auto_manage=False)
pool = KernelPool(idle=0, broker=b)
pool.kernels.update(one=Fake('/proj/one', idle_for=90), two=Fake('/proj/two', busy=True))
b.status()
```

    {'live': 2,
     'limit': 2,
     'runtimes': [{'key': 'one',
       'pid': 4242,
       'kind': 'ipykernel',
       'cwd': '/proj/one'},
      {'key': 'two', 'pid': 4242, 'kind': 'ipykernel', 'cwd': '/proj/two'}]}

`status` lists current kernels. `_candidate` selects the stalest idle
kernel that could be closed.

``` python
b.stalest()[1], b._candidate()
```

    ('one',
     {'key': 'one',
      'name': 'one',
      'cwd': '/proj/one',
      'pid': 4242,
      'idle_for': 90})

Without automatic management, `admit` reports a candidate and closes
nothing.

``` python
try: await b.admit(pool, 'three')
except KernelLimit as e: print(e); print(e.candidate['name'], e.limit)
```

    kernel limit reached (2); close an idle runtime or set KUNDA_MAX_KERNELS to a larger value
    one 2

When every kernel is busy, `candidate` is `None`.

``` python
pool.kernels['one'].busy = True
try: await b.admit(pool, 'three')
except KernelLimit as e: print(e); print(e.candidate, b.stalest())
```

    kernel limit reached (2) and every runtime is busy; stop one or set KUNDA_MAX_KERNELS to a larger value
    None None

With `auto_manage=True`, `admit` closes the stalest idle kernel and
returns its description.

``` python
auto = RuntimeBroker(max_kernels=1, auto_manage=True)
p2 = KernelPool(idle=0, broker=auto)
p2.kernels['one'] = Fake('/proj/one', idle_for=90)
evicted = await auto.admit(p2, 'two')
evicted['key'], list(p2.kernels), auto.status()['live']
```

    ('one', [], 0)

The pool passes `known_kernels` and `install_hints` to local Kunda
kernels.

``` python
p3 = KernelPool(idle=0, known_kernels={'rust': 'evcxr'}, install_hints={'rust': 'cargo install evcxr_jupyter'})
err = ''
try: await p3.get('rs', lang='rust')
except KernelStartError as e: err = str(e)
err
```

    'no Jupyter kernel is installed for rust. Install one with `cargo install evcxr_jupyter`.'

Pool and broker environment variables use the host prefix set by
[`use_app`](https://vedicreader.github.io/kunda/pythons.html#use_app).
