From 7637dfcb5c78f8038a135bfa76172d7266b77545 Mon Sep 17 00:00:00 2001 From: Marc Wouts Date: Thu, 13 Dec 2018 22:49:12 +0100 Subject: [PATCH] Extension prefix is limited to lgt/pct/spx/nb #87 #138 extension for file name with dot is simply the extension, unless the intermediate prefix is one of lgt/pct/spx/nb Fixes #138 --- HISTORY.rst | 3 ++- jupytext/cli.py | 2 +- jupytext/contentsmanager.py | 11 +++++------ jupytext/formats.py | 13 ++++++++++++- tests/test_cli.py | 2 +- tests/test_contentsmanager.py | 34 +++++++++++++++++++++++++++++++++- 6 files changed, 54 insertions(+), 11 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index b94ac5d90..8f7cc7b31 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -14,7 +14,8 @@ Release History **BugFixes** -- Main language of scripts is inferred from script extension. Fixes the round trip conversion on Python notebooks with a javascript cell. +- Main language of scripts is inferred from script extension. Fixes a round trip conversion issue for Python notebooks with a Javascript cell. +- Format extension must start with one of ``.lgt``,``.pct``,``.spx`` and ``.nb`` (#87,#138) 0.8.6 (2018-11-29) ++++++++++++++++++++++ diff --git a/jupytext/cli.py b/jupytext/cli.py index c71f4414a..ec890ab5e 100644 --- a/jupytext/cli.py +++ b/jupytext/cli.py @@ -303,6 +303,6 @@ def jupytext(args=None): update=args.update, freeze_metadata=args.freeze_metadata, comment_magics=args.comment_magics) - except ValueError as err: # (ValueError, TypeError, IOError) as err: + except (ValueError, TypeError, IOError) as err: print('jupytext: error: ' + str(err)) exit(1) diff --git a/jupytext/contentsmanager.py b/jupytext/contentsmanager.py index 7f2695d8a..a20679da1 100644 --- a/jupytext/contentsmanager.py +++ b/jupytext/contentsmanager.py @@ -19,9 +19,8 @@ import jupytext from .combine import combine_inputs_with_outputs -from .formats import check_file_version, NOTEBOOK_EXTENSIONS, \ +from .formats import check_file_version, NOTEBOOK_EXTENSIONS, EXTENSION_PREFIXES, \ format_name_for_ext, parse_one_format, parse_formats, transition_to_jupytext_section_in_metadata -from .metadata_filter import metadata_filter_as_dict def kernelspec_from_language(language): @@ -105,10 +104,10 @@ def file_fmt_ext(path): Return file name, format (possibly .nb.py) and extension (.py) """ file, ext = os.path.splitext(path) - file, intermediate_ext = os.path.splitext(file) - if file and len(intermediate_ext) <= 4: - return file, intermediate_ext + ext, ext - return file + intermediate_ext, ext, ext + file, ext_prefix = os.path.splitext(file) + if file and ext_prefix in EXTENSION_PREFIXES: + return file, ext_prefix + ext, ext + return file + ext_prefix, ext, ext class TextFileContentsManager(FileContentsManager, Configurable): diff --git a/jupytext/formats.py b/jupytext/formats.py index 7a9fe9b5c..68b45b4da 100644 --- a/jupytext/formats.py +++ b/jupytext/formats.py @@ -114,6 +114,7 @@ def __init__(self, NOTEBOOK_EXTENSIONS = list(dict.fromkeys( ['.ipynb'] + [fmt.extension for fmt in JUPYTEXT_FORMATS])) +EXTENSION_PREFIXES = ['.lgt', '.spx', '.pct', '.nb'] def get_format(ext, format_name=None): @@ -260,7 +261,17 @@ def parse_one_format(ext_and_format_name): if not ext.startswith('.'): ext = '.' + ext - return ext, format_name + legitimate_extensions = NOTEBOOK_EXTENSIONS + ['.auto'] + if ext in legitimate_extensions: + return ext, format_name + + if ext.rfind('.') > 0: + pre, short_ext = os.path.splitext(ext) + if short_ext in legitimate_extensions and pre in EXTENSION_PREFIXES: + return ext, format_name + + raise ValueError("Extension '{}' should have been one of '{}', with optional prefix among '{}'".format( + ext, "','".join(legitimate_extensions), "','".join(EXTENSION_PREFIXES))) def parse_formats(formats): diff --git a/tests/test_cli.py b/tests/test_cli.py index b7908e77e..2ff94aa1a 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -102,7 +102,7 @@ def test_error_not_notebook_ext_input(nb_file='notebook.ext'): def test_error_not_notebook_ext_dest1(nb_file=list_notebooks()[0]): - with pytest.raises(TypeError): + with pytest.raises(ValueError): convert_notebook_files([nb_file], fmt='ext') diff --git a/tests/test_contentsmanager.py b/tests/test_contentsmanager.py index cd85096b6..4f0d6d38f 100644 --- a/tests/test_contentsmanager.py +++ b/tests/test_contentsmanager.py @@ -394,6 +394,38 @@ def test_save_to_light_percent_sphinx_format(nb_file, tmpdir): compare_notebooks(nb, model['content']) +@skip_if_dict_is_not_ordered +@pytest.mark.parametrize('nb_file', list_notebooks('ipynb_py')) +def test_pair_notebook_with_dot(nb_file, tmpdir): + # Reproduce issue #138 + tmp_py = 'file.5.1.py' + tmp_ipynb = 'file.5.1.ipynb' + + cm = jupytext.TextFileContentsManager() + cm.root_dir = str(tmpdir) + + nb = jupytext.readf(nb_file) + nb['metadata']['jupytext'] = {'formats': 'ipynb,py:percent'} + + # save to ipynb and three python flavors + with mock.patch('jupytext.header.INSERT_AND_CHECK_VERSION_NUMBER', True): + cm.save(model=dict(type='notebook', content=nb), path=tmp_ipynb) + + assert os.path.isfile(str(tmpdir.join(tmp_ipynb))) + + # read files + with open(str(tmpdir.join(tmp_py))) as stream: + assert read_format_from_metadata(stream.read(), '.py') == 'percent' + + model = cm.get(path=tmp_py) + assert model['name'] == 'file.5.1.py' + compare_notebooks(nb, model['content']) + + model = cm.get(path=tmp_ipynb) + assert model['name'] == 'file.5.1.ipynb' + compare_notebooks(nb, model['content']) + + @pytest.mark.parametrize('nb_file', list_notebooks('ipynb_py')[:1]) def test_preferred_format_allows_to_read_others_format(nb_file, tmpdir): # 1. write py ipynb @@ -611,7 +643,7 @@ def test_metadata_filter_is_effective(nb_file, tmpdir): cm.save(model=dict(type='notebook', content=nb), path=tmp_ipynb) # set config - cm.default_jupytext_formats = 'ipynb, py' + cm.default_jupytext_formats = 'ipynb,py' cm.default_notebook_metadata_filter = 'jupytext,-all' cm.default_cell_metadata_filter = '-all'