--- title: tinykernel keywords: fastai sidebar: home_sidebar summary: "A minimal Python kernel, so you can run Python in your Python." description: "A minimal Python kernel, so you can run Python in your Python." nb_path: "index.ipynb" ---
{% raw %}
{% endraw %}

All the clever stuff in this library is provided by Python's builtin ast module and compilation/exec/eval system, along with IPython's CachingCompiler which does some deep magic. tinykernel just brings them together with a little glue.

Install

With pip:

pip install tinykernel

With conda:

conda install -c fastai tinykernel

How to use

This library provides a single class, TinyKernel, which is a tiny persistent kernel for Python code:

{% raw %}
k = TinyKernel()
{% endraw %}

Call it, passing Python code, to have the code executed in a separate Python environment:

{% raw %}
k("a=1")
{% endraw %}

Expressions return the value of the expression:

{% raw %}
k('a')
1
{% endraw %}

All variables are persisted across calls:

{% raw %}
k("a+=1")
k('a')
2
{% endraw %}

Multi-line inputs are supported. If the last line is an expression, it is returned:

{% raw %}
k("""import types
b = types.SimpleNamespace(foo=a)
b""")
namespace(foo=2)
{% endraw %}

The original source code is stored, so inspect.getsource works and, tracebacks have full details.

{% raw %}
k("""def f(): pass # a comment
import inspect
inspect.getsource(f)""")
'def f(): pass # a comment\n'
{% endraw %}

When creating a TinyKernel, you can pass a dict of globals to initialize the environment:

{% raw %}
k = TinyKernel(glb={'foo':'bar'})
k('foo*2')
'barbar'
{% endraw %}

Pass name to customize the string that appears in tracebacks ("kernel" by default):

{% raw %}
k = TinyKernel(name='myapp')
code = '''def f():
    return 1/0
print(f())'''
try: k(code)
except Exception as e: print(traceback.format_exc())
Traceback (most recent call last):
  File "<ipython-input-37-8b370e64c5cb>", line 5, in <module>
    try: k(code)
  File "/home/jhoward/git/tinykernel/tinykernel/__init__.py", line 20, in __call__
    if expr: return self._run(Expression(expr.value), nm, 'eval')
  File "/home/jhoward/git/tinykernel/tinykernel/__init__.py", line 12, in _run
    def _run(self, p, nm, mode='exec'): return eval(compiler(p, nm, mode), self.glb)
  File "<myapp--1-57331cf14e08>", line 3, in <module>
    print(f())
  File "<myapp--1-57331cf14e08>", line 2, in f
    return 1/0
ZeroDivisionError: division by zero

{% endraw %}

Acknowledgements

Thanks to Christopher Prohm, Matthias Bussonnier, and Aaron Meurer for their helpful insights in this twitter thread.