Show More
@@ -0,0 +1,72 b'' | |||||
|
1 | """API for converting notebooks between versions. | |||
|
2 | ||||
|
3 | Authors: | |||
|
4 | ||||
|
5 | * Jonathan Frederic | |||
|
6 | """ | |||
|
7 | ||||
|
8 | #----------------------------------------------------------------------------- | |||
|
9 | # Copyright (C) 2013 The IPython Development Team | |||
|
10 | # | |||
|
11 | # Distributed under the terms of the BSD License. The full license is in | |||
|
12 | # the file COPYING, distributed as part of this software. | |||
|
13 | #----------------------------------------------------------------------------- | |||
|
14 | ||||
|
15 | #----------------------------------------------------------------------------- | |||
|
16 | # Imports | |||
|
17 | #----------------------------------------------------------------------------- | |||
|
18 | ||||
|
19 | import re | |||
|
20 | ||||
|
21 | from .reader import get_version, versions | |||
|
22 | ||||
|
23 | #----------------------------------------------------------------------------- | |||
|
24 | # Functions | |||
|
25 | #----------------------------------------------------------------------------- | |||
|
26 | ||||
|
27 | def convert(nb, to_version): | |||
|
28 | """Convert a notebook node object to a specific version. Assumes that | |||
|
29 | all the versions starting from 1 to the latest major X are implemented. | |||
|
30 | In other words, there should never be a case where v1 v2 v3 v5 exist without | |||
|
31 | a v4. Also assumes that all conversions can be made in one step increments | |||
|
32 | between major versions and ignores minor revisions. | |||
|
33 | ||||
|
34 | PARAMETERS: | |||
|
35 | ----------- | |||
|
36 | nb : NotebookNode | |||
|
37 | to_version : int | |||
|
38 | Major revision to convert the notebook to. Can either be an upgrade or | |||
|
39 | a downgrade. | |||
|
40 | """ | |||
|
41 | ||||
|
42 | # Get input notebook version. | |||
|
43 | (version, version_minor) = get_version(nb) | |||
|
44 | ||||
|
45 | # Check if destination is current version, if so return contents | |||
|
46 | if version == to_version: | |||
|
47 | return nb | |||
|
48 | ||||
|
49 | # If the version exist, try to convert to it one step at a time. | |||
|
50 | elif to_version in versions: | |||
|
51 | ||||
|
52 | # Get the the version that this recursion will convert to as a step | |||
|
53 | # closer to the final revision. Make sure the newer of the conversion | |||
|
54 | # functions is used to perform the conversion. | |||
|
55 | if to_version > version: | |||
|
56 | step_version = version + 1 | |||
|
57 | convert_function = versions[step_version].upgrade | |||
|
58 | else: | |||
|
59 | step_version = version - 1 | |||
|
60 | convert_function = versions[version].downgrade | |||
|
61 | ||||
|
62 | # Convert and make sure version changed during conversion. | |||
|
63 | converted = convert_function(nb) | |||
|
64 | if converted.get('nbformat', 1) == version: | |||
|
65 | raise Exception("Cannot convert notebook from v%d to v%d. Operation" \ | |||
|
66 | "failed silently." % (major, step_version)) | |||
|
67 | ||||
|
68 | # Recursively convert until target version is reached. | |||
|
69 | return convert(converted, to_version) | |||
|
70 | else: | |||
|
71 | raise Exception("Cannot convert notebook to v%d because that " \ | |||
|
72 | "version doesn't exist" % (to_version)) |
@@ -0,0 +1,107 b'' | |||||
|
1 | """API for reading notebooks. | |||
|
2 | ||||
|
3 | Authors: | |||
|
4 | ||||
|
5 | * Jonathan Frederic | |||
|
6 | """ | |||
|
7 | ||||
|
8 | #----------------------------------------------------------------------------- | |||
|
9 | # Copyright (C) 2013 The IPython Development Team | |||
|
10 | # | |||
|
11 | # Distributed under the terms of the BSD License. The full license is in | |||
|
12 | # the file COPYING, distributed as part of this software. | |||
|
13 | #----------------------------------------------------------------------------- | |||
|
14 | ||||
|
15 | #----------------------------------------------------------------------------- | |||
|
16 | # Imports | |||
|
17 | #----------------------------------------------------------------------------- | |||
|
18 | ||||
|
19 | import json | |||
|
20 | ||||
|
21 | import v1 | |||
|
22 | import v2 | |||
|
23 | import v3 | |||
|
24 | ||||
|
25 | versions = { | |||
|
26 | 1: v1, | |||
|
27 | 2: v2, | |||
|
28 | 3: v3, | |||
|
29 | } | |||
|
30 | ||||
|
31 | #----------------------------------------------------------------------------- | |||
|
32 | # Code | |||
|
33 | #----------------------------------------------------------------------------- | |||
|
34 | ||||
|
35 | class NotJSONError(ValueError): | |||
|
36 | pass | |||
|
37 | ||||
|
38 | def parse_json(s, **kwargs): | |||
|
39 | """Parse a JSON string into a dict.""" | |||
|
40 | try: | |||
|
41 | nb_dict = json.loads(s, **kwargs) | |||
|
42 | except ValueError: | |||
|
43 | # Limit the error message to 80 characters. Display whatever JSON will fit. | |||
|
44 | raise NotJSONError(("Notebook does not appear to be JSON: %r" % s)[:77] + "...") | |||
|
45 | return nb_dict | |||
|
46 | ||||
|
47 | # High level API | |||
|
48 | ||||
|
49 | def get_version(nb): | |||
|
50 | """Get the version of a notebook. | |||
|
51 | ||||
|
52 | Parameters | |||
|
53 | ---------- | |||
|
54 | nb : dict | |||
|
55 | NotebookNode or dict containing notebook data. | |||
|
56 | ||||
|
57 | Returns | |||
|
58 | ------- | |||
|
59 | Tuple containing major (int) and minor (int) version numbers | |||
|
60 | """ | |||
|
61 | major = nb.get('nbformat', 1) | |||
|
62 | minor = nb.get('nbformat_minor', 0) | |||
|
63 | return (major, minor) | |||
|
64 | ||||
|
65 | ||||
|
66 | def reads(s, **kwargs): | |||
|
67 | """Read a notebook from a json string and return the | |||
|
68 | NotebookNode object. | |||
|
69 | ||||
|
70 | This function properly reads notebooks of any version. No version | |||
|
71 | conversion is performed. | |||
|
72 | ||||
|
73 | Parameters | |||
|
74 | ---------- | |||
|
75 | s : unicode | |||
|
76 | The raw unicode string to read the notebook from. | |||
|
77 | ||||
|
78 | Returns | |||
|
79 | ------- | |||
|
80 | nb : NotebookNode | |||
|
81 | The notebook that was read. | |||
|
82 | """ | |||
|
83 | nb_dict = parse_json(s, **kwargs) | |||
|
84 | (major, minor) = get_version(nb_dict) | |||
|
85 | if major in versions: | |||
|
86 | return versions[major].to_notebook_json(nb_dict, minor=minor) | |||
|
87 | else: | |||
|
88 | raise NBFormatError('Unsupported nbformat version %s' % major) | |||
|
89 | ||||
|
90 | ||||
|
91 | def read(fp, **kwargs): | |||
|
92 | """Read a notebook from a file and return the NotebookNode object. | |||
|
93 | ||||
|
94 | This function properly reads notebooks of any version. No version | |||
|
95 | conversion is performed. | |||
|
96 | ||||
|
97 | Parameters | |||
|
98 | ---------- | |||
|
99 | fp : file | |||
|
100 | Any file-like object with a read method. | |||
|
101 | ||||
|
102 | Returns | |||
|
103 | ------- | |||
|
104 | nb : NotebookNode | |||
|
105 | The notebook that was read. | |||
|
106 | """ | |||
|
107 | return reads(fp.read(), **kwargs) |
@@ -0,0 +1,42 b'' | |||||
|
1 | """ | |||
|
2 | Contains base test class for nbformat | |||
|
3 | """ | |||
|
4 | #----------------------------------------------------------------------------- | |||
|
5 | #Copyright (c) 2013, the IPython Development Team. | |||
|
6 | # | |||
|
7 | #Distributed under the terms of the Modified BSD License. | |||
|
8 | # | |||
|
9 | #The full license is in the file COPYING.txt, distributed with this software. | |||
|
10 | #----------------------------------------------------------------------------- | |||
|
11 | ||||
|
12 | #----------------------------------------------------------------------------- | |||
|
13 | # Imports | |||
|
14 | #----------------------------------------------------------------------------- | |||
|
15 | ||||
|
16 | import os | |||
|
17 | import unittest | |||
|
18 | ||||
|
19 | import IPython | |||
|
20 | ||||
|
21 | #----------------------------------------------------------------------------- | |||
|
22 | # Classes and functions | |||
|
23 | #----------------------------------------------------------------------------- | |||
|
24 | ||||
|
25 | class TestsBase(unittest.TestCase): | |||
|
26 | """Base tests class.""" | |||
|
27 | ||||
|
28 | def fopen(self, f, mode=u'r'): | |||
|
29 | return open(os.path.join(self._get_files_path(), f), mode) | |||
|
30 | ||||
|
31 | ||||
|
32 | def _get_files_path(self): | |||
|
33 | ||||
|
34 | #Get the relative path to this module in the IPython directory. | |||
|
35 | names = self.__module__.split(u'.')[1:-1] | |||
|
36 | ||||
|
37 | #Build a path using the IPython directory and the relative path we just | |||
|
38 | #found. | |||
|
39 | path = IPython.__path__[0] | |||
|
40 | for name in names: | |||
|
41 | path = os.path.join(path, name) | |||
|
42 | return path |
@@ -0,0 +1,419 b'' | |||||
|
1 | { | |||
|
2 | "metadata": { | |||
|
3 | "name": "01_notebook_introduction" | |||
|
4 | }, | |||
|
5 | "nbformat": 2, | |||
|
6 | "worksheets": [ | |||
|
7 | { | |||
|
8 | "cells": [ | |||
|
9 | { | |||
|
10 | "cell_type": "markdown", | |||
|
11 | "source": [ | |||
|
12 | "# An introduction to the IPython notebook", | |||
|
13 | "", | |||
|
14 | "The IPython web notebook is a frontend that allows for new modes", | |||
|
15 | "of interaction with IPython: this web-based interface allows you to execute Python and IPython", | |||
|
16 | "commands in each input cell just like you would at the IPython terminal or Qt console, but you can", | |||
|
17 | "also save an entire session as a document in a file with the `.ipynb` extension.", | |||
|
18 | "", | |||
|
19 | "The document you are reading now is precisely an example of one such notebook, and we will show you", | |||
|
20 | "here how to best use this new interface.", | |||
|
21 | "", | |||
|
22 | "The first thing to understand is that a notebook consists of a sequence of 'cells' that can contain ", | |||
|
23 | "either text (such as this one) or code meant for execution (such as the next one):", | |||
|
24 | "", | |||
|
25 | "* Text cells can be written using [Markdown syntax](http://daringfireball.net/projects/markdown/syntax) ", | |||
|
26 | "(in a future release we will also provide support for reStructuredText and Sphinx integration, and we ", | |||
|
27 | "welcome help from interested contributors to make that happen).", | |||
|
28 | "", | |||
|
29 | "* Code cells take IPython input (i.e. Python code, `%magics`, `!system calls`, etc) like IPython at", | |||
|
30 | "the terminal or at the Qt Console. The only difference is that in order to execute a cell, you *must*", | |||
|
31 | "use `Shift-Enter`, as pressing `Enter` will add a new line of text to the cell. When you type ", | |||
|
32 | "`Shift-Enter`, the cell content is executed, output displayed and a new cell is created below. Try", | |||
|
33 | "it now by putting your cursor on the next cell and typing `Shift-Enter`:" | |||
|
34 | ] | |||
|
35 | }, | |||
|
36 | { | |||
|
37 | "cell_type": "code", | |||
|
38 | "collapsed": false, | |||
|
39 | "input": [ | |||
|
40 | "\"This is the new IPython notebook\"" | |||
|
41 | ], | |||
|
42 | "language": "python", | |||
|
43 | "outputs": [ | |||
|
44 | { | |||
|
45 | "output_type": "pyout", | |||
|
46 | "prompt_number": 1, | |||
|
47 | "text": [ | |||
|
48 | "'This is the new IPython notebook'" | |||
|
49 | ] | |||
|
50 | } | |||
|
51 | ], | |||
|
52 | "prompt_number": 1 | |||
|
53 | }, | |||
|
54 | { | |||
|
55 | "cell_type": "markdown", | |||
|
56 | "source": [ | |||
|
57 | "You can re-execute the same cell over and over as many times as you want. Simply put your", | |||
|
58 | "cursor in the cell again, edit at will, and type `Shift-Enter` to execute. ", | |||
|
59 | "", | |||
|
60 | "**Tip:** A cell can also be executed", | |||
|
61 | "*in-place*, where IPython executes its content but leaves the cursor in the same cell. This is done by", | |||
|
62 | "typing `Ctrl-Enter` instead, and is useful if you want to quickly run a command to check something ", | |||
|
63 | "before tping the real content you want to leave in the cell. For example, in the next cell, try issuing", | |||
|
64 | "several system commands in-place with `Ctrl-Enter`, such as `pwd` and then `ls`:" | |||
|
65 | ] | |||
|
66 | }, | |||
|
67 | { | |||
|
68 | "cell_type": "code", | |||
|
69 | "collapsed": false, | |||
|
70 | "input": [ | |||
|
71 | "ls" | |||
|
72 | ], | |||
|
73 | "language": "python", | |||
|
74 | "outputs": [ | |||
|
75 | { | |||
|
76 | "output_type": "stream", | |||
|
77 | "stream": "stdout", | |||
|
78 | "text": [ | |||
|
79 | "00_notebook_tour.ipynb formatting.ipynb sympy_quantum_computing.ipynb", | |||
|
80 | "01_notebook_introduction.ipynb python-logo.svg trapezoid_rule.ipynb", | |||
|
81 | "display_protocol.ipynb sympy.ipynb" | |||
|
82 | ] | |||
|
83 | } | |||
|
84 | ], | |||
|
85 | "prompt_number": 2 | |||
|
86 | }, | |||
|
87 | { | |||
|
88 | "cell_type": "markdown", | |||
|
89 | "source": [ | |||
|
90 | "In a cell, you can type anything from a single python expression to an arbitrarily long amount of code ", | |||
|
91 | "(although for reasons of readability, you should probably limit this to a few dozen lines):" | |||
|
92 | ] | |||
|
93 | }, | |||
|
94 | { | |||
|
95 | "cell_type": "code", | |||
|
96 | "collapsed": false, | |||
|
97 | "input": [ | |||
|
98 | "def f(x):", | |||
|
99 | " \"\"\"My function", | |||
|
100 | " x : parameter\"\"\"", | |||
|
101 | " ", | |||
|
102 | " return x+1", | |||
|
103 | "", | |||
|
104 | "print \"f(3) = \", f(3)" | |||
|
105 | ], | |||
|
106 | "language": "python", | |||
|
107 | "outputs": [ | |||
|
108 | { | |||
|
109 | "output_type": "stream", | |||
|
110 | "stream": "stdout", | |||
|
111 | "text": [ | |||
|
112 | "f(3) = 4" | |||
|
113 | ] | |||
|
114 | } | |||
|
115 | ], | |||
|
116 | "prompt_number": 3 | |||
|
117 | }, | |||
|
118 | { | |||
|
119 | "cell_type": "markdown", | |||
|
120 | "source": [ | |||
|
121 | "## User interface", | |||
|
122 | "", | |||
|
123 | "When you start a new notebook server with `ipython notebook`, your", | |||
|
124 | "browser should open into the *Dashboard*, a page listing all notebooks", | |||
|
125 | "available in the current directory as well as letting you create new", | |||
|
126 | "notebooks. In this page, you can also drag and drop existing `.py` files", | |||
|
127 | "over the file list to import them as notebooks (see the manual for ", | |||
|
128 | "[further details on how these files are ", | |||
|
129 | "interpreted](http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html)).", | |||
|
130 | "", | |||
|
131 | "Once you open an existing notebook (like this one) or create a new one,", | |||
|
132 | "you are in the main notebook interface, which consists of a main editing", | |||
|
133 | "area (where these cells are contained) as well as a collapsible left panel, ", | |||
|
134 | "a permanent header area at the top, and a pager that rises from the", | |||
|
135 | "bottom when needed and can be collapsed again." | |||
|
136 | ] | |||
|
137 | }, | |||
|
138 | { | |||
|
139 | "cell_type": "markdown", | |||
|
140 | "source": [ | |||
|
141 | "### Main editing area", | |||
|
142 | "", | |||
|
143 | "Here, you can move with the arrow keys or using the ", | |||
|
144 | "scroll bars. The cursor enters code cells immediately, but only selects", | |||
|
145 | "text (markdown) cells without entering in them; to enter a text cell,", | |||
|
146 | "use `Enter`, and `Shift-Enter` to exit it again (just like to execute a ", | |||
|
147 | "code cell)." | |||
|
148 | ] | |||
|
149 | }, | |||
|
150 | { | |||
|
151 | "cell_type": "markdown", | |||
|
152 | "source": [ | |||
|
153 | "### Left panel", | |||
|
154 | "", | |||
|
155 | "This panel contains a number of panes that can be", | |||
|
156 | "collapsed vertically by clicking on their title bar, and the whole panel", | |||
|
157 | "can also be collapsed by clicking on the vertical divider (note that you", | |||
|
158 | "can not *drag* the divider, for now you can only click on it).", | |||
|
159 | "", | |||
|
160 | "The *Notebook* section contains actions that pertain to the whole notebook,", | |||
|
161 | "such as downloading the current notebook either in its original format", | |||
|
162 | "or as a `.py` script, and printing it. When you click the `Print` button,", | |||
|
163 | "a new HTML page opens with a static copy of the notebook; you can then", | |||
|
164 | "use your web browser's mechanisms to save or print this file.", | |||
|
165 | "", | |||
|
166 | "The *Cell* section lets you manipulate individual cells, and the names should ", | |||
|
167 | "be fairly self-explanatory.", | |||
|
168 | "", | |||
|
169 | "The *Kernel* section lets you signal the kernel executing your code. ", | |||
|
170 | "`Interrupt` does the equivalent of hitting `Ctrl-C` at a terminal, and", | |||
|
171 | "`Restart` fully kills the kernel process and starts a fresh one. Obviously", | |||
|
172 | "this means that all your previous variables are destroyed, but it also", | |||
|
173 | "makes it easy to get a fresh kernel in which to re-execute a notebook, perhaps", | |||
|
174 | "after changing an extension module for which Python's `reload` mechanism", | |||
|
175 | "does not work. If you check the 'Kill kernel upon exit' box, when you ", | |||
|
176 | "close the page IPython will automatically shut down the running kernel;", | |||
|
177 | "otherwise the kernels won't close until you stop the whole ", | |||
|
178 | "", | |||
|
179 | "The *Help* section contains links to the documentation of some projects", | |||
|
180 | "closely related to IPython as well as the minimal keybindings you need to", | |||
|
181 | "know. But you should use `Ctrl-m h` (or click the `QuickHelp` button at", | |||
|
182 | "the top) and learn some of the other keybindings, as it will make your ", | |||
|
183 | "workflow much more fluid and efficient.", | |||
|
184 | "", | |||
|
185 | "The *Configuration* section at the bottom lets you change some values", | |||
|
186 | "related to the display of tooltips and the behavior of the tab completer." | |||
|
187 | ] | |||
|
188 | }, | |||
|
189 | { | |||
|
190 | "cell_type": "markdown", | |||
|
191 | "source": [ | |||
|
192 | "### Header bar", | |||
|
193 | "", | |||
|
194 | "The header area at the top allows you to rename an existing ", | |||
|
195 | "notebook and open up a short help tooltip. This area also indicates", | |||
|
196 | "with a red **Busy** mark on the right whenever the kernel is busy executing", | |||
|
197 | "code." | |||
|
198 | ] | |||
|
199 | }, | |||
|
200 | { | |||
|
201 | "cell_type": "markdown", | |||
|
202 | "source": [ | |||
|
203 | "### The pager at the bottom", | |||
|
204 | "", | |||
|
205 | "Whenever IPython needs to display additional ", | |||
|
206 | "information, such as when you type `somefunction?` in a cell, the notebook", | |||
|
207 | "opens a pane at the bottom where this information is shown. You can keep", | |||
|
208 | "this pager pane open for reference (it doesn't block input in the main area)", | |||
|
209 | "or dismiss it by clicking on its divider bar." | |||
|
210 | ] | |||
|
211 | }, | |||
|
212 | { | |||
|
213 | "cell_type": "markdown", | |||
|
214 | "source": [ | |||
|
215 | "### Tab completion and tooltips", | |||
|
216 | "", | |||
|
217 | "The notebook uses the same underlying machinery for tab completion that ", | |||
|
218 | "IPython uses at the terminal, but displays the information differently.", | |||
|
219 | "Whey you complete with the `Tab` key, IPython shows a drop list with all", | |||
|
220 | "available completions. If you type more characters while this list is open,", | |||
|
221 | "IPython automatically eliminates from the list options that don't match the", | |||
|
222 | "new characters; once there is only one option left you can hit `Tab` once", | |||
|
223 | "more (or `Enter`) to complete. You can also select the completion you", | |||
|
224 | "want with the arrow keys or the mouse, and then hit `Enter`.", | |||
|
225 | "", | |||
|
226 | "In addition, if you hit `Tab` inside of open parentheses, IPython will ", | |||
|
227 | "search for the docstring of the last object left of the parens and will", | |||
|
228 | "display it on a tooltip. For example, type `list(<TAB>` and you will", | |||
|
229 | "see the docstring for the builtin `list` constructor:" | |||
|
230 | ] | |||
|
231 | }, | |||
|
232 | { | |||
|
233 | "cell_type": "code", | |||
|
234 | "collapsed": true, | |||
|
235 | "input": [ | |||
|
236 | "# Position your cursor after the ( and hit the Tab key:", | |||
|
237 | "list(" | |||
|
238 | ], | |||
|
239 | "language": "python", | |||
|
240 | "outputs": [] | |||
|
241 | }, | |||
|
242 | { | |||
|
243 | "cell_type": "markdown", | |||
|
244 | "source": [ | |||
|
245 | "## The frontend/kernel model", | |||
|
246 | "", | |||
|
247 | "The IPython notebook works on a client/server model where an *IPython kernel*", | |||
|
248 | "starts in a separate process and acts as a server to executes the code you type,", | |||
|
249 | "while the web browser provides acts as a client, providing a front end environment", | |||
|
250 | "for you to type. But one kernel is capable of simultaneously talking to more than", | |||
|
251 | "one client, and they do not all need to be of the same kind. All IPython frontends", | |||
|
252 | "are capable of communicating with a kernel, and any number of them can be active", | |||
|
253 | "at the same time. In addition to allowing you to have, for example, more than one", | |||
|
254 | "browser session active, this lets you connect clients with different user interface features.", | |||
|
255 | "", | |||
|
256 | "For example, you may want to connect a Qt console to your kernel and use it as a help", | |||
|
257 | "browser, calling `??` on objects in the Qt console (whose pager is more flexible than the", | |||
|
258 | "one in the notebook). You can start a new Qt console connected to your current kernel by ", | |||
|
259 | "using the `%qtconsole` magic, this will automatically detect the necessary connection", | |||
|
260 | "information.", | |||
|
261 | "", | |||
|
262 | "If you want to open one manually, or want to open a text console from a terminal, you can ", | |||
|
263 | "get your kernel's connection information with the `%connect_info` magic:" | |||
|
264 | ] | |||
|
265 | }, | |||
|
266 | { | |||
|
267 | "cell_type": "code", | |||
|
268 | "collapsed": false, | |||
|
269 | "input": [ | |||
|
270 | "%connect_info" | |||
|
271 | ], | |||
|
272 | "language": "python", | |||
|
273 | "outputs": [ | |||
|
274 | { | |||
|
275 | "output_type": "stream", | |||
|
276 | "stream": "stdout", | |||
|
277 | "text": [ | |||
|
278 | "{", | |||
|
279 | " \"stdin_port\": 53970, ", | |||
|
280 | " \"ip\": \"127.0.0.1\", ", | |||
|
281 | " \"hb_port\": 53971, ", | |||
|
282 | " \"key\": \"30daac61-6b73-4bae-a7d9-9dca538794d5\", ", | |||
|
283 | " \"shell_port\": 53968, ", | |||
|
284 | " \"iopub_port\": 53969", | |||
|
285 | "}", | |||
|
286 | "", | |||
|
287 | "Paste the above JSON into a file, and connect with:", | |||
|
288 | " $> ipython <app> --existing <file>", | |||
|
289 | "or, if you are local, you can connect with just:", | |||
|
290 | " $> ipython <app> --existing kernel-dd85d1cc-c335-44f4-bed8-f1a2173a819a.json ", | |||
|
291 | "or even just:", | |||
|
292 | " $> ipython <app> --existing ", | |||
|
293 | "if this is the most recent IPython session you have started." | |||
|
294 | ] | |||
|
295 | } | |||
|
296 | ], | |||
|
297 | "prompt_number": 4 | |||
|
298 | }, | |||
|
299 | { | |||
|
300 | "cell_type": "markdown", | |||
|
301 | "source": [ | |||
|
302 | "## The kernel's `raw_input` and `%debug`", | |||
|
303 | "", | |||
|
304 | "The one feature the notebook currently doesn't support as a client is the ability to send data to the kernel's", | |||
|
305 | "standard input socket. That is, if the kernel requires information to be typed interactively by calling the", | |||
|
306 | "builtin `raw_input` function, the notebook will be blocked. This happens for example if you run a script", | |||
|
307 | "that queries interactively for parameters, and very importantly, is how the interactive IPython debugger that ", | |||
|
308 | "activates when you type `%debug` works.", | |||
|
309 | "", | |||
|
310 | "So, in order to be able to use `%debug` or anything else that requires `raw_input`, you can either use a Qt ", | |||
|
311 | "console or a terminal console:", | |||
|
312 | "", | |||
|
313 | "- From the notebook, typing `%qtconsole` finds all the necessary connection data for you.", | |||
|
314 | "- From the terminal, first type `%connect_info` while still in the notebook, and then copy and paste the ", | |||
|
315 | "resulting information, using `qtconsole` or `console` depending on which type of client you want." | |||
|
316 | ] | |||
|
317 | }, | |||
|
318 | { | |||
|
319 | "cell_type": "markdown", | |||
|
320 | "source": [ | |||
|
321 | "## Display of complex objects", | |||
|
322 | "", | |||
|
323 | "As the 'tour' notebook shows, the IPython notebook has fairly sophisticated display capabilities. In addition", | |||
|
324 | "to the examples there, you can study the `display_protocol` notebook in this same examples folder, to ", | |||
|
325 | "learn how to customize arbitrary objects (in your own code or external libraries) to display in the notebook", | |||
|
326 | "in any way you want, including graphical forms or mathematical expressions." | |||
|
327 | ] | |||
|
328 | }, | |||
|
329 | { | |||
|
330 | "cell_type": "markdown", | |||
|
331 | "source": [ | |||
|
332 | "## Plotting support", | |||
|
333 | "", | |||
|
334 | "As we've explained already, the notebook is just another frontend talking to the same IPython kernel that", | |||
|
335 | "you're already familiar with, so the same options for plotting support apply.", | |||
|
336 | "", | |||
|
337 | "If you start the notebook with `--pylab`, you will get matplotlib's floating, interactive windows and you", | |||
|
338 | "can call the `display` function to paste figures into the notebook document. If you start it with ", | |||
|
339 | "`--pylab inline`, all plots will appear inline automatically. In this regard, the notebook works identically", | |||
|
340 | "to the Qt console.", | |||
|
341 | "", | |||
|
342 | "Note that if you start the notebook server with pylab support, *all* kernels are automatically started in", | |||
|
343 | "pylab mode and with the same choice of backend (i.e. floating windows or inline figures). But you can also", | |||
|
344 | "start the notebook server simply by typing `ipython notebook`, and then selectively turn on pylab support ", | |||
|
345 | "only for the notebooks you want by using the `%pylab` magic (see its docstring for details)." | |||
|
346 | ] | |||
|
347 | }, | |||
|
348 | { | |||
|
349 | "cell_type": "code", | |||
|
350 | "collapsed": false, | |||
|
351 | "input": [ | |||
|
352 | "%pylab inline", | |||
|
353 | "plot(rand(100))" | |||
|
354 | ], | |||
|
355 | "language": "python", | |||
|
356 | "outputs": [ | |||
|
357 | { | |||
|
358 | "output_type": "stream", | |||
|
359 | "stream": "stdout", | |||
|
360 | "text": [ | |||
|
361 | "", | |||
|
362 | "Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].", | |||
|
363 | "For more information, type 'help(pylab)'." | |||
|
364 | ] | |||
|
365 | }, | |||
|
366 | { | |||
|
367 | "output_type": "pyout", | |||
|
368 | "prompt_number": 5, | |||
|
369 | "text": [ | |||
|
370 | "[<matplotlib.lines.Line2D at 0x11165bcd0>]" | |||
|
371 | ] | |||
|
372 | }, | |||
|
373 | { | |||
|
374 | "output_type": "display_data", | |||
|
375 | "png": "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\n" | |||
|
376 | } | |||
|
377 | ], | |||
|
378 | "prompt_number": 5 | |||
|
379 | }, | |||
|
380 | { | |||
|
381 | "cell_type": "markdown", | |||
|
382 | "source": [ | |||
|
383 | "## Security", | |||
|
384 | "", | |||
|
385 | "By default the notebook only listens on localhost, so it does not expose your computer to attacks coming from", | |||
|
386 | "the internet. By default the notebook does not require any authentication, but you can configure it to", | |||
|
387 | "ask for a password before allowing access to the files. ", | |||
|
388 | "", | |||
|
389 | "Furthermore, you can require the notebook to encrypt all communications by using SSL and making all connections", | |||
|
390 | "using the https protocol instead of plain http. This is a good idea if you decide to run your notebook on", | |||
|
391 | "addresses that are visible from the internet. For further details on how to configure this, see the", | |||
|
392 | "[security section](http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html#security) of the ", | |||
|
393 | "manual.", | |||
|
394 | "", | |||
|
395 | "Finally, note that you can also run a notebook with the `--read-only` flag, which lets you provide access", | |||
|
396 | "to your notebook documents to others without letting them execute code (which can be useful to broadcast", | |||
|
397 | "a computation to colleagues or students, for example). The read-only flag behaves differently depending", | |||
|
398 | "on whether the server has a password or not:", | |||
|
399 | "", | |||
|
400 | "- Passwordless server: users directly see all notebooks in read-only mode.", | |||
|
401 | "- Password-protected server: users can see all notebooks in read-only mode, but a login button is available", | |||
|
402 | "and once a user authenticates, he or she obtains write/execute privileges.", | |||
|
403 | "", | |||
|
404 | "The first case above makes it easy to broadcast on the fly an existing notebook by simply starting a *second* ", | |||
|
405 | "notebook server in the same directory as the first, but in read-only mode. This can be done without having", | |||
|
406 | "to configure a password first (which requires calling a hashing function and editing a configuration file)." | |||
|
407 | ] | |||
|
408 | }, | |||
|
409 | { | |||
|
410 | "cell_type": "code", | |||
|
411 | "collapsed": true, | |||
|
412 | "input": [], | |||
|
413 | "language": "python", | |||
|
414 | "outputs": [] | |||
|
415 | } | |||
|
416 | ] | |||
|
417 | } | |||
|
418 | ] | |||
|
419 | } No newline at end of file |
@@ -0,0 +1,143 b'' | |||||
|
1 | { | |||
|
2 | "metadata": { | |||
|
3 | "cell_tags": [ | |||
|
4 | [ | |||
|
5 | "<None>", | |||
|
6 | null | |||
|
7 | ] | |||
|
8 | ], | |||
|
9 | "name": "" | |||
|
10 | }, | |||
|
11 | "nbformat": 3, | |||
|
12 | "nbformat_minor": 0, | |||
|
13 | "worksheets": [ | |||
|
14 | { | |||
|
15 | "cells": [ | |||
|
16 | { | |||
|
17 | "cell_type": "heading", | |||
|
18 | "level": 1, | |||
|
19 | "metadata": {}, | |||
|
20 | "source": [ | |||
|
21 | "nbconvert latex test" | |||
|
22 | ] | |||
|
23 | }, | |||
|
24 | { | |||
|
25 | "cell_type": "markdown", | |||
|
26 | "metadata": {}, | |||
|
27 | "source": [ | |||
|
28 | "**Lorem ipsum** dolor sit amet, consectetur adipiscing elit. Nunc luctus bibendum felis dictum sodales. Ut suscipit, orci ut interdum imperdiet, purus ligula mollis *justo*, non malesuada nisl augue eget lorem. Donec bibendum, erat sit amet porttitor aliquam, urna lorem ornare libero, in vehicula diam diam ut ante. Nam non urna rhoncus, accumsan elit sit amet, mollis tellus. Vestibulum nec tellus metus. Vestibulum tempor, ligula et vehicula rhoncus, sapien turpis faucibus lorem, id dapibus turpis mauris ac orci. Sed volutpat vestibulum venenatis." | |||
|
29 | ] | |||
|
30 | }, | |||
|
31 | { | |||
|
32 | "cell_type": "heading", | |||
|
33 | "level": 2, | |||
|
34 | "metadata": {}, | |||
|
35 | "source": [ | |||
|
36 | "Printed Using Python" | |||
|
37 | ] | |||
|
38 | }, | |||
|
39 | { | |||
|
40 | "cell_type": "code", | |||
|
41 | "collapsed": false, | |||
|
42 | "input": [ | |||
|
43 | "next_paragraph = \"\"\"\n", | |||
|
44 | "Aenean vitae diam consectetur, tempus arcu quis, ultricies urna. Vivamus venenatis sem \n", | |||
|
45 | "quis orci condimentum, sed feugiat dui porta.\n", | |||
|
46 | "\"\"\"\n", | |||
|
47 | "\n", | |||
|
48 | "def nifty_print(text):\n", | |||
|
49 | " \"\"\"Used to test syntax highlighting\"\"\"\n", | |||
|
50 | " \n", | |||
|
51 | " print(text * 2)\n", | |||
|
52 | "\n", | |||
|
53 | "nifty_print(next_paragraph)" | |||
|
54 | ], | |||
|
55 | "language": "python", | |||
|
56 | "metadata": {}, | |||
|
57 | "outputs": [ | |||
|
58 | { | |||
|
59 | "output_type": "stream", | |||
|
60 | "stream": "stdout", | |||
|
61 | "text": [ | |||
|
62 | "\n", | |||
|
63 | "Aenean vitae diam consectetur, tempus arcu quis, ultricies urna. Vivamus venenatis sem \n", | |||
|
64 | "quis orci condimentum, sed feugiat dui porta.\n", | |||
|
65 | "\n", | |||
|
66 | "Aenean vitae diam consectetur, tempus arcu quis, ultricies urna. Vivamus venenatis sem \n", | |||
|
67 | "quis orci condimentum, sed feugiat dui porta.\n", | |||
|
68 | "\n" | |||
|
69 | ] | |||
|
70 | } | |||
|
71 | ], | |||
|
72 | "prompt_number": 3 | |||
|
73 | }, | |||
|
74 | { | |||
|
75 | "cell_type": "heading", | |||
|
76 | "level": 2, | |||
|
77 | "metadata": {}, | |||
|
78 | "source": [ | |||
|
79 | "Pyout" | |||
|
80 | ] | |||
|
81 | }, | |||
|
82 | { | |||
|
83 | "cell_type": "code", | |||
|
84 | "collapsed": false, | |||
|
85 | "input": [ | |||
|
86 | "Text = \"\"\"\n", | |||
|
87 | "Aliquam blandit aliquet enim, eget scelerisque eros adipiscing quis. Nunc sed metus \n", | |||
|
88 | "ut lorem condimentum condimentum nec id enim. Sed malesuada cursus hendrerit. Praesent \n", | |||
|
89 | "et commodo justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. \n", | |||
|
90 | "Curabitur et magna ante. Proin luctus tellus sit amet egestas laoreet. Sed dapibus \n", | |||
|
91 | "neque ac nulla mollis cursus. Fusce mollis egestas libero mattis facilisis.\n", | |||
|
92 | "\"\"\"\n", | |||
|
93 | "Text" | |||
|
94 | ], | |||
|
95 | "language": "python", | |||
|
96 | "metadata": {}, | |||
|
97 | "outputs": [ | |||
|
98 | { | |||
|
99 | "metadata": {}, | |||
|
100 | "output_type": "pyout", | |||
|
101 | "prompt_number": 2, | |||
|
102 | "text": [ | |||
|
103 | "'\\nAliquam blandit aliquet enim, eget scelerisque eros adipiscing quis. Nunc sed metus \\nut lorem condimentum condimentum nec id enim. Sed malesuada cursus hendrerit. Praesent \\net commodo justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. \\nCurabitur et magna ante. Proin luctus tellus sit amet egestas laoreet. Sed dapibus \\nneque ac nulla mollis cursus. Fusce mollis egestas libero mattis facilisis.\\n'" | |||
|
104 | ] | |||
|
105 | } | |||
|
106 | ], | |||
|
107 | "prompt_number": 2 | |||
|
108 | }, | |||
|
109 | { | |||
|
110 | "cell_type": "heading", | |||
|
111 | "level": 3, | |||
|
112 | "metadata": {}, | |||
|
113 | "source": [ | |||
|
114 | "Image" | |||
|
115 | ] | |||
|
116 | }, | |||
|
117 | { | |||
|
118 | "cell_type": "code", | |||
|
119 | "collapsed": false, | |||
|
120 | "input": [ | |||
|
121 | "from IPython.core.display import Image\n", | |||
|
122 | "Image(data=\"http://ipython.org/_static/IPy_header.png\")" | |||
|
123 | ], | |||
|
124 | "language": "python", | |||
|
125 | "metadata": {}, | |||
|
126 | "outputs": [ | |||
|
127 | { | |||
|
128 | "metadata": {}, | |||
|
129 | "output_type": "pyout", | |||
|
130 | "png": "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\n", | |||
|
131 | "prompt_number": 6, | |||
|
132 | "text": [ | |||
|
133 | "<IPython.core.display.Image at 0x2100f90>" | |||
|
134 | ] | |||
|
135 | } | |||
|
136 | ], | |||
|
137 | "prompt_number": 6 | |||
|
138 | } | |||
|
139 | ], | |||
|
140 | "metadata": {} | |||
|
141 | } | |||
|
142 | ] | |||
|
143 | } No newline at end of file |
@@ -0,0 +1,69 b'' | |||||
|
1 | """ | |||
|
2 | Contains tests class for convert.py | |||
|
3 | """ | |||
|
4 | #----------------------------------------------------------------------------- | |||
|
5 | # Copyright (C) 2013 The IPython Development Team | |||
|
6 | # | |||
|
7 | # Distributed under the terms of the BSD License. The full license is in | |||
|
8 | # the file COPYING, distributed as part of this software. | |||
|
9 | #----------------------------------------------------------------------------- | |||
|
10 | ||||
|
11 | #----------------------------------------------------------------------------- | |||
|
12 | # Imports | |||
|
13 | #----------------------------------------------------------------------------- | |||
|
14 | ||||
|
15 | from .base import TestsBase | |||
|
16 | ||||
|
17 | from ..convert import convert | |||
|
18 | from ..reader import read, get_version | |||
|
19 | from ..current import current_nbformat | |||
|
20 | ||||
|
21 | #----------------------------------------------------------------------------- | |||
|
22 | # Classes and functions | |||
|
23 | #----------------------------------------------------------------------------- | |||
|
24 | ||||
|
25 | class TestConvert(TestsBase): | |||
|
26 | ||||
|
27 | def test_downgrade(self): | |||
|
28 | """Do notebook downgrades work?""" | |||
|
29 | ||||
|
30 | # Open a version 3 notebook and attempt to downgrade it to version 2. | |||
|
31 | with self.fopen(u'test3.ipynb', u'r') as f: | |||
|
32 | nb = read(f) | |||
|
33 | nb = convert(nb, 2) | |||
|
34 | ||||
|
35 | # Check if downgrade was successful. | |||
|
36 | (major, minor) = get_version(nb) | |||
|
37 | self.assertEqual(major, 2) | |||
|
38 | ||||
|
39 | ||||
|
40 | def test_upgrade(self): | |||
|
41 | """Do notebook upgrades work?""" | |||
|
42 | ||||
|
43 | # Open a version 2 notebook and attempt to upgrade it to version 3. | |||
|
44 | with self.fopen(u'test2.ipynb', u'r') as f: | |||
|
45 | nb = read(f) | |||
|
46 | nb = convert(nb, 3) | |||
|
47 | ||||
|
48 | # Check if upgrade was successful. | |||
|
49 | (major, minor) = get_version(nb) | |||
|
50 | self.assertEqual(major, 3) | |||
|
51 | ||||
|
52 | ||||
|
53 | def test_open_current(self): | |||
|
54 | """Can an old notebook be opened and converted to the current version | |||
|
55 | while remembering the original version of the notebook?""" | |||
|
56 | ||||
|
57 | # Open a version 2 notebook and attempt to upgrade it to the current version | |||
|
58 | # while remembering it's version information. | |||
|
59 | with self.fopen(u'test2.ipynb', u'r') as f: | |||
|
60 | nb = read(f) | |||
|
61 | (original_major, original_minor) = get_version(nb) | |||
|
62 | nb = convert(nb, current_nbformat) | |||
|
63 | ||||
|
64 | # Check if upgrade was successful. | |||
|
65 | (major, minor) = get_version(nb) | |||
|
66 | self.assertEqual(major, current_nbformat) | |||
|
67 | ||||
|
68 | # Check if the original major revision was remembered. | |||
|
69 | self.assertEqual(original_major, 2) |
@@ -0,0 +1,36 b'' | |||||
|
1 | """ | |||
|
2 | Contains tests class for current.py | |||
|
3 | """ | |||
|
4 | #----------------------------------------------------------------------------- | |||
|
5 | # Copyright (C) 2013 The IPython Development Team | |||
|
6 | # | |||
|
7 | # Distributed under the terms of the BSD License. The full license is in | |||
|
8 | # the file COPYING, distributed as part of this software. | |||
|
9 | #----------------------------------------------------------------------------- | |||
|
10 | ||||
|
11 | #----------------------------------------------------------------------------- | |||
|
12 | # Imports | |||
|
13 | #----------------------------------------------------------------------------- | |||
|
14 | ||||
|
15 | from .base import TestsBase | |||
|
16 | ||||
|
17 | from ..reader import get_version | |||
|
18 | from ..current import read, current_nbformat | |||
|
19 | ||||
|
20 | #----------------------------------------------------------------------------- | |||
|
21 | # Classes and functions | |||
|
22 | #----------------------------------------------------------------------------- | |||
|
23 | ||||
|
24 | class TestCurrent(TestsBase): | |||
|
25 | ||||
|
26 | def test_read(self): | |||
|
27 | """Can older notebooks be opened and automatically converted to the current | |||
|
28 | nbformat?""" | |||
|
29 | ||||
|
30 | # Open a version 2 notebook. | |||
|
31 | with self.fopen(u'test2.ipynb', u'r') as f: | |||
|
32 | nb = read(f, u'json') | |||
|
33 | ||||
|
34 | # Check that the notebook was upgraded to the latest version automatically. | |||
|
35 | (major, minor) = get_version(nb) | |||
|
36 | self.assertEqual(major, current_nbformat) |
@@ -0,0 +1,38 b'' | |||||
|
1 | """ | |||
|
2 | Contains tests class for reader.py | |||
|
3 | """ | |||
|
4 | #----------------------------------------------------------------------------- | |||
|
5 | # Copyright (C) 2013 The IPython Development Team | |||
|
6 | # | |||
|
7 | # Distributed under the terms of the BSD License. The full license is in | |||
|
8 | # the file COPYING, distributed as part of this software. | |||
|
9 | #----------------------------------------------------------------------------- | |||
|
10 | ||||
|
11 | #----------------------------------------------------------------------------- | |||
|
12 | # Imports | |||
|
13 | #----------------------------------------------------------------------------- | |||
|
14 | ||||
|
15 | from .base import TestsBase | |||
|
16 | ||||
|
17 | from ..reader import read, get_version | |||
|
18 | ||||
|
19 | #----------------------------------------------------------------------------- | |||
|
20 | # Classes and functions | |||
|
21 | #----------------------------------------------------------------------------- | |||
|
22 | ||||
|
23 | class TestReader(TestsBase): | |||
|
24 | ||||
|
25 | def test_read(self): | |||
|
26 | """Can older notebooks be opened without modification?""" | |||
|
27 | ||||
|
28 | # Open a version 3 notebook. Make sure it is still version 3. | |||
|
29 | with self.fopen(u'test3.ipynb', u'r') as f: | |||
|
30 | nb = read(f) | |||
|
31 | (major, minor) = get_version(nb) | |||
|
32 | self.assertEqual(major, 3) | |||
|
33 | ||||
|
34 | # Open a version 2 notebook. Make sure it is still version 2. | |||
|
35 | with self.fopen(u'test2.ipynb', u'r') as f: | |||
|
36 | nb = read(f) | |||
|
37 | (major, minor) = get_version(nb) | |||
|
38 | self.assertEqual(major, 2) |
@@ -1,226 +1,200 b'' | |||||
1 | """The official API for working with notebooks in the current format version. |
|
1 | """The official API for working with notebooks in the current format version. | |
2 |
|
2 | |||
3 | Authors: |
|
3 | Authors: | |
4 |
|
4 | |||
5 | * Brian Granger |
|
5 | * Brian Granger | |
|
6 | * Jonathan Frederic | |||
6 | """ |
|
7 | """ | |
7 |
|
8 | |||
8 | #----------------------------------------------------------------------------- |
|
9 | #----------------------------------------------------------------------------- | |
9 | # Copyright (C) 2008-2011 The IPython Development Team |
|
10 | # Copyright (C) 2008-2011 The IPython Development Team | |
10 | # |
|
11 | # | |
11 | # Distributed under the terms of the BSD License. The full license is in |
|
12 | # Distributed under the terms of the BSD License. The full license is in | |
12 | # the file COPYING, distributed as part of this software. |
|
13 | # the file COPYING, distributed as part of this software. | |
13 | #----------------------------------------------------------------------------- |
|
14 | #----------------------------------------------------------------------------- | |
14 |
|
15 | |||
15 | #----------------------------------------------------------------------------- |
|
16 | #----------------------------------------------------------------------------- | |
16 | # Imports |
|
17 | # Imports | |
17 | #----------------------------------------------------------------------------- |
|
18 | #----------------------------------------------------------------------------- | |
18 |
|
19 | |||
19 | from __future__ import print_function |
|
20 | from __future__ import print_function | |
20 | import json |
|
21 | ||
21 | from xml.etree import ElementTree as ET |
|
22 | from xml.etree import ElementTree as ET | |
22 | import re |
|
23 | import re | |
23 |
|
24 | |||
24 | from IPython.nbformat import v3 |
|
|||
25 | from IPython.nbformat import v2 |
|
|||
26 | from IPython.nbformat import v1 |
|
|||
27 |
|
||||
28 | from IPython.nbformat.v3 import ( |
|
25 | from IPython.nbformat.v3 import ( | |
29 | NotebookNode, |
|
26 | NotebookNode, | |
30 | new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, |
|
27 | new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, | |
31 | parse_filename, new_metadata, new_author, new_heading_cell, nbformat, |
|
28 | parse_filename, new_metadata, new_author, new_heading_cell, nbformat, | |
32 | nbformat_minor, to_notebook_json |
|
29 | nbformat_minor, to_notebook_json | |
33 | ) |
|
30 | ) | |
34 |
|
31 | |||
|
32 | from .reader import reads as reader_reads | |||
|
33 | from .reader import versions | |||
|
34 | from .convert import convert | |||
|
35 | ||||
35 | #----------------------------------------------------------------------------- |
|
36 | #----------------------------------------------------------------------------- | |
36 | # Code |
|
37 | # Code | |
37 | #----------------------------------------------------------------------------- |
|
38 | #----------------------------------------------------------------------------- | |
38 |
|
39 | |||
39 | current_nbformat = nbformat |
|
40 | current_nbformat = nbformat | |
40 | current_nbformat_minor = nbformat_minor |
|
41 | current_nbformat_minor = nbformat_minor | |
41 |
|
42 | |||
42 |
|
43 | |||
43 | class NBFormatError(ValueError): |
|
44 | class NBFormatError(ValueError): | |
44 | pass |
|
45 | pass | |
45 |
|
46 | |||
46 | class NotJSONError(ValueError): |
|
|||
47 | pass |
|
|||
48 |
|
||||
49 |
|
||||
50 | def parse_json(s, **kwargs): |
|
|||
51 | """Parse a string into a (nbformat, dict) tuple.""" |
|
|||
52 | try: |
|
|||
53 | d = json.loads(s, **kwargs) |
|
|||
54 | except ValueError: |
|
|||
55 | raise NotJSONError("Notebook does not appear to be JSON: %r" % s[:16]) |
|
|||
56 | nbf = d.get('nbformat', 1) |
|
|||
57 | nbm = d.get('nbformat_minor', 0) |
|
|||
58 | return nbf, nbm, d |
|
|||
59 |
|
||||
60 |
|
47 | |||
61 | def parse_py(s, **kwargs): |
|
48 | def parse_py(s, **kwargs): | |
62 | """Parse a string into a (nbformat, string) tuple.""" |
|
49 | """Parse a string into a (nbformat, string) tuple.""" | |
63 | nbf = current_nbformat |
|
50 | nbf = current_nbformat | |
64 | nbm = current_nbformat_minor |
|
51 | nbm = current_nbformat_minor | |
65 |
|
52 | |||
66 | pattern = r'# <nbformat>(?P<nbformat>\d+[\.\d+]*)</nbformat>' |
|
53 | pattern = r'# <nbformat>(?P<nbformat>\d+[\.\d+]*)</nbformat>' | |
67 | m = re.search(pattern,s) |
|
54 | m = re.search(pattern,s) | |
68 | if m is not None: |
|
55 | if m is not None: | |
69 | digits = m.group('nbformat').split('.') |
|
56 | digits = m.group('nbformat').split('.') | |
70 | nbf = int(digits[0]) |
|
57 | nbf = int(digits[0]) | |
71 | if len(digits) > 1: |
|
58 | if len(digits) > 1: | |
72 | nbm = int(digits[1]) |
|
59 | nbm = int(digits[1]) | |
73 |
|
60 | |||
74 | return nbf, nbm, s |
|
61 | return nbf, nbm, s | |
75 |
|
62 | |||
76 |
|
63 | |||
77 | def reads_json(s, **kwargs): |
|
64 | def reads_json(s, **kwargs): | |
78 | """Read a JSON notebook from a string and return the NotebookNode object.""" |
|
65 | """Read a JSON notebook from a string and return the NotebookNode object.""" | |
79 | nbf, minor, d = parse_json(s, **kwargs) |
|
66 | return convert(reader_reads(s), current_nbformat) | |
80 | if nbf == 1: |
|
|||
81 | nb = v1.to_notebook_json(d, **kwargs) |
|
|||
82 | nb = v3.convert_to_this_nbformat(nb, orig_version=1) |
|
|||
83 | elif nbf == 2: |
|
|||
84 | nb = v2.to_notebook_json(d, **kwargs) |
|
|||
85 | nb = v3.convert_to_this_nbformat(nb, orig_version=2) |
|
|||
86 | elif nbf == 3: |
|
|||
87 | nb = v3.to_notebook_json(d, **kwargs) |
|
|||
88 | nb = v3.convert_to_this_nbformat(nb, orig_version=3, orig_minor=minor) |
|
|||
89 | else: |
|
|||
90 | raise NBFormatError('Unsupported JSON nbformat version %s (supported version: %i)' % (nbf, 3)) |
|
|||
91 | return nb |
|
|||
92 |
|
67 | |||
93 |
|
68 | |||
94 | def writes_json(nb, **kwargs): |
|
69 | def writes_json(nb, **kwargs): | |
95 |
return v |
|
70 | return versions[current_nbformat].writes_json(nb, **kwargs) | |
96 |
|
71 | |||
97 |
|
72 | |||
98 | def reads_py(s, **kwargs): |
|
73 | def reads_py(s, **kwargs): | |
99 | """Read a .py notebook from a string and return the NotebookNode object.""" |
|
74 | """Read a .py notebook from a string and return the NotebookNode object.""" | |
100 | nbf, nbm, s = parse_py(s, **kwargs) |
|
75 | nbf, nbm, s = parse_py(s, **kwargs) | |
101 |
if nbf |
|
76 | if nbf in (2, 3): | |
102 |
nb = v |
|
77 | nb = versions[nbf].to_notebook_py(s, **kwargs) | |
103 | elif nbf == 3: |
|
|||
104 | nb = v3.to_notebook_py(s, **kwargs) |
|
|||
105 | else: |
|
78 | else: | |
106 | raise NBFormatError('Unsupported PY nbformat version: %i' % nbf) |
|
79 | raise NBFormatError('Unsupported PY nbformat version: %i' % nbf) | |
107 | return nb |
|
80 | return nb | |
108 |
|
81 | |||
109 |
|
82 | |||
110 | def writes_py(nb, **kwargs): |
|
83 | def writes_py(nb, **kwargs): | |
111 | return v3.writes_py(nb, **kwargs) |
|
84 | # nbformat 3 is the latest format that supports py | |
|
85 | return versions[3].writes_py(nb, **kwargs) | |||
112 |
|
86 | |||
113 |
|
87 | |||
114 | # High level API |
|
88 | # High level API | |
115 |
|
89 | |||
116 |
|
90 | |||
117 | def reads(s, format, **kwargs): |
|
91 | def reads(s, format, **kwargs): | |
118 | """Read a notebook from a string and return the NotebookNode object. |
|
92 | """Read a notebook from a string and return the NotebookNode object. | |
119 |
|
93 | |||
120 | This function properly handles notebooks of any version. The notebook |
|
94 | This function properly handles notebooks of any version. The notebook | |
121 | returned will always be in the current version's format. |
|
95 | returned will always be in the current version's format. | |
122 |
|
96 | |||
123 | Parameters |
|
97 | Parameters | |
124 | ---------- |
|
98 | ---------- | |
125 | s : unicode |
|
99 | s : unicode | |
126 | The raw unicode string to read the notebook from. |
|
100 | The raw unicode string to read the notebook from. | |
127 | format : (u'json', u'ipynb', u'py') |
|
101 | format : (u'json', u'ipynb', u'py') | |
128 | The format that the string is in. |
|
102 | The format that the string is in. | |
129 |
|
103 | |||
130 | Returns |
|
104 | Returns | |
131 | ------- |
|
105 | ------- | |
132 | nb : NotebookNode |
|
106 | nb : NotebookNode | |
133 | The notebook that was read. |
|
107 | The notebook that was read. | |
134 | """ |
|
108 | """ | |
135 | format = unicode(format) |
|
109 | format = unicode(format) | |
136 | if format == u'json' or format == u'ipynb': |
|
110 | if format == u'json' or format == u'ipynb': | |
137 | return reads_json(s, **kwargs) |
|
111 | return reads_json(s, **kwargs) | |
138 | elif format == u'py': |
|
112 | elif format == u'py': | |
139 | return reads_py(s, **kwargs) |
|
113 | return reads_py(s, **kwargs) | |
140 | else: |
|
114 | else: | |
141 | raise NBFormatError('Unsupported format: %s' % format) |
|
115 | raise NBFormatError('Unsupported format: %s' % format) | |
142 |
|
116 | |||
143 |
|
117 | |||
144 | def writes(nb, format, **kwargs): |
|
118 | def writes(nb, format, **kwargs): | |
145 | """Write a notebook to a string in a given format in the current nbformat version. |
|
119 | """Write a notebook to a string in a given format in the current nbformat version. | |
146 |
|
120 | |||
147 | This function always writes the notebook in the current nbformat version. |
|
121 | This function always writes the notebook in the current nbformat version. | |
148 |
|
122 | |||
149 | Parameters |
|
123 | Parameters | |
150 | ---------- |
|
124 | ---------- | |
151 | nb : NotebookNode |
|
125 | nb : NotebookNode | |
152 | The notebook to write. |
|
126 | The notebook to write. | |
153 | format : (u'json', u'ipynb', u'py') |
|
127 | format : (u'json', u'ipynb', u'py') | |
154 | The format to write the notebook in. |
|
128 | The format to write the notebook in. | |
155 |
|
129 | |||
156 | Returns |
|
130 | Returns | |
157 | ------- |
|
131 | ------- | |
158 | s : unicode |
|
132 | s : unicode | |
159 | The notebook string. |
|
133 | The notebook string. | |
160 | """ |
|
134 | """ | |
161 | format = unicode(format) |
|
135 | format = unicode(format) | |
162 | if format == u'json' or format == u'ipynb': |
|
136 | if format == u'json' or format == u'ipynb': | |
163 | return writes_json(nb, **kwargs) |
|
137 | return writes_json(nb, **kwargs) | |
164 | elif format == u'py': |
|
138 | elif format == u'py': | |
165 | return writes_py(nb, **kwargs) |
|
139 | return writes_py(nb, **kwargs) | |
166 | else: |
|
140 | else: | |
167 | raise NBFormatError('Unsupported format: %s' % format) |
|
141 | raise NBFormatError('Unsupported format: %s' % format) | |
168 |
|
142 | |||
169 |
|
143 | |||
170 | def read(fp, format, **kwargs): |
|
144 | def read(fp, format, **kwargs): | |
171 | """Read a notebook from a file and return the NotebookNode object. |
|
145 | """Read a notebook from a file and return the NotebookNode object. | |
172 |
|
146 | |||
173 | This function properly handles notebooks of any version. The notebook |
|
147 | This function properly handles notebooks of any version. The notebook | |
174 | returned will always be in the current version's format. |
|
148 | returned will always be in the current version's format. | |
175 |
|
149 | |||
176 | Parameters |
|
150 | Parameters | |
177 | ---------- |
|
151 | ---------- | |
178 | fp : file |
|
152 | fp : file | |
179 | Any file-like object with a read method. |
|
153 | Any file-like object with a read method. | |
180 | format : (u'json', u'ipynb', u'py') |
|
154 | format : (u'json', u'ipynb', u'py') | |
181 | The format that the string is in. |
|
155 | The format that the string is in. | |
182 |
|
156 | |||
183 | Returns |
|
157 | Returns | |
184 | ------- |
|
158 | ------- | |
185 | nb : NotebookNode |
|
159 | nb : NotebookNode | |
186 | The notebook that was read. |
|
160 | The notebook that was read. | |
187 | """ |
|
161 | """ | |
188 | return reads(fp.read(), format, **kwargs) |
|
162 | return reads(fp.read(), format, **kwargs) | |
189 |
|
163 | |||
190 |
|
164 | |||
191 | def write(nb, fp, format, **kwargs): |
|
165 | def write(nb, fp, format, **kwargs): | |
192 | """Write a notebook to a file in a given format in the current nbformat version. |
|
166 | """Write a notebook to a file in a given format in the current nbformat version. | |
193 |
|
167 | |||
194 | This function always writes the notebook in the current nbformat version. |
|
168 | This function always writes the notebook in the current nbformat version. | |
195 |
|
169 | |||
196 | Parameters |
|
170 | Parameters | |
197 | ---------- |
|
171 | ---------- | |
198 | nb : NotebookNode |
|
172 | nb : NotebookNode | |
199 | The notebook to write. |
|
173 | The notebook to write. | |
200 | fp : file |
|
174 | fp : file | |
201 | Any file-like object with a write method. |
|
175 | Any file-like object with a write method. | |
202 | format : (u'json', u'ipynb', u'py') |
|
176 | format : (u'json', u'ipynb', u'py') | |
203 | The format to write the notebook in. |
|
177 | The format to write the notebook in. | |
204 |
|
178 | |||
205 | Returns |
|
179 | Returns | |
206 | ------- |
|
180 | ------- | |
207 | s : unicode |
|
181 | s : unicode | |
208 | The notebook string. |
|
182 | The notebook string. | |
209 | """ |
|
183 | """ | |
210 | return fp.write(writes(nb, format, **kwargs)) |
|
184 | return fp.write(writes(nb, format, **kwargs)) | |
211 |
|
185 | |||
212 | def _convert_to_metadata(): |
|
186 | def _convert_to_metadata(): | |
213 | """Convert to a notebook having notebook metadata.""" |
|
187 | """Convert to a notebook having notebook metadata.""" | |
214 | import glob |
|
188 | import glob | |
215 | for fname in glob.glob('*.ipynb'): |
|
189 | for fname in glob.glob('*.ipynb'): | |
216 | print('Converting file:',fname) |
|
190 | print('Converting file:',fname) | |
217 | with open(fname,'r') as f: |
|
191 | with open(fname,'r') as f: | |
218 | nb = read(f,u'json') |
|
192 | nb = read(f,u'json') | |
219 | md = new_metadata() |
|
193 | md = new_metadata() | |
220 | if u'name' in nb: |
|
194 | if u'name' in nb: | |
221 | md.name = nb.name |
|
195 | md.name = nb.name | |
222 | del nb[u'name'] |
|
196 | del nb[u'name'] | |
223 | nb.metadata = md |
|
197 | nb.metadata = md | |
224 | with open(fname,'w') as f: |
|
198 | with open(fname,'w') as f: | |
225 | write(nb, f, u'json') |
|
199 | write(nb, f, u'json') | |
226 |
|
200 |
@@ -1,24 +1,23 b'' | |||||
1 | """The main module for the v1 notebook format.""" |
|
1 | """The main module for the v1 notebook format.""" | |
2 |
|
2 | |||
3 | #----------------------------------------------------------------------------- |
|
3 | #----------------------------------------------------------------------------- | |
4 | # Copyright (C) 2008-2011 The IPython Development Team |
|
4 | # Copyright (C) 2008-2011 The IPython Development Team | |
5 | # |
|
5 | # | |
6 | # Distributed under the terms of the BSD License. The full license is in |
|
6 | # Distributed under the terms of the BSD License. The full license is in | |
7 | # the file COPYING, distributed as part of this software. |
|
7 | # the file COPYING, distributed as part of this software. | |
8 | #----------------------------------------------------------------------------- |
|
8 | #----------------------------------------------------------------------------- | |
9 |
|
9 | |||
10 | #----------------------------------------------------------------------------- |
|
10 | #----------------------------------------------------------------------------- | |
11 | # Imports |
|
11 | # Imports | |
12 | #----------------------------------------------------------------------------- |
|
12 | #----------------------------------------------------------------------------- | |
13 |
|
13 | |||
14 | from .nbbase import ( |
|
14 | from .nbbase import ( | |
15 | NotebookNode, |
|
15 | NotebookNode, | |
16 | new_code_cell, new_text_cell, new_notebook |
|
16 | new_code_cell, new_text_cell, new_notebook | |
17 | ) |
|
17 | ) | |
18 |
|
18 | |||
19 | from .nbjson import reads as reads_json, writes as writes_json |
|
19 | from .nbjson import reads as reads_json, writes as writes_json | |
20 | from .nbjson import reads as read_json, writes as write_json |
|
20 | from .nbjson import reads as read_json, writes as write_json | |
21 | from .nbjson import to_notebook as to_notebook_json |
|
21 | from .nbjson import to_notebook as to_notebook_json | |
22 |
|
22 | |||
23 |
from .convert import |
|
23 | from .convert import upgrade | |
24 |
|
@@ -1,16 +1,16 b'' | |||||
1 | """Convert notebook to the v1 format.""" |
|
1 | """Convert notebook to the v1 format.""" | |
2 |
|
2 | |||
3 | #----------------------------------------------------------------------------- |
|
3 | #----------------------------------------------------------------------------- | |
4 | # Copyright (C) 2008-2011 The IPython Development Team |
|
4 | # Copyright (C) 2008-2011 The IPython Development Team | |
5 | # |
|
5 | # | |
6 | # Distributed under the terms of the BSD License. The full license is in |
|
6 | # Distributed under the terms of the BSD License. The full license is in | |
7 | # the file COPYING, distributed as part of this software. |
|
7 | # the file COPYING, distributed as part of this software. | |
8 | #----------------------------------------------------------------------------- |
|
8 | #----------------------------------------------------------------------------- | |
9 |
|
9 | |||
10 | #----------------------------------------------------------------------------- |
|
10 | #----------------------------------------------------------------------------- | |
11 | # Code |
|
11 | # Code | |
12 | #----------------------------------------------------------------------------- |
|
12 | #----------------------------------------------------------------------------- | |
13 |
|
13 | |||
14 |
def |
|
14 | def upgrade(nb, orig_version=None): | |
15 | raise ValueError('Cannot convert to v1 notebook format') |
|
15 | raise ValueError('Cannot convert to v1 notebook format') | |
16 |
|
16 |
@@ -1,78 +1,78 b'' | |||||
1 | """The main API for the v2 notebook format. |
|
1 | """The main API for the v2 notebook format. | |
2 |
|
2 | |||
3 | Authors: |
|
3 | Authors: | |
4 |
|
4 | |||
5 | * Brian Granger |
|
5 | * Brian Granger | |
6 | """ |
|
6 | """ | |
7 |
|
7 | |||
8 | #----------------------------------------------------------------------------- |
|
8 | #----------------------------------------------------------------------------- | |
9 | # Copyright (C) 2008-2011 The IPython Development Team |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
10 | # |
|
10 | # | |
11 | # Distributed under the terms of the BSD License. The full license is in |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
12 | # the file COPYING, distributed as part of this software. |
|
12 | # the file COPYING, distributed as part of this software. | |
13 | #----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
14 |
|
14 | |||
15 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
16 | # Imports |
|
16 | # Imports | |
17 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
18 |
|
18 | |||
19 | from .nbbase import ( |
|
19 | from .nbbase import ( | |
20 | NotebookNode, |
|
20 | NotebookNode, | |
21 | new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, |
|
21 | new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, | |
22 | new_metadata, new_author |
|
22 | new_metadata, new_author | |
23 | ) |
|
23 | ) | |
24 |
|
24 | |||
25 | from .nbjson import reads as reads_json, writes as writes_json |
|
25 | from .nbjson import reads as reads_json, writes as writes_json | |
26 | from .nbjson import reads as read_json, writes as write_json |
|
26 | from .nbjson import reads as read_json, writes as write_json | |
27 | from .nbjson import to_notebook as to_notebook_json |
|
27 | from .nbjson import to_notebook as to_notebook_json | |
28 |
|
28 | |||
29 | from .nbxml import reads as reads_xml |
|
29 | from .nbxml import reads as reads_xml | |
30 | from .nbxml import reads as read_xml |
|
30 | from .nbxml import reads as read_xml | |
31 | from .nbxml import to_notebook as to_notebook_xml |
|
31 | from .nbxml import to_notebook as to_notebook_xml | |
32 |
|
32 | |||
33 | from .nbpy import reads as reads_py, writes as writes_py |
|
33 | from .nbpy import reads as reads_py, writes as writes_py | |
34 | from .nbpy import reads as read_py, writes as write_py |
|
34 | from .nbpy import reads as read_py, writes as write_py | |
35 | from .nbpy import to_notebook as to_notebook_py |
|
35 | from .nbpy import to_notebook as to_notebook_py | |
36 |
|
36 | |||
37 |
from .convert import |
|
37 | from .convert import downgrade, upgrade | |
38 |
|
38 | |||
39 | #----------------------------------------------------------------------------- |
|
39 | #----------------------------------------------------------------------------- | |
40 | # Code |
|
40 | # Code | |
41 | #----------------------------------------------------------------------------- |
|
41 | #----------------------------------------------------------------------------- | |
42 |
|
42 | |||
43 | def parse_filename(fname): |
|
43 | def parse_filename(fname): | |
44 | """Parse a notebook filename. |
|
44 | """Parse a notebook filename. | |
45 |
|
45 | |||
46 | This function takes a notebook filename and returns the notebook |
|
46 | This function takes a notebook filename and returns the notebook | |
47 | format (json/py) and the notebook name. This logic can be |
|
47 | format (json/py) and the notebook name. This logic can be | |
48 | summarized as follows: |
|
48 | summarized as follows: | |
49 |
|
49 | |||
50 | * notebook.ipynb -> (notebook.ipynb, notebook, json) |
|
50 | * notebook.ipynb -> (notebook.ipynb, notebook, json) | |
51 | * notebook.json -> (notebook.json, notebook, json) |
|
51 | * notebook.json -> (notebook.json, notebook, json) | |
52 | * notebook.py -> (notebook.py, notebook, py) |
|
52 | * notebook.py -> (notebook.py, notebook, py) | |
53 | * notebook -> (notebook.ipynb, notebook, json) |
|
53 | * notebook -> (notebook.ipynb, notebook, json) | |
54 |
|
54 | |||
55 | Parameters |
|
55 | Parameters | |
56 | ---------- |
|
56 | ---------- | |
57 | fname : unicode |
|
57 | fname : unicode | |
58 | The notebook filename. The filename can use a specific filename |
|
58 | The notebook filename. The filename can use a specific filename | |
59 | extention (.ipynb, .json, .py) or none, in which case .ipynb will |
|
59 | extention (.ipynb, .json, .py) or none, in which case .ipynb will | |
60 | be assumed. |
|
60 | be assumed. | |
61 |
|
61 | |||
62 | Returns |
|
62 | Returns | |
63 | ------- |
|
63 | ------- | |
64 | (fname, name, format) : (unicode, unicode, unicode) |
|
64 | (fname, name, format) : (unicode, unicode, unicode) | |
65 | The filename, notebook name and format. |
|
65 | The filename, notebook name and format. | |
66 | """ |
|
66 | """ | |
67 | if fname.endswith(u'.ipynb'): |
|
67 | if fname.endswith(u'.ipynb'): | |
68 | format = u'json' |
|
68 | format = u'json' | |
69 | elif fname.endswith(u'.json'): |
|
69 | elif fname.endswith(u'.json'): | |
70 | format = u'json' |
|
70 | format = u'json' | |
71 | elif fname.endswith(u'.py'): |
|
71 | elif fname.endswith(u'.py'): | |
72 | format = u'py' |
|
72 | format = u'py' | |
73 | else: |
|
73 | else: | |
74 | fname = fname + u'.ipynb' |
|
74 | fname = fname + u'.ipynb' | |
75 | format = u'json' |
|
75 | format = u'json' | |
76 | name = fname.split('.')[0] |
|
76 | name = fname.split('.')[0] | |
77 | return fname, name, format |
|
77 | return fname, name, format | |
78 |
|
78 |
@@ -1,50 +1,61 b'' | |||||
1 | """Code for converting notebooks to and from the v2 format. |
|
1 | """Code for converting notebooks to and from the v2 format. | |
2 |
|
2 | |||
3 | Authors: |
|
3 | Authors: | |
4 |
|
4 | |||
5 | * Brian Granger |
|
5 | * Brian Granger | |
|
6 | * Jonathan Frederic | |||
6 | """ |
|
7 | """ | |
7 |
|
8 | |||
8 | #----------------------------------------------------------------------------- |
|
9 | #----------------------------------------------------------------------------- | |
9 | # Copyright (C) 2008-2011 The IPython Development Team |
|
10 | # Copyright (C) 2008-2011 The IPython Development Team | |
10 | # |
|
11 | # | |
11 | # Distributed under the terms of the BSD License. The full license is in |
|
12 | # Distributed under the terms of the BSD License. The full license is in | |
12 | # the file COPYING, distributed as part of this software. |
|
13 | # the file COPYING, distributed as part of this software. | |
13 | #----------------------------------------------------------------------------- |
|
14 | #----------------------------------------------------------------------------- | |
14 |
|
15 | |||
15 | #----------------------------------------------------------------------------- |
|
16 | #----------------------------------------------------------------------------- | |
16 | # Imports |
|
17 | # Imports | |
17 | #----------------------------------------------------------------------------- |
|
18 | #----------------------------------------------------------------------------- | |
18 |
|
19 | |||
19 | from .nbbase import ( |
|
20 | from .nbbase import ( | |
20 | new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output |
|
21 | new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output | |
21 | ) |
|
22 | ) | |
22 |
|
23 | |||
23 | #----------------------------------------------------------------------------- |
|
24 | #----------------------------------------------------------------------------- | |
24 | # Code |
|
25 | # Code | |
25 | #----------------------------------------------------------------------------- |
|
26 | #----------------------------------------------------------------------------- | |
26 |
|
27 | |||
27 | def convert_to_this_nbformat(nb, orig_version=1): |
|
28 | def upgrade(nb, from_version=1): | |
28 | """Convert a notebook to the v2 format. |
|
29 | """Convert a notebook to the v2 format. | |
29 |
|
30 | |||
30 | Parameters |
|
31 | Parameters | |
31 | ---------- |
|
32 | ---------- | |
32 | nb : NotebookNode |
|
33 | nb : NotebookNode | |
33 | The Python representation of the notebook to convert. |
|
34 | The Python representation of the notebook to convert. | |
34 | orig_version : int |
|
35 | orig_version : int | |
35 | The original version of the notebook to convert. |
|
36 | The original version of the notebook to convert. | |
36 | """ |
|
37 | """ | |
37 | if orig_version == 1: |
|
38 | if orig_version == 1: | |
38 | newnb = new_notebook() |
|
39 | newnb = new_notebook() | |
39 | ws = new_worksheet() |
|
40 | ws = new_worksheet() | |
40 | for cell in nb.cells: |
|
41 | for cell in nb.cells: | |
41 | if cell.cell_type == u'code': |
|
42 | if cell.cell_type == u'code': | |
42 | newcell = new_code_cell(input=cell.get('code'),prompt_number=cell.get('prompt_number')) |
|
43 | newcell = new_code_cell(input=cell.get('code'),prompt_number=cell.get('prompt_number')) | |
43 | elif cell.cell_type == u'text': |
|
44 | elif cell.cell_type == u'text': | |
44 | newcell = new_text_cell(u'markdown',source=cell.get('text')) |
|
45 | newcell = new_text_cell(u'markdown',source=cell.get('text')) | |
45 | ws.cells.append(newcell) |
|
46 | ws.cells.append(newcell) | |
46 | newnb.worksheets.append(ws) |
|
47 | newnb.worksheets.append(ws) | |
47 | return newnb |
|
48 | return newnb | |
48 | else: |
|
49 | else: | |
49 | raise ValueError('Cannot convert a notebook from v%s to v2' % orig_version) |
|
50 | raise ValueError('Cannot convert a notebook from v%s to v2' % orig_version) | |
50 |
|
51 | |||
|
52 | ||||
|
53 | def downgrade(nb): | |||
|
54 | """Convert a v2 notebook to v1. | |||
|
55 | ||||
|
56 | Parameters | |||
|
57 | ---------- | |||
|
58 | nb : NotebookNode | |||
|
59 | The Python representation of the notebook to convert. | |||
|
60 | """ | |||
|
61 | raise Exception("Downgrade from notebook v2 to v1 is not supported.") |
@@ -1,74 +1,74 b'' | |||||
1 | """The main API for the v3 notebook format. |
|
1 | """The main API for the v3 notebook format. | |
2 |
|
2 | |||
3 | Authors: |
|
3 | Authors: | |
4 |
|
4 | |||
5 | * Brian Granger |
|
5 | * Brian Granger | |
6 | """ |
|
6 | """ | |
7 |
|
7 | |||
8 | #----------------------------------------------------------------------------- |
|
8 | #----------------------------------------------------------------------------- | |
9 | # Copyright (C) 2008-2011 The IPython Development Team |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
10 | # |
|
10 | # | |
11 | # Distributed under the terms of the BSD License. The full license is in |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
12 | # the file COPYING, distributed as part of this software. |
|
12 | # the file COPYING, distributed as part of this software. | |
13 | #----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
14 |
|
14 | |||
15 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
16 | # Imports |
|
16 | # Imports | |
17 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
18 |
|
18 | |||
19 | from .nbbase import ( |
|
19 | from .nbbase import ( | |
20 | NotebookNode, |
|
20 | NotebookNode, | |
21 | new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, |
|
21 | new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet, | |
22 | new_metadata, new_author, new_heading_cell, nbformat, nbformat_minor |
|
22 | new_metadata, new_author, new_heading_cell, nbformat, nbformat_minor | |
23 | ) |
|
23 | ) | |
24 |
|
24 | |||
25 | from .nbjson import reads as reads_json, writes as writes_json |
|
25 | from .nbjson import reads as reads_json, writes as writes_json | |
26 | from .nbjson import reads as read_json, writes as write_json |
|
26 | from .nbjson import reads as read_json, writes as write_json | |
27 | from .nbjson import to_notebook as to_notebook_json |
|
27 | from .nbjson import to_notebook as to_notebook_json | |
28 |
|
28 | |||
29 | from .nbpy import reads as reads_py, writes as writes_py |
|
29 | from .nbpy import reads as reads_py, writes as writes_py | |
30 | from .nbpy import reads as read_py, writes as write_py |
|
30 | from .nbpy import reads as read_py, writes as write_py | |
31 | from .nbpy import to_notebook as to_notebook_py |
|
31 | from .nbpy import to_notebook as to_notebook_py | |
32 |
|
32 | |||
33 |
from .convert import |
|
33 | from .convert import downgrade, upgrade | |
34 |
|
34 | |||
35 | #----------------------------------------------------------------------------- |
|
35 | #----------------------------------------------------------------------------- | |
36 | # Code |
|
36 | # Code | |
37 | #----------------------------------------------------------------------------- |
|
37 | #----------------------------------------------------------------------------- | |
38 |
|
38 | |||
39 | def parse_filename(fname): |
|
39 | def parse_filename(fname): | |
40 | """Parse a notebook filename. |
|
40 | """Parse a notebook filename. | |
41 |
|
41 | |||
42 | This function takes a notebook filename and returns the notebook |
|
42 | This function takes a notebook filename and returns the notebook | |
43 | format (json/py) and the notebook name. This logic can be |
|
43 | format (json/py) and the notebook name. This logic can be | |
44 | summarized as follows: |
|
44 | summarized as follows: | |
45 |
|
45 | |||
46 | * notebook.ipynb -> (notebook.ipynb, notebook, json) |
|
46 | * notebook.ipynb -> (notebook.ipynb, notebook, json) | |
47 | * notebook.json -> (notebook.json, notebook, json) |
|
47 | * notebook.json -> (notebook.json, notebook, json) | |
48 | * notebook.py -> (notebook.py, notebook, py) |
|
48 | * notebook.py -> (notebook.py, notebook, py) | |
49 | * notebook -> (notebook.ipynb, notebook, json) |
|
49 | * notebook -> (notebook.ipynb, notebook, json) | |
50 |
|
50 | |||
51 | Parameters |
|
51 | Parameters | |
52 | ---------- |
|
52 | ---------- | |
53 | fname : unicode |
|
53 | fname : unicode | |
54 | The notebook filename. The filename can use a specific filename |
|
54 | The notebook filename. The filename can use a specific filename | |
55 | extention (.ipynb, .json, .py) or none, in which case .ipynb will |
|
55 | extention (.ipynb, .json, .py) or none, in which case .ipynb will | |
56 | be assumed. |
|
56 | be assumed. | |
57 |
|
57 | |||
58 | Returns |
|
58 | Returns | |
59 | ------- |
|
59 | ------- | |
60 | (fname, name, format) : (unicode, unicode, unicode) |
|
60 | (fname, name, format) : (unicode, unicode, unicode) | |
61 | The filename, notebook name and format. |
|
61 | The filename, notebook name and format. | |
62 | """ |
|
62 | """ | |
63 | if fname.endswith(u'.ipynb'): |
|
63 | if fname.endswith(u'.ipynb'): | |
64 | format = u'json' |
|
64 | format = u'json' | |
65 | elif fname.endswith(u'.json'): |
|
65 | elif fname.endswith(u'.json'): | |
66 | format = u'json' |
|
66 | format = u'json' | |
67 | elif fname.endswith(u'.py'): |
|
67 | elif fname.endswith(u'.py'): | |
68 | format = u'py' |
|
68 | format = u'py' | |
69 | else: |
|
69 | else: | |
70 | fname = fname + u'.ipynb' |
|
70 | fname = fname + u'.ipynb' | |
71 | format = u'json' |
|
71 | format = u'json' | |
72 | name = fname.split('.')[0] |
|
72 | name = fname.split('.')[0] | |
73 | return fname, name, format |
|
73 | return fname, name, format | |
74 |
|
74 |
@@ -1,59 +1,90 b'' | |||||
1 | """Code for converting notebooks to and from the v2 format. |
|
1 | """Code for converting notebooks to and from the v2 format. | |
2 |
|
2 | |||
3 | Authors: |
|
3 | Authors: | |
4 |
|
4 | |||
5 | * Brian Granger |
|
5 | * Brian Granger | |
|
6 | * Min RK | |||
|
7 | * Jonathan Frederic | |||
6 | """ |
|
8 | """ | |
7 |
|
9 | |||
8 | #----------------------------------------------------------------------------- |
|
10 | #----------------------------------------------------------------------------- | |
9 | # Copyright (C) 2008-2011 The IPython Development Team |
|
11 | # Copyright (C) 2008-2011 The IPython Development Team | |
10 | # |
|
12 | # | |
11 | # Distributed under the terms of the BSD License. The full license is in |
|
13 | # Distributed under the terms of the BSD License. The full license is in | |
12 | # the file COPYING, distributed as part of this software. |
|
14 | # the file COPYING, distributed as part of this software. | |
13 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
14 |
|
16 | |||
15 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
16 | # Imports |
|
18 | # Imports | |
17 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
18 |
|
20 | |||
19 | from .nbbase import ( |
|
21 | from .nbbase import ( | |
20 | new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output, |
|
22 | new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output, | |
21 | nbformat, nbformat_minor |
|
23 | nbformat, nbformat_minor | |
22 | ) |
|
24 | ) | |
23 |
|
25 | |||
24 | from IPython.nbformat import v2 |
|
26 | from IPython.nbformat import v2 | |
25 |
|
27 | |||
26 | #----------------------------------------------------------------------------- |
|
28 | #----------------------------------------------------------------------------- | |
27 | # Code |
|
29 | # Code | |
28 | #----------------------------------------------------------------------------- |
|
30 | #----------------------------------------------------------------------------- | |
29 |
|
31 | |||
30 |
def |
|
32 | def upgrade(nb, from_version=2, from_minor=0): | |
31 |
"""Convert a notebook to |
|
33 | """Convert a notebook to v3. | |
32 |
|
34 | |||
33 | Parameters |
|
35 | Parameters | |
34 | ---------- |
|
36 | ---------- | |
35 | nb : NotebookNode |
|
37 | nb : NotebookNode | |
36 | The Python representation of the notebook to convert. |
|
38 | The Python representation of the notebook to convert. | |
37 |
|
|
39 | from_version : int | |
38 | The original version of the notebook to convert. |
|
40 | The original version of the notebook to convert. | |
39 |
|
|
41 | from_minor : int | |
40 | The original minor version of the notebook to convert (only relevant for v >= 3). |
|
42 | The original minor version of the notebook to convert (only relevant for v >= 3). | |
41 | """ |
|
43 | """ | |
42 |
if |
|
44 | if from_version == 2: | |
43 | nb = v2.convert_to_this_nbformat(nb) |
|
|||
44 | orig_version = 2 |
|
|||
45 | if orig_version == 2: |
|
|||
46 | # Mark the original nbformat so consumers know it has been converted. |
|
45 | # Mark the original nbformat so consumers know it has been converted. | |
47 | nb.nbformat = nbformat |
|
46 | nb.nbformat = nbformat | |
48 | nb.nbformat_minor = nbformat_minor |
|
47 | nb.nbformat_minor = nbformat_minor | |
49 |
|
48 | |||
50 | nb.orig_nbformat = 2 |
|
49 | nb.orig_nbformat = 2 | |
51 | return nb |
|
50 | return nb | |
52 |
elif |
|
51 | elif from_version == 3: | |
53 |
if |
|
52 | if from_minor != nbformat_minor: | |
54 |
nb.orig_nbformat_minor = |
|
53 | nb.orig_nbformat_minor = from_minor | |
55 | nb.nbformat_minor = nbformat_minor |
|
54 | nb.nbformat_minor = nbformat_minor | |
56 | return nb |
|
55 | return nb | |
57 | else: |
|
56 | else: | |
58 |
raise ValueError('Cannot convert a notebook from v%s to v3' |
|
57 | raise ValueError('Cannot convert a notebook directly from v%s to v3. ' \ | |
|
58 | 'Try using the IPython.nbformat.convert module.' % from_version) | |||
|
59 | ||||
59 |
|
60 | |||
|
61 | def heading_to_md(cell): | |||
|
62 | """turn heading cell into corresponding markdown""" | |||
|
63 | cell.cell_type = "markdown" | |||
|
64 | level = cell.pop('level', 1) | |||
|
65 | cell.source = '#'*level + ' ' + cell.source | |||
|
66 | ||||
|
67 | ||||
|
68 | def raw_to_md(cell): | |||
|
69 | """let raw passthrough as markdown""" | |||
|
70 | cell.cell_type = "markdown" | |||
|
71 | ||||
|
72 | ||||
|
73 | def downgrade(nb): | |||
|
74 | """Convert a v3 notebook to v2. | |||
|
75 | ||||
|
76 | Parameters | |||
|
77 | ---------- | |||
|
78 | nb : NotebookNode | |||
|
79 | The Python representation of the notebook to convert. | |||
|
80 | """ | |||
|
81 | if nb.nbformat != 3: | |||
|
82 | return nb | |||
|
83 | nb.nbformat = 2 | |||
|
84 | for ws in nb.worksheets: | |||
|
85 | for cell in ws.cells: | |||
|
86 | if cell.cell_type == 'heading': | |||
|
87 | heading_to_md(cell) | |||
|
88 | elif cell.cell_type == 'raw': | |||
|
89 | raw_to_md(cell) | |||
|
90 | return nb No newline at end of file |
@@ -1,502 +1,503 b'' | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | """ |
|
2 | """ | |
3 | This module defines the things that are used in setup.py for building IPython |
|
3 | This module defines the things that are used in setup.py for building IPython | |
4 |
|
4 | |||
5 | This includes: |
|
5 | This includes: | |
6 |
|
6 | |||
7 | * The basic arguments to setup |
|
7 | * The basic arguments to setup | |
8 | * Functions for finding things like packages, package data, etc. |
|
8 | * Functions for finding things like packages, package data, etc. | |
9 | * A function for checking dependencies. |
|
9 | * A function for checking dependencies. | |
10 | """ |
|
10 | """ | |
11 | from __future__ import print_function |
|
11 | from __future__ import print_function | |
12 |
|
12 | |||
13 | #------------------------------------------------------------------------------- |
|
13 | #------------------------------------------------------------------------------- | |
14 | # Copyright (C) 2008 The IPython Development Team |
|
14 | # Copyright (C) 2008 The IPython Development Team | |
15 | # |
|
15 | # | |
16 | # Distributed under the terms of the BSD License. The full license is in |
|
16 | # Distributed under the terms of the BSD License. The full license is in | |
17 | # the file COPYING, distributed as part of this software. |
|
17 | # the file COPYING, distributed as part of this software. | |
18 | #------------------------------------------------------------------------------- |
|
18 | #------------------------------------------------------------------------------- | |
19 |
|
19 | |||
20 | #------------------------------------------------------------------------------- |
|
20 | #------------------------------------------------------------------------------- | |
21 | # Imports |
|
21 | # Imports | |
22 | #------------------------------------------------------------------------------- |
|
22 | #------------------------------------------------------------------------------- | |
23 | import os |
|
23 | import os | |
24 | import sys |
|
24 | import sys | |
25 |
|
25 | |||
26 | try: |
|
26 | try: | |
27 | from configparser import ConfigParser |
|
27 | from configparser import ConfigParser | |
28 | except: |
|
28 | except: | |
29 | from ConfigParser import ConfigParser |
|
29 | from ConfigParser import ConfigParser | |
30 | from distutils.command.build_py import build_py |
|
30 | from distutils.command.build_py import build_py | |
31 | from distutils.cmd import Command |
|
31 | from distutils.cmd import Command | |
32 | from glob import glob |
|
32 | from glob import glob | |
33 | from subprocess import call |
|
33 | from subprocess import call | |
34 |
|
34 | |||
35 | from setupext import install_data_ext |
|
35 | from setupext import install_data_ext | |
36 |
|
36 | |||
37 | #------------------------------------------------------------------------------- |
|
37 | #------------------------------------------------------------------------------- | |
38 | # Useful globals and utility functions |
|
38 | # Useful globals and utility functions | |
39 | #------------------------------------------------------------------------------- |
|
39 | #------------------------------------------------------------------------------- | |
40 |
|
40 | |||
41 | # A few handy globals |
|
41 | # A few handy globals | |
42 | isfile = os.path.isfile |
|
42 | isfile = os.path.isfile | |
43 | pjoin = os.path.join |
|
43 | pjoin = os.path.join | |
44 | repo_root = os.path.dirname(os.path.abspath(__file__)) |
|
44 | repo_root = os.path.dirname(os.path.abspath(__file__)) | |
45 |
|
45 | |||
46 | def oscmd(s): |
|
46 | def oscmd(s): | |
47 | print(">", s) |
|
47 | print(">", s) | |
48 | os.system(s) |
|
48 | os.system(s) | |
49 |
|
49 | |||
50 | # Py3 compatibility hacks, without assuming IPython itself is installed with |
|
50 | # Py3 compatibility hacks, without assuming IPython itself is installed with | |
51 | # the full py3compat machinery. |
|
51 | # the full py3compat machinery. | |
52 |
|
52 | |||
53 | try: |
|
53 | try: | |
54 | execfile |
|
54 | execfile | |
55 | except NameError: |
|
55 | except NameError: | |
56 | def execfile(fname, globs, locs=None): |
|
56 | def execfile(fname, globs, locs=None): | |
57 | locs = locs or globs |
|
57 | locs = locs or globs | |
58 | exec(compile(open(fname).read(), fname, "exec"), globs, locs) |
|
58 | exec(compile(open(fname).read(), fname, "exec"), globs, locs) | |
59 |
|
59 | |||
60 | # A little utility we'll need below, since glob() does NOT allow you to do |
|
60 | # A little utility we'll need below, since glob() does NOT allow you to do | |
61 | # exclusion on multiple endings! |
|
61 | # exclusion on multiple endings! | |
62 | def file_doesnt_endwith(test,endings): |
|
62 | def file_doesnt_endwith(test,endings): | |
63 | """Return true if test is a file and its name does NOT end with any |
|
63 | """Return true if test is a file and its name does NOT end with any | |
64 | of the strings listed in endings.""" |
|
64 | of the strings listed in endings.""" | |
65 | if not isfile(test): |
|
65 | if not isfile(test): | |
66 | return False |
|
66 | return False | |
67 | for e in endings: |
|
67 | for e in endings: | |
68 | if test.endswith(e): |
|
68 | if test.endswith(e): | |
69 | return False |
|
69 | return False | |
70 | return True |
|
70 | return True | |
71 |
|
71 | |||
72 | #--------------------------------------------------------------------------- |
|
72 | #--------------------------------------------------------------------------- | |
73 | # Basic project information |
|
73 | # Basic project information | |
74 | #--------------------------------------------------------------------------- |
|
74 | #--------------------------------------------------------------------------- | |
75 |
|
75 | |||
76 | # release.py contains version, authors, license, url, keywords, etc. |
|
76 | # release.py contains version, authors, license, url, keywords, etc. | |
77 | execfile(pjoin(repo_root, 'IPython','core','release.py'), globals()) |
|
77 | execfile(pjoin(repo_root, 'IPython','core','release.py'), globals()) | |
78 |
|
78 | |||
79 | # Create a dict with the basic information |
|
79 | # Create a dict with the basic information | |
80 | # This dict is eventually passed to setup after additional keys are added. |
|
80 | # This dict is eventually passed to setup after additional keys are added. | |
81 | setup_args = dict( |
|
81 | setup_args = dict( | |
82 | name = name, |
|
82 | name = name, | |
83 | version = version, |
|
83 | version = version, | |
84 | description = description, |
|
84 | description = description, | |
85 | long_description = long_description, |
|
85 | long_description = long_description, | |
86 | author = author, |
|
86 | author = author, | |
87 | author_email = author_email, |
|
87 | author_email = author_email, | |
88 | url = url, |
|
88 | url = url, | |
89 | download_url = download_url, |
|
89 | download_url = download_url, | |
90 | license = license, |
|
90 | license = license, | |
91 | platforms = platforms, |
|
91 | platforms = platforms, | |
92 | keywords = keywords, |
|
92 | keywords = keywords, | |
93 | classifiers = classifiers, |
|
93 | classifiers = classifiers, | |
94 | cmdclass = {'install_data': install_data_ext}, |
|
94 | cmdclass = {'install_data': install_data_ext}, | |
95 | ) |
|
95 | ) | |
96 |
|
96 | |||
97 |
|
97 | |||
98 | #--------------------------------------------------------------------------- |
|
98 | #--------------------------------------------------------------------------- | |
99 | # Find packages |
|
99 | # Find packages | |
100 | #--------------------------------------------------------------------------- |
|
100 | #--------------------------------------------------------------------------- | |
101 |
|
101 | |||
102 | def find_packages(): |
|
102 | def find_packages(): | |
103 | """ |
|
103 | """ | |
104 | Find all of IPython's packages. |
|
104 | Find all of IPython's packages. | |
105 | """ |
|
105 | """ | |
106 | excludes = ['deathrow', 'quarantine'] |
|
106 | excludes = ['deathrow', 'quarantine'] | |
107 | packages = [] |
|
107 | packages = [] | |
108 | for dir,subdirs,files in os.walk('IPython'): |
|
108 | for dir,subdirs,files in os.walk('IPython'): | |
109 | package = dir.replace(os.path.sep, '.') |
|
109 | package = dir.replace(os.path.sep, '.') | |
110 | if any(package.startswith('IPython.'+exc) for exc in excludes): |
|
110 | if any(package.startswith('IPython.'+exc) for exc in excludes): | |
111 | # package is to be excluded (e.g. deathrow) |
|
111 | # package is to be excluded (e.g. deathrow) | |
112 | continue |
|
112 | continue | |
113 | if '__init__.py' not in files: |
|
113 | if '__init__.py' not in files: | |
114 | # not a package |
|
114 | # not a package | |
115 | continue |
|
115 | continue | |
116 | packages.append(package) |
|
116 | packages.append(package) | |
117 | return packages |
|
117 | return packages | |
118 |
|
118 | |||
119 | #--------------------------------------------------------------------------- |
|
119 | #--------------------------------------------------------------------------- | |
120 | # Find package data |
|
120 | # Find package data | |
121 | #--------------------------------------------------------------------------- |
|
121 | #--------------------------------------------------------------------------- | |
122 |
|
122 | |||
123 | def find_package_data(): |
|
123 | def find_package_data(): | |
124 | """ |
|
124 | """ | |
125 | Find IPython's package_data. |
|
125 | Find IPython's package_data. | |
126 | """ |
|
126 | """ | |
127 | # This is not enough for these things to appear in an sdist. |
|
127 | # This is not enough for these things to appear in an sdist. | |
128 | # We need to muck with the MANIFEST to get this to work |
|
128 | # We need to muck with the MANIFEST to get this to work | |
129 |
|
129 | |||
130 | # exclude static things that we don't ship (e.g. mathjax) |
|
130 | # exclude static things that we don't ship (e.g. mathjax) | |
131 | excludes = ['mathjax'] |
|
131 | excludes = ['mathjax'] | |
132 |
|
132 | |||
133 | # add 'static/' prefix to exclusions, and tuplify for use in startswith |
|
133 | # add 'static/' prefix to exclusions, and tuplify for use in startswith | |
134 | excludes = tuple([os.path.join('static', ex) for ex in excludes]) |
|
134 | excludes = tuple([os.path.join('static', ex) for ex in excludes]) | |
135 |
|
135 | |||
136 | # walk notebook resources: |
|
136 | # walk notebook resources: | |
137 | cwd = os.getcwd() |
|
137 | cwd = os.getcwd() | |
138 | os.chdir(os.path.join('IPython', 'html')) |
|
138 | os.chdir(os.path.join('IPython', 'html')) | |
139 | static_walk = list(os.walk('static')) |
|
139 | static_walk = list(os.walk('static')) | |
140 | static_data = [] |
|
140 | static_data = [] | |
141 | for parent, dirs, files in static_walk: |
|
141 | for parent, dirs, files in static_walk: | |
142 | if parent.startswith(excludes): |
|
142 | if parent.startswith(excludes): | |
143 | continue |
|
143 | continue | |
144 | for f in files: |
|
144 | for f in files: | |
145 | static_data.append(os.path.join(parent, f)) |
|
145 | static_data.append(os.path.join(parent, f)) | |
146 |
|
146 | |||
147 | os.chdir(os.path.join('tests',)) |
|
147 | os.chdir(os.path.join('tests',)) | |
148 | js_tests = glob('casperjs/*.*') + glob('casperjs/*/*') |
|
148 | js_tests = glob('casperjs/*.*') + glob('casperjs/*/*') | |
149 | os.chdir(cwd) |
|
149 | os.chdir(cwd) | |
150 |
|
150 | |||
151 | package_data = { |
|
151 | package_data = { | |
152 | 'IPython.config.profile' : ['README*', '*/*.py'], |
|
152 | 'IPython.config.profile' : ['README*', '*/*.py'], | |
153 | 'IPython.core.tests' : ['*.png', '*.jpg'], |
|
153 | 'IPython.core.tests' : ['*.png', '*.jpg'], | |
154 | 'IPython.testing' : ['*.txt'], |
|
154 | 'IPython.testing' : ['*.txt'], | |
155 | 'IPython.testing.plugin' : ['*.txt'], |
|
155 | 'IPython.testing.plugin' : ['*.txt'], | |
156 | 'IPython.html' : ['templates/*'] + static_data, |
|
156 | 'IPython.html' : ['templates/*'] + static_data, | |
157 | 'IPython.html.tests' : js_tests, |
|
157 | 'IPython.html.tests' : js_tests, | |
158 | 'IPython.qt.console' : ['resources/icon/*.svg'], |
|
158 | 'IPython.qt.console' : ['resources/icon/*.svg'], | |
159 | 'IPython.nbconvert' : ['templates/*.tpl', 'templates/latex/*.tplx', |
|
159 | 'IPython.nbconvert' : ['templates/*.tpl', 'templates/latex/*.tplx', | |
160 | 'templates/latex/skeleton/*.tplx', 'templates/skeleton/*', |
|
160 | 'templates/latex/skeleton/*.tplx', 'templates/skeleton/*', | |
161 | 'templates/reveal_internals/*.tpl', 'tests/files/*.*', |
|
161 | 'templates/reveal_internals/*.tpl', 'tests/files/*.*', | |
162 | 'exporters/tests/files/*.*'] |
|
162 | 'exporters/tests/files/*.*'], | |
|
163 | 'IPython.nbformat' : ['tests/*.ipynb'] | |||
163 | } |
|
164 | } | |
164 | return package_data |
|
165 | return package_data | |
165 |
|
166 | |||
166 |
|
167 | |||
167 | #--------------------------------------------------------------------------- |
|
168 | #--------------------------------------------------------------------------- | |
168 | # Find data files |
|
169 | # Find data files | |
169 | #--------------------------------------------------------------------------- |
|
170 | #--------------------------------------------------------------------------- | |
170 |
|
171 | |||
171 | def make_dir_struct(tag,base,out_base): |
|
172 | def make_dir_struct(tag,base,out_base): | |
172 | """Make the directory structure of all files below a starting dir. |
|
173 | """Make the directory structure of all files below a starting dir. | |
173 |
|
174 | |||
174 | This is just a convenience routine to help build a nested directory |
|
175 | This is just a convenience routine to help build a nested directory | |
175 | hierarchy because distutils is too stupid to do this by itself. |
|
176 | hierarchy because distutils is too stupid to do this by itself. | |
176 |
|
177 | |||
177 | XXX - this needs a proper docstring! |
|
178 | XXX - this needs a proper docstring! | |
178 | """ |
|
179 | """ | |
179 |
|
180 | |||
180 | # we'll use these a lot below |
|
181 | # we'll use these a lot below | |
181 | lbase = len(base) |
|
182 | lbase = len(base) | |
182 | pathsep = os.path.sep |
|
183 | pathsep = os.path.sep | |
183 | lpathsep = len(pathsep) |
|
184 | lpathsep = len(pathsep) | |
184 |
|
185 | |||
185 | out = [] |
|
186 | out = [] | |
186 | for (dirpath,dirnames,filenames) in os.walk(base): |
|
187 | for (dirpath,dirnames,filenames) in os.walk(base): | |
187 | # we need to strip out the dirpath from the base to map it to the |
|
188 | # we need to strip out the dirpath from the base to map it to the | |
188 | # output (installation) path. This requires possibly stripping the |
|
189 | # output (installation) path. This requires possibly stripping the | |
189 | # path separator, because otherwise pjoin will not work correctly |
|
190 | # path separator, because otherwise pjoin will not work correctly | |
190 | # (pjoin('foo/','/bar') returns '/bar'). |
|
191 | # (pjoin('foo/','/bar') returns '/bar'). | |
191 |
|
192 | |||
192 | dp_eff = dirpath[lbase:] |
|
193 | dp_eff = dirpath[lbase:] | |
193 | if dp_eff.startswith(pathsep): |
|
194 | if dp_eff.startswith(pathsep): | |
194 | dp_eff = dp_eff[lpathsep:] |
|
195 | dp_eff = dp_eff[lpathsep:] | |
195 | # The output path must be anchored at the out_base marker |
|
196 | # The output path must be anchored at the out_base marker | |
196 | out_path = pjoin(out_base,dp_eff) |
|
197 | out_path = pjoin(out_base,dp_eff) | |
197 | # Now we can generate the final filenames. Since os.walk only produces |
|
198 | # Now we can generate the final filenames. Since os.walk only produces | |
198 | # filenames, we must join back with the dirpath to get full valid file |
|
199 | # filenames, we must join back with the dirpath to get full valid file | |
199 | # paths: |
|
200 | # paths: | |
200 | pfiles = [pjoin(dirpath,f) for f in filenames] |
|
201 | pfiles = [pjoin(dirpath,f) for f in filenames] | |
201 | # Finally, generate the entry we need, which is a pari of (output |
|
202 | # Finally, generate the entry we need, which is a pari of (output | |
202 | # path, files) for use as a data_files parameter in install_data. |
|
203 | # path, files) for use as a data_files parameter in install_data. | |
203 | out.append((out_path, pfiles)) |
|
204 | out.append((out_path, pfiles)) | |
204 |
|
205 | |||
205 | return out |
|
206 | return out | |
206 |
|
207 | |||
207 |
|
208 | |||
208 | def find_data_files(): |
|
209 | def find_data_files(): | |
209 | """ |
|
210 | """ | |
210 | Find IPython's data_files. |
|
211 | Find IPython's data_files. | |
211 |
|
212 | |||
212 | Most of these are docs. |
|
213 | Most of these are docs. | |
213 | """ |
|
214 | """ | |
214 |
|
215 | |||
215 | docdirbase = pjoin('share', 'doc', 'ipython') |
|
216 | docdirbase = pjoin('share', 'doc', 'ipython') | |
216 | manpagebase = pjoin('share', 'man', 'man1') |
|
217 | manpagebase = pjoin('share', 'man', 'man1') | |
217 |
|
218 | |||
218 | # Simple file lists can be made by hand |
|
219 | # Simple file lists can be made by hand | |
219 | manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)] |
|
220 | manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)] | |
220 | if not manpages: |
|
221 | if not manpages: | |
221 | # When running from a source tree, the manpages aren't gzipped |
|
222 | # When running from a source tree, the manpages aren't gzipped | |
222 | manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)] |
|
223 | manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)] | |
223 |
|
224 | |||
224 | igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)] |
|
225 | igridhelpfiles = [f for f in glob(pjoin('IPython','extensions','igrid_help.*')) if isfile(f)] | |
225 |
|
226 | |||
226 | # For nested structures, use the utility above |
|
227 | # For nested structures, use the utility above | |
227 | example_files = make_dir_struct( |
|
228 | example_files = make_dir_struct( | |
228 | 'data', |
|
229 | 'data', | |
229 | pjoin('docs','examples'), |
|
230 | pjoin('docs','examples'), | |
230 | pjoin(docdirbase,'examples') |
|
231 | pjoin(docdirbase,'examples') | |
231 | ) |
|
232 | ) | |
232 | manual_files = make_dir_struct( |
|
233 | manual_files = make_dir_struct( | |
233 | 'data', |
|
234 | 'data', | |
234 | pjoin('docs','html'), |
|
235 | pjoin('docs','html'), | |
235 | pjoin(docdirbase,'manual') |
|
236 | pjoin(docdirbase,'manual') | |
236 | ) |
|
237 | ) | |
237 |
|
238 | |||
238 | # And assemble the entire output list |
|
239 | # And assemble the entire output list | |
239 | data_files = [ (manpagebase, manpages), |
|
240 | data_files = [ (manpagebase, manpages), | |
240 | (pjoin(docdirbase, 'extensions'), igridhelpfiles), |
|
241 | (pjoin(docdirbase, 'extensions'), igridhelpfiles), | |
241 | ] + manual_files + example_files |
|
242 | ] + manual_files + example_files | |
242 |
|
243 | |||
243 | return data_files |
|
244 | return data_files | |
244 |
|
245 | |||
245 |
|
246 | |||
246 | def make_man_update_target(manpage): |
|
247 | def make_man_update_target(manpage): | |
247 | """Return a target_update-compliant tuple for the given manpage. |
|
248 | """Return a target_update-compliant tuple for the given manpage. | |
248 |
|
249 | |||
249 | Parameters |
|
250 | Parameters | |
250 | ---------- |
|
251 | ---------- | |
251 | manpage : string |
|
252 | manpage : string | |
252 | Name of the manpage, must include the section number (trailing number). |
|
253 | Name of the manpage, must include the section number (trailing number). | |
253 |
|
254 | |||
254 | Example |
|
255 | Example | |
255 | ------- |
|
256 | ------- | |
256 |
|
257 | |||
257 | >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE |
|
258 | >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE | |
258 | ('docs/man/ipython.1.gz', |
|
259 | ('docs/man/ipython.1.gz', | |
259 | ['docs/man/ipython.1'], |
|
260 | ['docs/man/ipython.1'], | |
260 | 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz') |
|
261 | 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz') | |
261 | """ |
|
262 | """ | |
262 | man_dir = pjoin('docs', 'man') |
|
263 | man_dir = pjoin('docs', 'man') | |
263 | manpage_gz = manpage + '.gz' |
|
264 | manpage_gz = manpage + '.gz' | |
264 | manpath = pjoin(man_dir, manpage) |
|
265 | manpath = pjoin(man_dir, manpage) | |
265 | manpath_gz = pjoin(man_dir, manpage_gz) |
|
266 | manpath_gz = pjoin(man_dir, manpage_gz) | |
266 | gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" % |
|
267 | gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" % | |
267 | locals() ) |
|
268 | locals() ) | |
268 | return (manpath_gz, [manpath], gz_cmd) |
|
269 | return (manpath_gz, [manpath], gz_cmd) | |
269 |
|
270 | |||
270 | # The two functions below are copied from IPython.utils.path, so we don't need |
|
271 | # The two functions below are copied from IPython.utils.path, so we don't need | |
271 | # to import IPython during setup, which fails on Python 3. |
|
272 | # to import IPython during setup, which fails on Python 3. | |
272 |
|
273 | |||
273 | def target_outdated(target,deps): |
|
274 | def target_outdated(target,deps): | |
274 | """Determine whether a target is out of date. |
|
275 | """Determine whether a target is out of date. | |
275 |
|
276 | |||
276 | target_outdated(target,deps) -> 1/0 |
|
277 | target_outdated(target,deps) -> 1/0 | |
277 |
|
278 | |||
278 | deps: list of filenames which MUST exist. |
|
279 | deps: list of filenames which MUST exist. | |
279 | target: single filename which may or may not exist. |
|
280 | target: single filename which may or may not exist. | |
280 |
|
281 | |||
281 | If target doesn't exist or is older than any file listed in deps, return |
|
282 | If target doesn't exist or is older than any file listed in deps, return | |
282 | true, otherwise return false. |
|
283 | true, otherwise return false. | |
283 | """ |
|
284 | """ | |
284 | try: |
|
285 | try: | |
285 | target_time = os.path.getmtime(target) |
|
286 | target_time = os.path.getmtime(target) | |
286 | except os.error: |
|
287 | except os.error: | |
287 | return 1 |
|
288 | return 1 | |
288 | for dep in deps: |
|
289 | for dep in deps: | |
289 | dep_time = os.path.getmtime(dep) |
|
290 | dep_time = os.path.getmtime(dep) | |
290 | if dep_time > target_time: |
|
291 | if dep_time > target_time: | |
291 | #print "For target",target,"Dep failed:",dep # dbg |
|
292 | #print "For target",target,"Dep failed:",dep # dbg | |
292 | #print "times (dep,tar):",dep_time,target_time # dbg |
|
293 | #print "times (dep,tar):",dep_time,target_time # dbg | |
293 | return 1 |
|
294 | return 1 | |
294 | return 0 |
|
295 | return 0 | |
295 |
|
296 | |||
296 |
|
297 | |||
297 | def target_update(target,deps,cmd): |
|
298 | def target_update(target,deps,cmd): | |
298 | """Update a target with a given command given a list of dependencies. |
|
299 | """Update a target with a given command given a list of dependencies. | |
299 |
|
300 | |||
300 | target_update(target,deps,cmd) -> runs cmd if target is outdated. |
|
301 | target_update(target,deps,cmd) -> runs cmd if target is outdated. | |
301 |
|
302 | |||
302 | This is just a wrapper around target_outdated() which calls the given |
|
303 | This is just a wrapper around target_outdated() which calls the given | |
303 | command if target is outdated.""" |
|
304 | command if target is outdated.""" | |
304 |
|
305 | |||
305 | if target_outdated(target,deps): |
|
306 | if target_outdated(target,deps): | |
306 | os.system(cmd) |
|
307 | os.system(cmd) | |
307 |
|
308 | |||
308 | #--------------------------------------------------------------------------- |
|
309 | #--------------------------------------------------------------------------- | |
309 | # Find scripts |
|
310 | # Find scripts | |
310 | #--------------------------------------------------------------------------- |
|
311 | #--------------------------------------------------------------------------- | |
311 |
|
312 | |||
312 | def find_scripts(entry_points=False, suffix=''): |
|
313 | def find_scripts(entry_points=False, suffix=''): | |
313 | """Find IPython's scripts. |
|
314 | """Find IPython's scripts. | |
314 |
|
315 | |||
315 | if entry_points is True: |
|
316 | if entry_points is True: | |
316 | return setuptools entry_point-style definitions |
|
317 | return setuptools entry_point-style definitions | |
317 | else: |
|
318 | else: | |
318 | return file paths of plain scripts [default] |
|
319 | return file paths of plain scripts [default] | |
319 |
|
320 | |||
320 | suffix is appended to script names if entry_points is True, so that the |
|
321 | suffix is appended to script names if entry_points is True, so that the | |
321 | Python 3 scripts get named "ipython3" etc. |
|
322 | Python 3 scripts get named "ipython3" etc. | |
322 | """ |
|
323 | """ | |
323 | if entry_points: |
|
324 | if entry_points: | |
324 | console_scripts = [s % suffix for s in [ |
|
325 | console_scripts = [s % suffix for s in [ | |
325 | 'ipython%s = IPython:start_ipython', |
|
326 | 'ipython%s = IPython:start_ipython', | |
326 | 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance', |
|
327 | 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance', | |
327 | 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance', |
|
328 | 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance', | |
328 | 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance', |
|
329 | 'iplogger%s = IPython.parallel.apps.iploggerapp:launch_new_instance', | |
329 | 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance', |
|
330 | 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance', | |
330 | 'iptest%s = IPython.testing.iptestcontroller:main', |
|
331 | 'iptest%s = IPython.testing.iptestcontroller:main', | |
331 | 'irunner%s = IPython.lib.irunner:main', |
|
332 | 'irunner%s = IPython.lib.irunner:main', | |
332 | ]] |
|
333 | ]] | |
333 | gui_scripts = [] |
|
334 | gui_scripts = [] | |
334 | scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts) |
|
335 | scripts = dict(console_scripts=console_scripts, gui_scripts=gui_scripts) | |
335 | else: |
|
336 | else: | |
336 | parallel_scripts = pjoin('IPython','parallel','scripts') |
|
337 | parallel_scripts = pjoin('IPython','parallel','scripts') | |
337 | main_scripts = pjoin('IPython','scripts') |
|
338 | main_scripts = pjoin('IPython','scripts') | |
338 | scripts = [ |
|
339 | scripts = [ | |
339 | pjoin(parallel_scripts, 'ipengine'), |
|
340 | pjoin(parallel_scripts, 'ipengine'), | |
340 | pjoin(parallel_scripts, 'ipcontroller'), |
|
341 | pjoin(parallel_scripts, 'ipcontroller'), | |
341 | pjoin(parallel_scripts, 'ipcluster'), |
|
342 | pjoin(parallel_scripts, 'ipcluster'), | |
342 | pjoin(parallel_scripts, 'iplogger'), |
|
343 | pjoin(parallel_scripts, 'iplogger'), | |
343 | pjoin(main_scripts, 'ipython'), |
|
344 | pjoin(main_scripts, 'ipython'), | |
344 | pjoin(main_scripts, 'irunner'), |
|
345 | pjoin(main_scripts, 'irunner'), | |
345 | pjoin(main_scripts, 'iptest') |
|
346 | pjoin(main_scripts, 'iptest') | |
346 | ] |
|
347 | ] | |
347 | return scripts |
|
348 | return scripts | |
348 |
|
349 | |||
349 | #--------------------------------------------------------------------------- |
|
350 | #--------------------------------------------------------------------------- | |
350 | # Verify all dependencies |
|
351 | # Verify all dependencies | |
351 | #--------------------------------------------------------------------------- |
|
352 | #--------------------------------------------------------------------------- | |
352 |
|
353 | |||
353 | def check_for_dependencies(): |
|
354 | def check_for_dependencies(): | |
354 | """Check for IPython's dependencies. |
|
355 | """Check for IPython's dependencies. | |
355 |
|
356 | |||
356 | This function should NOT be called if running under setuptools! |
|
357 | This function should NOT be called if running under setuptools! | |
357 | """ |
|
358 | """ | |
358 | from setupext.setupext import ( |
|
359 | from setupext.setupext import ( | |
359 | print_line, print_raw, print_status, |
|
360 | print_line, print_raw, print_status, | |
360 | check_for_sphinx, check_for_pygments, |
|
361 | check_for_sphinx, check_for_pygments, | |
361 | check_for_nose, check_for_pexpect, |
|
362 | check_for_nose, check_for_pexpect, | |
362 | check_for_pyzmq, check_for_readline, |
|
363 | check_for_pyzmq, check_for_readline, | |
363 | check_for_jinja2, check_for_tornado |
|
364 | check_for_jinja2, check_for_tornado | |
364 | ) |
|
365 | ) | |
365 | print_line() |
|
366 | print_line() | |
366 | print_raw("BUILDING IPYTHON") |
|
367 | print_raw("BUILDING IPYTHON") | |
367 | print_status('python', sys.version) |
|
368 | print_status('python', sys.version) | |
368 | print_status('platform', sys.platform) |
|
369 | print_status('platform', sys.platform) | |
369 | if sys.platform == 'win32': |
|
370 | if sys.platform == 'win32': | |
370 | print_status('Windows version', sys.getwindowsversion()) |
|
371 | print_status('Windows version', sys.getwindowsversion()) | |
371 |
|
372 | |||
372 | print_raw("") |
|
373 | print_raw("") | |
373 | print_raw("OPTIONAL DEPENDENCIES") |
|
374 | print_raw("OPTIONAL DEPENDENCIES") | |
374 |
|
375 | |||
375 | check_for_sphinx() |
|
376 | check_for_sphinx() | |
376 | check_for_pygments() |
|
377 | check_for_pygments() | |
377 | check_for_nose() |
|
378 | check_for_nose() | |
378 | check_for_pexpect() |
|
379 | check_for_pexpect() | |
379 | check_for_pyzmq() |
|
380 | check_for_pyzmq() | |
380 | check_for_tornado() |
|
381 | check_for_tornado() | |
381 | check_for_readline() |
|
382 | check_for_readline() | |
382 | check_for_jinja2() |
|
383 | check_for_jinja2() | |
383 |
|
384 | |||
384 | #--------------------------------------------------------------------------- |
|
385 | #--------------------------------------------------------------------------- | |
385 | # VCS related |
|
386 | # VCS related | |
386 | #--------------------------------------------------------------------------- |
|
387 | #--------------------------------------------------------------------------- | |
387 |
|
388 | |||
388 | # utils.submodule has checks for submodule status |
|
389 | # utils.submodule has checks for submodule status | |
389 | execfile(pjoin('IPython','utils','submodule.py'), globals()) |
|
390 | execfile(pjoin('IPython','utils','submodule.py'), globals()) | |
390 |
|
391 | |||
391 | class UpdateSubmodules(Command): |
|
392 | class UpdateSubmodules(Command): | |
392 | """Update git submodules |
|
393 | """Update git submodules | |
393 |
|
394 | |||
394 | IPython's external javascript dependencies live in a separate repo. |
|
395 | IPython's external javascript dependencies live in a separate repo. | |
395 | """ |
|
396 | """ | |
396 | description = "Update git submodules" |
|
397 | description = "Update git submodules" | |
397 | user_options = [] |
|
398 | user_options = [] | |
398 |
|
399 | |||
399 | def initialize_options(self): |
|
400 | def initialize_options(self): | |
400 | pass |
|
401 | pass | |
401 |
|
402 | |||
402 | def finalize_options(self): |
|
403 | def finalize_options(self): | |
403 | pass |
|
404 | pass | |
404 |
|
405 | |||
405 | def run(self): |
|
406 | def run(self): | |
406 | failure = False |
|
407 | failure = False | |
407 | try: |
|
408 | try: | |
408 | self.spawn('git submodule init'.split()) |
|
409 | self.spawn('git submodule init'.split()) | |
409 | self.spawn('git submodule update --recursive'.split()) |
|
410 | self.spawn('git submodule update --recursive'.split()) | |
410 | except Exception as e: |
|
411 | except Exception as e: | |
411 | failure = e |
|
412 | failure = e | |
412 | print(e) |
|
413 | print(e) | |
413 |
|
414 | |||
414 | if not check_submodule_status(repo_root) == 'clean': |
|
415 | if not check_submodule_status(repo_root) == 'clean': | |
415 | print("submodules could not be checked out") |
|
416 | print("submodules could not be checked out") | |
416 | sys.exit(1) |
|
417 | sys.exit(1) | |
417 |
|
418 | |||
418 |
|
419 | |||
419 | def git_prebuild(pkg_dir, build_cmd=build_py): |
|
420 | def git_prebuild(pkg_dir, build_cmd=build_py): | |
420 | """Return extended build or sdist command class for recording commit |
|
421 | """Return extended build or sdist command class for recording commit | |
421 |
|
422 | |||
422 | records git commit in IPython.utils._sysinfo.commit |
|
423 | records git commit in IPython.utils._sysinfo.commit | |
423 |
|
424 | |||
424 | for use in IPython.utils.sysinfo.sys_info() calls after installation. |
|
425 | for use in IPython.utils.sysinfo.sys_info() calls after installation. | |
425 |
|
426 | |||
426 | Also ensures that submodules exist prior to running |
|
427 | Also ensures that submodules exist prior to running | |
427 | """ |
|
428 | """ | |
428 |
|
429 | |||
429 | class MyBuildPy(build_cmd): |
|
430 | class MyBuildPy(build_cmd): | |
430 | ''' Subclass to write commit data into installation tree ''' |
|
431 | ''' Subclass to write commit data into installation tree ''' | |
431 | def run(self): |
|
432 | def run(self): | |
432 | build_cmd.run(self) |
|
433 | build_cmd.run(self) | |
433 | # this one will only fire for build commands |
|
434 | # this one will only fire for build commands | |
434 | if hasattr(self, 'build_lib'): |
|
435 | if hasattr(self, 'build_lib'): | |
435 | self._record_commit(self.build_lib) |
|
436 | self._record_commit(self.build_lib) | |
436 |
|
437 | |||
437 | def make_release_tree(self, base_dir, files): |
|
438 | def make_release_tree(self, base_dir, files): | |
438 | # this one will fire for sdist |
|
439 | # this one will fire for sdist | |
439 | build_cmd.make_release_tree(self, base_dir, files) |
|
440 | build_cmd.make_release_tree(self, base_dir, files) | |
440 | self._record_commit(base_dir) |
|
441 | self._record_commit(base_dir) | |
441 |
|
442 | |||
442 | def _record_commit(self, base_dir): |
|
443 | def _record_commit(self, base_dir): | |
443 | import subprocess |
|
444 | import subprocess | |
444 | proc = subprocess.Popen('git rev-parse --short HEAD', |
|
445 | proc = subprocess.Popen('git rev-parse --short HEAD', | |
445 | stdout=subprocess.PIPE, |
|
446 | stdout=subprocess.PIPE, | |
446 | stderr=subprocess.PIPE, |
|
447 | stderr=subprocess.PIPE, | |
447 | shell=True) |
|
448 | shell=True) | |
448 | repo_commit, _ = proc.communicate() |
|
449 | repo_commit, _ = proc.communicate() | |
449 | repo_commit = repo_commit.strip().decode("ascii") |
|
450 | repo_commit = repo_commit.strip().decode("ascii") | |
450 |
|
451 | |||
451 | out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py') |
|
452 | out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py') | |
452 | if os.path.isfile(out_pth) and not repo_commit: |
|
453 | if os.path.isfile(out_pth) and not repo_commit: | |
453 | # nothing to write, don't clobber |
|
454 | # nothing to write, don't clobber | |
454 | return |
|
455 | return | |
455 |
|
456 | |||
456 | print("writing git commit '%s' to %s" % (repo_commit, out_pth)) |
|
457 | print("writing git commit '%s' to %s" % (repo_commit, out_pth)) | |
457 |
|
458 | |||
458 | # remove to avoid overwriting original via hard link |
|
459 | # remove to avoid overwriting original via hard link | |
459 | try: |
|
460 | try: | |
460 | os.remove(out_pth) |
|
461 | os.remove(out_pth) | |
461 | except (IOError, OSError): |
|
462 | except (IOError, OSError): | |
462 | pass |
|
463 | pass | |
463 | with open(out_pth, 'w') as out_file: |
|
464 | with open(out_pth, 'w') as out_file: | |
464 | out_file.writelines([ |
|
465 | out_file.writelines([ | |
465 | '# GENERATED BY setup.py\n', |
|
466 | '# GENERATED BY setup.py\n', | |
466 | 'commit = "%s"\n' % repo_commit, |
|
467 | 'commit = "%s"\n' % repo_commit, | |
467 | ]) |
|
468 | ]) | |
468 | return require_submodules(MyBuildPy) |
|
469 | return require_submodules(MyBuildPy) | |
469 |
|
470 | |||
470 |
|
471 | |||
471 | def require_submodules(command): |
|
472 | def require_submodules(command): | |
472 | """decorator for instructing a command to check for submodules before running""" |
|
473 | """decorator for instructing a command to check for submodules before running""" | |
473 | class DecoratedCommand(command): |
|
474 | class DecoratedCommand(command): | |
474 | def run(self): |
|
475 | def run(self): | |
475 | if not check_submodule_status(repo_root) == 'clean': |
|
476 | if not check_submodule_status(repo_root) == 'clean': | |
476 | print("submodules missing! Run `setup.py submodule` and try again") |
|
477 | print("submodules missing! Run `setup.py submodule` and try again") | |
477 | sys.exit(1) |
|
478 | sys.exit(1) | |
478 | command.run(self) |
|
479 | command.run(self) | |
479 | return DecoratedCommand |
|
480 | return DecoratedCommand | |
480 |
|
481 | |||
481 | #--------------------------------------------------------------------------- |
|
482 | #--------------------------------------------------------------------------- | |
482 | # Notebook related |
|
483 | # Notebook related | |
483 | #--------------------------------------------------------------------------- |
|
484 | #--------------------------------------------------------------------------- | |
484 |
|
485 | |||
485 | class CompileCSS(Command): |
|
486 | class CompileCSS(Command): | |
486 | """Recompile Notebook CSS |
|
487 | """Recompile Notebook CSS | |
487 |
|
488 | |||
488 | Regenerate the compiled CSS from LESS sources. |
|
489 | Regenerate the compiled CSS from LESS sources. | |
489 |
|
490 | |||
490 | Requires various dev dependencies, such as fabric and lessc. |
|
491 | Requires various dev dependencies, such as fabric and lessc. | |
491 | """ |
|
492 | """ | |
492 | description = "Recompile Notebook CSS" |
|
493 | description = "Recompile Notebook CSS" | |
493 | user_options = [] |
|
494 | user_options = [] | |
494 |
|
495 | |||
495 | def initialize_options(self): |
|
496 | def initialize_options(self): | |
496 | pass |
|
497 | pass | |
497 |
|
498 | |||
498 | def finalize_options(self): |
|
499 | def finalize_options(self): | |
499 | pass |
|
500 | pass | |
500 |
|
501 | |||
501 | def run(self): |
|
502 | def run(self): | |
502 | call("fab css", shell=True, cwd=pjoin(repo_root, "IPython", "html")) |
|
503 | call("fab css", shell=True, cwd=pjoin(repo_root, "IPython", "html")) |
General Comments 0
You need to be logged in to leave comments.
Login now