Repository: mwouts/jupytext

Issue:
jupytext.write doesn't error if `fmt` is unknown, but the output file has a known extension
Just playing around with MyST -> ipynb -> MyST right now. The ipynb syncing works beautifully! I love it

I am having a hard time getting MyST code cells to render as `ipynb` code cells.

E.g., this markdown:

````
---
jupytext:
  formats: ipynb,md:myst
  text_representation:
    extension: .md
    format_name: myst
    format_version: '0.6'
    jupytext_version: 1.4.1
kernelspec:
  display_name: Python 3
  language: python
  name: python3
---

```{code-cell} python3
print('hi')
```


```{code-cell} python3
print('hi')
```

````

has its ` ```{code-cell} ``` ` read in as markdown instead of being converted to Python code cells.

I tried adding double-spaces between them but that didn't do the trick. Anything I am missing?

(I also noticed a weird thing where the YAML header of the MyST markdown file would be read into the notebook as a `raw` cell at the top, rather than in the notebook metadata...is that expected behavior?)

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/mwouts/jupytext/pull/470?src=pr&el=h1) Report
> Merging [#470](https://codecov.io/gh/mwouts/jupytext/pull/470?src=pr&el=desc) into [master](https://codecov.io/gh/mwouts/jupytext/commit/6458f625d4e7ba9cf4cbda03812e27f2853fb0e3&el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `86.95%`.

[![Impacted file tree graph](https://codecov.io/gh/mwouts/jupytext/pull/470/graphs/tree.svg?width=650&height=150&src=pr&token=a1pNjsiILi)](https://codecov.io/gh/mwouts/jupytext/pull/470?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #470      +/-   ##
==========================================
- Coverage   98.89%   98.87%   -0.02%     
==========================================
  Files          80       80              
  Lines        7862     8032     +170     
==========================================
+ Hits         7775     7942     +167     
- Misses         87       90       +3     
```


| [Impacted Files](https://codecov.io/gh/mwouts/jupytext/pull/470?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [jupytext/formats.py](https://codecov.io/gh/mwouts/jupytext/pull/470/diff?src=pr&el=tree#diff-anVweXRleHQvZm9ybWF0cy5weQ==) | `97.48% <62.50%> (-0.91%)` | :arrow_down: |
| [tests/test\_cli.py](https://codecov.io/gh/mwouts/jupytext/pull/470/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9jbGkucHk=) | `100.00% <100.00%> (ø)` | |
| [tests/test\_formats.py](https://codecov.io/gh/mwouts/jupytext/pull/470/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9mb3JtYXRzLnB5) | `100.00% <100.00%> (ø)` | |
| [jupytext/kernels.py](https://codecov.io/gh/mwouts/jupytext/pull/470/diff?src=pr&el=tree#diff-anVweXRleHQva2VybmVscy5weQ==) | `83.33% <0.00%> (+6.66%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mwouts/jupytext/pull/470?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mwouts/jupytext/pull/470?src=pr&el=footer). Last update [6458f62...ecd1b70](https://codecov.io/gh/mwouts/jupytext/pull/470?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fd5cfe3..45aa942 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
 
 **Fixed**
 - Fixed the CLI example for not commenting out magic commands: `--opt comment_magics=false`. In addition, most of the `jupytext` commands in `using-cli.md` are now tested! (#465)
+- `jupytext.read` and `jupytext.write` now give more meaningful errors when the format information is incorrect (#462)
 
 1.4.1 (2020-03-19)
 ------------------
diff --git a/jupytext/formats.py b/jupytext/formats.py
index 826e650..7ddc85f 100644
--- a/jupytext/formats.py
+++ b/jupytext/formats.py
@@ -474,7 +474,8 @@ def long_form_one_format(jupytext_format, metadata=None, update=None, auto_ext_r
         'markdown': 'md',
         'script': 'auto',
         'c++': 'cpp',
-        'myst': 'md'
+        'myst': 'md:myst',
+        'pandoc': 'md:pandoc'
     }
     if jupytext_format.lower() in common_name_to_ext:
         jupytext_format = common_name_to_ext[jupytext_format.lower()]
@@ -491,8 +492,15 @@ def long_form_one_format(jupytext_format, metadata=None, update=None, auto_ext_r
                 "The `bare` format has been renamed to `nomarker` - (see https://github.com/mwouts/jupytext/issues/397)",
                 DeprecationWarning)
             fmt['format_name'] = 'nomarker'
-    else:
+    elif not jupytext_format or '.' in jupytext_format or ('.' + jupytext_format) in NOTEBOOK_EXTENSIONS + ['.auto']:
         ext = jupytext_format
+    elif jupytext_format in _VALID_FORMAT_NAMES:
+        fmt['format_name'] = jupytext_format
+        ext = ''
+    else:
+        raise JupytextFormatError(
+            "'{}' is not a notebook extension (one of {}), nor a notebook format (one of {})".format(
+                jupytext_format, ', '.join(NOTEBOOK_EXTENSIONS), ', '.join(_VALID_FORMAT_NAMES)))
 
     if ext.rfind('.') > 0:
         fmt['suffix'], ext = os.path.splitext(ext)
@@ -565,6 +573,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', 'use_runtools']
 _VALID_FORMAT_OPTIONS = _BINARY_FORMAT_OPTIONS + ['notebook_metadata_filter', 'cell_metadata_filter', 'cell_markers']
+_VALID_FORMAT_NAMES = {fmt.format_name for fmt in JUPYTEXT_FORMATS}
 
 
 def validate_one_format(jupytext_format):
@@ -572,6 +581,10 @@ def validate_one_format(jupytext_format):
     if not isinstance(jupytext_format, dict):
         raise JupytextFormatError('Jupytext format should be a dictionary')
 
+    if 'format_name' in jupytext_format and jupytext_format['format_name'] not in _VALID_FORMAT_NAMES:
+        raise JupytextFormatError('{} is not a valid format name. Please choose one of {}'.format(
+            jupytext_format.get('format_name'), ', '.join(_VALID_FORMAT_NAMES)))
+
     for key in jupytext_format:
         if key not in _VALID_FORMAT_INFO + _VALID_FORMAT_OPTIONS:
             raise JupytextFormatError("Unknown format option '{}' - should be one of '{}'".format(


Test patch:
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 4666d06..be54514 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -165,7 +165,7 @@ def tmp_py(tmpdir):
 
 
 def test_error_not_notebook_ext_to(tmp_ipynb):
-    with pytest.raises(JupytextFormatError, match="Extension '.ext' is not a notebook extension. Please use one of"):
+    with pytest.raises(JupytextFormatError, match="'ext' is not a notebook extension"):
         jupytext([tmp_ipynb, '--to', 'ext'])
 
 
diff --git a/tests/test_formats.py b/tests/test_formats.py
index c66db32..5a5e639 100644
--- a/tests/test_formats.py
+++ b/tests/test_formats.py
@@ -5,7 +5,7 @@ import jupytext
 from jupytext.formats import guess_format, divine_format, read_format_from_metadata, rearrange_jupytext_metadata
 from jupytext.formats import long_form_multiple_formats, short_form_multiple_formats, update_jupytext_formats_metadata
 from jupytext.formats import get_format_implementation, validate_one_format, JupytextFormatError
-from .utils import list_notebooks
+from .utils import list_notebooks, requires_myst, requires_pandoc
 
 
 @pytest.mark.parametrize('nb_file', list_notebooks('python'))
@@ -224,9 +224,33 @@ def test_set_auto_ext():
         long_form_multiple_formats('ipynb,auto:percent', {})
 
 
+@requires_pandoc
 def test_pandoc_format_is_preserved():
     formats_org = 'ipynb,md,.pandoc.md:pandoc,py:light'
     long = long_form_multiple_formats(formats_org)
     formats_new = short_form_multiple_formats(long)
 
     compare(formats_new, formats_org)
+
+
+@requires_myst
+def test_write_as_myst(tmpdir):
+    """Inspired by https://github.com/mwouts/jupytext/issues/462"""
+    nb = new_notebook()
+    tmp_md = str(tmpdir.join('notebook.md'))
+
+    jupytext.write(nb, tmp_md, fmt='myst')
+
+    with open(tmp_md) as fp:
+        md = fp.read()
+
+    assert 'myst' in md
+
+
+def test_write_raises_when_fmt_does_not_exists(tmpdir):
+    """Inspired by https://github.com/mwouts/jupytext/issues/462"""
+    nb = new_notebook()
+    tmp_md = str(tmpdir.join('notebook.md'))
+
+    with pytest.raises(JupytextFormatError):
+        jupytext.write(nb, tmp_md, fmt='unknown_format')