diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6517cf9b..858d5daf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,16 @@ Collect fragments into this file with: scriv collect --version X.Y.Z
+
+
+## 0.9.2 (2023-09-21)
+
+### Bug fixes
+
+- Fix how strings are rendered in the parameters cell of notebooks. Previously string parameters were missing Python quotes. Now parameters are passed to Jinja in their `repr` string forms to be proper Python code.
+
+
## 0.9.1 (2023-07-31)
### Bug fixes
diff --git a/src/timessquare/domain/page.py b/src/timessquare/domain/page.py
index a0ea345a..141c0af7 100644
--- a/src/timessquare/domain/page.py
+++ b/src/timessquare/domain/page.py
@@ -461,8 +461,12 @@ def render_parameters(
JSON-encoded notebook source.
"""
# Build Jinja render context
- jinja_env = jinja2.Environment(autoescape=True)
- jinja_env.globals.update({"params": values})
+ # Turn off autoescaping to avoid escaping the parameter values
+ jinja_env = jinja2.Environment(autoescape=False) # noqa: S701
+ value_code_strings = {
+ name: repr(value) for name, value in values.items()
+ }
+ jinja_env.globals.update({"params": value_code_strings})
# Read notebook and render cell-by-cell
notebook = PageModel.read_ipynb(self.ipynb)
@@ -483,7 +487,7 @@ def render_parameters(
cell.source = template.render()
# Modify notebook metadata to include values
- if "times-square" not in notebook.metadata.keys():
+ if "times-square" not in notebook.metadata:
notebook.metadata["times-square"] = {}
notebook.metadata["times-square"]["values"] = values
diff --git a/tests/data/demo.ipynb b/tests/data/demo.ipynb
index 3e162ba8..6d4cec17 100644
--- a/tests/data/demo.ipynb
+++ b/tests/data/demo.ipynb
@@ -23,7 +23,8 @@
"source": [
"A = 2\n",
"y0 = 4\n",
- "lambd = 1"
+ "lambd = 1\n",
+ "title = \"hello world\""
]
},
{
@@ -103,6 +104,11 @@
"default": 0,
"description": "Y-axis offset",
"type": "number"
+ },
+ "title": {
+ "default": "hello world",
+ "description": "A string value",
+ "type": "string"
}
}
}
diff --git a/tests/domain/page_test.py b/tests/domain/page_test.py
index 6541ca59..7f8af7d4 100644
--- a/tests/domain/page_test.py
+++ b/tests/domain/page_test.py
@@ -94,7 +94,9 @@ def test_render_parameters() -> None:
title="Demo",
uploader_username="testuser",
)
- rendered = page.render_parameters(values={"A": 2, "y0": 1.0, "lambd": 0.5})
+ rendered = page.render_parameters(
+ values={"A": 2, "y0": 1.0, "lambd": 0.5, "title": "Demo"}
+ )
rendered_nb = PageModel.read_ipynb(rendered)
# Check that the markdown got rendered
@@ -110,7 +112,7 @@ def test_render_parameters() -> None:
# Check that the first code cell got replaced
assert rendered_nb["cells"][1]["source"] == (
- "# Parameters\nA = 2\nlambd = 0.5\ny0 = 1.0"
+ "# Parameters\nA = 2\nlambd = 0.5\ntitle = 'Demo'\ny0 = 1.0"
)
# Check that the second code cell was unchanged
diff --git a/tests/handlers/v1/pages_test.py b/tests/handlers/v1/pages_test.py
index 9fc585c0..d1183de0 100644
--- a/tests/handlers/v1/pages_test.py
+++ b/tests/handlers/v1/pages_test.py
@@ -55,13 +55,18 @@ async def test_pages(client: AsyncClient, respx_mock: respx.Router) -> None:
"description": "Amplitude",
"default": 4,
},
- "y0": {"type": "number", "description": "Y-axis offset", "default": 0},
"lambd": {
"type": "number",
"minimum": 0,
"description": "Wavelength",
"default": 2,
},
+ "title": {
+ "default": "hello world",
+ "description": "A string value",
+ "type": "string",
+ },
+ "y0": {"type": "number", "description": "Y-axis offset", "default": 0},
}
# List page summaries
@@ -121,6 +126,7 @@ async def test_pages(client: AsyncClient, respx_mock: respx.Router) -> None:
"A": 4,
"y0": 0,
"lambd": 2,
+ "title": "hello world",
}
# Render the page template with some parameters set
@@ -140,6 +146,7 @@ async def test_pages(client: AsyncClient, respx_mock: respx.Router) -> None:
"A": 2,
"y0": 0,
"lambd": 2,
+ "title": "hello world",
}
# Try to get HTML rendering; should be unavailable right now.