Skip to content

Commit be08058

Browse files
authored
Correctly escape queries with newlines for graph visualizations (#41)
* Escape unescaped single newline characters within the query string. * index.js
1 parent af84ff3 commit be08058

2 files changed

Lines changed: 51 additions & 16 deletions

File tree

spanner_graphs/graph_visualization.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import base64
1818
import uuid
1919
import os
20+
import re
2021

2122
from jinja2 import Template
2223

@@ -43,6 +44,40 @@ def _load_image(path: list[str]) -> str:
4344
else:
4445
with open(file_path, 'rb') as file:
4546
return base64.b64decode(file.read()).decode('utf-8')
47+
48+
def escape_newlines_within_query(query: str) -> str:
49+
"""
50+
Replaces unescaped single newline characters within the query string
51+
with escaped newline sequences '\\\\n'.
52+
It ignores newlines that are already part of an escaped sequence
53+
or likely intended for formatting at the start/end.
54+
"""
55+
def replace_newline(match):
56+
return '\\\\n'
57+
58+
# This regex looks for a newline character that is NOT preceded by a backslash
59+
# and is NOT at the very beginning or end of a non-empty query.
60+
pattern = r"(?<!\\)\\n"
61+
62+
# We'll add conditions to avoid replacing leading/trailing newlines if the query has content
63+
processed_query = query
64+
65+
if query.strip(): # Check if the query has non-whitespace content
66+
if processed_query.startswith('\n'):
67+
processed_query = processed_query[1:]
68+
if processed_query.endswith('\n'):
69+
processed_query = processed_query[:-1]
70+
71+
escaped_query = re.sub(pattern, replace_newline, processed_query)
72+
73+
# Add back the leading/trailing newlines if they were there
74+
if query.startswith('\n') and query.strip():
75+
escaped_query = '\n' + escaped_query
76+
if query.endswith('\n') and query.strip():
77+
escaped_query = escaped_query + '\n'
78+
79+
return escaped_query
80+
4681

4782
def generate_visualization_html(query: str, port: int, params: str):
4883
# Get the directory of the current file (magics.py)
@@ -72,12 +107,12 @@ def generate_visualization_html(query: str, port: int, params: str):
72107

73108
# Create a Jinja2 template
74109
template = Template(template_content)
75-
110+
escaped_query = escape_newlines_within_query(query)
76111
# Render the template with the graph data and JavaScript content
77112
html_content = template.render(
78113
graph_background_image=graph_background_image,
79114
bundled_js_code=bundled_js_code, # Pass the actual JS code instead of the path
80-
query=query,
115+
query=escaped_query,
81116
params=params,
82117
port=port,
83118
id=uuid.uuid4().hex # Prevent html/js selector collisions between cells

0 commit comments

Comments
 (0)