Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tag Based Parameters #23

Closed
wants to merge 16 commits into from
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ This is a tool to run notebooks with input values. When you write the notebook,
these are defined in the first code cell, with regular assignments like this:

.. code-block:: python

stock = 'YHOO'
days_back = 600

Expand All @@ -27,6 +26,7 @@ interface, can be stored in notebook metadata.
Nbparameterise is written in Python 3, but it can handle notebooks that use
Python 2.


Usage:

.. code-block:: python
Expand Down
79 changes: 79 additions & 0 deletions examples/Coin Price.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Coin Prices displays. Code cribbed from [this notebook](http://nbviewer.ipython.org/github/twiecki/financial-analysis-python-tutorial/blob/master/1.%20Pandas%20Basics.ipynb) by [Thomas Wiecki](https://github.com/twiecki)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"Parameters"
]
},
"outputs": [],
"source": [
"COIN = ['bitcoin','eth','doge'] # Display names are stored in notebook metadata"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import requests\n",
"try:\n",
" headers = {\n",
" 'X-Mboum-Secret': \"demo\"\n",
" }\n",
" res = requests.get(\n",
" f\"https://mboum.com/api/v1/cr/crypto/coin/quote?key={COIN}\", \n",
" headers=headers\n",
" )\n",
" data = res.json()['data']\n",
" for key in data:\n",
" print(key,\"\\t\", data[key])\n",
"except Exception as e:\n",
" print(e)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
},
"parameterise": {
"stock": {
"display_name": "Stock symbol"
}
}
},
"nbformat": 4,
"nbformat_minor": 1
}
6 changes: 6 additions & 0 deletions examples/htmlform.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ def __init__(self, *children, css_classes=None, id=None):
def make_input_element(var):
if var.type is bool:
input_elm = Checkbox(var.name, var.value)

elif var.type is list:
input_elm = htmlgen.Select(name=var.name)
for item in var.value:
input_elm.create_option(item,item)

else:
input_elm = Input(py_type_to_html_input_type[var.type], var.name,
str(var.value))
Expand Down
3 changes: 3 additions & 0 deletions examples/webapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ def post(self):
for v in self.application.parameters:
if v.type is bool:
inp = v.with_value(self.get_argument(v.name, default='off') == 'on')
elif v.type is list:
#parse select element as string otherwise v.type will convert to a series of letters
inp = v.with_value(str(self.get_argument(v.name)))
else:
inp = v.with_value(v.type(self.get_argument(v.name)))
defined.append(inp)
Expand Down
2 changes: 1 addition & 1 deletion nbparameterise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
__version__ = '0.5'

from .code import (
Parameter, extract_parameters, replace_definitions, parameter_values,
Parameter, extract_parameters, replace_definitions, parameter_values, get_parameter_cell,
)
26 changes: 22 additions & 4 deletions nbparameterise/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ def __eq__(self, other):
and self.value == other.value
)

def get_parameter_cell(nb, tag):
cell = find_first_tagged_cell(nb,tag)
if cell is None:
cell = first_code_cell(nb)
return cell

def find_first_tagged_cell(nb,tag):
for cell in nb.cells:
if cell.cell_type == 'code':
tags = cell.get('metadata', {}).get('tags', [])
if any([i == tag for i in tags]):
return cell


def first_code_cell(nb):
for cell in nb.cells:
if cell.cell_type == 'code':
Expand All @@ -51,7 +65,7 @@ def get_driver_module(nb, override=None):
assert kernel_name_re.match(module_name)
return importlib.import_module('nbparameterise.code_drivers.%s' % module_name)

def extract_parameters(nb, lang=None):
def extract_parameters(nb, lang=None, tag='Parameters'):
"""Returns a list of Parameter instances derived from the notebook.

This looks for assignments (like 'n = 50') in the first code cell of the
Expand All @@ -62,7 +76,10 @@ def extract_parameters(nb, lang=None):
now, nbparameterise only handles 'python3' and 'python2'.
"""
drv = get_driver_module(nb, override=lang)
params = list(drv.extract_definitions(first_code_cell(nb).source))
cell = get_parameter_cell(nb,tag)

params = list(drv.extract_definitions(cell.source))


# Add extra info from notebook metadata
for param in params:
Expand Down Expand Up @@ -91,7 +108,7 @@ def parameter_values(params, **kwargs):
return res

def replace_definitions(nb, values, execute=False, execute_resources=None,
lang=None, *, comments=True):
lang=None, *, comments=True, tag='Parameters'):
"""Return a copy of nb with the first code cell defining the given parameters.

values should be a list of Parameter objects (as returned by extract_parameters),
Expand All @@ -110,7 +127,8 @@ def replace_definitions(nb, values, execute=False, execute_resources=None,
"""
nb = copy.deepcopy(nb)
drv = get_driver_module(nb, override=lang)
first_code_cell(nb).source = drv.build_definitions(values, comments=comments)
cell = get_parameter_cell(nb,tag)
cell.source = drv.build_definitions(values, comments=comments)
if execute:
resources = execute_resources or {}
nb, resources = ExecutePreprocessor().preprocess(nb, resources)
Expand Down
69 changes: 69 additions & 0 deletions tests/sample_parameters_tag.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"woooo=\"1123\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"Parameters"
]
},
"outputs": [],
"source": [
"a = \"Some text\"\n",
"b = 12\n",
"b2 = -7\n",
"c = 14.0\n",
"d = False # comment:bool\n",
"e = [0, 1.0, True, \"text\", [0, 1]]\n",
"f = {0: 0, \"item\": True, \"dict\": {0: \"text\"}} # comment:dict\n",
"print(\"This should be ignored\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"a, b, c, d, woooo"
]
}
],
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
},
"parameterise": {
"c": {
"display_name": "Sea"
}
}
},
"nbformat": 4,
"nbformat_minor": 1
}
8 changes: 4 additions & 4 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import unittest

import nbformat
from nbparameterise import code, Parameter
from nbparameterise import code, Parameter,get_parameter_cell

samplenb = os.path.join(os.path.dirname(__file__), 'sample.ipynb')

Expand Down Expand Up @@ -36,11 +36,11 @@ def test_rebuild(self):
self.params[4].with_value(True),
]
nb = code.replace_definitions(self.nb, from_form, execute=False)

assert "# comment:bool" in nb.cells[0].source
cell = get_parameter_cell(nb)
assert "# comment:bool" in cell.source

ns = {}
exec(nb.cells[0].source, ns)
exec(cell.source, ns)
assert ns['a'] == "New text"
assert ns['b'] == 21
assert ns['b2'] == -3
Expand Down
70 changes: 70 additions & 0 deletions tests/test_parameters_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import os.path
import unittest

import nbformat
from nbparameterise import code, Parameter,get_parameter_cell

samplenb = os.path.join(os.path.dirname(__file__), 'sample_parameters_tag.ipynb')

class BasicParameterTestCase(unittest.TestCase):
def setUp(self):
with open(samplenb) as f:
self.nb = nbformat.read(f, as_version=4)

self.params = code.extract_parameters(self.nb)

def test_extract(self):
assert self.params == [
Parameter('a', str, "Some text"),
Parameter('b', int, 12),
Parameter('b2', int, -7),
Parameter('c', float, 14.0),
Parameter('d', bool, False),
Parameter('e', list, [0, 1.0, True, "text", [0, 1]]),
Parameter('f', dict, {0: 0, "item": True, "dict": {0: "text"}}),
]
assert self.params[4].comment == '# comment:bool'
assert self.params[6].comment == '# comment:dict'
assert self.params[3].metadata == {'display_name': 'Sea'}

def test_rebuild(self):
from_form = [
self.params[0].with_value("New text"),
self.params[1].with_value(21),
self.params[2].with_value(-3),
self.params[3].with_value(0.25),
self.params[4].with_value(True),
]
nb = code.replace_definitions(self.nb, from_form, execute=False)
cell = get_parameter_cell(nb,'Parameters')
assert "# comment:bool" in cell.source

ns = {}
exec(cell.source, ns)
assert ns['a'] == "New text"
assert ns['b'] == 21
assert ns['b2'] == -3
assert ns['c'] == 0.25
assert ns['d'] == True

def test_new_values(self):
params = code.parameter_values(self.params,
a = "New text",
c = 12.0
)

assert [p.name for p in params] == ['a', 'b', 'b2', 'c', 'd', 'e', 'f']

assert params[0].value == 'New text'
assert params[1].value == 12
assert params[3].value == 12.0
assert isinstance(params[3].value, float)
assert params[4].value == False


def test_parameter_repr():
p = Parameter('days', int, 7, metadata={'foo': 'boo'}, comment='# Days to show')
p2 = eval(repr(p)) # The repr should eval to an identical Parameter
assert p2 == p
assert p2.metadata == p.metadata
assert p2.comment == p.comment