From bd7befebff03516640229f7897fdeb7e1266dc28 Mon Sep 17 00:00:00 2001 From: Robert David Grant Date: Thu, 15 Oct 2015 17:29:18 -0500 Subject: [PATCH 1/5] Add 0.6 release notes. --- docs/sphinx/source/releases/release-0.6.rst | 123 ++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 docs/sphinx/source/releases/release-0.6.rst diff --git a/docs/sphinx/source/releases/release-0.6.rst b/docs/sphinx/source/releases/release-0.6.rst new file mode 100644 index 00000000..e2aa5e68 --- /dev/null +++ b/docs/sphinx/source/releases/release-0.6.rst @@ -0,0 +1,123 @@ +============================================================================== +DistArray 0.6 release +============================================================================== + +**Mailing List:** distarray@googlegroups.com + +**Documentation:** http://distarray.readthedocs.org + +**License:** Three-clause BSD + +**Python versions:** 2.7, 3.4, and 3.5 + +**OS support:** \*nix and Mac OS X + +What is DistArray? +------------------ + +DistArray aims to bring the ease-of-use of NumPy to data-parallel +high-performance computing. It provides distributed multi-dimensional NumPy +arrays, distributed ufuncs, and distributed IO capabilities. It can +efficiently interoperate with external distributed libraries like Trilinos. +DistArray works with NumPy and builds on top of it in a flexible and natural +way. + +0.6 Release +----------- + +Noteworthy improvements in this release include: + +* a new website, (http://docs.enthought.com/distarray/), with links to + DistArray talks and presentations, +* redistribution for block-distributed (and non-distributed) DistArrays, +* experimental "quickstart" installation scripts, +* an easier API for ``Context.apply`` +* expanded examples (parallel Gaussian Elimination by Prashant Mital) +* compatibility with NumPy 1.9, +* expanded TravisCI testing on (OS X and Python 3.5), +* logos by Erick Michaud, and +* bug-fixes and code improvements + +Existing features +----------------- + +DistArray + +* supports NumPy-like slicing, reductions, and ufuncs on distributed + multidimensional arrays; +* has a client-engine process design -- data resides on the worker processes, + commands are initiated from master; +* allows full control over what is executed on the worker processes and + integrates transparently with the master process; +* allows direct communication between workers, bypassing the master process + for scalability; +* integrates with IPython.parallel for interactive creation and exploration of + distributed data; +* supports distributed ufuncs (currently without broadcasting); +* builds on and leverages MPI via MPI4Py in a transparent and user-friendly + way; +* has basic support for unstructured arrays; +* supports user-controllable array distributions across workers (block, + cyclic, block-cyclic, and unstructured) on a per-axis basis; +* has a straightforward API to control how an array is distributed; +* has basic plotting support for visualization of array distributions; +* separates the array’s distribution from the array’s data -- useful for + slicing, reductions, redistribution, broadcasting, and other operations; +* implements distributed random arrays; +* supports ``.npy``-like flat-file IO and hdf5 parallel IO (via ``h5py``); + leverages MPI-based IO parallelism in an easy-to-use and transparent way; + and +* supports the distributed array protocol [protocol]_, which allows + independently developed parallel libraries to share distributed arrays + without copying, analogous to the PEP-3118 new buffer protocol. + +Planned features +---------------- + +Planned features include + +* array re-distribution capabilities for more distribution types; +* lazy evaluation and deferred computation for latency hiding; +* examples of interoperation with Trilinos [Trilinos]_; +* distributed broadcasting support. +* integration with other packages [petsc]_ that subscribe to the distributed + array protocol [protocol]_; +* distributed fancy indexing; +* out-of-core computations; +* support for distributed sorting and other non-trivial distributed + algorithms; and +* end-user control over communication and temporary array creation, and other + performance aspects of distributed computations. + +History and funding +------------------- + +Brian Granger started DistArray as a NASA-funded SBIR project in 2008. +Enthought picked it up as part of a DOE Phase II SBIR [SBIR]_ to provide a +generally useful distributed array package. It builds on NumPy, MPI, MPI4Py, +IPython, IPython.parallel, and interfaces with the Trilinos suite of +distributed HPC solvers (via PyTrilinos [Trilinos]_). + +This material is based upon work supported by the Department of Energy under +Award Number DE-SC0007699. + +This report was prepared as an account of work sponsored by an agency of the +United States Government. Neither the United States Government nor any agency +thereof, nor any of their employees, makes any warranty, express or implied, +or assumes any legal liability or responsibility for the accuracy, +completeness, or usefulness of any information, apparatus, product, or process +disclosed, or represents that its use would not infringe privately owned +rights. Reference herein to any specific commercial product, process, or +service by trade name, trademark, manufacturer, or otherwise does not +necessarily constitute or imply its endorsement, recommendation, or favoring +by the United States Government or any agency thereof. The views and opinions +of authors expressed herein do not necessarily state or reflect those of the +United States Government or any agency thereof. + + +.. [protocol] http://distributed-array-protocol.readthedocs.org/en/rel-0.10.0/ +.. [Trilinos] http://trilinos.org/ +.. [petsc] http://www.mcs.anl.gov/petsc/ +.. [SBIR] http://www.sbir.gov/sbirsearch/detail/410257 + +.. vim:spell From 3ec946fb52f28cea8ef6e210a745b5cf85ebb492 Mon Sep 17 00:00:00 2001 From: Robert David Grant Date: Thu, 15 Oct 2015 17:34:16 -0500 Subject: [PATCH 2/5] Fix typos and make slight improvements. --- docs/sphinx/source/releases/release-0.6.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/source/releases/release-0.6.rst b/docs/sphinx/source/releases/release-0.6.rst index e2aa5e68..646decd2 100644 --- a/docs/sphinx/source/releases/release-0.6.rst +++ b/docs/sphinx/source/releases/release-0.6.rst @@ -32,11 +32,12 @@ Noteworthy improvements in this release include: * redistribution for block-distributed (and non-distributed) DistArrays, * experimental "quickstart" installation scripts, * an easier API for ``Context.apply`` -* expanded examples (parallel Gaussian Elimination by Prashant Mital) +* expanded and improved example notebooks, including a new parallel Gaussian + Elimination example by Prashant Mital, * compatibility with NumPy 1.9, -* expanded TravisCI testing on (OS X and Python 3.5), +* expanded TravisCI testing (OS X and Python 3.5), * logos by Erick Michaud, and -* bug-fixes and code improvements +* several bug-fixes and code improvements. Existing features ----------------- From 3a467e110d83532ae860dfbefcae9a40eb716034 Mon Sep 17 00:00:00 2001 From: Robert David Grant Date: Thu, 15 Oct 2015 17:47:49 -0500 Subject: [PATCH 3/5] Include the release notes. --- docs/www/content/release-notes/release-0-6-0.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/www/content/release-notes/release-0-6-0.rst diff --git a/docs/www/content/release-notes/release-0-6-0.rst b/docs/www/content/release-notes/release-0-6-0.rst new file mode 100644 index 00000000..248c1132 --- /dev/null +++ b/docs/www/content/release-notes/release-0-6-0.rst @@ -0,0 +1,5 @@ +:date: 2015-10-15 +:category: Release Notes +:tags: releases + +.. include:: ../../../sphinx/source/releases/release-0.6.rst From e473ed166a22f0571c2b0994fd5186edabe8ca98 Mon Sep 17 00:00:00 2001 From: Robert David Grant Date: Thu, 15 Oct 2015 18:29:46 -0500 Subject: [PATCH 4/5] Fix typos in the README. --- docs/www/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/www/README.rst b/docs/www/README.rst index f90bf347..811df5a6 100644 --- a/docs/www/README.rst +++ b/docs/www/README.rst @@ -23,9 +23,9 @@ documentation). If you want to change major features or styling of the site, you can change settings in ``pelicanconf.py``. We're currently using the ``pelican-bootstrap3`` theme for Pelican, and the ``flatly`` theme for -Bootstrap. The easist way to change the look of the site is to choose another +Bootstrap. The easiest way to change the look of the site is to choose another Bootstrap theme from `bootswatch`_, but there are also other `pelican themes`_ -available +available. .. _pelican: http://blog.getpelican.com/ From 31b75785276d3e3016a5906b6f5b0e3862f43e25 Mon Sep 17 00:00:00 2001 From: Robert David Grant Date: Thu, 15 Oct 2015 18:30:09 -0500 Subject: [PATCH 5/5] Update liquid_tags plugin. --- .../pelican-plugins/liquid_tags/.gitignore | 4 + .../www/pelican-plugins/liquid_tags/Readme.md | 83 +- docs/www/pelican-plugins/liquid_tags/audio.py | 75 ++ .../www/pelican-plugins/liquid_tags/b64img.py | 88 ++ docs/www/pelican-plugins/liquid_tags/diag.py | 177 +++ .../www/pelican-plugins/liquid_tags/flickr.py | 116 ++ docs/www/pelican-plugins/liquid_tags/giphy.py | 89 ++ docs/www/pelican-plugins/liquid_tags/gram.py | 103 ++ .../pelican-plugins/liquid_tags/graphviz.py | 128 ++ docs/www/pelican-plugins/liquid_tags/img.py | 7 +- .../liquid_tags/include_code.py | 18 +- .../liquid_tags/liquid_tags.py | 9 +- .../liquid_tags/mdx_liquid_tags.py | 29 +- .../pelican-plugins/liquid_tags/notebook.py | 85 +- .../liquid_tags/pelicanhtml_3.tpl | 55 + .../pelican-plugins/liquid_tags/soundcloud.py | 59 + .../pelican-plugins/liquid_tags/spotify.py | 55 + .../pelican-plugins/liquid_tags/test_audio.py | 38 + .../content/notebooks/test_nbformat3.ipynb | 810 ++++++++++++ .../content/notebooks/test_nbformat4.ipynb | 853 ++++++++++++ .../test-ipython-notebook-nbformat3.md | 17 + .../test-ipython-notebook-nbformat4.md | 17 + .../liquid_tags/test_data/flickr.json | 1 + .../liquid_tags/test_data/giphy.json | 2 + .../liquid_tags/test_data/output/index.html | 1153 +++++++++++++++++ .../test-ipython-notebook-nb-format-3.html | 1130 ++++++++++++++++ .../test-ipython-notebook-nb-format-4.html | 1130 ++++++++++++++++ .../liquid_tags/test_data/pelicanconf.py | 34 + .../liquid_tags/test_data/pelicanhtml_2.tpl | 1 + .../liquid_tags/test_data/pelicanhtml_3.tpl | 1 + .../liquid_tags/test_flickr.py | 79 ++ .../liquid_tags/test_generation.py | 97 ++ .../pelican-plugins/liquid_tags/test_giphy.py | 29 + .../liquid_tags/test_notebook.py | 2 +- .../liquid_tags/test_soundcloud.py | 27 + docs/www/pelican-plugins/liquid_tags/tox.ini | 18 + docs/www/pelican-plugins/liquid_tags/vimeo.py | 19 +- .../pelican-plugins/liquid_tags/youtube.py | 17 +- 38 files changed, 6598 insertions(+), 57 deletions(-) create mode 100644 docs/www/pelican-plugins/liquid_tags/.gitignore create mode 100644 docs/www/pelican-plugins/liquid_tags/audio.py create mode 100644 docs/www/pelican-plugins/liquid_tags/b64img.py create mode 100644 docs/www/pelican-plugins/liquid_tags/diag.py create mode 100644 docs/www/pelican-plugins/liquid_tags/flickr.py create mode 100644 docs/www/pelican-plugins/liquid_tags/giphy.py create mode 100644 docs/www/pelican-plugins/liquid_tags/gram.py create mode 100644 docs/www/pelican-plugins/liquid_tags/graphviz.py create mode 100644 docs/www/pelican-plugins/liquid_tags/pelicanhtml_3.tpl create mode 100644 docs/www/pelican-plugins/liquid_tags/soundcloud.py create mode 100644 docs/www/pelican-plugins/liquid_tags/spotify.py create mode 100644 docs/www/pelican-plugins/liquid_tags/test_audio.py create mode 100644 docs/www/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat3.ipynb create mode 100644 docs/www/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat4.ipynb create mode 100644 docs/www/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat3.md create mode 100644 docs/www/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat4.md create mode 100644 docs/www/pelican-plugins/liquid_tags/test_data/flickr.json create mode 100644 docs/www/pelican-plugins/liquid_tags/test_data/giphy.json create mode 100644 docs/www/pelican-plugins/liquid_tags/test_data/output/index.html create mode 100644 docs/www/pelican-plugins/liquid_tags/test_data/output/test-ipython-notebook-nb-format-3.html create mode 100644 docs/www/pelican-plugins/liquid_tags/test_data/output/test-ipython-notebook-nb-format-4.html create mode 100644 docs/www/pelican-plugins/liquid_tags/test_data/pelicanconf.py create mode 120000 docs/www/pelican-plugins/liquid_tags/test_data/pelicanhtml_2.tpl create mode 120000 docs/www/pelican-plugins/liquid_tags/test_data/pelicanhtml_3.tpl create mode 100644 docs/www/pelican-plugins/liquid_tags/test_flickr.py create mode 100644 docs/www/pelican-plugins/liquid_tags/test_generation.py create mode 100644 docs/www/pelican-plugins/liquid_tags/test_giphy.py create mode 100644 docs/www/pelican-plugins/liquid_tags/test_soundcloud.py create mode 100644 docs/www/pelican-plugins/liquid_tags/tox.ini diff --git a/docs/www/pelican-plugins/liquid_tags/.gitignore b/docs/www/pelican-plugins/liquid_tags/.gitignore new file mode 100644 index 00000000..08d9ce7e --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/.gitignore @@ -0,0 +1,4 @@ +.tox +test_data/cache/ +test_data/output/theme/ +_nb_header.html diff --git a/docs/www/pelican-plugins/liquid_tags/Readme.md b/docs/www/pelican-plugins/liquid_tags/Readme.md index 4cc37864..3b6871c5 100644 --- a/docs/www/pelican-plugins/liquid_tags/Readme.md +++ b/docs/www/pelican-plugins/liquid_tags/Readme.md @@ -25,6 +25,50 @@ To insert a sized and labeled image in your document, enable the {% img [class name(s)] path/to/image [width [height]] [title text | "title text" ["alt text"]] %} +### Base64 Image (inline image) tag + +There is one more tag for image: ``b64img``. It is based on ``img`` tag, but instead of inserting link on image it acutally reads image and inserts it as base64 text into ``= 1.0 [[1](#1)] +- First, you will need to install IPython: + + pip install ipython==2.4.1 - After typing "make html" when using the notebook tag, a file called ``_nb_header.html`` will be produced in the main directory. The content @@ -143,8 +207,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 ``# `` will be collapsed when the html page is loaded and can be expanded by clicking on them. Cells containing the @@ -152,4 +216,13 @@ comment line ``# `` 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. -[1] http://ipython.org/ +## Testing + +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 +tox +``` + +[IPython]: http://ipython.org/ diff --git a/docs/www/pelican-plugins/liquid_tags/audio.py b/docs/www/pelican-plugins/liquid_tags/audio.py new file mode 100644 index 00000000..a500ce6a --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/audio.py @@ -0,0 +1,75 @@ +""" +Audio Tag +--------- +This implements a Liquid-style audio tag for Pelican, +based on the pelican video plugin [1]_ + +Syntax +------ +{% audio url/to/audio [url/to/audio] [/url/to/audio] %} + +Example +------- +{% audio http://example.tld/foo.mp3 http://example.tld/foo.ogg %} + +Output +------ + + +[1] https://github.com/getpelican/pelican-plugins/blob/master/liquid_tags/video.py +""" +import os +import re +from .mdx_liquid_tags import LiquidTags + +SYNTAX = "{% audio url/to/audio [url/to/audio] [/url/to/audio] %}" +AUDIO = re.compile(r'(/\S+|https?:\S+)(?:\s+(/\S+|https?:\S+))?(?:\s+(/\S+|https?:\S+))?') + +AUDIO_TYPEDICT = {'.mp3': 'audio/mpeg', + '.ogg': 'audio/ogg', + '.oga': 'audio/ogg', + '.opus': 'audio/ogg', + '.wav': 'audio/wav', + '.mp4': 'audio/mp4'} + + +def create_html(markup): + match = AUDIO.search(markup) + if match: + groups = match.groups() + audio_files = [g for g in groups if g] + + if any(audio_files): + audio_out = '' + + else: + raise ValueError("Error processing input, " + "expected syntax: {0}".format(SYNTAX)) + + return audio_out + + +@LiquidTags.register('audio') +def audio(preprocessor, tag, markup): + return create_html(markup) + + +# --------------------------------------------------- +# This import allows image tag to be a Pelican plugin +from liquid_tags import register diff --git a/docs/www/pelican-plugins/liquid_tags/b64img.py b/docs/www/pelican-plugins/liquid_tags/b64img.py new file mode 100644 index 00000000..a1ee919d --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/b64img.py @@ -0,0 +1,88 @@ +""" +Image Tag +--------- +This implements a Liquid-style image tag for Pelican, +based on the liquid img tag which is based on the octopress image tag [1]_ + +Syntax +------ +{% b64img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | "title text" ["alt text"]] %} + +Examples +-------- +{% b64img /images/ninja.png Ninja Attack! %} +{% b64img left half http://site.com/images/ninja.png Ninja Attack! %} +{% b64img left half http://site.com/images/ninja.png 150 150 "Ninja Attack!" "Ninja in attack posture" %} + +Output +------ + +Ninja Attack! +Ninja in attack posture + +[1] https://github.com/imathis/octopress/blob/master/plugins/image_tag.rb +""" +import re +import base64 +import urllib2 +from .mdx_liquid_tags import LiquidTags +import six + +SYNTAX = '{% b64img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | "title text" ["alt text"]] %}' + +# Regular expression to match the entire syntax +ReImg = re.compile("""(?P\S.*\s+)?(?P(?:https?:\/\/|\/|\S+\/)\S+)(?:\s+(?P\d+))?(?:\s+(?P\d+))?(?P\s+.+)?""") + +# Regular expression to split the title and alt text +ReTitleAlt = re.compile("""(?:"|')(?P<title>[^"']+)?(?:"|')\s+(?:"|')(?P<alt>[^"']+)?(?:"|')""") + + +def _get_file(src): + """ Return content from local or remote file. """ + try: + if '://' in src or src[0:2] == '//': # Most likely this is remote file + response = urllib2.urlopen(src) + return response.read() + else: + with open(src, 'rb') as fh: + return fh.read() + except Exception as e: + raise RuntimeError('Error generating base64image: {}'.format(e)) + + +def base64image(src): + """ Generate base64 encoded image from srouce file. """ + return base64.b64encode(_get_file(src)) + + +@LiquidTags.register('b64img') +def b64img(preprocessor, tag, markup): + attrs = None + + # Parse the markup string + match = ReImg.search(markup) + if match: + attrs = dict([(key, val.strip()) + for (key, val) in six.iteritems(match.groupdict()) if val]) + else: + raise ValueError('Error processing input. ' + 'Expected syntax: {0}'.format(SYNTAX)) + + # Check if alt text is present -- if so, split it from title + if 'title' in attrs: + match = ReTitleAlt.search(attrs['title']) + if match: + attrs.update(match.groupdict()) + if not attrs.get('alt'): + attrs['alt'] = attrs['title'] + + attrs['src'] = 'data:;base64,{}'.format(base64image(attrs['src'])) + + # Return the formatted text + return "<img {0}>".format(' '.join('{0}="{1}"'.format(key, val) + for (key, val) in six.iteritems(attrs))) + + +#---------------------------------------------------------------------- +# This import allows image tag to be a Pelican plugin +from .liquid_tags import register diff --git a/docs/www/pelican-plugins/liquid_tags/diag.py b/docs/www/pelican-plugins/liquid_tags/diag.py new file mode 100644 index 00000000..553e3b3a --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/diag.py @@ -0,0 +1,177 @@ +""" +Blockdiag Tag +--------- +This tag implements a liquid style tag for blockdiag [1]. You can use different +diagram types like blockdiag, seqdiag, packetdiag etc. [1] + + +[1] http://blockdiag.com/en/blockdiag/ + +Syntax +------ +{% blockdiag { + <diagramm type> { + <CODE> + } + } +%} + +Examples +-------- +{% blockdiag { + blockdiag { + A -> B -> C; + B -> D; + } + } +%} + + +{% blockdiag { + actdiag { + A -> B -> C -> D -> E; + + lane { + A; C; E; + } + lane { + B; D; + } + } + } +%} + + +{% blockdiag { + packetdiag { + 0-7: Source Port + 8-15: Destination Port + 16-31: Sequence Number + 32-47: Acknowledgment Number + } + } +%} + +... + + +Output +------ +<div class="blockdiag" style="align: center;"><img src="data:image/png;base64,_BASE64_IMAGE DATA_/></div> + +""" + +import io +import os +import sys + +import base64 +import re +from .mdx_liquid_tags import LiquidTags + + +SYNTAX = '{% blockdiag [diagram type] [code] %}' +DOT_BLOCK_RE = re.compile(r'^\s*(?P<diagram>\w+).*$', re.MULTILINE | re.DOTALL) + +_draw_mode = 'PNG' +_publish_mode = 'PNG' + + +def get_diag(code, command): + """ Generate diagramm and return data """ + import tempfile + import shutil + code = code + u'\n' + + try: + tmpdir = tempfile.mkdtemp() + fd, diag_name = tempfile.mkstemp(dir=tmpdir) + + f = os.fdopen(fd, "w") + f.write(code.encode('utf-8')) + f.close() + + format = _draw_mode.lower() + draw_name = diag_name + '.' + format + + saved_argv = sys.argv + argv = [diag_name, '-T', format, '-o', draw_name] + + if _draw_mode == 'SVG': + argv += ['--ignore-pil'] + + # Run command + command.main(argv) + + # Read image data from file + file_name = diag_name + '.' + _publish_mode.lower() + + with io.open(file_name, 'rb') as f: + data = f.read() + f.close() + + finally: + for file in os.listdir(tmpdir): + os.unlink(tmpdir + "/" + file) + + # os.rmdir will fail -> use shutil + shutil.rmtree(tmpdir) + + return data + + +def diag(code, command): + if command == "blockdiag": # blockdiag + import blockdiag.command + return get_diag(code, blockdiag.command) + + elif command == "diagram": # diagram + import blockdiag.command + return get_diag(code, blockdiag.command) + + elif command == "seqdiag": # seqdiag + import seqdiag.command + return get_diag(code, seqdiag.command) + + elif command == "actdiag": # actdiag + import actdiag.command + return get_diag(code, actdiag.command) + + elif command == "nwdiag": # nwdiag + import nwdiag.command + return get_diag(code, nwdiag.command) + + elif command == "packetdiag": # packetdiag + import packetdiag.command + return get_diag(code, packetdiag.command) + + elif command == "rackdiag": # racketdiag + import rackdiag.command + return get_diag(code, rackdiag.command) + + else: # not found + print("No such command %s" % command) + return None + + +@LiquidTags.register("blockdiag") +def blockdiag_parser(preprocessor, tag, markup): + """ Blockdiag parser """ + m = DOT_BLOCK_RE.search(markup) + if m: + # Get diagram type and code + diagram = m.group('diagram').strip() + code = markup + + # Run command + output = diag(code, diagram) + + if output: + # Return Base64 encoded image + return '<div class="blockdiag" style="align: center;"><img src="data:image/png;base64,%s"></div>' % base64.b64encode(output) + else: + raise ValueError('Error processing input. ' + 'Expected syntax: {0}'.format(SYNTAX)) + +# This import allows image tag to be a Pelican plugin +from .liquid_tags import register diff --git a/docs/www/pelican-plugins/liquid_tags/flickr.py b/docs/www/pelican-plugins/liquid_tags/flickr.py new file mode 100644 index 00000000..63da2205 --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/flickr.py @@ -0,0 +1,116 @@ +""" +Flickr Tag +---------- +This implements a Liquid-style flickr tag for Pelican. + +IMPORTANT: You have to create a API key to access the flickr api. +You can do this `here <https://www.flickr.com/services/apps/create/apply>`_. +Add the created key to your config under FLICKR_API_KEY. + +Syntax +------ +{% flickr image_id [small|medium|large] ["alt text"|'alt text'] %} + +Example +-------- +{% flickr 18841055371 large "Fichte"} + +Output +------ +<a href="https://www.flickr.com/photos/marvinxsteadfast/18841055371/"><img src="https://farm6.staticflickr.com/5552/18841055371_17ac287217_b.jpg" alt="Fichte"></a> +""" +import json +import re +try: + from urllib.request import urlopen + from urllib.parse import urlencode +except ImportError: + from urllib import urlopen, urlencode +from .mdx_liquid_tags import LiquidTags + + +SYNTAX = '''{% flickr image_id [small|medium|large] ["alt text"|'alt text'] %}''' +PARSE_SYNTAX = re.compile(('''(?P<photo_id>\S+)''' + '''(?:\s+(?P<size>large|medium|small))?''' + '''(?:\s+(['"]{0,1})(?P<alt>.+)(\\3))?''')) + + +def get_info(photo_id, api_key): + ''' Get photo informations from flickr api. ''' + query = urlencode({ + 'method': 'flickr.photos.getInfo', + 'api_key': api_key, + 'photo_id': photo_id, + 'format': 'json', + 'nojsoncallback': '1' + }) + + r = urlopen('https://api.flickr.com/services/rest/?' + query) + info = json.loads(r.read().decode('utf-8')) + + if info['stat'] == 'fail': + raise ValueError(info['message']) + + return info + + +def source_url(farm, server, id, secret, size): + ''' Url for direct jpg use. ''' + if size == 'small': + img_size = 'n' + elif size == 'medium': + img_size = 'c' + elif size == 'large': + img_size = 'b' + + return 'https://farm{}.staticflickr.com/{}/{}_{}_{}.jpg'.format( + farm, server, id, secret, img_size) + + +def generate_html(attrs, api_key): + ''' Returns html code. ''' + # getting flickr api data + flickr_data = get_info(attrs['photo_id'], api_key) + + # if size is not defined it will use large as image size + if 'size' not in attrs.keys(): + attrs['size'] = 'large' + + # if no alt is defined it will use the flickr image title + if 'alt' not in attrs.keys(): + attrs['alt'] = flickr_data['photo']['title']['_content'] + + # return final html code + return '<a href="{}"><img src="{}" alt="{}"></a>'.format( + flickr_data['photo']['urls']['url'][0]['_content'], + source_url(flickr_data['photo']['farm'], + flickr_data['photo']['server'], + attrs['photo_id'], + flickr_data['photo']['secret'], + attrs['size']), + attrs['alt']) + + +@LiquidTags.register('flickr') +def flickr(preprocessor, tag, markup): + # getting flickr api key out of config + api_key = preprocessor.configs.getConfig('FLICKR_API_KEY') + + # parse markup and extract data + attrs = None + + match = PARSE_SYNTAX.search(markup) + if match: + attrs = dict( + [(key, value.strip()) + for (key, value) in match.groupdict().items() if value]) + else: + raise ValueError('Error processing input. ' + 'Expected syntax: {}'.format(SYNTAX)) + + return generate_html(attrs, api_key) + + +# --------------------------------------------------- +# This import allows image tag to be a Pelican plugin +from liquid_tags import register diff --git a/docs/www/pelican-plugins/liquid_tags/giphy.py b/docs/www/pelican-plugins/liquid_tags/giphy.py new file mode 100644 index 00000000..c61b305d --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/giphy.py @@ -0,0 +1,89 @@ +""" +Giphy Tag +--------- + +This implements a Liquid-style Giphy tag for Pelican. + +IMPORTANT: You have to request a production API key from giphy `here <https://api.giphy.com/submit>`. +For the first runs you could also use the public beta key you can get `here <https://github.com/giphy/GiphyAPI>`. + +Syntax +------ +{% giphy gif_id ["alt text"|'alt text'] %} + +Example +------- +{% giphy aMSJFS6oFX0fC 'ive had some free time' %} + +Output +------ +<a href="http://giphy.com/gifs/veronica-mars-aMSJFS6oFX0fC"><img src="http://media4.giphy.com/media/aMSJFS6oFX0fC/giphy.gif" alt="ive had some free time"></a> +""" +import json +import re +try: + from urllib.request import urlopen +except ImportError: + from urllib import urlopen +from .mdx_liquid_tags import LiquidTags + + +SYNTAX = '''{% giphy gif_id ["alt text"|'alt text'] %}''' +GIPHY = re.compile('''(?P<gif_id>[\S+]+)(?:\s+(['"]{0,1})(?P<alt>.+)(\\2))?''') + + +def get_gif(api_key, gif_id): + '''Returns dict with gif informations from the API.''' + url = 'http://api.giphy.com/v1/gifs/{}?api_key={}'.format(gif_id, api_key) + r = urlopen(url) + + return json.loads(r.read().decode('utf-8')) + + +def create_html(api_key, attrs): + '''Returns complete html tag string.''' + gif = get_gif(api_key, attrs['gif_id']) + + if 'alt' not in attrs.keys(): + attrs['alt'] = 'source: {}'.format(gif['data']['source']) + + html_out = '<a href="{}">'.format(gif['data']['url']) + html_out += '<img src="{}" alt="{}">'.format( + gif['data']['images']['original']['url'], + attrs['alt']) + html_out += '</a>' + + return html_out + + +def main(api_key, markup): + '''Doing the regex parsing and running the create_html function.''' + match = GIPHY.search(markup) + + attrs = None + + if match: + attrs = dict( + [(key, value.strip()) + for (key, value) in match.groupdict().items() if value]) + + else: + raise ValueError('Error processing input. ' + 'Expected syntax: {}'.format(SYNTAX)) + + return create_html(api_key, attrs) + + +@LiquidTags.register('giphy') +def giphy(preprocessor, tag, markup): + api_key = preprocessor.configs.getConfig('GIPHY_API_KEY') + + if api_key is None: + raise ValueError('Please set GIPHY_API_KEY.') + + return main(api_key, markup) + + +# --------------------------------------------------- +# This import allows image tag to be a Pelican plugin +from liquid_tags import register diff --git a/docs/www/pelican-plugins/liquid_tags/gram.py b/docs/www/pelican-plugins/liquid_tags/gram.py new file mode 100644 index 00000000..8fe18dde --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/gram.py @@ -0,0 +1,103 @@ +""" +Instagram Image Tag +------------------- + +By `Tom Spalding <https://github.com/digitalvapor>`_ + +You can see a working example at `antivapor.net/instagram-tag.html <http://antivapor.net/instagram-tag.html>`_. + +Based on `Liquid Image Tag <https://github.com/getpelican/pelican-plugins/blob/master/liquid_tags/img.py>`_ by `Jake Vanderplas <https://github.com/jakevdp>`_. + +Optional Todo: +* Query JSON to automatically include descriptions. + http://api.instagram.com/oembed?url=http://instagr.am/p/olw8jXiz1_/ + and option to add wrapping anchor link to original http://instagram.com/p/olw8jXiz1_ +* Default to size m + http://instagr.am/p/olw8jXiz1_/media/?size=t + http://instagr.am/p/olw8jXiz1_/media +* Provide examples using with [Better Figures and Images](https://github.com/getpelican/pelican-plugins/tree/master/better_figures_and_images). + +Syntax +------ + + {% gram shortcode [size] [width] [class name(s)] [title text | "title text" ["alt text"]] %} + +where size is t, m, or l, and it defaults to m. see http://instagram.com/developer/embedding. + +Examples +-------- + + {% gram pFG7naIZkr t %} + {% gram pFJE11IZnx %} + {% gram pFI0CAIZna l 400 figure 'pretty turkey tail fungus' %} + {% gram rOru21oZpe l 450 test_class instagram 'warehouse window title' 'alt text' %} + +Output +------ + + <img src="http://photos-c.ak.instagram.com/hphotos-ak-xaf1/t51.2885-15/917172_604907902963826_254280879_n.jpg" width="450" title="warehouse window title" alt="alt text" class="test_class instagram"> +""" +import re +try: + from urllib.request import urlopen +except ImportError: + from urllib import urlopen +from .mdx_liquid_tags import LiquidTags + +SYNTAX = '{% gram shortcode [size] [width] [class name(s)] [title text | "title text" ["alt text"]] %}' + +# Regular expression for full syntax +# ReGram = re.compile("""(?P<shortcode>\S+)(?:\s+(?P<size>[tml]?))?(?:\s+(?P<width>\d*))?(?:\s+(?P<class>\S*))?(?P<title>\s+.+)?""") +ReGram = re.compile("""(?P<shortcode>\S+)(?:\s+(?P<size>[tml]?))?(?:\s+(?P<width>\d*))?(?:\s+(?P<class>[^']*))?(?P<title>.+)?""") + +# Regular expression to split the title and alt text +ReTitleAlt = re.compile("""(?:"|')(?P<title>[^"']+)?(?:"|')\s+(?:"|')(?P<alt>[^"']+)?(?:"|')""") + +@LiquidTags.register('gram') +def gram(preprocessor, tag, markup): + + attrs = None + + # Parse the markup string + match = ReGram.search(markup) + if match: + attrs = dict([(key, val.strip()) + for (key, val) in match.groupdict().items() if val]) + else: + raise ValueError('Error processing input. ' + 'Expected syntax: {0}'.format(SYNTAX)) + + # Construct URI + #print(attrs) + shortcode = attrs['shortcode'] + url = 'http://instagr.am/p/'+shortcode+'/media/' + del attrs['shortcode'] + + if 'size' in attrs: + size = '?size={0}'.format(attrs['size']) + url = url+size + del attrs['size'] + + r = urlopen(url) + + if(r.getcode()==404): + raise ValueError('%s isnt a photo.'%shortcode) + + gram_url = r.geturl() + + # Check if alt text is present -- if so, split it from title + if 'title' in attrs: + match = ReTitleAlt.search(attrs['title']) + if match: + attrs.update(match.groupdict()) + if not attrs.get('alt'): + attrs['alt'] = attrs['title'] + + #print('updated dict: '+repr(attrs)) + + # Return the formatted text + return '<img src="{0}"{1}>'.format(gram_url,' '.join(' {0}="{1}"'.format(key,val) for (key,val) in attrs.items())) + +#---------------------------------------------------------------------- +# This import allows image tag to be a Pelican plugin +from liquid_tags import register diff --git a/docs/www/pelican-plugins/liquid_tags/graphviz.py b/docs/www/pelican-plugins/liquid_tags/graphviz.py new file mode 100644 index 00000000..d2dea0b1 --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/graphviz.py @@ -0,0 +1,128 @@ +""" +GraphViz Tag +--------- +This implements a Liquid-style graphviz tag for Pelican. You can use different +Graphviz programs like dot, neato, twopi etc. [1] + + +[1] http://www.graphviz.org/ + +Syntax +------ +{% graphviz + <program> { + <DOT code> + } +%} + +Examples +-------- +{% graphviz + dot { + digraph graphname { + a -> b -> c; + b -> d; + } + } +%} + + +{% graphviz + twopi { + <code goes here> + } +%} + + +{% graphviz + neato { + <code goes here> + } +%} + +... + + +Output +------ +<div class="graphviz" style="text-align: center;"><img src="data:image/png;base64,_BASE64_IMAGE DATA_/></div> + +""" + +import base64 +import re +from .mdx_liquid_tags import LiquidTags + +SYNTAX = '{% dot graphviz [program] [dot code] %}' +DOT_BLOCK_RE = re.compile(r'^\s*(?P<program>\w+)\s*\{\s*(?P<code>.*\})\s*\}$', re.MULTILINE | re.DOTALL) + + +def run_graphviz(program, code, options=[], format='png'): + """ Runs graphviz programs and returns image data + + Copied from https://github.com/tkf/ipython-hierarchymagic/blob/master/hierarchymagic.py + """ + import os + from subprocess import Popen, PIPE + + dot_args = [program] + options + ['-T', format] + + if os.name == 'nt': + # Avoid opening shell window. + # * https://github.com/tkf/ipython-hierarchymagic/issues/1 + # * http://stackoverflow.com/a/2935727/727827 + p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE, creationflags=0x08000000) + else: + p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE) + wentwrong = False + + try: + # Graphviz may close standard input when an error occurs, + # resulting in a broken pipe on communicate() + stdout, stderr = p.communicate(code.encode('utf-8')) + except (OSError, IOError) as err: + if err.errno != EPIPE: + raise + wentwrong = True + except IOError as err: + if err.errno != EINVAL: + raise + wentwrong = True + + if wentwrong: + # in this case, read the standard output and standard error streams + # directly, to get the error message(s) + stdout, stderr = p.stdout.read(), p.stderr.read() + p.wait() + + if p.returncode != 0: + raise RuntimeError('dot exited with error:\n[stderr]\n{0}'.format(stderr.decode('utf-8'))) + + return stdout + + +@LiquidTags.register('graphviz') +def graphviz_parser(preprocessor, tag, markup): + """ Simple Graphviz parser """ + + # Parse the markup string + m = DOT_BLOCK_RE.search(markup) + if m: + # Get program and DOT code + code = m.group('code') + program = m.group('program').strip() + + # Run specified program with our markup + output = run_graphviz(program, code) + + # Return Base64 encoded image + return '<div class="graphviz" style="text-align: center;"><img src="data:image/png;base64,%s"></div>' % base64.b64encode(output) + + else: + raise ValueError('Error processing input. ' + 'Expected syntax: {0}'.format(SYNTAX)) + +#---------------------------------------------------------------------- +# This import allows image tag to be a Pelican plugin +from .liquid_tags import register + diff --git a/docs/www/pelican-plugins/liquid_tags/img.py b/docs/www/pelican-plugins/liquid_tags/img.py index 26039e4c..22d83e43 100644 --- a/docs/www/pelican-plugins/liquid_tags/img.py +++ b/docs/www/pelican-plugins/liquid_tags/img.py @@ -24,6 +24,7 @@ """ import re from .mdx_liquid_tags import LiquidTags +import six SYNTAX = '{% img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | "title text" ["alt text"]] %}' @@ -42,7 +43,7 @@ def img(preprocessor, tag, markup): match = ReImg.search(markup) if match: attrs = dict([(key, val.strip()) - for (key, val) in match.groupdict().iteritems() if val]) + for (key, val) in six.iteritems(match.groupdict()) if val]) else: raise ValueError('Error processing input. ' 'Expected syntax: {0}'.format(SYNTAX)) @@ -57,9 +58,9 @@ def img(preprocessor, tag, markup): # Return the formatted text return "<img {0}>".format(' '.join('{0}="{1}"'.format(key, val) - for (key, val) in attrs.iteritems())) + for (key, val) in six.iteritems(attrs))) #---------------------------------------------------------------------- # This import allows image tag to be a Pelican plugin -from liquid_tags import register +from .liquid_tags import register diff --git a/docs/www/pelican-plugins/liquid_tags/include_code.py b/docs/www/pelican-plugins/liquid_tags/include_code.py index f454c6c4..ac466e7a 100644 --- a/docs/www/pelican-plugins/liquid_tags/include_code.py +++ b/docs/www/pelican-plugins/liquid_tags/include_code.py @@ -6,7 +6,7 @@ Syntax ------ -{% include_code path/to/code [lang:python] [Title text] %} +{% include_code path/to/code [lang:python] [Title text] [codec:utf8] %} The "path to code" is specified relative to the ``code`` subdirectory of the content directory Optionally, this subdirectory can be specified in the @@ -14,6 +14,14 @@ CODE_DIR = 'code' +If your input file is not ASCII/UTF-8 encoded, you need to specify the +appropriate input codec by using the ``codec`` option. +Example ``codec:iso-8859-1`` +Using this option does not affect the output encoding. + +For a list of valid codec identifiers, see +https://docs.python.org/2/library/codecs.html#standard-encodings + Example ------- {% include_code myscript.py %} @@ -45,6 +53,8 @@ (?:\s+)? # Whitespace (?P<hidefilename>:hidefilename:)? # Hidefilename flag (?:\s+)? # Whitespace +(?:(?:codec:)(?P<codec>\S+))? # Optional language +(?:\s+)? # Whitespace (?P<title>.+)?$ # Optional title """, re.VERBOSE) @@ -61,6 +71,7 @@ def include_code(preprocessor, tag, markup): argdict = match.groupdict() title = argdict['title'] or "" lang = argdict['lang'] + codec = argdict['codec'] or "utf8" lines = argdict['lines'] hide_filename = bool(argdict['hidefilename']) if lines: @@ -71,8 +82,7 @@ def include_code(preprocessor, tag, markup): raise ValueError("Error processing input, " "expected syntax: {0}".format(SYNTAX)) - settings = preprocessor.configs.config['settings'] - code_dir = settings.get('CODE_DIR', 'code') + code_dir = preprocessor.configs.getConfig('CODE_DIR') code_path = os.path.join('content', code_dir, src) if not os.path.exists(code_path): @@ -117,7 +127,7 @@ def include_code(preprocessor, tag, markup): source = (open_tag + '\n\n ' + lang_include - + '\n '.join(code.split('\n')) + '\n\n' + + '\n '.join(code.decode(codec).split('\n')) + '\n\n' + close_tag + '\n') return source diff --git a/docs/www/pelican-plugins/liquid_tags/liquid_tags.py b/docs/www/pelican-plugins/liquid_tags/liquid_tags.py index 6a3518b2..ed016645 100644 --- a/docs/www/pelican-plugins/liquid_tags/liquid_tags.py +++ b/docs/www/pelican-plugins/liquid_tags/liquid_tags.py @@ -1,5 +1,5 @@ from pelican import signals -from .mdx_liquid_tags import LiquidTags +from .mdx_liquid_tags import LiquidTags, LT_CONFIG def addLiquidTags(gen): @@ -8,7 +8,12 @@ def addLiquidTags(gen): gen.settings['MD_EXTENSIONS'] = DEFAULT_CONFIG['MD_EXTENSIONS'] if LiquidTags not in gen.settings['MD_EXTENSIONS']: - configs = dict(settings=gen.settings) + configs = dict() + for key,value in LT_CONFIG.items(): + configs[key]=value + for key,value in gen.settings.items(): + if key in LT_CONFIG: + configs[key]=value gen.settings['MD_EXTENSIONS'].append(LiquidTags(configs)) diff --git a/docs/www/pelican-plugins/liquid_tags/mdx_liquid_tags.py b/docs/www/pelican-plugins/liquid_tags/mdx_liquid_tags.py index 3204f87f..60edf5e2 100644 --- a/docs/www/pelican-plugins/liquid_tags/mdx_liquid_tags.py +++ b/docs/www/pelican-plugins/liquid_tags/mdx_liquid_tags.py @@ -17,9 +17,18 @@ from functools import wraps # Define some regular expressions -LIQUID_TAG = re.compile(r'\{%.*?%\}') +LIQUID_TAG = re.compile(r'\{%.*?%\}', re.MULTILINE | re.DOTALL) EXTRACT_TAG = re.compile(r'(?:\s*)(\S+)(?:\s*)') - +LT_CONFIG = { 'CODE_DIR': 'code', + 'NOTEBOOK_DIR': 'notebooks', + 'FLICKR_API_KEY': 'flickr', + 'GIPHY_API_KEY': 'giphy' +} +LT_HELP = { 'CODE_DIR' : 'Code directory for include_code subplugin', + 'NOTEBOOK_DIR' : 'Notebook directory for notebook subplugin', + 'FLICKR_API_KEY': 'Flickr key for accessing the API', + 'GIPHY_API_KEY': 'Giphy key for accessing the API' +} class _LiquidTagsPreprocessor(markdown.preprocessors.Preprocessor): _tags = {} @@ -37,10 +46,10 @@ def run(self, lines): markup = EXTRACT_TAG.sub('', markup, 1) if tag in self._tags: liquid_tags[i] = self._tags[tag](self, tag, markup.strip()) - + # add an empty string to liquid_tags so that chaining works liquid_tags.append('') - + # reconstruct string page = ''.join(itertools.chain(*zip(LIQUID_TAG.split(page), liquid_tags))) @@ -51,6 +60,18 @@ def run(self, lines): class LiquidTags(markdown.Extension): """Wrapper for MDPreprocessor""" + def __init__(self, config): + try: + # Needed for markdown versions >= 2.5 + for key,value in LT_CONFIG.items(): + self.config[key] = [value,LT_HELP[key]] + super(LiquidTags,self).__init__(**config) + except AttributeError: + # Markdown versions < 2.5 + for key,value in LT_CONFIG.items(): + config[key] = [config[key],LT_HELP[key]] + super(LiquidTags,self).__init__(config) + @classmethod def register(cls, tag): """Decorator to register a new include tag""" diff --git a/docs/www/pelican-plugins/liquid_tags/notebook.py b/docs/www/pelican-plugins/liquid_tags/notebook.py index 27954636..fb46587a 100644 --- a/docs/www/pelican-plugins/liquid_tags/notebook.py +++ b/docs/www/pelican-plugins/liquid_tags/notebook.py @@ -44,43 +44,65 @@ that this CSS will not override formats within the blog theme, but there may still be some conflicts. """ +import warnings import re import os from functools import partial from .mdx_liquid_tags import LiquidTags -from distutils.version import LooseVersion import IPython -if not LooseVersion(IPython.__version__) >= '1.0': +IPYTHON_VERSION = IPython.version_info[0] + +try: + import nbformat +except: + pass + +if not IPYTHON_VERSION >= 1: raise ValueError("IPython version 1.0+ required for notebook tag") -from IPython import nbconvert +if IPYTHON_VERSION > 1: + warnings.warn("Pelican plugin is not designed to work with IPython " + "versions greater than 1.x. CSS styles have changed in " + "later releases.") try: - from IPython.nbconvert.filters.highlight import _pygments_highlight + from nbconvert.filters.highlight import _pygments_highlight except ImportError: - # IPython < 2.0 - from IPython.nbconvert.filters.highlight import _pygment_highlight as _pygments_highlight + try: + from IPython.nbconvert.filters.highlight import _pygments_highlight + except ImportError: + # IPython < 2.0 + from IPython.nbconvert.filters.highlight import _pygment_highlight as _pygments_highlight 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 nbconvert.exporters import HTMLExporter +except ImportError: + from IPython.nbconvert.exporters import HTMLExporter try: - from IPython.nbconvert.preprocessors import Preprocessor + from traitlets.config import Config except ImportError: - # IPython < 2.0 - from IPython.nbconvert.transformers import Transformer as Preprocessor + from IPython.config import Config -from IPython.utils.traitlets import Integer -from copy import deepcopy +try: + from nbconvert.preprocessors import Preprocessor +except ImportError: + try: + from IPython.nbconvert.preprocessors import Preprocessor + except ImportError: + # IPython < 2.0 + from IPython.nbconvert.transformers import Transformer as Preprocessor -from jinja2 import DictLoader +try: + from traitlets import Integer +except ImportError: + from IPython.utils.traitlets import Integer +from copy import deepcopy #---------------------------------------------------------------------- # Some code that will be added to the header: @@ -209,9 +231,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 @@ -261,8 +287,7 @@ def notebook(preprocessor, tag, markup): language_applied_highlighter = partial(custom_highlighter, language=language) - settings = preprocessor.configs.config['settings'] - nb_dir = settings.get('NOTEBOOK_DIR', 'notebooks') + nb_dir = preprocessor.configs.getConfig('NOTEBOOK_DIR') nb_path = os.path.join('content', nb_dir, src) if not os.path.exists(nb_path): @@ -275,18 +300,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}, @@ -295,7 +323,14 @@ 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: + try: + nb_json = nbformat.reads(nb_text, as_version=4) + except: + 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. diff --git a/docs/www/pelican-plugins/liquid_tags/pelicanhtml_3.tpl b/docs/www/pelican-plugins/liquid_tags/pelicanhtml_3.tpl new file mode 100644 index 00000000..cb9687e4 --- /dev/null +++ b/docs/www/pelican-plugins/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 %} diff --git a/docs/www/pelican-plugins/liquid_tags/soundcloud.py b/docs/www/pelican-plugins/liquid_tags/soundcloud.py new file mode 100644 index 00000000..6722ffca --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/soundcloud.py @@ -0,0 +1,59 @@ +""" +Soundcloud Tag +-------------- +This implements a Liquid-style soundcloud tag for Pelican. + +It asks the official Soundcloud-API for the widget html code. + +Syntax +------ +`{% soundcloud track_url %}` + +Example +------- +`{% soundcloud https://soundcloud.com/luftmentsh/hakotel %}` + +Output +------ +`<iframe width="100%" height="400" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?visual=true&url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F33875102&show_artwork=true"></iframe>` +""" +from .mdx_liquid_tags import LiquidTags +import re +import json +try: + from urllib.request import urlopen +except ImportError: + from urllib import urlopen + + +SYNTAX = '{% soundcloud track_url %}' +PARSE_SYNTAX = re.compile(r'(?P<track_url>https?://soundcloud.com/[\S]+)') + + +def get_widget(track_url): + r = urlopen( + 'http://soundcloud.com/oembed', + data='format=json&url={}'.format(track_url).encode('utf-8')) + + return json.loads(r.read().decode('utf-8'))['html'] + + +def match_it(markup): + match = PARSE_SYNTAX.search(markup) + if match: + return match.groupdict() + else: + raise ValueError('Error processing input. ' + 'Expected syntax: {}'.format(SYNTAX)) + + +@LiquidTags.register('soundcloud') +def soundcloud(preprocessor, tag, markup): + track_url = match_it(markup)['track_url'] + + return get_widget(track_url) + + +# --------------------------------------------------- +# This import allows image tag to be a Pelican plugin +from liquid_tags import register diff --git a/docs/www/pelican-plugins/liquid_tags/spotify.py b/docs/www/pelican-plugins/liquid_tags/spotify.py new file mode 100644 index 00000000..f3eb8752 --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/spotify.py @@ -0,0 +1,55 @@ +""" +Spotify Tag +--------- +This implements a Liquid-style spotify tag for Pelican, +based on the jekyll / octopress youtube tag [1]_ + +Syntax +------ +{% spotify id %} + +Example +------- +{% spotify 1HNZcRFlIKwHAJD3LxvX4d %} + +Output +------ +<iframe + src='https://embed.spotify.com/?uri=spotify:track:1HNZcRFlIKwHAJD3LxvX4d' + width='300' height='380' frameborder='0' allowtransparency='true'> +</iframe> +""" +import re +from .mdx_liquid_tags import LiquidTags + +SYNTAX = "{% spotify id %}" + +SPOTIFY = re.compile(r'(\w+)(\s+(\d+)\s(\d+))?') + + +@LiquidTags.register('spotify') +def spotify(preprocessor, tag, markup): + spotify_id = None + + match = SPOTIFY.search(markup) + if match: + groups = match.groups() + spotify_id = groups[0] + + if spotify_id: + spotify_out = """ + <iframe src='https://embed.spotify.com/?uri=spotify:track:{}' + width='300' + height='380' + frameborder='0' + allowtransparency='true'></iframe>""".format(spotify_id).strip() + else: + raise ValueError("Error processing input, " + "expected syntax: {0}".format(SYNTAX)) + + return spotify_out + + +# --------------------------------------------------- +# This import allows image tag to be a Pelican plugin +from liquid_tags import register # noqa diff --git a/docs/www/pelican-plugins/liquid_tags/test_audio.py b/docs/www/pelican-plugins/liquid_tags/test_audio.py new file mode 100644 index 00000000..5d261040 --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_audio.py @@ -0,0 +1,38 @@ +from . import audio +import pytest +import re + + +@pytest.mark.parametrize('input,expected', [ + ('http://foo.bar https://bar.foo', + ('http://foo.bar', 'https://bar.foo', None)), + ('http://test.foo', + ('http://test.foo', None, None)), + ('https://test.foo', + ('https://test.foo', None, None)), + ('http://foo.foo https://bar.bar http://zonk.zonk', + ('http://foo.foo', 'https://bar.bar', 'http://zonk.zonk')) +]) +def test_regex(input, expected): + assert re.match(audio.AUDIO, input).groups() == expected + + +@pytest.mark.parametrize('input,expected', [ + ('http://foo.foo/foo.mp3', + ('<audio controls>' + '<source src="http://foo.foo/foo.mp3" type="audio/mpeg">' + 'Your browser does not support the audio element.</audio>')), + ('https://foo.foo/foo.ogg http://bar.bar/bar.opus', + ('<audio controls>' + '<source src="https://foo.foo/foo.ogg" type="audio/ogg">' + '<source src="http://bar.bar/bar.opus" type="audio/ogg">' + 'Your browser does not support the audio element.</audio>')), + ('http://1.de/1.wav http://2.de/2.mp4 http://3.de/3.ogg', + ('<audio controls>' + '<source src="http://1.de/1.wav" type="audio/wav">' + '<source src="http://2.de/2.mp4" type="audio/mp4">' + '<source src="http://3.de/3.ogg" type="audio/ogg">' + 'Your browser does not support the audio element.</audio>')) +]) +def test_create_html(input, expected): + assert audio.create_html(input) == expected diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat3.ipynb b/docs/www/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat3.ipynb new file mode 100644 index 00000000..4327ae5b --- /dev/null +++ b/docs/www/pelican-plugins/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": {} + } + ] +} diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat4.ipynb b/docs/www/pelican-plugins/liquid_tags/test_data/content/notebooks/test_nbformat4.ipynb new file mode 100644 index 00000000..e8cf0bfb --- /dev/null +++ b/docs/www/pelican-plugins/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 +} diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat3.md b/docs/www/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat3.md new file mode 100644 index 00000000..0f5d2d88 --- /dev/null +++ b/docs/www/pelican-plugins/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] %} diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat4.md b/docs/www/pelican-plugins/liquid_tags/test_data/content/test-ipython-notebook-nbformat4.md new file mode 100644 index 00000000..680817af --- /dev/null +++ b/docs/www/pelican-plugins/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] %} diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/flickr.json b/docs/www/pelican-plugins/liquid_tags/test_data/flickr.json new file mode 100644 index 00000000..dbc7078d --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_data/flickr.json @@ -0,0 +1 @@ +{"photo":{"id":"18841055371","secret":"17ac287217","server":"5552","farm":6,"dateuploaded":"1434394882","isfavorite":0,"license":"4","safety_level":"0","rotation":0,"originalsecret":"ee4b63c8d8","originalformat":"jpg","owner":{"nsid":"8810721@N07","username":"marvinxsteadfast","realname":"","location":"","iconserver":"0","iconfarm":0,"path_alias":"marvinxsteadfast"},"title":{"_content":"fichte"},"description":{"_content":"Processed with VSCOcam with k1 preset"},"visibility":{"ispublic":1,"isfriend":0,"isfamily":0},"dates":{"posted":"1434394882","taken":"2015-06-13 12:51:14","takengranularity":"0","takenunknown":"0","lastupdate":"1434445581"},"views":"23","editability":{"cancomment":0,"canaddmeta":0},"publiceditability":{"cancomment":1,"canaddmeta":0},"usage":{"candownload":1,"canblog":0,"canprint":0,"canshare":1},"comments":{"_content":"0"},"notes":{"note":[]},"people":{"haspeople":0},"tags":{"tag":[]},"location":{"latitude":"52.418917","longitude":"10.785100","accuracy":"16","context":"0","neighbourhood":{"_content":"Stadtteil Wolfsburg","place_id":"WhlihUxTVLJhqUkmNQ","woeid":"26823353"},"locality":{"_content":"Wolfsburg","place_id":"E0zyhhBWUr1dyKs","woeid":"707678"},"county":{"_content":"Stadtkreis Wolfsburg","place_id":"3NbeUiNQUL9_BB.8dg","woeid":"12596975"},"region":{"_content":"Lower Saxony","place_id":"ul2d17NTUb4lkTA_","woeid":"2345486"},"country":{"_content":"Germany","place_id":"h7eZVDlTUb50Btij9Q","woeid":"23424829"},"place_id":"WhlihUxTVLJhqUkmNQ","woeid":"26823353"},"geoperms":{"ispublic":1,"iscontact":0,"isfriend":0,"isfamily":0},"urls":{"url":[{"type":"photopage","_content":"https:\/\/www.flickr.com\/photos\/marvinxsteadfast\/18841055371\/"}]},"media":"photo"},"stat":"ok"} \ No newline at end of file diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/giphy.json b/docs/www/pelican-plugins/liquid_tags/test_data/giphy.json new file mode 100644 index 00000000..8a2d5e11 --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_data/giphy.json @@ -0,0 +1,2 @@ + +{"data":{"type":"gif","id":"aMSJFS6oFX0fC","url":"http:\/\/giphy.com\/gifs\/veronica-mars-aMSJFS6oFX0fC","bitly_gif_url":"http:\/\/gph.is\/1hcFOSo","bitly_url":"http:\/\/gph.is\/1hcFOSo","embed_url":"http:\/\/giphy.com\/embed\/aMSJFS6oFX0fC","username":"","source":"http:\/\/www.tumblr.com","rating":"pg","caption":"","content_url":"","import_datetime":"2014-02-21 04:43:11","trending_datetime":"1970-01-01 00:00:00","images":{"fixed_height":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200.gif","width":"350","height":"200","size":"296611","mp4":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200.mp4","mp4_size":"64850","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200.webp","webp_size":"529942"},"fixed_height_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200_s.gif","width":"350","height":"200"},"fixed_height_downsampled":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200_d.gif","width":"350","height":"200","size":"243264","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200_d.webp","webp_size":"138300"},"fixed_width":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w.gif","width":"200","height":"114","size":"120246","mp4":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w.mp4","mp4_size":"96166","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w.webp","webp_size":"210140"},"fixed_width_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w_s.gif","width":"200","height":"114"},"fixed_width_downsampled":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w_d.gif","width":"200","height":"114","size":"113909","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/200w_d.webp","webp_size":"55084"},"fixed_height_small":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100.gif","width":"175","height":"100","size":"296611","mp4":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100.mp4","mp4_size":"243931","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100.webp","webp_size":"136098"},"fixed_height_small_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100_s.gif","width":"175","height":"100"},"fixed_width_small":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100w.gif","width":"100","height":"57","size":"120246","mp4":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100w.mp4","mp4_size":"101604","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100w.webp","webp_size":"55480"},"fixed_width_small_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/100w_s.gif","width":"100","height":"57"},"downsized":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy.gif","width":"245","height":"140","size":"487767"},"downsized_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy_s.gif","width":"245","height":"140"},"downsized_large":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy.gif","width":"245","height":"140","size":"487767"},"original":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy.gif","width":"245","height":"140","size":"487767","frames":"23","mp4":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy.mp4","mp4_size":"219528","webp":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy.webp","webp_size":"272262"},"original_still":{"url":"http:\/\/media2.giphy.com\/media\/aMSJFS6oFX0fC\/giphy_s.gif","width":"245","height":"140"}}},"meta":{"status":200,"msg":"OK"}} \ No newline at end of file diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/output/index.html b/docs/www/pelican-plugins/liquid_tags/test_data/output/index.html new file mode 100644 index 00000000..c0742a3d --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_data/output/index.html @@ -0,0 +1,1153 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8" /> + <title>Testing site + + + + + + + + + +
+

Other articles

+
+
    + +
  1. +
    +

    test ipython notebook nb format 4

    +
    + +
    +

    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 ...

    + read more +
    +
  2. +
+
+
+
+ + + + + \ No newline at end of file diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/output/test-ipython-notebook-nb-format-3.html b/docs/www/pelican-plugins/liquid_tags/test_data/output/test-ipython-notebook-nb-format-3.html new file mode 100644 index 00000000..a2ff3ca5 --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_data/output/test-ipython-notebook-nb-format-3.html @@ -0,0 +1,1130 @@ + + + + + test ipython notebook nb format 3 + + + + + + + +
+
+
+

+ test ipython notebook nb format 3

+
+ +
+

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 a notebook nbformat = 3.0

+

+

+
+
+
+
+

Il s’agit de repérer les données nécessaires voire indispensables à la +résolution. Ces données peuvent être:

+
    +
  • numériques,
  • +
  • ou sous forme de textes (on dit souvent chaînes de caractères),
  • +
  • ou de type logique (à deux valeurs possibles, vrai ou faux).
  • +
+

Pour affecter une valeur à une variable en python, la syntaxe est de la forme:

+
nom = valeur
+
+
Affectation
+
Dans un programme, une affectation donne une valeur à une variable, elle est de la forme v = e avec v une variable et e une expression. +
+
+ +

Valeurs numériques

+

Python distingue les entiers(integers), des nombres à +virgule(floating-point) par l'ajout d'une virgule:

+
+
+
+
+
+
In [2]:
+
+
+
a = 1
+b = 1.0
+
+print(a, type(a), b, type(b))
+
+ +
+
+
+ +
+
+ + +
+
+
+1 <class 'int'> 1.0 <class 'float'>
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Mais il implémente aussi des nombres plus exotiques tels que les +décimaux, les fractions ou encore les nombres complexes

+

Chaînes de caractères

+

En python3, il existe deux grands types de chaînes de caractères, les +strings, et les bytes. Les strings sont simplement entourées par des +guillemets simples ou doubles.

+
+
+
+
+
+
In [3]:
+
+
+
a = 'Mon texte'
+b = "Mon deuxième texte"
+
+print(a, type(a), b, type(b))
+
+ +
+
+
+ +
+
+ + +
+
+
+Mon texte <class 'str'> Mon deuxième texte <class 'str'>
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Les bytes sont précédées de la lettre b, il s'agit de texte brut utilisé +par la machine mais pas toujours lisible par un humain.

+
+
+
+
+
+
In [4]:
+
+
+
a = b'Mon texte'
+print(a, type(a))
+
+ +
+
+
+ +
+
+ + +
+
+
+b'Mon texte' <class 'bytes'>
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

On ne peut représenter simplement que certains caractères(les caractères +ASCII), les caractères accentués ne peuvent être écrits directement.

+
+
+
+
+
+
In [5]:
+
+
+
b = b'Mon deuxième texte'
+
+ +
+
+
+ +
+
+ + +
+
+
+  File "<ipython-input-5-b0bf0ef2715d>", line 1
+    b = b'Mon deuxième texte'
+       ^
+SyntaxError: bytes can only contain ASCII literal characters.
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Un encodage autre que le ASCII doit être utilisé pour ces caractères, +comme par exemple le UTF-8 qui est un codage sur huit bits très répandu.

+
+
+
+
+
+
In [6]:
+
+
+
b = b'Mon deuxi\xc3\xa8me texte'
+print(b)
+print(b.decode('utf-8'))
+
+ +
+
+
+ +
+
+ + +
+
+
+b'Mon deuxi\xc3\xa8me texte'
+Mon deuxième texte
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Valeurs logiques

+

On les appelle les booléens, il ne peuvent avoir que deux valeurs:

+
    +
  • VRAI ou FAUX: True ou False en anglais;
  • +
  • 1 ou 0.
  • +
+

On les utilise pour réaliser des tests.

+
+
+
+
+
+
In [7]:
+
+
+
1 < 2, 1 > 2, 2 == 10, 2 >= 1.9, 2 == 2, 2 != 2
+
+ +
+
+
+ +
+
+ + +
Out[7]:
+ + +
+
+(True, False, False, True, True, False)
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

On peut aussi les utiliser pour savoir si une variable existe.

+
+
+
+
+
+
In [8]:
+
+
+
bool(a)
+
+ +
+
+
+ +
+
+ + +
Out[8]:
+ + +
+
+True
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

On peut aussi combiner plusieurs tests avec les opérateurs booléens and, or et not.

+
+
+
+
+
+
In [9]:
+
+
+
1 < 2 or 2 < 1
+
+ +
+
+
+ +
+
+ + +
Out[9]:
+ + +
+
+True
+
+
+ +
+ +
+
+ +
+
+
+
In [10]:
+
+
+
1 < 2 and 2 < 1
+
+ +
+
+
+ +
+
+ + +
Out[10]:
+ + +
+
+False
+
+
+ +
+ +
+
+ +
+
+
+
In [11]:
+
+
+
not(1 < 2 and 2 < 1)
+
+ +
+
+
+ +
+
+ + +
Out[11]:
+ + +
+
+True
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Listes et dictionnaires

+

Souvent les données pertinentes doivent être agencées sous une forme +plus vaste, comme par exemple des listes où on peut ranger de façon ordonnée plusieurs valeurs.

+

Listes

+

Les listes sont des collections ordonnées de valeurs, elles sont +entourées par des crochets [], leurs éléments sont séparés par des +virgules.

+
+
+
+
+
+
In [12]:
+
+
+
a = [ 1, 'deux' , 3]
+type(a)
+
+ +
+
+
+ +
+
+ + +
Out[12]:
+ + +
+
+list
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

On peut facilement accéder à la longueur de la liste grace à la fonction +len et à chacun de ces éléments grâce à leur index (Attention le +premier élement à l'index 0)

+
+
+
+
+
+
In [13]:
+
+
+
len(a)
+
+ +
+
+
+ +
+
+ + +
Out[13]:
+ + +
+
+3
+
+
+ +
+ +
+
+ +
+
+
+
In [14]:
+
+
+
a[0], a[2]
+
+ +
+
+
+ +
+
+ + +
Out[14]:
+ + +
+
+(1, 3)
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

On peut inversement connaître l'indice correspondant à une valeur grâce à l'attribut index.

+
+
+
+
+
+
In [15]:
+
+
+
a.index(1), a.index('deux'), a.index(3)
+
+ +
+
+
+ +
+
+ + +
Out[15]:
+ + +
+
+(0, 1, 2)
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Dictionnaires

+

Dans un dictionnaire les valeurs de la collection ne sont pas repéré par +un index, mais par une clé. Les dictionnaires sont entourés d'accolades {}.

+
+
+
+
+
+
In [16]:
+
+
+
a = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }
+type(a)
+
+ +
+
+
+ +
+
+ + +
Out[16]:
+ + +
+
+dict
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Pour accéder aux éléments du dictionnaire, il suffit d'appeler la clé +correspondante, d'autres part la fonction len est égalemnt disponible.

+
+
+
+
+
+
In [17]:
+
+
+
len(a)
+
+ +
+
+
+ +
+
+ + +
Out[17]:
+ + +
+
+3
+
+
+ +
+ +
+
+ +
+
+
+
In [18]:
+
+
+
a['nom'], a['age']
+
+ +
+
+
+ +
+
+ + +
Out[18]:
+ + +
+
+('Doe', 77)
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Modification des listes et dictionnaires

+

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.

+
+
+
+
+
+
In [19]:
+
+
+
# Valeurs initiales
+liste = [ 1, 'deux' , 3]
+dict = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }
+print("Valeurs initiales:\n", liste, dict)
+
+# Modification des valeurs par assignation
+liste[1] = 2
+dict['age'] = 17
+
+print("Modification des valeurs par assignation:\n", liste, dict)
+
+# Ajout d'éléments
+liste.append(4)
+dict['nationalité'] = 'française'
+
+print("Ajout d'éléments:\n", liste, dict)
+
+# Suppression d'éléments
+liste.pop(0)
+dict.pop('age')
+
+print("Suppression d'éléments:\n", liste, dict)
+
+ +
+
+
+ +
+
+ + +
+
+
+Valeurs initiales:
+ [1, 'deux', 3] {'age': 77, 'nom': 'Doe', 'prenom': 'John'}
+Modification des valeurs par assignation:
+ [1, 2, 3] {'age': 17, 'nom': 'Doe', 'prenom': 'John'}
+Ajout d'éléments:
+ [1, 2, 3, 4] {'age': 17, 'nom': 'Doe', 'nationalité': 'française', 'prenom': 'John'}
+Suppression d'éléments:
+ [2, 3, 4] {'nom': 'Doe', 'nationalité': 'française', 'prenom': 'John'}
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

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.

+
+
+
+
+
+
In [20]:
+
+
+
# Valeurs initiales
+L = [ 1, 'deux' , 3]
+print("Valeurs initiales:\n", L)
+
+# Création d'une référencxe à la liste par simple assignation
+L_ref = L
+
+# Création d'une copie de la liste
+L_copie = list(L)
+
+# Modification de la liste initiale
+L[1] = 2
+
+print("Modification de la liste L:")
+print("La liste L a bien, été modifiée:", L)
+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)
+print("La copie L_copie n'a pas été modifiée:", L_copie)
+
+ +
+
+
+ +
+
+ + +
+
+
+Valeurs initiales:
+ [1, 'deux', 3]
+Modification de la liste L:
+La liste L a bien, été modifiée: [1, 2, 3]
+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]
+La copie L_copie n'a pas été modifiée: [1, 'deux', 3]
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Entrée des données

+

Dans cette phase peut aussi figurer ce qu’on appelle l’entrée des +données, qui peut se manifester par la saisie de caractères ou de +nombres sur le clavier, ou la lecture de la position du pointeur de la +souris, ou encore par la lecture d’un fichier contenant ces nombres ou +caractères.

+

Il s’agit aussi de repérer les résultats intermédiaires qu’il est bon de +mémoriser pour la suite car indispensables au traitement.

+

Il est parfois utile d’utiliser des variables auxiliaires pour ne pas +perturber les données initiales.

+

Entrée des données au clavier

+
+
+
+
+
+
In [21]:
+
+
+
chiffre = input("Entrer la valeur du chiffre souhaité: ")
+
+ +
+
+
+ +
+
+ + +
+
+
+Entrer la valeur du chiffre souhaité: 7
+
+
+
+
+ +
+
+ +
+
+
+
In [22]:
+
+
+
print(chiffre)
+
+ +
+
+
+ +
+
+ + +
+
+
+7
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Attention cependant, cette valeur est une chaîne de caractère, et les opérations sont celles des strings.

+
+
+
+
+
+
In [23]:
+
+
+
print(type(chiffre))
+chiffre*10
+
+ +
+
+
+ +
+
+ + +
+
+
+<class 'str'>
+
+
+
+
+ +
Out[23]:
+ + +
+
+'7777777777'
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Il convient de la convertir un nombre entier ou flottant avant d'utiliser la valeur.

+
+
+
+
+
+
In [24]:
+
+
+
chiffre = int(chiffre)
+10*chiffre
+
+ +
+
+
+ +
+
+ + +
Out[24]:
+ + +
+
+70
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Lecture d'un fichier

+

Voici par exemple comment ouvrir et lire un fichier appelé lorem.txt +contenu dans le répértoire data du dossier courant.

+
+
+
+
+
+
In [27]:
+
+
+
fichier = open('data/lorem.txt')
+fichier.read()
+
+ +
+
+
+ +
+
+ + +
Out[27]:
+ + +
+
+'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'
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Dans la sortie précédente, les caractères \n représentent les sauts de +ligne, on peut aussi lire le fichier ligne par ligne.

+
+
+
+
+
+
In [29]:
+
+
+
fichier = open('data/lorem.txt')
+fichier.readline()
+
+ +
+
+
+ +
+
+ + +
Out[29]:
+ + +
+
+'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\n'
+
+
+ +
+ +
+
+ +

+
+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/output/test-ipython-notebook-nb-format-4.html b/docs/www/pelican-plugins/liquid_tags/test_data/output/test-ipython-notebook-nb-format-4.html new file mode 100644 index 00000000..e8cb60ec --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_data/output/test-ipython-notebook-nb-format-4.html @@ -0,0 +1,1130 @@ + + + + + test ipython notebook nb format 4 + + + + + + + +
+
+
+

+ test ipython notebook nb format 4

+
+ +
+

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 a notebook nbformat = 4.0

+

+

+
+
+
+
+

Il s’agit de repérer les données nécessaires voire indispensables à la +résolution. Ces données peuvent être:

+
    +
  • numériques,
  • +
  • ou sous forme de textes (on dit souvent chaînes de caractères),
  • +
  • ou de type logique (à deux valeurs possibles, vrai ou faux).
  • +
+

Pour affecter une valeur à une variable en python, la syntaxe est de la forme:

+
nom = valeur
+
+
Affectation
+
Dans un programme, une affectation donne une valeur à une variable, elle est de la forme v = e avec v une variable et e une expression. +
+
+ +

Valeurs numériques

+

Python distingue les entiers(integers), des nombres à +virgule(floating-point) par l'ajout d'une virgule:

+
+
+
+
+
+
In [2]:
+
+
+
a = 1
+b = 1.0
+
+print(a, type(a), b, type(b))
+
+ +
+
+
+ +
+
+ + +
+
+
+1 <class 'int'> 1.0 <class 'float'>
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Mais il implémente aussi des nombres plus exotiques tels que les +décimaux, les fractions ou encore les nombres complexes

+

Chaînes de caractères

+

En python3, il existe deux grands types de chaînes de caractères, les +strings, et les bytes. Les strings sont simplement entourées par des +guillemets simples ou doubles.

+
+
+
+
+
+
In [3]:
+
+
+
a = 'Mon texte'
+b = "Mon deuxième texte"
+
+print(a, type(a), b, type(b))
+
+ +
+
+
+ +
+
+ + +
+
+
+Mon texte <class 'str'> Mon deuxième texte <class 'str'>
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Les bytes sont précédées de la lettre b, il s'agit de texte brut utilisé +par la machine mais pas toujours lisible par un humain.

+
+
+
+
+
+
In [4]:
+
+
+
a = b'Mon texte'
+print(a, type(a))
+
+ +
+
+
+ +
+
+ + +
+
+
+b'Mon texte' <class 'bytes'>
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

On ne peut représenter simplement que certains caractères(les caractères +ASCII), les caractères accentués ne peuvent être écrits directement.

+
+
+
+
+
+
In [5]:
+
+
+
b = b'Mon deuxième texte'
+
+ +
+
+
+ +
+
+ + +
+
+
+  File "<ipython-input-5-b0bf0ef2715d>", line 1
+    b = b'Mon deuxième texte'
+       ^
+SyntaxError: bytes can only contain ASCII literal characters.
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Un encodage autre que le ASCII doit être utilisé pour ces caractères, +comme par exemple le UTF-8 qui est un codage sur huit bits très répandu.

+
+
+
+
+
+
In [6]:
+
+
+
b = b'Mon deuxi\xc3\xa8me texte'
+print(b)
+print(b.decode('utf-8'))
+
+ +
+
+
+ +
+
+ + +
+
+
+b'Mon deuxi\xc3\xa8me texte'
+Mon deuxième texte
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Valeurs logiques

+

On les appelle les booléens, il ne peuvent avoir que deux valeurs:

+
    +
  • VRAI ou FAUX: True ou False en anglais;
  • +
  • 1 ou 0.
  • +
+

On les utilise pour réaliser des tests.

+
+
+
+
+
+
In [7]:
+
+
+
1 < 2, 1 > 2, 2 == 10, 2 >= 1.9, 2 == 2, 2 != 2
+
+ +
+
+
+ +
+
+ + +
Out[7]:
+ + +
+
+(True, False, False, True, True, False)
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

On peut aussi les utiliser pour savoir si une variable existe.

+
+
+
+
+
+
In [8]:
+
+
+
bool(a)
+
+ +
+
+
+ +
+
+ + +
Out[8]:
+ + +
+
+True
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

On peut aussi combiner plusieurs tests avec les opérateurs booléens and, or et not.

+
+
+
+
+
+
In [9]:
+
+
+
1 < 2 or 2 < 1
+
+ +
+
+
+ +
+
+ + +
Out[9]:
+ + +
+
+True
+
+
+ +
+ +
+
+ +
+
+
+
In [10]:
+
+
+
1 < 2 and 2 < 1
+
+ +
+
+
+ +
+
+ + +
Out[10]:
+ + +
+
+False
+
+
+ +
+ +
+
+ +
+
+
+
In [11]:
+
+
+
not(1 < 2 and 2 < 1)
+
+ +
+
+
+ +
+
+ + +
Out[11]:
+ + +
+
+True
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Listes et dictionnaires

+

Souvent les données pertinentes doivent être agencées sous une forme +plus vaste, comme par exemple des listes où on peut ranger de façon ordonnée plusieurs valeurs.

+

Listes

+

Les listes sont des collections ordonnées de valeurs, elles sont +entourées par des crochets [], leurs éléments sont séparés par des +virgules.

+
+
+
+
+
+
In [12]:
+
+
+
a = [ 1, 'deux' , 3]
+type(a)
+
+ +
+
+
+ +
+
+ + +
Out[12]:
+ + +
+
+list
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

On peut facilement accéder à la longueur de la liste grace à la fonction +len et à chacun de ces éléments grâce à leur index (Attention le +premier élement à l'index 0)

+
+
+
+
+
+
In [13]:
+
+
+
len(a)
+
+ +
+
+
+ +
+
+ + +
Out[13]:
+ + +
+
+3
+
+
+ +
+ +
+
+ +
+
+
+
In [14]:
+
+
+
a[0], a[2]
+
+ +
+
+
+ +
+
+ + +
Out[14]:
+ + +
+
+(1, 3)
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

On peut inversement connaître l'indice correspondant à une valeur grâce à l'attribut index.

+
+
+
+
+
+
In [15]:
+
+
+
a.index(1), a.index('deux'), a.index(3)
+
+ +
+
+
+ +
+
+ + +
Out[15]:
+ + +
+
+(0, 1, 2)
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Dictionnaires

+

Dans un dictionnaire les valeurs de la collection ne sont pas repéré par +un index, mais par une clé. Les dictionnaires sont entourés d'accolades {}.

+
+
+
+
+
+
In [16]:
+
+
+
a = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }
+type(a)
+
+ +
+
+
+ +
+
+ + +
Out[16]:
+ + +
+
+dict
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Pour accéder aux éléments du dictionnaire, il suffit d'appeler la clé +correspondante, d'autres part la fonction len est égalemnt disponible.

+
+
+
+
+
+
In [17]:
+
+
+
len(a)
+
+ +
+
+
+ +
+
+ + +
Out[17]:
+ + +
+
+3
+
+
+ +
+ +
+
+ +
+
+
+
In [18]:
+
+
+
a['nom'], a['age']
+
+ +
+
+
+ +
+
+ + +
Out[18]:
+ + +
+
+('Doe', 77)
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Modification des listes et dictionnaires

+

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.

+
+
+
+
+
+
In [19]:
+
+
+
# Valeurs initiales
+liste = [ 1, 'deux' , 3]
+dict = { 'nom': 'Doe' , 'prenom': 'John', 'age': 77 }
+print("Valeurs initiales:\n", liste, dict)
+
+# Modification des valeurs par assignation
+liste[1] = 2
+dict['age'] = 17
+
+print("Modification des valeurs par assignation:\n", liste, dict)
+
+# Ajout d'éléments
+liste.append(4)
+dict['nationalité'] = 'française'
+
+print("Ajout d'éléments:\n", liste, dict)
+
+# Suppression d'éléments
+liste.pop(0)
+dict.pop('age')
+
+print("Suppression d'éléments:\n", liste, dict)
+
+ +
+
+
+ +
+
+ + +
+
+
+Valeurs initiales:
+ [1, 'deux', 3] {'age': 77, 'nom': 'Doe', 'prenom': 'John'}
+Modification des valeurs par assignation:
+ [1, 2, 3] {'age': 17, 'nom': 'Doe', 'prenom': 'John'}
+Ajout d'éléments:
+ [1, 2, 3, 4] {'age': 17, 'nom': 'Doe', 'nationalité': 'française', 'prenom': 'John'}
+Suppression d'éléments:
+ [2, 3, 4] {'nom': 'Doe', 'nationalité': 'française', 'prenom': 'John'}
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

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.

+
+
+
+
+
+
In [20]:
+
+
+
# Valeurs initiales
+L = [ 1, 'deux' , 3]
+print("Valeurs initiales:\n", L)
+
+# Création d'une référencxe à la liste par simple assignation
+L_ref = L
+
+# Création d'une copie de la liste
+L_copie = list(L)
+
+# Modification de la liste initiale
+L[1] = 2
+
+print("Modification de la liste L:")
+print("La liste L a bien, été modifiée:", L)
+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)
+print("La copie L_copie n'a pas été modifiée:", L_copie)
+
+ +
+
+
+ +
+
+ + +
+
+
+Valeurs initiales:
+ [1, 'deux', 3]
+Modification de la liste L:
+La liste L a bien, été modifiée: [1, 2, 3]
+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]
+La copie L_copie n'a pas été modifiée: [1, 'deux', 3]
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Entrée des données

+

Dans cette phase peut aussi figurer ce qu’on appelle l’entrée des +données, qui peut se manifester par la saisie de caractères ou de +nombres sur le clavier, ou la lecture de la position du pointeur de la +souris, ou encore par la lecture d’un fichier contenant ces nombres ou +caractères.

+

Il s’agit aussi de repérer les résultats intermédiaires qu’il est bon de +mémoriser pour la suite car indispensables au traitement.

+

Il est parfois utile d’utiliser des variables auxiliaires pour ne pas +perturber les données initiales.

+

Entrée des données au clavier

+
+
+
+
+
+
In [21]:
+
+
+
chiffre = input("Entrer la valeur du chiffre souhaité: ")
+
+ +
+
+
+ +
+
+ + +
+
+
+Entrer la valeur du chiffre souhaité: 7
+
+
+
+
+ +
+
+ +
+
+
+
In [22]:
+
+
+
print(chiffre)
+
+ +
+
+
+ +
+
+ + +
+
+
+7
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Attention cependant, cette valeur est une chaîne de caractère, et les opérations sont celles des strings.

+
+
+
+
+
+
In [23]:
+
+
+
print(type(chiffre))
+chiffre*10
+
+ +
+
+
+ +
+
+ + +
+
+
+<class 'str'>
+
+
+
+
+ +
Out[23]:
+ + +
+
+'7777777777'
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Il convient de la convertir un nombre entier ou flottant avant d'utiliser la valeur.

+
+
+
+
+
+
In [24]:
+
+
+
chiffre = int(chiffre)
+10*chiffre
+
+ +
+
+
+ +
+
+ + +
Out[24]:
+ + +
+
+70
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Lecture d'un fichier

+

Voici par exemple comment ouvrir et lire un fichier appelé lorem.txt +contenu dans le répértoire data du dossier courant.

+
+
+
+
+
+
In [27]:
+
+
+
fichier = open('data/lorem.txt')
+fichier.read()
+
+ +
+
+
+ +
+
+ + +
Out[27]:
+ + +
+
+'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'
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+

Dans la sortie précédente, les caractères \n représentent les sauts de +ligne, on peut aussi lire le fichier ligne par ligne.

+
+
+
+
+
+
In [29]:
+
+
+
fichier = open('data/lorem.txt')
+fichier.readline()
+
+ +
+
+
+ +
+
+ + +
Out[29]:
+ + +
+
+'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\n'
+
+
+ +
+ +
+
+ +

+
+ +
+
+
+
+ + + + + \ No newline at end of file diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/pelicanconf.py b/docs/www/pelican-plugins/liquid_tags/test_data/pelicanconf.py new file mode 100644 index 00000000..f624dd73 --- /dev/null +++ b/docs/www/pelican-plugins/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' diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/pelicanhtml_2.tpl b/docs/www/pelican-plugins/liquid_tags/test_data/pelicanhtml_2.tpl new file mode 120000 index 00000000..0f51030b --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_data/pelicanhtml_2.tpl @@ -0,0 +1 @@ +../pelicanhtml_2.tpl \ No newline at end of file diff --git a/docs/www/pelican-plugins/liquid_tags/test_data/pelicanhtml_3.tpl b/docs/www/pelican-plugins/liquid_tags/test_data/pelicanhtml_3.tpl new file mode 120000 index 00000000..662692d3 --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_data/pelicanhtml_3.tpl @@ -0,0 +1 @@ +../pelicanhtml_3.tpl \ No newline at end of file diff --git a/docs/www/pelican-plugins/liquid_tags/test_flickr.py b/docs/www/pelican-plugins/liquid_tags/test_flickr.py new file mode 100644 index 00000000..f8684dc9 --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_flickr.py @@ -0,0 +1,79 @@ +from . import flickr +try: + from unittest.mock import patch +except ImportError: + from mock import patch +import os +import pytest +import re + + +PLUGIN_DIR = os.path.dirname(__file__) +TEST_DATA_DIR = os.path.join(PLUGIN_DIR, 'test_data') + + +@pytest.mark.parametrize('input,expected', [ + ('18873146680 large "test 1"', + dict(photo_id='18873146680', + size='large', + alt='test 1')), + ('18873146680 large \'test 1\'', + dict(photo_id='18873146680', + size='large', + alt='test 1')), + ('18873143536360 medium "test number two"', + dict(photo_id='18873143536360', + size='medium', + alt='test number two')), + ('18873143536360 small "test number 3"', + dict(photo_id='18873143536360', + size='small', + alt='test number 3')), + ('18873143536360 "test 4"', + dict(photo_id='18873143536360', + size=None, + alt='test 4')), + ('18873143536360', + dict(photo_id='18873143536360', + size=None, + alt=None)), + ('123456 small', + dict(photo_id='123456', + size='small', + alt=None)) +]) +def test_regex(input, expected): + assert re.match(flickr.PARSE_SYNTAX, input).groupdict() == expected + + +@pytest.mark.parametrize('input,expected', [ + (['1', 'server1', '1', 'secret1', 'small'], + 'https://farm1.staticflickr.com/server1/1_secret1_n.jpg'), + (['2', 'server2', '2', 'secret2', 'medium'], + 'https://farm2.staticflickr.com/server2/2_secret2_c.jpg'), + (['3', 'server3', '3', 'secret3', 'large'], + 'https://farm3.staticflickr.com/server3/3_secret3_b.jpg') +]) +def test_source_url(input, expected): + assert flickr.source_url( + input[0], input[1], input[2], input[3], input[4]) == expected + + +@patch('liquid_tags.flickr.urlopen') +def test_generage_html(mock_urlopen): + # mock the return to deliver the flickr.json file instead + with open(TEST_DATA_DIR + '/flickr.json', 'rb') as f: + mock_urlopen.return_value.read.return_value = f.read() + + attrs = dict( + photo_id='1234567', + size='large', + alt='this is a test' + ) + + expected = ('' + 'this is a test') + + assert flickr.generate_html(attrs, 'abcdef') == expected diff --git a/docs/www/pelican-plugins/liquid_tags/test_generation.py b/docs/www/pelican-plugins/liquid_tags/test_generation.py new file mode 100644 index 00000000..c0a22a72 --- /dev/null +++ b/docs/www/pelican-plugins/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')) diff --git a/docs/www/pelican-plugins/liquid_tags/test_giphy.py b/docs/www/pelican-plugins/liquid_tags/test_giphy.py new file mode 100644 index 00000000..1ce80c13 --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_giphy.py @@ -0,0 +1,29 @@ +from . import giphy +try: + from unittest.mock import patch +except ImportError: + from mock import patch +import os +import pytest + + +PLUGIN_DIR = os.path.dirname(__file__) +TEST_DATA_DIR = os.path.join(PLUGIN_DIR, 'test_data') + + +@pytest.mark.parametrize('input,expected', [ + (dict(gif_id='abc123'), + ('' + 'source: http://www.tumblr.com')), + (dict(gif_id='abc123', alt='ive had some free time'), + ('' + 'ive had some free time')) +]) +@patch('liquid_tags.giphy.urlopen') +def test_create_html(mock_urlopen, input, expected): + with open(TEST_DATA_DIR + '/giphy.json', 'rb') as f: + mock_urlopen.return_value.read.return_value = f.read() + + assert giphy.create_html('test_api_key', input) == expected diff --git a/docs/www/pelican-plugins/liquid_tags/test_notebook.py b/docs/www/pelican-plugins/liquid_tags/test_notebook.py index 042d6d1c..8b34e9a6 100644 --- a/docs/www/pelican-plugins/liquid_tags/test_notebook.py +++ b/docs/www/pelican-plugins/liquid_tags/test_notebook.py @@ -2,7 +2,7 @@ from pelican.tests.support import unittest -import notebook +from . import notebook class TestNotebookTagRegex(unittest.TestCase): diff --git a/docs/www/pelican-plugins/liquid_tags/test_soundcloud.py b/docs/www/pelican-plugins/liquid_tags/test_soundcloud.py new file mode 100644 index 00000000..d7edd19f --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/test_soundcloud.py @@ -0,0 +1,27 @@ +from . import soundcloud +import pytest + + +@pytest.mark.parametrize('input,expected', [ + ('https://soundcloud.com/forss/in-paradisum', + dict(track_url='https://soundcloud.com/forss/in-paradisum')), + ('http://soundcloud.com/forss/in-paradisum', + dict(track_url='http://soundcloud.com/forss/in-paradisum')), + ('https://soundcloud.com/toroymoi/real-love-ft-kool-ad', + dict(track_url='https://soundcloud.com/toroymoi/real-love-ft-kool-ad')), + ('https://soundcloud.com/capturedtracks/sets/wild-nothing-nocturne', + dict(track_url=('https://soundcloud.com/capturedtracks/' + 'sets/wild-nothing-nocturne'))) +]) +def test_match_it(input, expected): + assert soundcloud.match_it(input) == expected + + +@pytest.mark.parametrize('input', [ + 'http://foobar.com', + 'foobar', + 'https://google.com' +]) +def test_match_it_exception(input): + with pytest.raises(ValueError): + soundcloud.match_it(input) diff --git a/docs/www/pelican-plugins/liquid_tags/tox.ini b/docs/www/pelican-plugins/liquid_tags/tox.ini new file mode 100644 index 00000000..bc49a1ee --- /dev/null +++ b/docs/www/pelican-plugins/liquid_tags/tox.ini @@ -0,0 +1,18 @@ +[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 + mock + ipython2: ipython[notebook]>=2,<3 + ipython3: ipython[notebook] diff --git a/docs/www/pelican-plugins/liquid_tags/vimeo.py b/docs/www/pelican-plugins/liquid_tags/vimeo.py index 9f911ed1..77b0df24 100644 --- a/docs/www/pelican-plugins/liquid_tags/vimeo.py +++ b/docs/www/pelican-plugins/liquid_tags/vimeo.py @@ -15,7 +15,13 @@ Output ------ -
+
+ +
[1] https://gist.github.com/jamieowen/2063748 """ @@ -43,9 +49,10 @@ def vimeo(preprocessor, tag, markup): if vimeo_id: vimeo_out = """
-
""".format(width=width, height=height, vimeo_id=vimeo_id).strip() @@ -56,6 +63,6 @@ def vimeo(preprocessor, tag, markup): return vimeo_out -#---------------------------------------------------------------------- +# --------------------------------------------------- # This import allows vimeo tag to be a Pelican plugin -from liquid_tags import register +from liquid_tags import register # noqa diff --git a/docs/www/pelican-plugins/liquid_tags/youtube.py b/docs/www/pelican-plugins/liquid_tags/youtube.py index 4b915986..0e2360a1 100644 --- a/docs/www/pelican-plugins/liquid_tags/youtube.py +++ b/docs/www/pelican-plugins/liquid_tags/youtube.py @@ -14,11 +14,13 @@ Output ------ - + [1] https://gist.github.com/jamieowen/2063748 """ -import os import re from .mdx_liquid_tags import LiquidTags @@ -26,6 +28,7 @@ YOUTUBE = re.compile(r'([\S]+)(\s+(\d+)\s(\d+))?') + @LiquidTags.register('youtube') def youtube(preprocessor, tag, markup): width = 640 @@ -43,9 +46,9 @@ def youtube(preprocessor, tag, markup): youtube_out = """
""".format(width=width, height=height, youtube_id=youtube_id).strip() @@ -56,6 +59,6 @@ def youtube(preprocessor, tag, markup): return youtube_out -#---------------------------------------------------------------------- +# --------------------------------------------------- # This import allows image tag to be a Pelican plugin -from liquid_tags import register +from liquid_tags import register # noqa