Skip to content

Commit

Permalink
Merge pull request #60 from mwouts/v0.6.3
Browse files Browse the repository at this point in the history
V0.6.3
Fixes #57 
Fixes #46
  • Loading branch information
mwouts committed Sep 7, 2018
2 parents e345b3c + 130889f commit fcbb17e
Show file tree
Hide file tree
Showing 14 changed files with 80 additions and 31 deletions.
13 changes: 13 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ Release History
dev
+++

0.6.3 (2018-09-07)
+++++++++++++++++++

**Improvements**

- Lighter cell markers for Python and Julia scripts (#57). Corresponding file
format version at 1.2. Scripts in previous version 1.1 can still be opened.
- New screenshots for the README.

**BugFixes**

- Command line conversion tool `jupytext` fixed on Python 2.7 (#46)

0.6.2 (2018-09-05)
+++++++++++++++++++

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ Implement these [specifications](https://rmarkdown.rstudio.com/articles_report_f
We wanted to represent Jupyter notebooks with the least explicit markers possible. The rationale for that is to allow **arbitrary** python files to open as Jupyter notebooks, even files which were never prepared to become a notebook. Precisely:
- Jupyter metadata go to an escaped YAML header
- Markdown cells are commented with `# `, and separated with a blank line
- Code cells are exported verbatim (except for Jupyter magics, which are escaped), and separated with blank lines. Code cells are reconstructed from consistent python paragraphs (no function, class or multiline comment will be broken). A start-of-cell delimiter `# + {}` is used for cells that have explicit metadata (inside the curly bracket, in JSON format), and for cells that include blank lines (outside of functions, classes, etc). The end of cell delimiter is `# -`, and is omitted when followed by another explicit start of cell marker.
- Code cells are exported verbatim (except for Jupyter magics, which are escaped), and separated with blank lines. Code cells are reconstructed from consistent python paragraphs (no function, class or multiline comment will be broken). A start-of-cell delimiter `# + {}` is used for cells that have explicit metadata (inside the curly bracket, in JSON format), and `# +` is used for cells that include blank lines (outside of functions, classes, etc). The end of cell delimiter is `# -`, and is omitted when followed by another explicit start of cell marker.

## Will my notebook really run in an IDE?

Expand Down
2 changes: 1 addition & 1 deletion binder/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
jupytext>=0.6.2
jupytext>=0.6.3
plotly
matplotlib
pandas
Expand Down
4 changes: 2 additions & 2 deletions demo/Paired Jupyter notebook and python script.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# nbconvert_exporter: python
# pygments_lexer: ipython3
# version: 3.6.5
# jupytext_format_version: '1.1'
# jupytext_format_version: '1.2'
# jupytext_formats: ipynb,py
# ---

Expand All @@ -36,7 +36,7 @@

# %matplotlib inline

# + {}
# +
import matplotlib.pyplot as plt
import numpy as np

Expand Down
6 changes: 3 additions & 3 deletions demo/World population.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ---
# jupyter:
# jupytext_format_version: '1.1'
# jupytext_format_version: '1.2'
# jupytext_formats: ipynb,py,md
# kernelspec:
# display_name: Python 3
Expand All @@ -26,7 +26,7 @@
# [World Bank](http://www.worldbank.org/)
# using the [wbdata](https://github.com/OliverSherouse/wbdata) python package

# + {}
# +
import pandas as pd
import wbdata as wb

Expand Down Expand Up @@ -90,7 +90,7 @@
# [on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For
# now we just do a stacked bar plot.

# + {}
# +
import plotly.offline as offline
import plotly.graph_objs as go

Expand Down
18 changes: 14 additions & 4 deletions jupytext/cell_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
_END_CODE_MD = re.compile(r"^```\s*$")
_CODE_OPTION_R = re.compile(r"^#\+(.*)\s*$")
_CODE_OPTION_PY = re.compile(r"^(#|# )\+(\s*){(.*)}\s*$")
_SIMPLE_START_CODE_PY = re.compile(r"^(#|# )\+(\s*)$")
_BLANK_LINE = re.compile(r"^\s*$")
_PY_COMMENT = re.compile(r"^\s*#")
_PY_INDENTED = re.compile(r"^\s")
Expand Down Expand Up @@ -123,10 +124,14 @@ def metadata_and_language_from_option_line(self, line):
self.language, self.metadata = \
rmd_options_to_metadata('r ' + _CODE_OPTION_R.findall(line)[0])

if self.ext in ['.py', '.jl'] and _CODE_OPTION_PY.match(line):
self.language = 'python' if self.ext == '.py' else 'julia'
self.metadata = json_options_to_metadata(
_CODE_OPTION_PY.match(line).group(3))
if self.ext in ['.py', '.jl']:
if _CODE_OPTION_PY.match(line):
self.language = 'python' if self.ext == '.py' else 'julia'
self.metadata = json_options_to_metadata(
_CODE_OPTION_PY.match(line).group(3))
if _SIMPLE_START_CODE_PY.match(line):
self.language = 'python' if self.ext == '.py' else 'julia'
self.metadata = {}

if self.metadata and 'language' in self.metadata:
self.language = self.metadata['language']
Expand Down Expand Up @@ -225,6 +230,11 @@ def find_cell_end_code(self, lines, cell_end_re,
return i - 1, i, False
return i, i, False

if i > 0 and cell_start_re == _CODE_OPTION_PY and \
_BLANK_LINE.match(lines[i - 1]) and \
_SIMPLE_START_CODE_PY.match(line):
return i - 1, i, False

if cell_end_re:
if cell_end_re.match(line):
return i, i + 1, True
Expand Down
20 changes: 17 additions & 3 deletions jupytext/file_format_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
# Version 1.0 on 2018-08-31 - jupytext v0.6.0 : Initial version

# Python and Julia scripts
'.py': '1.1',
'.jl': '1.1',
'.py': '1.2',
'.jl': '1.2',
# Version 1.2 on 2018-09-05 - jupytext v0.6.3 : Metadata bracket can be
# omitted when empty, if previous line is empty #57
# Version 1.1 on 2018-08-25 - jupytext v0.6.0 : Cells separated with one
# blank line #38
# Version 1.0 on 2018-08-22 - jupytext v0.5.2 : Initial version
Expand All @@ -23,6 +25,9 @@
# Version 1.0 on 2018-08-22 - jupytext v0.5.2 : Initial version
}

MIN_FILE_FORMAT_VERSION = {'.Rmd': '1.0', '.md': '1.0', '.py': '1.1',
'.jl': '1.1', '.R': '1.0'}

FILE_FORMAT_VERSION_ORG = FILE_FORMAT_VERSION


Expand All @@ -31,6 +36,11 @@ def file_format_version(ext):
return FILE_FORMAT_VERSION.get(ext)


def min_file_format_version(ext):
"""Return minimum file format version for given ext"""
return MIN_FILE_FORMAT_VERSION.get(ext)


def check_file_version(notebook, source_path, outputs_path):
"""Raise if file version in source file would override outputs"""
_, ext = os.path.splitext(source_path)
Expand All @@ -47,7 +57,11 @@ def check_file_version(notebook, source_path, outputs_path):
if version == current:
return

# Not merging? OK
# Version larger than minimum readable version
if version <= current and version >= min_file_format_version(ext):
return

# Not merging? OK
if source_path == outputs_path:
return

Expand Down
4 changes: 4 additions & 0 deletions jupytext/jupytext.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ def writes(self, nb, **kwargs):
for i, cell in enumerate(cells):
text = texts[i]

# Simplify cell marker when previous line is blank
if text[0] == '# + {}' and (not lines or not lines[-1]):
text[0] = '# +'

# remove end of cell marker when redundant
# with next explicit marker
if self.ext in ['.py', '.jl'] and cell.is_code() \
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

setup(
name='jupytext',
version='0.6.2',
version='0.6.3',
author='Marc Wouts',
author_email='marc.wouts@gmail.com',
description='Jupyter notebooks as Markdown documents, '
Expand Down
2 changes: 1 addition & 1 deletion tests/python_notebook_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def f(x):
# metadata and an end-of-cell marker. Metadata information in json format,
# escaped with '#+' or '# +'

# + {}
# +
def g(x):
return x + 2

Expand Down
6 changes: 5 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .utils import list_all_notebooks, list_py_notebooks

file_format_version.FILE_FORMAT_VERSION = {}
file_format_version.MIN_FILE_FORMAT_VERSION = {}


@pytest.mark.parametrize('nb_file',
Expand Down Expand Up @@ -172,4 +173,7 @@ def test_combine_lower_version_raises(tmpdir):
with pytest.raises(SystemExit):
with mock.patch('jupytext.file_format_version.FILE_FORMAT_VERSION',
{'.py': '1.0'}):
jupytext(args=[tmp_nbpy, '--to', 'ipynb', '--update'])
with mock.patch(
'jupytext.file_format_version.MIN_FILE_FORMAT_VERSION',
{'.py': '1.0'}):
jupytext(args=[tmp_nbpy, '--to', 'ipynb', '--update'])
1 change: 1 addition & 0 deletions tests/test_contentsmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .utils import list_all_notebooks, list_py_notebooks

jupytext.file_format_version.FILE_FORMAT_VERSION = {}
jupytext.file_format_version.MIN_FILE_FORMAT_VERSION = {}


@pytest.mark.skipif(isinstance(TextFileContentsManager, str),
Expand Down
5 changes: 4 additions & 1 deletion tests/test_load_multiple.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,7 @@ def test_combine_lower_version_raises(tmpdir):
with pytest.raises(HTTPError):
with mock.patch('jupytext.file_format_version.FILE_FORMAT_VERSION',
{'.py': '1.0'}):
cm.get(tmp_ipynb)
with mock.patch(
'jupytext.file_format_version.MIN_FILE_FORMAT_VERSION',
{'.py': '1.0'}):
cm.get(tmp_ipynb)
26 changes: 13 additions & 13 deletions tests/test_read_simple_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def test_read_cell_two_blank_lines(pynb="""# ---
# title: cell with two consecutive blank lines
# ---
# + {}
# +
a = 1
Expand All @@ -137,7 +137,7 @@ def test_read_cell_two_blank_lines(pynb="""# ---

def test_read_cell_explicit_start(pynb='''
import pandas as pd
# + {}
# +
def data():
return pd.DataFrame({'A': [0, 1]})
Expand All @@ -151,21 +151,21 @@ def data():

def test_read_complex_cells(pynb='''import pandas as pd
# + {}
# +
def data():
return pd.DataFrame({'A': [0, 1]})
data()
# + {}
# +
def data2():
return pd.DataFrame({'B': [0, 1]})
data2()
# + {}
# +
# Finally we have a cell with only comments
# This cell should remain a code cell and not get converted
# to markdown
Expand Down Expand Up @@ -203,7 +203,7 @@ def data2():
def test_read_prev_function(
pynb="""def test_read_cell_explicit_start_end(pynb='''
import pandas as pd
# + {}
# +
def data():
return pd.DataFrame({'A': [0, 1]})
Expand All @@ -228,7 +228,7 @@ def test_read_cell_with_one_blank_line_end(pynb="""import pandas
compare(pynb, pynb2)


def test_read_code_cell_fully_commented(pynb="""# + {}
def test_read_code_cell_fully_commented(pynb="""# +
# This is a code cell that
# only contains comments
"""):
Expand All @@ -250,7 +250,7 @@ def test_file_with_two_blank_line_end(pynb="""import pandas
compare(pynb, pynb2)


def test_one_blank_lines_after_endofcell(pynb="""# + {}
def test_one_blank_lines_after_endofcell(pynb="""# +
# This is a code cell with explicit end of cell
1 + 1
Expand All @@ -275,13 +275,13 @@ def test_one_blank_lines_after_endofcell(pynb="""# + {}
compare(pynb, pynb2)


def test_two_cells_with_explicit_start(pynb="""# + {}
def test_two_cells_with_explicit_start(pynb="""# +
# Cell one
1 + 1
1 + 1
# + {}
# +
# Cell two
2 + 2
Expand All @@ -303,7 +303,7 @@ def test_two_cells_with_explicit_start(pynb="""# + {}
compare(pynb, pynb2)


def test_escape_start_pattern(pynb="""# The code start pattern '# + {}' can
def test_escape_start_pattern(pynb="""# The code start pattern '# +' can
# appear in code and markdown cells.
# In markdown cells it is escaped like here:
Expand Down Expand Up @@ -454,7 +454,7 @@ def test_read_write_script(pynb="""#!/usr/bin/env python
compare(pynb, pynb2)


def test_notebook_blank_lines(script="""# + {}
def test_notebook_blank_lines(script="""# +
# This is a comment
# followed by two variables
a = 3
Expand All @@ -466,7 +466,7 @@ def test_notebook_blank_lines(script="""# + {}
c = 5
# + {}
# +
# Now we have two functions
def f(x):
return x + x
Expand Down

0 comments on commit fcbb17e

Please sign in to comment.