Ver código fonte

Merge pull request #466 from benjaminabel/liquidtags_notebook-support-ipython3

Liquidtags: support IPython3 notebook
Justin Mayer 9 anos atrás
pai
commit
4b4aa408d9

+ 4 - 0
liquid_tags/.gitignore

@@ -0,0 +1,4 @@
+.tox
+test_data/cache/
+test_data/output/theme/
+_nb_header.html

+ 5 - 5
liquid_tags/Readme.md

@@ -168,8 +168,8 @@ The notebook tag also has two optional arguments: ``cells`` and ``language``.
 ### Collapsible Code in IPython Notebooks
 
 The plugin also enables collapsible code input boxes. For this to work
-you first need to copy the file ``pelicanhtml_1.tpl`` (for IPython
-1.x) ``pelicanhtml_2.tpl`` (for IPython 2.x) to the top level of your
+you first need to copy the file ``pelicanhtml_3.tpl`` (for IPython
+3.x, ``pelicanhtml_2.tpl`` (for IPython 2.x)...) to the top level of your
 Pelican blog. Notebook input cells containing the comment line ``#
 <!-- collapse=True -->`` will be collapsed when the html page is
 loaded and can be expanded by clicking on them. Cells containing the
@@ -177,13 +177,13 @@ comment line ``# <!-- collapse=False -->`` will be open on load but
 can be collapsed by clicking on their header. Cells without collapse
 comments are rendered as standard code input cells.
 
-### Run unitests
+## Testing
 
-The file `test_notebook.py` contains tests that can be run using [nose](https://nose.readthedocs.org/en/latest/index.html)
+To test the plugin in multiple environments we use [tox](http://tox.readthedocs.org/en/latest/), to run the entire test suite, just type:
 
 ```
 cd path/to/liquid_tags
-nosetests
+tox
 ```
 
 [IPython]: http://ipython.org/

+ 21 - 16
liquid_tags/notebook.py

@@ -50,12 +50,11 @@ from functools import partial
 
 from .mdx_liquid_tags import LiquidTags
 
-from distutils.version import LooseVersion
 import IPython
-if not LooseVersion(IPython.__version__) >= '1.0':
-    raise ValueError("IPython version 1.0+ required for notebook tag")
+IPYTHON_VERSION = IPython.version_info[0]
 
-from IPython import nbconvert
+if not IPYTHON_VERSION >= 1:
+    raise ValueError("IPython version 1.0+ required for notebook tag")
 
 try:
     from IPython.nbconvert.filters.highlight import _pygments_highlight
@@ -68,8 +67,6 @@ from pygments.formatters import HtmlFormatter
 from IPython.nbconvert.exporters import HTMLExporter
 from IPython.config import Config
 
-from IPython.nbformat import current as nbformat
-
 try:
     from IPython.nbconvert.preprocessors import Preprocessor
 except ImportError:
@@ -79,9 +76,6 @@ except ImportError:
 from IPython.utils.traitlets import Integer
 from copy import deepcopy
 
-from jinja2 import DictLoader
-
-
 #----------------------------------------------------------------------
 # Some code that will be added to the header:
 #  Some of the following javascript/css include is adapted from
@@ -209,9 +203,13 @@ class SubCell(Preprocessor):
 
     def preprocess(self, nb, resources):
         nbc = deepcopy(nb)
-        for worksheet in nbc.worksheets:
-            cells = worksheet.cells[:]
-            worksheet.cells = cells[self.start:self.end]
+        if IPYTHON_VERSION < 3:
+            for worksheet in nbc.worksheets:
+                cells = worksheet.cells[:]
+                worksheet.cells = cells[self.start:self.end]
+        else:
+            nbc.cells = nbc.cells[self.start:self.end]
+        
         return nbc, resources
 
     call = preprocess # IPython < 2.0
@@ -274,18 +272,21 @@ def notebook(preprocessor, tag, markup):
                     {'enabled':True, 'start':start, 'end':end}})
 
     template_file = 'basic'
-    if LooseVersion(IPython.__version__) >= '2.0':
+    if IPYTHON_VERSION >= 3:
+        if os.path.exists('pelicanhtml_3.tpl'):
+            template_file = 'pelicanhtml_3'
+    elif IPYTHON_VERSION == 2:
         if os.path.exists('pelicanhtml_2.tpl'):
             template_file = 'pelicanhtml_2'
     else:
         if os.path.exists('pelicanhtml_1.tpl'):
             template_file = 'pelicanhtml_1'
 
-    if LooseVersion(IPython.__version__) >= '2.0':
+    if IPYTHON_VERSION >= 2:
         subcell_kwarg = dict(preprocessors=[SubCell])
     else:
         subcell_kwarg = dict(transformers=[SubCell])
-    
+
     exporter = HTMLExporter(config=c,
                             template_file=template_file,
                             filters={'highlight2html': language_applied_highlighter},
@@ -294,7 +295,11 @@ def notebook(preprocessor, tag, markup):
     # read and parse the notebook
     with open(nb_path) as f:
         nb_text = f.read()
-    nb_json = nbformat.reads_json(nb_text)
+        if IPYTHON_VERSION < 3:
+            nb_json = IPython.nbformat.current.reads_json(nb_text)
+        else:
+            nb_json = IPython.nbformat.reads(nb_text, as_version=4)
+
     (body, resources) = exporter.from_notebook_node(nb_json)
 
     # if we haven't already saved the header, save it here.

+ 55 - 0
liquid_tags/pelicanhtml_3.tpl

@@ -0,0 +1,55 @@
+{%- extends 'basic.tpl' -%}
+
+{% block stream_stdout -%}
+<div class="output_subarea output_stream output_stdout output_text">
+<pre class="ipynb">
+{{- output.text | ansi2html -}}
+</pre>
+</div>
+{%- endblock stream_stdout %}
+
+{% block stream_stderr -%}
+<div class="output_subarea output_stream output_stderr output_text">
+<pre class="ipynb">
+{{- output.text | ansi2html -}}
+</pre>
+</div>
+{%- endblock stream_stderr %}
+
+{% block error -%}
+<div class="output_subarea output_text output_error">
+<pre class="ipynb">
+{{- super() -}}
+</pre>
+</div>
+{%- endblock error %}
+
+{%- block data_text scoped %}
+<div class="output_text output_subarea {{extra_class}}">
+<pre class="ipynb">
+{{- output.data['text/plain'] | ansi2html -}}
+</pre>
+</div>
+{%- endblock -%}
+
+{% block input %}
+{% if "# <!-- collapse=True -->" in cell.source %}
+<div class="collapseheader inner_cell"><span style="font-weight: bold;">Expand Code</span>
+<div class="input_area" style="display:none">
+{{ cell.source.replace("# <!-- collapse=True -->\n", "") | highlight2html(metadata=cell.metadata) }}
+</div>
+</div>
+{% elif "# <!-- collapse=False -->" in cell.source %}
+<div class="collapseheader inner_cell"><span style="font-weight: bold;">Collapse Code</span>
+<div class="input_area">
+{{ cell.source.replace("# <!-- collapse=False -->\n", "") | highlight2html(metadata=cell.metadata) }}
+</div>
+</div>
+{% else %}
+<div class="inner_cell">
+    <div class="input_area">
+        {{ cell.source | highlight_code(metadata=cell.metadata) }}
+    </div>
+</div>
+{% endif %}
+{%- endblock input %}

+ 810 - 0
liquid_tags/test_data/content/notebooks/test_nbformat3.ipynb

@@ -0,0 +1,810 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:1630a227c54e735b7c11051ce8769271ee46f9bca6c1916bb26cfdba7c01b546"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Il s\u2019agit de rep\u00e9rer les donn\u00e9es n\u00e9cessaires voire indispensables \u00e0 la\n",
+      "r\u00e9solution. Ces donn\u00e9es peuvent \u00eatre:\n",
+      "\n",
+      "-   num\u00e9riques,\n",
+      "-   ou sous forme de textes (on dit souvent cha\u00eenes de caract\u00e8res),\n",
+      "-   ou de type logique (\u00e0 deux valeurs possibles, vrai ou faux).\n",
+      "\n",
+      "Pour affecter une valeur \u00e0 une variable en python, la syntaxe est de la forme:\n",
+      "\n",
+      "```\n",
+      "nom = valeur\n",
+      "```\n",
+      "\n",
+      "<dl>\n",
+      "<dt>Affectation</dt>\n",
+      "<dd>Dans un programme, une affectation donne une valeur \u00e0 une variable, elle est de la forme <code>v = e</code> avec `v` une variable et `e` une expression.\n",
+      "</dd>\n",
+      "</dl>\n",
+      "\n",
+      "Valeurs num\u00e9riques\n",
+      "==================\n",
+      "\n",
+      "Python distingue les entiers(integers), des nombres \u00e0\n",
+      "virgule(floating-point) par l'ajout d'une virgule:\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a = 1\n",
+      "b = 1.0\n",
+      "\n",
+      "print(a, type(a), b, type(b))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "1 <class 'int'> 1.0 <class 'float'>\n"
+       ]
+      }
+     ],
+     "prompt_number": 2
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Mais il impl\u00e9mente aussi des nombres plus exotiques tels que les\n",
+      "d\u00e9cimaux, les fractions ou encore les nombres complexes\n",
+      "\n",
+      "Cha\u00eenes de caract\u00e8res\n",
+      "=====================\n",
+      "\n",
+      "En python3, il existe deux grands types de cha\u00eenes de caract\u00e8res, les\n",
+      "strings, et les bytes. Les strings sont simplement entour\u00e9es par des\n",
+      "guillemets simples ou doubles.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a = 'Mon texte'\n",
+      "b = \"Mon deuxi\u00e8me texte\"\n",
+      "\n",
+      "print(a, type(a), b, type(b))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "Mon texte <class 'str'> Mon deuxi\u00e8me texte <class 'str'>\n"
+       ]
+      }
+     ],
+     "prompt_number": 3
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Les bytes sont pr\u00e9c\u00e9d\u00e9es de la lettre b, il s'agit de texte brut utilis\u00e9\n",
+      "par la machine mais pas toujours lisible par un humain.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a = b'Mon texte'\n",
+      "print(a, type(a))\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "b'Mon texte' <class 'bytes'>\n"
+       ]
+      }
+     ],
+     "prompt_number": 4
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "On ne peut repr\u00e9senter simplement que certains caract\u00e8res(les caract\u00e8res\n",
+      "ASCII), les caract\u00e8res accentu\u00e9s ne peuvent \u00eatre \u00e9crits directement.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "b = b'Mon deuxi\u00e8me texte'"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "ename": "SyntaxError",
+       "evalue": "bytes can only contain ASCII literal characters. (<ipython-input-5-b0bf0ef2715d>, line 1)",
+       "output_type": "pyerr",
+       "traceback": [
+        "\u001b[1;36m  File \u001b[1;32m\"<ipython-input-5-b0bf0ef2715d>\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m    b = b'Mon deuxi\u00e8me texte'\u001b[0m\n\u001b[1;37m       ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m bytes can only contain ASCII literal characters.\n"
+       ]
+      }
+     ],
+     "prompt_number": 5
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Un encodage autre que le ASCII doit \u00eatre utilis\u00e9 pour ces caract\u00e8res,\n",
+      "comme par exemple le UTF-8 qui est un codage sur huit bits tr\u00e8s r\u00e9pandu.\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "b = b'Mon deuxi\\xc3\\xa8me texte'\n",
+      "print(b)\n",
+      "print(b.decode('utf-8'))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "b'Mon deuxi\\xc3\\xa8me texte'\n",
+        "Mon deuxi\u00e8me texte\n"
+       ]
+      }
+     ],
+     "prompt_number": 6
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Valeurs logiques\n",
+      "================\n",
+      "\n",
+      "On les appelle les bool\u00e9ens, il ne peuvent avoir que deux valeurs:\n",
+      "\n",
+      "-   VRAI ou FAUX: True ou False en anglais;\n",
+      "-   1 ou 0.\n",
+      "\n",
+      "On les utilise pour r\u00e9aliser des tests.\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "1 < 2, 1 > 2, 2 == 10, 2 >= 1.9, 2 == 2, 2 != 2"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 7,
+       "text": [
+        "(True, False, False, True, True, False)"
+       ]
+      }
+     ],
+     "prompt_number": 7
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "On peut aussi les utiliser pour savoir si une variable existe.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "bool(a)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 8,
+       "text": [
+        "True"
+       ]
+      }
+     ],
+     "prompt_number": 8
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "On peut aussi combiner plusieurs tests avec les op\u00e9rateurs bool\u00e9ens `and`, `or` et `not`."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "1 < 2 or 2 < 1"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 9,
+       "text": [
+        "True"
+       ]
+      }
+     ],
+     "prompt_number": 9
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "1 < 2 and 2 < 1"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 10,
+       "text": [
+        "False"
+       ]
+      }
+     ],
+     "prompt_number": 10
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "not(1 < 2 and 2 < 1)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 11,
+       "text": [
+        "True"
+       ]
+      }
+     ],
+     "prompt_number": 11
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "#Listes et dictionnaires\n",
+      "\n",
+      "Souvent les donn\u00e9es pertinentes doivent \u00eatre agenc\u00e9es sous une forme\n",
+      "plus vaste, comme par exemple des listes o\u00f9 on peut ranger de fa\u00e7on ordonn\u00e9e *plusieurs valeurs*.\n",
+      "\n",
+      "##Listes\n",
+      "\n",
+      "\n",
+      "Les listes sont des collections *ordonn\u00e9es de valeurs*, elles sont\n",
+      "entour\u00e9es par des crochets `[]`, leurs \u00e9l\u00e9ments sont s\u00e9par\u00e9s par des\n",
+      "virgules.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a = [ 1, 'deux' , 3]\n",
+      "type(a)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 12,
+       "text": [
+        "list"
+       ]
+      }
+     ],
+     "prompt_number": 12
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "On peut facilement acc\u00e9der \u00e0 la longueur de la liste grace \u00e0 la fonction\n",
+      "`len` et \u00e0 chacun de ces \u00e9l\u00e9ments gr\u00e2ce \u00e0 leur index *(Attention le\n",
+      "premier \u00e9lement \u00e0 l'index 0)*\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "len(a)\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 13,
+       "text": [
+        "3"
+       ]
+      }
+     ],
+     "prompt_number": 13
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a[0], a[2]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 14,
+       "text": [
+        "(1, 3)"
+       ]
+      }
+     ],
+     "prompt_number": 14
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "On peut inversement conna\u00eetre l'indice correspondant \u00e0 une valeur gr\u00e2ce \u00e0 l'attribut `index`."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a.index(1), a.index('deux'), a.index(3)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 15,
+       "text": [
+        "(0, 1, 2)"
+       ]
+      }
+     ],
+     "prompt_number": 15
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "##Dictionnaires\n",
+      "\n",
+      "Dans un dictionnaire les valeurs de la collection ne sont pas rep\u00e9r\u00e9 par\n",
+      "un index, mais par une *cl\u00e9*. Les dictionnaires sont entour\u00e9s d'accolades `{}`.\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }\n",
+      "type(a)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 16,
+       "text": [
+        "dict"
+       ]
+      }
+     ],
+     "prompt_number": 16
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Pour acc\u00e9der aux \u00e9l\u00e9ments du dictionnaire, il suffit d'appeler la cl\u00e9\n",
+      "correspondante, d'autres part la fonction `len` est \u00e9galemnt disponible.\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "len(a)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 17,
+       "text": [
+        "3"
+       ]
+      }
+     ],
+     "prompt_number": 17
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "a['nom'], a['age']"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 18,
+       "text": [
+        "('Doe', 77)"
+       ]
+      }
+     ],
+     "prompt_number": 18
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "##Modification des listes et dictionnaires\n",
+      "\n",
+      "Les listes et dictionnaires sont des objets **mutables**, c'est \u00e0 dire que l'on peut modifier leur contenu sans cr\u00e9er un nouvel objet. On dit qu'il s'agit de donn\u00e9es [non-persistantes](http://fr.wikipedia.org/wiki/Persistance_%28informatique%29).\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Valeurs initiales\n",
+      "liste = [ 1, 'deux' , 3]\n",
+      "dict = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }\n",
+      "print(\"Valeurs initiales:\\n\", liste, dict)\n",
+      "\n",
+      "# Modification des valeurs par assignation\n",
+      "liste[1] = 2\n",
+      "dict['age'] = 17\n",
+      "\n",
+      "print(\"Modification des valeurs par assignation:\\n\", liste, dict)\n",
+      "\n",
+      "# Ajout d'\u00e9l\u00e9ments\n",
+      "liste.append(4)\n",
+      "dict['nationalit\u00e9'] = 'fran\u00e7aise'\n",
+      "\n",
+      "print(\"Ajout d'\u00e9l\u00e9ments:\\n\", liste, dict)\n",
+      "\n",
+      "# Suppression d'\u00e9l\u00e9ments\n",
+      "liste.pop(0)\n",
+      "dict.pop('age')\n",
+      "\n",
+      "print(\"Suppression d'\u00e9l\u00e9ments:\\n\", liste, dict)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "Valeurs initiales:\n",
+        " [1, 'deux', 3] {'age': 77, 'nom': 'Doe', 'prenom': 'John'}\n",
+        "Modification des valeurs par assignation:\n",
+        " [1, 2, 3] {'age': 17, 'nom': 'Doe', 'prenom': 'John'}\n",
+        "Ajout d'\u00e9l\u00e9ments:\n",
+        " [1, 2, 3, 4] {'age': 17, 'nom': 'Doe', 'nationalit\u00e9': 'fran\u00e7aise', 'prenom': 'John'}\n",
+        "Suppression d'\u00e9l\u00e9ments:\n",
+        " [2, 3, 4] {'nom': 'Doe', 'nationalit\u00e9': 'fran\u00e7aise', 'prenom': 'John'}\n"
+       ]
+      }
+     ],
+     "prompt_number": 19
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Si on a besoin de modifier une liste ou un dictionnaire, mais que l'on veut garder une trace des objets initiaux, il faut commencer par en cr\u00e9er une **copie**, il ne suffit pas de cr\u00e9er une variable supl\u00e9mentaire sans quoi cette variable serait elle aussi modifi\u00e9e si l'objet initial changeait: l'assignation est dite par **r\u00e9f\u00e9rence** dans ce cas."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Valeurs initiales\n",
+      "L = [ 1, 'deux' , 3]\n",
+      "print(\"Valeurs initiales:\\n\", L)\n",
+      "\n",
+      "# Cr\u00e9ation d'une r\u00e9f\u00e9rencxe \u00e0 la liste par simple assignation\n",
+      "L_ref = L\n",
+      "\n",
+      "# Cr\u00e9ation d'une copie de la liste\n",
+      "L_copie = list(L)\n",
+      "\n",
+      "# Modification de la liste initiale\n",
+      "L[1] = 2\n",
+      "\n",
+      "print(\"Modification de la liste L:\")\n",
+      "print(\"La liste L a bien, \u00e9t\u00e9 modifi\u00e9e:\", L)\n",
+      "print(\"La liste L_ref a aussi \u00e9t\u00e9 modifi\u00e9e car il s'agit juste d'une r\u00e9f\u00e9rence vers la liste L:\", L_ref)\n",
+      "print(\"La copie L_copie n'a pas \u00e9t\u00e9 modifi\u00e9e:\", L_copie)\n",
+      "\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "Valeurs initiales:\n",
+        " [1, 'deux', 3]\n",
+        "Modification de la liste L:\n",
+        "La liste L a bien, \u00e9t\u00e9 modifi\u00e9e: [1, 2, 3]\n",
+        "La liste L_ref a aussi \u00e9t\u00e9 modifi\u00e9e car il s'agit juste d'une r\u00e9f\u00e9rence vers la liste L: [1, 2, 3]\n",
+        "La copie L_copie n'a pas \u00e9t\u00e9 modifi\u00e9e: [1, 'deux', 3]\n"
+       ]
+      }
+     ],
+     "prompt_number": 20
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Entr\u00e9e des donn\u00e9es\n",
+      "==================\n",
+      "\n",
+      "Dans cette phase peut aussi figurer ce qu\u2019on appelle l\u2019entr\u00e9e des\n",
+      "donn\u00e9es, qui peut se manifester par la saisie de caract\u00e8res ou de\n",
+      "nombres sur le clavier, ou la lecture de la position du pointeur de la\n",
+      "souris, ou encore par la lecture d\u2019un fichier contenant ces nombres ou\n",
+      "caract\u00e8res.\n",
+      "\n",
+      "Il s\u2019agit aussi de rep\u00e9rer les r\u00e9sultats interm\u00e9diaires qu\u2019il est bon de\n",
+      "m\u00e9moriser pour la suite car indispensables au traitement.\n",
+      "\n",
+      "Il est parfois utile d\u2019utiliser des variables auxiliaires pour ne pas\n",
+      "perturber les donn\u00e9es initiales.\n",
+      "\n",
+      "Entr\u00e9e des donn\u00e9es au clavier\n",
+      "-----------------------------\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "chiffre = input(\"Entrer la valeur du chiffre souhait\u00e9: \")\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "name": "stdout",
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "Entrer la valeur du chiffre souhait\u00e9: 7\n"
+       ]
+      }
+     ],
+     "prompt_number": 21
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print(chiffre)\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "7\n"
+       ]
+      }
+     ],
+     "prompt_number": 22
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Attention cependant, cette valeur est une cha\u00eene de caract\u00e8re, et les op\u00e9rations sont celles des `string`s."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print(type(chiffre))\n",
+      "chiffre*10"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "<class 'str'>\n"
+       ]
+      },
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 23,
+       "text": [
+        "'7777777777'"
+       ]
+      }
+     ],
+     "prompt_number": 23
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Il convient de la convertir un nombre entier ou flottant avant d'utiliser la valeur."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "chiffre = int(chiffre)\n",
+      "10*chiffre"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 24,
+       "text": [
+        "70"
+       ]
+      }
+     ],
+     "prompt_number": 24
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Lecture d'un fichier\n",
+      "--------------------\n",
+      "\n",
+      "Voici par exemple comment ouvrir et lire un fichier appel\u00e9 `lorem.txt`\n",
+      "contenu dans le r\u00e9p\u00e9rtoire `data` du dossier courant.\n",
+      "\n"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "fichier = open('data/lorem.txt')\n",
+      "fichier.read()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 27,
+       "text": [
+        "'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\\nvero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,\\nno sea takimata sanctus est Lorem ipsum dolor sit amet.\\n'"
+       ]
+      }
+     ],
+     "prompt_number": 27
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Dans la sortie pr\u00e9c\u00e9dente, les caract\u00e8res `\\n` repr\u00e9sentent les sauts de\n",
+      "ligne, on peut aussi lire le fichier ligne par ligne."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "fichier = open('data/lorem.txt')\n",
+      "fichier.readline()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 29,
+       "text": [
+        "'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\\n'"
+       ]
+      }
+     ],
+     "prompt_number": 29
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}

+ 853 - 0
liquid_tags/test_data/content/notebooks/test_nbformat4.ipynb

@@ -0,0 +1,853 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Il s’agit de repérer les données nécessaires voire indispensables à la\n",
+    "résolution. Ces données peuvent être:\n",
+    "\n",
+    "-   numériques,\n",
+    "-   ou sous forme de textes (on dit souvent chaînes de caractères),\n",
+    "-   ou de type logique (à deux valeurs possibles, vrai ou faux).\n",
+    "\n",
+    "Pour affecter une valeur à une variable en python, la syntaxe est de la forme:\n",
+    "\n",
+    "```\n",
+    "nom = valeur\n",
+    "```\n",
+    "\n",
+    "<dl>\n",
+    "<dt>Affectation</dt>\n",
+    "<dd>Dans un programme, une affectation donne une valeur à une variable, elle est de la forme <code>v = e</code> avec `v` une variable et `e` une expression.\n",
+    "</dd>\n",
+    "</dl>\n",
+    "\n",
+    "Valeurs numériques\n",
+    "==================\n",
+    "\n",
+    "Python distingue les entiers(integers), des nombres à\n",
+    "virgule(floating-point) par l'ajout d'une virgule:\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "1 <class 'int'> 1.0 <class 'float'>\n"
+     ]
+    }
+   ],
+   "source": [
+    "# <!-- collapse=True -->\n",
+    "a = 1\n",
+    "b = 1.0\n",
+    "\n",
+    "print(a, type(a), b, type(b))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Mais il implémente aussi des nombres plus exotiques tels que les\n",
+    "décimaux, les fractions ou encore les nombres complexes\n",
+    "\n",
+    "Chaînes de caractères\n",
+    "=====================\n",
+    "\n",
+    "En python3, il existe deux grands types de chaînes de caractères, les\n",
+    "strings, et les bytes. Les strings sont simplement entourées par des\n",
+    "guillemets simples ou doubles.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Mon texte <class 'str'> Mon deuxième texte <class 'str'>\n"
+     ]
+    }
+   ],
+   "source": [
+    "# <!-- collapse=False -->\n",
+    "a = 'Mon texte'\n",
+    "b = \"Mon deuxième texte\"\n",
+    "\n",
+    "print(a, type(a), b, type(b))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Les bytes sont précédées de la lettre b, il s'agit de texte brut utilisé\n",
+    "par la machine mais pas toujours lisible par un humain.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "b'Mon texte' <class 'bytes'>\n"
+     ]
+    }
+   ],
+   "source": [
+    "a = b'Mon texte'\n",
+    "print(a, type(a))\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On ne peut représenter simplement que certains caractères(les caractères\n",
+    "ASCII), les caractères accentués ne peuvent être écrits directement.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "ename": "SyntaxError",
+     "evalue": "bytes can only contain ASCII literal characters. (<ipython-input-5-b0bf0ef2715d>, line 1)",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1;36m  File \u001b[1;32m\"<ipython-input-5-b0bf0ef2715d>\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m    b = b'Mon deuxième texte'\u001b[0m\n\u001b[1;37m       ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m bytes can only contain ASCII literal characters.\n"
+     ]
+    }
+   ],
+   "source": [
+    "b = b'Mon deuxième texte'"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Un encodage autre que le ASCII doit être utilisé pour ces caractères,\n",
+    "comme par exemple le UTF-8 qui est un codage sur huit bits très répandu.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "b'Mon deuxi\\xc3\\xa8me texte'\n",
+      "Mon deuxième texte\n"
+     ]
+    }
+   ],
+   "source": [
+    "b = b'Mon deuxi\\xc3\\xa8me texte'\n",
+    "print(b)\n",
+    "print(b.decode('utf-8'))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Valeurs logiques\n",
+    "================\n",
+    "\n",
+    "On les appelle les booléens, il ne peuvent avoir que deux valeurs:\n",
+    "\n",
+    "-   VRAI ou FAUX: True ou False en anglais;\n",
+    "-   1 ou 0.\n",
+    "\n",
+    "On les utilise pour réaliser des tests.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(True, False, False, True, True, False)"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "1 < 2, 1 > 2, 2 == 10, 2 >= 1.9, 2 == 2, 2 != 2"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On peut aussi les utiliser pour savoir si une variable existe.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "bool(a)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On peut aussi combiner plusieurs tests avec les opérateurs booléens `and`, `or` et `not`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 9,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "1 < 2 or 2 < 1"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "False"
+      ]
+     },
+     "execution_count": 10,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "1 < 2 and 2 < 1"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "not(1 < 2 and 2 < 1)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#Listes et dictionnaires\n",
+    "\n",
+    "Souvent les données pertinentes doivent être agencées sous une forme\n",
+    "plus vaste, comme par exemple des listes où on peut ranger de façon ordonnée *plusieurs valeurs*.\n",
+    "\n",
+    "##Listes\n",
+    "\n",
+    "\n",
+    "Les listes sont des collections *ordonnées de valeurs*, elles sont\n",
+    "entourées par des crochets `[]`, leurs éléments sont séparés par des\n",
+    "virgules.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "list"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "a = [ 1, 'deux' , 3]\n",
+    "type(a)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On peut facilement accéder à la longueur de la liste grace à la fonction\n",
+    "`len` et à chacun de ces éléments grâce à leur index *(Attention le\n",
+    "premier élement à l'index 0)*\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 13,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "len(a)\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(1, 3)"
+      ]
+     },
+     "execution_count": 14,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "a[0], a[2]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On peut inversement connaître l'indice correspondant à une valeur grâce à l'attribut `index`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(0, 1, 2)"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "a.index(1), a.index('deux'), a.index(3)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##Dictionnaires\n",
+    "\n",
+    "Dans un dictionnaire les valeurs de la collection ne sont pas repéré par\n",
+    "un index, mais par une *clé*. Les dictionnaires sont entourés d'accolades `{}`.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "dict"
+      ]
+     },
+     "execution_count": 16,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "a = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }\n",
+    "type(a)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Pour accéder aux éléments du dictionnaire, il suffit d'appeler la clé\n",
+    "correspondante, d'autres part la fonction `len` est égalemnt disponible.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 17,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "len(a)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "('Doe', 77)"
+      ]
+     },
+     "execution_count": 18,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "a['nom'], a['age']"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##Modification des listes et dictionnaires\n",
+    "\n",
+    "Les listes et dictionnaires sont des objets **mutables**, c'est à dire que l'on peut modifier leur contenu sans créer un nouvel objet. On dit qu'il s'agit de données [non-persistantes](http://fr.wikipedia.org/wiki/Persistance_%28informatique%29).\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Valeurs initiales:\n",
+      " [1, 'deux', 3] {'age': 77, 'nom': 'Doe', 'prenom': 'John'}\n",
+      "Modification des valeurs par assignation:\n",
+      " [1, 2, 3] {'age': 17, 'nom': 'Doe', 'prenom': 'John'}\n",
+      "Ajout d'éléments:\n",
+      " [1, 2, 3, 4] {'age': 17, 'nom': 'Doe', 'nationalité': 'française', 'prenom': 'John'}\n",
+      "Suppression d'éléments:\n",
+      " [2, 3, 4] {'nom': 'Doe', 'nationalité': 'française', 'prenom': 'John'}\n"
+     ]
+    }
+   ],
+   "source": [
+    "# Valeurs initiales\n",
+    "liste = [ 1, 'deux' , 3]\n",
+    "dict = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }\n",
+    "print(\"Valeurs initiales:\\n\", liste, dict)\n",
+    "\n",
+    "# Modification des valeurs par assignation\n",
+    "liste[1] = 2\n",
+    "dict['age'] = 17\n",
+    "\n",
+    "print(\"Modification des valeurs par assignation:\\n\", liste, dict)\n",
+    "\n",
+    "# Ajout d'éléments\n",
+    "liste.append(4)\n",
+    "dict['nationalité'] = 'française'\n",
+    "\n",
+    "print(\"Ajout d'éléments:\\n\", liste, dict)\n",
+    "\n",
+    "# Suppression d'éléments\n",
+    "liste.pop(0)\n",
+    "dict.pop('age')\n",
+    "\n",
+    "print(\"Suppression d'éléments:\\n\", liste, dict)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Si on a besoin de modifier une liste ou un dictionnaire, mais que l'on veut garder une trace des objets initiaux, il faut commencer par en créer une **copie**, il ne suffit pas de créer une variable suplémentaire sans quoi cette variable serait elle aussi modifiée si l'objet initial changeait: l'assignation est dite par **référence** dans ce cas."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Valeurs initiales:\n",
+      " [1, 'deux', 3]\n",
+      "Modification de la liste L:\n",
+      "La liste L a bien, été modifiée: [1, 2, 3]\n",
+      "La liste L_ref a aussi été modifiée car il s'agit juste d'une référence vers la liste L: [1, 2, 3]\n",
+      "La copie L_copie n'a pas été modifiée: [1, 'deux', 3]\n"
+     ]
+    }
+   ],
+   "source": [
+    "# Valeurs initiales\n",
+    "L = [ 1, 'deux' , 3]\n",
+    "print(\"Valeurs initiales:\\n\", L)\n",
+    "\n",
+    "# Création d'une référencxe à la liste par simple assignation\n",
+    "L_ref = L\n",
+    "\n",
+    "# Création d'une copie de la liste\n",
+    "L_copie = list(L)\n",
+    "\n",
+    "# Modification de la liste initiale\n",
+    "L[1] = 2\n",
+    "\n",
+    "print(\"Modification de la liste L:\")\n",
+    "print(\"La liste L a bien, été modifiée:\", L)\n",
+    "print(\"La liste L_ref a aussi été modifiée car il s'agit juste d'une référence vers la liste L:\", L_ref)\n",
+    "print(\"La copie L_copie n'a pas été modifiée:\", L_copie)\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Entrée des données\n",
+    "==================\n",
+    "\n",
+    "Dans cette phase peut aussi figurer ce qu’on appelle l’entrée des\n",
+    "données, qui peut se manifester par la saisie de caractères ou de\n",
+    "nombres sur le clavier, ou la lecture de la position du pointeur de la\n",
+    "souris, ou encore par la lecture d’un fichier contenant ces nombres ou\n",
+    "caractères.\n",
+    "\n",
+    "Il s’agit aussi de repérer les résultats intermédiaires qu’il est bon de\n",
+    "mémoriser pour la suite car indispensables au traitement.\n",
+    "\n",
+    "Il est parfois utile d’utiliser des variables auxiliaires pour ne pas\n",
+    "perturber les données initiales.\n",
+    "\n",
+    "Entrée des données au clavier\n",
+    "-----------------------------\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Entrer la valeur du chiffre souhaité: 7\n"
+     ]
+    }
+   ],
+   "source": [
+    "chiffre = input(\"Entrer la valeur du chiffre souhaité: \")\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "7\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(chiffre)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Attention cependant, cette valeur est une chaîne de caractère, et les opérations sont celles des `string`s."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "<class 'str'>\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "'7777777777'"
+      ]
+     },
+     "execution_count": 23,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "print(type(chiffre))\n",
+    "chiffre*10"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Il convient de la convertir un nombre entier ou flottant avant d'utiliser la valeur."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "70"
+      ]
+     },
+     "execution_count": 24,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "chiffre = int(chiffre)\n",
+    "10*chiffre"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Lecture d'un fichier\n",
+    "--------------------\n",
+    "\n",
+    "Voici par exemple comment ouvrir et lire un fichier appelé `lorem.txt`\n",
+    "contenu dans le répértoire `data` du dossier courant.\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\\nvero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,\\nno sea takimata sanctus est Lorem ipsum dolor sit amet.\\n'"
+      ]
+     },
+     "execution_count": 27,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "fichier = open('data/lorem.txt')\n",
+    "fichier.read()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Dans la sortie précédente, les caractères `\\n` représentent les sauts de\n",
+    "ligne, on peut aussi lire le fichier ligne par ligne."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\\n'"
+      ]
+     },
+     "execution_count": 29,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "fichier = open('data/lorem.txt')\n",
+    "fichier.readline()"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.4.0"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}

+ 17 - 0
liquid_tags/test_data/content/test-ipython-notebook-nbformat3.md

@@ -0,0 +1,17 @@
+Title: test ipython notebook nb format 3
+Date: 2015-03-03
+Authors: Testing Man
+
+
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
+no sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+#Loading an entire notebook nbformat = 3.0
+
+{% notebook test_nbformat3.ipynb %}
+
+#Loading selected cells from a notebook nbformat = 3.0
+
+{% notebook test_nbformat3.ipynb cells[1:5] %}

+ 17 - 0
liquid_tags/test_data/content/test-ipython-notebook-nbformat4.md

@@ -0,0 +1,17 @@
+Title: test ipython notebook nb format 4
+Date: 2015-03-03
+Authors: Testing Man
+
+
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,
+no sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+#Loading an entire notebook nbformat = 4.0
+
+{% notebook test_nbformat4.ipynb %}
+
+#Loading selected cells from a notebook nbformat = 4.0
+
+{% notebook test_nbformat4.ipynb cells[1:5] %}

Diferenças do arquivo suprimidas por serem muito extensas
+ 1153 - 0
liquid_tags/test_data/output/index.html


Diferenças do arquivo suprimidas por serem muito extensas
+ 1130 - 0
liquid_tags/test_data/output/test-ipython-notebook-nb-format-3.html


Diferenças do arquivo suprimidas por serem muito extensas
+ 1130 - 0
liquid_tags/test_data/output/test-ipython-notebook-nb-format-4.html


+ 34 - 0
liquid_tags/test_data/pelicanconf.py

@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*- #
+from __future__ import unicode_literals
+
+AUTHOR = 'The Tester'
+SITENAME = 'Testing site'
+SITEURL = 'http://example.com/test'
+
+# to make the test suite portable
+TIMEZONE = 'UTC'
+PATH = 'content'
+
+READERS = {'html': None}
+
+# Generate only one feed
+FEED_ALL_ATOM = None
+CATEGORY_FEED_ATOM = None
+TRANSLATION_FEED_ATOM = None
+AUTHOR_FEED_ATOM = None
+AUTHOR_FEED_RSS = None
+
+# Disable unnecessary pages
+CATEGORY_SAVE_AS = ''
+TAG_SAVE_AS = ''
+AUTHOR_SAVE_AS = ''
+ARCHIVES_SAVE_AS = ''
+AUTHORS_SAVE_AS = ''
+CATEGORIES_SAVE_AS = ''
+TAGS_SAVE_AS = ''
+
+PLUGIN_PATHS = ['../../']
+PLUGINS = ['liquid_tags.notebook']
+
+NOTEBOOK_DIR = 'notebooks'

+ 1 - 0
liquid_tags/test_data/pelicanhtml_2.tpl

@@ -0,0 +1 @@
+../pelicanhtml_2.tpl

+ 1 - 0
liquid_tags/test_data/pelicanhtml_3.tpl

@@ -0,0 +1 @@
+../pelicanhtml_3.tpl

+ 97 - 0
liquid_tags/test_generation.py

@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+from __future__ import print_function
+
+import filecmp
+import os
+import unittest
+from shutil import rmtree
+from tempfile import mkdtemp
+
+import pytest
+from pelican import Pelican
+from pelican.settings import read_settings
+
+from .notebook import IPYTHON_VERSION
+
+PLUGIN_DIR = os.path.dirname(__file__)
+TEST_DATA_DIR = os.path.join(PLUGIN_DIR, 'test_data')
+
+
+class TestFullRun(unittest.TestCase):
+    '''Test running Pelican with the Plugin'''
+
+    def setUp(self):
+        '''Create temporary output and cache folders'''
+        self.temp_path = mkdtemp(prefix='pelicantests.')
+        self.temp_cache = mkdtemp(prefix='pelican_cache.')
+        os.chdir(TEST_DATA_DIR)
+
+    def tearDown(self):
+        '''Remove output and cache folders'''
+        rmtree(self.temp_path)
+        rmtree(self.temp_cache)
+        os.chdir(PLUGIN_DIR)
+
+    @pytest.mark.skipif(IPYTHON_VERSION >= 3,
+                        reason="output must be created with ipython version 2")
+    def test_generate_with_ipython3(self):
+        '''Test generation of site with the plugin.'''
+
+        base_path = os.path.dirname(os.path.abspath(__file__))
+        base_path = os.path.join(base_path, 'test_data')
+        content_path = os.path.join(base_path, 'content')
+        output_path = os.path.join(base_path, 'output')
+        settings_path = os.path.join(base_path, 'pelicanconf.py')
+        settings = read_settings(path=settings_path,
+                                 override={'PATH': content_path,
+                                           'OUTPUT_PATH': self.temp_path,
+                                           'CACHE_PATH': self.temp_cache,
+                                           }
+                                 )
+
+        pelican = Pelican(settings)
+        pelican.run()
+
+        # test existence
+        assert os.path.exists(os.path.join(self.temp_path,
+                                           'test-ipython-notebook-nb-format-3.html'))
+        assert os.path.exists(os.path.join(self.temp_path,
+                                           'test-ipython-notebook-nb-format-4.html'))
+
+        # test differences
+        #assert filecmp.cmp(os.path.join(output_path,
+        #                                'test-ipython-notebook-v2.html'),
+        #                   os.path.join(self.temp_path,
+        #                                'test-ipython-notebook.html'))
+
+    @pytest.mark.skipif(IPYTHON_VERSION < 3,
+                        reason="output must be created with ipython version 3")
+    def test_generate_with_ipython2(self):
+        '''Test generation of site with the plugin.'''
+
+        base_path = os.path.dirname(os.path.abspath(__file__))
+        base_path = os.path.join(base_path, 'test_data')
+        content_path = os.path.join(base_path, 'content')
+        output_path = os.path.join(base_path, 'output')
+        settings_path = os.path.join(base_path, 'pelicanconf.py')
+        settings = read_settings(path=settings_path,
+                                 override={'PATH': content_path,
+                                           'OUTPUT_PATH': self.temp_path,
+                                           'CACHE_PATH': self.temp_cache,
+                                           }
+                                 )
+
+        pelican = Pelican(settings)
+        pelican.run()
+
+        # test existence
+        assert os.path.exists(os.path.join(self.temp_path,
+                                           'test-ipython-notebook-nb-format-3.html'))
+        assert os.path.exists(os.path.join(self.temp_path,
+                                           'test-ipython-notebook-nb-format-4.html'))
+
+        # test differences
+        #assert filecmp.cmp(os.path.join(output_path,
+        #                                'test-ipython-notebook-v3.html'),
+        #                   os.path.join(self.temp_path,
+        #                                'test-ipython-notebook.html'))

+ 17 - 0
liquid_tags/tox.ini

@@ -0,0 +1,17 @@
+[tox]
+skipsdist = True
+minversion = 1.8
+envlist =
+       py{27,34}-ipython2,
+       py{27,34}-ipython3,
+
+[testenv]
+commands = py.test
+
+deps =
+	pytest
+  	pytest-capturelog
+	pelican
+	markdown
+	ipython2: ipython[notebook]>=2,<3
+	ipython3: ipython[notebook]