Browse Source

add ablility to specify notebook cells

Jake Vanderplas 12 years ago
parent
commit
d5e4d179e9
1 changed files with 58 additions and 3 deletions
  1. 58 3
      liquid_tags/notebook.py

+ 58 - 3
liquid_tags/notebook.py

@@ -6,7 +6,7 @@ notebook in a blog post.
 
 Syntax
 ------
-{% notebook filename.ipynb %}
+{% notebook filename.ipynb [ cells[start:end] ]%}
 
 The file should be specified relative to the ``notebooks`` subdirectory of the
 content directory.  Optionally, this subdirectory can be specified in the
@@ -14,6 +14,9 @@ config file:
 
     NOTEBOOK_DIR = 'notebooks'
 
+The cells[start:end] statement is optional, and can be used to specify which
+block of cells from the notebook to include.
+
 Details
 -------
 Because the conversion and formatting of notebooks is rather involved, there
@@ -52,8 +55,8 @@ except ImportError:
     from converters import ConverterBloggerHTML  # requires nbconvert package
     separate_available = False
 
-SYNTAX = "{% notebook /path/to/notebook.ipynb %}"
-FORMAT = re.compile(r"""^(?:\s+)?(?P<src>\S+)(?:\s+)?$""")
+SYNTAX = "{% notebook /path/to/notebook.ipynb [ cells[start:end] ] %}"
+FORMAT = re.compile(r"""^(\s+)?(?P<src>\S+)(\s+)?((cells\[)(?P<start>-?[0-9]*):(?P<end>-?[0-9]*)(\]))?(\s+)?$""")
 
 
 def process_body(body):
@@ -108,16 +111,66 @@ def process_header(header):
     return header.split('\n')
 
 
+def strip_divs(body, start=None, end=None):
+    """Strip divs from the body for partial notebook insertion
+
+    If L represents the list of parsed main divs, then this returns
+    the document corresponding to the divs L[start:end].
+
+    body should be a list of lines in the body of the html file.
+    """
+    # TODO: this is a bit hackish.  It would be better to add a PR to
+    #       nbconvert which does this at the source.
+    DIV = re.compile('<div')
+    UNDIV = re.compile('</div')
+
+    # remove ipynb div
+    body_lines = body[1:-1]
+    
+    # split divs
+    L = []
+    count = 0
+    div_start = 0
+    for i, line in enumerate(body_lines):
+        count += len(DIV.findall(line))
+        count -= len(UNDIV.findall(line))
+        
+        if count == 0:
+            L.append(body_lines[div_start:i + 1])
+            div_start = i + 1
+        elif count < 0:
+            raise ValueError("parsing error: lost a tag")
+
+    if div_start != len(body_lines):
+        raise ValueError("parsing error: didn't find the end of the div")
+
+    body_lines = sum(L[start:end], [])
+
+    return body[:1] + body_lines + body[-1:]
+
+
 @LiquidTags.register('notebook')
 def notebook(preprocessor, tag, markup):
     match = FORMAT.search(markup)
     if match:
         argdict = match.groupdict()
         src = argdict['src']
+        start = argdict['start']
+        end = argdict['end']
     else:
         raise ValueError("Error processing input, "
                          "expected syntax: {0}".format(SYNTAX))
 
+    if start:
+        start = int(start)
+    else:
+        start = None
+
+    if end:
+        end = int(end)
+    else:
+        end = None
+
     settings = preprocessor.configs.config['settings']
     nb_dir =  settings.get('NOTEBOOK_DIR', 'notebooks')
     nb_path = os.path.join('content', nb_dir, src)
@@ -144,6 +197,8 @@ def notebook(preprocessor, tag, markup):
            "this should be included in the theme.\n")
     open('_nb_header.html', 'w').write('\n'.join(header_lines).encode('utf-8'))
 
+    body_lines = strip_divs(body_lines, start, end)
+
     body = preprocessor.configs.htmlStash.store('\n'.join(body_lines),
                                                 safe=True)
     return body