Skip to content

Commit

Permalink
Strings in the metadata of code cells are quoted in the Rmd represent…
Browse files Browse the repository at this point in the history
…ation.

We escape R code in chunk options with `#R_CODE#`.
And use double quote by default for R strings.
  • Loading branch information
mwouts committed Nov 14, 2019
1 parent a04f548 commit 4c78df0
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- `jupytext --test textfile.ext` now really compares the text file to its round trip (rather than the corresponding notebook) (#339)
- Markdown cells that contain code are now preserved in a round trip through the Markdown and R Markdown formats (#361)
- Code cells with a `%%python3` cell magic are now preserved in a round trip through the Markdown format (#365)
- Strings in the metadata of code cells are quoted in the Rmd representation. And we escape R code in chunk options with `#R_CODE#` (#383)


1.2.4 (2019-09-19)
Expand Down
14 changes: 11 additions & 3 deletions jupytext/cell_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ def metadata_to_rmd_options(language, metadata, use_runtools=False):
options += ' {}={},'.format(
opt_name, 'c({})'.format(
', '.join(['"{}"'.format(str(v)) for v in opt_value])))
elif isinstance(opt_value, (str, unicode)):
if opt_value.startswith('#R_CODE#'):
options += ' {}={},'.format(opt_name, opt_value[8:])
elif '"' not in opt_value:
options += ' {}="{}",'.format(opt_name, opt_value)
else:
options += " {}='{}',".format(opt_name, opt_value)
else:
options += ' {}={},'.format(opt_name, str(opt_value))
if not language:
Expand Down Expand Up @@ -267,13 +274,12 @@ def metadata_to_md_options(metadata):


def try_eval_metadata(metadata, name):
"""Evaluate given metadata to a python object, if possible"""
"""Evaluate the metadata to a python object, if possible"""
value = metadata[name]
if not isinstance(value, (str, unicode)):
return
if (value.startswith('"') and value.endswith('"')) or (value.startswith("'") and value.endswith("'")):
if name in ['active', 'magic_args', 'language']:
metadata[name] = value[1:-1]
metadata[name] = value[1:-1]
return
if value.startswith('c(') and value.endswith(')'):
value = '[' + value[2:-1] + ']'
Expand All @@ -282,6 +288,8 @@ def try_eval_metadata(metadata, name):
try:
metadata[name] = ast.literal_eval(value)
except (SyntaxError, ValueError):
if name != 'name':
metadata[name] = '#R_CODE#' + value
return


Expand Down
3 changes: 0 additions & 3 deletions jupytext/cell_to_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ def __init__(self, cell, default_language, fmt=None):

if self.language:
if magic_args:
if self.ext.endswith('.Rmd'):
quote = '"' if "'" in magic_args else "'"
magic_args = quote + magic_args + quote
self.metadata['magic_args'] = magic_args

if not self.ext.endswith('.Rmd'):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ This is a markdown cell with cell metadata `{"key": "value"}`
"""This is a code cell with metadata `{"tags":["parameters"], ".class":null}`"""
```

```{python key=value, active="", eval=FALSE}
```{python key="value", active="", eval=FALSE}
This is a raw cell with cell metadata `{"key": "value"}`
```
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ ggplot(iris, aes(x = Sepal.Length, y = Petal.Length, color=Species)) + geom_poin

The default plot dimensions are not good for us, so we use the -w and -h parameters in %%R magic to set the plot size

```{r magic_args='-w 400 -h 240'}
```{r magic_args="-w 400 -h 240"}
ggplot(iris, aes(x = Sepal.Length, y = Petal.Length, color=Species)) + geom_point()
```
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ df = pd.DataFrame(
)
```

```{r magic_args='-i df'}
```{r magic_args="-i df"}
library("ggplot2")
ggplot(data = df) + geom_point(aes(x = X, y = Y, color = Letter, size = Z))
```
10 changes: 5 additions & 5 deletions tests/notebooks/mirror/ipynb_to_Rmd/The flavors of raw cells.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ jupyter:
name: python3
---

```{python raw_mimetype=text/latex, active="", eval=FALSE}
```{python raw_mimetype="text/latex", active="", eval=FALSE}
$1+1$
```

```{python raw_mimetype=text/restructuredtext, active="", eval=FALSE}
```{python raw_mimetype="text/restructuredtext", active="", eval=FALSE}
:math:`1+1`
```

```{python raw_mimetype=text/html, active="", eval=FALSE}
```{python raw_mimetype="text/html", active="", eval=FALSE}
<b>Bold text<b>
```

```{python raw_mimetype=text/markdown, active="", eval=FALSE}
```{python raw_mimetype="text/markdown", active="", eval=FALSE}
**Bold text**
```

```{python raw_mimetype=text/x-python, active="", eval=FALSE}
```{python raw_mimetype="text/x-python", active="", eval=FALSE}
1 + 1
```

Expand Down
10 changes: 5 additions & 5 deletions tests/test_cell_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@

SAMPLES = [('r', ('R', {})),
('r plot_1, dpi=72, fig.path="fig_path/"',
('R', {'name': 'plot_1', 'dpi': 72, 'fig.path': '"fig_path/"'})),
("r plot_1, bool=TRUE, fig.path='fig_path/'",
('R', {'name': 'plot_1', 'dpi': 72, 'fig.path': 'fig_path/'})),
('r plot_1, bool=TRUE, fig.path="fig_path/"',
('R', {'name': 'plot_1', 'bool': True,
'fig.path': "'fig_path/'"})),
'fig.path': 'fig_path/'})),
('r echo=FALSE',
('R', {'tags': ['remove_input']})),
('r plot_1, echo=TRUE',
('R', {'name': 'plot_1', 'echo': True})),
('python echo=if a==5 then TRUE else FALSE',
('python', {'echo': 'if a==5 then TRUE else FALSE'})),
('python', {'echo': '#R_CODE#if a==5 then TRUE else FALSE'})),
('python noname, tags=c("a", "b", "c"), echo={sum(a+c(1,2))>1}',
('python', {'name': 'noname', 'tags': ['a', 'b', 'c'],
'echo': '{sum(a+c(1,2))>1}'})),
'echo': '#R_CODE#{sum(a+c(1,2))>1}'})),
('python active="ipynb,py"',
('python', {'active': 'ipynb,py'})),
('python include=FALSE, active="Rmd"',
Expand Down
24 changes: 22 additions & 2 deletions tests/test_read_simple_rmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_read_mostly_py_rmd_file(rmd="""---
ls()
```
```{r, results='asis', magic_args='-i x'}
```{r, results="asis", magic_args="-i x"}
cat(stringi::stri_rand_lipsum(3), sep='\n\n')
```
"""):
Expand All @@ -49,7 +49,7 @@ def test_read_mostly_py_rmd_file(rmd="""---
'source': '%%R\nls()',
'outputs': []},
{'cell_type': 'code',
'metadata': {'results': "'asis'"},
'metadata': {'results': 'asis'},
'execution_count': None,
'source': "%%R -i x\ncat(stringi::"
"stri_rand_lipsum(3), sep='\n\n')",
Expand Down Expand Up @@ -99,3 +99,23 @@ def test_tags_in_rmd(rmd='''---
''', nb=new_notebook(cells=[new_code_cell('p = 1', metadata={'tags': ['parameters']})])):
nb2 = jupytext.reads(rmd, 'Rmd')
compare_notebooks(nb2, nb)


def round_trip_cell_metadata(cell_metadata):
nb = new_notebook(metadata={'jupytext': {'main_language': 'python'}},
cells=[new_code_cell('1 + 1', metadata=cell_metadata)])
text = jupytext.writes(nb, 'Rmd')
nb2 = jupytext.reads(text, 'Rmd')
compare_notebooks(nb2, nb)


def test_comma_in_metadata(cell_metadata={'a': 'b, c'}):
round_trip_cell_metadata(cell_metadata)


def test_dict_in_metadata(cell_metadata={'a': {'b': 'c'}}):
round_trip_cell_metadata(cell_metadata)


def test_list_in_metadata(cell_metadata={'d': ['e']}):
round_trip_cell_metadata(cell_metadata)

0 comments on commit 4c78df0

Please sign in to comment.