Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V0.3.0 #15

Merged
merged 44 commits into from
Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
46d6a82
Reproduce and fix #11
Jul 5, 2018
62e4fcd
Fix link
Jul 6, 2018
ae08786
Simpler is better
mwouts Jul 6, 2018
cf00a0b
unicode is str in python 3
mwouts Jul 6, 2018
00d0675
Header parsing in specialized function
mwouts Jul 6, 2018
2d3857e
Header export done in specialized function
mwouts Jul 6, 2018
808c111
language identification go to a specialized module
mwouts Jul 6, 2018
16c6505
Full path of local file
mwouts Jul 6, 2018
efd2f90
Preparing room for py and R extensions
mwouts Jul 6, 2018
628e3f9
Use fp
mwouts Jul 6, 2018
bf37a10
Cell methods moved to a specialized file.
mwouts Jul 9, 2018
66d56e7
more generic cell metadata parsing
mwouts Jul 9, 2018
af96e76
Some progresses towards python scripts
mwouts Jul 10, 2018
3875ef2
New version 0.2.5
mwouts Jul 10, 2018
141cc78
lost in git
mwouts Jul 10, 2018
3b32616
Merge remote-tracking branch 'origin/master' into v0.3.0
mwouts Jul 11, 2018
b78554b
Fix ContentsManager on older python
Jul 12, 2018
8c1908a
Introducing default_nbrmd_formats #12
mwouts Jul 12, 2018
a10c45e
Trust .Rmd notebook if .ipynb is trusted #12
mwouts Jul 12, 2018
dd2eb6b
Load cell inputs from nbrmd_sourceonly_format extension #12
mwouts Jul 13, 2018
560c379
Mention that merging R markdown is much simpler than ipynb
mwouts Jul 13, 2018
a91a479
Filter out nbrmd_options in metadata when testing
mwouts Jul 13, 2018
14bb0d7
Fix citation
mwouts Jul 13, 2018
49eb5c5
python code cell example
mwouts Jul 13, 2018
eb354a7
New version 0.2.6 #12
mwouts Jul 13, 2018
fd6d891
Preparing room for py and R extensions
mwouts Jul 6, 2018
bfcb648
Fix tests
mwouts Jul 16, 2018
f55f253
Fix readme for pypi
mwouts Jul 16, 2018
93737ec
Escape markdown with ## in .py
mwouts Jul 16, 2018
82ca4c9
pycodestyle
mwouts Jul 17, 2018
308a4db
Focus on Rmd, py, R
mwouts Jul 17, 2018
ca52191
TextNotebookReader class
mwouts Jul 17, 2018
8cc5e35
ipynb to py now ok
mwouts Jul 17, 2018
b6719f4
Two blank lines between markdown cells
mwouts Jul 17, 2018
db0b6d6
Test loading modules as notebooks
mwouts Jul 17, 2018
a9bed4f
Fix tests on older python
mwouts Jul 17, 2018
aa92b34
Python scripts end with line return, no blank line
mwouts Jul 17, 2018
6f798bf
Fix parsing of python code options & encoding
mwouts Jul 17, 2018
ed51dd5
Log input/output on two lines
mwouts Jul 17, 2018
120b59a
Test python executable, plus sample python notebook with cell metadata
mwouts Jul 17, 2018
17033cb
Basename is enough
mwouts Jul 17, 2018
0da5633
No blank line after last cell
mwouts Jul 17, 2018
908dd6b
Towards new release
mwouts Jul 17, 2018
9780779
Merge branch 'master' into v0.3.0
mwouts Jul 17, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ python:
# command to install dependencies
install:
- pip install codecov
- pip install pytest pytest-cov
- pip install pytest pytest-cov testfixtures
- pip install notebook
- pip install -r requirements.txt
- pip install .
Expand Down
8 changes: 8 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ Release History
dev
+++


0.3.0 (2018-07-17)
+++++++++++++++++++

**Improvements**

- Introducing support for notebooks as python `.py` or R scripts `.R`

0.2.6 (2018-07-13)
+++++++++++++++++++

Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ The format actually supports [many languages](https://yihui
.name/knitr/demo/engines/).

R markdown is almost like plain markdown. There are only two differences:
- R markdown has a specific syntax for active code cells, that start with
```
```{python}
```
These active cells may optionally contain cell options.
- R markdown has a specific syntax for active code cells: language, and
optional cell options are enclosed into a pair of curly brackets:

```
```{python}

- a YAML header, that describes the notebook title, author, and desired
output (HTML, slides, PDF...).

Expand Down
4 changes: 2 additions & 2 deletions nbrmd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
R Markdown notebooks.
"""

from .nbrmd import read, reads, readf, write, writes, writef
from .nbrmd import readf, writef, writes, reads, notebook_extensions, readme

try:
from .rmarkdownexporter import RMarkdownExporter
except ImportError as e:
RMarkdownExporter = str(e)

try:
from .cm import RmdFileContentsManager
from .contentsmanager import RmdFileContentsManager
except ImportError as e:
RmdFileContentsManager = str(e)
21 changes: 17 additions & 4 deletions nbrmd/chunk_options.py → nbrmd/cell_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""

import ast
import json

_boolean_options_dictionary = [('hide_input', 'echo', True),
('hide_output', 'include', True)]
Expand Down Expand Up @@ -36,7 +37,7 @@ def _py_logical_values(rbool):
raise RLogicalValueError


def to_chunk_options(language, metadata):
def metadata_to_rmd_options(language, metadata):
options = language.lower()
if 'name' in metadata:
options += ' ' + metadata['name'] + ','
Expand All @@ -60,7 +61,7 @@ def to_chunk_options(language, metadata):
', '.join(['"{}"'.format(str(v)) for v in co_value])))
else:
options += ' {}={},'.format(co_name, str(co_value))
return options.strip(',')
return options.strip(',').strip()


def update_metadata_using_dictionary(name, value, metadata):
Expand Down Expand Up @@ -141,7 +142,7 @@ def parse_rmd_options(line):
if len(result) and name is '':
raise RMarkdownOptionParsingError(
'Option line "{}" has no name for '
'option value {}' .format(line, value))
'option value {}'.format(line, value))
result.append((name.strip(), value.strip()))
name = ''
value = ''
Expand All @@ -166,7 +167,7 @@ def parse_rmd_options(line):
return result


def to_metadata(options):
def rmd_options_to_metadata(options):
options = options.split(' ', 1)
if len(options) == 1:
language = options[0]
Expand Down Expand Up @@ -207,3 +208,15 @@ def to_metadata(options):
continue

return language, metadata


def json_options_to_metadata(options):
try:
return json.loads(options)
except ValueError:
return {}


def metadata_to_json_options(metadata):
return json.dumps({k: metadata[k] for k in metadata
if k not in _ignore_metadata})
198 changes: 198 additions & 0 deletions nbrmd/cells.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
from .languages import cell_language
from .cell_metadata import metadata_to_rmd_options, rmd_options_to_metadata, \
json_options_to_metadata, metadata_to_json_options
from nbformat.v4.nbbase import new_code_cell, new_markdown_cell
import re


def code_to_rmd(source, metadata, default_language):
lines = []
language = cell_language(source) or default_language
options = metadata_to_rmd_options(language, metadata)
lines.append(u'```{{{}}}'.format(options))
lines.extend(source)
lines.append(u'```')
return lines


def cell_to_text(self,
cell,
next_cell=None,
default_language='python'):
source = cell.get('source').splitlines()
metadata = cell.get('metadata', {})
skipline = True
if 'noskipline' in metadata:
skipline = not metadata['noskipline']
del metadata['noskipline']

lines = []
if cell.cell_type == 'code':
if self.ext == '.Rmd':
lines.extend(code_to_rmd(source, metadata, default_language))
else:
language = cell_language(source) or default_language
if language == default_language:
if self.ext == '.R':
options = metadata_to_rmd_options(language, metadata)[2:]
if options != '':
lines.append('#+ ' + options)
else:
options = metadata_to_json_options(metadata)
if options != '{}':
lines.append('#+ ' + options)
lines.extend(source)
else:
lines.extend(self.markdown_escape(
code_to_rmd(source, metadata, default_language)))

# Two blank lines before next code cell
if next_cell and next_cell.cell_type == 'code':
lines.append('')
else:
if source == []:
source = ['']
lines.extend(self.markdown_escape(source))

# Two blank lines between consecutive markdown cells
if self.ext == '.Rmd' and next_cell \
and next_cell.cell_type == 'markdown':
lines.append('')

if skipline and next_cell:
lines.append('')

return lines


_start_code_rmd = re.compile(r"^```\{(.*)\}\s*$")
_start_code_md = re.compile(r"^```(.*)$")
_end_code_md = re.compile(r"^```\s*$")
_option_code_rpy = re.compile(r"^#\+(.*)$")
_blank = re.compile(r"^\s*$")


def start_code_rmd(line):
return _start_code_rmd.match(line)


def start_code_rpy(line):
return _option_code_rpy.match(line)


def text_to_cell(self, lines):
if self.start_code(lines[0]):
return self.code_to_cell(lines, parse_opt=True)
elif self.prefix != '' and not lines[0].startswith(self.prefix):
return self.code_to_cell(lines, parse_opt=False)
else:
return self.markdown_to_cell(lines)


def parse_code_options(line, ext):
if ext == '.Rmd':
return rmd_options_to_metadata(_start_code_rmd.findall(line)[0])
elif ext == '.R':
return rmd_options_to_metadata(_option_code_rpy.findall(line)[0])
else: # .py
return 'python', json_options_to_metadata(_option_code_rpy.findall(
line)[0])


def code_to_cell(self, lines, parse_opt):
# Parse options
if parse_opt:
language, metadata = parse_code_options(lines[0], self.ext)
if self.ext == '.Rmd':
metadata['language'] = language
else:
metadata = {}

# Find end of cell and return
if self.ext == '.Rmd':
for pos, line in enumerate(lines):
if pos > 0 and _end_code_md.match(line):
next_line_blank = pos + 1 == len(lines) or \
_blank.match(lines[pos + 1])
if next_line_blank and pos + 2 != len(lines):
return new_code_cell(
source='\n'.join(lines[1:pos]), metadata=metadata), \
pos + 2
else:
r = new_code_cell(
source='\n'.join(lines[1:pos]),
metadata=metadata)
r.metadata['noskipline'] = True
return r, pos + 1
else:
prev_blank = False
for pos, line in enumerate(lines):
if parse_opt and pos == 0:
continue

if self.prefix != '' and line.startswith(self.prefix):
if prev_blank:
return new_code_cell(
source='\n'.join(lines[parse_opt:(pos - 1)]),
metadata=metadata), pos
else:
r = new_code_cell(
source='\n'.join(lines[parse_opt:pos]),
metadata=metadata)
r.metadata['noskipline'] = True
return r, pos

if _blank.match(line):
if prev_blank:
# Two blank lines at the end == empty code cell
return new_code_cell(
source='\n'.join(lines[parse_opt:(pos - 1)]),
metadata=metadata), min(pos + 1, len(lines) - 1)
prev_blank = True
else:
prev_blank = False

# Unterminated cell?
return new_code_cell(
source='\n'.join(lines[parse_opt:]),
metadata=metadata), len(lines)


def markdown_to_cell(self, lines):
md = []
for pos, line in enumerate(lines):
# Markdown stops with the end of comments
if line.startswith(self.prefix):
md.append(self.markdown_unescape(line))
elif _blank.match(line):
return new_markdown_cell(source='\n'.join(md)), pos + 1
else:
r = new_markdown_cell(source='\n'.join(md))
r.metadata['noskipline'] = True
return r, pos

# still here => unterminated markdown
return new_markdown_cell(source='\n'.join(md)), len(lines)


def markdown_to_cell_rmd(lines):
prev_blank = False
for pos, line in enumerate(lines):
if start_code_rmd(line):
if prev_blank and pos > 1:
return new_markdown_cell(
source='\n'.join(lines[:(pos - 1)])), pos
else:

r = new_markdown_cell(
source='\n'.join(lines[:pos]))
r.metadata['noskipline'] = True
return r, pos

if _blank.match(line) and prev_blank:
return new_markdown_cell(
source='\n'.join(lines[:(pos - 1)])), pos + 1
prev_blank = _blank.match(line)

# Unterminated cell?
return new_markdown_cell(source='\n'.join(lines)), len(lines)
Loading