Skip to content

Commit

Permalink
Markdown cells with one or more backslash are encoded as raw strings …
Browse files Browse the repository at this point in the history
…in the py:percent format
  • Loading branch information
mwouts committed Sep 8, 2021
1 parent 937037b commit 3ed4b73
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 35 deletions.
7 changes: 7 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Jupytext ChangeLog
==================

1.12.0+dev (2021-09-??)
-----------------------

**Added**
- The `py:percent` format can encode Markdown cells as raw strings (#836)


1.12.0 (2021-09-09)
-------------------

Expand Down
53 changes: 26 additions & 27 deletions jupytext/cell_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,33 +298,32 @@ def extract_content(self, lines):
self.ext, self.metadata, self.cell_type == "code"
):
content = "\n".join(lines).strip()
for triple_quote in ['"""', "'''"]:
if (
content.startswith(
(triple_quote, "r" + triple_quote, "R" + triple_quote)
)
and content.endswith(triple_quote)
and len(content) >= 6
):
if content.startswith(("r" + triple_quote, "R" + triple_quote)):
left = content[:4]
right = triple_quote
content = content[4:-3]
else:
left = right = triple_quote
content = content[3:-3]
# Trim first/last line return
if content.startswith("\n"):
content = content[1:]
left = left + "\n"
if content.endswith("\n"):
content = content[:-1]
right = "\n" + right
if len(left) == len(right) == 4:
self.metadata["cell_marker"] = left[:3]
else:
self.metadata["cell_marker"] = left + "," + right
return content.splitlines()
for prefix in [""] if self.ext != ".py" else ["", "r", "R"]:
for triple_quote in ['"""', "'''"]:
left = prefix + triple_quote
right = triple_quote
if (
content.startswith(left)
and content.endswith(right)
and len(content) >= len(left + right)
):
content = content[len(left) : -len(right)]
# Trim first/last line return
if content.startswith("\n"):
content = content[1:]
left = left + "\n"
if content.endswith("\n"):
content = content[:-1]
right = "\n" + right

if not prefix:
if len(left) == len(right) == 4:
self.metadata["cell_marker"] = left[:3]
elif len(left[1:]) == len(right) == 4:
self.metadata["cell_marker"] = left[:4]
else:
self.metadata["cell_marker"] = left + "," + right
return content.splitlines()

if not is_active(self.ext, self.metadata) or (
"active" not in self.metadata
Expand Down
16 changes: 15 additions & 1 deletion jupytext/cell_to_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,22 @@ def markdown_to_text(self, source):
left, right = cell_markers.split(",", 1)
else:
left = cell_markers + "\n"
if cell_markers.startswith(("r", "R")):
cell_markers = cell_markers[1:]
right = "\n" + cell_markers
if left[:3] == right[-3:] and left[:3] in ['"""', "'''"]:

if (
left[:3] == right[-3:]
or (left[:1] in ["r", "R"] and left[1:4] == right[-3:])
) and right[-3:] in ['"""', "'''"]:
# Markdown cells that contain a backslash should be encoded as raw strings
if (
left[:1] not in ["r", "R"]
and "\\" in "\n".join(source)
and self.fmt.get("format_name") == "percent"
):
left = "r" + left

source = copy(source)
source[0] = left + source[0]
source[-1] = source[-1] + right
Expand Down
2 changes: 1 addition & 1 deletion jupytext/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Jupytext's version number"""

__version__ = "1.12.0"
__version__ = "1.12.0+dev"
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
# $$

# %% [markdown]
'''
r'''
This cell uses the triple quote cell markers introduced at https://github.com/mwouts/jupytext/issues/305
$$
Expand Down
13 changes: 8 additions & 5 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-

import itertools
import os
import sys
import time
Expand Down Expand Up @@ -861,10 +860,8 @@ def test_remove_jupytext_metadata(tmpdir, cwd_tmpdir):
}


@pytest.mark.parametrize(
"nb_file,fmt",
itertools.product(list_notebooks("ipynb_py"), ["py:light", "py:percent", "md"]),
)
@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py"))
@pytest.mark.parametrize("fmt", ["py:light", "py:percent", "md"])
def test_convert_and_update_preserves_notebook(nb_file, fmt, tmpdir, cwd_tmpdir):
# cannot encode magic parameters in markdown yet
if ("magic" in nb_file or "LateX" in nb_file) and fmt == "md":
Expand All @@ -880,6 +877,12 @@ def test_convert_and_update_preserves_notebook(nb_file, fmt, tmpdir, cwd_tmpdir)

nb_org = read(nb_file)
nb_now = read(tmp_ipynb)

# The cell marker changes from """ to r""" on the LateX notebook #836
if "LateX" in nb_file and fmt == "py:percent":
last_cell = nb_now.cells[-1]
last_cell.metadata["cell_marker"] = last_cell.metadata["cell_marker"][1:]

compare(nb_now, nb_org)


Expand Down
42 changes: 42 additions & 0 deletions tests/test_raw_strings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import pytest
from nbformat.v4.nbbase import new_markdown_cell, new_notebook

import jupytext
from jupytext.compare import compare


def test_markdown_cell_with_backslash_is_encoded_with_raw_string(
nb=new_notebook(cells=[new_markdown_cell(r"A $\LaTeX$ expression")]),
py=r'''# %% [markdown]
r"""
A $\LaTeX$ expression
"""
''',
):
nb.metadata["jupytext"] = {
"cell_markers": '"""',
"notebook_metadata_filter": "-all",
}
py2 = jupytext.writes(nb, "py:percent")
compare(py2, py)


@pytest.mark.parametrize("r", ["r", "R"])
@pytest.mark.parametrize("triple_quote", ['"""', "'''"])
@pytest.mark.parametrize("expr", ["$\\LaTeX$", "common"])
def test_raw_string_is_stable_over_round_trip(r, triple_quote, expr):
py = f"""# %% [markdown]
{r}{triple_quote}
A {expr} expression
{triple_quote}
"""

nb = jupytext.reads(py, "py:percent")

(cell,) = nb.cells
assert cell.cell_type == "markdown"
assert cell.source == f"A {expr} expression"
assert cell.metadata["cell_marker"] == f"{r}{triple_quote}"

py2 = jupytext.writes(nb, "py:percent")
compare(py2, py)

0 comments on commit 3ed4b73

Please sign in to comment.