From 5bee5c7e00777f8dd211918668ebc18fbbc24f18 Mon Sep 17 00:00:00 2001 From: Marc Wouts Date: Mon, 28 Oct 2019 23:37:23 +0100 Subject: [PATCH 1/6] Map the R Markdown visibility options to the runtools ones #337 --- jupytext/cell_metadata.py | 35 ++++++++++--------- ...est_hide_remove_input_outputs_rmarkdown.py | 34 ++++++++++++++++++ 2 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 tests/test_hide_remove_input_outputs_rmarkdown.py diff --git a/jupytext/cell_metadata.py b/jupytext/cell_metadata.py index fffd6ff0b..b80e41588 100644 --- a/jupytext/cell_metadata.py +++ b/jupytext/cell_metadata.py @@ -21,11 +21,16 @@ except NameError: unicode = str # Python 3 -# Map R Markdown's "echo" and "include" to "hide_input" and "hide_output", that are understood by the `runtools` -# extension for Jupyter notebook, and by nbconvert (use the `hide_input_output.tpl` template). +# Map R Markdown's "echo", "results" and "include" to "hide_input" and "hide_output", that are understood by the +# `runtools` extension for Jupyter notebook, and by nbconvert (use the `hide_input_output.tpl` template). # See http://jupyter-contrib-nbextensions.readthedocs.io/en/latest/nbextensions/runtools/readme.html -_BOOLEAN_OPTIONS_DICTIONARY = [('hide_input', 'echo', True), - ('hide_output', 'include', True)] +_RMARKDOWN_TO_RUNTOOLS_OPTION_MAP = [ + (('include', 'FALSE'), [('hide_input', True), ('hide_output', True)]), + (('echo', 'FALSE'), [('hide_input', True)]), + (('results', "'hide'"), [('hide_output', True)]), + (('results', '"hide"'), [('hide_output', True)]) +] + _JUPYTEXT_CELL_METADATA = [ # Pre-jupytext metadata 'skipline', 'noskipline', @@ -71,11 +76,11 @@ def metadata_to_rmd_options(language, metadata): if 'name' in metadata: options += ' ' + metadata['name'] + ',' del metadata['name'] - for jupyter_option, rmd_option, rev in _BOOLEAN_OPTIONS_DICTIONARY: - if jupyter_option in metadata: - options += ' {}={},'.format( - rmd_option, _r_logical_values(metadata[jupyter_option] != rev)) - del metadata[jupyter_option] + for rmd_option, jupyter_options in _RMARKDOWN_TO_RUNTOOLS_OPTION_MAP: + if all([metadata.get(opt_name) == opt_value for opt_name, opt_value in jupyter_options]): + options += ' {}={},'.format(rmd_option[0], 'FALSE' if rmd_option[1] is False else rmd_option[1]) + for opt_name, _ in jupyter_options: + metadata.pop(opt_name) for opt_name in metadata: opt_value = metadata[opt_name] opt_name = opt_name.strip() @@ -103,13 +108,11 @@ def update_metadata_from_rmd_options(name, value, metadata): :param metadata: :return: """ - for jupyter_option, rmd_option, rev in _BOOLEAN_OPTIONS_DICTIONARY: - if name == rmd_option: - try: - metadata[jupyter_option] = _py_logical_values(value) != rev - return True - except RLogicalValueError: - pass + for rmd_option, jupyter_options in _RMARKDOWN_TO_RUNTOOLS_OPTION_MAP: + if name == rmd_option[0] and value == rmd_option[1]: + for opt_name, opt_value in jupyter_options: + metadata[opt_name] = opt_value + return True return False diff --git a/tests/test_hide_remove_input_outputs_rmarkdown.py b/tests/test_hide_remove_input_outputs_rmarkdown.py new file mode 100644 index 000000000..954b37034 --- /dev/null +++ b/tests/test_hide_remove_input_outputs_rmarkdown.py @@ -0,0 +1,34 @@ +import pytest +import jupytext +from jupytext.compare import compare, compare_notebooks +from .utils import skip_if_dict_is_not_ordered + + +@skip_if_dict_is_not_ordered +@pytest.mark.parametrize('md,rmd', [('hide_input=true hide_output=true', 'include=FALSE'), + ('hide_output=true', "results='hide'"), + ('hide_output=true', 'results="hide"'), + ('hide_input=true', 'echo=FALSE')]) +def test_runtools_options_to_rmarkdown(md, rmd): + """Options set by the runtools extension are mapped to the corresponding R Markdown options + https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/nbextensions/runtools/readme.html""" + md = '```python ' + md + """ +1 + 1 +``` +""" + + rmd = '```{python ' + rmd + """} +1 + 1 +``` +""" + + nb_md = jupytext.reads(md, 'md') + nb_rmd = jupytext.reads(rmd, 'Rmd') + compare_notebooks(nb_rmd, nb_md) + + md2 = jupytext.writes(nb_rmd, 'md') + compare(md2, md) + + rmd = rmd.replace('"hide"', "'hide'") + rmd2 = jupytext.writes(nb_md, 'Rmd') + compare(rmd2, rmd) From 6da821c1b653e9a7a61cc102331e6958d9cfb50e Mon Sep 17 00:00:00 2001 From: Marc Wouts Date: Mon, 28 Oct 2019 23:38:46 +0100 Subject: [PATCH 2/6] Tests updated #337 --- tests/test_active_cells.py | 5 ++-- tests/test_cell_metadata.py | 10 +++---- tests/test_read_simple_rmd.py | 50 +++++++++++++++++------------------ 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/tests/test_active_cells.py b/tests/test_active_cells.py index 5ce8618f0..f4ec96206 100644 --- a/tests/test_active_cells.py +++ b/tests/test_active_cells.py @@ -233,20 +233,21 @@ def test_active_rmd(ext, no_jupytext_version_number): compare(nb.cells[0], ACTIVE_RMD['.ipynb']) -ACTIVE_NOT_INCLUDE_RMD = {'.py': """# + hide_output=true active="Rmd" +ACTIVE_NOT_INCLUDE_RMD = {'.py': """# + hide_input=true hide_output=true active="Rmd" # # This cell is active in Rmd only """, '.Rmd': """```{python include=FALSE, active="Rmd"} # This cell is active in Rmd only ``` """, - '.R': """# + hide_output=true active="Rmd" + '.R': """# + hide_input=true hide_output=true active="Rmd" # # This cell is active in Rmd only """, '.ipynb': {'cell_type': 'raw', 'source': '# This cell is active in Rmd only', 'metadata': {'active': 'Rmd', + 'hide_input': True, 'hide_output': True}}} diff --git a/tests/test_cell_metadata.py b/tests/test_cell_metadata.py index 485fb5301..c2ffe5eb1 100644 --- a/tests/test_cell_metadata.py +++ b/tests/test_cell_metadata.py @@ -15,7 +15,7 @@ ('r echo=FALSE', ('R', {'hide_input': True})), ('r plot_1, echo=TRUE', - ('R', {'name': 'plot_1', 'hide_input': False})), + ('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 noname, tags=c("a", "b", "c"), echo={sum(a+c(1,2))>1}', @@ -24,23 +24,23 @@ ('python active="ipynb,py"', ('python', {'active': 'ipynb,py'})), ('python include=FALSE, active="Rmd"', - ('python', {'active': 'Rmd', 'hide_output': True})), + ('python', {'active': 'Rmd', 'hide_output': True, 'hide_input': True})), ('r chunk_name, include=FALSE, active="Rmd"', ('R', - {'name': 'chunk_name', 'active': 'Rmd', 'hide_output': True})), + {'name': 'chunk_name', 'active': 'Rmd', 'hide_output': True, 'hide_input': True})), ('python tags=c("parameters")', ('python', {'tags': ['parameters']}))] @pytest.mark.parametrize('options,language_and_metadata', SAMPLES) def test_parse_rmd_options(options, language_and_metadata): - assert rmd_options_to_metadata(options) == language_and_metadata + compare(rmd_options_to_metadata(options), language_and_metadata) @skip_if_dict_is_not_ordered @pytest.mark.parametrize('options,language_and_metadata', SAMPLES) def test_build_options(options, language_and_metadata): - assert metadata_to_rmd_options(*language_and_metadata) == options + compare(metadata_to_rmd_options(*language_and_metadata), options) @pytest.mark.parametrize('options,language_and_metadata', SAMPLES) diff --git a/tests/test_read_simple_rmd.py b/tests/test_read_simple_rmd.py index 75a9e2072..4fd7a4beb 100644 --- a/tests/test_read_simple_rmd.py +++ b/tests/test_read_simple_rmd.py @@ -29,31 +29,31 @@ def test_read_mostly_py_rmd_file(rmd="""--- ``` """): nb = jupytext.reads(rmd, 'Rmd') - assert nb.cells == [{'cell_type': 'raw', - 'source': '---\ntitle: Simple file\n---', - 'metadata': {}}, - {'cell_type': 'code', - 'metadata': {'hide_input': False}, - 'execution_count': None, - 'source': 'import numpy as np\n' - 'x = np.arange(0, 2*math.pi, eps)', - 'outputs': []}, - {'cell_type': 'code', - 'metadata': {'hide_input': False}, - 'execution_count': None, - 'source': 'x = np.arange(0,1,eps)\ny = np.abs(x)-.5', - 'outputs': []}, - {'cell_type': 'code', - 'metadata': {}, - 'execution_count': None, - 'source': '%%R\nls()', - 'outputs': []}, - {'cell_type': 'code', - 'metadata': {'results': "'asis'"}, - 'execution_count': None, - 'source': "%%R -i x\ncat(stringi::" - "stri_rand_lipsum(3), sep='\n\n')", - 'outputs': []}] + compare(nb.cells, [{'cell_type': 'raw', + 'source': '---\ntitle: Simple file\n---', + 'metadata': {}}, + {'cell_type': 'code', + 'metadata': {'echo': True}, + 'execution_count': None, + 'source': 'import numpy as np\n' + 'x = np.arange(0, 2*math.pi, eps)', + 'outputs': []}, + {'cell_type': 'code', + 'metadata': {'echo': True}, + 'execution_count': None, + 'source': 'x = np.arange(0,1,eps)\ny = np.abs(x)-.5', + 'outputs': []}, + {'cell_type': 'code', + 'metadata': {}, + 'execution_count': None, + 'source': '%%R\nls()', + 'outputs': []}, + {'cell_type': 'code', + 'metadata': {'results': "'asis'"}, + 'execution_count': None, + 'source': "%%R -i x\ncat(stringi::" + "stri_rand_lipsum(3), sep='\n\n')", + 'outputs': []}]) rmd2 = jupytext.writes(nb, 'Rmd') rmd2 = re.sub(r'```{r ', '```{r, ', rmd2) From e51924972c6bb40d1779d8780eceec9aa72ed230 Mon Sep 17 00:00:00 2001 From: Marc Wouts Date: Mon, 28 Oct 2019 23:39:03 +0100 Subject: [PATCH 3/6] Mirror files updated --- tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb | 5 +++-- tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb | 6 +++--- tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb | 5 +++-- tests/notebooks/mirror/script_to_ipynb/knitr-spin.ipynb | 5 +++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb b/tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb index ac73d28d3..8f3591955 100644 --- a/tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb +++ b/tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb @@ -15,6 +15,7 @@ "cell_type": "code", "execution_count": null, "metadata": { + "hide_input": true, "hide_output": true, "lines_to_next_cell": 2, "name": "knitr_setup" @@ -29,7 +30,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "hide_input": false + "echo": true }, "outputs": [], "source": [ @@ -67,4 +68,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb b/tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb index dd4eac34f..08385b26d 100644 --- a/tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb +++ b/tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb @@ -43,7 +43,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "hide_input": false + "echo": true }, "outputs": [], "source": [ @@ -88,11 +88,11 @@ ], "metadata": { "jupytext": { - "cell_metadata_filter": "fig.width,hide_input,fig.height,-all", + "cell_metadata_filter": "echo,fig.height,hide_input,fig.width,-all", "main_language": "python", "notebook_metadata_filter": "-all" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb b/tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb index 36bead53d..b401306ca 100644 --- a/tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb +++ b/tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb @@ -17,6 +17,7 @@ "cell_type": "code", "execution_count": null, "metadata": { + "hide_input": true, "hide_output": true, "name": "setup" }, @@ -173,11 +174,11 @@ ], "metadata": { "jupytext": { - "cell_metadata_filter": "name,fig.width,cache,hide_output,fig.height,-all", + "cell_metadata_filter": "hide_output,cache,fig.height,name,hide_input,fig.width,-all", "main_language": "R", "notebook_metadata_filter": "-all" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/tests/notebooks/mirror/script_to_ipynb/knitr-spin.ipynb b/tests/notebooks/mirror/script_to_ipynb/knitr-spin.ipynb index 36bead53d..d07eb3f54 100644 --- a/tests/notebooks/mirror/script_to_ipynb/knitr-spin.ipynb +++ b/tests/notebooks/mirror/script_to_ipynb/knitr-spin.ipynb @@ -17,6 +17,7 @@ "cell_type": "code", "execution_count": null, "metadata": { + "hide_input": true, "hide_output": true, "name": "setup" }, @@ -173,11 +174,11 @@ ], "metadata": { "jupytext": { - "cell_metadata_filter": "name,fig.width,cache,hide_output,fig.height,-all", + "cell_metadata_filter": "name,hide_input,fig.width,fig.height,cache,hide_output,-all", "main_language": "R", "notebook_metadata_filter": "-all" } }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} From 8caa8c713369cfe6b27de868aad96afe7646d026 Mon Sep 17 00:00:00 2001 From: Marc Wouts Date: Tue, 29 Oct 2019 23:31:02 +0100 Subject: [PATCH 4/6] R Markdown visibility options are mapped to Jupyter Book ones By default. And a `use_runtools` option is provided to map them to the runtools options instead. Closes #337 --- jupytext/cell_metadata.py | 70 ++++++++++--------- jupytext/cell_reader.py | 5 +- jupytext/cell_to_text.py | 8 +-- jupytext/formats.py | 2 +- jupytext/jupytext.py | 5 ++ tests/test_active_cells.py | 8 +-- tests/test_cell_metadata.py | 8 +-- ...est_hide_remove_input_outputs_rmarkdown.py | 31 +++++++- 8 files changed, 88 insertions(+), 49 deletions(-) diff --git a/jupytext/cell_metadata.py b/jupytext/cell_metadata.py index b80e41588..c2463858b 100644 --- a/jupytext/cell_metadata.py +++ b/jupytext/cell_metadata.py @@ -30,6 +30,13 @@ (('results', "'hide'"), [('hide_output', True)]), (('results', '"hide"'), [('hide_output', True)]) ] +# Alternatively, Jupytext can also map the Jupyter Book options to R Markdown +_RMARKDOWN_TO_JUPYTER_BOOK_MAP = [ + (('include', 'FALSE'), 'remove_cell'), + (('echo', 'FALSE'), 'remove_input'), + (('results', "'hide'"), 'remove_output'), + (('results', '"hide"'), 'remove_output') +] _JUPYTEXT_CELL_METADATA = [ # Pre-jupytext metadata @@ -65,22 +72,25 @@ def _py_logical_values(rbool): raise RLogicalValueError -def metadata_to_rmd_options(language, metadata): - """ - Convert language and metadata information to their rmd representation - :param language: - :param metadata: - :return: - """ +def metadata_to_rmd_options(language, metadata, use_runtools=False): + """Convert language and metadata information to their rmd representation""" options = (language or 'R').lower() if 'name' in metadata: options += ' ' + metadata['name'] + ',' del metadata['name'] - for rmd_option, jupyter_options in _RMARKDOWN_TO_RUNTOOLS_OPTION_MAP: - if all([metadata.get(opt_name) == opt_value for opt_name, opt_value in jupyter_options]): - options += ' {}={},'.format(rmd_option[0], 'FALSE' if rmd_option[1] is False else rmd_option[1]) - for opt_name, _ in jupyter_options: - metadata.pop(opt_name) + if use_runtools: + for rmd_option, jupyter_options in _RMARKDOWN_TO_RUNTOOLS_OPTION_MAP: + if all([metadata.get(opt_name) == opt_value for opt_name, opt_value in jupyter_options]): + options += ' {}={},'.format(rmd_option[0], 'FALSE' if rmd_option[1] is False else rmd_option[1]) + for opt_name, _ in jupyter_options: + metadata.pop(opt_name) + else: + for rmd_option, tag in _RMARKDOWN_TO_JUPYTER_BOOK_MAP: + if tag in metadata.get('tags', []): + options += ' {}={},'.format(rmd_option[0], 'FALSE' if rmd_option[1] is False else rmd_option[1]) + metadata['tags'] = [i for i in metadata['tags'] if i != tag] + if not metadata['tags']: + metadata.pop('tags') for opt_name in metadata: opt_value = metadata[opt_name] opt_name = opt_name.strip() @@ -100,19 +110,19 @@ def metadata_to_rmd_options(language, metadata): return options.strip(',').strip() -def update_metadata_from_rmd_options(name, value, metadata): - """ - Update metadata using the _BOOLEAN_OPTIONS_DICTIONARY mapping - :param name: option name - :param value: option value - :param metadata: - :return: - """ - for rmd_option, jupyter_options in _RMARKDOWN_TO_RUNTOOLS_OPTION_MAP: - if name == rmd_option[0] and value == rmd_option[1]: - for opt_name, opt_value in jupyter_options: - metadata[opt_name] = opt_value - return True +def update_metadata_from_rmd_options(name, value, metadata, use_runtools=False): + """Map the R Markdown cell visibility options to the Jupyter ones""" + if use_runtools: + for rmd_option, jupyter_options in _RMARKDOWN_TO_RUNTOOLS_OPTION_MAP: + if name == rmd_option[0] and value == rmd_option[1]: + for opt_name, opt_value in jupyter_options: + metadata[opt_name] = opt_value + return True + else: + for rmd_option, tag in _RMARKDOWN_TO_JUPYTER_BOOK_MAP: + if name == rmd_option[0] and value == rmd_option[1]: + metadata.setdefault('tags', []).append(tag) + return True return False @@ -213,12 +223,8 @@ def parse_rmd_options(line): return result -def rmd_options_to_metadata(options): - """ - Parse rmd options and return a metadata dictionary - :param options: - :return: - """ +def rmd_options_to_metadata(options, use_runtools=False): + """Parse rmd options and return a metadata dictionary""" options = re.split(r'\s|,', options, 1) if len(options) == 1: language = options[0] @@ -236,7 +242,7 @@ def rmd_options_to_metadata(options): if i == 0 and name == '': metadata['name'] = value continue - if update_metadata_from_rmd_options(name, value, metadata): + if update_metadata_from_rmd_options(name, value, metadata, use_runtools=use_runtools): continue try: metadata[name] = _py_logical_values(value) diff --git a/jupytext/cell_reader.py b/jupytext/cell_reader.py index e5ff6038a..307a96676 100644 --- a/jupytext/cell_reader.py +++ b/jupytext/cell_reader.py @@ -98,6 +98,7 @@ def __init__(self, fmt=None, default_language=None): self.ext = fmt.get('extension') self.default_language = default_language or _SCRIPT_EXTENSIONS.get(self.ext, {}).get('language', 'python') self.comment_magics = fmt.get('comment_magics', self.default_comment_magics) + self.use_runtools = fmt.get('use_runtools', False) self.format_version = fmt.get('format_version') self.metadata = None self.org_content = [] @@ -398,7 +399,7 @@ class RMarkdownCellReader(MarkdownCellReader): default_comment_magics = True def options_to_metadata(self, options): - return rmd_options_to_metadata(options) + return rmd_options_to_metadata(options, self.use_runtools) def uncomment_code_and_magics(self, lines): if self.cell_type == 'code' and self.comment_magics and is_active(self.ext, self.metadata): @@ -436,7 +437,7 @@ class RScriptCellReader(ScriptCellReader): default_comment_magics = True def options_to_metadata(self, options): - return rmd_options_to_metadata('r ' + options) + return rmd_options_to_metadata('r ' + options, self.use_runtools) def find_cell_end(self, lines): """Return position of end of cell marker, and position diff --git a/jupytext/cell_to_text.py b/jupytext/cell_to_text.py index 2d02d167d..7474b823f 100644 --- a/jupytext/cell_to_text.py +++ b/jupytext/cell_to_text.py @@ -52,9 +52,9 @@ def __init__(self, cell, default_language, fmt=None): self.language = self.language or default_language self.default_language = default_language self.comment = _SCRIPT_EXTENSIONS.get(self.ext, {}).get('comment', '#') - self.comment_magics = self.fmt['comment_magics'] if 'comment_magics' in self.fmt \ - else self.default_comment_magics + self.comment_magics = self.fmt.get('comment_magics', self.default_comment_magics) self.cell_metadata_json = self.fmt.get('cell_metadata_json', False) + self.use_runtools = self.fmt.get('use_runtools', False) # how many blank lines before next cell self.lines_to_next_cell = cell.metadata.get('lines_to_next_cell') @@ -206,7 +206,7 @@ def code_to_text(self): lines = [] if not is_active(self.ext, self.metadata): self.metadata['eval'] = False - options = metadata_to_rmd_options(self.language, self.metadata) + options = metadata_to_rmd_options(self.language, self.metadata, self.use_runtools) lines.append('```{{{}}}'.format(options)) lines.extend(source) lines.append('```') @@ -373,7 +373,7 @@ def code_to_text(self): lines = [] if not is_active(self.ext, self.metadata): self.metadata['eval'] = False - options = metadata_to_rmd_options(None, self.metadata) + options = metadata_to_rmd_options(None, self.metadata, self.use_runtools) if options: lines.append('#+ {}'.format(options)) lines.extend(source) diff --git a/jupytext/formats.py b/jupytext/formats.py index 515e19154..f28494ce4 100644 --- a/jupytext/formats.py +++ b/jupytext/formats.py @@ -534,7 +534,7 @@ def short_form_multiple_formats(jupytext_formats): _VALID_FORMAT_INFO = ['extension', 'format_name', 'suffix', 'prefix'] -_BINARY_FORMAT_OPTIONS = ['comment_magics', 'split_at_heading', 'rst2md', 'cell_metadata_json'] +_BINARY_FORMAT_OPTIONS = ['comment_magics', 'split_at_heading', 'rst2md', 'cell_metadata_json', 'use_runtools'] _VALID_FORMAT_OPTIONS = _BINARY_FORMAT_OPTIONS + ['notebook_metadata_filter', 'cell_metadata_filter', 'cell_markers'] diff --git a/jupytext/jupytext.py b/jupytext/jupytext.py index 15dbf0d03..5379a91f4 100644 --- a/jupytext/jupytext.py +++ b/jupytext/jupytext.py @@ -132,6 +132,11 @@ def writes(self, nb, metadata=None, **kwargs): metadata = nb.metadata default_language = default_language_from_metadata_and_ext(metadata, self.implementation.extension) or 'python' self.update_fmt_with_notebook_options(nb.metadata) + if 'use_runtools' not in self.fmt: + for cell in nb.cells: + if cell.metadata.get('hide_input', False) or cell.metadata.get('hide_output', False): + self.fmt['use_runtools'] = True + break if 'main_language' in metadata.get('jupytext', {}): del metadata['jupytext']['main_language'] diff --git a/tests/test_active_cells.py b/tests/test_active_cells.py index f4ec96206..a63ce7d88 100644 --- a/tests/test_active_cells.py +++ b/tests/test_active_cells.py @@ -233,22 +233,20 @@ def test_active_rmd(ext, no_jupytext_version_number): compare(nb.cells[0], ACTIVE_RMD['.ipynb']) -ACTIVE_NOT_INCLUDE_RMD = {'.py': """# + hide_input=true hide_output=true active="Rmd" +ACTIVE_NOT_INCLUDE_RMD = {'.py': """# + tags=["remove_cell"] active="Rmd" # # This cell is active in Rmd only """, '.Rmd': """```{python include=FALSE, active="Rmd"} # This cell is active in Rmd only ``` """, - '.R': """# + hide_input=true hide_output=true active="Rmd" + '.R': """# + tags=["remove_cell"] active="Rmd" # # This cell is active in Rmd only """, '.ipynb': {'cell_type': 'raw', 'source': '# This cell is active in Rmd only', - 'metadata': {'active': 'Rmd', - 'hide_input': True, - 'hide_output': True}}} + 'metadata': {'active': 'Rmd', 'tags': ['remove_cell']}}} @skip_if_dict_is_not_ordered diff --git a/tests/test_cell_metadata.py b/tests/test_cell_metadata.py index c2ffe5eb1..0245eff5a 100644 --- a/tests/test_cell_metadata.py +++ b/tests/test_cell_metadata.py @@ -13,7 +13,7 @@ ('R', {'name': 'plot_1', 'bool': True, 'fig.path': "'fig_path/'"})), ('r echo=FALSE', - ('R', {'hide_input': True})), + ('R', {'tags': ['remove_input']})), ('r plot_1, echo=TRUE', ('R', {'name': 'plot_1', 'echo': True})), ('python echo=if a==5 then TRUE else FALSE', @@ -24,10 +24,10 @@ ('python active="ipynb,py"', ('python', {'active': 'ipynb,py'})), ('python include=FALSE, active="Rmd"', - ('python', {'active': 'Rmd', 'hide_output': True, 'hide_input': True})), + ('python', {'active': 'Rmd', 'tags': ['remove_cell']})), ('r chunk_name, include=FALSE, active="Rmd"', ('R', - {'name': 'chunk_name', 'active': 'Rmd', 'hide_output': True, 'hide_input': True})), + {'name': 'chunk_name', 'active': 'Rmd', 'tags': ['remove_cell']})), ('python tags=c("parameters")', ('python', {'tags': ['parameters']}))] @@ -63,7 +63,7 @@ def test_parsing_error(options): def test_ignore_metadata(): - metadata = {'trusted': True, 'hide_input': True} + metadata = {'trusted': True, 'tags': ['remove_input']} metadata = filter_metadata(metadata, None, _IGNORE_CELL_METADATA) assert metadata_to_rmd_options('R', metadata) == 'r echo=FALSE' diff --git a/tests/test_hide_remove_input_outputs_rmarkdown.py b/tests/test_hide_remove_input_outputs_rmarkdown.py index 954b37034..93a33e80b 100644 --- a/tests/test_hide_remove_input_outputs_rmarkdown.py +++ b/tests/test_hide_remove_input_outputs_rmarkdown.py @@ -4,6 +4,35 @@ from .utils import skip_if_dict_is_not_ordered +@skip_if_dict_is_not_ordered +@pytest.mark.parametrize('md,rmd', [('tags=["remove_cell"]', 'include=FALSE'), + ('tags=["remove_output"]', "results='hide'"), + ('tags=["remove_output"]', 'results="hide"'), + ('tags=["remove_input"]', 'echo=FALSE')]) +def test_jupyter_book_options_to_rmarkdown(md, rmd): + """By default, Jupyter Book tags are mapped to R Markdown options, and vice versa #337""" + md = '```python ' + md + """ +1 + 1 +``` +""" + + rmd = '```{python ' + rmd + """} +1 + 1 +``` +""" + + nb_md = jupytext.reads(md, 'md') + nb_rmd = jupytext.reads(rmd, 'Rmd') + compare_notebooks(nb_rmd, nb_md) + + md2 = jupytext.writes(nb_rmd, 'md') + compare(md2, md) + + rmd = rmd.replace('"hide"', "'hide'") + rmd2 = jupytext.writes(nb_md, 'Rmd') + compare(rmd2, rmd) + + @skip_if_dict_is_not_ordered @pytest.mark.parametrize('md,rmd', [('hide_input=true hide_output=true', 'include=FALSE'), ('hide_output=true', "results='hide'"), @@ -23,7 +52,7 @@ def test_runtools_options_to_rmarkdown(md, rmd): """ nb_md = jupytext.reads(md, 'md') - nb_rmd = jupytext.reads(rmd, 'Rmd') + nb_rmd = jupytext.reads(rmd, fmt={'extension': '.Rmd', 'use_runtools': True}) compare_notebooks(nb_rmd, nb_md) md2 = jupytext.writes(nb_rmd, 'md') From d9075497be3a13c8b39391cdf1b128d54c224901 Mon Sep 17 00:00:00 2001 From: Marc Wouts Date: Tue, 29 Oct 2019 23:32:57 +0100 Subject: [PATCH 5/6] Update HISTORY.rst --- HISTORY.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.rst b/HISTORY.rst index 9938461ea..9508d6212 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -16,6 +16,7 @@ Release History - The light format uses ``# + [markdown]`` rather than the previous ``cell_type`` metadata to identify markdown cells with metadata (#356) - Explicit Markdown cells in the light format ``# + [markdown]`` can use triple quotes (#356) - Cell metadata can be encoded as either key=value (the new default) or in JSON. An automatic option ``cell_metadata_json`` should help minimize the impact on existing files (#344) +- R Markdown hidden inputs, outputs, or cells are now mapped to the corresponding Jupyter Book tags by default (#337) - The Jupyter Notebook extension for Jupytext is compatible with Jupyter Notebook 6.0 (#346) - ``jupytext notebook.py --to ipynb`` updates the timestamp of ``notebook.py`` so that the paired notebook still works in Jupyter (#335, #254) - Added support for Rust/Evxcr, by Jonas Bushart (#351) From 4ad84628bb0e4c762d34032243e109940da4b9bc Mon Sep 17 00:00:00 2001 From: Marc Wouts Date: Tue, 29 Oct 2019 23:35:30 +0100 Subject: [PATCH 6/6] Mirror notebooks now use tags --- tests/notebooks/mirror/Rmd_to_ipynb/R_sample.ipynb | 2 +- .../mirror/Rmd_to_ipynb/chunk_options.ipynb | 13 ++++++++----- tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb | 8 +++++--- .../notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb | 9 +++++---- tests/notebooks/mirror/Rmd_to_ipynb/markdown.ipynb | 2 +- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/R_sample.ipynb b/tests/notebooks/mirror/Rmd_to_ipynb/R_sample.ipynb index 5507f0a97..f125af779 100644 --- a/tests/notebooks/mirror/Rmd_to_ipynb/R_sample.ipynb +++ b/tests/notebooks/mirror/Rmd_to_ipynb/R_sample.ipynb @@ -49,4 +49,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb b/tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb index 8f3591955..88688ddb5 100644 --- a/tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb +++ b/tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb @@ -15,10 +15,11 @@ "cell_type": "code", "execution_count": null, "metadata": { - "hide_input": true, - "hide_output": true, "lines_to_next_cell": 2, - "name": "knitr_setup" + "name": "knitr_setup", + "tags": [ + "remove_cell" + ] }, "outputs": [], "source": [ @@ -44,9 +45,11 @@ "metadata": { "fig.height": 5, "fig.width": 8, - "hide_input": true, "lines_to_next_cell": 0, - "name": "bar_plot" + "name": "bar_plot", + "tags": [ + "remove_input" + ] }, "outputs": [], "source": [ diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb b/tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb index 08385b26d..1af743ee1 100644 --- a/tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb +++ b/tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb @@ -70,8 +70,10 @@ "metadata": { "fig.height": 5, "fig.width": 8, - "hide_input": true, - "lines_to_next_cell": 0 + "lines_to_next_cell": 0, + "tags": [ + "remove_input" + ] }, "outputs": [], "source": [ @@ -88,7 +90,7 @@ ], "metadata": { "jupytext": { - "cell_metadata_filter": "echo,fig.height,hide_input,fig.width,-all", + "cell_metadata_filter": "echo,tags,fig.width,fig.height,-all", "main_language": "python", "notebook_metadata_filter": "-all" } diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb b/tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb index b401306ca..a34ef4b5d 100644 --- a/tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb +++ b/tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb @@ -17,9 +17,10 @@ "cell_type": "code", "execution_count": null, "metadata": { - "hide_input": true, - "hide_output": true, - "name": "setup" + "name": "setup", + "tags": [ + "remove_cell" + ] }, "outputs": [], "source": [ @@ -174,7 +175,7 @@ ], "metadata": { "jupytext": { - "cell_metadata_filter": "hide_output,cache,fig.height,name,hide_input,fig.width,-all", + "cell_metadata_filter": "tags,name,fig.width,cache,fig.height,-all", "main_language": "R", "notebook_metadata_filter": "-all" } diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/markdown.ipynb b/tests/notebooks/mirror/Rmd_to_ipynb/markdown.ipynb index 33bd90ccd..35e49ce63 100644 --- a/tests/notebooks/mirror/Rmd_to_ipynb/markdown.ipynb +++ b/tests/notebooks/mirror/Rmd_to_ipynb/markdown.ipynb @@ -42,4 +42,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +}