Skip to content
Permalink
Browse files
Merge pull request #1834 from getnikola/update_ipynb
Agnostic IPynb, supersede 1774
  • Loading branch information
Kwpolska committed Jun 20, 2015
2 parents 34b5750 + 57d4bf6 commit 7f5e201c87e6f2dc7e5839d407bcf4ba1ac739db
@@ -4,6 +4,8 @@ New in master
Features
--------

* Support other kernels for ipynb/Jupyter using
``nikola new_post -f ipynb@kernel`` (Issues #1774, #1834)
* Add distinct styling for the site footer in bootstrap3
* Bootstrap v3.3.5 (Issue #1828)
* Use ``watchdog`` in ``nikola auto`` (Issue #1810)
@@ -841,9 +841,12 @@ config option:
IPython Notebook/Jupyter
````````````````````````

To use IPython Notebooks (a.k.a. Jupyter) as posts/pages, make sure ``ipynb``
is in your ``COMPILERS`` and that the ``.ipynb`` extension is defined in ``POSTS``
and ``PAGES``.
To use Jupyter notebooks (previously known as IPython notebooks) as posts/pages,
make sure ``ipynb`` is in your ``COMPILERS`` and that the ``.ipynb`` extension
is defined in ``POSTS`` and ``PAGES``.

The ``-f`` argument to ``new_post`` should be used in the ``ipynb@KERNEL`` format.
It defaults to Python in the version used by Nikola if not specified.

HTML
````
@@ -52,10 +52,6 @@ div.text_cell_render {
margin: 0 0 0px;
}

h1, h2, h3, .metadata p {
margin-left: 15px;
}

.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #408080; font-style: italic } /* Comment */
@@ -268,6 +268,10 @@ def _execute(self, options, args):
onefile = self.site.config.get('ONE_FILE_POSTS', True)

content_format = options['content_format']
content_subformat = None

if "@" in content_format:
content_format, content_subformat = content_format.split("@")

if not content_format: # Issue #400
content_format = get_default_compiler(
@@ -367,6 +371,11 @@ def _execute(self, options, args):
metadata.update(self.site.config['ADDITIONAL_METADATA'])
data.update(metadata)

# ipynb plugin needs the ipython kernel info. We get the kernel name
# from the content_subformat and pass it to the compiler in the metadata
if content_format == "ipynb" and content_subformat is not None:
metadata["ipython_kernel"] = content_subformat

# Override onefile if not really supported.
if not compiler_plugin.supports_onefile and onefile:
onefile = False
@@ -376,8 +385,6 @@ def _execute(self, options, args):
with io.open(import_file, 'r', encoding='utf-8') as fh:
content = fh.read()
else:
# ipynb's create_post depends on this exact string, take care
# if you're changing it
content = "Write your {0} here.".format('page' if is_page else 'post')
compiler_plugin.create_post(
txt_path, content=content, onefile=onefile, title=title,
@@ -3,8 +3,8 @@ Name = ipynb
Module = ipynb

[Documentation]
Author = Damian Avila
Version = 1.0
Website = http://www.oquanta.info
Description = Compile IPython notebooks into HTML
Author = Damian Avila and others
Version = 1.1
Website = http://www.damian.oquanta.info
Description = Compile IPython notebooks into Nikola posts

@@ -29,36 +29,43 @@
from __future__ import unicode_literals, print_function
import io
import os
import sys

try:
import IPython
from IPython.nbconvert.exporters import HTMLExporter
if IPython.version_info[0] >= 3: # API changed with 3.0.0
from IPython import nbformat
current_nbformat = nbformat.current_nbformat
from IPython.kernel import kernelspec
else:
import IPython.nbformat.current as nbformat
current_nbformat = 'json'
kernelspec = None

from IPython.config import Config
flag = True
except ImportError:
flag = None

from nikola.plugin_categories import PageCompiler
from nikola.utils import makedirs, req_missing
from nikola.utils import makedirs, req_missing, get_logger


class CompileIPynb(PageCompiler):
"""Compile IPynb into HTML."""

name = "ipynb"
supports_onefile = False
demote_headers = True
default_kernel = 'python2' if sys.version_info[0] == 2 else 'python3'

def set_site(self, site):
self.logger = get_logger('compile_ipynb', site.loghandlers)
super(CompileIPynb, self).set_site(site)

def compile_html(self, source, dest, is_two_file=True):
if flag is None:
req_missing(['ipython>=1.1.0'], 'build this site (compile ipynb)')
req_missing(['ipython[notebook]>=2.0.0'], 'build this site (compile ipynb)')
makedirs(os.path.dirname(dest))
HTMLExporter.default_template = 'basic'
c = Config(self.site.config['IPYNB_CONFIG'])
@@ -75,45 +82,68 @@ def read_metadata(self, post, file_metadata_regexp=None, unslugify_titles=False,
As ipynb file support arbitrary metadata as json, the metadata used by Nikola
will be assume to be in the 'nikola' subfield.
"""
if flag is None:
req_missing(['ipython[notebook]>=2.0.0'], 'build this site (compile ipynb)')
source = post.source_path
with io.open(source, "r", encoding="utf8") as in_file:
nb_json = nbformat.read(in_file, current_nbformat)
# metadata should always exist, but we never know if
# the user crafted the ipynb by hand and did not add it.
# Metadata might not exist in two-file posts or in hand-crafted
# .ipynb files.
return nb_json.get('metadata', {}).get('nikola', {})

def create_post(self, path, **kw):
if flag is None:
req_missing(['ipython[notebook]>=2.0.0'], 'build this site (compile ipynb)')
content = kw.pop('content', None)
onefile = kw.pop('onefile', False)
kernel = kw.pop('ipython_kernel', None)
# is_page is not needed to create the file
kw.pop('is_page', False)

metadata = {}
metadata.update(self.default_metadata)
metadata.update(kw)

makedirs(os.path.dirname(path))

if content.startswith("{"):
# .ipynb imported file, guaranteed to start with "{" because it’s JSON.
nb = nbformat.reads(content, current_nbformat)
else:
if IPython.version_info[0] >= 3:
nb = nbformat.v4.new_notebook()
nb["cells"] = [nbformat.v4.new_markdown_cell(content)]
else:
nb = nbformat.new_notebook()
nb["worksheets"] = [nbformat.new_worksheet(cells=[nbformat.new_text_cell('markdown', [content])])]

if kernelspec is not None:
if kernel is None:
kernel = self.default_kernel
self.logger.notice('No kernel specified, assuming "{0}".'.format(kernel))

IPYNB_KERNELS = {}
ksm = kernelspec.KernelSpecManager()
for k in ksm.find_kernel_specs():
IPYNB_KERNELS[k] = ksm.get_kernel_spec(k).to_dict()
IPYNB_KERNELS[k]['name'] = k
del IPYNB_KERNELS[k]['argv']

if kernel not in IPYNB_KERNELS:
self.logger.error('Unknown kernel "{0}". Maybe you mispelled it?'.format(kernel))
self.logger.info("Available kernels: {0}".format(", ".join(sorted(IPYNB_KERNELS))))
raise Exception('Unknown kernel "{0}"'.format(kernel))

nb["metadata"]["kernelspec"] = IPYNB_KERNELS[kernel]
else:
# Older IPython versions don’t need kernelspecs.
pass

if onefile:
raise Exception('The one-file format is not supported by this compiler.')
nb["metadata"]["nikola"] = metadata

with io.open(path, "w+", encoding="utf8") as fd:
if not content.startswith("Write your"):
fd.write(content)
if IPython.version_info[0] >= 3:
nbformat.write(nb, fd, 4)
else:
fd.write("""{
"metadata": {
"name": ""
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}
}
]
}""")
nbformat.write(nb, fd, 'ipynb')

This file was deleted.

@@ -7,7 +7,7 @@ pygal==1.7.0
typogrify>=2.0.4
phpserialize>=1.3
webassets>=0.10.1
ipython[notebook]>=1.2.1
ipython[notebook]>=2.0.0
ghp-import>=0.4.1
ws4py==0.3.4
watchdog==0.8.3
@@ -6,7 +6,7 @@
import subprocess
import sys
import shutil
from setuptools import setup
from setuptools import setup, find_packages
from setuptools.command.install import install
from setuptools.command.test import test as TestCommand

@@ -118,17 +118,7 @@ def run(self):
author='Roberto Alsina and others',
author_email='ralsina@netmanagers.com.ar',
url='https://getnikola.com/',
packages=['nikola',
'nikola.plugins',
'nikola.plugins.command',
'nikola.plugins.compile',
'nikola.plugins.compile.ipynb',
'nikola.plugins.compile.markdown',
'nikola.plugins.compile.rest',
'nikola.plugins.task',
'nikola.plugins.task.sitemap',
'nikola.plugins.template',
],
packages=find_packages(exclude=('tests',)),
license='MIT',
keywords='website, static',
classifiers=('Development Status :: 5 - Production/Stable',

0 comments on commit 7f5e201

Please sign in to comment.