##// END OF EJS Templates
Merge branch 'master' of https://github.com/ipython/ipython into cython-magic-name-argument
Cyrille Rossant -
r12791:12dec511 merge
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,41 b''
1 from IPython.utils.capture import capture_output
2
3 import nose.tools as nt
4
5 def test_alias_lifecycle():
6 name = 'test_alias1'
7 cmd = 'echo "Hello"'
8 am = _ip.alias_manager
9 am.clear_aliases()
10 am.define_alias(name, cmd)
11 assert am.is_alias(name)
12 nt.assert_equal(am.retrieve_alias(name), cmd)
13 nt.assert_in((name, cmd), am.aliases)
14
15 # Test running the alias
16 orig_system = _ip.system
17 result = []
18 _ip.system = result.append
19 try:
20 _ip.run_cell('%{}'.format(name))
21 result = [c.strip() for c in result]
22 nt.assert_equal(result, [cmd])
23 finally:
24 _ip.system = orig_system
25
26 # Test removing the alias
27 am.undefine_alias(name)
28 assert not am.is_alias(name)
29 with nt.assert_raises(ValueError):
30 am.retrieve_alias(name)
31 nt.assert_not_in((name, cmd), am.aliases)
32
33
34 def test_alias_args_error():
35 """Error expanding with wrong number of arguments"""
36 _ip.alias_manager.define_alias('parts', 'echo first %s second %s')
37 # capture stderr:
38 with capture_output() as cap:
39 _ip.run_cell('parts 1')
40
41 nt.assert_equal(cap.stderr.split(':')[0], 'UsageError') No newline at end of file
@@ -0,0 +1,25 b''
1 """Test NotebookApp"""
2
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 import nose.tools as nt
15
16 import IPython.testing.tools as tt
17
18 #-----------------------------------------------------------------------------
19 # Test functions
20 #-----------------------------------------------------------------------------
21
22 def test_help_output():
23 """ipython notebook --help-all works"""
24 tt.help_all_output_test('notebook')
25
@@ -0,0 +1,167 b''
1 """utilities for testing IPython kernels"""
2
3 #-------------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-------------------------------------------------------------------------------
9
10 #-------------------------------------------------------------------------------
11 # Imports
12 #-------------------------------------------------------------------------------
13
14 import atexit
15
16 from contextlib import contextmanager
17 from subprocess import PIPE
18 from Queue import Empty
19
20 import nose.tools as nt
21
22 from IPython.kernel import KernelManager
23
24 #-------------------------------------------------------------------------------
25 # Globals
26 #-------------------------------------------------------------------------------
27
28 STARTUP_TIMEOUT = 60
29 TIMEOUT = 15
30
31 KM = None
32 KC = None
33
34 #-------------------------------------------------------------------------------
35 # code
36 #-------------------------------------------------------------------------------
37
38
39 def start_new_kernel():
40 """start a new kernel, and return its Manager and Client"""
41 km = KernelManager()
42 km.start_kernel(stdout=PIPE, stderr=PIPE)
43 kc = km.client()
44 kc.start_channels()
45
46 msg_id = kc.kernel_info()
47 kc.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT)
48 flush_channels(kc)
49 return km, kc
50
51 def flush_channels(kc=None):
52 """flush any messages waiting on the queue"""
53 from .test_message_spec import validate_message
54
55 if kc is None:
56 kc = KC
57 for channel in (kc.shell_channel, kc.iopub_channel):
58 while True:
59 try:
60 msg = channel.get_msg(block=True, timeout=0.1)
61 except Empty:
62 break
63 else:
64 validate_message(msg)
65
66
67 def execute(code='', kc=None, **kwargs):
68 """wrapper for doing common steps for validating an execution request"""
69 from .test_message_spec import validate_message
70 if kc is None:
71 kc = KC
72 msg_id = kc.execute(code=code, **kwargs)
73 reply = kc.get_shell_msg(timeout=TIMEOUT)
74 validate_message(reply, 'execute_reply', msg_id)
75 busy = kc.get_iopub_msg(timeout=TIMEOUT)
76 validate_message(busy, 'status', msg_id)
77 nt.assert_equal(busy['content']['execution_state'], 'busy')
78
79 if not kwargs.get('silent'):
80 pyin = kc.get_iopub_msg(timeout=TIMEOUT)
81 validate_message(pyin, 'pyin', msg_id)
82 nt.assert_equal(pyin['content']['code'], code)
83
84 return msg_id, reply['content']
85
86 def start_global_kernel():
87 """start the global kernel (if it isn't running) and return its client"""
88 global KM, KC
89 if KM is None:
90 KM, KC = start_new_kernel()
91 atexit.register(stop_global_kernel)
92 return KC
93
94 @contextmanager
95 def kernel():
96 """Context manager for the global kernel instance
97
98 Should be used for most kernel tests
99
100 Returns
101 -------
102 kernel_client: connected KernelClient instance
103 """
104 yield start_global_kernel()
105
106 def uses_kernel(test_f):
107 """Decorator for tests that use the global kernel"""
108 def wrapped_test():
109 with kernel() as kc:
110 test_f(kc)
111 wrapped_test.__doc__ = test_f.__doc__
112 wrapped_test.__name__ = test_f.__name__
113 return wrapped_test
114
115 def stop_global_kernel():
116 """Stop the global shared kernel instance, if it exists"""
117 global KM, KC
118 KC.stop_channels()
119 KC = None
120 if KM is None:
121 return
122 KM.shutdown_kernel(now=True)
123 KM = None
124
125 @contextmanager
126 def new_kernel():
127 """Context manager for a new kernel in a subprocess
128
129 Should only be used for tests where the kernel must not be re-used.
130
131 Returns
132 -------
133 kernel_client: connected KernelClient instance
134 """
135 km, kc = start_new_kernel()
136 try:
137 yield kc
138 finally:
139 kc.stop_channels()
140 km.shutdown_kernel(now=True)
141
142
143 def assemble_output(iopub):
144 """assemble stdout/err from an execution"""
145 stdout = ''
146 stderr = ''
147 while True:
148 msg = iopub.get_msg(block=True, timeout=1)
149 msg_type = msg['msg_type']
150 content = msg['content']
151 if msg_type == 'status' and content['execution_state'] == 'idle':
152 # idle message signals end of output
153 break
154 elif msg['msg_type'] == 'stream':
155 if content['name'] == 'stdout':
156 stdout += content['data']
157 elif content['name'] == 'stderr':
158 stderr += content['data']
159 else:
160 raise KeyError("bad stream: %r" % content['name'])
161 else:
162 # other output, ignored
163 pass
164 return stdout, stderr
165
166
167
@@ -0,0 +1,316 b''
1 """This module defines Exporter, a highly configurable converter
2 that uses Jinja2 to export notebook files into different formats.
3 """
4
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
7 #
8 # Distributed under the terms of the Modified BSD License.
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 from __future__ import print_function, absolute_import
18
19 # Stdlib imports
20 import os
21
22 # other libs/dependencies
23 from jinja2 import Environment, FileSystemLoader, ChoiceLoader, TemplateNotFound
24
25 # IPython imports
26 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, Dict, Any
27 from IPython.utils.importstring import import_item
28 from IPython.utils import py3compat, text
29
30 from IPython.nbconvert import filters
31 from .exporter import Exporter
32
33 #-----------------------------------------------------------------------------
34 # Globals and constants
35 #-----------------------------------------------------------------------------
36
37 #Jinja2 extensions to load.
38 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
39
40 default_filters = {
41 'indent': text.indent,
42 'markdown2html': filters.markdown2html,
43 'ansi2html': filters.ansi2html,
44 'filter_data_type': filters.DataTypeFilter,
45 'get_lines': filters.get_lines,
46 'highlight2html': filters.highlight2html,
47 'highlight2latex': filters.highlight2latex,
48 'ipython2python': filters.ipython2python,
49 'posix_path': filters.posix_path,
50 'markdown2latex': filters.markdown2latex,
51 'markdown2rst': filters.markdown2rst,
52 'comment_lines': filters.comment_lines,
53 'strip_ansi': filters.strip_ansi,
54 'strip_dollars': filters.strip_dollars,
55 'strip_files_prefix': filters.strip_files_prefix,
56 'html2text' : filters.html2text,
57 'add_anchor': filters.add_anchor,
58 'ansi2latex': filters.ansi2latex,
59 'strip_math_space': filters.strip_math_space,
60 'wrap_text': filters.wrap_text,
61 'escape_latex': filters.escape_latex,
62 'citation2latex': filters.citation2latex,
63 'path2url': filters.path2url,
64 'add_prompts': filters.add_prompts,
65 }
66
67 #-----------------------------------------------------------------------------
68 # Class
69 #-----------------------------------------------------------------------------
70
71 class TemplateExporter(Exporter):
72 """
73 Exports notebooks into other file formats. Uses Jinja 2 templating engine
74 to output new formats. Inherit from this class if you are creating a new
75 template type along with new filters/preprocessors. If the filters/
76 preprocessors provided by default suffice, there is no need to inherit from
77 this class. Instead, override the template_file and file_extension
78 traits via a config file.
79
80 {filters}
81 """
82
83 # finish the docstring
84 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
85
86
87 template_file = Unicode(u'default',
88 config=True,
89 help="Name of the template file to use")
90 def _template_file_changed(self, name, old, new):
91 if new == 'default':
92 self.template_file = self.default_template
93 else:
94 self.template_file = new
95 self.template = None
96 self._load_template()
97
98 default_template = Unicode(u'')
99 template = Any()
100 environment = Any()
101
102 template_path = List(['.'], config=True)
103 def _template_path_changed(self, name, old, new):
104 self._load_template()
105
106 default_template_path = Unicode(
107 os.path.join("..", "templates"),
108 help="Path where the template files are located.")
109
110 template_skeleton_path = Unicode(
111 os.path.join("..", "templates", "skeleton"),
112 help="Path where the template skeleton files are located.")
113
114 #Jinja block definitions
115 jinja_comment_block_start = Unicode("", config=True)
116 jinja_comment_block_end = Unicode("", config=True)
117 jinja_variable_block_start = Unicode("", config=True)
118 jinja_variable_block_end = Unicode("", config=True)
119 jinja_logic_block_start = Unicode("", config=True)
120 jinja_logic_block_end = Unicode("", config=True)
121
122 #Extension that the template files use.
123 template_extension = Unicode(".tpl", config=True)
124
125 filters = Dict(config=True,
126 help="""Dictionary of filters, by name and namespace, to add to the Jinja
127 environment.""")
128
129
130 def __init__(self, config=None, extra_loaders=None, **kw):
131 """
132 Public constructor
133
134 Parameters
135 ----------
136 config : config
137 User configuration instance.
138 extra_loaders : list[of Jinja Loaders]
139 ordered list of Jinja loader to find templates. Will be tried in order
140 before the default FileSystem ones.
141 template : str (optional, kw arg)
142 Template to use when exporting.
143 """
144 if not config:
145 config = self.default_config
146
147 super(Exporter, self).__init__(config=config, **kw)
148
149 #Init
150 self._init_template()
151 self._init_environment(extra_loaders=extra_loaders)
152 self._init_preprocessors()
153 self._init_filters()
154
155
156 def _load_template(self):
157 """Load the Jinja template object from the template file
158
159 This is a no-op if the template attribute is already defined,
160 or the Jinja environment is not setup yet.
161
162 This is triggered by various trait changes that would change the template.
163 """
164 if self.template is not None:
165 return
166 # called too early, do nothing
167 if self.environment is None:
168 return
169 # Try different template names during conversion. First try to load the
170 # template by name with extension added, then try loading the template
171 # as if the name is explicitly specified, then try the name as a
172 # 'flavor', and lastly just try to load the template by module name.
173 module_name = self.__module__.rsplit('.', 1)[-1]
174 try_names = []
175 if self.template_file:
176 try_names.extend([
177 self.template_file + self.template_extension,
178 self.template_file,
179 module_name + '_' + self.template_file + self.template_extension,
180 ])
181 try_names.append(module_name + self.template_extension)
182 for try_name in try_names:
183 self.log.debug("Attempting to load template %s", try_name)
184 try:
185 self.template = self.environment.get_template(try_name)
186 except (TemplateNotFound, IOError):
187 pass
188 except Exception as e:
189 self.log.warn("Unexpected exception loading template: %s", try_name, exc_info=True)
190 else:
191 self.log.info("Loaded template %s", try_name)
192 break
193
194 def from_notebook_node(self, nb, resources=None, **kw):
195 """
196 Convert a notebook from a notebook node instance.
197
198 Parameters
199 ----------
200 nb : Notebook node
201 resources : dict (**kw)
202 of additional resources that can be accessed read/write by
203 preprocessors and filters.
204 """
205 nb_copy, resources = super(TemplateExporter, self).from_notebook_node(nb, resources, **kw)
206
207 self._load_template()
208
209 if self.template is not None:
210 output = self.template.render(nb=nb_copy, resources=resources)
211 else:
212 raise IOError('template file "%s" could not be found' % self.template_file)
213 return output, resources
214
215
216 def register_filter(self, name, jinja_filter):
217 """
218 Register a filter.
219 A filter is a function that accepts and acts on one string.
220 The filters are accesible within the Jinja templating engine.
221
222 Parameters
223 ----------
224 name : str
225 name to give the filter in the Jinja engine
226 filter : filter
227 """
228 if jinja_filter is None:
229 raise TypeError('filter')
230 isclass = isinstance(jinja_filter, type)
231 constructed = not isclass
232
233 #Handle filter's registration based on it's type
234 if constructed and isinstance(jinja_filter, py3compat.string_types):
235 #filter is a string, import the namespace and recursively call
236 #this register_filter method
237 filter_cls = import_item(jinja_filter)
238 return self.register_filter(name, filter_cls)
239
240 if constructed and hasattr(jinja_filter, '__call__'):
241 #filter is a function, no need to construct it.
242 self.environment.filters[name] = jinja_filter
243 return jinja_filter
244
245 elif isclass and isinstance(jinja_filter, MetaHasTraits):
246 #filter is configurable. Make sure to pass in new default for
247 #the enabled flag if one was specified.
248 filter_instance = jinja_filter(parent=self)
249 self.register_filter(name, filter_instance )
250
251 elif isclass:
252 #filter is not configurable, construct it
253 filter_instance = jinja_filter()
254 self.register_filter(name, filter_instance)
255
256 else:
257 #filter is an instance of something without a __call__
258 #attribute.
259 raise TypeError('filter')
260
261
262 def _init_template(self):
263 """
264 Make sure a template name is specified. If one isn't specified, try to
265 build one from the information we know.
266 """
267 self._template_file_changed('template_file', self.template_file, self.template_file)
268
269
270 def _init_environment(self, extra_loaders=None):
271 """
272 Create the Jinja templating environment.
273 """
274 here = os.path.dirname(os.path.realpath(__file__))
275 loaders = []
276 if extra_loaders:
277 loaders.extend(extra_loaders)
278
279 paths = self.template_path
280 paths.extend([os.path.join(here, self.default_template_path),
281 os.path.join(here, self.template_skeleton_path)])
282 loaders.append(FileSystemLoader(paths))
283
284 self.environment = Environment(
285 loader= ChoiceLoader(loaders),
286 extensions=JINJA_EXTENSIONS
287 )
288
289 #Set special Jinja2 syntax that will not conflict with latex.
290 if self.jinja_logic_block_start:
291 self.environment.block_start_string = self.jinja_logic_block_start
292 if self.jinja_logic_block_end:
293 self.environment.block_end_string = self.jinja_logic_block_end
294 if self.jinja_variable_block_start:
295 self.environment.variable_start_string = self.jinja_variable_block_start
296 if self.jinja_variable_block_end:
297 self.environment.variable_end_string = self.jinja_variable_block_end
298 if self.jinja_comment_block_start:
299 self.environment.comment_start_string = self.jinja_comment_block_start
300 if self.jinja_comment_block_end:
301 self.environment.comment_end_string = self.jinja_comment_block_end
302
303
304 def _init_filters(self):
305 """
306 Register all of the filters required for the exporter.
307 """
308
309 #Add default filters to the Jinja2 environment
310 for key, value in default_filters.items():
311 self.register_filter(key, value)
312
313 #Load user filters. Overwrite existing filters if need be.
314 if self.filters:
315 for key, user_filter in self.filters.items():
316 self.register_filter(key, user_filter)
@@ -0,0 +1,108 b''
1 """
2 Module with tests for templateexporter.py
3 """
4
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
7 #
8 # Distributed under the terms of the Modified BSD License.
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 from IPython.config import Config
18
19 from .base import ExportersTestsBase
20 from .cheese import CheesePreprocessor
21 from ..templateexporter import TemplateExporter
22
23
24 #-----------------------------------------------------------------------------
25 # Class
26 #-----------------------------------------------------------------------------
27
28 class TestExporter(ExportersTestsBase):
29 """Contains test functions for exporter.py"""
30
31
32 def test_constructor(self):
33 """
34 Can a TemplateExporter be constructed?
35 """
36 TemplateExporter()
37
38
39 def test_export(self):
40 """
41 Can a TemplateExporter export something?
42 """
43 exporter = self._make_exporter()
44 (output, resources) = exporter.from_filename(self._get_notebook())
45 assert len(output) > 0
46
47
48 def test_extract_outputs(self):
49 """
50 If the ExtractOutputPreprocessor is enabled, are outputs extracted?
51 """
52 config = Config({'ExtractOutputPreprocessor': {'enabled': True}})
53 exporter = self._make_exporter(config=config)
54 (output, resources) = exporter.from_filename(self._get_notebook())
55 assert resources is not None
56 assert isinstance(resources['outputs'], dict)
57 assert len(resources['outputs']) > 0
58
59
60 def test_preprocessor_class(self):
61 """
62 Can a preprocessor be added to the preprocessors list by class type?
63 """
64 config = Config({'Exporter': {'preprocessors': [CheesePreprocessor]}})
65 exporter = self._make_exporter(config=config)
66 (output, resources) = exporter.from_filename(self._get_notebook())
67 assert resources is not None
68 assert resources['cheese'] == 'real'
69
70
71 def test_preprocessor_instance(self):
72 """
73 Can a preprocessor be added to the preprocessors list by instance?
74 """
75 config = Config({'Exporter': {'preprocessors': [CheesePreprocessor()]}})
76 exporter = self._make_exporter(config=config)
77 (output, resources) = exporter.from_filename(self._get_notebook())
78 assert resources is not None
79 assert resources['cheese'] == 'real'
80
81
82 def test_preprocessor_dottedobjectname(self):
83 """
84 Can a preprocessor be added to the preprocessors list by dotted object name?
85 """
86 config = Config({'Exporter': {'preprocessors': ['IPython.nbconvert.exporters.tests.cheese.CheesePreprocessor']}})
87 exporter = self._make_exporter(config=config)
88 (output, resources) = exporter.from_filename(self._get_notebook())
89 assert resources is not None
90 assert resources['cheese'] == 'real'
91
92
93 def test_preprocessor_via_method(self):
94 """
95 Can a preprocessor be added via the Exporter convenience method?
96 """
97 exporter = self._make_exporter()
98 exporter.register_preprocessor(CheesePreprocessor, enabled=True)
99 (output, resources) = exporter.from_filename(self._get_notebook())
100 assert resources is not None
101 assert resources['cheese'] == 'real'
102
103
104 def _make_exporter(self, config=None):
105 # Create the exporter instance, make sure to set a template name since
106 # the base TemplateExporter doesn't have a template associated with it.
107 exporter = TemplateExporter(config=config, template_file='python')
108 return exporter
@@ -0,0 +1,72 b''
1 """Citation handling for LaTeX output."""
2
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2013, the IPython Development Team.
5 #
6 # Distributed under the terms of the Modified BSD License.
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Code
13 #-----------------------------------------------------------------------------
14
15
16 __all__ = ['citation2latex']
17
18
19 def citation2latex(s):
20 """Parse citations in Markdown cells.
21
22 This looks for HTML tags having a data attribute names `data-cite`
23 and replaces it by the call to LaTeX cite command. The tranformation
24 looks like this:
25
26 `<cite data-cite="granger">(Granger, 2013)</cite>`
27
28 Becomes
29
30 `\\cite{granger}`
31
32 Any HTML tag can be used, which allows the citations to be formatted
33 in HTML in any manner.
34 """
35 try:
36 from lxml import html
37 except ImportError:
38 return s
39
40 tree = html.fragment_fromstring(s, create_parent='div')
41 _process_node_cite(tree)
42 s = html.tostring(tree, encoding='unicode')
43 if s.endswith('</div>'):
44 s = s[:-6]
45 if s.startswith('<div>'):
46 s = s[5:]
47 return s
48
49
50 def _process_node_cite(node):
51 """Do the citation replacement as we walk the lxml tree."""
52
53 def _get(o, name):
54 value = getattr(o, name, None)
55 return '' if value is None else value
56
57 if 'data-cite' in node.attrib:
58 cite = '\cite{%(ref)s}' % {'ref': node.attrib['data-cite']}
59 prev = node.getprevious()
60 if prev is not None:
61 prev.tail = _get(prev, 'tail') + cite + _get(node, 'tail')
62 else:
63 parent = node.getparent()
64 if parent is not None:
65 parent.text = _get(parent, 'text') + cite + _get(node, 'tail')
66 try:
67 node.getparent().remove(node)
68 except AttributeError:
69 pass
70 else:
71 for child in node:
72 _process_node_cite(child)
@@ -0,0 +1,58 b''
1 #-----------------------------------------------------------------------------
2 # Copyright (c) 2013, the IPython Development Team.
3 #
4 # Distributed under the terms of the Modified BSD License.
5 #
6 # The full license is in the file COPYING.txt, distributed with this software.
7 #-----------------------------------------------------------------------------
8
9 #-----------------------------------------------------------------------------
10 # Imports
11 #-----------------------------------------------------------------------------
12
13 from ..citation import citation2latex
14
15 #-----------------------------------------------------------------------------
16 # Tests
17 #-----------------------------------------------------------------------------
18
19 test_md = """
20 # My Heading
21
22 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ac magna non augue
23 porttitor scelerisque ac id diam <cite data-cite="granger">Granger</cite>. Mauris elit
24 velit, lobortis sed interdum at, vestibulum vitae libero <strong data-cite="fperez">Perez</strong>.
25 Lorem ipsum dolor sit amet, consectetur adipiscing elit
26 <em data-cite="takluyver">Thomas</em>. Quisque iaculis ligula ut ipsum mattis viverra.
27
28 <p>Here is a plain paragraph that should be unaffected.</p>
29
30 * One <cite data-cite="jdfreder">Jonathan</cite>.
31 * Two <cite data-cite="carreau">Matthias</cite>.
32 * Three <cite data-cite="ivanov">Paul</cite>.
33 """
34
35 test_md_parsed = """
36 # My Heading
37
38 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ac magna non augue
39 porttitor scelerisque ac id diam \cite{granger}. Mauris elit
40 velit, lobortis sed interdum at, vestibulum vitae libero \cite{fperez}.
41 Lorem ipsum dolor sit amet, consectetur adipiscing elit
42 \cite{takluyver}. Quisque iaculis ligula ut ipsum mattis viverra.
43
44 <p>Here is a plain paragraph that should be unaffected.</p>
45
46 * One \cite{jdfreder}.
47 * Two \cite{carreau}.
48 * Three \cite{ivanov}.
49 """
50
51 def test_citation2latex():
52 """Are citations parsed properly?"""
53 try:
54 import lxml
55 except ImportError:
56 assert test_md == citation2latex(test_md)
57 else:
58 assert test_md_parsed == citation2latex(test_md)
@@ -0,0 +1,113 b''
1 """This preprocessor detect cells using a different language through
2 magic extensions such as `%%R` or `%%octave`. Cell's metadata is marked
3 so that the appropriate highlighter can be used in the `highlight`
4 filter.
5 """
6
7 #-----------------------------------------------------------------------------
8 # Copyright (c) 2013, the IPython Development Team.
9 #
10 # Distributed under the terms of the Modified BSD License.
11 #
12 # The full license is in the file COPYING.txt, distributed with this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
19 from __future__ import print_function, absolute_import
20
21 import re
22
23 # Our own imports
24 from .base import Preprocessor
25 from IPython.utils.traitlets import Dict
26
27 #-----------------------------------------------------------------------------
28 # Classes
29 #-----------------------------------------------------------------------------
30
31
32 class HighlightMagicsPreprocessor(Preprocessor):
33 """
34 Detects and tags code cells that use a different languages than Python.
35 """
36
37 # list of magic language extensions and their associated pygment lexers
38 default_languages = Dict(
39 default_value={
40 '%%R': 'r',
41 '%%bash': 'bash',
42 '%%cython': 'cython',
43 '%%javascript': 'javascript',
44 '%%julia': 'julia',
45 '%%latex': 'latex',
46 '%%octave': 'octave',
47 '%%perl': 'perl',
48 '%%ruby': 'ruby',
49 '%%sh': 'sh'})
50
51 # user defined language extensions
52 languages = Dict(
53 config=True,
54 help=("Syntax highlighting for magic's extension languages. "
55 "Each item associates a language magic extension such as %%R, "
56 "with a pygments lexer such as r."))
57
58 def __init__(self, config=None, **kw):
59 """Public constructor"""
60
61 super(HighlightMagicsPreprocessor, self).__init__(config=config, **kw)
62
63 # Update the default languages dict with the user configured ones
64 self.default_languages.update(self.languages)
65
66 # build a regular expression to catch language extensions and choose
67 # an adequate pygments lexer
68 any_language = "|".join(self.default_languages.keys())
69 self.re_magic_language = re.compile(
70 r'^\s*({0})\s+'.format(any_language))
71
72 def which_magic_language(self, source):
73 """
74 When a cell uses another language through a magic extension,
75 the other language is returned.
76 If no language magic is detected, this function returns None.
77
78 Parameters
79 ----------
80 source: str
81 Source code of the cell to highlight
82 """
83
84 m = self.re_magic_language.match(source)
85
86 if m:
87 # By construction of the re, the matched language must be in the
88 # languages dictionary
89 return self.default_languages[m.group(1)]
90 else:
91 return None
92
93 def preprocess_cell(self, cell, resources, cell_index):
94 """
95 Tags cells using a magic extension language
96
97 Parameters
98 ----------
99 cell : NotebookNode cell
100 Notebook cell being processed
101 resources : dictionary
102 Additional resources used in the conversion process. Allows
103 preprocessors to pass variables into the Jinja engine.
104 cell_index : int
105 Index of the cell being processed (see base.py)
106 """
107
108 # Only tag code cells
109 if hasattr(cell, "input") and cell.cell_type == "code":
110 magic_language = self.which_magic_language(cell.input)
111 if magic_language:
112 cell['metadata']['magics_language'] = magic_language
113 return cell, resources
@@ -0,0 +1,197 b''
1 ((= Latex base template (must inherit)
2 This template builds upon the abstract template, adding common latex output
3 functions. Figures, data_text,
4 This template does not define a docclass, the inheriting class must define this.=))
5
6 ((*- extends 'display_priority.tplx' -*))
7
8 %===============================================================================
9 % Abstract overrides
10 %===============================================================================
11
12 ((* block header *))
13 ((* block docclass *))((* endblock docclass *))
14
15 ((* block packages *))
16 \usepackage{graphicx} % Used to insert images
17 \usepackage{adjustbox} % Used to constrain images to a maximum size
18 \usepackage{color} % Allow colors to be defined
19 \usepackage{enumerate} % Needed for markdown enumerations to work
20 \usepackage{geometry} % Used to adjust the document margins
21 \usepackage{amsmath} % Equations
22 \usepackage{amssymb} % Equations
23 \usepackage[utf8]{inputenc} % Allow utf-8 characters in the tex document
24 \usepackage{ucs} % Extended unicode (utf-8) support
25 \usepackage{fancyvrb} % verbatim replacement that allows latex
26 \usepackage{grffile} % extends the file name processing of package graphics
27 % to support a larger range
28 % The hyperref package gives us a pdf with properly built
29 % internal navigation ('pdf bookmarks' for the table of contents,
30 % internal cross-reference links, web links for URLs, etc.)
31 \usepackage{hyperref}
32 ((* endblock packages *))
33
34 ((* block definitions *))
35 \definecolor{orange}{cmyk}{0,0.4,0.8,0.2}
36 \definecolor{darkorange}{rgb}{.71,0.21,0.01}
37 \definecolor{darkgreen}{rgb}{.12,.54,.11}
38 \definecolor{myteal}{rgb}{.26, .44, .56}
39 \definecolor{gray}{gray}{0.45}
40 \definecolor{lightgray}{gray}{.95}
41 \definecolor{mediumgray}{gray}{.8}
42 \definecolor{inputbackground}{rgb}{.95, .95, .85}
43 \definecolor{outputbackground}{rgb}{.95, .95, .95}
44 \definecolor{traceback}{rgb}{1, .95, .95}
45 % new ansi colors
46 \definecolor{brown}{rgb}{0.54,0.27,0.07}
47 \definecolor{purple}{rgb}{0.5,0.0,0.5}
48 \definecolor{darkgray}{gray}{0.25}
49 \definecolor{lightred}{rgb}{1.0,0.39,0.28}
50 \definecolor{lightgreen}{rgb}{0.48,0.99,0.0}
51 \definecolor{lightblue}{rgb}{0.53,0.81,0.92}
52 \definecolor{lightpurple}{rgb}{0.87,0.63,0.87}
53 \definecolor{lightcyan}{rgb}{0.5,1.0,0.83}
54 % Define a nice break command that doesn't care if a line doesn't already
55 % exist.
56 \def\br{\hspace*{\fill} \\* }
57 % Math Jax compatability definitions
58 \def\gt{>}
59 \def\lt{<}
60 % Document parameters
61 ((* block title *))\title{((( resources.metadata.name | escape_latex )))}((* endblock title *))
62 ((* block date *))((* endblock date *))
63 ((* block author *))((* endblock author *))
64 ((* endblock definitions *))
65
66 ((* block commands *))
67 % Prevent overflowing lines due to hard-to-break entities
68 \sloppy
69 % Setup hyperref package
70 \hypersetup{
71 breaklinks=true, % so long urls are correctly broken across lines
72 colorlinks=true,
73 urlcolor=blue,
74 linkcolor=darkorange,
75 citecolor=darkgreen,
76 }
77 % Slightly bigger margins than the latex defaults
78 ((* block margins *))
79 \geometry{verbose,tmargin=1in,bmargin=1in,lmargin=1in,rmargin=1in}
80 ((* endblock margins *))
81 ((* endblock commands *))
82 ((* endblock header *))
83
84 ((* block body *))
85 \begin{document}
86
87 ((* block predoc *))
88 ((* block maketitle *))\maketitle((* endblock maketitle *))
89 ((* block abstract *))((* endblock abstract *))
90 ((* endblock predoc *))
91
92 ((( super() )))
93
94 % Add a bibliography block to the postdoc
95 ((* block postdoc *))
96 ((* block bibliography *))((* endblock bibliography *))
97 ((* endblock postdoc *))
98 \end{document}
99 ((* endblock body *))
100
101 %===============================================================================
102 % Support blocks
103 %===============================================================================
104
105 % Displaying simple data text
106 ((* block data_text *))
107 \begin{verbatim}
108 ((( output.text )))
109 \end{verbatim}
110 ((* endblock data_text *))
111
112 % Display python error text as-is
113 ((* block pyerr *))
114 \begin{Verbatim}[commandchars=\\\{\}]
115 ((( super() )))
116 \end{Verbatim}
117 ((* endblock pyerr *))
118 ((* block traceback_line *))
119 ((( line | indent | strip_ansi | escape_latex )))
120 ((* endblock traceback_line *))
121
122 % Display stream ouput with coloring
123 ((* block stream *))
124 \begin{Verbatim}[commandchars=\\\{\}]
125 ((( output.text | escape_latex | ansi2latex )))
126 \end{Verbatim}
127 ((* endblock stream *))
128
129 % Display latex
130 ((* block data_latex -*))
131 ((*- if output.latex.startswith('$'): -*))
132 ((= Replace $ symbols with more explicit, equation block. =))
133 \begin{equation*}
134 ((( output.latex | strip_dollars )))
135 \end{equation*}
136 ((*- else -*))
137 ((( output.latex )))
138 ((*- endif *))
139 ((* endblock data_latex *))
140
141 % Default mechanism for rendering figures
142 ((*- block data_png -*))((( draw_figure(output.png_filename) )))((*- endblock -*))
143 ((*- block data_jpg -*))((( draw_figure(output.jpeg_filename) )))((*- endblock -*))
144 ((*- block data_svg -*))((( draw_figure(output.svg_filename) )))((*- endblock -*))
145 ((*- block data_pdf -*))((( draw_figure(output.pdf_filename) )))((*- endblock -*))
146
147 % Draw a figure using the graphicx package.
148 ((* macro draw_figure(filename) -*))
149 ((* set filename = filename | posix_path *))
150 ((*- block figure scoped -*))
151 \begin{center}
152 \adjustimage{max size={0.9\linewidth}{0.9\paperheight}}{((( filename )))}
153 \end{center}
154 { \hspace*{\fill} \\}
155 ((*- endblock figure -*))
156 ((*- endmacro *))
157
158 % Draw heading cell. Explicitly map different cell levels.
159 ((* block headingcell scoped *))
160
161 ((* if cell.level == 1 -*))
162 ((* block h1 -*))\section((* endblock h1 -*))
163 ((* elif cell.level == 2 -*))
164 ((* block h2 -*))\subsection((* endblock h2 -*))
165 ((* elif cell.level == 3 -*))
166 ((* block h3 -*))\subsubsection((* endblock h3 -*))
167 ((* elif cell.level == 4 -*))
168 ((* block h4 -*))\paragraph((* endblock h4 -*))
169 ((* elif cell.level == 5 -*))
170 ((* block h5 -*))\subparagraph((* endblock h5 -*))
171 ((* elif cell.level == 6 -*))
172 ((* block h6 -*))\\*\textit((* endblock h6 -*))
173 ((*- endif -*))
174 {((( cell.source | replace('\n', ' ') | citation2latex | markdown2latex )))}
175
176 ((* endblock headingcell *))
177
178 % Redirect pyout to display data priority.
179 ((* block pyout scoped *))
180 ((* block data_priority scoped *))
181 ((( super() )))
182 ((* endblock *))
183 ((* endblock pyout *))
184
185 % Render markdown
186 ((* block markdowncell scoped *))
187 ((( cell.source | citation2latex | markdown2latex )))
188 ((* endblock markdowncell *))
189
190 % Spit out the contents of raw cells unmodified
191 ((* block rawcell scoped *))
192 ((( cell.source )))
193 ((* endblock rawcell *))
194
195 % Don't display unknown types
196 ((* block unknowncell scoped *))
197 ((* endblock unknowncell *))
@@ -0,0 +1,22 b''
1
2 % Default to the notebook output style
3 ((* if not cell_style is defined *))
4 ((* set cell_style = 'style_ipython.tplx' *))
5 ((* endif *))
6
7 % Inherit from the specified cell style.
8 ((* extends cell_style *))
9
10
11 %===============================================================================
12 % Latex Book
13 %===============================================================================
14
15 ((* block predoc *))
16 ((( super() )))
17 ((* block tableofcontents *))\tableofcontents((* endblock tableofcontents *))
18 ((* endblock predoc *))
19
20 ((* block docclass *))
21 \documentclass{report}
22 ((* endblock docclass *))
@@ -0,0 +1,41 b''
1 ((= Black&white ipython input/output style =))
2
3 ((*- extends 'latex_base.tplx' -*))
4
5 %===============================================================================
6 % Input
7 %===============================================================================
8
9 ((* block input scoped *))
10 ((( add_prompt(cell.input, cell, 'In ') )))
11 ((* endblock input *))
12
13
14 %===============================================================================
15 % Output
16 %===============================================================================
17
18 ((* block pyout scoped *))
19 ((*- for type in output | filter_data_type -*))
20 ((*- if type in ['text']*))
21 ((( add_prompt(output.text, cell, 'Out') )))
22 ((*- else -*))
23 \verb+Out[((( cell.prompt_number )))]:+((( super() )))
24 ((*- endif -*))
25 ((*- endfor -*))
26 ((* endblock pyout *))
27
28
29 %==============================================================================
30 % Support Macros
31 %==============================================================================
32
33 % Name: draw_prompt
34 % Purpose: Renders an output/input prompt
35 ((* macro add_prompt(text, cell, prompt) -*))
36 ((*- set prompt_number = "" ~ cell.prompt_number -*))
37 ((*- set indentation = " " * (prompt_number | length + 7) -*))
38 \begin{verbatim}
39 (((- text | add_prompts(first=prompt ~ '[' ~ prompt_number ~ ']: ', cont=indentation) -)))
40 \end{verbatim}
41 ((*- endmacro *))
@@ -0,0 +1,13 b''
1 ((= Black&white Python input/output style =))
2
3 ((*- extends 'latex_base.tplx' -*))
4
5 %===============================================================================
6 % Input
7 %===============================================================================
8
9 ((* block input scoped *))
10 \begin{verbatim}
11 ((( cell.input | add_prompts )))
12 \end{verbatim}
13 ((* endblock input *))
@@ -0,0 +1,54 b''
1 ((= IPython input/output style =))
2
3 ((*- extends 'latex_base.tplx' -*))
4
5 % Custom definitions
6 ((* block definitions *))
7 ((( super() )))
8
9 % Pygments definitions
10 ((( resources.latex.pygments_definitions )))
11
12 % Exact colors from NB
13 \definecolor{incolor}{rgb}{0.0, 0.0, 0.5}
14 \definecolor{outcolor}{rgb}{0.545, 0.0, 0.0}
15
16 ((* endblock definitions *))
17
18 %===============================================================================
19 % Input
20 %===============================================================================
21
22 ((* block input scoped *))
23 ((( add_prompt(cell.input | highlight2latex(strip_verbatim=True), cell, 'In ', 'incolor') )))
24 ((* endblock input *))
25
26
27 %===============================================================================
28 % Output
29 %===============================================================================
30
31 ((* block pyout scoped *))
32 ((*- for type in output | filter_data_type -*))
33 ((*- if type in ['text']*))
34 ((( add_prompt(output.text | escape_latex, cell, 'Out', 'outcolor') )))
35 ((* else -*))
36 \texttt{\color{outcolor}Out[{\color{outcolor}((( cell.prompt_number )))}]:}((( super() )))
37 ((*- endif -*))
38 ((*- endfor -*))
39 ((* endblock pyout *))
40
41
42 %==============================================================================
43 % Support Macros
44 %==============================================================================
45
46 % Name: draw_prompt
47 % Purpose: Renders an output/input prompt
48 ((* macro add_prompt(text, cell, prompt, prompt_color) -*))
49 ((*- set prompt_number = "" ~ cell.prompt_number -*))
50 ((*- set indention = " " * (prompt_number | length + 7) -*))
51 \begin{Verbatim}[commandchars=\\\{\}]
52 ((( text | add_prompts(first='{\color{' ~ prompt_color ~ '}' ~ prompt ~ '[{\\color{' ~ prompt_color ~ '}' ~ prompt_number ~ '}]:} ', cont=indention) )))
53 \end{Verbatim}
54 ((*- endmacro *))
@@ -0,0 +1,21 b''
1 ((= Python input/output style =))
2
3 ((*- extends 'latex_base.tplx' -*))
4
5 % Custom definitions
6 ((* block definitions *))
7 ((( super() )))
8
9 % Pygments definitions
10 ((( resources.latex.pygments_definitions )))
11 ((* endblock definitions *))
12
13 %===============================================================================
14 % Input
15 %===============================================================================
16
17 ((* block input scoped *))
18 \begin{Verbatim}[commandchars=\\\{\}]
19 ((( cell.input | highlight2latex(strip_verbatim=True) | add_prompts )))
20 \end{Verbatim}
21 ((* endblock input *))
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -4,13 +4,11 b' python:'
4 4 - 2.7
5 5 - 3.3
6 6 before_install:
7 - pip install jinja2
8 7 - easy_install -q pyzmq
8 - pip install jinja2 sphinx pygments tornado
9 9 - sudo apt-get install pandoc
10 - pip install pygments
11 - pip install sphinx
12 10 install:
13 11 - python setup.py install -q
14 12 script:
15 - if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then iptest -w /tmp; fi
16 - if [[ $TRAVIS_PYTHON_VERSION == '3.'* ]]; then iptest3 -w /tmp; fi
13 - if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then cd /tmp; iptest; fi
14 - if [[ $TRAVIS_PYTHON_VERSION == '3.'* ]]; then cd /tmp; iptest3; fi
@@ -28,8 +28,8 b' import sys'
28 28 #-----------------------------------------------------------------------------
29 29
30 30 # Don't forget to also update setup.py when this changes!
31 if sys.version[0:3] < '2.6':
32 raise ImportError('Python Version 2.6 or above is required for IPython.')
31 if sys.version_info[:2] < (2,7):
32 raise ImportError('IPython requires Python Version 2.7 or above.')
33 33
34 34 # Make it easy to import extensions - they are always directly on pythonpath.
35 35 # Therefore, non-IPython modules can be added to extensions directory.
@@ -142,6 +142,9 b' class Application(SingletonConfigurable):'
142 142
143 143 # The version string of this application.
144 144 version = Unicode(u'0.0')
145
146 # the argv used to initialize the application
147 argv = List()
145 148
146 149 # The log level for the application
147 150 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
@@ -454,6 +457,7 b' class Application(SingletonConfigurable):'
454 457 def parse_command_line(self, argv=None):
455 458 """Parse the command line arguments."""
456 459 argv = sys.argv[1:] if argv is None else argv
460 self.argv = list(argv)
457 461
458 462 if argv and argv[0] == 'help':
459 463 # turn `ipython help notebook` into `ipython notebook -h`
@@ -24,11 +24,11 b' Authors'
24 24 #-----------------------------------------------------------------------------
25 25
26 26 import __builtin__ as builtin_mod
27 import argparse
27 28 import os
28 29 import re
29 30 import sys
30 31
31 from IPython.external import argparse
32 32 from IPython.utils.path import filefind, get_ipython_dir
33 33 from IPython.utils import py3compat, warn
34 34 from IPython.utils.encoding import DEFAULT_ENCODING
@@ -1,3 +1,4 b''
1 # coding: utf-8
1 2 """
2 3 Tests for IPython.config.application.Application
3 4
@@ -185,4 +186,8 b' class TestApplication(TestCase):'
185 186 self.assertEqual(app.bar.b, 5)
186 187 self.assertEqual(app.extra_args, ['extra', '--disable', 'args'])
187 188
189 def test_unicode_argv(self):
190 app = MyApp()
191 app.parse_command_line(['ünîcødé'])
192
188 193
@@ -53,7 +53,7 b' from IPython.kernel.connect import ConnectionFileMixin'
53 53 # Network Constants
54 54 #-----------------------------------------------------------------------------
55 55
56 from IPython.utils.localinterfaces import LOCALHOST
56 from IPython.utils.localinterfaces import localhost
57 57
58 58 #-----------------------------------------------------------------------------
59 59 # Globals
@@ -254,7 +254,7 b' class IPythonConsoleApp(ConnectionFileMixin):'
254 254 with open(fname) as f:
255 255 cfg = json.load(f)
256 256 self.transport = cfg.get('transport', 'tcp')
257 self.ip = cfg.get('ip', LOCALHOST)
257 self.ip = cfg.get('ip', localhost())
258 258
259 259 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
260 260 name = channel + '_port'
@@ -282,7 +282,7 b' class IPythonConsoleApp(ConnectionFileMixin):'
282 282 if self.sshkey and not self.sshserver:
283 283 # specifying just the key implies that we are connecting directly
284 284 self.sshserver = ip
285 ip = LOCALHOST
285 ip = localhost()
286 286
287 287 # build connection dict for tunnels:
288 288 info = dict(ip=ip,
@@ -295,7 +295,7 b' class IPythonConsoleApp(ConnectionFileMixin):'
295 295 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
296 296
297 297 # tunnels return a new set of ports, which will be on localhost:
298 self.ip = LOCALHOST
298 self.ip = localhost()
299 299 try:
300 300 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
301 301 except:
@@ -20,17 +20,15 b' Authors:'
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23 import __builtin__
24 import keyword
25 23 import os
26 24 import re
27 25 import sys
28 26
29 27 from IPython.config.configurable import Configurable
30 from IPython.core.splitinput import split_user_input
28 from IPython.core.error import UsageError
31 29
32 30 from IPython.utils.traitlets import List, Instance
33 from IPython.utils.warn import warn, error
31 from IPython.utils.warn import error
34 32
35 33 #-----------------------------------------------------------------------------
36 34 # Utilities
@@ -104,6 +102,70 b' class AliasError(Exception):'
104 102 class InvalidAliasError(AliasError):
105 103 pass
106 104
105 class Alias(object):
106 """Callable object storing the details of one alias.
107
108 Instances are registered as magic functions to allow use of aliases.
109 """
110
111 # Prepare blacklist
112 blacklist = {'cd','popd','pushd','dhist','alias','unalias'}
113
114 def __init__(self, shell, name, cmd):
115 self.shell = shell
116 self.name = name
117 self.cmd = cmd
118 self.nargs = self.validate()
119
120 def validate(self):
121 """Validate the alias, and return the number of arguments."""
122 if self.name in self.blacklist:
123 raise InvalidAliasError("The name %s can't be aliased "
124 "because it is a keyword or builtin." % self.name)
125 try:
126 caller = self.shell.magics_manager.magics['line'][self.name]
127 except KeyError:
128 pass
129 else:
130 if not isinstance(caller, Alias):
131 raise InvalidAliasError("The name %s can't be aliased "
132 "because it is another magic command." % self.name)
133
134 if not (isinstance(self.cmd, basestring)):
135 raise InvalidAliasError("An alias command must be a string, "
136 "got: %r" % self.cmd)
137
138 nargs = self.cmd.count('%s')
139
140 if (nargs > 0) and (self.cmd.find('%l') >= 0):
141 raise InvalidAliasError('The %s and %l specifiers are mutually '
142 'exclusive in alias definitions.')
143
144 return nargs
145
146 def __repr__(self):
147 return "<alias {} for {!r}>".format(self.name, self.cmd)
148
149 def __call__(self, rest=''):
150 cmd = self.cmd
151 nargs = self.nargs
152 # Expand the %l special to be the user's input line
153 if cmd.find('%l') >= 0:
154 cmd = cmd.replace('%l', rest)
155 rest = ''
156 if nargs==0:
157 # Simple, argument-less aliases
158 cmd = '%s %s' % (cmd, rest)
159 else:
160 # Handle aliases with positional arguments
161 args = rest.split(None, nargs)
162 if len(args) < nargs:
163 raise UsageError('Alias <%s> requires %s arguments, %s given.' %
164 (self.name, nargs, len(args)))
165 cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:]))
166
167 self.shell.system(cmd)
168
107 169 #-----------------------------------------------------------------------------
108 170 # Main AliasManager class
109 171 #-----------------------------------------------------------------------------
@@ -116,35 +178,19 b' class AliasManager(Configurable):'
116 178
117 179 def __init__(self, shell=None, **kwargs):
118 180 super(AliasManager, self).__init__(shell=shell, **kwargs)
119 self.alias_table = {}
120 self.exclude_aliases()
181 # For convenient access
182 self.linemagics = self.shell.magics_manager.magics['line']
121 183 self.init_aliases()
122 184
123 def __contains__(self, name):
124 return name in self.alias_table
125
126 @property
127 def aliases(self):
128 return [(item[0], item[1][1]) for item in self.alias_table.iteritems()]
129
130 def exclude_aliases(self):
131 # set of things NOT to alias (keywords, builtins and some magics)
132 no_alias = set(['cd','popd','pushd','dhist','alias','unalias'])
133 no_alias.update(set(keyword.kwlist))
134 no_alias.update(set(__builtin__.__dict__.keys()))
135 self.no_alias = no_alias
136
137 185 def init_aliases(self):
138 # Load default aliases
139 for name, cmd in self.default_aliases:
140 self.soft_define_alias(name, cmd)
141
142 # Load user aliases
143 for name, cmd in self.user_aliases:
186 # Load default & user aliases
187 for name, cmd in self.default_aliases + self.user_aliases:
144 188 self.soft_define_alias(name, cmd)
145 189
146 def clear_aliases(self):
147 self.alias_table.clear()
190 @property
191 def aliases(self):
192 return [(n, func.cmd) for (n, func) in self.linemagics.items()
193 if isinstance(func, Alias)]
148 194
149 195 def soft_define_alias(self, name, cmd):
150 196 """Define an alias, but don't raise on an AliasError."""
@@ -159,104 +205,33 b' class AliasManager(Configurable):'
159 205 This will raise an :exc:`AliasError` if there are validation
160 206 problems.
161 207 """
162 nargs = self.validate_alias(name, cmd)
163 self.alias_table[name] = (nargs, cmd)
208 caller = Alias(shell=self.shell, name=name, cmd=cmd)
209 self.shell.magics_manager.register_function(caller, magic_kind='line',
210 magic_name=name)
164 211
165 def undefine_alias(self, name):
166 if name in self.alias_table:
167 del self.alias_table[name]
212 def get_alias(self, name):
213 """Return an alias, or None if no alias by that name exists."""
214 aname = self.linemagics.get(name, None)
215 return aname if isinstance(aname, Alias) else None
168 216
169 def validate_alias(self, name, cmd):
170 """Validate an alias and return the its number of arguments."""
171 if name in self.no_alias:
172 raise InvalidAliasError("The name %s can't be aliased "
173 "because it is a keyword or builtin." % name)
174 if not (isinstance(cmd, basestring)):
175 raise InvalidAliasError("An alias command must be a string, "
176 "got: %r" % cmd)
177 nargs = cmd.count('%s')
178 if nargs>0 and cmd.find('%l')>=0:
179 raise InvalidAliasError('The %s and %l specifiers are mutually '
180 'exclusive in alias definitions.')
181 return nargs
217 def is_alias(self, name):
218 """Return whether or not a given name has been defined as an alias"""
219 return self.get_alias(name) is not None
182 220
183 def call_alias(self, alias, rest=''):
184 """Call an alias given its name and the rest of the line."""
185 cmd = self.transform_alias(alias, rest)
186 try:
187 self.shell.system(cmd)
188 except:
189 self.shell.showtraceback()
190
191 def transform_alias(self, alias,rest=''):
192 """Transform alias to system command string."""
193 nargs, cmd = self.alias_table[alias]
194
195 if ' ' in cmd and os.path.isfile(cmd):
196 cmd = '"%s"' % cmd
197
198 # Expand the %l special to be the user's input line
199 if cmd.find('%l') >= 0:
200 cmd = cmd.replace('%l', rest)
201 rest = ''
202 if nargs==0:
203 # Simple, argument-less aliases
204 cmd = '%s %s' % (cmd, rest)
221 def undefine_alias(self, name):
222 if self.is_alias(name):
223 del self.linemagics[name]
205 224 else:
206 # Handle aliases with positional arguments
207 args = rest.split(None, nargs)
208 if len(args) < nargs:
209 raise AliasError('Alias <%s> requires %s arguments, %s given.' %
210 (alias, nargs, len(args)))
211 cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:]))
212 return cmd
213
214 def expand_alias(self, line):
215 """ Expand an alias in the command line
225 raise ValueError('%s is not an alias' % name)
216 226
217 Returns the provided command line, possibly with the first word
218 (command) translated according to alias expansion rules.
219
220 [ipython]|16> _ip.expand_aliases("np myfile.txt")
221 <16> 'q:/opt/np/notepad++.exe myfile.txt'
222 """
223
224 pre,_,fn,rest = split_user_input(line)
225 res = pre + self.expand_aliases(fn, rest)
226 return res
227
228 def expand_aliases(self, fn, rest):
229 """Expand multiple levels of aliases:
230
231 if:
232
233 alias foo bar /tmp
234 alias baz foo
235
236 then:
237
238 baz huhhahhei -> bar /tmp huhhahhei
239 """
240 line = fn + " " + rest
241
242 done = set()
243 while 1:
244 pre,_,fn,rest = split_user_input(line, shell_line_split)
245 if fn in self.alias_table:
246 if fn in done:
247 warn("Cyclic alias definition, repeated '%s'" % fn)
248 return ""
249 done.add(fn)
250
251 l2 = self.transform_alias(fn, rest)
252 if l2 == line:
253 break
254 # ls -> ls -F should not recurse forever
255 if l2.split(None,1)[0] == line.split(None,1)[0]:
256 line = l2
257 break
258 line = l2
259 else:
260 break
261
262 return line
227 def clear_aliases(self):
228 for name, cmd in self.aliases:
229 self.undefine_alias(name)
230
231 def retrieve_alias(self, name):
232 """Retrieve the command to which an alias expands."""
233 caller = self.get_alias(name)
234 if caller:
235 return caller.cmd
236 else:
237 raise ValueError('%s is not an alias' % name)
@@ -53,6 +53,7 b' from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instan'
53 53 # aliases and flags
54 54
55 55 base_aliases = {
56 'profile-dir' : 'ProfileDir.location',
56 57 'profile' : 'BaseIPythonApplication.profile',
57 58 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
58 59 'log-level' : 'Application.log_level',
@@ -24,26 +24,26 b' Tip: to use the tab key as the completion key, call'
24 24 Notes:
25 25
26 26 - Exceptions raised by the completer function are *ignored* (and
27 generally cause the completion to fail). This is a feature -- since
28 readline sets the tty device in raw (or cbreak) mode, printing a
29 traceback wouldn't work well without some complicated hoopla to save,
30 reset and restore the tty state.
27 generally cause the completion to fail). This is a feature -- since
28 readline sets the tty device in raw (or cbreak) mode, printing a
29 traceback wouldn't work well without some complicated hoopla to save,
30 reset and restore the tty state.
31 31
32 32 - The evaluation of the NAME.NAME... form may cause arbitrary
33 application defined code to be executed if an object with a
34 __getattr__ hook is found. Since it is the responsibility of the
35 application (or the user) to enable this feature, I consider this an
36 acceptable risk. More complicated expressions (e.g. function calls or
37 indexing operations) are *not* evaluated.
33 application defined code to be executed if an object with a
34 ``__getattr__`` hook is found. Since it is the responsibility of the
35 application (or the user) to enable this feature, I consider this an
36 acceptable risk. More complicated expressions (e.g. function calls or
37 indexing operations) are *not* evaluated.
38 38
39 39 - GNU readline is also used by the built-in functions input() and
40 raw_input(), and thus these also benefit/suffer from the completer
41 features. Clearly an interactive application can benefit by
42 specifying its own completer function and using raw_input() for all
43 its input.
40 raw_input(), and thus these also benefit/suffer from the completer
41 features. Clearly an interactive application can benefit by
42 specifying its own completer function and using raw_input() for all
43 its input.
44 44
45 45 - When the original stdin is not a tty device, GNU readline is never
46 used, and this module (and the readline module) are silently inactive.
46 used, and this module (and the readline module) are silently inactive.
47 47 """
48 48
49 49 #*****************************************************************************
@@ -431,8 +431,7 b' class IPCompleter(Completer):'
431 431 )
432 432
433 433 def __init__(self, shell=None, namespace=None, global_namespace=None,
434 alias_table=None, use_readline=True,
435 config=None, **kwargs):
434 use_readline=True, config=None, **kwargs):
436 435 """IPCompleter() -> completer
437 436
438 437 Return a completer object suitable for use by the readline library
@@ -441,17 +440,14 b' class IPCompleter(Completer):'
441 440 Inputs:
442 441
443 442 - shell: a pointer to the ipython shell itself. This is needed
444 because this completer knows about magic functions, and those can
445 only be accessed via the ipython instance.
443 because this completer knows about magic functions, and those can
444 only be accessed via the ipython instance.
446 445
447 446 - namespace: an optional dict where completions are performed.
448 447
449 448 - global_namespace: secondary optional dict for completions, to
450 handle cases (such as IPython embedded inside functions) where
451 both Python scopes are visible.
452
453 - If alias_table is supplied, it should be a dictionary of aliases
454 to complete.
449 handle cases (such as IPython embedded inside functions) where
450 both Python scopes are visible.
455 451
456 452 use_readline : bool, optional
457 453 If true, use the readline library. This completer can still function
@@ -476,9 +472,6 b' class IPCompleter(Completer):'
476 472 # List where completion matches will be stored
477 473 self.matches = []
478 474 self.shell = shell
479 if alias_table is None:
480 alias_table = {}
481 self.alias_table = alias_table
482 475 # Regexp to split filenames with spaces in them
483 476 self.space_name_re = re.compile(r'([^\\] )')
484 477 # Hold a local ref. to glob.glob for speed
@@ -505,7 +498,6 b' class IPCompleter(Completer):'
505 498 self.matchers = [self.python_matches,
506 499 self.file_matches,
507 500 self.magic_matches,
508 self.alias_matches,
509 501 self.python_func_kw_matches,
510 502 ]
511 503
@@ -628,22 +620,6 b' class IPCompleter(Completer):'
628 620 comp += [ pre+m for m in line_magics if m.startswith(bare_text)]
629 621 return comp
630 622
631 def alias_matches(self, text):
632 """Match internal system aliases"""
633 #print 'Completer->alias_matches:',text,'lb',self.text_until_cursor # dbg
634
635 # if we are not in the first 'item', alias matching
636 # doesn't make sense - unless we are starting with 'sudo' command.
637 main_text = self.text_until_cursor.lstrip()
638 if ' ' in main_text and not main_text.startswith('sudo'):
639 return []
640 text = os.path.expanduser(text)
641 aliases = self.alias_table.keys()
642 if text == '':
643 return aliases
644 else:
645 return [a for a in aliases if a.startswith(text)]
646
647 623 def python_matches(self,text):
648 624 """Match attributes or global python names"""
649 625
@@ -96,19 +96,24 b' class Tracer(object):'
96 96 def __init__(self,colors=None):
97 97 """Create a local debugger instance.
98 98
99 :Parameters:
99 Parameters
100 ----------
100 101
101 - `colors` (None): a string containing the name of the color scheme to
102 use, it must be one of IPython's valid color schemes. If not given, the
103 function will default to the current IPython scheme when running inside
104 IPython, and to 'NoColor' otherwise.
102 colors : str, optional
103 The name of the color scheme to use, it must be one of IPython's
104 valid color schemes. If not given, the function will default to
105 the current IPython scheme when running inside IPython, and to
106 'NoColor' otherwise.
105 107
106 Usage example:
108 Examples
109 --------
110 ::
107 111
108 from IPython.core.debugger import Tracer; debug_here = Tracer()
112 from IPython.core.debugger import Tracer; debug_here = Tracer()
109 113
110 ... later in your code
111 debug_here() # -> will open up the debugger at that point.
114 Later in your code::
115
116 debug_here() # -> will open up the debugger at that point.
112 117
113 118 Once the debugger activates, you can use all of its regular commands to
114 119 step through code, set breakpoints, etc. See the pdb documentation
@@ -508,9 +508,9 b' class Image(DisplayObject):'
508 508 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
509 509
510 510 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
511 """Create a display an PNG/JPEG image given raw data.
511 """Create a PNG/JPEG image object given raw data.
512 512
513 When this object is returned by an expression or passed to the
513 When this object is returned by an input cell or passed to the
514 514 display function, it will result in the image being displayed
515 515 in the frontend.
516 516
@@ -657,35 +657,19 b' class Image(DisplayObject):'
657 657 return unicode(s.split('.')[-1].lower())
658 658
659 659
660 def clear_output(stdout=True, stderr=True, other=True):
660 def clear_output(wait=False):
661 661 """Clear the output of the current cell receiving output.
662 662
663 Optionally, each of stdout/stderr or other non-stream data (e.g. anything
664 produced by display()) can be excluded from the clear event.
665
666 By default, everything is cleared.
667
668 663 Parameters
669 664 ----------
670 stdout : bool [default: True]
671 Whether to clear stdout.
672 stderr : bool [default: True]
673 Whether to clear stderr.
674 other : bool [default: True]
675 Whether to clear everything else that is not stdout/stderr
676 (e.g. figures,images,HTML, any result of display()).
677 """
665 wait : bool [default: false]
666 Wait to clear the output until new output is available to replace it."""
678 667 from IPython.core.interactiveshell import InteractiveShell
679 668 if InteractiveShell.initialized():
680 InteractiveShell.instance().display_pub.clear_output(
681 stdout=stdout, stderr=stderr, other=other,
682 )
669 InteractiveShell.instance().display_pub.clear_output(wait)
683 670 else:
684 671 from IPython.utils import io
685 if stdout:
686 print('\033[2K\r', file=io.stdout, end='')
687 io.stdout.flush()
688 if stderr:
689 print('\033[2K\r', file=io.stderr, end='')
690 io.stderr.flush()
691
672 print('\033[2K\r', file=io.stdout, end='')
673 io.stdout.flush()
674 print('\033[2K\r', file=io.stderr, end='')
675 io.stderr.flush()
@@ -108,14 +108,12 b' class DisplayPublisher(Configurable):'
108 108 if 'text/plain' in data:
109 109 print(data['text/plain'], file=io.stdout)
110 110
111 def clear_output(self, stdout=True, stderr=True, other=True):
111 def clear_output(self, wait=False):
112 112 """Clear the output of the cell receiving output."""
113 if stdout:
114 print('\033[2K\r', file=io.stdout, end='')
115 io.stdout.flush()
116 if stderr:
117 print('\033[2K\r', file=io.stderr, end='')
118 io.stderr.flush()
113 print('\033[2K\r', file=io.stdout, end='')
114 io.stdout.flush()
115 print('\033[2K\r', file=io.stderr, end='')
116 io.stderr.flush()
119 117
120 118
121 119 class CapturingDisplayPublisher(DisplayPublisher):
@@ -125,8 +123,8 b' class CapturingDisplayPublisher(DisplayPublisher):'
125 123 def publish(self, source, data, metadata=None):
126 124 self.outputs.append((source, data, metadata))
127 125
128 def clear_output(self, stdout=True, stderr=True, other=True):
129 super(CapturingDisplayPublisher, self).clear_output(stdout, stderr, other)
126 def clear_output(self, wait=False):
127 super(CapturingDisplayPublisher, self).clear_output(wait)
130 128 if other:
131 129 # empty the list, *do not* reassign a new list
132 130 del self.outputs[:]
@@ -278,6 +278,25 b' _help_end_re = re.compile(r"""(%{0,2}'
278 278 """,
279 279 re.VERBOSE)
280 280
281 # Extra pseudotokens for multiline strings and data structures
282 _MULTILINE_STRING = object()
283 _MULTILINE_STRUCTURE = object()
284
285 def _line_tokens(line):
286 """Helper for has_comment and ends_in_comment_or_string."""
287 readline = StringIO(line).readline
288 toktypes = set()
289 try:
290 for t in generate_tokens(readline):
291 toktypes.add(t[0])
292 except TokenError as e:
293 # There are only two cases where a TokenError is raised.
294 if 'multi-line string' in e.args[0]:
295 toktypes.add(_MULTILINE_STRING)
296 else:
297 toktypes.add(_MULTILINE_STRUCTURE)
298 return toktypes
299
281 300 def has_comment(src):
282 301 """Indicate whether an input line has (i.e. ends in, or is) a comment.
283 302
@@ -293,21 +312,31 b' def has_comment(src):'
293 312 comment : bool
294 313 True if source has a comment.
295 314 """
296 readline = StringIO(src).readline
297 toktypes = set()
298 try:
299 for t in generate_tokens(readline):
300 toktypes.add(t[0])
301 except TokenError:
302 pass
303 return(tokenize2.COMMENT in toktypes)
315 return (tokenize2.COMMENT in _line_tokens(src))
304 316
317 def ends_in_comment_or_string(src):
318 """Indicates whether or not an input line ends in a comment or within
319 a multiline string.
320
321 Parameters
322 ----------
323 src : string
324 A single line input string.
325
326 Returns
327 -------
328 comment : bool
329 True if source ends in a comment or multiline string.
330 """
331 toktypes = _line_tokens(src)
332 return (tokenize2.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
333
305 334
306 335 @StatelessInputTransformer.wrap
307 336 def help_end(line):
308 337 """Translate lines with ?/?? at the end"""
309 338 m = _help_end_re.search(line)
310 if m is None or has_comment(line):
339 if m is None or ends_in_comment_or_string(line):
311 340 return line
312 341 target = m.group(1)
313 342 esc = m.group(3)
@@ -359,8 +388,26 b' def cellmagic(end_on_blank_line=False):'
359 388 line = tpl % (magic_name, first, u'\n'.join(body))
360 389
361 390
362 def _strip_prompts(prompt_re):
363 """Remove matching input prompts from a block of input."""
391 def _strip_prompts(prompt_re, initial_re=None):
392 """Remove matching input prompts from a block of input.
393
394 Parameters
395 ----------
396 prompt_re : regular expression
397 A regular expression matching any input prompt (including continuation)
398 initial_re : regular expression, optional
399 A regular expression matching only the initial prompt, but not continuation.
400 If no initial expression is given, prompt_re will be used everywhere.
401 Used mainly for plain Python prompts, where the continuation prompt
402 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
403
404 If initial_re and prompt_re differ,
405 only initial_re will be tested against the first line.
406 If any prompt is found on the first two lines,
407 prompts will be stripped from the rest of the block.
408 """
409 if initial_re is None:
410 initial_re = prompt_re
364 411 line = ''
365 412 while True:
366 413 line = (yield line)
@@ -368,18 +415,19 b' def _strip_prompts(prompt_re):'
368 415 # First line of cell
369 416 if line is None:
370 417 continue
371 out, n1 = prompt_re.subn('', line, count=1)
418 out, n1 = initial_re.subn('', line, count=1)
372 419 line = (yield out)
373 420
374 # Second line of cell, because people often copy from just after the
375 # first prompt, so we might not see it in the first line.
376 421 if line is None:
377 422 continue
423 # check for any prompt on the second line of the cell,
424 # because people often copy from just after the first prompt,
425 # so we might not see it in the first line.
378 426 out, n2 = prompt_re.subn('', line, count=1)
379 427 line = (yield out)
380 428
381 429 if n1 or n2:
382 # Found the input prompt in the first two lines - check for it in
430 # Found a prompt in the first two lines - check for it in
383 431 # the rest of the cell as well.
384 432 while line is not None:
385 433 line = (yield prompt_re.sub('', line, count=1))
@@ -394,7 +442,8 b' def classic_prompt():'
394 442 """Strip the >>>/... prompts of the Python interactive shell."""
395 443 # FIXME: non-capturing version (?:...) usable?
396 444 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
397 return _strip_prompts(prompt_re)
445 initial_re = re.compile(r'^(>>> ?)')
446 return _strip_prompts(prompt_re, initial_re)
398 447
399 448 @CoroutineInputTransformer.wrap
400 449 def ipy_prompt():
@@ -47,7 +47,6 b' from IPython.core.displayhook import DisplayHook'
47 47 from IPython.core.displaypub import DisplayPublisher
48 48 from IPython.core.error import UsageError
49 49 from IPython.core.extensions import ExtensionManager
50 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
51 50 from IPython.core.formatters import DisplayFormatter
52 51 from IPython.core.history import HistoryManager
53 52 from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGIC2
@@ -186,6 +185,13 b' class ReadlineNoRecord(object):'
186 185 ghi = self.shell.readline.get_history_item
187 186 return [ghi(x) for x in range(start, end)]
188 187
188
189 @undoc
190 class DummyMod(object):
191 """A dummy module used for IPython's interactive module when
192 a namespace must be assigned to the module's __dict__."""
193 pass
194
189 195 #-----------------------------------------------------------------------------
190 196 # Main IPython class
191 197 #-----------------------------------------------------------------------------
@@ -464,7 +470,6 b' class InteractiveShell(SingletonConfigurable):'
464 470 # because it and init_io have to come after init_readline.
465 471 self.init_user_ns()
466 472 self.init_logger()
467 self.init_alias()
468 473 self.init_builtins()
469 474
470 475 # The following was in post_config_initialization
@@ -496,6 +501,7 b' class InteractiveShell(SingletonConfigurable):'
496 501 self.init_displayhook()
497 502 self.init_latextool()
498 503 self.init_magics()
504 self.init_alias()
499 505 self.init_logstart()
500 506 self.init_pdb()
501 507 self.init_extension_manager()
@@ -819,15 +825,18 b' class InteractiveShell(SingletonConfigurable):'
819 825 # Things related to the "main" module
820 826 #-------------------------------------------------------------------------
821 827
822 def new_main_mod(self, filename):
828 def new_main_mod(self, filename, modname):
823 829 """Return a new 'main' module object for user code execution.
824 830
825 831 ``filename`` should be the path of the script which will be run in the
826 832 module. Requests with the same filename will get the same module, with
827 833 its namespace cleared.
828 834
835 ``modname`` should be the module name - normally either '__main__' or
836 the basename of the file without the extension.
837
829 838 When scripts are executed via %run, we must keep a reference to their
830 __main__ module (a FakeModule instance) around so that Python doesn't
839 __main__ module around so that Python doesn't
831 840 clear it, rendering references to module globals useless.
832 841
833 842 This method keeps said reference in a private dict, keyed by the
@@ -840,9 +849,16 b' class InteractiveShell(SingletonConfigurable):'
840 849 try:
841 850 main_mod = self._main_mod_cache[filename]
842 851 except KeyError:
843 main_mod = self._main_mod_cache[filename] = FakeModule()
852 main_mod = self._main_mod_cache[filename] = types.ModuleType(modname,
853 doc="Module created for script run in IPython")
844 854 else:
845 init_fakemod_dict(main_mod)
855 main_mod.__dict__.clear()
856 main_mod.__name__ = modname
857
858 main_mod.__file__ = filename
859 # It seems pydoc (and perhaps others) needs any module instance to
860 # implement a __nonzero__ method
861 main_mod.__nonzero__ = lambda : True
846 862
847 863 return main_mod
848 864
@@ -856,7 +872,7 b' class InteractiveShell(SingletonConfigurable):'
856 872
857 873 In [15]: import IPython
858 874
859 In [16]: m = _ip.new_main_mod(IPython.__file__)
875 In [16]: m = _ip.new_main_mod(IPython.__file__, 'IPython')
860 876
861 877 In [17]: len(_ip._main_mod_cache) > 0
862 878 Out[17]: True
@@ -900,9 +916,9 b' class InteractiveShell(SingletonConfigurable):'
900 916 Keywords:
901 917
902 918 - force(False): by default, this routine checks the instance call_pdb
903 flag and does not actually invoke the debugger if the flag is false.
904 The 'force' option forces the debugger to activate even if the flag
905 is false.
919 flag and does not actually invoke the debugger if the flag is false.
920 The 'force' option forces the debugger to activate even if the flag
921 is false.
906 922 """
907 923
908 924 if not (force or self.call_pdb):
@@ -970,7 +986,7 b' class InteractiveShell(SingletonConfigurable):'
970 986
971 987 # A record of hidden variables we have added to the user namespace, so
972 988 # we can list later only variables defined in actual interactive use.
973 self.user_ns_hidden = set()
989 self.user_ns_hidden = {}
974 990
975 991 # Now that FakeModule produces a real module, we've run into a nasty
976 992 # problem: after script execution (via %run), the module where the user
@@ -1035,9 +1051,6 b' class InteractiveShell(SingletonConfigurable):'
1035 1051 """
1036 1052 if user_module is None and user_ns is not None:
1037 1053 user_ns.setdefault("__name__", "__main__")
1038 class DummyMod(object):
1039 "A dummy module used for IPython's interactive namespace."
1040 pass
1041 1054 user_module = DummyMod()
1042 1055 user_module.__dict__ = user_ns
1043 1056
@@ -1150,7 +1163,7 b' class InteractiveShell(SingletonConfigurable):'
1150 1163
1151 1164 Note that this does not include the displayhook, which also caches
1152 1165 objects from the output."""
1153 return [self.user_ns, self.user_global_ns] + \
1166 return [self.user_ns, self.user_global_ns, self.user_ns_hidden] + \
1154 1167 [m.__dict__ for m in self._main_mod_cache.values()]
1155 1168
1156 1169 def reset(self, new_session=True):
@@ -1301,7 +1314,8 b' class InteractiveShell(SingletonConfigurable):'
1301 1314 # And configure interactive visibility
1302 1315 user_ns_hidden = self.user_ns_hidden
1303 1316 if interactive:
1304 user_ns_hidden.difference_update(vdict)
1317 for name in vdict:
1318 user_ns_hidden.pop(name, None)
1305 1319 else:
1306 1320 user_ns_hidden.update(vdict)
1307 1321
@@ -1321,7 +1335,7 b' class InteractiveShell(SingletonConfigurable):'
1321 1335 for name, obj in variables.iteritems():
1322 1336 if name in self.user_ns and self.user_ns[name] is obj:
1323 1337 del self.user_ns[name]
1324 self.user_ns_hidden.discard(name)
1338 self.user_ns_hidden.pop(name, None)
1325 1339
1326 1340 #-------------------------------------------------------------------------
1327 1341 # Things related to object introspection
@@ -1349,9 +1363,7 b' class InteractiveShell(SingletonConfigurable):'
1349 1363 namespaces = [ ('Interactive', self.user_ns),
1350 1364 ('Interactive (global)', self.user_global_ns),
1351 1365 ('Python builtin', builtin_mod.__dict__),
1352 ('Alias', self.alias_manager.alias_table),
1353 1366 ]
1354 alias_ns = self.alias_manager.alias_table
1355 1367
1356 1368 # initialize results to 'null'
1357 1369 found = False; obj = None; ospace = None; ds = None;
@@ -1390,8 +1402,6 b' class InteractiveShell(SingletonConfigurable):'
1390 1402 # If we finish the for loop (no break), we got all members
1391 1403 found = True
1392 1404 ospace = nsname
1393 if ns == alias_ns:
1394 isalias = True
1395 1405 break # namespace loop
1396 1406
1397 1407 # Try to see if it's magic
@@ -1926,7 +1936,6 b' class InteractiveShell(SingletonConfigurable):'
1926 1936 self.Completer = IPCompleter(shell=self,
1927 1937 namespace=self.user_ns,
1928 1938 global_namespace=self.user_global_ns,
1929 alias_table=self.alias_manager.alias_table,
1930 1939 use_readline=self.has_readline,
1931 1940 parent=self,
1932 1941 )
@@ -2291,7 +2300,6 b' class InteractiveShell(SingletonConfigurable):'
2291 2300 def init_alias(self):
2292 2301 self.alias_manager = AliasManager(shell=self, parent=self)
2293 2302 self.configurables.append(self.alias_manager)
2294 self.ns_table['alias'] = self.alias_manager.alias_table,
2295 2303
2296 2304 #-------------------------------------------------------------------------
2297 2305 # Things related to extensions
@@ -2973,7 +2981,7 b' class InteractiveShell(SingletonConfigurable):'
2973 2981 Optional inputs:
2974 2982
2975 2983 - data(None): if data is given, it gets written out to the temp file
2976 immediately, and the file is closed again."""
2984 immediately, and the file is closed again."""
2977 2985
2978 2986 filename = tempfile.mktemp('.py', prefix)
2979 2987 self.tempfiles.append(filename)
@@ -3016,13 +3024,14 b' class InteractiveShell(SingletonConfigurable):'
3016 3024
3017 3025 Optional Parameters:
3018 3026 - raw(False): by default, the processed input is used. If this is
3019 true, the raw input history is used instead.
3027 true, the raw input history is used instead.
3020 3028
3021 3029 Note that slices can be called with two notations:
3022 3030
3023 3031 N:M -> standard python form, means including items N...(M-1).
3024 3032
3025 N-M -> include items N..M (closed endpoint)."""
3033 N-M -> include items N..M (closed endpoint).
3034 """
3026 3035 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
3027 3036 return "\n".join(x for _, _, x in lines)
3028 3037
@@ -171,11 +171,11 b' which already exists. But you must first start the logging process with'
171 171 Inputs:
172 172
173 173 - line_mod: possibly modified input, such as the transformations made
174 by input prefilters or input handlers of various kinds. This should
175 always be valid Python.
174 by input prefilters or input handlers of various kinds. This should
175 always be valid Python.
176 176
177 - line_ori: unmodified input line from the user. This is not
178 necessarily valid Python.
177 - line_ori: unmodified input line from the user. This is not
178 necessarily valid Python.
179 179 """
180 180
181 181 # Write the log line, but decide which one according to the
@@ -489,11 +489,11 b' class Magics(object):'
489 489 MUST:
490 490
491 491 - Use the method decorators `@line_magic` and `@cell_magic` to decorate
492 individual methods as magic functions, AND
492 individual methods as magic functions, AND
493 493
494 494 - Use the class decorator `@magics_class` to ensure that the magic
495 methods are properly registered at the instance level upon instance
496 initialization.
495 methods are properly registered at the instance level upon instance
496 initialization.
497 497
498 498 See :mod:`magic_functions` for examples of actual implementation classes.
499 499 """
@@ -50,9 +50,9 b' Inheritance diagram:'
50 50 #
51 51 # The full license is in the file COPYING.txt, distributed with this software.
52 52 #-----------------------------------------------------------------------------
53 import argparse
53 54
54 55 # Our own imports
55 from IPython.external import argparse
56 56 from IPython.core.error import UsageError
57 57 from IPython.utils.process import arg_split
58 58 from IPython.utils.text import dedent
@@ -56,6 +56,37 b' from IPython.utils.warn import warn, error'
56 56 # Magic implementation classes
57 57 #-----------------------------------------------------------------------------
58 58
59
60 class TimeitResult(object):
61 """
62 Object returned by the timeit magic with info about the run.
63
64 Contain the following attributes :
65
66 loops: (int) number of loop done per measurement
67 repeat: (int) number of time the mesurement has been repeated
68 best: (float) best execusion time / number
69 all_runs: (list of float) execusion time of each run (in s)
70 compile_time: (float) time of statement compilation (s)
71
72 """
73
74 def __init__(self, loops, repeat, best, all_runs, compile_time, precision):
75 self.loops = loops
76 self.repeat = repeat
77 self.best = best
78 self.all_runs = all_runs
79 self.compile_time = compile_time
80 self._precision = precision
81
82 def _repr_pretty_(self, p , cycle):
83 unic = u"%d loops, best of %d: %s per loop" % (self.loops, self.repeat,
84 _format_time(self.best, self._precision))
85 p.text(u'<TimeitResult : '+unic+u'>')
86
87
88
89
59 90 @magics_class
60 91 class ExecutionMagics(Magics):
61 92 """Magics related to code execution, debugging, profiling, etc.
@@ -102,75 +133,84 b' python-profiler package from non-free.""")'
102 133
103 134 Options:
104 135
105 -l <limit>: you can place restrictions on what or how much of the
106 profile gets printed. The limit value can be:
107
108 * A string: only information for function names containing this string
109 is printed.
110
111 * An integer: only these many lines are printed.
112
113 * A float (between 0 and 1): this fraction of the report is printed
114 (for example, use a limit of 0.4 to see the topmost 40% only).
115
116 You can combine several limits with repeated use of the option. For
117 example, '-l __init__ -l 5' will print only the topmost 5 lines of
118 information about class constructors.
119
120 -r: return the pstats.Stats object generated by the profiling. This
121 object has all the information about the profile in it, and you can
122 later use it for further analysis or in other functions.
123
124 -s <key>: sort profile by given key. You can provide more than one key
125 by using the option several times: '-s key1 -s key2 -s key3...'. The
126 default sorting key is 'time'.
127
128 The following is copied verbatim from the profile documentation
129 referenced below:
130
131 When more than one key is provided, additional keys are used as
132 secondary criteria when the there is equality in all keys selected
133 before them.
134
135 Abbreviations can be used for any key names, as long as the
136 abbreviation is unambiguous. The following are the keys currently
137 defined:
138
139 Valid Arg Meaning
140 "calls" call count
141 "cumulative" cumulative time
142 "file" file name
143 "module" file name
144 "pcalls" primitive call count
145 "line" line number
146 "name" function name
147 "nfl" name/file/line
148 "stdname" standard name
149 "time" internal time
150
151 Note that all sorts on statistics are in descending order (placing
152 most time consuming items first), where as name, file, and line number
153 searches are in ascending order (i.e., alphabetical). The subtle
154 distinction between "nfl" and "stdname" is that the standard name is a
155 sort of the name as printed, which means that the embedded line
156 numbers get compared in an odd way. For example, lines 3, 20, and 40
157 would (if the file names were the same) appear in the string order
158 "20" "3" and "40". In contrast, "nfl" does a numeric compare of the
159 line numbers. In fact, sort_stats("nfl") is the same as
160 sort_stats("name", "file", "line").
161
162 -T <filename>: save profile results as shown on screen to a text
163 file. The profile is still shown on screen.
164
165 -D <filename>: save (via dump_stats) profile statistics to given
166 filename. This data is in a format understood by the pstats module, and
167 is generated by a call to the dump_stats() method of profile
168 objects. The profile is still shown on screen.
169
170 -q: suppress output to the pager. Best used with -T and/or -D above.
136 -l <limit>
137 you can place restrictions on what or how much of the
138 profile gets printed. The limit value can be:
139
140 * A string: only information for function names containing this string
141 is printed.
142
143 * An integer: only these many lines are printed.
144
145 * A float (between 0 and 1): this fraction of the report is printed
146 (for example, use a limit of 0.4 to see the topmost 40% only).
147
148 You can combine several limits with repeated use of the option. For
149 example, ``-l __init__ -l 5`` will print only the topmost 5 lines of
150 information about class constructors.
151
152 -r
153 return the pstats.Stats object generated by the profiling. This
154 object has all the information about the profile in it, and you can
155 later use it for further analysis or in other functions.
156
157 -s <key>
158 sort profile by given key. You can provide more than one key
159 by using the option several times: '-s key1 -s key2 -s key3...'. The
160 default sorting key is 'time'.
161
162 The following is copied verbatim from the profile documentation
163 referenced below:
164
165 When more than one key is provided, additional keys are used as
166 secondary criteria when the there is equality in all keys selected
167 before them.
168
169 Abbreviations can be used for any key names, as long as the
170 abbreviation is unambiguous. The following are the keys currently
171 defined:
172
173 ============ =====================
174 Valid Arg Meaning
175 ============ =====================
176 "calls" call count
177 "cumulative" cumulative time
178 "file" file name
179 "module" file name
180 "pcalls" primitive call count
181 "line" line number
182 "name" function name
183 "nfl" name/file/line
184 "stdname" standard name
185 "time" internal time
186 ============ =====================
187
188 Note that all sorts on statistics are in descending order (placing
189 most time consuming items first), where as name, file, and line number
190 searches are in ascending order (i.e., alphabetical). The subtle
191 distinction between "nfl" and "stdname" is that the standard name is a
192 sort of the name as printed, which means that the embedded line
193 numbers get compared in an odd way. For example, lines 3, 20, and 40
194 would (if the file names were the same) appear in the string order
195 "20" "3" and "40". In contrast, "nfl" does a numeric compare of the
196 line numbers. In fact, sort_stats("nfl") is the same as
197 sort_stats("name", "file", "line").
198
199 -T <filename>
200 save profile results as shown on screen to a text
201 file. The profile is still shown on screen.
202
203 -D <filename>
204 save (via dump_stats) profile statistics to given
205 filename. This data is in a format understood by the pstats module, and
206 is generated by a call to the dump_stats() method of profile
207 objects. The profile is still shown on screen.
208
209 -q
210 suppress output to the pager. Best used with -T and/or -D above.
171 211
172 212 If you want to run complete programs under the profiler's control, use
173 '%run -p [prof_opts] filename.py [args to program]' where prof_opts
213 ``%run -p [prof_opts] filename.py [args to program]`` where prof_opts
174 214 contains profiler specific options as described here.
175 215
176 216 You can read the complete documentation for the profile module with::
@@ -362,7 +402,8 b' python-profiler package from non-free.""")'
362 402 file_finder=get_py_filename):
363 403 """Run the named file inside IPython as a program.
364 404
365 Usage:
405 Usage::
406
366 407 %run [-n -i -e -G]
367 408 [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )]
368 409 ( -m mod | file ) [args]
@@ -371,14 +412,13 b' python-profiler package from non-free.""")'
371 412 the program (put in sys.argv). Then, control returns to IPython's
372 413 prompt.
373 414
374 This is similar to running at a system prompt:\\
375 $ python file args\\
415 This is similar to running at a system prompt ``python file args``,
376 416 but with the advantage of giving you IPython's tracebacks, and of
377 417 loading all variables into your interactive namespace for further use
378 418 (unless -p is used, see below).
379 419
380 420 The file is executed in a namespace initially consisting only of
381 __name__=='__main__' and sys.argv constructed as indicated. It thus
421 ``__name__=='__main__'`` and sys.argv constructed as indicated. It thus
382 422 sees its environment as if it were being run as a stand-alone program
383 423 (except for sharing global objects such as previously imported
384 424 modules). But after execution, the IPython interactive namespace gets
@@ -390,33 +430,37 b' python-profiler package from non-free.""")'
390 430 '*', '?', '[seq]' and '[!seq]' can be used. Additionally,
391 431 tilde '~' will be expanded into user's home directory. Unlike
392 432 real shells, quotation does not suppress expansions. Use
393 *two* back slashes (e.g., '\\\\*') to suppress expansions.
433 *two* back slashes (e.g. ``\\\\*``) to suppress expansions.
394 434 To completely disable these expansions, you can use -G flag.
395 435
396 436 Options:
397 437
398 -n: __name__ is NOT set to '__main__', but to the running file's name
399 without extension (as python does under import). This allows running
400 scripts and reloading the definitions in them without calling code
401 protected by an ' if __name__ == "__main__" ' clause.
402
403 -i: run the file in IPython's namespace instead of an empty one. This
404 is useful if you are experimenting with code written in a text editor
405 which depends on variables defined interactively.
406
407 -e: ignore sys.exit() calls or SystemExit exceptions in the script
408 being run. This is particularly useful if IPython is being used to
409 run unittests, which always exit with a sys.exit() call. In such
410 cases you are interested in the output of the test results, not in
411 seeing a traceback of the unittest module.
412
413 -t: print timing information at the end of the run. IPython will give
414 you an estimated CPU time consumption for your script, which under
415 Unix uses the resource module to avoid the wraparound problems of
416 time.clock(). Under Unix, an estimate of time spent on system tasks
417 is also given (for Windows platforms this is reported as 0.0).
418
419 If -t is given, an additional -N<N> option can be given, where <N>
438 -n
439 __name__ is NOT set to '__main__', but to the running file's name
440 without extension (as python does under import). This allows running
441 scripts and reloading the definitions in them without calling code
442 protected by an ``if __name__ == "__main__"`` clause.
443
444 -i
445 run the file in IPython's namespace instead of an empty one. This
446 is useful if you are experimenting with code written in a text editor
447 which depends on variables defined interactively.
448
449 -e
450 ignore sys.exit() calls or SystemExit exceptions in the script
451 being run. This is particularly useful if IPython is being used to
452 run unittests, which always exit with a sys.exit() call. In such
453 cases you are interested in the output of the test results, not in
454 seeing a traceback of the unittest module.
455
456 -t
457 print timing information at the end of the run. IPython will give
458 you an estimated CPU time consumption for your script, which under
459 Unix uses the resource module to avoid the wraparound problems of
460 time.clock(). Under Unix, an estimate of time spent on system tasks
461 is also given (for Windows platforms this is reported as 0.0).
462
463 If -t is given, an additional ``-N<N>`` option can be given, where <N>
420 464 must be an integer indicating how many times you want the script to
421 465 run. The final timing report will include total and per run results.
422 466
@@ -424,74 +468,78 b' python-profiler package from non-free.""")'
424 468
425 469 In [1]: run -t uniq_stable
426 470
427 IPython CPU timings (estimated):\\
428 User : 0.19597 s.\\
429 System: 0.0 s.\\
471 IPython CPU timings (estimated):
472 User : 0.19597 s.
473 System: 0.0 s.
430 474
431 475 In [2]: run -t -N5 uniq_stable
432 476
433 IPython CPU timings (estimated):\\
434 Total runs performed: 5\\
435 Times : Total Per run\\
436 User : 0.910862 s, 0.1821724 s.\\
477 IPython CPU timings (estimated):
478 Total runs performed: 5
479 Times : Total Per run
480 User : 0.910862 s, 0.1821724 s.
437 481 System: 0.0 s, 0.0 s.
438 482
439 -d: run your program under the control of pdb, the Python debugger.
440 This allows you to execute your program step by step, watch variables,
441 etc. Internally, what IPython does is similar to calling:
483 -d
484 run your program under the control of pdb, the Python debugger.
485 This allows you to execute your program step by step, watch variables,
486 etc. Internally, what IPython does is similar to calling::
442 487
443 pdb.run('execfile("YOURFILENAME")')
488 pdb.run('execfile("YOURFILENAME")')
444 489
445 with a breakpoint set on line 1 of your file. You can change the line
446 number for this automatic breakpoint to be <N> by using the -bN option
447 (where N must be an integer). For example::
490 with a breakpoint set on line 1 of your file. You can change the line
491 number for this automatic breakpoint to be <N> by using the -bN option
492 (where N must be an integer). For example::
448 493
449 %run -d -b40 myscript
494 %run -d -b40 myscript
450 495
451 will set the first breakpoint at line 40 in myscript.py. Note that
452 the first breakpoint must be set on a line which actually does
453 something (not a comment or docstring) for it to stop execution.
496 will set the first breakpoint at line 40 in myscript.py. Note that
497 the first breakpoint must be set on a line which actually does
498 something (not a comment or docstring) for it to stop execution.
454 499
455 Or you can specify a breakpoint in a different file::
500 Or you can specify a breakpoint in a different file::
456 501
457 %run -d -b myotherfile.py:20 myscript
502 %run -d -b myotherfile.py:20 myscript
458 503
459 When the pdb debugger starts, you will see a (Pdb) prompt. You must
460 first enter 'c' (without quotes) to start execution up to the first
461 breakpoint.
504 When the pdb debugger starts, you will see a (Pdb) prompt. You must
505 first enter 'c' (without quotes) to start execution up to the first
506 breakpoint.
462 507
463 Entering 'help' gives information about the use of the debugger. You
464 can easily see pdb's full documentation with "import pdb;pdb.help()"
465 at a prompt.
508 Entering 'help' gives information about the use of the debugger. You
509 can easily see pdb's full documentation with "import pdb;pdb.help()"
510 at a prompt.
466 511
467 -p: run program under the control of the Python profiler module (which
468 prints a detailed report of execution times, function calls, etc).
512 -p
513 run program under the control of the Python profiler module (which
514 prints a detailed report of execution times, function calls, etc).
469 515
470 You can pass other options after -p which affect the behavior of the
471 profiler itself. See the docs for %prun for details.
516 You can pass other options after -p which affect the behavior of the
517 profiler itself. See the docs for %prun for details.
472 518
473 In this mode, the program's variables do NOT propagate back to the
474 IPython interactive namespace (because they remain in the namespace
475 where the profiler executes them).
519 In this mode, the program's variables do NOT propagate back to the
520 IPython interactive namespace (because they remain in the namespace
521 where the profiler executes them).
476 522
477 Internally this triggers a call to %prun, see its documentation for
478 details on the options available specifically for profiling.
523 Internally this triggers a call to %prun, see its documentation for
524 details on the options available specifically for profiling.
479 525
480 526 There is one special usage for which the text above doesn't apply:
481 527 if the filename ends with .ipy, the file is run as ipython script,
482 528 just as if the commands were written on IPython prompt.
483 529
484 -m: specify module name to load instead of script path. Similar to
485 the -m option for the python interpreter. Use this option last if you
486 want to combine with other %run options. Unlike the python interpreter
487 only source modules are allowed no .pyc or .pyo files.
488 For example::
530 -m
531 specify module name to load instead of script path. Similar to
532 the -m option for the python interpreter. Use this option last if you
533 want to combine with other %run options. Unlike the python interpreter
534 only source modules are allowed no .pyc or .pyo files.
535 For example::
489 536
490 %run -m example
537 %run -m example
491 538
492 will run the example module.
539 will run the example module.
493 540
494 -G: disable shell-like glob expansion of arguments.
541 -G
542 disable shell-like glob expansion of arguments.
495 543
496 544 """
497 545
@@ -550,6 +598,11 b' python-profiler package from non-free.""")'
550 598 __name__save = self.shell.user_ns['__name__']
551 599 prog_ns['__name__'] = '__main__'
552 600 main_mod = self.shell.user_module
601
602 # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
603 # set the __file__ global in the script's namespace
604 # TK: Is this necessary in interactive mode?
605 prog_ns['__file__'] = filename
553 606 else:
554 607 # Run in a fresh, empty namespace
555 608 if 'n' in opts:
@@ -560,13 +613,8 b' python-profiler package from non-free.""")'
560 613 # The shell MUST hold a reference to prog_ns so after %run
561 614 # exits, the python deletion mechanism doesn't zero it out
562 615 # (leaving dangling references). See interactiveshell for details
563 main_mod = self.shell.new_main_mod(filename)
616 main_mod = self.shell.new_main_mod(filename, name)
564 617 prog_ns = main_mod.__dict__
565 prog_ns['__name__'] = name
566
567 # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
568 # set the __file__ global in the script's namespace
569 prog_ns['__file__'] = filename
570 618
571 619 # pickle fix. See interactiveshell for an explanation. But we need to
572 620 # make sure that, if we overwrite __main__, we replace it at the end
@@ -786,9 +834,9 b' python-profiler package from non-free.""")'
786 834 """Time execution of a Python statement or expression
787 835
788 836 Usage, in line mode:
789 %timeit [-n<N> -r<R> [-t|-c]] statement
837 %timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] statement
790 838 or in cell mode:
791 %%timeit [-n<N> -r<R> [-t|-c]] setup_code
839 %%timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] setup_code
792 840 code
793 841 code...
794 842
@@ -819,6 +867,11 b' python-profiler package from non-free.""")'
819 867 -p<P>: use a precision of <P> digits to display the timing result.
820 868 Default: 3
821 869
870 -q: Quiet, do not print result.
871
872 -o: return a TimeitResult that can be stored in a variable to inspect
873 the result in more details.
874
822 875
823 876 Examples
824 877 --------
@@ -851,7 +904,7 b' python-profiler package from non-free.""")'
851 904
852 905 import timeit
853 906
854 opts, stmt = self.parse_options(line,'n:r:tcp:',
907 opts, stmt = self.parse_options(line,'n:r:tcp:qo',
855 908 posix=False, strict=False)
856 909 if stmt == "" and cell is None:
857 910 return
@@ -860,6 +913,8 b' python-profiler package from non-free.""")'
860 913 number = int(getattr(opts, "n", 0))
861 914 repeat = int(getattr(opts, "r", timeit.default_repeat))
862 915 precision = int(getattr(opts, "p", 3))
916 quiet = 'q' in opts
917 return_result = 'o' in opts
863 918 if hasattr(opts, "t"):
864 919 timefunc = time.time
865 920 if hasattr(opts, "c"):
@@ -931,13 +986,15 b' python-profiler package from non-free.""")'
931 986 if timer.timeit(number) >= 0.2:
932 987 break
933 988 number *= 10
934
935 best = min(timer.repeat(repeat, number)) / number
936
937 print u"%d loops, best of %d: %s per loop" % (number, repeat,
938 _format_time(best, precision))
939 if tc > tc_min:
940 print "Compiler time: %.2f s" % tc
989 all_runs = timer.repeat(repeat, number)
990 best = min(all_runs) / number
991 if not quiet :
992 print u"%d loops, best of %d: %s per loop" % (number, repeat,
993 _format_time(best, precision))
994 if tc > tc_min:
995 print "Compiler time: %.2f s" % tc
996 if return_result:
997 return TimeitResult(number, repeat, best, all_runs, tc, precision)
941 998
942 999 @skip_doctest
943 1000 @needs_local_scope
@@ -42,34 +42,48 b' class LoggingMagics(Magics):'
42 42 history up to that point and then continues logging.
43 43
44 44 %logstart takes a second optional parameter: logging mode. This can be one
45 of (note that the modes are given unquoted):\\
46 append: well, that says it.\\
47 backup: rename (if exists) to name~ and start name.\\
48 global: single logfile in your home dir, appended to.\\
49 over : overwrite existing log.\\
50 rotate: create rotating logs name.1~, name.2~, etc.
45 of (note that the modes are given unquoted):
46
47 append
48 Keep logging at the end of any existing file.
49
50 backup
51 Rename any existing file to name~ and start name.
52
53 global
54 Append to a single logfile in your home directory.
55
56 over
57 Overwrite any existing log.
58
59 rotate
60 Create rotating logs: name.1~, name.2~, etc.
51 61
52 62 Options:
53 63
54 -o: log also IPython's output. In this mode, all commands which
55 generate an Out[NN] prompt are recorded to the logfile, right after
56 their corresponding input line. The output lines are always
57 prepended with a '#[Out]# ' marker, so that the log remains valid
58 Python code.
64 -o
65 log also IPython's output. In this mode, all commands which
66 generate an Out[NN] prompt are recorded to the logfile, right after
67 their corresponding input line. The output lines are always
68 prepended with a '#[Out]# ' marker, so that the log remains valid
69 Python code.
59 70
60 71 Since this marker is always the same, filtering only the output from
61 72 a log is very easy, using for example a simple awk call::
62 73
63 74 awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py
64 75
65 -r: log 'raw' input. Normally, IPython's logs contain the processed
66 input, so that user lines are logged in their final form, converted
67 into valid Python. For example, %Exit is logged as
68 _ip.magic("Exit"). If the -r flag is given, all input is logged
69 exactly as typed, with no transformations applied.
70
71 -t: put timestamps before each input line logged (these are put in
72 comments)."""
76 -r
77 log 'raw' input. Normally, IPython's logs contain the processed
78 input, so that user lines are logged in their final form, converted
79 into valid Python. For example, %Exit is logged as
80 _ip.magic("Exit"). If the -r flag is given, all input is logged
81 exactly as typed, with no transformations applied.
82
83 -t
84 put timestamps before each input line logged (these are put in
85 comments).
86 """
73 87
74 88 opts,par = self.parse_options(parameter_s,'ort')
75 89 log_output = 'o' in opts
@@ -265,9 +265,10 b' class NamespaceMagics(Magics):'
265 265
266 266 user_ns = self.shell.user_ns
267 267 user_ns_hidden = self.shell.user_ns_hidden
268 nonmatching = object() # This can never be in user_ns
268 269 out = [ i for i in user_ns
269 270 if not i.startswith('_') \
270 and not i in user_ns_hidden ]
271 and (user_ns[i] is not user_ns_hidden.get(i, nonmatching)) ]
271 272
272 273 typelist = parameter_s.split()
273 274 if typelist:
@@ -353,10 +354,10 b' class NamespaceMagics(Magics):'
353 354 - For {},[],(): their length.
354 355
355 356 - For numpy arrays, a summary with shape, number of
356 elements, typecode and size in memory.
357 elements, typecode and size in memory.
357 358
358 359 - Everything else: a string representation, snipping their middle if
359 too long.
360 too long.
360 361
361 362 Examples
362 363 --------
@@ -26,6 +26,7 b' from pprint import pformat'
26 26 from IPython.core import magic_arguments
27 27 from IPython.core import oinspect
28 28 from IPython.core import page
29 from IPython.core.alias import AliasError, Alias
29 30 from IPython.core.error import UsageError
30 31 from IPython.core.magic import (
31 32 Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic
@@ -113,10 +114,14 b' class OSMagics(Magics):'
113 114 # Now try to define a new one
114 115 try:
115 116 alias,cmd = par.split(None, 1)
116 except:
117 print oinspect.getdoc(self.alias)
118 else:
119 self.shell.alias_manager.soft_define_alias(alias, cmd)
117 except TypeError:
118 print(oinspect.getdoc(self.alias))
119 return
120
121 try:
122 self.shell.alias_manager.define_alias(alias, cmd)
123 except AliasError as e:
124 print(e)
120 125 # end magic_alias
121 126
122 127 @line_magic
@@ -124,7 +129,12 b' class OSMagics(Magics):'
124 129 """Remove an alias"""
125 130
126 131 aname = parameter_s.strip()
127 self.shell.alias_manager.undefine_alias(aname)
132 try:
133 self.shell.alias_manager.undefine_alias(aname)
134 except ValueError as e:
135 print(e)
136 return
137
128 138 stored = self.shell.db.get('stored_aliases', {} )
129 139 if aname in stored:
130 140 print "Removing %stored alias",aname
@@ -182,7 +192,7 b' class OSMagics(Magics):'
182 192 try:
183 193 # Removes dots from the name since ipython
184 194 # will assume names with dots to be python.
185 if ff not in self.shell.alias_manager:
195 if not self.shell.alias_manager.is_alias(ff):
186 196 self.shell.alias_manager.define_alias(
187 197 ff.replace('.',''), ff)
188 198 except InvalidAliasError:
@@ -190,7 +200,7 b' class OSMagics(Magics):'
190 200 else:
191 201 syscmdlist.append(ff)
192 202 else:
193 no_alias = self.shell.alias_manager.no_alias
203 no_alias = Alias.blacklist
194 204 for pdir in path:
195 205 os.chdir(pdir)
196 206 for ff in os.listdir(pdir):
@@ -156,8 +156,8 b' def getsource(obj,is_binary=False):'
156 156 Optional inputs:
157 157
158 158 - is_binary: whether the object is known to come from a binary source.
159 This implementation will skip returning any output for binary objects, but
160 custom extractors may know how to meaningfully process them."""
159 This implementation will skip returning any output for binary objects, but
160 custom extractors may know how to meaningfully process them."""
161 161
162 162 if is_binary:
163 163 return None
@@ -545,7 +545,7 b' class Inspector:'
545 545 - formatter: special formatter for docstrings (see pdoc)
546 546
547 547 - info: a structure with some information fields which may have been
548 precomputed already.
548 precomputed already.
549 549
550 550 - detail_level: if set to 1, more information is given.
551 551 """
@@ -609,7 +609,7 b' class Inspector:'
609 609 - formatter: special formatter for docstrings (see pdoc)
610 610
611 611 - info: a structure with some information fields which may have been
612 precomputed already.
612 precomputed already.
613 613
614 614 - detail_level: if set to 1, more information is given.
615 615 """
@@ -829,8 +829,8 b' class Inspector:'
829 829 Arguments:
830 830
831 831 - pattern: string containing shell-like wildcards to use in namespace
832 searches and optionally a type specification to narrow the search to
833 objects of that type.
832 searches and optionally a type specification to narrow the search to
833 objects of that type.
834 834
835 835 - ns_table: dict of name->namespaces for search.
836 836
@@ -841,7 +841,7 b' class Inspector:'
841 841 - ignore_case(False): make the search case-insensitive.
842 842
843 843 - show_all(False): show all names, including those starting with
844 underscores.
844 underscores.
845 845 """
846 846 #print 'ps pattern:<%r>' % pattern # dbg
847 847
@@ -325,9 +325,11 b" def snip_print(str,width = 75,print_full = 0,header = ''):"
325 325 """Print a string snipping the midsection to fit in width.
326 326
327 327 print_full: mode control:
328
328 329 - 0: only snip long strings
329 330 - 1: send to page() directly.
330 331 - 2: snip long strings and ask for full length viewing with page()
332
331 333 Return 1 if snipping was necessary, 0 otherwise."""
332 334
333 335 if print_full == 1:
@@ -488,22 +488,6 b' class AutoMagicChecker(PrefilterChecker):'
488 488 return self.prefilter_manager.get_handler_by_name('magic')
489 489
490 490
491 class AliasChecker(PrefilterChecker):
492
493 priority = Integer(800, config=True)
494
495 def check(self, line_info):
496 "Check if the initital identifier on the line is an alias."
497 # Note: aliases can not contain '.'
498 head = line_info.ifun.split('.',1)[0]
499 if line_info.ifun not in self.shell.alias_manager \
500 or head not in self.shell.alias_manager \
501 or is_shadowed(head, self.shell):
502 return None
503
504 return self.prefilter_manager.get_handler_by_name('alias')
505
506
507 491 class PythonOpsChecker(PrefilterChecker):
508 492
509 493 priority = Integer(900, config=True)
@@ -591,20 +575,6 b' class PrefilterHandler(Configurable):'
591 575 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
592 576
593 577
594 class AliasHandler(PrefilterHandler):
595
596 handler_name = Unicode('alias')
597
598 def handle(self, line_info):
599 """Handle alias input lines. """
600 transformed = self.shell.alias_manager.expand_aliases(line_info.ifun,line_info.the_rest)
601 # pre is needed, because it carries the leading whitespace. Otherwise
602 # aliases won't work in indented sections.
603 line_out = '%sget_ipython().system(%r)' % (line_info.pre_whitespace, transformed)
604
605 return line_out
606
607
608 578 class MacroHandler(PrefilterHandler):
609 579 handler_name = Unicode("macro")
610 580
@@ -730,14 +700,12 b' _default_checkers = ['
730 700 IPyAutocallChecker,
731 701 AssignmentChecker,
732 702 AutoMagicChecker,
733 AliasChecker,
734 703 PythonOpsChecker,
735 704 AutocallChecker
736 705 ]
737 706
738 707 _default_handlers = [
739 708 PrefilterHandler,
740 AliasHandler,
741 709 MacroHandler,
742 710 MagicHandler,
743 711 AutoHandler,
@@ -11,7 +11,7 b' Authors:'
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 # Copyright (C) 2008-2011 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
15 15 #
16 16 # Distributed under the terms of the BSD License. The full license is in
17 17 # the file COPYING, distributed as part of this software.
@@ -28,6 +28,7 b' from IPython.core.application import ('
28 28 BaseIPythonApplication, base_flags
29 29 )
30 30 from IPython.core.profiledir import ProfileDir
31 from IPython.utils.importstring import import_item
31 32 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
32 33 from IPython.utils.traitlets import Unicode, Bool, Dict
33 34
@@ -206,6 +207,8 b' class ProfileCreate(BaseIPythonApplication):'
206 207 description = create_help
207 208 examples = _create_examples
208 209 auto_create = Bool(True, config=False)
210 def _log_format_default(self):
211 return "[%(name)s] %(message)s"
209 212
210 213 def _copy_config_files_default(self):
211 214 return True
@@ -234,31 +237,32 b' class ProfileCreate(BaseIPythonApplication):'
234 237 flags = Dict(create_flags)
235 238
236 239 classes = [ProfileDir]
240
241 def _import_app(self, app_path):
242 """import an app class"""
243 app = None
244 name = app_path.rsplit('.', 1)[-1]
245 try:
246 app = import_item(app_path)
247 except ImportError as e:
248 self.log.info("Couldn't import %s, config file will be excluded", name)
249 except Exception:
250 self.log.warn('Unexpected error importing %s', name, exc_info=True)
251 return app
237 252
238 253 def init_config_files(self):
239 254 super(ProfileCreate, self).init_config_files()
240 255 # use local imports, since these classes may import from here
241 256 from IPython.terminal.ipapp import TerminalIPythonApp
242 257 apps = [TerminalIPythonApp]
243 try:
244 from IPython.qt.console.qtconsoleapp import IPythonQtConsoleApp
245 except Exception:
246 # this should be ImportError, but under weird circumstances
247 # this might be an AttributeError, or possibly others
248 # in any case, nothing should cause the profile creation to crash.
249 pass
250 else:
251 apps.append(IPythonQtConsoleApp)
252 try:
253 from IPython.html.notebookapp import NotebookApp
254 except ImportError:
255 pass
256 except Exception:
257 self.log.debug('Unexpected error when importing NotebookApp',
258 exc_info=True
259 )
260 else:
261 apps.append(NotebookApp)
258 for app_path in (
259 'IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp',
260 'IPython.html.notebookapp.NotebookApp',
261 'IPython.nbconvert.nbconvertapp.NbConvertApp',
262 ):
263 app = self._import_app(app_path)
264 if app is not None:
265 apps.append(app)
262 266 if self.parallel:
263 267 from IPython.parallel.apps.ipcontrollerapp import IPControllerApp
264 268 from IPython.parallel.apps.ipengineapp import IPEngineApp
@@ -214,7 +214,11 b' def find_gui_and_backend(gui=None, gui_select=None):'
214 214 # select backend based on requested gui
215 215 backend = backends[gui]
216 216 else:
217 backend = matplotlib.rcParams['backend']
217 # We need to read the backend from the original data structure, *not*
218 # from mpl.rcParams, since a prior invocation of %matplotlib may have
219 # overwritten that.
220 # WARNING: this assumes matplotlib 1.1 or newer!!
221 backend = matplotlib.rcParamsOrig['backend']
218 222 # In this case, we need to find what the appropriate gui selection call
219 223 # should be for IPython, so we can activate inputhook accordingly
220 224 gui = backend2gui.get(backend, None)
@@ -26,7 +26,7 b" _version_extra = 'dev'"
26 26 # _version_extra = 'rc1'
27 27 # _version_extra = '' # Uncomment this for full releases
28 28
29 codename = 'An Afternoon Hack'
29 codename = 'Work in Progress'
30 30
31 31 # Construct full version string from these.
32 32 _ver = [_version_major, _version_minor, _version_patch]
@@ -141,11 +141,8 b' classifiers = ['
141 141 'License :: OSI Approved :: BSD License',
142 142 'Programming Language :: Python',
143 143 'Programming Language :: Python :: 2',
144 'Programming Language :: Python :: 2.6',
145 144 'Programming Language :: Python :: 2.7',
146 145 'Programming Language :: Python :: 3',
147 'Programming Language :: Python :: 3.2',
148 'Programming Language :: Python :: 3.3',
149 146 'Topic :: System :: Distributed Computing',
150 147 'Topic :: System :: Shells'
151 148 ]
@@ -36,11 +36,14 b' from IPython.utils.path import filefind'
36 36 from IPython.utils.traitlets import (
37 37 Unicode, Instance, List, Bool, CaselessStrEnum, Dict
38 38 )
39 from IPython.lib.inputhook import guis
39 40
40 41 #-----------------------------------------------------------------------------
41 42 # Aliases and Flags
42 43 #-----------------------------------------------------------------------------
43 44
45 gui_keys = tuple(sorted([ key for key in guis if key is not None ]))
46
44 47 backend_keys = sorted(pylabtools.backends.keys())
45 48 backend_keys.insert(0, 'auto')
46 49
@@ -175,8 +178,8 b' class InteractiveShellApp(Configurable):'
175 178 module_to_run = Unicode('', config=True,
176 179 help="Run the module as a script."
177 180 )
178 gui = CaselessStrEnum(('qt', 'wx', 'gtk', 'glut', 'pyglet', 'osx'), config=True,
179 help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'glut', 'pyglet', 'osx')."
181 gui = CaselessStrEnum(gui_keys, config=True,
182 help="Enable GUI event loop integration with any of {0}.".format(gui_keys)
180 183 )
181 184 matplotlib = CaselessStrEnum(backend_keys,
182 185 config=True,
@@ -198,7 +201,7 b' class InteractiveShellApp(Configurable):'
198 201 )
199 202 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
200 203
201 user_ns = Dict(default_value=None)
204 user_ns = Instance(dict, args=None, allow_none=True)
202 205 def _user_ns_changed(self, name, old, new):
203 206 if self.shell is not None:
204 207 self.shell.user_ns = new
@@ -117,8 +117,7 b' def test_ipdb_magics():'
117 117 ipdb> pinfo a
118 118 Type: ExampleClass
119 119 String Form:ExampleClass()
120 Namespace: Locals
121 File: ...
120 Namespace: Local...
122 121 Docstring: Docstring for ExampleClass.
123 122 Constructor Docstring:Docstring for ExampleClass.__init__
124 123 ipdb> continue
@@ -45,28 +45,6 b' def run(tests):'
45 45
46 46
47 47 def test_handlers():
48 # alias expansion
49
50 # We're using 'true' as our syscall of choice because it doesn't
51 # write anything to stdout.
52
53 # Turn off actual execution of aliases, because it's noisy
54 old_system_cmd = ip.system
55 ip.system = lambda cmd: None
56
57
58 ip.alias_manager.alias_table['an_alias'] = (0, 'true')
59 # These are useful for checking a particular recursive alias issue
60 ip.alias_manager.alias_table['top'] = (0, 'd:/cygwin/top')
61 ip.alias_manager.alias_table['d'] = (0, 'true')
62 run([(i,py3compat.u_format(o)) for i,o in \
63 [("an_alias", "get_ipython().system({u}'true ')"), # alias
64 # Below: recursive aliases should expand whitespace-surrounded
65 # chars, *not* initial chars which happen to be aliases:
66 ("top", "get_ipython().system({u}'d:/cygwin/top ')"),
67 ]])
68 ip.system = old_system_cmd
69
70 48 call_idx = CallableIndexable()
71 49 ip.user_ns['call_idx'] = call_idx
72 50
@@ -9,9 +9,6 b' def test_import_crashhandler():'
9 9 def test_import_debugger():
10 10 from IPython.core import debugger
11 11
12 def test_import_fakemodule():
13 from IPython.core import fakemodule
14
15 12 def test_import_excolors():
16 13 from IPython.core import excolors
17 14
@@ -183,9 +183,18 b' syntax_ml = \\'
183 183 ('... 456"""','456"""'),
184 184 ],
185 185 [('a="""','a="""'),
186 ('>>> 123','123'),
187 ('... 456"""','456"""'),
188 ],
189 [('a="""','a="""'),
186 190 ('123','123'),
187 191 ('... 456"""','... 456"""'),
188 192 ],
193 [('....__class__','....__class__'),
194 ],
195 [('a=5', 'a=5'),
196 ('...', ''),
197 ],
189 198 [('>>> def f(x):', 'def f(x):'),
190 199 ('...', ''),
191 200 ('... return x', ' return x'),
@@ -205,6 +214,10 b' syntax_ml = \\'
205 214 (' ...: 456"""','456"""'),
206 215 ],
207 216 [('a="""','a="""'),
217 ('In [1]: 123','123'),
218 (' ...: 456"""','456"""'),
219 ],
220 [('a="""','a="""'),
208 221 ('123','123'),
209 222 (' ...: 456"""',' ...: 456"""'),
210 223 ],
@@ -237,6 +250,12 b' syntax_ml = \\'
237 250 ],
238 251 ],
239 252
253 multiline_string =
254 [ [("'''foo?", None),
255 ("bar'''", "'''foo?\nbar'''"),
256 ],
257 ],
258
240 259 leading_indent =
241 260 [ [(' print "hi"','print "hi"'),
242 261 ],
@@ -106,17 +106,6 b' class InteractiveShellTestCase(unittest.TestCase):'
106 106 ip.run_cell('a = """\n%exit\n"""')
107 107 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
108 108
109 def test_alias_crash(self):
110 """Errors in prefilter can't crash IPython"""
111 ip.run_cell('%alias parts echo first %s second %s')
112 # capture stderr:
113 save_err = io.stderr
114 io.stderr = StringIO()
115 ip.run_cell('parts 1')
116 err = io.stderr.getvalue()
117 io.stderr = save_err
118 self.assertEqual(err.split(':')[0], 'ERROR')
119
120 109 def test_trailing_newline(self):
121 110 """test that running !(command) does not raise a SyntaxError"""
122 111 ip.run_cell('!(true)\n', False)
@@ -48,16 +48,16 b' class DummyMagics(magic.Magics): pass'
48 48 def test_rehashx():
49 49 # clear up everything
50 50 _ip = get_ipython()
51 _ip.alias_manager.alias_table.clear()
51 _ip.alias_manager.clear_aliases()
52 52 del _ip.db['syscmdlist']
53 53
54 54 _ip.magic('rehashx')
55 55 # Practically ALL ipython development systems will have more than 10 aliases
56 56
57 nt.assert_true(len(_ip.alias_manager.alias_table) > 10)
58 for key, val in _ip.alias_manager.alias_table.iteritems():
57 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
58 for name, cmd in _ip.alias_manager.aliases:
59 59 # we must strip dots from alias names
60 nt.assert_not_in('.', key)
60 nt.assert_not_in('.', name)
61 61
62 62 # rehashx must fill up syscmdlist
63 63 scoms = _ip.db['syscmdlist']
@@ -487,7 +487,21 b' def test_timeit_special_syntax():'
487 487 # cell mode test
488 488 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
489 489 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
490
490
491 def test_timeit_return():
492 """
493 test wether timeit -o return object
494 """
495
496 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
497 assert(res is not None)
498
499 def test_timeit_quiet():
500 """
501 test quiet option of timeit magic
502 """
503 with tt.AssertNotPrints("loops"):
504 _ip.run_cell("%timeit -n1 -r1 -q 1")
491 505
492 506 @dec.skipif(execution.profile is None)
493 507 def test_prun_special_syntax():
@@ -6,12 +6,11 b''
6 6 # The full license is in the file COPYING.txt, distributed with this software.
7 7 #-----------------------------------------------------------------------------
8 8
9 from nose.tools import assert_equal, assert_true
9 import argparse
10 from nose.tools import assert_equal
10 11
11 from IPython.external import argparse
12 12 from IPython.core.magic_arguments import (argument, argument_group, kwds,
13 13 magic_arguments, parse_argstring, real_name)
14 from IPython.testing.decorators import parametric
15 14
16 15
17 16 @magic_arguments()
@@ -74,48 +73,46 b' def foo(self, args):'
74 73 return parse_argstring(foo, args)
75 74
76 75
77 @parametric
78 76 def test_magic_arguments():
79 # Ideally, these would be doctests, but I could not get it to work.
80 yield assert_equal(magic_foo1.__doc__, '%foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
81 yield assert_equal(getattr(magic_foo1, 'argcmd_name', None), None)
82 yield assert_equal(real_name(magic_foo1), 'foo1')
83 yield assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None))
84 yield assert_true(hasattr(magic_foo1, 'has_arguments'))
85
86 yield assert_equal(magic_foo2.__doc__, '%foo2\n\n A docstring.\n')
87 yield assert_equal(getattr(magic_foo2, 'argcmd_name', None), None)
88 yield assert_equal(real_name(magic_foo2), 'foo2')
89 yield assert_equal(magic_foo2(None, ''), argparse.Namespace())
90 yield assert_true(hasattr(magic_foo2, 'has_arguments'))
91
92 yield assert_equal(magic_foo3.__doc__, '%foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n')
93 yield assert_equal(getattr(magic_foo3, 'argcmd_name', None), None)
94 yield assert_equal(real_name(magic_foo3), 'foo3')
95 yield assert_equal(magic_foo3(None, ''),
77 assert_equal(magic_foo1.__doc__, '%foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
78 assert_equal(getattr(magic_foo1, 'argcmd_name', None), None)
79 assert_equal(real_name(magic_foo1), 'foo1')
80 assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None))
81 assert hasattr(magic_foo1, 'has_arguments')
82
83 assert_equal(magic_foo2.__doc__, '%foo2\n\n A docstring.\n')
84 assert_equal(getattr(magic_foo2, 'argcmd_name', None), None)
85 assert_equal(real_name(magic_foo2), 'foo2')
86 assert_equal(magic_foo2(None, ''), argparse.Namespace())
87 assert hasattr(magic_foo2, 'has_arguments')
88
89 assert_equal(magic_foo3.__doc__, '%foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n')
90 assert_equal(getattr(magic_foo3, 'argcmd_name', None), None)
91 assert_equal(real_name(magic_foo3), 'foo3')
92 assert_equal(magic_foo3(None, ''),
96 93 argparse.Namespace(bar=None, baz=None, foo=None))
97 yield assert_true(hasattr(magic_foo3, 'has_arguments'))
98
99 yield assert_equal(magic_foo4.__doc__, '%foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
100 yield assert_equal(getattr(magic_foo4, 'argcmd_name', None), None)
101 yield assert_equal(real_name(magic_foo4), 'foo4')
102 yield assert_equal(magic_foo4(None, ''), argparse.Namespace())
103 yield assert_true(hasattr(magic_foo4, 'has_arguments'))
104
105 yield assert_equal(magic_foo5.__doc__, '%frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
106 yield assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate')
107 yield assert_equal(real_name(magic_foo5), 'frobnicate')
108 yield assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None))
109 yield assert_true(hasattr(magic_foo5, 'has_arguments'))
110
111 yield assert_equal(magic_magic_foo.__doc__, '%magic_foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
112 yield assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None)
113 yield assert_equal(real_name(magic_magic_foo), 'magic_foo')
114 yield assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None))
115 yield assert_true(hasattr(magic_magic_foo, 'has_arguments'))
116
117 yield assert_equal(foo.__doc__, '%foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
118 yield assert_equal(getattr(foo, 'argcmd_name', None), None)
119 yield assert_equal(real_name(foo), 'foo')
120 yield assert_equal(foo(None, ''), argparse.Namespace(foo=None))
121 yield assert_true(hasattr(foo, 'has_arguments'))
94 assert hasattr(magic_foo3, 'has_arguments')
95
96 assert_equal(magic_foo4.__doc__, '%foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
97 assert_equal(getattr(magic_foo4, 'argcmd_name', None), None)
98 assert_equal(real_name(magic_foo4), 'foo4')
99 assert_equal(magic_foo4(None, ''), argparse.Namespace())
100 assert hasattr(magic_foo4, 'has_arguments')
101
102 assert_equal(magic_foo5.__doc__, '%frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
103 assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate')
104 assert_equal(real_name(magic_foo5), 'frobnicate')
105 assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None))
106 assert hasattr(magic_foo5, 'has_arguments')
107
108 assert_equal(magic_magic_foo.__doc__, '%magic_foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
109 assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None)
110 assert_equal(real_name(magic_magic_foo), 'magic_foo')
111 assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None))
112 assert hasattr(magic_magic_foo, 'has_arguments')
113
114 assert_equal(foo.__doc__, '%foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
115 assert_equal(getattr(foo, 'argcmd_name', None), None)
116 assert_equal(real_name(foo), 'foo')
117 assert_equal(foo(None, ''), argparse.Namespace(foo=None))
118 assert hasattr(foo, 'has_arguments')
@@ -6,7 +6,6 b''
6 6 import nose.tools as nt
7 7
8 8 from IPython.core.prefilter import AutocallChecker
9 from IPython.testing import decorators as dec
10 9 from IPython.testing.globalipapp import get_ipython
11 10
12 11 #-----------------------------------------------------------------------------
@@ -14,7 +13,6 b' from IPython.testing.globalipapp import get_ipython'
14 13 #-----------------------------------------------------------------------------
15 14 ip = get_ipython()
16 15
17 @dec.parametric
18 16 def test_prefilter():
19 17 """Test user input conversions"""
20 18
@@ -23,19 +21,18 b' def test_prefilter():'
23 21 ]
24 22
25 23 for raw, correct in pairs:
26 yield nt.assert_equal(ip.prefilter(raw), correct)
24 nt.assert_equal(ip.prefilter(raw), correct)
27 25
28 26
29 @dec.parametric
30 27 def test_autocall_binops():
31 28 """See https://github.com/ipython/ipython/issues/81"""
32 29 ip.magic('autocall 2')
33 30 f = lambda x: x
34 31 ip.user_ns['f'] = f
35 32 try:
36 yield nt.assert_equal(ip.prefilter('f 1'),'f(1)')
33 nt.assert_equal(ip.prefilter('f 1'),'f(1)')
37 34 for t in ['f +1', 'f -1']:
38 yield nt.assert_equal(ip.prefilter(t), t)
35 nt.assert_equal(ip.prefilter(t), t)
39 36
40 37 # Run tests again with a more permissive exclude_regexp, which will
41 38 # allow transformation of binary operations ('f -1' -> 'f(-1)').
@@ -47,8 +44,8 b' def test_autocall_binops():'
47 44 ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or '
48 45 pm.sort_checkers()
49 46
50 yield nt.assert_equal(ip.prefilter('f -1'), 'f(-1)')
51 yield nt.assert_equal(ip.prefilter('f +1'), 'f(+1)')
47 nt.assert_equal(ip.prefilter('f -1'), 'f(-1)')
48 nt.assert_equal(ip.prefilter('f +1'), 'f(+1)')
52 49 finally:
53 50 pm.unregister_checker(ac)
54 51 finally:
@@ -56,7 +53,6 b' def test_autocall_binops():'
56 53 del ip.user_ns['f']
57 54
58 55
59 @dec.parametric
60 56 def test_issue_114():
61 57 """Check that multiline string literals don't expand as magic
62 58 see http://github.com/ipython/ipython/issues/114"""
@@ -70,7 +66,7 b' def test_issue_114():'
70 66 try:
71 67 for mgk in ip.magics_manager.lsmagic()['line']:
72 68 raw = template % mgk
73 yield nt.assert_equal(ip.prefilter(raw), raw)
69 nt.assert_equal(ip.prefilter(raw), raw)
74 70 finally:
75 71 ip.prefilter_manager.multi_line_specials = msp
76 72
@@ -24,7 +24,6 b' import numpy as np'
24 24
25 25 # Our own imports
26 26 from IPython.core.interactiveshell import InteractiveShell
27 from IPython.testing import decorators as dec
28 27 from .. import pylabtools as pt
29 28
30 29 #-----------------------------------------------------------------------------
@@ -39,11 +38,10 b' from .. import pylabtools as pt'
39 38 # Classes and functions
40 39 #-----------------------------------------------------------------------------
41 40
42 @dec.parametric
43 41 def test_figure_to_svg():
44 42 # simple empty-figure test
45 43 fig = plt.figure()
46 yield nt.assert_equal(pt.print_figure(fig, 'svg'), None)
44 nt.assert_equal(pt.print_figure(fig, 'svg'), None)
47 45
48 46 plt.close('all')
49 47
@@ -53,7 +51,7 b' def test_figure_to_svg():'
53 51 ax.plot([1,2,3])
54 52 plt.draw()
55 53 svg = pt.print_figure(fig, 'svg')[:100].lower()
56 yield nt.assert_true('doctype svg' in svg)
54 nt.assert_in(b'doctype svg', svg)
57 55
58 56
59 57 def test_import_pylab():
@@ -15,6 +15,7 b' from __future__ import absolute_import'
15 15
16 16 import functools
17 17 import os
18 from os.path import join as pjoin
18 19 import random
19 20 import sys
20 21 import tempfile
@@ -359,6 +360,17 b' tclass.py: deleting object: C-third'
359 360 self.mktmp(src)
360 361 _ip.magic('run -t -N 1 %s' % self.fname)
361 362 _ip.magic('run -t -N 10 %s' % self.fname)
363
364 def test_ignore_sys_exit(self):
365 """Test the -e option to ignore sys.exit()"""
366 src = "import sys; sys.exit(1)"
367 self.mktmp(src)
368 with tt.AssertPrints('SystemExit'):
369 _ip.magic('run %s' % self.fname)
370
371 with tt.AssertNotPrints('SystemExit'):
372 _ip.magic('run -e %s' % self.fname)
373
362 374
363 375
364 376 class TestMagicRunWithPackage(unittest.TestCase):
@@ -398,6 +410,7 b' class TestMagicRunWithPackage(unittest.TestCase):'
398 410 self.tempdir.cleanup()
399 411
400 412 def check_run_submodule(self, submodule, opts=''):
413 _ip.user_ns.pop('x', None)
401 414 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
402 415 self.assertEqual(_ip.user_ns['x'], self.value,
403 416 'Variable `x` is not loaded from module `{0}`.'
@@ -430,3 +443,16 b' class TestMagicRunWithPackage(unittest.TestCase):'
430 443 @with_fake_debugger
431 444 def test_debug_run_submodule_with_relative_import(self):
432 445 self.check_run_submodule('relative', '-d')
446
447 def test_run__name__():
448 with TemporaryDirectory() as td:
449 path = pjoin(td, 'foo.py')
450 with open(path, 'w') as f:
451 f.write("q = __name__")
452
453 _ip.user_ns.pop('q', None)
454 _ip.magic('run {}'.format(path))
455 nt.assert_equal(_ip.user_ns.pop('q'), '__main__')
456
457 _ip.magic('run -n {}'.format(path))
458 nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
@@ -108,8 +108,33 b' class IndentationErrorTest(unittest.TestCase):'
108 108 with tt.AssertPrints("zoon()", suppress=False):
109 109 ip.magic('run %s' % fname)
110 110
111 se_file_1 = """1
112 2
113 7/
114 """
115
116 se_file_2 = """7/
117 """
118
111 119 class SyntaxErrorTest(unittest.TestCase):
112 120 def test_syntaxerror_without_lineno(self):
113 121 with tt.AssertNotPrints("TypeError"):
114 122 with tt.AssertPrints("line unknown"):
115 123 ip.run_cell("raise SyntaxError()")
124
125 def test_changing_py_file(self):
126 with TemporaryDirectory() as td:
127 fname = os.path.join(td, "foo.py")
128 with open(fname, 'w') as f:
129 f.write(se_file_1)
130
131 with tt.AssertPrints(["7/", "SyntaxError"]):
132 ip.magic("run " + fname)
133
134 # Modify the file
135 with open(fname, 'w') as f:
136 f.write(se_file_2)
137
138 # The SyntaxError should point to the correct line
139 with tt.AssertPrints(["7/", "SyntaxError"]):
140 ip.magic("run " + fname)
@@ -8,7 +8,8 b' ColorTB class is a solution to that problem. It colors the different parts of a'
8 8 traceback in a manner similar to what you would expect from a syntax-highlighting
9 9 text editor.
10 10
11 Installation instructions for ColorTB:
11 Installation instructions for ColorTB::
12
12 13 import sys,ultratb
13 14 sys.excepthook = ultratb.ColorTB()
14 15
@@ -21,7 +22,7 b' but kind of neat, and maybe useful for long-running programs that you believe'
21 22 are bug-free. If a crash *does* occur in that type of program you want details.
22 23 Give it a shot--you'll love it or you'll hate it.
23 24
24 Note:
25 .. note::
25 26
26 27 The Verbose mode prints the variables currently visible where the exception
27 28 happened (shortening their strings if too long). This can potentially be
@@ -36,25 +37,28 b' Note:'
36 37 Verbose).
37 38
38 39
39 Installation instructions for ColorTB:
40 Installation instructions for ColorTB::
41
40 42 import sys,ultratb
41 43 sys.excepthook = ultratb.VerboseTB()
42 44
43 45 Note: Much of the code in this module was lifted verbatim from the standard
44 46 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
45 47
46 * Color schemes
48 Color schemes
49 -------------
50
47 51 The colors are defined in the class TBTools through the use of the
48 52 ColorSchemeTable class. Currently the following exist:
49 53
50 54 - NoColor: allows all of this module to be used in any terminal (the color
51 escapes are just dummy blank strings).
55 escapes are just dummy blank strings).
52 56
53 57 - Linux: is meant to look good in a terminal like the Linux console (black
54 or very dark background).
58 or very dark background).
55 59
56 60 - LightBG: similar to Linux but swaps dark/light colors to be more readable
57 in light background terminals.
61 in light background terminals.
58 62
59 63 You can implement other color schemes easily, the syntax is fairly
60 64 self-explanatory. Please send back new schemes you develop to the author for
@@ -359,8 +363,8 b' class TBTools(object):'
359 363 Valid values are:
360 364
361 365 - None: the default, which means that IPython will dynamically resolve
362 to io.stdout. This ensures compatibility with most tools, including
363 Windows (where plain stdout doesn't recognize ANSI escapes).
366 to io.stdout. This ensures compatibility with most tools, including
367 Windows (where plain stdout doesn't recognize ANSI escapes).
364 368
365 369 - Any object with 'write' and 'flush' attributes.
366 370 """
@@ -974,9 +978,9 b' class VerboseTB(TBTools):'
974 978 Keywords:
975 979
976 980 - force(False): by default, this routine checks the instance call_pdb
977 flag and does not actually invoke the debugger if the flag is false.
978 The 'force' option forces the debugger to activate even if the flag
979 is false.
981 flag and does not actually invoke the debugger if the flag is false.
982 The 'force' option forces the debugger to activate even if the flag
983 is false.
980 984
981 985 If the call_pdb flag is set, the pdb interactive debugger is
982 986 invoked. In all cases, the self.tb reference to the current traceback
@@ -1193,6 +1197,20 b' class SyntaxTB(ListTB):'
1193 1197 self.last_syntax_error = value
1194 1198 ListTB.__call__(self,etype,value,elist)
1195 1199
1200 def structured_traceback(self, etype, value, elist, tb_offset=None,
1201 context=5):
1202 # If the source file has been edited, the line in the syntax error can
1203 # be wrong (retrieved from an outdated cache). This replaces it with
1204 # the current value.
1205 if isinstance(value.filename, py3compat.string_types) \
1206 and isinstance(value.lineno, int):
1207 linecache.checkcache(value.filename)
1208 newtext = ulinecache.getline(value.filename, value.lineno)
1209 if newtext:
1210 value.text = newtext
1211 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1212 tb_offset=tb_offset, context=context)
1213
1196 1214 def clear_err_state(self):
1197 1215 """Return the current error state and clear it"""
1198 1216 e = self.last_syntax_error
@@ -4,9 +4,18 b''
4 4 Cython related magics
5 5 =====================
6 6
7 Magic command interface for interactive work with Cython
8
9 .. note::
10
11 The ``Cython`` package needs to be installed separately. It
12 can be obtained using ``easy_install`` or ``pip``.
13
7 14 Usage
8 15 =====
9 16
17 To enable the magics below, execute ``%load_ext cythonmagic``.
18
10 19 ``%%cython``
11 20
12 21 {CYTHON_DOC}
@@ -11,9 +11,13 b' Magics for interacting with Octave via oct2py.'
11 11 The ``oct2py`` module needs to be installed separately and
12 12 can be obtained using ``easy_install`` or ``pip``.
13 13
14 You will also need a working copy of GNU Octave.
15
14 16 Usage
15 17 =====
16 18
19 To enable the magics below, execute ``%load_ext octavemagic``.
20
17 21 ``%octave``
18 22
19 23 {OCTAVE_DOC}
@@ -6,9 +6,18 b' Rmagic'
6 6
7 7 Magic command interface for interactive work with R via rpy2
8 8
9 .. note::
10
11 The ``rpy2`` package needs to be installed separately. It
12 can be obtained using ``easy_install`` or ``pip``.
13
14 You will also need a working copy of R.
15
9 16 Usage
10 17 =====
11 18
19 To enable the magics below, execute ``%load_ext rmagic``.
20
12 21 ``%R``
13 22
14 23 {R_DOC}
@@ -425,12 +434,12 b' class RMagics(Magics):'
425 434 is printed if it would printed be when evaluating the same code
426 435 within a standard R REPL.
427 436
428 Nothing is returned to python by default in cell mode.
437 Nothing is returned to python by default in cell mode::
429 438
430 439 In [10]: %%R
431 440 ....: Y = c(2,4,3,9)
432 441 ....: summary(lm(Y~X))
433
442
434 443 Call:
435 444 lm(formula = Y ~ X)
436 445
@@ -447,9 +456,9 b' class RMagics(Magics):'
447 456 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
448 457 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
449 458
450 In the notebook, plots are published as the output of the cell.
459 In the notebook, plots are published as the output of the cell::
451 460
452 %R plot(X, Y)
461 %R plot(X, Y)
453 462
454 463 will create a scatter plot of X bs Y.
455 464
@@ -475,19 +484,19 b' class RMagics(Magics):'
475 484 * If the cell is not None, the magic returns None.
476 485
477 486 * If the cell evaluates as False, the resulting value is returned
478 unless the final line prints something to the console, in
479 which case None is returned.
487 unless the final line prints something to the console, in
488 which case None is returned.
480 489
481 490 * If the final line results in a NULL value when evaluated
482 by rpy2, then None is returned.
491 by rpy2, then None is returned.
483 492
484 493 * No attempt is made to convert the final value to a structured array.
485 Use the --dataframe flag or %Rget to push / return a structured array.
494 Use the --dataframe flag or %Rget to push / return a structured array.
486 495
487 496 * If the -n flag is present, there is no return value.
488 497
489 498 * A trailing ';' will also result in no return value as the last
490 value in the line is an empty string.
499 value in the line is an empty string.
491 500
492 501 The --dataframe argument will attempt to return structured arrays.
493 502 This is useful for dataframes with
@@ -25,10 +25,11 b' To automatically restore stored variables at startup, add this to your'
25 25 import inspect, os, sys, textwrap
26 26
27 27 # Our own
28 from IPython.config.configurable import Configurable
28 29 from IPython.core.error import UsageError
29 from IPython.core.fakemodule import FakeModule
30 30 from IPython.core.magic import Magics, magics_class, line_magic
31 31 from IPython.testing.skipdoctest import skip_doctest
32 from IPython.utils.traitlets import Bool
32 33
33 34 #-----------------------------------------------------------------------------
34 35 # Functions and classes
@@ -68,10 +69,23 b' def restore_data(ip):'
68 69
69 70
70 71 @magics_class
71 class StoreMagics(Magics):
72 class StoreMagics(Magics, Configurable):
72 73 """Lightweight persistence for python variables.
73 74
74 75 Provides the %store magic."""
76
77 autorestore = Bool(False, config=True, help=
78 """If True, any %store-d variables will be automatically restored
79 when IPython starts.
80 """
81 )
82
83 def __init__(self, shell):
84 Configurable.__init__(self, config=shell.config)
85 Magics.__init__(self, shell=shell)
86 self.shell.configurables.append(self)
87 if self.autorestore:
88 restore_data(self.shell)
75 89
76 90 @skip_doctest
77 91 @line_magic
@@ -196,20 +210,21 b' class StoreMagics(Magics):'
196 210 obj = ip.user_ns[args[0]]
197 211 except KeyError:
198 212 # it might be an alias
199 # This needs to be refactored to use the new AliasManager stuff.
200 if args[0] in ip.alias_manager:
201 name = args[0]
202 nargs, cmd = ip.alias_manager.alias_table[ name ]
203 staliases = db.get('stored_aliases',{})
204 staliases[ name ] = cmd
205 db['stored_aliases'] = staliases
206 print "Alias stored: %s (%s)" % (name, cmd)
207 return
208 else:
209 raise UsageError("Unknown variable '%s'" % args[0])
213 name = args[0]
214 try:
215 cmd = ip.alias_manager.retrieve_alias(name)
216 except ValueError:
217 raise UsageError("Unknown variable '%s'" % name)
218
219 staliases = db.get('stored_aliases',{})
220 staliases[name] = cmd
221 db['stored_aliases'] = staliases
222 print "Alias stored: %s (%s)" % (name, cmd)
223 return
210 224
211 225 else:
212 if isinstance(inspect.getmodule(obj), FakeModule):
226 modname = getattr(inspect.getmodule(obj), '__name__', '')
227 if modname == '__main__':
213 228 print textwrap.dedent("""\
214 229 Warning:%s is %s
215 230 Proper storage of interactively declared classes (or instances
@@ -225,3 +240,4 b' class StoreMagics(Magics):'
225 240 def load_ipython_extension(ip):
226 241 """Load the extension in IPython."""
227 242 ip.register_magics(StoreMagics)
243
@@ -1,5 +1,6 b''
1 1 import tempfile, os
2 2
3 from IPython.config.loader import Config
3 4 import nose.tools as nt
4 5
5 6 ip = get_ipython()
@@ -26,7 +27,24 b' def test_store_restore():'
26 27 # Check restoring
27 28 ip.magic('store -r')
28 29 nt.assert_equal(ip.user_ns['foo'], 78)
29 nt.assert_in('bar', ip.alias_manager.alias_table)
30 assert ip.alias_manager.is_alias('bar')
30 31 nt.assert_in(os.path.realpath(tmpd), ip.user_ns['_dh'])
31 32
32 33 os.rmdir(tmpd)
34
35 def test_autorestore():
36 ip.user_ns['foo'] = 95
37 ip.magic('store foo')
38 del ip.user_ns['foo']
39 c = Config()
40 c.StoreMagics.autorestore = False
41 orig_config = ip.config
42 try:
43 ip.config = c
44 ip.extension_manager.reload_extension('storemagic')
45 nt.assert_not_in('foo', ip.user_ns)
46 c.StoreMagics.autorestore = True
47 ip.extension_manager.reload_extension('storemagic')
48 nt.assert_equal(ip.user_ns['foo'], 95)
49 finally:
50 ip.config = orig_config
@@ -19,6 +19,10 b' From the command line:'
19 19
20 20 $ python -m IPython.external.mathjax
21 21
22 To a specific profile:
23
24 $ python -m IPython.external.mathjax --profile=research
25
22 26 To install MathJax from a file you have already downloaded:
23 27
24 28 $ python -m IPython.external.mathjax mathjax-xxx.tar.gz
@@ -46,6 +50,7 b' To find the directory where IPython would like MathJax installed:'
46 50 # Imports
47 51 #-----------------------------------------------------------------------------
48 52
53 import argparse
49 54 import os
50 55 import shutil
51 56 import sys
@@ -55,7 +60,6 b' import zipfile'
55 60
56 61
57 62 from IPython.utils.path import locate_profile
58 from IPython.external import argparse
59 63 #-----------------------------------------------------------------------------
60 64 #
61 65 #-----------------------------------------------------------------------------
@@ -200,20 +204,26 b' def main() :'
200 204 )
201 205
202 206 parser.add_argument(
207 '-p',
208 '--profile',
209 default='default',
210 help='profile to install MathJax to (default is default)')
211
212 parser.add_argument(
203 213 '-i',
204 214 '--install-dir',
205 default=default_dest,
206 help='installation directory (by default : %s)' % (default_dest))
215 help='custom installation directory')
216
207 217 parser.add_argument(
208 218 '-d',
209 219 '--dest',
210 220 action='store_true',
211 help='print where is current mathjax would be installed and exit')
221 help='print where current mathjax would be installed and exit')
212 222 parser.add_argument(
213 223 '-r',
214 224 '--replace',
215 225 action='store_true',
216 help='Wether to replace current mathjax if already exist')
226 help='Whether to replace current mathjax if it already exists')
217 227 parser.add_argument(
218 228 '-t',
219 229 '--test',
@@ -225,7 +235,13 b' def main() :'
225 235
226 236 pargs = parser.parse_args()
227 237
228 dest = pargs.install_dir
238 if pargs.install_dir:
239 # Explicit install_dir overrides profile
240 dest = pargs.install_dir
241 else:
242 profile = pargs.profile
243 dest = os.path.join(locate_profile(profile), 'static', 'mathjax')
244
229 245 if pargs.dest :
230 246 print dest
231 247 return
@@ -259,7 +275,7 b' def main() :'
259 275 if __name__ == '__main__' :
260 276 sys.exit(main())
261 277
262 __all__ = ['install_mathjax', 'main', 'dest']
278 __all__ = ['install_mathjax', 'main', 'default_dest']
263 279
264 280 """
265 281 Test notes:
@@ -78,7 +78,7 b' from IPython.kernel.zmq.kernelapp import ('
78 78 kernel_aliases,
79 79 )
80 80 from IPython.utils.importstring import import_item
81 from IPython.utils.localinterfaces import LOCALHOST
81 from IPython.utils.localinterfaces import localhost
82 82 from IPython.utils import submodule
83 83 from IPython.utils.traitlets import (
84 84 Dict, Unicode, Integer, List, Bool, Bytes,
@@ -253,7 +253,7 b' aliases.update({'
253 253 aliases.pop('f', None)
254 254
255 255 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
256 u'notebook-dir']
256 u'notebook-dir', u'profile', u'profile-dir']
257 257
258 258 #-----------------------------------------------------------------------------
259 259 # NotebookApp
@@ -293,9 +293,11 b' class NotebookApp(BaseIPythonApplication):'
293 293
294 294 # Network related information.
295 295
296 ip = Unicode(LOCALHOST, config=True,
296 ip = Unicode(config=True,
297 297 help="The IP address the notebook server will listen on."
298 298 )
299 def _ip_default(self):
300 return localhost()
299 301
300 302 def _ip_changed(self, name, old, new):
301 303 if new == u'*': self.ip = u''
@@ -471,17 +473,10 b' class NotebookApp(BaseIPythonApplication):'
471 473 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
472 474 "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL")
473 475 )
474
476
475 477 def parse_command_line(self, argv=None):
476 478 super(NotebookApp, self).parse_command_line(argv)
477 if argv is None:
478 argv = sys.argv[1:]
479
480 # Scrub frontend-specific flags
481 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
482 # Kernel should inherit default config file from frontend
483 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
484
479
485 480 if self.extra_args:
486 481 f = os.path.abspath(self.extra_args[0])
487 482 if os.path.isdir(f):
@@ -491,6 +486,15 b' class NotebookApp(BaseIPythonApplication):'
491 486 nbdir = os.path.dirname(f)
492 487 self.config.NotebookManager.notebook_dir = nbdir
493 488
489 def init_kernel_argv(self):
490 """construct the kernel arguments"""
491 # Scrub frontend-specific flags
492 self.kernel_argv = swallow_argv(self.argv, notebook_aliases, notebook_flags)
493 # Kernel should inherit default config file from frontend
494 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
495 # Kernel should get *absolute* path to profile directory
496 self.kernel_argv.extend(["--profile-dir", self.profile_dir.location])
497
494 498 def init_configurables(self):
495 499 # force Session default to be secure
496 500 default_secure(self.config)
@@ -657,6 +661,7 b' class NotebookApp(BaseIPythonApplication):'
657 661 def initialize(self, argv=None):
658 662 self.init_logging()
659 663 super(NotebookApp, self).initialize(argv)
664 self.init_kernel_argv()
660 665 self.init_configurables()
661 666 self.init_components()
662 667 self.init_webapp()
@@ -691,7 +696,7 b' class NotebookApp(BaseIPythonApplication):'
691 696 info("Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).")
692 697
693 698 if self.open_browser or self.file_to_run:
694 ip = self.ip or LOCALHOST
699 ip = self.ip or localhost()
695 700 try:
696 701 browser = webbrowser.get(self.browser or None)
697 702 except webbrowser.Error as e:
@@ -1,4 +1,4 b''
1 1 @import "../base/less/variables.less";
2 2 @import "../base/less/mixins.less";
3 3 @import "../base/less/flexbox.less";
4 @import "../base/less/page.less";
4
@@ -17,12 +17,14 b''
17 17 * Create a custom button in toolbar that execute `%qtconsole` in kernel
18 18 * and hence open a qtconsole attached to the same kernel as the current notebook
19 19 *
20 * $([IPython.events]).on('notebook_loaded.Notebook', function(){
20 * $([IPython.events]).on('app_initialized.NotebookApp', function(){
21 21 * IPython.toolbar.add_buttons_group([
22 22 * {
23 23 * 'label' : 'run qtconsole',
24 * 'icon' : 'ui-icon-calculator', // select your icon from http://jqueryui.com/themeroller/
25 * 'callback': function(){IPython.notebook.kernel.execute('%qtconsole')}
24 * 'icon' : 'icon-terminal', // select your icon from http://fortawesome.github.io/Font-Awesome/icons
25 * 'callback': function () {
26 * IPython.notebook.kernel.execute('%qtconsole')
27 * }
26 28 * }
27 29 * // add more button here if needed.
28 30 * ]);
@@ -207,7 +207,7 b' var IPython = (function (IPython) {'
207 207
208 208
209 209 /**
210 * can the cell be splitted in 2 cells.
210 * can the cell be split into two cells
211 211 * @method is_splittable
212 212 **/
213 213 Cell.prototype.is_splittable = function () {
@@ -216,6 +216,15 b' var IPython = (function (IPython) {'
216 216
217 217
218 218 /**
219 * can the cell be merged with other cells
220 * @method is_mergeable
221 **/
222 Cell.prototype.is_mergeable = function () {
223 return true;
224 };
225
226
227 /**
219 228 * @return {String} - the text before the cursor
220 229 * @method get_pre_cursor
221 230 **/
@@ -323,8 +332,13 b' var IPython = (function (IPython) {'
323 332 }
324 333 }
325 334 }
326 // fallback on default (python)
327 var default_mode = this.default_mode || 'text/plain';
335 // fallback on default
336 var default_mode
337 try {
338 default_mode = this._options.cm_config.mode;
339 } catch(e) {
340 default_mode = 'text/plain';
341 }
328 342 this.code_mirror.setOption('mode', default_mode);
329 343 };
330 344
@@ -65,8 +65,8 b' var IPython = (function (IPython) {'
65 65 this.code_mirror = null;
66 66 this.input_prompt_number = null;
67 67 this.collapsed = false;
68 this.default_mode = 'ipython';
69 68 this.cell_type = "code";
69 this.last_msg_id = null;
70 70
71 71
72 72 var cm_overwrite_options = {
@@ -167,7 +167,7 b' var IPython = (function (IPython) {'
167 167 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
168 168 // browser and keyboard layout !
169 169 // Pressing '(' , request tooltip, don't forget to reappend it
170 // The second argument says to hide the tooltip if the docstring
170 // The second argument says to hide the tooltip if the docstring
171 171 // is actually empty
172 172 IPython.tooltip.pending(that, true);
173 173 } else if (event.which === key.UPARROW && event.type === 'keydown') {
@@ -180,8 +180,7 b' var IPython = (function (IPython) {'
180 180 return true;
181 181 };
182 182 } else if (event.which === key.ESC) {
183 IPython.tooltip.remove_and_cancel_tooltip(true);
184 return true;
183 return IPython.tooltip.remove_and_cancel_tooltip(true);
185 184 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
186 185 // If we are not at the bottom, let CM handle the down arrow and
187 186 // prevent the global keydown handler from handling it.
@@ -242,9 +241,12 b' var IPython = (function (IPython) {'
242 241 * @method execute
243 242 */
244 243 CodeCell.prototype.execute = function () {
245 this.output_area.clear_output(true, true, true);
244 this.output_area.clear_output();
246 245 this.set_input_prompt('*');
247 246 this.element.addClass("running");
247 if (this.last_msg_id) {
248 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
249 }
248 250 var callbacks = {
249 251 'execute_reply': $.proxy(this._handle_execute_reply, this),
250 252 'output': $.proxy(this.output_area.handle_output, this.output_area),
@@ -252,7 +254,7 b' var IPython = (function (IPython) {'
252 254 'set_next_input': $.proxy(this._handle_set_next_input, this),
253 255 'input_request': $.proxy(this._handle_input_request, this)
254 256 };
255 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
257 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
256 258 };
257 259
258 260 /**
@@ -273,7 +275,7 b' var IPython = (function (IPython) {'
273 275 var data = {'cell': this, 'text': text}
274 276 $([IPython.events]).trigger('set_next_input.Notebook', data);
275 277 }
276
278
277 279 /**
278 280 * @method _handle_input_request
279 281 * @private
@@ -388,8 +390,8 b' var IPython = (function (IPython) {'
388 390 };
389 391
390 392
391 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
392 this.output_area.clear_output(stdout, stderr, other);
393 CodeCell.prototype.clear_output = function (wait) {
394 this.output_area.clear_output(wait);
393 395 };
394 396
395 397
@@ -50,12 +50,13 b' var IPython = (function (IPython) {'
50 50 * cell_magic_highlight['javascript'] = {'reg':[/^var/]}
51 51 */
52 52 cell_magic_highlight : {
53 'magic_javascript':{'reg':[/^%%javascript/]}
54 ,'magic_perl' :{'reg':[/^%%perl/]}
55 ,'magic_ruby' :{'reg':[/^%%ruby/]}
56 ,'magic_python' :{'reg':[/^%%python3?/]}
57 ,'magic_shell' :{'reg':[/^%%bash/]}
58 ,'magic_r' :{'reg':[/^%%R/]}
53 'magic_javascript' :{'reg':[/^%%javascript/]}
54 ,'magic_perl' :{'reg':[/^%%perl/]}
55 ,'magic_ruby' :{'reg':[/^%%ruby/]}
56 ,'magic_python' :{'reg':[/^%%python3?/]}
57 ,'magic_shell' :{'reg':[/^%%bash/]}
58 ,'magic_r' :{'reg':[/^%%R/]}
59 ,'magic_text/x-cython' :{'reg':[/^%%cython/]}
59 60 },
60 61
61 62 /**
@@ -99,6 +99,10 b' function (marked) {'
99 99 tables: true,
100 100 langPrefix: "language-",
101 101 highlight: function(code, lang) {
102 if (!lang) {
103 // no language, no highlight
104 return code;
105 }
102 106 var highlighted;
103 107 try {
104 108 highlighted = hljs.highlight(lang, code, false);
@@ -1175,8 +1175,14 b' var IPython = (function (IPython) {'
1175 1175 Notebook.prototype.merge_cell_above = function () {
1176 1176 var index = this.get_selected_index();
1177 1177 var cell = this.get_cell(index);
1178 if (!cell.is_mergeable()) {
1179 return;
1180 }
1178 1181 if (index > 0) {
1179 1182 var upper_cell = this.get_cell(index-1);
1183 if (!upper_cell.is_mergeable()) {
1184 return;
1185 }
1180 1186 var upper_text = upper_cell.get_text();
1181 1187 var text = cell.get_text();
1182 1188 if (cell instanceof IPython.CodeCell) {
@@ -1199,8 +1205,14 b' var IPython = (function (IPython) {'
1199 1205 Notebook.prototype.merge_cell_below = function () {
1200 1206 var index = this.get_selected_index();
1201 1207 var cell = this.get_cell(index);
1208 if (!cell.is_mergeable()) {
1209 return;
1210 }
1202 1211 if (index < this.ncells()-1) {
1203 1212 var lower_cell = this.get_cell(index+1);
1213 if (!lower_cell.is_mergeable()) {
1214 return;
1215 }
1204 1216 var lower_text = lower_cell.get_text();
1205 1217 var text = cell.get_text();
1206 1218 if (cell instanceof IPython.CodeCell) {
@@ -1327,7 +1339,7 b' var IPython = (function (IPython) {'
1327 1339 var cells = this.get_cells();
1328 1340 for (var i=0; i<ncells; i++) {
1329 1341 if (cells[i] instanceof IPython.CodeCell) {
1330 cells[i].clear_output(true,true,true);
1342 cells[i].clear_output();
1331 1343 // Make all In[] prompts blank, as well
1332 1344 // TODO: make this configurable (via checkbox?)
1333 1345 cells[i].set_input_prompt();
@@ -31,7 +31,7 b' var IPython = (function (IPython) {'
31 31 this.outputs = [];
32 32 this.collapsed = false;
33 33 this.scrolled = false;
34 this.clear_out_timeout = null;
34 this.clear_queued = null;
35 35 if (prompt_area === undefined) {
36 36 this.prompt_area = true;
37 37 } else {
@@ -289,7 +289,12 b' var IPython = (function (IPython) {'
289 289 OutputArea.prototype.append_output = function (json, dynamic) {
290 290 // If dynamic is true, javascript output will be eval'd.
291 291 this.expand();
292 this.flush_clear_timeout();
292
293 // Clear the output if clear is queued.
294 if (this.clear_queued) {
295 this.clear_output(false);
296 }
297
293 298 if (json.output_type === 'pyout') {
294 299 this.append_pyout(json, dynamic);
295 300 } else if (json.output_type === 'pyerr') {
@@ -300,6 +305,7 b' var IPython = (function (IPython) {'
300 305 this.append_stream(json);
301 306 }
302 307 this.outputs.push(json);
308 this.element.height('auto');
303 309 var that = this;
304 310 setTimeout(function(){that.element.trigger('resize');}, 100);
305 311 };
@@ -312,6 +318,33 b' var IPython = (function (IPython) {'
312 318 }
313 319 return oa;
314 320 };
321
322 OutputArea.prototype._append_javascript_error = function (err, container) {
323 // display a message when a javascript error occurs in display output
324 var msg = "Javascript error adding output!"
325 console.log(msg, err);
326 if ( container === undefined ) return;
327 container.append(
328 $('<div/>').html(msg + "<br/>" +
329 err.toString() +
330 '<br/>See your browser Javascript console for more details.'
331 ).addClass('js-error')
332 );
333 container.show();
334 };
335
336 OutputArea.prototype._safe_append = function (toinsert) {
337 // safely append an item to the document
338 // this is an object created by user code,
339 // and may have errors, which should not be raised
340 // under any circumstances.
341 try {
342 this.element.append(toinsert);
343 } catch(err) {
344 console.log(err);
345 this._append_javascript_error(err, this.element);
346 }
347 };
315 348
316 349
317 350 OutputArea.prototype.append_pyout = function (json, dynamic) {
@@ -321,19 +354,7 b' var IPython = (function (IPython) {'
321 354 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
322 355 }
323 356 this.append_mime_type(json, toinsert, dynamic);
324 try {
325 this.element.append(toinsert);
326 } catch(err) {
327 console.log("Error attaching output!");
328 console.log(err);
329 this.element.show();
330 toinsert.html($('<div/>')
331 .html("Javascript error adding output!<br/>" +
332 err.toString() +
333 '<br/>See your browser Javascript console for more details.')
334 .addClass('js-error')
335 );
336 }
357 this._safe_append(toinsert);
337 358 // If we just output latex, typeset it.
338 359 if ((json.latex !== undefined) || (json.html !== undefined)) {
339 360 this.typeset();
@@ -352,7 +373,7 b' var IPython = (function (IPython) {'
352 373 s = s + '\n';
353 374 var toinsert = this.create_output_area();
354 375 this.append_text(s, {}, toinsert);
355 this.element.append(toinsert);
376 this._safe_append(toinsert);
356 377 }
357 378 };
358 379
@@ -389,14 +410,14 b' var IPython = (function (IPython) {'
389 410 // If we got here, attach a new div
390 411 var toinsert = this.create_output_area();
391 412 this.append_text(text, {}, toinsert, "output_stream "+subclass);
392 this.element.append(toinsert);
413 this._safe_append(toinsert);
393 414 };
394 415
395 416
396 417 OutputArea.prototype.append_display_data = function (json, dynamic) {
397 418 var toinsert = this.create_output_area();
398 419 this.append_mime_type(json, toinsert, dynamic);
399 this.element.append(toinsert);
420 this._safe_append(toinsert);
400 421 // If we just output latex, typeset it.
401 422 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
402 423 this.typeset();
@@ -444,15 +465,7 b' var IPython = (function (IPython) {'
444 465 try {
445 466 eval(js);
446 467 } catch(err) {
447 console.log('Error in Javascript!');
448 console.log(err);
449 container.show();
450 element.append($('<div/>')
451 .html("Error in Javascript !<br/>"+
452 err.toString()+
453 '<br/>See your browser Javascript console for more details.')
454 .addClass('js-error')
455 );
468 this._append_javascript_error(err, container);
456 469 }
457 470 };
458 471
@@ -546,7 +559,6 b' var IPython = (function (IPython) {'
546 559 OutputArea.prototype.append_raw_input = function (content) {
547 560 var that = this;
548 561 this.expand();
549 this.flush_clear_timeout();
550 562 var area = this.create_output_area();
551 563
552 564 // disable any other raw_inputs, if they are left around
@@ -599,81 +611,35 b' var IPython = (function (IPython) {'
599 611
600 612
601 613 OutputArea.prototype.handle_clear_output = function (content) {
602 this.clear_output(content.stdout, content.stderr, content.other);
614 this.clear_output(content.wait);
603 615 };
604 616
605 617
606 OutputArea.prototype.clear_output = function (stdout, stderr, other) {
607 var that = this;
608 if (this.clear_out_timeout != null){
609 // fire previous pending clear *immediately*
610 clearTimeout(this.clear_out_timeout);
611 this.clear_out_timeout = null;
612 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
613 }
614 // store flags for flushing the timeout
615 this._clear_stdout = stdout;
616 this._clear_stderr = stderr;
617 this._clear_other = other;
618 this.clear_out_timeout = setTimeout(function() {
619 // really clear timeout only after a short delay
620 // this reduces flicker in 'clear_output; print' cases
621 that.clear_out_timeout = null;
622 that._clear_stdout = that._clear_stderr = that._clear_other = null;
623 that.clear_output_callback(stdout, stderr, other);
624 }, 500
625 );
626 };
618 OutputArea.prototype.clear_output = function(wait) {
619 if (wait) {
627 620
621 // If a clear is queued, clear before adding another to the queue.
622 if (this.clear_queued) {
623 this.clear_output(false);
624 };
628 625
629 OutputArea.prototype.clear_output_callback = function (stdout, stderr, other) {
630 var output_div = this.element;
626 this.clear_queued = true;
627 } else {
631 628
632 if (stdout && stderr && other){
629 // Fix the output div's height if the clear_output is waiting for
630 // new output (it is being used in an animation).
631 if (this.clear_queued) {
632 var height = this.element.height();
633 this.element.height(height);
634 this.clear_queued = false;
635 }
636
633 637 // clear all, no need for logic
634 output_div.html("");
638 this.element.html("");
635 639 this.outputs = [];
636 640 this.unscroll_area();
637 641 return;
638 }
639 // remove html output
640 // each output_subarea that has an identifying class is in an output_area
641 // which is the element to be removed.
642 if (stdout) {
643 output_div.find("div.output_stdout").parent().remove();
644 }
645 if (stderr) {
646 output_div.find("div.output_stderr").parent().remove();
647 }
648 if (other) {
649 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
650 }
651 this.unscroll_area();
652
653 // remove cleared outputs from JSON list:
654 for (var i = this.outputs.length - 1; i >= 0; i--) {
655 var out = this.outputs[i];
656 var output_type = out.output_type;
657 if (output_type == "display_data" && other) {
658 this.outputs.splice(i,1);
659 } else if (output_type == "stream") {
660 if (stdout && out.stream == "stdout") {
661 this.outputs.splice(i,1);
662 } else if (stderr && out.stream == "stderr") {
663 this.outputs.splice(i,1);
664 }
665 }
666 }
667 };
668
669
670 OutputArea.prototype.flush_clear_timeout = function() {
671 var output_div = this.element;
672 if (this.clear_out_timeout){
673 clearTimeout(this.clear_out_timeout);
674 this.clear_out_timeout = null;
675 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
676 }
642 };
677 643 };
678 644
679 645
@@ -52,7 +52,7 b' var IPython = (function (IPython) {'
52 52 $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) {
53 53 that.set_last_checkpoint(data[0]);
54 54 });
55
55
56 56 $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) {
57 57 that.set_last_checkpoint(data);
58 58 });
@@ -104,7 +104,7 b' var IPython = (function (IPython) {'
104 104 return false;
105 105 }
106 106 });
107 that.find('input[type="text"]').focus();
107 that.find('input[type="text"]').focus().select();
108 108 }
109 109 });
110 110 }
@@ -482,6 +482,22 b' var IPython = (function (IPython) {'
482 482 return data;
483 483 };
484 484
485 /**
486 * can the cell be split into two cells
487 * @method is_splittable
488 **/
489 HeadingCell.prototype.is_splittable = function () {
490 return false;
491 };
492
493
494 /**
495 * can the cell be merged with other cells
496 * @method is_mergeable
497 **/
498 HeadingCell.prototype.is_mergeable = function () {
499 return false;
500 };
485 501
486 502 /**
487 503 * Change heading level of cell, and re-render
@@ -161,16 +161,23 b' var IPython = (function (IPython) {'
161 161 this.code_mirror = null;
162 162 }
163 163
164 // return true on successfully removing a visible tooltip; otherwise return
165 // false.
164 166 Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
165 167 // note that we don't handle closing directly inside the calltip
166 168 // as in the completer, because it is not focusable, so won't
167 169 // get the event.
168 if (this._sticky == false || force == true) {
169 this.cancel_stick();
170 this._hide();
170 if (!this._hidden) {
171 if (force || !this._sticky) {
172 this.cancel_stick();
173 this._hide();
174 }
175 this.cancel_pending();
176 this.reset_tabs_function();
177 return true;
178 } else {
179 return false;
171 180 }
172 this.cancel_pending();
173 this.reset_tabs_function();
174 181 }
175 182
176 183 // cancel autocall done after '(' for example.
@@ -335,7 +342,7 b' var IPython = (function (IPython) {'
335 342 if (docstring == null) {
336 343 docstring = reply.docstring;
337 344 }
338
345
339 346 if (docstring == null) {
340 347 // For reals this time, no docstring
341 348 if (this._hide_if_no_docstring) {
@@ -1,4 +1,4 b''
1 .cell {
1 div.cell {
2 2 border: 1px solid transparent;
3 3 .vbox();
4 4
@@ -6,9 +6,6 b''
6 6 .corner-all;
7 7 border : thin @border_color solid;
8 8 }
9 }
10
11 div.cell {
12 9 width: 100%;
13 10 padding: 5px 5px 5px 0px;
14 11 /* This acts as a spacer between cells, that is outside the border */
@@ -22,7 +22,7 b''
22 22 overflow-x: auto;
23 23 }
24 24
25 @-moz-document {
25 @-moz-document url-prefix() {
26 26 /* Firefox does weird and terrible things (#3549) when overflow-x is auto */
27 27 /* It doesn't respect the overflow setting anyway, so we can workaround it with this */
28 28 .CodeMirror-scroll {
@@ -119,7 +119,6 b' var IPython = (function (IPython) {'
119 119 this.ws_url = ws_url;
120 120 this.kernel_url = this.base_url + "/" + this.kernel_id;
121 121 this.start_channels();
122 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
123 122 };
124 123
125 124
@@ -144,11 +143,7 b' var IPython = (function (IPython) {'
144 143 this.shell_channel = new this.WebSocket(ws_url + "/shell");
145 144 this.stdin_channel = new this.WebSocket(ws_url + "/stdin");
146 145 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
147 send_cookie = function(){
148 // send the session id so the Session object Python-side
149 // has the same identity
150 this.send(that.session_id + ':' + document.cookie);
151 };
146
152 147 var already_called_onclose = false; // only alert once
153 148 var ws_closed_early = function(evt){
154 149 if (already_called_onclose){
@@ -170,7 +165,7 b' var IPython = (function (IPython) {'
170 165 };
171 166 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
172 167 for (var i=0; i < channels.length; i++) {
173 channels[i].onopen = send_cookie;
168 channels[i].onopen = $.proxy(this._ws_opened, this);
174 169 channels[i].onclose = ws_closed_early;
175 170 }
176 171 // switch from early-close to late-close message after 1s
@@ -187,7 +182,27 b' var IPython = (function (IPython) {'
187 182 };
188 183
189 184 /**
190 * Stop the `shell`and `iopub` channels.
185 * Handle a websocket entering the open state
186 * sends session and cookie authentication info as first message.
187 * Once all sockets are open, signal the Kernel.status_started event.
188 * @method _ws_opened
189 */
190 Kernel.prototype._ws_opened = function (evt) {
191 // send the session id so the Session object Python-side
192 // has the same identity
193 evt.target.send(this.session_id + ':' + document.cookie);
194
195 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
196 for (var i=0; i < channels.length; i++) {
197 // if any channel is not ready, don't trigger event.
198 if ( !channels[i].readyState ) return;
199 }
200 // all events ready, trigger started event.
201 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
202 };
203
204 /**
205 * Stop the websocket channels.
191 206 * @method stop_channels
192 207 */
193 208 Kernel.prototype.stop_channels = function () {
@@ -388,9 +403,16 b' var IPython = (function (IPython) {'
388 403 };
389 404
390 405
406 Kernel.prototype.clear_callbacks_for_msg = function (msg_id) {
407 if (this._msg_callbacks[msg_id] !== undefined ) {
408 delete this._msg_callbacks[msg_id];
409 }
410 };
411
412
391 413 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
392 414 this._msg_callbacks[msg_id] = callbacks || {};
393 }
415 };
394 416
395 417
396 418 Kernel.prototype._handle_shell_reply = function (e) {
@@ -18,20 +18,6 b''
18 18 .start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;}
19 19 .end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;}
20 20 .center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;}
21 body{background-color:white;position:absolute;left:0px;right:0px;top:0px;bottom:0px;overflow:visible;}
22 div#header{display:none;}
23 #ipython_notebook{padding-left:16px;}
24 #noscript{width:auto;padding-top:16px;padding-bottom:16px;text-align:center;font-size:22px;color:red;font-weight:bold;}
25 #ipython_notebook img{font-family:Verdana,"Helvetica Neue",Arial,Helvetica,Geneva,sans-serif;height:24px;text-decoration:none;color:black;}
26 #site{width:100%;display:none;}
27 .ui-button .ui-button-text{padding:0.2em 0.8em;font-size:77%;}
28 input.ui-button{padding:0.3em 0.9em;}
29 .navbar span{margin-top:3px;}
30 span#login_widget{float:right;}
31 .nav-header{text-transform:none;}
32 .navbar-nobg{background-color:transparent;background-image:none;}
33 #header>span{margin-top:10px;}
34 .modal-body{max-height:500px;}
35 21 .center-nav{display:inline-block;margin-bottom:-4px;}
36 22 .alternate_upload{background-color:none;display:inline;}
37 23 .alternate_upload.form{padding:0;margin:0;}
@@ -67,8 +53,7 b' input.engine_num_input{height:20px;margin-bottom:2px;padding-top:0;padding-botto'
67 53 .ansibgpurple{background-color:magenta;}
68 54 .ansibgcyan{background-color:cyan;}
69 55 .ansibggray{background-color:gray;}
70 .cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}.cell.selected{border-radius:4px;border:thin #ababab solid;}
71 div.cell{width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 7px;outline:none;}
56 div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 7px;outline:none;}div.cell.selected{border-radius:4px;border:thin #ababab solid;}
72 57 div.prompt{width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
73 58 .celltoolbar{border:thin solid #CFCFCF;border-bottom:none;background:#EEE;border-top-right-radius:3px;border-top-left-radius:3px;width:100%;-webkit-box-pack:end;height:22px;}
74 59 .no_input_radius{border-top-right-radius:0px;border-top-left-radius:0px;}
@@ -98,7 +83,7 b' div.out_prompt_overlay:hover{-webkit-box-shadow:inset 0 0 1px #000000;-moz-box-s'
98 83 div.output_prompt{color:darkred;}
99 84 .CodeMirror{line-height:1.231em;height:auto;background:none;}
100 85 .CodeMirror-scroll{overflow-y:hidden;overflow-x:auto;}
101 @-moz-document {.CodeMirror-scroll{overflow-x:hidden;}}.CodeMirror-lines{padding:0.4em;}
86 @-moz-document url-prefix(){.CodeMirror-scroll{overflow-x:hidden;}}.CodeMirror-lines{padding:0.4em;}
102 87 .CodeMirror-linenumber{padding:0 8px 0 4px;}
103 88 .CodeMirror-gutters{border-bottom-left-radius:4px;border-top-left-radius:4px;}
104 89 .CodeMirror pre{padding:0;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
@@ -8,6 +8,7 b''
8 8
9 9 // base
10 10 @import "../base/less/style.less";
11 @import "../base/less/page.less";
11 12
12 13 // auth
13 14 @import "../auth/less/style.less";
@@ -1434,8 +1434,7 b' input.engine_num_input{height:20px;margin-bottom:2px;padding-top:0;padding-botto'
1434 1434 .ansibgpurple{background-color:magenta;}
1435 1435 .ansibgcyan{background-color:cyan;}
1436 1436 .ansibggray{background-color:gray;}
1437 .cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}.cell.selected{border-radius:4px;border:thin #ababab solid;}
1438 div.cell{width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 7px;outline:none;}
1437 div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 7px;outline:none;}div.cell.selected{border-radius:4px;border:thin #ababab solid;}
1439 1438 div.prompt{width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
1440 1439 .celltoolbar{border:thin solid #CFCFCF;border-bottom:none;background:#EEE;border-top-right-radius:3px;border-top-left-radius:3px;width:100%;-webkit-box-pack:end;height:22px;}
1441 1440 .no_input_radius{border-top-right-radius:0px;border-top-left-radius:0px;}
@@ -1465,7 +1464,7 b' div.out_prompt_overlay:hover{-webkit-box-shadow:inset 0 0 1px #000000;-moz-box-s'
1465 1464 div.output_prompt{color:darkred;}
1466 1465 .CodeMirror{line-height:1.231em;height:auto;background:none;}
1467 1466 .CodeMirror-scroll{overflow-y:hidden;overflow-x:auto;}
1468 @-moz-document {.CodeMirror-scroll{overflow-x:hidden;}}.CodeMirror-lines{padding:0.4em;}
1467 @-moz-document url-prefix(){.CodeMirror-scroll{overflow-x:hidden;}}.CodeMirror-lines{padding:0.4em;}
1469 1468 .CodeMirror-linenumber{padding:0 8px 0 4px;}
1470 1469 .CodeMirror-gutters{border-bottom-left-radius:4px;border-top-left-radius:4px;}
1471 1470 .CodeMirror pre{padding:0;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
@@ -34,14 +34,13 b' import zmq'
34 34 from IPython.external.ssh import tunnel
35 35
36 36 # IPython imports
37 # from IPython.config import Configurable
37 from IPython.config import Configurable
38 38 from IPython.core.profiledir import ProfileDir
39 from IPython.utils.localinterfaces import LOCALHOST
39 from IPython.utils.localinterfaces import localhost
40 40 from IPython.utils.path import filefind, get_ipython_dir
41 41 from IPython.utils.py3compat import str_to_bytes, bytes_to_str
42 42 from IPython.utils.traitlets import (
43 43 Bool, Integer, Unicode, CaselessStrEnum,
44 HasTraits,
45 44 )
46 45
47 46
@@ -50,7 +49,7 b' from IPython.utils.traitlets import ('
50 49 #-----------------------------------------------------------------------------
51 50
52 51 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
53 control_port=0, ip=LOCALHOST, key=b'', transport='tcp',
52 control_port=0, ip='', key=b'', transport='tcp',
54 53 signature_scheme='hmac-sha256',
55 54 ):
56 55 """Generates a JSON config file, including the selection of random ports.
@@ -91,6 +90,8 b' def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, '
91 90 and 'sha256' is the default hash function.
92 91
93 92 """
93 if not ip:
94 ip = localhost()
94 95 # default to temporary connector file
95 96 if not fname:
96 97 fname = tempfile.mktemp('.json')
@@ -383,7 +384,7 b' channel_socket_types = {'
383 384
384 385 port_names = [ "%s_port" % channel for channel in ('shell', 'stdin', 'iopub', 'hb', 'control')]
385 386
386 class ConnectionFileMixin(HasTraits):
387 class ConnectionFileMixin(Configurable):
387 388 """Mixin for configurable classes that work with connection files"""
388 389
389 390 # The addresses for the communication channels
@@ -392,7 +393,7 b' class ConnectionFileMixin(HasTraits):'
392 393
393 394 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
394 395
395 ip = Unicode(LOCALHOST, config=True,
396 ip = Unicode(config=True,
396 397 help="""Set the kernel\'s IP address [default localhost].
397 398 If the IP address is something other than localhost, then
398 399 Consoles on other machines will be able to connect
@@ -406,7 +407,7 b' class ConnectionFileMixin(HasTraits):'
406 407 else:
407 408 return 'kernel-ipc'
408 409 else:
409 return LOCALHOST
410 return localhost()
410 411
411 412 def _ip_changed(self, name, old, new):
412 413 if new == '*':
@@ -1,5 +1,4 b''
1 """Base class to manage a running kernel
2 """
1 """Base class to manage a running kernel"""
3 2
4 3 #-----------------------------------------------------------------------------
5 4 # Copyright (C) 2013 The IPython Development Team
@@ -24,7 +23,7 b' import zmq'
24 23 # Local imports
25 24 from IPython.config.configurable import LoggingConfigurable
26 25 from IPython.utils.importstring import import_item
27 from IPython.utils.localinterfaces import LOCAL_IPS
26 from IPython.utils.localinterfaces import is_local_ip, local_ips
28 27 from IPython.utils.traitlets import (
29 28 Any, Instance, Unicode, List, Bool, Type, DottedObjectName
30 29 )
@@ -185,11 +184,11 b' class KernelManager(LoggingConfigurable, ConnectionFileMixin):'
185 184 keyword arguments that are passed down to build the kernel_cmd
186 185 and launching the kernel (e.g. Popen kwargs).
187 186 """
188 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
187 if self.transport == 'tcp' and not is_local_ip(self.ip):
189 188 raise RuntimeError("Can only launch a kernel on a local interface. "
190 189 "Make sure that the '*_address' attributes are "
191 190 "configured properly. "
192 "Currently valid addresses are: %s"%LOCAL_IPS
191 "Currently valid addresses are: %s" % local_ips()
193 192 )
194 193
195 194 # write connection file / get default ports
@@ -171,7 +171,7 b' class MultiKernelManager(LoggingConfigurable):'
171 171 self.log.info("Signaled Kernel %s with %s" % (kernel_id, signum))
172 172
173 173 @kernel_method
174 def restart_kernel(self, kernel_id):
174 def restart_kernel(self, kernel_id, now=False):
175 175 """Restart a kernel by its uuid, keeping the same ports.
176 176
177 177 Parameters
@@ -11,101 +11,18 b''
11 11 # Imports
12 12 #-------------------------------------------------------------------------------
13 13
14 import os
15 import shutil
16 14 import sys
17 import tempfile
18
19 from contextlib import contextmanager
20 from subprocess import PIPE
21 15
22 16 import nose.tools as nt
23 17
24 from IPython.kernel import KernelManager
25 from IPython.kernel.tests.test_message_spec import execute, flush_channels
26 from IPython.testing import decorators as dec
27 from IPython.utils import path
18 from IPython.testing import decorators as dec, tools as tt
19 from IPython.utils import py3compat
20
21 from .utils import new_kernel, kernel, TIMEOUT, assemble_output, execute, flush_channels
28 22
29 23 #-------------------------------------------------------------------------------
30 24 # Tests
31 25 #-------------------------------------------------------------------------------
32 IPYTHONDIR = None
33 save_env = None
34 save_get_ipython_dir = None
35
36 def setup():
37 """setup temporary IPYTHONDIR for tests"""
38 global IPYTHONDIR
39 global save_env
40 global save_get_ipython_dir
41
42 IPYTHONDIR = tempfile.mkdtemp()
43
44 save_env = os.environ.copy()
45 os.environ["IPYTHONDIR"] = IPYTHONDIR
46
47 save_get_ipython_dir = path.get_ipython_dir
48 path.get_ipython_dir = lambda : IPYTHONDIR
49
50
51 def teardown():
52 path.get_ipython_dir = save_get_ipython_dir
53 os.environ = save_env
54
55 try:
56 shutil.rmtree(IPYTHONDIR)
57 except (OSError, IOError):
58 # no such file
59 pass
60
61
62 @contextmanager
63 def new_kernel():
64 """start a kernel in a subprocess, and wait for it to be ready
65
66 Returns
67 -------
68 kernel_manager: connected KernelManager instance
69 """
70 KM = KernelManager()
71
72 KM.start_kernel(stdout=PIPE, stderr=PIPE)
73 KC = KM.client()
74 KC.start_channels()
75
76 # wait for kernel to be ready
77 KC.shell_channel.execute("import sys")
78 KC.shell_channel.get_msg(block=True, timeout=5)
79 flush_channels(KC)
80 try:
81 yield KC
82 finally:
83 KC.stop_channels()
84 KM.shutdown_kernel()
85
86
87 def assemble_output(iopub):
88 """assemble stdout/err from an execution"""
89 stdout = ''
90 stderr = ''
91 while True:
92 msg = iopub.get_msg(block=True, timeout=1)
93 msg_type = msg['msg_type']
94 content = msg['content']
95 if msg_type == 'status' and content['execution_state'] == 'idle':
96 # idle message signals end of output
97 break
98 elif msg['msg_type'] == 'stream':
99 if content['name'] == 'stdout':
100 stdout = stdout + content['data']
101 elif content['name'] == 'stderr':
102 stderr = stderr + content['data']
103 else:
104 raise KeyError("bad stream: %r" % content['name'])
105 else:
106 # other output, ignored
107 pass
108 return stdout, stderr
109 26
110 27
111 28 def _check_mp_mode(kc, expected=False, stream="stdout"):
@@ -116,16 +33,17 b' def _check_mp_mode(kc, expected=False, stream="stdout"):'
116 33 nt.assert_equal(eval(stdout.strip()), expected)
117 34
118 35
36 # printing tests
37
119 38 def test_simple_print():
120 39 """simple print statement in kernel"""
121 with new_kernel() as kc:
40 with kernel() as kc:
122 41 iopub = kc.iopub_channel
123 42 msg_id, content = execute(kc=kc, code="print ('hi')")
124 43 stdout, stderr = assemble_output(iopub)
125 44 nt.assert_equal(stdout, 'hi\n')
126 45 nt.assert_equal(stderr, '')
127 46 _check_mp_mode(kc, expected=False)
128 print ('hello')
129 47
130 48
131 49 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
@@ -161,7 +79,7 b' def test_subprocess_print():'
161 79
162 80 def test_subprocess_noprint():
163 81 """mp.Process without print doesn't trigger iostream mp_mode"""
164 with new_kernel() as kc:
82 with kernel() as kc:
165 83 iopub = kc.iopub_channel
166 84
167 85 np = 5
@@ -202,3 +120,51 b' def test_subprocess_error():'
202 120 _check_mp_mode(kc, expected=False)
203 121 _check_mp_mode(kc, expected=False, stream="stderr")
204 122
123 # raw_input tests
124
125 def test_raw_input():
126 """test [raw_]input"""
127 with kernel() as kc:
128 iopub = kc.iopub_channel
129
130 input_f = "input" if py3compat.PY3 else "raw_input"
131 theprompt = "prompt> "
132 code = 'print({input_f}("{theprompt}"))'.format(**locals())
133 msg_id = kc.execute(code, allow_stdin=True)
134 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
135 nt.assert_equal(msg['header']['msg_type'], u'input_request')
136 content = msg['content']
137 nt.assert_equal(content['prompt'], theprompt)
138 text = "some text"
139 kc.input(text)
140 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
141 nt.assert_equal(reply['content']['status'], 'ok')
142 stdout, stderr = assemble_output(iopub)
143 nt.assert_equal(stdout, text + "\n")
144
145
146 @dec.skipif(py3compat.PY3)
147 def test_eval_input():
148 """test input() on Python 2"""
149 with kernel() as kc:
150 iopub = kc.iopub_channel
151
152 input_f = "input" if py3compat.PY3 else "raw_input"
153 theprompt = "prompt> "
154 code = 'print(input("{theprompt}"))'.format(**locals())
155 msg_id = kc.execute(code, allow_stdin=True)
156 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
157 nt.assert_equal(msg['header']['msg_type'], u'input_request')
158 content = msg['content']
159 nt.assert_equal(content['prompt'], theprompt)
160 kc.input("1+1")
161 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
162 nt.assert_equal(reply['content']['status'], 'ok')
163 stdout, stderr = assemble_output(iopub)
164 nt.assert_equal(stdout, "2\n")
165
166
167 def test_help_output():
168 """ipython kernel --help-all works"""
169 tt.help_all_output_test('kernel')
170
@@ -26,18 +26,11 b' class TestKernelManager(TestCase):'
26 26 def _run_lifecycle(self, km):
27 27 km.start_kernel(stdout=PIPE, stderr=PIPE)
28 28 self.assertTrue(km.is_alive())
29 km.restart_kernel()
29 km.restart_kernel(now=True)
30 30 self.assertTrue(km.is_alive())
31 # We need a delay here to give the restarting kernel a chance to
32 # restart. Otherwise, the interrupt will kill it, causing the test
33 # suite to hang. The reason it *hangs* is that the shutdown
34 # message for the restart sometimes hasn't been sent to the kernel.
35 # Because linger is oo on the shell channel, the context can't
36 # close until the message is sent to the kernel, which is not dead.
37 time.sleep(1.0)
38 31 km.interrupt_kernel()
39 32 self.assertTrue(isinstance(km, KernelManager))
40 km.shutdown_kernel()
33 km.shutdown_kernel(now=True)
41 34
42 35 def test_tcp_lifecycle(self):
43 36 km = self._get_tcp_km()
@@ -20,14 +20,12 b' Authors'
20 20 import nose.tools as nt
21 21
22 22 # Our own imports
23 from IPython.testing import decorators as dec
24 23 from IPython.kernel.launcher import swallow_argv
25 24
26 25 #-----------------------------------------------------------------------------
27 26 # Classes and functions
28 27 #-----------------------------------------------------------------------------
29 28
30 @dec.parametric
31 29 def test_swallow_argv():
32 30 tests = [
33 31 # expected , argv , aliases, flags
@@ -56,5 +54,5 b' def test_swallow_argv():'
56 54 "expected : %r" % expected,
57 55 "returned : %r" % stripped,
58 56 ])
59 yield nt.assert_equal(expected, stripped, message)
57 nt.assert_equal(expected, stripped, message)
60 58
@@ -1,7 +1,7 b''
1 """Test suite for our zeromq-based messaging specification.
1 """Test suite for our zeromq-based message specification.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2010-2011 The IPython Development Team
4 # Copyright (C) 2010 The IPython Development Team
5 5 #
6 6 # Distributed under the terms of the BSD License. The full license is in
7 7 # the file COPYING.txt, distributed as part of this software.
@@ -15,77 +15,25 b' import nose.tools as nt'
15 15
16 16 from IPython.kernel import KernelManager
17 17
18 from IPython.testing import decorators as dec
19 18 from IPython.utils.traitlets import (
20 19 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
21 20 )
22 21
22 from .utils import TIMEOUT, start_global_kernel, flush_channels, execute
23
23 24 #-----------------------------------------------------------------------------
24 # Global setup and utilities
25 # Globals
25 26 #-----------------------------------------------------------------------------
26
27 STARTUP_TIMEOUT = 60
28 TIMEOUT = 15
27 KC = None
29 28
30 29 def setup():
31 global KM, KC
32 KM = KernelManager()
33 KM.start_kernel(stdout=PIPE, stderr=PIPE)
34 KC = KM.client()
35 KC.start_channels()
36
37 # wait for kernel to be ready
38 try:
39 msg = KC.iopub_channel.get_msg(block=True, timeout=STARTUP_TIMEOUT)
40 except Empty:
41 pass
42 msg_id = KC.kernel_info()
43 KC.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT)
44 flush_channels()
45
46
47 def teardown():
48 KC.stop_channels()
49 KM.shutdown_kernel()
50
51
52 def flush_channels(kc=None):
53 """flush any messages waiting on the queue"""
54 if kc is None:
55 kc = KC
56 for channel in (kc.shell_channel, kc.iopub_channel):
57 while True:
58 try:
59 msg = channel.get_msg(block=True, timeout=0.1)
60 except Empty:
61 break
62 else:
63 list(validate_message(msg))
64
65
66 def execute(code='', kc=None, **kwargs):
67 """wrapper for doing common steps for validating an execution request"""
68 if kc is None:
69 kc = KC
70 msg_id = kc.execute(code=code, **kwargs)
71 reply = kc.get_shell_msg(timeout=TIMEOUT)
72 list(validate_message(reply, 'execute_reply', msg_id))
73 busy = kc.get_iopub_msg(timeout=TIMEOUT)
74 list(validate_message(busy, 'status', msg_id))
75 nt.assert_equal(busy['content']['execution_state'], 'busy')
76
77 if not kwargs.get('silent'):
78 pyin = kc.get_iopub_msg(timeout=TIMEOUT)
79 list(validate_message(pyin, 'pyin', msg_id))
80 nt.assert_equal(pyin['content']['code'], code)
81
82 return msg_id, reply['content']
30 global KC
31 KC = start_global_kernel()
83 32
84 33 #-----------------------------------------------------------------------------
85 # MSG Spec References
34 # Message Spec References
86 35 #-----------------------------------------------------------------------------
87 36
88
89 37 class Reference(HasTraits):
90 38
91 39 """
@@ -101,14 +49,14 b' class Reference(HasTraits):'
101 49 def check(self, d):
102 50 """validate a dict against our traits"""
103 51 for key in self.trait_names():
104 yield nt.assert_true(key in d, "Missing key: %r, should be found in %s" % (key, d))
52 nt.assert_in(key, d)
105 53 # FIXME: always allow None, probably not a good idea
106 54 if d[key] is None:
107 55 continue
108 56 try:
109 57 setattr(self, key, d[key])
110 58 except TraitError as e:
111 yield nt.assert_true(False, str(e))
59 nt.assert_true(False, str(e))
112 60
113 61
114 62 class RMessage(Reference):
@@ -133,14 +81,11 b' class ExecuteReply(Reference):'
133 81 status = Enum((u'ok', u'error'))
134 82
135 83 def check(self, d):
136 for tst in Reference.check(self, d):
137 yield tst
84 Reference.check(self, d)
138 85 if d['status'] == 'ok':
139 for tst in ExecuteReplyOkay().check(d):
140 yield tst
86 ExecuteReplyOkay().check(d)
141 87 elif d['status'] == 'error':
142 for tst in ExecuteReplyError().check(d):
143 yield tst
88 ExecuteReplyError().check(d)
144 89
145 90
146 91 class ExecuteReplyOkay(Reference):
@@ -177,11 +122,9 b' class OInfoReply(Reference):'
177 122 source = Unicode()
178 123
179 124 def check(self, d):
180 for tst in Reference.check(self, d):
181 yield tst
125 Reference.check(self, d)
182 126 if d['argspec'] is not None:
183 for tst in ArgSpec().check(d['argspec']):
184 yield tst
127 ArgSpec().check(d['argspec'])
185 128
186 129
187 130 class ArgSpec(Reference):
@@ -212,10 +155,8 b' class KernelInfoReply(Reference):'
212 155
213 156 def _ipython_version_changed(self, name, old, new):
214 157 for v in new:
215 nt.assert_true(
216 isinstance(v, int) or isinstance(v, basestring),
217 'expected int or string as version component, got {0!r}'
218 .format(v))
158 assert isinstance(v, int) or isinstance(v, basestring), \
159 'expected int or string as version component, got {0!r}'.format(v)
219 160
220 161
221 162 # IOPub messages
@@ -241,8 +182,8 b' class DisplayData(Reference):'
241 182 data = Dict()
242 183 def _data_changed(self, name, old, new):
243 184 for k,v in new.iteritems():
244 nt.assert_true(mime_pat.match(k))
245 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
185 assert mime_pat.match(k)
186 nt.assert_is_instance(v, basestring)
246 187
247 188
248 189 class PyOut(Reference):
@@ -250,8 +191,8 b' class PyOut(Reference):'
250 191 data = Dict()
251 192 def _data_changed(self, name, old, new):
252 193 for k,v in new.iteritems():
253 nt.assert_true(mime_pat.match(k))
254 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
194 assert mime_pat.match(k)
195 nt.assert_is_instance(v, basestring)
255 196
256 197
257 198 references = {
@@ -282,13 +223,12 b' def validate_message(msg, msg_type=None, parent=None):'
282 223 """
283 224 RMessage().check(msg)
284 225 if msg_type:
285 yield nt.assert_equal(msg['msg_type'], msg_type)
226 nt.assert_equal(msg['msg_type'], msg_type)
286 227 if parent:
287 yield nt.assert_equal(msg['parent_header']['msg_id'], parent)
228 nt.assert_equal(msg['parent_header']['msg_id'], parent)
288 229 content = msg['content']
289 230 ref = references[msg['msg_type']]
290 for tst in ref.check(content):
291 yield tst
231 ref.check(content)
292 232
293 233
294 234 #-----------------------------------------------------------------------------
@@ -297,54 +237,47 b' def validate_message(msg, msg_type=None, parent=None):'
297 237
298 238 # Shell channel
299 239
300 @dec.parametric
301 240 def test_execute():
302 241 flush_channels()
303 242
304 243 msg_id = KC.execute(code='x=1')
305 244 reply = KC.get_shell_msg(timeout=TIMEOUT)
306 for tst in validate_message(reply, 'execute_reply', msg_id):
307 yield tst
245 validate_message(reply, 'execute_reply', msg_id)
308 246
309 247
310 @dec.parametric
311 248 def test_execute_silent():
312 249 flush_channels()
313 250 msg_id, reply = execute(code='x=1', silent=True)
314 251
315 252 # flush status=idle
316 253 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
317 for tst in validate_message(status, 'status', msg_id):
318 yield tst
254 validate_message(status, 'status', msg_id)
319 255 nt.assert_equal(status['content']['execution_state'], 'idle')
320 256
321 yield nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
257 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
322 258 count = reply['execution_count']
323 259
324 260 msg_id, reply = execute(code='x=2', silent=True)
325 261
326 262 # flush status=idle
327 263 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
328 for tst in validate_message(status, 'status', msg_id):
329 yield tst
330 yield nt.assert_equal(status['content']['execution_state'], 'idle')
264 validate_message(status, 'status', msg_id)
265 nt.assert_equal(status['content']['execution_state'], 'idle')
331 266
332 yield nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
267 nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1)
333 268 count_2 = reply['execution_count']
334 yield nt.assert_equal(count_2, count)
269 nt.assert_equal(count_2, count)
335 270
336 271
337 @dec.parametric
338 272 def test_execute_error():
339 273 flush_channels()
340 274
341 275 msg_id, reply = execute(code='1/0')
342 yield nt.assert_equal(reply['status'], 'error')
343 yield nt.assert_equal(reply['ename'], 'ZeroDivisionError')
276 nt.assert_equal(reply['status'], 'error')
277 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
344 278
345 279 pyerr = KC.iopub_channel.get_msg(timeout=TIMEOUT)
346 for tst in validate_message(pyerr, 'pyerr', msg_id):
347 yield tst
280 validate_message(pyerr, 'pyerr', msg_id)
348 281
349 282
350 283 def test_execute_inc():
@@ -405,17 +338,14 b' def test_user_expressions_fail():'
405 338 nt.assert_equal(foo['ename'], 'NameError')
406 339
407 340
408 @dec.parametric
409 341 def test_oinfo():
410 342 flush_channels()
411 343
412 344 msg_id = KC.object_info('a')
413 345 reply = KC.get_shell_msg(timeout=TIMEOUT)
414 for tst in validate_message(reply, 'object_info_reply', msg_id):
415 yield tst
346 validate_message(reply, 'object_info_reply', msg_id)
416 347
417 348
418 @dec.parametric
419 349 def test_oinfo_found():
420 350 flush_channels()
421 351
@@ -423,15 +353,13 b' def test_oinfo_found():'
423 353
424 354 msg_id = KC.object_info('a')
425 355 reply = KC.get_shell_msg(timeout=TIMEOUT)
426 for tst in validate_message(reply, 'object_info_reply', msg_id):
427 yield tst
356 validate_message(reply, 'object_info_reply', msg_id)
428 357 content = reply['content']
429 yield nt.assert_true(content['found'])
358 assert content['found']
430 359 argspec = content['argspec']
431 yield nt.assert_true(argspec is None, "didn't expect argspec dict, got %r" % argspec)
360 nt.assert_is(argspec, None)
432 361
433 362
434 @dec.parametric
435 363 def test_oinfo_detail():
436 364 flush_channels()
437 365
@@ -439,28 +367,24 b' def test_oinfo_detail():'
439 367
440 368 msg_id = KC.object_info('ip.object_inspect', detail_level=2)
441 369 reply = KC.get_shell_msg(timeout=TIMEOUT)
442 for tst in validate_message(reply, 'object_info_reply', msg_id):
443 yield tst
370 validate_message(reply, 'object_info_reply', msg_id)
444 371 content = reply['content']
445 yield nt.assert_true(content['found'])
372 assert content['found']
446 373 argspec = content['argspec']
447 yield nt.assert_true(isinstance(argspec, dict), "expected non-empty argspec dict, got %r" % argspec)
448 yield nt.assert_equal(argspec['defaults'], [0])
374 nt.assert_is_instance(argspec, dict, "expected non-empty argspec dict, got %r" % argspec)
375 nt.assert_equal(argspec['defaults'], [0])
449 376
450 377
451 @dec.parametric
452 378 def test_oinfo_not_found():
453 379 flush_channels()
454 380
455 381 msg_id = KC.object_info('dne')
456 382 reply = KC.get_shell_msg(timeout=TIMEOUT)
457 for tst in validate_message(reply, 'object_info_reply', msg_id):
458 yield tst
383 validate_message(reply, 'object_info_reply', msg_id)
459 384 content = reply['content']
460 yield nt.assert_false(content['found'])
385 nt.assert_false(content['found'])
461 386
462 387
463 @dec.parametric
464 388 def test_complete():
465 389 flush_channels()
466 390
@@ -468,49 +392,42 b' def test_complete():'
468 392
469 393 msg_id = KC.complete('al', 'al', 2)
470 394 reply = KC.get_shell_msg(timeout=TIMEOUT)
471 for tst in validate_message(reply, 'complete_reply', msg_id):
472 yield tst
395 validate_message(reply, 'complete_reply', msg_id)
473 396 matches = reply['content']['matches']
474 397 for name in ('alpha', 'albert'):
475 yield nt.assert_true(name in matches, "Missing match: %r" % name)
398 nt.assert_in(name, matches)
476 399
477 400
478 @dec.parametric
479 401 def test_kernel_info_request():
480 402 flush_channels()
481 403
482 404 msg_id = KC.kernel_info()
483 405 reply = KC.get_shell_msg(timeout=TIMEOUT)
484 for tst in validate_message(reply, 'kernel_info_reply', msg_id):
485 yield tst
406 validate_message(reply, 'kernel_info_reply', msg_id)
486 407
487 408
488 409 # IOPub channel
489 410
490 411
491 @dec.parametric
492 412 def test_stream():
493 413 flush_channels()
494 414
495 415 msg_id, reply = execute("print('hi')")
496 416
497 417 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
498 for tst in validate_message(stdout, 'stream', msg_id):
499 yield tst
418 validate_message(stdout, 'stream', msg_id)
500 419 content = stdout['content']
501 yield nt.assert_equal(content['name'], u'stdout')
502 yield nt.assert_equal(content['data'], u'hi\n')
420 nt.assert_equal(content['name'], u'stdout')
421 nt.assert_equal(content['data'], u'hi\n')
503 422
504 423
505 @dec.parametric
506 424 def test_display_data():
507 425 flush_channels()
508 426
509 427 msg_id, reply = execute("from IPython.core.display import display; display(1)")
510 428
511 429 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
512 for tst in validate_message(display, 'display_data', parent=msg_id):
513 yield tst
430 validate_message(display, 'display_data', parent=msg_id)
514 431 data = display['content']['data']
515 yield nt.assert_equal(data['text/plain'], u'1')
432 nt.assert_equal(data['text/plain'], u'1')
516 433
@@ -7,7 +7,7 b' from unittest import TestCase'
7 7 from IPython.testing import decorators as dec
8 8
9 9 from IPython.config.loader import Config
10 from IPython.utils.localinterfaces import LOCALHOST
10 from IPython.utils.localinterfaces import localhost
11 11 from IPython.kernel import KernelManager
12 12 from IPython.kernel.multikernelmanager import MultiKernelManager
13 13
@@ -31,20 +31,13 b' class TestKernelManager(TestCase):'
31 31 self.assertTrue(kid in km)
32 32 self.assertTrue(kid in km.list_kernel_ids())
33 33 self.assertEqual(len(km),1)
34 km.restart_kernel(kid)
34 km.restart_kernel(kid, now=True)
35 35 self.assertTrue(km.is_alive(kid))
36 36 self.assertTrue(kid in km.list_kernel_ids())
37 # We need a delay here to give the restarting kernel a chance to
38 # restart. Otherwise, the interrupt will kill it, causing the test
39 # suite to hang. The reason it *hangs* is that the shutdown
40 # message for the restart sometimes hasn't been sent to the kernel.
41 # Because linger is oo on the shell channel, the context can't
42 # close until the message is sent to the kernel, which is not dead.
43 time.sleep(1.0)
44 37 km.interrupt_kernel(kid)
45 38 k = km.get_kernel(kid)
46 39 self.assertTrue(isinstance(k, KernelManager))
47 km.shutdown_kernel(kid)
40 km.shutdown_kernel(kid, now=True)
48 41 self.assertTrue(not kid in km)
49 42
50 43 def _run_cinfo(self, km, transport, ip):
@@ -63,7 +56,7 b' class TestKernelManager(TestCase):'
63 56 self.assertTrue('hb_port' in cinfo)
64 57 stream = km.connect_hb(kid)
65 58 stream.close()
66 km.shutdown_kernel(kid)
59 km.shutdown_kernel(kid, now=True)
67 60
68 61 def test_tcp_lifecycle(self):
69 62 km = self._get_tcp_km()
@@ -71,7 +64,7 b' class TestKernelManager(TestCase):'
71 64
72 65 def test_tcp_cinfo(self):
73 66 km = self._get_tcp_km()
74 self._run_cinfo(km, 'tcp', LOCALHOST)
67 self._run_cinfo(km, 'tcp', localhost())
75 68
76 69 @dec.skip_win32
77 70 def test_ipc_lifecycle(self):
@@ -14,8 +14,6 b' Authors'
14 14
15 15 import nose.tools as nt
16 16
17 from IPython.testing import decorators as dec
18
19 17 from IPython.kernel import launcher, connect
20 18 from IPython import kernel
21 19
@@ -23,25 +21,21 b' from IPython import kernel'
23 21 # Classes and functions
24 22 #-----------------------------------------------------------------------------
25 23
26 @dec.parametric
27 24 def test_kms():
28 25 for base in ("", "Multi"):
29 26 KM = base + "KernelManager"
30 yield nt.assert_true(KM in dir(kernel), KM)
27 nt.assert_in(KM, dir(kernel))
31 28
32 @dec.parametric
33 29 def test_kcs():
34 30 for base in ("", "Blocking"):
35 31 KM = base + "KernelClient"
36 yield nt.assert_true(KM in dir(kernel), KM)
32 nt.assert_in(KM, dir(kernel))
37 33
38 @dec.parametric
39 34 def test_launcher():
40 35 for name in launcher.__all__:
41 yield nt.assert_true(name in dir(kernel), name)
36 nt.assert_in(name, dir(kernel))
42 37
43 @dec.parametric
44 38 def test_connect():
45 39 for name in connect.__all__:
46 yield nt.assert_true(name in dir(kernel), name)
40 nt.assert_in(name, dir(kernel))
47 41
@@ -19,7 +19,7 b' from threading import Thread'
19 19
20 20 import zmq
21 21
22 from IPython.utils.localinterfaces import LOCALHOST
22 from IPython.utils.localinterfaces import localhost
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Code
@@ -29,7 +29,9 b' from IPython.utils.localinterfaces import LOCALHOST'
29 29 class Heartbeat(Thread):
30 30 "A simple ping-pong style heartbeat that runs in a thread."
31 31
32 def __init__(self, context, addr=('tcp', LOCALHOST, 0)):
32 def __init__(self, context, addr=None):
33 if addr is None:
34 addr = ('tcp', localhost(), 0)
33 35 Thread.__init__(self)
34 36 self.context = context
35 37 self.transport, self.ip, self.port = addr
@@ -87,7 +87,7 b' class Kernel(Configurable):'
87 87 if self.shell is not None:
88 88 self.shell.user_module = new
89 89
90 user_ns = Dict(default_value=None)
90 user_ns = Instance(dict, args=None, allow_none=True)
91 91 def _user_ns_changed(self, name, old, new):
92 92 if self.shell is not None:
93 93 self.shell.user_ns = new
@@ -131,6 +131,7 b' class Kernel(Configurable):'
131 131 # A reference to the Python builtin 'raw_input' function.
132 132 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
133 133 _sys_raw_input = Any()
134 _sys_eval_input = Any()
134 135
135 136 # set of aborted msg_ids
136 137 aborted = Set()
@@ -352,15 +353,18 b' class Kernel(Configurable):'
352 353 # raw_input in the user namespace.
353 354 if content.get('allow_stdin', False):
354 355 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
356 input = lambda prompt='': eval(raw_input(prompt))
355 357 else:
356 raw_input = lambda prompt='' : self._no_raw_input()
358 raw_input = input = lambda prompt='' : self._no_raw_input()
357 359
358 360 if py3compat.PY3:
359 361 self._sys_raw_input = __builtin__.input
360 362 __builtin__.input = raw_input
361 363 else:
362 364 self._sys_raw_input = __builtin__.raw_input
365 self._sys_eval_input = __builtin__.input
363 366 __builtin__.raw_input = raw_input
367 __builtin__.input = input
364 368
365 369 # Set the parent message of the display hook and out streams.
366 370 shell.displayhook.set_parent(parent)
@@ -403,6 +407,7 b' class Kernel(Configurable):'
403 407 __builtin__.input = self._sys_raw_input
404 408 else:
405 409 __builtin__.raw_input = self._sys_raw_input
410 __builtin__.input = self._sys_eval_input
406 411
407 412 reply_content[u'status'] = status
408 413
@@ -39,7 +39,7 b' from IPython.core.shellapp import ('
39 39 InteractiveShellApp, shell_flags, shell_aliases
40 40 )
41 41 from IPython.utils import io
42 from IPython.utils.localinterfaces import LOCALHOST
42 from IPython.utils.localinterfaces import localhost
43 43 from IPython.utils.path import filefind
44 44 from IPython.utils.py3compat import str_to_bytes
45 45 from IPython.utils.traitlets import (
@@ -156,7 +156,8 b' class IPKernelApp(BaseIPythonApplication, InteractiveShellApp):'
156 156 else:
157 157 return 'kernel-ipc'
158 158 else:
159 return LOCALHOST
159 return localhost()
160
160 161 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
161 162 shell_port = Integer(0, config=True, help="set the shell (ROUTER) port [default: random]")
162 163 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
@@ -436,8 +436,15 b' class Session(Configurable):'
436 436 msg = dict(a=[1,'hi'])
437 437 try:
438 438 packed = pack(msg)
439 except Exception:
440 raise ValueError("packer could not serialize a simple message")
439 except Exception as e:
440 msg = "packer '{packer}' could not serialize a simple message: {e}{jsonmsg}"
441 if self.packer == 'json':
442 jsonmsg = "\nzmq.utils.jsonapi.jsonmod = %s" % jsonapi.jsonmod
443 else:
444 jsonmsg = ""
445 raise ValueError(
446 msg.format(packer=self.packer, e=e, jsonmsg=jsonmsg)
447 )
441 448
442 449 # ensure packed message is bytes
443 450 if not isinstance(packed, bytes):
@@ -446,8 +453,16 b' class Session(Configurable):'
446 453 # check that unpack is pack's inverse
447 454 try:
448 455 unpacked = unpack(packed)
449 except Exception:
450 raise ValueError("unpacker could not handle the packer's output")
456 assert unpacked == msg
457 except Exception as e:
458 msg = "unpacker '{unpacker}' could not handle output from packer '{packer}': {e}{jsonmsg}"
459 if self.packer == 'json':
460 jsonmsg = "\nzmq.utils.jsonapi.jsonmod = %s" % jsonapi.jsonmod
461 else:
462 jsonmsg = ""
463 raise ValueError(
464 msg.format(packer=self.packer, unpacker=self.unpacker, e=e, jsonmsg=jsonmsg)
465 )
451 466
452 467 # check datetime support
453 468 msg = dict(t=datetime.now())
@@ -46,7 +46,6 b" DTYPES = ('uint8', 'float64', 'int32', [('g', 'float32')], '|S10')"
46 46 # Tests
47 47 #-------------------------------------------------------------------------------
48 48
49 @dec.parametric
50 49 def test_roundtrip_simple():
51 50 for obj in [
52 51 'hello',
@@ -55,18 +54,16 b' def test_roundtrip_simple():'
55 54 (b'123', 'hello'),
56 55 ]:
57 56 obj2 = roundtrip(obj)
58 yield nt.assert_equals(obj, obj2)
57 nt.assert_equal(obj, obj2)
59 58
60 @dec.parametric
61 59 def test_roundtrip_nested():
62 60 for obj in [
63 61 dict(a=range(5), b={1:b'hello'}),
64 62 [range(5),[range(3),(1,[b'whoda'])]],
65 63 ]:
66 64 obj2 = roundtrip(obj)
67 yield nt.assert_equals(obj, obj2)
65 nt.assert_equal(obj, obj2)
68 66
69 @dec.parametric
70 67 def test_roundtrip_buffered():
71 68 for obj in [
72 69 dict(a=b"x"*1025),
@@ -74,10 +71,10 b' def test_roundtrip_buffered():'
74 71 [b"hello"*501, 1,2,3]
75 72 ]:
76 73 bufs = serialize_object(obj)
77 yield nt.assert_equals(len(bufs), 2)
74 nt.assert_equal(len(bufs), 2)
78 75 obj2, remainder = unserialize_object(bufs)
79 yield nt.assert_equals(remainder, [])
80 yield nt.assert_equals(obj, obj2)
76 nt.assert_equal(remainder, [])
77 nt.assert_equal(obj, obj2)
81 78
82 79 def _scrub_nan(A):
83 80 """scrub nans out of empty arrays
@@ -93,7 +90,6 b' def _scrub_nan(A):'
93 90 # e.g. str dtype
94 91 pass
95 92
96 @dec.parametric
97 93 @dec.skip_without('numpy')
98 94 def test_numpy():
99 95 import numpy
@@ -104,12 +100,11 b' def test_numpy():'
104 100 _scrub_nan(A)
105 101 bufs = serialize_object(A)
106 102 B, r = unserialize_object(bufs)
107 yield nt.assert_equals(r, [])
108 yield nt.assert_equals(A.shape, B.shape)
109 yield nt.assert_equals(A.dtype, B.dtype)
110 yield assert_array_equal(A,B)
103 nt.assert_equal(r, [])
104 nt.assert_equal(A.shape, B.shape)
105 nt.assert_equal(A.dtype, B.dtype)
106 assert_array_equal(A,B)
111 107
112 @dec.parametric
113 108 @dec.skip_without('numpy')
114 109 def test_recarray():
115 110 import numpy
@@ -124,12 +119,11 b' def test_recarray():'
124 119
125 120 bufs = serialize_object(A)
126 121 B, r = unserialize_object(bufs)
127 yield nt.assert_equals(r, [])
128 yield nt.assert_equals(A.shape, B.shape)
129 yield nt.assert_equals(A.dtype, B.dtype)
130 yield assert_array_equal(A,B)
122 nt.assert_equal(r, [])
123 nt.assert_equal(A.shape, B.shape)
124 nt.assert_equal(A.dtype, B.dtype)
125 assert_array_equal(A,B)
131 126
132 @dec.parametric
133 127 @dec.skip_without('numpy')
134 128 def test_numpy_in_seq():
135 129 import numpy
@@ -140,15 +134,14 b' def test_numpy_in_seq():'
140 134 _scrub_nan(A)
141 135 bufs = serialize_object((A,1,2,b'hello'))
142 136 canned = pickle.loads(bufs[0])
143 yield nt.assert_true(canned[0], CannedArray)
137 nt.assert_is_instance(canned[0], CannedArray)
144 138 tup, r = unserialize_object(bufs)
145 139 B = tup[0]
146 yield nt.assert_equals(r, [])
147 yield nt.assert_equals(A.shape, B.shape)
148 yield nt.assert_equals(A.dtype, B.dtype)
149 yield assert_array_equal(A,B)
140 nt.assert_equal(r, [])
141 nt.assert_equal(A.shape, B.shape)
142 nt.assert_equal(A.dtype, B.dtype)
143 assert_array_equal(A,B)
150 144
151 @dec.parametric
152 145 @dec.skip_without('numpy')
153 146 def test_numpy_in_dict():
154 147 import numpy
@@ -159,27 +152,25 b' def test_numpy_in_dict():'
159 152 _scrub_nan(A)
160 153 bufs = serialize_object(dict(a=A,b=1,c=range(20)))
161 154 canned = pickle.loads(bufs[0])
162 yield nt.assert_true(canned['a'], CannedArray)
155 nt.assert_is_instance(canned['a'], CannedArray)
163 156 d, r = unserialize_object(bufs)
164 157 B = d['a']
165 yield nt.assert_equals(r, [])
166 yield nt.assert_equals(A.shape, B.shape)
167 yield nt.assert_equals(A.dtype, B.dtype)
168 yield assert_array_equal(A,B)
158 nt.assert_equal(r, [])
159 nt.assert_equal(A.shape, B.shape)
160 nt.assert_equal(A.dtype, B.dtype)
161 assert_array_equal(A,B)
169 162
170 @dec.parametric
171 163 def test_class():
172 164 @interactive
173 165 class C(object):
174 166 a=5
175 167 bufs = serialize_object(dict(C=C))
176 168 canned = pickle.loads(bufs[0])
177 yield nt.assert_true(canned['C'], CannedClass)
169 nt.assert_is_instance(canned['C'], CannedClass)
178 170 d, r = unserialize_object(bufs)
179 171 C2 = d['C']
180 yield nt.assert_equal(C2.a, C.a)
172 nt.assert_equal(C2.a, C.a)
181 173
182 @dec.parametric
183 174 def test_class_oldstyle():
184 175 @interactive
185 176 class C:
@@ -187,42 +178,38 b' def test_class_oldstyle():'
187 178
188 179 bufs = serialize_object(dict(C=C))
189 180 canned = pickle.loads(bufs[0])
190 yield nt.assert_true(isinstance(canned['C'], CannedClass))
181 nt.assert_is_instance(canned['C'], CannedClass)
191 182 d, r = unserialize_object(bufs)
192 183 C2 = d['C']
193 yield nt.assert_equal(C2.a, C.a)
184 nt.assert_equal(C2.a, C.a)
194 185
195 @dec.parametric
196 186 def test_tuple():
197 187 tup = (lambda x:x, 1)
198 188 bufs = serialize_object(tup)
199 189 canned = pickle.loads(bufs[0])
200 yield nt.assert_true(isinstance(canned, tuple))
190 nt.assert_is_instance(canned, tuple)
201 191 t2, r = unserialize_object(bufs)
202 yield nt.assert_equal(t2[0](t2[1]), tup[0](tup[1]))
192 nt.assert_equal(t2[0](t2[1]), tup[0](tup[1]))
203 193
204 194 point = namedtuple('point', 'x y')
205 195
206 @dec.parametric
207 196 def test_namedtuple():
208 197 p = point(1,2)
209 198 bufs = serialize_object(p)
210 199 canned = pickle.loads(bufs[0])
211 yield nt.assert_true(isinstance(canned, point))
200 nt.assert_is_instance(canned, point)
212 201 p2, r = unserialize_object(bufs, globals())
213 yield nt.assert_equal(p2.x, p.x)
214 yield nt.assert_equal(p2.y, p.y)
202 nt.assert_equal(p2.x, p.x)
203 nt.assert_equal(p2.y, p.y)
215 204
216 @dec.parametric
217 205 def test_list():
218 206 lis = [lambda x:x, 1]
219 207 bufs = serialize_object(lis)
220 208 canned = pickle.loads(bufs[0])
221 yield nt.assert_true(isinstance(canned, list))
209 nt.assert_is_instance(canned, list)
222 210 l2, r = unserialize_object(bufs)
223 yield nt.assert_equal(l2[0](l2[1]), lis[0](lis[1]))
211 nt.assert_equal(l2[0](l2[1]), lis[0](lis[1]))
224 212
225 @dec.parametric
226 213 def test_class_inheritance():
227 214 @interactive
228 215 class C(object):
@@ -234,8 +221,8 b' def test_class_inheritance():'
234 221
235 222 bufs = serialize_object(dict(D=D))
236 223 canned = pickle.loads(bufs[0])
237 yield nt.assert_true(canned['D'], CannedClass)
224 nt.assert_is_instance(canned['D'], CannedClass)
238 225 d, r = unserialize_object(bufs)
239 226 D2 = d['D']
240 yield nt.assert_equal(D2.a, D.a)
241 yield nt.assert_equal(D2.b, D.b)
227 nt.assert_equal(D2.a, D.a)
228 nt.assert_equal(D2.b, D.b)
@@ -20,6 +20,12 b' from zmq.eventloop.zmqstream import ZMQStream'
20 20
21 21 from IPython.kernel.zmq import session as ss
22 22
23 def _bad_packer(obj):
24 raise TypeError("I don't work")
25
26 def _bad_unpacker(bytes):
27 raise TypeError("I don't work either")
28
23 29 class SessionTestCase(BaseZMQTestCase):
24 30
25 31 def setUp(self):
@@ -222,4 +228,44 b' class TestSession(SessionTestCase):'
222 228 self.assertTrue(len(session.digest_history) == 100)
223 229 session._add_digest(uuid.uuid4().bytes)
224 230 self.assertTrue(len(session.digest_history) == 91)
225
231
232 def test_bad_pack(self):
233 try:
234 session = ss.Session(pack=_bad_packer)
235 except ValueError as e:
236 self.assertIn("could not serialize", str(e))
237 self.assertIn("don't work", str(e))
238 else:
239 self.fail("Should have raised ValueError")
240
241 def test_bad_unpack(self):
242 try:
243 session = ss.Session(unpack=_bad_unpacker)
244 except ValueError as e:
245 self.assertIn("could not handle output", str(e))
246 self.assertIn("don't work either", str(e))
247 else:
248 self.fail("Should have raised ValueError")
249
250 def test_bad_packer(self):
251 try:
252 session = ss.Session(packer=__name__ + '._bad_packer')
253 except ValueError as e:
254 self.assertIn("could not serialize", str(e))
255 self.assertIn("don't work", str(e))
256 else:
257 self.fail("Should have raised ValueError")
258
259 def test_bad_unpacker(self):
260 try:
261 session = ss.Session(unpacker=__name__ + '._bad_unpacker')
262 except ValueError as e:
263 self.assertIn("could not handle output", str(e))
264 self.assertIn("don't work either", str(e))
265 else:
266 self.fail("Should have raised ValueError")
267
268 def test_bad_roundtrip(self):
269 with self.assertRaises(ValueError):
270 session= ss.Session(unpack=lambda b: 5)
271
@@ -8,10 +8,38 b' def test_ipython_start_kernel_userns():'
8 8 cmd = ('from IPython import start_kernel\n'
9 9 'ns = {"tre": 123}\n'
10 10 'start_kernel(user_ns=ns)')
11
11
12 12 with setup_kernel(cmd) as client:
13 13 msg_id = client.object_info('tre')
14 14 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
15 15 content = msg['content']
16 16 assert content['found']
17 nt.assert_equal(content['string_form'], u'123') No newline at end of file
17 nt.assert_equal(content['string_form'], u'123')
18
19 # user_module should be an instance of DummyMod
20 msg_id = client.execute("usermod = get_ipython().user_module")
21 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
22 content = msg['content']
23 nt.assert_equal(content['status'], u'ok')
24 msg_id = client.object_info('usermod')
25 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
26 content = msg['content']
27 assert content['found']
28 nt.assert_in('DummyMod', content['string_form'])
29
30 def test_ipython_start_kernel_no_userns():
31 # Issue #4188 - user_ns should be passed to shell as None, not {}
32 cmd = ('from IPython import start_kernel\n'
33 'start_kernel()')
34
35 with setup_kernel(cmd) as client:
36 # user_module should not be an instance of DummyMod
37 msg_id = client.execute("usermod = get_ipython().user_module")
38 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
39 content = msg['content']
40 nt.assert_equal(content['status'], u'ok')
41 msg_id = client.object_info('usermod')
42 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
43 content = msg['content']
44 assert content['found']
45 nt.assert_not_in('DummyMod', content['string_form'])
@@ -86,14 +86,11 b' class ZMQDisplayPublisher(DisplayPublisher):'
86 86 parent=self.parent_header, ident=self.topic,
87 87 )
88 88
89 def clear_output(self, stdout=True, stderr=True, other=True):
90 content = dict(stdout=stdout, stderr=stderr, other=other)
91
92 if stdout:
93 print('\r', file=sys.stdout, end='')
94 if stderr:
95 print('\r', file=sys.stderr, end='')
96
89 def clear_output(self, wait=False):
90 content = dict(wait=wait)
91
92 print('\r', file=sys.stdout, end='')
93 print('\r', file=sys.stderr, end='')
97 94 self._flush_streams()
98 95
99 96 self.session.send(
@@ -14,14 +14,14 b' The classes are (see their docstrings for further details):'
14 14 - Demo: pure python demos
15 15
16 16 - IPythonDemo: demos with input to be processed by IPython as if it had been
17 typed interactively (so magics work, as well as any other special syntax you
18 may have added via input prefilters).
17 typed interactively (so magics work, as well as any other special syntax you
18 may have added via input prefilters).
19 19
20 20 - LineDemo: single-line version of the Demo class. These demos are executed
21 one line at a time, and require no markup.
21 one line at a time, and require no markup.
22 22
23 23 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
24 executed a line at a time, but processed via IPython).
24 executed a line at a time, but processed via IPython).
25 25
26 26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
27 27 declares an empty marquee and a pre_cmd that clears the screen before each
@@ -214,18 +214,18 b' class Demo(object):'
214 214 Optional inputs:
215 215
216 216 - title: a string to use as the demo name. Of most use when the demo
217 you are making comes from an object that has no filename, or if you
218 want an alternate denotation distinct from the filename.
217 you are making comes from an object that has no filename, or if you
218 want an alternate denotation distinct from the filename.
219 219
220 220 - arg_str(''): a string of arguments, internally converted to a list
221 just like sys.argv, so the demo script can see a similar
222 environment.
221 just like sys.argv, so the demo script can see a similar
222 environment.
223 223
224 224 - auto_all(None): global flag to run all blocks automatically without
225 confirmation. This attribute overrides the block-level tags and
226 applies to the whole demo. It is an attribute of the object, and
227 can be changed at runtime simply by reassigning it to a boolean
228 value.
225 confirmation. This attribute overrides the block-level tags and
226 applies to the whole demo. It is an attribute of the object, and
227 can be changed at runtime simply by reassigning it to a boolean
228 value.
229 229 """
230 230 if hasattr(src, "read"):
231 231 # It seems to be a file or a file-like object
@@ -481,6 +481,19 b' set_inputhook = inputhook_manager.set_inputhook'
481 481 current_gui = inputhook_manager.current_gui
482 482 clear_app_refs = inputhook_manager.clear_app_refs
483 483
484 guis = {None: clear_inputhook,
485 GUI_NONE: clear_inputhook,
486 GUI_OSX: lambda app=False: None,
487 GUI_TK: enable_tk,
488 GUI_GTK: enable_gtk,
489 GUI_WX: enable_wx,
490 GUI_QT: enable_qt4, # qt3 not supported
491 GUI_QT4: enable_qt4,
492 GUI_GLUT: enable_glut,
493 GUI_PYGLET: enable_pyglet,
494 GUI_GTK3: enable_gtk3,
495 }
496
484 497
485 498 # Convenience function to switch amongst them
486 499 def enable_gui(gui=None, app=None):
@@ -507,18 +520,6 b' def enable_gui(gui=None, app=None):'
507 520 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
508 521 one.
509 522 """
510 guis = {None: clear_inputhook,
511 GUI_NONE: clear_inputhook,
512 GUI_OSX: lambda app=False: None,
513 GUI_TK: enable_tk,
514 GUI_GTK: enable_gtk,
515 GUI_WX: enable_wx,
516 GUI_QT: enable_qt4, # qt3 not supported
517 GUI_QT4: enable_qt4,
518 GUI_GLUT: enable_glut,
519 GUI_PYGLET: enable_pyglet,
520 GUI_GTK3: enable_gtk3,
521 }
522 523 try:
523 524 gui_hook = guis[gui]
524 525 except KeyError:
@@ -93,9 +93,9 b' class InteractiveRunner(object):'
93 93 - program: command to execute the given program.
94 94
95 95 - prompts: a list of patterns to match as valid prompts, in the
96 format used by pexpect. This basically means that it can be either
97 a string (to be compiled as a regular expression) or a list of such
98 (it must be a true list, as pexpect does type checks).
96 format used by pexpect. This basically means that it can be either
97 a string (to be compiled as a regular expression) or a list of such
98 (it must be a true list, as pexpect does type checks).
99 99
100 100 If more than one prompt is given, the first is treated as the main
101 101 program prompt and the others as 'continuation' prompts, like
@@ -107,19 +107,19 b' class InteractiveRunner(object):'
107 107 Optional inputs:
108 108
109 109 - args(None): optional list of strings to pass as arguments to the
110 child program.
110 child program.
111 111
112 112 - out(sys.stdout): if given, an output stream to be used when writing
113 output. The only requirement is that it must have a .write() method.
113 output. The only requirement is that it must have a .write() method.
114 114
115 115 Public members not parameterized in the constructor:
116 116
117 117 - delaybeforesend(0): Newer versions of pexpect have a delay before
118 sending each new input. For our purposes here, it's typically best
119 to just set this to zero, but if you encounter reliability problems
120 or want an interactive run to pause briefly at each prompt, just
121 increase this value (it is measured in seconds). Note that this
122 variable is not honored at all by older versions of pexpect.
118 sending each new input. For our purposes here, it's typically best
119 to just set this to zero, but if you encounter reliability problems
120 or want an interactive run to pause briefly at each prompt, just
121 increase this value (it is measured in seconds). Note that this
122 variable is not honored at all by older versions of pexpect.
123 123 """
124 124
125 125 self.program = program
@@ -154,7 +154,7 b' class InteractiveRunner(object):'
154 154
155 155 Inputs:
156 156
157 -fname: name of the file to execute.
157 - fname: name of the file to execute.
158 158
159 159 See the run_source docstring for the meaning of the optional
160 160 arguments."""
@@ -173,15 +173,15 b' class InteractiveRunner(object):'
173 173 Inputs:
174 174
175 175 - source: a string of code to be executed, or an open file object we
176 can iterate over.
176 can iterate over.
177 177
178 178 Optional inputs:
179 179
180 180 - interact(False): if true, start to interact with the running
181 program at the end of the script. Otherwise, just exit.
181 program at the end of the script. Otherwise, just exit.
182 182
183 183 - get_output(False): if true, capture the output of the child process
184 (filtering the input commands out) and return it as a string.
184 (filtering the input commands out) and return it as a string.
185 185
186 186 Returns:
187 187 A string containing the process output, but only if requested.
@@ -229,7 +229,17 b' class PrettyPrinter(_PrettyPrinterBase):'
229 229 self.buffer.append(Breakable(sep, width, self))
230 230 self.buffer_width += width
231 231 self._break_outer_groups()
232
232
233 def break_(self):
234 """
235 Explicitly insert a newline into the output, maintaining correct indentation.
236 """
237 self.flush()
238 self.output.write(self.newline)
239 self.output.write(' ' * self.indentation)
240 self.output_width = self.indentation
241 self.buffer_width = 0
242
233 243
234 244 def begin_group(self, indent=0, open=''):
235 245 """
@@ -478,8 +488,12 b' def _default_pprint(obj, p, cycle):'
478 488 """
479 489 klass = getattr(obj, '__class__', None) or type(obj)
480 490 if getattr(klass, '__repr__', None) not in _baseclass_reprs:
481 # A user-provided repr.
482 p.text(repr(obj))
491 # A user-provided repr. Find newlines and replace them with p.break_()
492 output = repr(obj)
493 for idx,output_line in enumerate(output.splitlines()):
494 if idx:
495 p.break_()
496 p.text(output_line)
483 497 return
484 498 p.begin_group(1, '<')
485 499 p.pretty(klass)
@@ -58,6 +58,23 b' class NoModule(object):'
58 58
59 59 NoModule.__module__ = None
60 60
61 class Breaking(object):
62 def _repr_pretty_(self, p, cycle):
63 with p.group(4,"TG: ",":"):
64 p.text("Breaking(")
65 p.break_()
66 p.text(")")
67
68 class BreakingRepr(object):
69 def __repr__(self):
70 return "Breaking(\n)"
71
72 class BreakingReprParent(object):
73 def _repr_pretty_(self, p, cycle):
74 with p.group(4,"TG: ",":"):
75 p.pretty(BreakingRepr())
76
77
61 78
62 79 def test_indentation():
63 80 """Test correct indentation in groups"""
@@ -118,3 +135,19 b' def test_pprint_nomod():'
118 135 """
119 136 output = pretty.pretty(NoModule)
120 137 nt.assert_equal(output, 'NoModule')
138
139 def test_pprint_break():
140 """
141 Test that p.break_ produces expected output
142 """
143 output = pretty.pretty(Breaking())
144 expected = "TG: Breaking(\n ):"
145 nt.assert_equal(output, expected)
146
147 def test_pprint_break_repr():
148 """
149 Test that p.break_ is used in repr
150 """
151 output = pretty.pretty(BreakingReprParent())
152 expected = "TG: Breaking(\n ):"
153 nt.assert_equal(output, expected) No newline at end of file
@@ -1,8 +1,9 b''
1 1 from .export import *
2 2 from .html import HTMLExporter
3 3 from .slides import SlidesExporter
4 from .exporter import Exporter
4 from .templateexporter import TemplateExporter
5 5 from .latex import LatexExporter
6 6 from .markdown import MarkdownExporter
7 7 from .python import PythonExporter
8 8 from .rst import RSTExporter
9 from .exporter import Exporter
@@ -19,6 +19,7 b' from IPython.nbformat.v3.nbbase import NotebookNode'
19 19 from IPython.config import Config
20 20
21 21 from .exporter import Exporter
22 from .templateexporter import TemplateExporter
22 23 from .html import HTMLExporter
23 24 from .slides import SlidesExporter
24 25 from .latex import LatexExporter
@@ -122,7 +123,7 b' def export(exporter, nb, **kw):'
122 123 return output, resources
123 124
124 125 exporter_map = dict(
125 custom=Exporter,
126 custom=TemplateExporter,
126 127 html=HTMLExporter,
127 128 slides=SlidesExporter,
128 129 latex=LatexExporter,
@@ -19,56 +19,21 b' from __future__ import print_function, absolute_import'
19 19 # Stdlib imports
20 20 import io
21 21 import os
22 import inspect
23 22 import copy
24 23 import collections
25 24 import datetime
26 25
27 # other libs/dependencies
28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader, TemplateNotFound
29 26
30 27 # IPython imports
31 28 from IPython.config.configurable import LoggingConfigurable
32 29 from IPython.config import Config
33 30 from IPython.nbformat import current as nbformat
34 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict, Any
31 from IPython.utils.traitlets import MetaHasTraits, Unicode, List
35 32 from IPython.utils.importstring import import_item
36 from IPython.utils.text import indent
37 from IPython.utils import py3compat
33 from IPython.utils import text, py3compat
38 34
39 35 from IPython.nbconvert import preprocessors as nbpreprocessors
40 from IPython.nbconvert import filters
41 36
42 #-----------------------------------------------------------------------------
43 # Globals and constants
44 #-----------------------------------------------------------------------------
45
46 #Jinja2 extensions to load.
47 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
48
49 default_filters = {
50 'indent': indent,
51 'markdown2html': filters.markdown2html,
52 'ansi2html': filters.ansi2html,
53 'filter_data_type': filters.DataTypeFilter,
54 'get_lines': filters.get_lines,
55 'highlight2html': filters.highlight2html,
56 'highlight2latex': filters.highlight2latex,
57 'ipython2python': filters.ipython2python,
58 'posix_path': filters.posix_path,
59 'markdown2latex': filters.markdown2latex,
60 'markdown2rst': filters.markdown2rst,
61 'comment_lines': filters.comment_lines,
62 'strip_ansi': filters.strip_ansi,
63 'strip_dollars': filters.strip_dollars,
64 'strip_files_prefix': filters.strip_files_prefix,
65 'html2text' : filters.html2text,
66 'add_anchor': filters.add_anchor,
67 'ansi2latex': filters.ansi2latex,
68 'strip_math_space': filters.strip_math_space,
69 'wrap_text': filters.wrap_text,
70 'escape_latex': filters.escape_latex,
71 }
72 37
73 38 #-----------------------------------------------------------------------------
74 39 # Class
@@ -81,70 +46,21 b' class ResourcesDict(collections.defaultdict):'
81 46
82 47 class Exporter(LoggingConfigurable):
83 48 """
84 Exports notebooks into other file formats. Uses Jinja 2 templating engine
85 to output new formats. Inherit from this class if you are creating a new
86 template type along with new filters/preprocessors. If the filters/
87 preprocessors provided by default suffice, there is no need to inherit from
88 this class. Instead, override the template_file and file_extension
89 traits via a config file.
90
91 {filters}
49 Class containing methods that sequentially run a list of preprocessors on a
50 NotebookNode object and then return the modified NotebookNode object and
51 accompanying resources dict.
92 52 """
93
94 # finish the docstring
95 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
96
97
98 template_file = Unicode(u'default',
99 config=True,
100 help="Name of the template file to use")
101 def _template_file_changed(self, name, old, new):
102 if new=='default':
103 self.template_file = self.default_template
104 else:
105 self.template_file = new
106 self.template = None
107 self._load_template()
108
109 default_template = Unicode(u'')
110 template = Any()
111 environment = Any()
112 53
113 54 file_extension = Unicode(
114 'txt', config=True,
55 'txt', config=True,
115 56 help="Extension of the file that should be written to disk"
116 57 )
117 58
118 template_path = List(['.'], config=True)
119 def _template_path_changed(self, name, old, new):
120 self._load_template()
121
122 default_template_path = Unicode(
123 os.path.join("..", "templates"),
124 help="Path where the template files are located.")
125
126 template_skeleton_path = Unicode(
127 os.path.join("..", "templates", "skeleton"),
128 help="Path where the template skeleton files are located.")
129
130 #Jinja block definitions
131 jinja_comment_block_start = Unicode("", config=True)
132 jinja_comment_block_end = Unicode("", config=True)
133 jinja_variable_block_start = Unicode("", config=True)
134 jinja_variable_block_end = Unicode("", config=True)
135 jinja_logic_block_start = Unicode("", config=True)
136 jinja_logic_block_end = Unicode("", config=True)
137
138 #Extension that the template files use.
139 template_extension = Unicode(".tpl", config=True)
140
141 59 #Configurability, allows the user to easily add filters and preprocessors.
142 60 preprocessors = List(config=True,
143 61 help="""List of preprocessors, by name or namespace, to enable.""")
144 62
145 filters = Dict(config=True,
146 help="""Dictionary of filters, by name and namespace, to add to the Jinja
147 environment.""")
63 _preprocessors = None
148 64
149 65 default_preprocessors = List([nbpreprocessors.coalesce_streams,
150 66 nbpreprocessors.SVG2PDFPreprocessor,
@@ -152,42 +68,34 b' class Exporter(LoggingConfigurable):'
152 68 nbpreprocessors.CSSHTMLHeaderPreprocessor,
153 69 nbpreprocessors.RevealHelpPreprocessor,
154 70 nbpreprocessors.LatexPreprocessor,
155 nbpreprocessors.SphinxPreprocessor],
71 nbpreprocessors.HighlightMagicsPreprocessor],
156 72 config=True,
157 73 help="""List of preprocessors available by default, by name, namespace,
158 74 instance, or type.""")
159 75
160 76
161 def __init__(self, config=None, extra_loaders=None, **kw):
77 def __init__(self, config=None, **kw):
162 78 """
163 79 Public constructor
164
80
165 81 Parameters
166 82 ----------
167 83 config : config
168 84 User configuration instance.
169 extra_loaders : list[of Jinja Loaders]
170 ordered list of Jinja loader to find templates. Will be tried in order
171 before the default FileSystem ones.
172 template : str (optional, kw arg)
173 Template to use when exporting.
174 85 """
175 86 if not config:
176 87 config = self.default_config
177
88
178 89 super(Exporter, self).__init__(config=config, **kw)
179 90
180 91 #Init
181 self._init_template()
182 self._init_environment(extra_loaders=extra_loaders)
183 92 self._init_preprocessors()
184 self._init_filters()
185 93
186 94
187 95 @property
188 96 def default_config(self):
189 97 return Config()
190
98
191 99 def _config_changed(self, name, old, new):
192 100 """When setting config, make sure to start with our default_config"""
193 101 c = self.default_config
@@ -196,56 +104,18 b' class Exporter(LoggingConfigurable):'
196 104 if c != old:
197 105 self.config = c
198 106 super(Exporter, self)._config_changed(name, old, c)
199
200
201 def _load_template(self):
202 """Load the Jinja template object from the template file
203
204 This is a no-op if the template attribute is already defined,
205 or the Jinja environment is not setup yet.
206
207 This is triggered by various trait changes that would change the template.
208 """
209 if self.template is not None:
210 return
211 # called too early, do nothing
212 if self.environment is None:
213 return
214 # Try different template names during conversion. First try to load the
215 # template by name with extension added, then try loading the template
216 # as if the name is explicitly specified, then try the name as a
217 # 'flavor', and lastly just try to load the template by module name.
218 module_name = self.__module__.rsplit('.', 1)[-1]
219 try_names = []
220 if self.template_file:
221 try_names.extend([
222 self.template_file + self.template_extension,
223 self.template_file,
224 module_name + '_' + self.template_file + self.template_extension,
225 ])
226 try_names.append(module_name + self.template_extension)
227 for try_name in try_names:
228 self.log.debug("Attempting to load template %s", try_name)
229 try:
230 self.template = self.environment.get_template(try_name)
231 except (TemplateNotFound, IOError):
232 pass
233 except Exception as e:
234 self.log.warn("Unexpected exception loading template: %s", try_name, exc_info=True)
235 else:
236 self.log.info("Loaded template %s", try_name)
237 break
238
107
108
239 109 def from_notebook_node(self, nb, resources=None, **kw):
240 110 """
241 111 Convert a notebook from a notebook node instance.
242
112
243 113 Parameters
244 114 ----------
245 115 nb : Notebook node
246 resources : dict (**kw)
247 of additional resources that can be accessed read/write by
248 preprocessors and filters.
116 resources : dict (**kw)
117 of additional resources that can be accessed read/write by
118 preprocessors.
249 119 """
250 120 nb_copy = copy.deepcopy(nb)
251 121 resources = self._init_resources(resources)
@@ -253,26 +123,20 b' class Exporter(LoggingConfigurable):'
253 123 # Preprocess
254 124 nb_copy, resources = self._preprocess(nb_copy, resources)
255 125
256 self._load_template()
257
258 if self.template is not None:
259 output = self.template.render(nb=nb_copy, resources=resources)
260 else:
261 raise IOError('template file "%s" could not be found' % self.template_file)
262 return output, resources
126 return nb_copy, resources
263 127
264 128
265 129 def from_filename(self, filename, resources=None, **kw):
266 130 """
267 131 Convert a notebook from a notebook file.
268
132
269 133 Parameters
270 134 ----------
271 135 filename : str
272 136 Full filename of the notebook file to open and convert.
273 137 """
274 138
275 #Pull the metadata from the filesystem.
139 # Pull the metadata from the filesystem.
276 140 if resources is None:
277 141 resources = ResourcesDict()
278 142 if not 'metadata' in resources or resources['metadata'] == '':
@@ -282,16 +146,16 b' class Exporter(LoggingConfigurable):'
282 146 resources['metadata']['name'] = notebook_name
283 147
284 148 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
285 resources['metadata']['modified_date'] = modified_date.strftime("%B %d, %Y")
286
149 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
150
287 151 with io.open(filename) as f:
288 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
152 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources, **kw)
289 153
290 154
291 155 def from_file(self, file_stream, resources=None, **kw):
292 156 """
293 157 Convert a notebook from a notebook file.
294
158
295 159 Parameters
296 160 ----------
297 161 file_stream : file-like object
@@ -304,10 +168,10 b' class Exporter(LoggingConfigurable):'
304 168 """
305 169 Register a preprocessor.
306 170 Preprocessors are classes that act upon the notebook before it is
307 passed into the Jinja templating engine. Preprocessors are also
171 passed into the Jinja templating engine. preprocessors are also
308 172 capable of passing additional information to the Jinja
309 173 templating engine.
310
174
311 175 Parameters
312 176 ----------
313 177 preprocessor : preprocessor
@@ -317,130 +181,43 b' class Exporter(LoggingConfigurable):'
317 181 isclass = isinstance(preprocessor, type)
318 182 constructed = not isclass
319 183
320 #Handle preprocessor's registration based on it's type
184 # Handle preprocessor's registration based on it's type
321 185 if constructed and isinstance(preprocessor, py3compat.string_types):
322 #Preprocessor is a string, import the namespace and recursively call
323 #this register_preprocessor method
186 # Preprocessor is a string, import the namespace and recursively call
187 # this register_preprocessor method
324 188 preprocessor_cls = import_item(preprocessor)
325 189 return self.register_preprocessor(preprocessor_cls, enabled)
326
190
327 191 if constructed and hasattr(preprocessor, '__call__'):
328 #Preprocessor is a function, no need to construct it.
329 #Register and return the preprocessor.
192 # Preprocessor is a function, no need to construct it.
193 # Register and return the preprocessor.
330 194 if enabled:
331 195 preprocessor.enabled = True
332 196 self._preprocessors.append(preprocessor)
333 197 return preprocessor
334 198
335 199 elif isclass and isinstance(preprocessor, MetaHasTraits):
336 #Preprocessor is configurable. Make sure to pass in new default for
337 #the enabled flag if one was specified.
200 # Preprocessor is configurable. Make sure to pass in new default for
201 # the enabled flag if one was specified.
338 202 self.register_preprocessor(preprocessor(parent=self), enabled)
339 203
340 204 elif isclass:
341 #Preprocessor is not configurable, construct it
205 # Preprocessor is not configurable, construct it
342 206 self.register_preprocessor(preprocessor(), enabled)
343 207
344 208 else:
345 #Preprocessor is an instance of something without a __call__
346 #attribute.
209 # Preprocessor is an instance of something without a __call__
210 # attribute.
347 211 raise TypeError('preprocessor')
348 212
349 213
350 def register_filter(self, name, jinja_filter):
351 """
352 Register a filter.
353 A filter is a function that accepts and acts on one string.
354 The filters are accesible within the Jinja templating engine.
355
356 Parameters
357 ----------
358 name : str
359 name to give the filter in the Jinja engine
360 filter : filter
361 """
362 if jinja_filter is None:
363 raise TypeError('filter')
364 isclass = isinstance(jinja_filter, type)
365 constructed = not isclass
366
367 #Handle filter's registration based on it's type
368 if constructed and isinstance(jinja_filter, py3compat.string_types):
369 #filter is a string, import the namespace and recursively call
370 #this register_filter method
371 filter_cls = import_item(jinja_filter)
372 return self.register_filter(name, filter_cls)
373
374 if constructed and hasattr(jinja_filter, '__call__'):
375 #filter is a function, no need to construct it.
376 self.environment.filters[name] = jinja_filter
377 return jinja_filter
378
379 elif isclass and isinstance(jinja_filter, MetaHasTraits):
380 #filter is configurable. Make sure to pass in new default for
381 #the enabled flag if one was specified.
382 filter_instance = jinja_filter(parent=self)
383 self.register_filter(name, filter_instance )
384
385 elif isclass:
386 #filter is not configurable, construct it
387 filter_instance = jinja_filter()
388 self.register_filter(name, filter_instance)
389
390 else:
391 #filter is an instance of something without a __call__
392 #attribute.
393 raise TypeError('filter')
394
395
396 def _init_template(self):
397 """
398 Make sure a template name is specified. If one isn't specified, try to
399 build one from the information we know.
400 """
401 self._template_file_changed('template_file', self.template_file, self.template_file)
402
403
404 def _init_environment(self, extra_loaders=None):
405 """
406 Create the Jinja templating environment.
407 """
408 here = os.path.dirname(os.path.realpath(__file__))
409 loaders = []
410 if extra_loaders:
411 loaders.extend(extra_loaders)
412
413 paths = self.template_path
414 paths.extend([os.path.join(here, self.default_template_path),
415 os.path.join(here, self.template_skeleton_path)])
416 loaders.append(FileSystemLoader(paths))
417
418 self.environment = Environment(
419 loader= ChoiceLoader(loaders),
420 extensions=JINJA_EXTENSIONS
421 )
422
423 #Set special Jinja2 syntax that will not conflict with latex.
424 if self.jinja_logic_block_start:
425 self.environment.block_start_string = self.jinja_logic_block_start
426 if self.jinja_logic_block_end:
427 self.environment.block_end_string = self.jinja_logic_block_end
428 if self.jinja_variable_block_start:
429 self.environment.variable_start_string = self.jinja_variable_block_start
430 if self.jinja_variable_block_end:
431 self.environment.variable_end_string = self.jinja_variable_block_end
432 if self.jinja_comment_block_start:
433 self.environment.comment_start_string = self.jinja_comment_block_start
434 if self.jinja_comment_block_end:
435 self.environment.comment_end_string = self.jinja_comment_block_end
436
437
438 214 def _init_preprocessors(self):
439 215 """
440 216 Register all of the preprocessors needed for this exporter, disabled
441 217 unless specified explicitly.
442 218 """
443 self._preprocessors = []
219 if self._preprocessors is None:
220 self._preprocessors = []
444 221
445 222 #Load default preprocessors (not necessarly enabled by default).
446 223 if self.default_preprocessors:
@@ -451,21 +228,6 b' class Exporter(LoggingConfigurable):'
451 228 if self.preprocessors:
452 229 for preprocessor in self.preprocessors:
453 230 self.register_preprocessor(preprocessor, enabled=True)
454
455
456 def _init_filters(self):
457 """
458 Register all of the filters required for the exporter.
459 """
460
461 #Add default filters to the Jinja2 environment
462 for key, value in default_filters.items():
463 self.register_filter(key, value)
464
465 #Load user filters. Overwrite existing filters if need be.
466 if self.filters:
467 for key, user_filter in self.filters.items():
468 self.register_filter(key, user_filter)
469 231
470 232
471 233 def _init_resources(self, resources):
@@ -476,7 +238,7 b' class Exporter(LoggingConfigurable):'
476 238 if not isinstance(resources, ResourcesDict):
477 239 new_resources = ResourcesDict()
478 240 new_resources.update(resources)
479 resources = new_resources
241 resources = new_resources
480 242
481 243 #Make sure the metadata extension exists in resources
482 244 if 'metadata' in resources:
@@ -484,29 +246,28 b' class Exporter(LoggingConfigurable):'
484 246 resources['metadata'] = ResourcesDict(resources['metadata'])
485 247 else:
486 248 resources['metadata'] = ResourcesDict()
487 if not resources['metadata']['name']:
249 if not resources['metadata']['name']:
488 250 resources['metadata']['name'] = 'Notebook'
489 251
490 252 #Set the output extension
491 253 resources['output_extension'] = self.file_extension
492 254 return resources
493
255
494 256
495 257 def _preprocess(self, nb, resources):
496 258 """
497 259 Preprocess the notebook before passing it into the Jinja engine.
498 To preprocess the notebook is to apply all of the
499
260 To preprocess the notebook is to apply all of the
261
500 262 Parameters
501 263 ----------
502 264 nb : notebook node
503 265 notebook that is being exported.
504 266 resources : a dict of additional resources that
505 267 can be accessed read/write by preprocessors
506 and filters.
507 268 """
508
509 # Do a copy.deepcopy first,
269
270 # Do a copy.deepcopy first,
510 271 # we are never safe enough with what the preprocessors could do.
511 272 nbc = copy.deepcopy(nb)
512 273 resc = copy.deepcopy(resources)
@@ -19,13 +19,13 b' from IPython.utils.traitlets import Unicode, List'
19 19 from IPython.nbconvert import preprocessors
20 20 from IPython.config import Config
21 21
22 from .exporter import Exporter
22 from .templateexporter import TemplateExporter
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Classes
26 26 #-----------------------------------------------------------------------------
27 27
28 class HTMLExporter(Exporter):
28 class HTMLExporter(TemplateExporter):
29 29 """
30 30 Exports a basic HTML document. This exporter assists with the export of
31 31 HTML. Inherit from it if you are writing your own HTML template and need
@@ -46,7 +46,10 b' class HTMLExporter(Exporter):'
46 46 c = Config({
47 47 'CSSHTMLHeaderPreprocessor':{
48 48 'enabled':True
49 }
49 },
50 'HighlightMagicsPreprocessor': {
51 'enabled':True
52 }
50 53 })
51 54 c.merge(super(HTMLExporter,self).default_config)
52 55 return c
@@ -24,13 +24,13 b' from IPython.utils.traitlets import Unicode, List'
24 24 from IPython.config import Config
25 25
26 26 from IPython.nbconvert import filters, preprocessors
27 from .exporter import Exporter
27 from .templateexporter import TemplateExporter
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Classes and functions
31 31 #-----------------------------------------------------------------------------
32 32
33 class LatexExporter(Exporter):
33 class LatexExporter(TemplateExporter):
34 34 """
35 35 Exports to a Latex template. Inherit from this class if your template is
36 36 LaTeX based and you need custom tranformers/filters. Inherit from it if
@@ -45,7 +45,7 b' class LatexExporter(Exporter):'
45 45 help="Extension of the file that should be written to disk")
46 46
47 47 default_template = Unicode('article', config=True, help="""Template of the
48 data format to use. I.E. 'full' or 'basic'""")
48 data format to use. I.E. 'article' or 'report'""")
49 49
50 50 #Latex constants
51 51 default_template_path = Unicode(
@@ -85,6 +85,9 b' class LatexExporter(Exporter):'
85 85 },
86 86 'SphinxPreprocessor': {
87 87 'enabled':True
88 },
89 'HighlightMagicsPreprocessor': {
90 'enabled':True
88 91 }
89 92 })
90 93 c.merge(super(LatexExporter,self).default_config)
@@ -13,15 +13,16 b' Exporter that will export your ipynb to Markdown.'
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15
16 from IPython.config import Config
16 17 from IPython.utils.traitlets import Unicode
17 18
18 from .exporter import Exporter
19 from .templateexporter import TemplateExporter
19 20
20 21 #-----------------------------------------------------------------------------
21 22 # Classes
22 23 #-----------------------------------------------------------------------------
23 24
24 class MarkdownExporter(Exporter):
25 class MarkdownExporter(TemplateExporter):
25 26 """
26 27 Exports to a markdown document (.md)
27 28 """
@@ -29,3 +30,9 b' class MarkdownExporter(Exporter):'
29 30 file_extension = Unicode(
30 31 'md', config=True,
31 32 help="Extension of the file that should be written to disk")
33
34 @property
35 def default_config(self):
36 c = Config({'ExtractOutputPreprocessor':{'enabled':True}})
37 c.merge(super(MarkdownExporter,self).default_config)
38 return c
@@ -15,13 +15,13 b' Python exporter which exports Notebook code into a PY file.'
15 15
16 16 from IPython.utils.traitlets import Unicode
17 17
18 from .exporter import Exporter
18 from .templateexporter import TemplateExporter
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Classes
22 22 #-----------------------------------------------------------------------------
23 23
24 class PythonExporter(Exporter):
24 class PythonExporter(TemplateExporter):
25 25 """
26 26 Exports a Python code file.
27 27 """
@@ -16,13 +16,13 b' Exporter for exporting notebooks to restructured text.'
16 16 from IPython.utils.traitlets import Unicode
17 17 from IPython.config import Config
18 18
19 from .exporter import Exporter
19 from .templateexporter import TemplateExporter
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Classes
23 23 #-----------------------------------------------------------------------------
24 24
25 class RSTExporter(Exporter):
25 class RSTExporter(TemplateExporter):
26 26 """
27 27 Exports restructured text documents.
28 28 """
@@ -19,13 +19,13 b' from IPython.utils.traitlets import Unicode'
19 19 from IPython.nbconvert import preprocessors
20 20 from IPython.config import Config
21 21
22 from .exporter import Exporter
22 from .templateexporter import TemplateExporter
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Classes
26 26 #-----------------------------------------------------------------------------
27 27
28 class SlidesExporter(Exporter):
28 class SlidesExporter(TemplateExporter):
29 29 """
30 30 Exports slides
31 31 """
@@ -47,6 +47,9 b' class SlidesExporter(Exporter):'
47 47 'RevealHelpPreprocessor':{
48 48 'enabled':True,
49 49 },
50 'HighlightMagicsPreprocessor': {
51 'enabled':True
52 }
50 53 })
51 54 c.merge(super(SlidesExporter,self).default_config)
52 55 return c
@@ -27,4 +27,4 b' class ExportersTestsBase(TestsBase):'
27 27
28 28 def _get_notebook(self):
29 29 return os.path.join(self._get_files_path(), 'notebook2.ipynb')
30 No newline at end of file
30
@@ -99,4 +99,4 b' class TestExport(ExportersTestsBase):'
99 99 (output, resources) = export(None, self._get_notebook())
100 100 except TypeError:
101 101 pass
102 No newline at end of file
102
@@ -17,7 +17,7 b' Module with tests for exporter.py'
17 17 from IPython.config import Config
18 18
19 19 from .base import ExportersTestsBase
20 from .cheese import CheesePreprocessor
20 from ...preprocessors.base import Preprocessor
21 21 from ..exporter import Exporter
22 22
23 23
@@ -25,84 +25,35 b' from ..exporter import Exporter'
25 25 # Class
26 26 #-----------------------------------------------------------------------------
27 27
28 class PizzaPreprocessor(Preprocessor):
29 """Simple preprocessor that adds a 'pizza' entry to the NotebookNode. Used
30 to test Exporter.
31 """
32
33 def preprocess(self, nb, resources):
34 nb['pizza'] = 'cheese'
35 return nb, resources
36
37
28 38 class TestExporter(ExportersTestsBase):
29 39 """Contains test functions for exporter.py"""
30 40
31 41
32 42 def test_constructor(self):
33 """
34 Can an Exporter be constructed?
35 """
43 """Can an Exporter be constructed?"""
36 44 Exporter()
37 45
38 46
39 47 def test_export(self):
40 """
41 Can an Exporter export something?
42 """
43 exporter = self._make_exporter()
44 (output, resources) = exporter.from_filename(self._get_notebook())
45 assert len(output) > 0
46
47
48 def test_extract_outputs(self):
49 """
50 If the ExtractOutputPreprocessor is enabled, are outputs extracted?
51 """
52 config = Config({'ExtractOutputPreprocessor': {'enabled': True}})
53 exporter = self._make_exporter(config=config)
54 (output, resources) = exporter.from_filename(self._get_notebook())
55 assert resources is not None
56 assert isinstance(resources['outputs'], dict)
57 assert len(resources['outputs']) > 0
58
59
60 def test_preprocessor_class(self):
61 """
62 Can a preprocessor be added to the preprocessors list by class type?
63 """
64 config = Config({'Exporter': {'preprocessors': [CheesePreprocessor]}})
65 exporter = self._make_exporter(config=config)
66 (output, resources) = exporter.from_filename(self._get_notebook())
67 assert resources is not None
68 assert resources['cheese'] == 'real'
69
70
71 def test_preprocessor_instance(self):
72 """
73 Can a preprocessor be added to the preprocessors list by instance?
74 """
75 config = Config({'Exporter': {'preprocessors': [CheesePreprocessor()]}})
76 exporter = self._make_exporter(config=config)
77 (output, resources) = exporter.from_filename(self._get_notebook())
78 assert resources is not None
79 assert resources['cheese'] == 'real'
80
81
82 def test_preprocessor_dottedobjectname(self):
83 """
84 Can a preprocessor be added to the preprocessors list by dotted object name?
85 """
86 config = Config({'Exporter': {'preprocessors': ['IPython.nbconvert.exporters.tests.cheese.CheesePreprocessor']}})
87 exporter = self._make_exporter(config=config)
88 (output, resources) = exporter.from_filename(self._get_notebook())
89 assert resources is not None
90 assert resources['cheese'] == 'real'
91
92
93 def test_preprocessor_via_method(self):
94 """
95 Can a preprocessor be added via the Exporter convenience method?
96 """
97 exporter = self._make_exporter()
98 exporter.register_preprocessor(CheesePreprocessor, enabled=True)
99 (output, resources) = exporter.from_filename(self._get_notebook())
100 assert resources is not None
101 assert resources['cheese'] == 'real'
102
103
104 def _make_exporter(self, config=None):
105 #Create the exporter instance, make sure to set a template name since
106 #the base Exporter doesn't have a template associated with it.
107 exporter = Exporter(config=config, template_file='python')
108 return exporter
48 """Can an Exporter export something?"""
49 exporter = Exporter()
50 (notebook, resources) = exporter.from_filename(self._get_notebook())
51 assert isinstance(notebook, dict)
52
53
54 def test_preprocessor(self):
55 """Do preprocessors work?"""
56 config = Config({'Exporter': {'preprocessors': [PizzaPreprocessor()]}})
57 exporter = Exporter(config=config)
58 (notebook, resources) = exporter.from_filename(self._get_notebook())
59 self.assertEqual(notebook['pizza'], 'cheese')
@@ -44,18 +44,18 b' class TestLatexExporter(ExportersTestsBase):'
44 44 @onlyif_cmds_exist('pandoc')
45 45 def test_export_book(self):
46 46 """
47 Can a LatexExporter export using 'book' template?
47 Can a LatexExporter export using 'report' template?
48 48 """
49 (output, resources) = LatexExporter(template_file='book').from_filename(self._get_notebook())
49 (output, resources) = LatexExporter(template_file='report').from_filename(self._get_notebook())
50 50 assert len(output) > 0
51 51
52 52
53 53 @onlyif_cmds_exist('pandoc')
54 54 def test_export_basic(self):
55 55 """
56 Can a LatexExporter export using 'basic' template?
56 Can a LatexExporter export using 'article' template?
57 57 """
58 (output, resources) = LatexExporter(template_file='basic').from_filename(self._get_notebook())
58 (output, resources) = LatexExporter(template_file='article').from_filename(self._get_notebook())
59 59 assert len(output) > 0
60 60
61 61
@@ -1,6 +1,7 b''
1 1 from .ansi import *
2 from .citation import *
2 3 from .datatypefilter import *
3 4 from .highlight import *
4 5 from .latex import *
5 6 from .markdown import *
6 from .strings import * No newline at end of file
7 from .strings import *
@@ -14,7 +14,7 b' from within Jinja templates.'
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 from pygments import highlight as pygements_highlight
17 from pygments import highlight as pygements_highlight
18 18 from pygments.lexers import get_lexer_by_name
19 19 from pygments.formatters import HtmlFormatter
20 20 from pygments.formatters import LatexFormatter
@@ -38,51 +38,73 b' __all__ = ['
38 38 ]
39 39
40 40
41 def highlight2html(source, language='ipython'):
41 def highlight2html(source, language='ipython', metadata=None):
42 42 """
43 43 Return a syntax-highlighted version of the input source as html output.
44
44
45 45 Parameters
46 46 ----------
47 47 source : str
48 Source code to highlight the syntax of.
48 source of the cell to highlight
49 49 language : str
50 Language to highlight the syntax of.
50 language to highlight the syntax of
51 metadata : NotebookNode cell metadata
52 metadata of the cell to highlight
51 53 """
52
53 return _pygment_highlight(source, HtmlFormatter(), language)
54
55 return _pygment_highlight(source, HtmlFormatter(), language, metadata)
54 56
55 57
56 def highlight2latex(source, language='ipython'):
58 def highlight2latex(source, language='ipython', metadata=None, strip_verbatim=False):
57 59 """
58 60 Return a syntax-highlighted version of the input source as latex output.
59
61
60 62 Parameters
61 63 ----------
62 64 source : str
63 Source code to highlight the syntax of.
65 source of the cell to highlight
64 66 language : str
65 Language to highlight the syntax of.
67 language to highlight the syntax of
68 metadata : NotebookNode cell metadata
69 metadata of the cell to highlight
70 strip_verbatim : bool
71 remove the Verbatim environment that pygments provides by default
66 72 """
67 return _pygment_highlight(source, LatexFormatter(), language)
73 latex = _pygment_highlight(source, LatexFormatter(), language, metadata)
74 if strip_verbatim:
75 latex = latex.replace(r'\begin{Verbatim}[commandchars=\\\{\}]' + '\n', '')
76 return latex.replace('\n\\end{Verbatim}\n', '')
77 else:
78 return latex
79
68 80
69 81
70 def _pygment_highlight(source, output_formatter, language='ipython'):
82 def _pygment_highlight(source, output_formatter, language='ipython', metadata=None):
71 83 """
72 84 Return a syntax-highlighted version of the input source
73
85
74 86 Parameters
75 87 ----------
76 88 source : str
77 Source code to highlight the syntax of.
89 source of the cell to highlight
78 90 output_formatter : Pygments formatter
79 91 language : str
80 Language to highlight the syntax of.
92 language to highlight the syntax of
93 metadata : NotebookNode cell metadata
94 metadata of the cell to highlight
81 95 """
82
96
97 # If the cell uses a magic extension language,
98 # use the magic language instead.
99 if language == 'ipython' \
100 and metadata \
101 and 'magics_language' in metadata:
102
103 language = metadata['magics_language']
104
83 105 if language == 'ipython':
84 106 lexer = IPythonLexer()
85 107 else:
86 108 lexer = get_lexer_by_name(language, stripall=True)
87 109
88 return pygements_highlight(source, lexer, output_formatter)
110 return pygements_highlight(source, lexer, output_formatter)
@@ -19,6 +19,7 b' templates.'
19 19 import os
20 20 import re
21 21 import textwrap
22 from urllib2 import quote
22 23 from xml.etree import ElementTree
23 24
24 25 from IPython.core.interactiveshell import InteractiveShell
@@ -38,6 +39,8 b' __all__ = ['
38 39 'get_lines',
39 40 'ipython2python',
40 41 'posix_path',
42 'path2url',
43 'add_prompts'
41 44 ]
42 45
43 46
@@ -80,7 +83,7 b' def add_anchor(html):'
80 83
81 84 For use in heading cells
82 85 """
83 h = ElementTree.fromstring(py3compat.cast_bytes_py2(html))
86 h = ElementTree.fromstring(py3compat.cast_bytes_py2(html, encoding='utf-8'))
84 87 link = html2text(h).replace(' ', '-')
85 88 h.set('id', link)
86 89 a = ElementTree.Element("a", {"class" : "anchor-link", "href" : "#" + link})
@@ -93,6 +96,16 b' def add_anchor(html):'
93 96 return py3compat.decode(ElementTree.tostring(h), 'utf-8')
94 97
95 98
99 def add_prompts(code, first='>>> ', cont='... '):
100 """Add prompts to code snippets"""
101 new_code = []
102 code_list = code.split('\n')
103 new_code.append(first + code_list[0])
104 for line in code_list[1:]:
105 new_code.append(cont + line)
106 return '\n'.join(new_code)
107
108
96 109 def strip_dollars(text):
97 110 """
98 111 Remove all dollar symbols from text
@@ -181,3 +194,8 b' def posix_path(path):'
181 194 if os.path.sep != '/':
182 195 return path.replace(os.path.sep, '/')
183 196 return path
197
198 def path2url(path):
199 """Turn a file path into a URL"""
200 parts = path.split(os.path.sep)
201 return '/'.join(quote(part) for part in parts)
@@ -39,7 +39,7 b' class TestAnsi(TestsBase):'
39 39 'hello' : 'hello'}
40 40
41 41 for inval, outval in correct_outputs.items():
42 yield self._try_strip_ansi(inval, outval)
42 self._try_strip_ansi(inval, outval)
43 43
44 44
45 45 def _try_strip_ansi(self, inval, outval):
@@ -58,7 +58,7 b' class TestAnsi(TestsBase):'
58 58 'hello' : 'hello'}
59 59
60 60 for inval, outval in correct_outputs.items():
61 yield self._try_ansi2html(inval, outval)
61 self._try_ansi2html(inval, outval)
62 62
63 63
64 64 def _try_ansi2html(self, inval, outval):
@@ -77,7 +77,7 b' class TestAnsi(TestsBase):'
77 77 'hello' : 'hello'}
78 78
79 79 for inval, outval in correct_outputs.items():
80 yield self._try_ansi2latex(inval, outval)
80 self._try_ansi2latex(inval, outval)
81 81
82 82
83 83 def _try_ansi2latex(self, inval, outval):
@@ -49,13 +49,13 b' class TestHighlight(TestsBase):'
49 49 def test_highlight2html(self):
50 50 """highlight2html test"""
51 51 for index, test in enumerate(self.tests):
52 yield self._try_highlight(highlight2html, test, self.tokens[index])
52 self._try_highlight(highlight2html, test, self.tokens[index])
53 53
54 54
55 55 def test_highlight2latex(self):
56 56 """highlight2latex test"""
57 57 for index, test in enumerate(self.tests):
58 yield self._try_highlight(highlight2latex, test, self.tokens[index])
58 self._try_highlight(highlight2latex, test, self.tokens[index])
59 59
60 60
61 61 def _try_highlight(self, method, test, tokens):
@@ -35,7 +35,7 b' class TestLatex(TestsBase):'
35 35 ('','')]
36 36
37 37 for test in tests:
38 yield self._try_escape_latex(test[0], test[1])
38 self._try_escape_latex(test[0], test[1])
39 39
40 40
41 41 def _try_escape_latex(self, test, result):
@@ -56,7 +56,7 b' class TestLatex(TestsBase):'
56 56 ('','')]
57 57
58 58 for test in tests:
59 yield self._try_strip_math_space(test[0], test[1])
59 self._try_strip_math_space(test[0], test[1])
60 60
61 61
62 62 def _try_strip_math_space(self, test, result):
@@ -61,14 +61,14 b' class TestMarkdown(TestsBase):'
61 61 def test_markdown2latex(self):
62 62 """markdown2latex test"""
63 63 for index, test in enumerate(self.tests):
64 yield self._try_markdown(markdown2latex, test, self.tokens[index])
64 self._try_markdown(markdown2latex, test, self.tokens[index])
65 65
66 66
67 67 @dec.onlyif_cmds_exist('pandoc')
68 68 def test_markdown2html(self):
69 69 """markdown2html test"""
70 70 for index, test in enumerate(self.tests):
71 yield self._try_markdown(markdown2html, test, self.tokens[index])
71 self._try_markdown(markdown2html, test, self.tokens[index])
72 72
73 73
74 74 @dec.onlyif_cmds_exist('pandoc')
@@ -81,7 +81,7 b' class TestMarkdown(TestsBase):'
81 81 tokens[1] = r'\*\*test'
82 82
83 83 for index, test in enumerate(self.tests):
84 yield self._try_markdown(markdown2rst, test, tokens[index])
84 self._try_markdown(markdown2rst, test, tokens[index])
85 85
86 86
87 87 def _try_markdown(self, method, test, tokens):
@@ -15,10 +15,10 b' Module with tests for Strings'
15 15 #-----------------------------------------------------------------------------
16 16 import os
17 17
18 from IPython.testing import decorators as dec
19 18 from ...tests.base import TestsBase
20 19 from ..strings import (wrap_text, html2text, add_anchor, strip_dollars,
21 20 strip_files_prefix, get_lines, comment_lines, ipython2python, posix_path,
21 add_prompts
22 22 )
23 23
24 24
@@ -36,7 +36,7 b' class TestStrings(TestsBase):'
36 36 As if the strings were thine, shouldst know of this.
37 37 """
38 38 for length in [30,5,1]:
39 yield self._confirm_wrap_text(test_text, length)
39 self._confirm_wrap_text(test_text, length)
40 40
41 41
42 42 def _confirm_wrap_text(self, text, length):
@@ -73,7 +73,7 b' class TestStrings(TestsBase):'
73 73 ('Hello', 'Hello'),
74 74 ('W$o$rld', 'W$o$rld')]
75 75 for test in tests:
76 yield self._try_strip_dollars(test[0], test[1])
76 self._try_strip_dollars(test[0], test[1])
77 77
78 78
79 79 def _try_strip_dollars(self, test, result):
@@ -89,7 +89,7 b' class TestStrings(TestsBase):'
89 89 ('My files are in `files/`', 'My files are in `files/`'),
90 90 ('<a href="files/test.html">files/test.html</a>', '<a href="test.html">files/test.html</a>')]
91 91 for test in tests:
92 yield self._try_files_prefix(test[0], test[1])
92 self._try_files_prefix(test[0], test[1])
93 93
94 94
95 95 def _try_files_prefix(self, test, result):
@@ -121,8 +121,15 b' class TestStrings(TestsBase):'
121 121 ignore_spaces=True, ignore_newlines=True)
122 122
123 123 def test_posix_path(self):
124 """posix_path test"""
124 125 path_list = ['foo', 'bar']
125 126 expected = '/'.join(path_list)
126 127 native = os.path.join(*path_list)
127 128 filtered = posix_path(native)
128 129 self.assertEqual(filtered, expected)
130
131 def test_add_prompts(self):
132 """add_prompts test"""
133 text1 = """for i in range(10):\n i += 1\n print i"""
134 text2 = """>>> for i in range(10):\n... i += 1\n... print i"""
135 self.assertEqual(text2, add_prompts(text1))
@@ -25,6 +25,7 b' import glob'
25 25
26 26 # From IPython
27 27 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
28 from IPython.core.profiledir import ProfileDir
28 29 from IPython.config import catch_config_error, Configurable
29 30 from IPython.utils.traitlets import (
30 31 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
@@ -58,12 +59,11 b' nbconvert_aliases = {}'
58 59 nbconvert_aliases.update(base_aliases)
59 60 nbconvert_aliases.update({
60 61 'to' : 'NbConvertApp.export_format',
61 'template' : 'Exporter.template_file',
62 'template' : 'TemplateExporter.template_file',
62 63 'writer' : 'NbConvertApp.writer_class',
63 64 'post': 'NbConvertApp.postprocessor_class',
64 65 'output': 'NbConvertApp.output_base',
65 'offline-slides': 'RevealHelpTransformer.url_prefix',
66 'slide-notes': 'RevealHelpTransformer.speaker_notes'
66 'reveal-prefix': 'RevealHelpPreprocessor.url_prefix',
67 67 })
68 68
69 69 nbconvert_flags = {}
@@ -87,12 +87,13 b' class NbConvertApp(BaseIPythonApplication):'
87 87 return logging.INFO
88 88
89 89 def _classes_default(self):
90 classes = [NbConvertBase]
90 classes = [NbConvertBase, ProfileDir]
91 91 for pkg in (exporters, preprocessors, writers):
92 92 for name in dir(pkg):
93 93 cls = getattr(pkg, name)
94 94 if isinstance(cls, type) and issubclass(cls, Configurable):
95 95 classes.append(cls)
96
96 97 return classes
97 98
98 99 description = Unicode(
@@ -1,3 +1,8 b''
1 1 from .base import PostProcessorBase
2 2 from .pdf import PDFPostProcessor
3 from .serve import ServePostProcessor
3
4 # protect against unavailable tornado
5 try:
6 from .serve import ServePostProcessor
7 except ImportError:
8 pass
@@ -26,35 +26,107 b' from .base import PostProcessorBase'
26 26 class PDFPostProcessor(PostProcessorBase):
27 27 """Writer designed to write to PDF files"""
28 28
29 iteration_count = Integer(3, config=True, help="""
29 latex_count = Integer(3, config=True, help="""
30 30 How many times pdflatex will be called.
31 31 """)
32 32
33 command = List(["pdflatex", "{filename}"], config=True, help="""
33 latex_command = List(["pdflatex", "{filename}"], config=True, help="""
34 34 Shell command used to compile PDF.""")
35 35
36 bib_command = List(["bibtex", "{filename}"], config=True, help="""
37 Shell command used to run bibtex.""")
38
36 39 verbose = Bool(False, config=True, help="""
37 40 Whether or not to display the output of the compile call.
38 41 """)
39 42
40 def postprocess(self, input):
41 """
42 Consume and write Jinja output a PDF.
43 See files.py for more...
44 """
45 command = [c.format(filename=input) for c in self.command]
46 self.log.info("Building PDF: %s", command)
47 with open(os.devnull, 'rb') as null:
48 stdout = subprocess.PIPE if not self.verbose else None
49 for index in range(self.iteration_count):
50 p = subprocess.Popen(command, stdout=stdout, stdin=null)
51 out, err = p.communicate()
52 if p.returncode:
53 if self.verbose:
54 # verbose means I didn't capture stdout with PIPE,
55 # so it's already been displayed and `out` is None.
56 out = u''
57 else:
58 out = out.decode('utf-8', 'replace')
59 self.log.critical(u"PDF conversion failed: %s\n%s", command, out)
60 return
43 temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'],
44 config=True, help="""
45 Filename extensions of temp files to remove after running.
46 """)
47
48 def run_command(self, command_list, filename, count, log_function):
49 """Run command_list count times.
50
51 Parameters
52 ----------
53 command_list : list
54 A list of args to provide to Popen. Each element of this
55 list will be interpolated with the filename to convert.
56 filename : unicode
57 The name of the file to convert.
58 count : int
59 How many times to run the command.
60
61 Returns
62 -------
63 continue : bool
64 A boolean indicating if the command was successful (True)
65 or failed (False).
66 """
67 command = [c.format(filename=filename) for c in command_list]
68 times = 'time' if count == 1 else 'times'
69 self.log.info("Running %s %i %s: %s", command_list[0], count, times, command)
70 with open(os.devnull, 'rb') as null:
71 stdout = subprocess.PIPE if not self.verbose else None
72 for index in range(count):
73 p = subprocess.Popen(command, stdout=stdout, stdin=null)
74 out, err = p.communicate()
75 if p.returncode:
76 if self.verbose:
77 # verbose means I didn't capture stdout with PIPE,
78 # so it's already been displayed and `out` is None.
79 out = u''
80 else:
81 out = out.decode('utf-8', 'replace')
82 log_function(command, out)
83 return False # failure
84 return True # success
85
86 def run_latex(self, filename):
87 """Run pdflatex self.latex_count times."""
88
89 def log_error(command, out):
90 self.log.critical(u"%s failed: %s\n%s", command[0], command, out)
91
92 return self.run_command(self.latex_command, filename,
93 self.latex_count, log_error)
94
95 def run_bib(self, filename):
96 """Run bibtex self.latex_count times."""
97 filename = os.path.splitext(filename)[0]
98
99 def log_error(command, out):
100 self.log.warn('%s had problems, most likely because there were no citations',
101 command[0])
102 self.log.debug(u"%s output: %s\n%s", command[0], command, out)
103
104 return self.run_command(self.bib_command, filename, 1, log_error)
105
106 def clean_temp_files(self, filename):
107 """Remove temporary files created by pdflatex/bibtext."""
108 self.log.info("Removing temporary LaTeX files")
109 filename = os.path.splitext(filename)[0]
110 for ext in self.temp_file_exts:
111 try:
112 os.remove(filename+ext)
113 except OSError:
114 pass
115
116 def postprocess(self, filename):
117 """Build a PDF by running pdflatex and bibtex"""
118 self.log.info("Building PDF")
119 cont = self.run_latex(filename)
120 if cont:
121 cont = self.run_bib(filename)
122 else:
123 self.clean_temp_files(filename)
124 return
125 if cont:
126 cont = self.run_latex(filename)
127 self.clean_temp_files(filename)
128 filename = os.path.splitext(filename)[0]
129 if os.path.isfile(filename+'.pdf'):
130 self.log.info('PDF successfully created')
131 return
132
@@ -1,6 +1,4 b''
1 """
2 Contains postprocessor for serving nbconvert output.
3 """
1 """PostProcessor for serving reveal.js HTML slideshows."""
4 2 #-----------------------------------------------------------------------------
5 3 #Copyright (c) 2013, the IPython Development Team.
6 4 #
@@ -16,40 +14,94 b' Contains postprocessor for serving nbconvert output.'
16 14 import os
17 15 import webbrowser
18 16
19 from BaseHTTPServer import HTTPServer
20 from SimpleHTTPServer import SimpleHTTPRequestHandler
17 from tornado import web, ioloop, httpserver
18 from tornado.httpclient import AsyncHTTPClient
21 19
22 from IPython.utils.traitlets import Bool
20 from IPython.utils.traitlets import Bool, Unicode, Int
23 21
24 22 from .base import PostProcessorBase
25 23
26 24 #-----------------------------------------------------------------------------
27 25 # Classes
28 26 #-----------------------------------------------------------------------------
27
28 class ProxyHandler(web.RequestHandler):
29 """handler the proxies requests from a local prefix to a CDN"""
30 @web.asynchronous
31 def get(self, prefix, url):
32 """proxy a request to a CDN"""
33 proxy_url = "/".join([self.settings['cdn'], url])
34 client = self.settings['client']
35 client.fetch(proxy_url, callback=self.finish_get)
36
37 def finish_get(self, response):
38 """finish the request"""
39 # copy potentially relevant headers
40 for header in ["Content-Type", "Cache-Control", "Date", "Last-Modified", "Expires"]:
41 if header in response.headers:
42 self.set_header(header, response.headers[header])
43 self.finish(response.body)
44
29 45 class ServePostProcessor(PostProcessorBase):
30 """Post processor designed to serve files"""
46 """Post processor designed to serve files
47
48 Proxies reveal.js requests to a CDN if no local reveal.js is present
49 """
31 50
32 51
33 52 open_in_browser = Bool(True, config=True,
34 help="""Set to False to deactivate
35 the opening of the browser""")
53 help="""Should the browser be opened automatically?"""
54 )
55 reveal_cdn = Unicode("https://cdn.jsdelivr.net/reveal.js/2.4.0", config=True,
56 help="""URL for reveal.js CDN."""
57 )
58 reveal_prefix = Unicode("reveal.js", config=True, help="URL prefix for reveal.js")
59 ip = Unicode("127.0.0.1", config=True, help="The IP address to listen on.")
60 port = Int(8000, config=True, help="port for the server to listen on.")
36 61
37 62 def postprocess(self, input):
38 """
39 Simple implementation to serve the build directory.
40 """
41
63 """Serve the build directory with a webserver."""
64 dirname, filename = os.path.split(input)
65 handlers = [
66 (r"/(.+)", web.StaticFileHandler, {'path' : dirname}),
67 (r"/", web.RedirectHandler, {"url": "/%s" % filename})
68 ]
69
70 if ('://' in self.reveal_prefix or self.reveal_prefix.startswith("//")):
71 # reveal specifically from CDN, nothing to do
72 pass
73 elif os.path.isdir(os.path.join(dirname, self.reveal_prefix)):
74 # reveal prefix exists
75 self.log.info("Serving local %s", self.reveal_prefix)
76 else:
77 self.log.info("Redirecting %s requests to %s", self.reveal_prefix, self.reveal_cdn)
78 handlers.insert(0, (r"/(%s)/(.*)" % self.reveal_prefix, ProxyHandler))
79
80 app = web.Application(handlers,
81 cdn=self.reveal_cdn,
82 client=AsyncHTTPClient(),
83 )
84 # hook up tornado logging to our logger
85 from tornado import log
86 log.app_log = self.log
87
88 http_server = httpserver.HTTPServer(app)
89 http_server.listen(self.port, address=self.ip)
90 url = "http://%s:%i/%s" % (self.ip, self.port, filename)
91 print("Serving your slides at %s" % url)
92 print("Use Control-C to stop this server")
93 if self.open_in_browser:
94 webbrowser.open(url, new=2)
42 95 try:
43 dirname, filename = os.path.split(input)
44 if dirname:
45 os.chdir(dirname)
46 httpd = HTTPServer(('127.0.0.1', 8000), SimpleHTTPRequestHandler)
47 sa = httpd.socket.getsockname()
48 url = "http://" + sa[0] + ":" + str(sa[1]) + "/" + filename
49 if self.open_in_browser:
50 webbrowser.open(url, new=2)
51 print("Serving your slides on " + url)
52 print("Use Control-C to stop this server.")
53 httpd.serve_forever()
96 ioloop.IOLoop.instance().start()
54 97 except KeyboardInterrupt:
55 print("The server is shut down.")
98 print("\nInterrupted")
99
100 def main(path):
101 """allow running this module to serve the slides"""
102 server = ServePostProcessor()
103 server(path)
104
105 if __name__ == '__main__':
106 import sys
107 main(sys.argv[1])
@@ -62,3 +62,7 b' class TestPDF(TestsBase):'
62 62
63 63 # Check that the PDF was created.
64 64 assert os.path.isfile('a.pdf')
65
66 # Make sure that temp files are cleaned up
67 for ext in processor.temp_file_exts:
68 assert not os.path.isfile('a'+ext)
@@ -5,8 +5,8 b' from .svg2pdf import SVG2PDFPreprocessor'
5 5 from .extractoutput import ExtractOutputPreprocessor
6 6 from .revealhelp import RevealHelpPreprocessor
7 7 from .latex import LatexPreprocessor
8 from .sphinx import SphinxPreprocessor
9 8 from .csshtmlheader import CSSHTMLHeaderPreprocessor
9 from .highlightmagics import HighlightMagicsPreprocessor
10 10
11 11 # decorated function Preprocessors
12 12 from .coalescestreams import coalesce_streams
@@ -19,7 +19,8 b' def cell_preprocessor(function):'
19 19 Wrap a function to be executed on all cells of a notebook
20 20
21 21 Wrapped Parameters
22 ----------
22 ------------------
23
23 24 cell : NotebookNode cell
24 25 Notebook cell being processed
25 26 resources : dictionary
@@ -69,7 +70,7 b' def coalesce_streams(cell, resources, index):'
69 70 last.text += output.text
70 71 else:
71 72 new_outputs.append(output)
72 last = output
73 last = output
73 74
74 75 cell.outputs = new_outputs
75 76 return cell, resources
@@ -14,9 +14,12 b' they are converted.'
14 14 #-----------------------------------------------------------------------------
15 15
16 16 from __future__ import print_function, absolute_import
17 import os
17 18
18 # Our own imports
19 # Needed to override preprocessor
19 # Third-party import, needed for Pygments latex definitions.
20 from pygments.formatters import LatexFormatter
21
22 # ipy imports
20 23 from .base import (Preprocessor)
21 24 from IPython.nbconvert import filters
22 25
@@ -29,6 +32,24 b' class LatexPreprocessor(Preprocessor):'
29 32 Converter for latex destined documents.
30 33 """
31 34
35 def preprocess(self, nb, resources):
36 """
37 Preprocessing to apply on each notebook.
38
39 Parameters
40 ----------
41 nb : NotebookNode
42 Notebook being converted
43 resources : dictionary
44 Additional resources used in the conversion process. Allows
45 preprocessors to pass variables into the Jinja engine.
46 """
47 # Generate Pygments definitions for Latex
48 resources["latex"] = {}
49 resources["latex"]["pygments_definitions"] = LatexFormatter().get_style_defs()
50 return super(LatexPreprocessor, self).preprocess(nb, resources)
51
52
32 53 def preprocess_cell(self, cell, resources, index):
33 54 """
34 55 Apply a transformation on each cell,
@@ -24,15 +24,14 b' from IPython.utils.traitlets import Unicode, Bool'
24 24
25 25 class RevealHelpPreprocessor(Preprocessor):
26 26
27 url_prefix = Unicode('//cdn.jsdelivr.net/reveal.js/2.4.0',
28 config=True,
29 help="""If you want to use a local reveal.js library,
30 use 'url_prefix':'reveal.js' in your config object.""")
31
32 speaker_notes = Bool(False,
33 config=True,
34 help="""If you want to use the speaker notes
35 set this to True.""")
27 url_prefix = Unicode('reveal.js', config=True,
28 help="""The URL prefix for reveal.js.
29 This can be a a relative URL for a local copy of reveal.js,
30 or point to a CDN.
31
32 For speaker notes to work, a local reveal.js prefix must be used.
33 """
34 )
36 35
37 36 def preprocess(self, nb, resources):
38 37 """
@@ -65,30 +64,4 b' class RevealHelpPreprocessor(Preprocessor):'
65 64 if not isinstance(resources['reveal'], dict):
66 65 resources['reveal'] = {}
67 66 resources['reveal']['url_prefix'] = self.url_prefix
68 resources['reveal']['notes_prefix'] = self.url_prefix
69
70 cdn = 'http://cdn.jsdelivr.net/reveal.js/2.4.0'
71 local = 'local'
72 html_path = 'plugin/notes/notes.html'
73 js_path = 'plugin/notes/notes.js'
74
75 html_infile = os.path.join(cdn, html_path)
76 js_infile = os.path.join(cdn, js_path)
77 html_outfile = os.path.join(local, html_path)
78 js_outfile = os.path.join(local, js_path)
79
80 if self.speaker_notes:
81 if 'outputs' not in resources:
82 resources['outputs'] = {}
83 resources['outputs'][html_outfile] = self.notes_helper(html_infile)
84 resources['outputs'][js_outfile] = self.notes_helper(js_infile)
85 resources['reveal']['notes_prefix'] = local
86
87 67 return nb, resources
88
89 def notes_helper(self, infile):
90 """Helper function to get the content from an url."""
91
92 content = urllib2.urlopen(infile).read()
93
94 return content
@@ -36,7 +36,7 b' class PreprocessorTestsBase(TestsBase):'
36 36 nbformat.new_output(output_type="stream", stream="stdout", output_text="d"),
37 37 nbformat.new_output(output_type="stream", stream="stderr", output_text="e"),
38 38 nbformat.new_output(output_type="stream", stream="stderr", output_text="f"),
39 nbformat.new_output(output_type="png", output_png=b'Zw==')] #g
39 nbformat.new_output(output_type="png", output_png='Zw==')] #g
40 40
41 41 cells=[nbformat.new_code_cell(input="$ e $", prompt_number=1,outputs=outputs),
42 42 nbformat.new_text_cell('markdown', source="$ e $")]
@@ -14,6 +14,8 b' Module with tests for the coalescestreams preprocessor'
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 from IPython.nbformat import current as nbformat
18
17 19 from .base import PreprocessorTestsBase
18 20 from ..coalescestreams import coalesce_streams
19 21
@@ -35,4 +37,24 b' class TestCoalesceStreams(PreprocessorTestsBase):'
35 37 self.assertEqual(outputs[1].output_type, "text")
36 38 self.assertEqual(outputs[2].text, "cd")
37 39 self.assertEqual(outputs[3].text, "ef")
38
40
41
42 def test_coalesce_sequenced_streams(self):
43 """Can the coalesce streams preprocessor merge a sequence of streams?"""
44
45 outputs = [nbformat.new_output(output_type="stream", stream="stdout", output_text="0"),
46 nbformat.new_output(output_type="stream", stream="stdout", output_text="1"),
47 nbformat.new_output(output_type="stream", stream="stdout", output_text="2"),
48 nbformat.new_output(output_type="stream", stream="stdout", output_text="3"),
49 nbformat.new_output(output_type="stream", stream="stdout", output_text="4"),
50 nbformat.new_output(output_type="stream", stream="stdout", output_text="5"),
51 nbformat.new_output(output_type="stream", stream="stdout", output_text="6"),
52 nbformat.new_output(output_type="stream", stream="stdout", output_text="7")]
53 cells=[nbformat.new_code_cell(input="# None", prompt_number=1,outputs=outputs)]
54 worksheets = [nbformat.new_worksheet(name="worksheet1", cells=cells)]
55
56 nb = nbformat.new_notebook(name="notebook1", worksheets=worksheets)
57 res = self.build_resources()
58 nb, res = coalesce_streams(nb, res)
59 outputs = nb.worksheets[0].cells[0].outputs
60 self.assertEqual(outputs[0].text, u'01234567')
@@ -46,12 +46,14 b' class TestExtractOutput(PreprocessorTestsBase):'
46 46 nb, res = preprocessor(nb, res)
47 47
48 48 # Check if text was extracted.
49 assert 'text_filename' in nb.worksheets[0].cells[0].outputs[1]
50 text_filename = nb.worksheets[0].cells[0].outputs[1]['text_filename']
49 output = nb.worksheets[0].cells[0].outputs[1]
50 assert 'text_filename' in output
51 text_filename = output['text_filename']
51 52
52 53 # Check if png was extracted.
53 assert 'png_filename' in nb.worksheets[0].cells[0].outputs[6]
54 png_filename = nb.worksheets[0].cells[0].outputs[6]['png_filename']
54 output = nb.worksheets[0].cells[0].outputs[6]
55 assert 'png_filename' in output
56 png_filename = output['png_filename']
55 57
56 58 # Verify text output
57 59 assert text_filename in res['outputs']
@@ -1,5 +1,5 b''
1 1 """
2 Module with tests for the sphinx preprocessor
2 Module with tests for the HighlightMagics preprocessor
3 3 """
4 4
5 5 #-----------------------------------------------------------------------------
@@ -15,43 +15,54 b' Module with tests for the sphinx preprocessor'
15 15 #-----------------------------------------------------------------------------
16 16
17 17 from .base import PreprocessorTestsBase
18 from ..sphinx import SphinxPreprocessor
18 from ..highlightmagics import HighlightMagicsPreprocessor
19 19
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Class
23 23 #-----------------------------------------------------------------------------
24 24
25 class TestSphinx(PreprocessorTestsBase):
26 """Contains test functions for sphinx.py"""
25 class TestHighlightMagics(PreprocessorTestsBase):
26 """Contains test functions for highlightmagics.py"""
27 27
28 28
29 29 def build_preprocessor(self):
30 30 """Make an instance of a preprocessor"""
31 preprocessor = SphinxPreprocessor()
31 preprocessor = HighlightMagicsPreprocessor()
32 32 preprocessor.enabled = True
33 33 return preprocessor
34 34
35
36 35 def test_constructor(self):
37 """Can a SphinxPreprocessor be constructed?"""
36 """Can a HighlightMagicsPreprocessor be constructed?"""
38 37 self.build_preprocessor()
39
40 38
41 def test_resources(self):
42 """Make sure the SphinxPreprocessor adds the appropriate resources to the
43 resources dict."""
39 def test_tagging(self):
40 """Test the HighlightMagicsPreprocessor tagging"""
41 nb = self.build_notebook()
42 res = self.build_resources()
43 preprocessor = self.build_preprocessor()
44 nb.worksheets[0].cells[0].input = """%%R -i x,y -o XYcoef
45 lm.fit <- lm(y~x)
46 par(mfrow=c(2,2))
47 print(summary(lm.fit))
48 plot(lm.fit)
49 XYcoef <- coef(lm.fit)"""
50
51 nb, res = preprocessor(nb, res)
52
53 assert('magics_language' in nb.worksheets[0].cells[0]['metadata'])
54
55 self.assertEqual(nb.worksheets[0].cells[0]['metadata']['magics_language'], 'r')
56
57 def test_no_false_positive(self):
58 """Test that HighlightMagicsPreprocessor does not tag false positives"""
44 59 nb = self.build_notebook()
45 60 res = self.build_resources()
46 61 preprocessor = self.build_preprocessor()
62 nb.worksheets[0].cells[0].input = """# this should not be detected
63 print(\"""
64 %%R -i x, y
65 \""")"""
47 66 nb, res = preprocessor(nb, res)
48 assert "author" in res['sphinx']
49 assert "version" in res['sphinx']
50 assert "release" in res['sphinx']
51 assert "date" in res['sphinx']
52 assert "chapterstyle" in res['sphinx']
53 assert "outputstyle" in res['sphinx']
54 assert "centeroutput" in res['sphinx']
55 assert "header" in res['sphinx']
56 assert "texinputs" in res['sphinx']
57 assert "pygment_definitions" in res['sphinx']
67
68 assert('magics_language' not in nb.worksheets[0].cells[0]['metadata']) No newline at end of file
@@ -36,7 +36,7 b' In&nbsp;[{{ cell.prompt_number }}]:'
36 36
37 37 {% block input %}
38 38 <div class="input_area box-flex1">
39 {{ cell.input | highlight2html }}
39 {{ cell.input | highlight2html(metadata=cell.metadata) }}
40 40 </div>
41 41 {%- endblock input %}
42 42
@@ -135,6 +135,12 b' unknown type {{ cell.type }}'
135 135 </pre>
136 136 {%- endblock -%}
137 137
138 {%- block data_javascript %}
139 <script type="text/javascript">
140 {{ output.javascript }}
141 </script>
142 {%- endblock -%}
143
138 144 {%- block display_data scoped -%}
139 145 <div class="box-flex1 output_subarea output_display_data">
140 146 {{ super() }}
@@ -1,26 +1,17 b''
1 ((============================================================================
2 NBConvert Sphinx-Latex HowTo Template
3 1
4 Purpose: Allow export of PDF friendly Latex inspired by Sphinx HowTo
5 document style. Most of the is derived directly from Sphinx source.
2 % Default to the notebook output style
3 ((* if not cell_style is defined *))
4 ((* set cell_style = 'style_ipython.tplx' *))
5 ((* endif *))
6 6
7 Inheritance: null>display_priority>sphinx
7 % Inherit from the specified cell style.
8 ((* extends cell_style *))
8 9
9 ==========================================================================))
10 10
11 ((*- extends 'sphinx.tplx' -*))
11 %===============================================================================
12 % Latex Article
13 %===============================================================================
12 14
13
14 ((* set parentdocumentclass = 'article' *))
15 ((* set documentclass = 'howto' *))
16
17 ((* block h1 -*))part((* endblock h1 -*))
18 ((* block h2 -*))section((* endblock h2 -*))
19 ((* block h3 -*))subsection((* endblock h3 -*))
20 ((* block h4 -*))subsubsection((* endblock h4 -*))
21 ((* block h5 -*))paragraph((* endblock h5 -*))
22 ((* block h6 -*))subparagraph((* endblock h6 -*))
23
24 % Diasble table of contents for howto
25 ((* block toc *))
26 ((* endblock toc *))
15 ((* block docclass *))
16 \documentclass{article}
17 ((* endblock docclass *)) No newline at end of file
@@ -1,4 +1,7 b''
1 ((= autogenerated file do not edit =))
1 ((= Auto-generated template file, DO NOT edit directly!
2 To edit this file, please refer to ../../skeleton/README.md =))
3
4
2 5 ((*- extends 'null.tplx' -*))
3 6
4 7 ((=display data priority=))
@@ -30,10 +33,13 b''
30 33 ((*- block data_text -*))
31 34 ((*- endblock -*))
32 35 ((*- endif -*))
33
34 36 ((*- if type in ['latex']*))
35 37 ((*- block data_latex -*))
36 38 ((*- endblock -*))
37 39 ((*- endif -*))
40 ((*- if type in ['javascript']*))
41 ((*- block data_javascript -*))
42 ((*- endblock -*))
43 ((*- endif -*))
38 44 ((*- endfor -*))
39 45 ((*- endblock data_priority -*))
@@ -1,11 +1,14 b''
1 ((= autogenerated file do not edit =))
1 ((= Auto-generated template file, DO NOT edit directly!
2 To edit this file, please refer to ../../skeleton/README.md =))
3
4
2 5 ((=
3 6
4 DO NOT USE THIS AS A BASE WORK,
7 DO NOT USE THIS AS A BASE,
5 8 IF YOU ARE COPY AND PASTING THIS FILE
6 YOU ARE PROBABLY DOING THINGS WRONG.
9 YOU ARE PROBABLY DOING THINGS INCORRECTLY.
7 10
8 Null template, Does nothing except defining a basic structure
11 Null template, does nothing except defining a basic structure
9 12 To layout the different blocks of a notebook.
10 13
11 14 Subtemplates can override blocks to define their custom representation.
@@ -2,19 +2,13 b''
2 2
3 3
4 4 {% block in_prompt %}
5 In[{{ cell.prompt_number if cell.prompt_number else ' ' }}]:
6 5 {% endblock in_prompt %}
7 6
8 7 {% block output_prompt %}
9 {% if cell.haspyout %}
10 Out[{{ cell.prompt_number }}]:
11 {%- endif %}
12 8 {%- endblock output_prompt %}
13 9
14 10 {% block input %}
15 ```
16 {{ cell.input }}
17 ```
11 {{ cell.input | indent(4)}}
18 12 {% endblock input %}
19 13
20 14 {% block pyerr %}
@@ -26,6 +20,7 b' Out[{{ cell.prompt_number }}]:'
26 20 {% endblock traceback_line %}
27 21
28 22 {% block pyout %}
23
29 24 {% block data_priority scoped %}
30 25 {{ super() }}
31 26 {% endblock %}
@@ -36,23 +31,25 b' Out[{{ cell.prompt_number }}]:'
36 31 {% endblock stream %}
37 32
38 33 {% block data_svg %}
39 [!image]({{ output.svg_filename }})
34 ![svg]({{ output.svg_filename | path2url }})
40 35 {% endblock data_svg %}
41 36
42 37 {% block data_png %}
43 [!image]({{ output.png_filename }})
38 ![png]({{ output.png_filename | path2url }})
44 39 {% endblock data_png %}
45 40
46 41 {% block data_jpg %}
47 [!image]({{ output.jpg_filename }})
42 ![jpeg]({{ output.jpeg_filename | path2url }})
48 43 {% endblock data_jpg %}
49 44
50 45 {% block data_latex %}
51 $$
52 46 {{ output.latex }}
53 $$
54 47 {% endblock data_latex %}
55 48
49 {% block data_html scoped %}
50 {{ output.html }}
51 {% endblock data_html %}
52
56 53 {% block data_text scoped %}
57 54 {{ output.text | indent }}
58 55 {% endblock data_text %}
@@ -61,6 +58,7 b' $$'
61 58 {{ cell.source | wrap_text(80) }}
62 59 {% endblock markdowncell %}
63 60
61
64 62 {% block headingcell scoped %}
65 63 {{ '#' * cell.level }} {{ cell.source | replace('\n', ' ') }}
66 64 {% endblock headingcell %}
@@ -2,25 +2,22 b''
2 2
3 3
4 4 {% block in_prompt %}
5
6 In[{{ cell.prompt_number if cell.prompt_number else ' ' }}]:
7
8 .. code:: python
9
10 5 {% endblock in_prompt %}
11 6
12 7 {% block output_prompt %}
13 {% if cell.haspyout -%}
14 Out[{{ cell.prompt_number }}]:
15 {% endif %}
16 8 {% endblock output_prompt %}
17 9
18 10 {% block input %}
11 {%- if not cell.input.isspace() -%}
12 .. code:: python
13
19 14 {{ cell.input | indent}}
15 {%- endif -%}
20 16 {% endblock input %}
21 17
22 18 {% block pyerr %}
23 19 ::
20
24 21 {{ super() }}
25 22 {% endblock pyerr %}
26 23
@@ -49,19 +46,27 b" In[{{ cell.prompt_number if cell.prompt_number else ' ' }}]:"
49 46 {% endblock data_png %}
50 47
51 48 {% block data_jpg %}
52 ..jpg image:: {{ output.jpg_filename }}
49 .. image:: {{ output.jpeg_filename }}
53 50 {% endblock data_jpg %}
54 51
55 52 {% block data_latex %}
56 53 .. math::
57 {{ output.latex | indent }}
54
55 {{ output.latex | strip_dollars | indent }}
58 56 {% endblock data_latex %}
59 57
60 58 {% block data_text scoped %}
61 59 .. parsed-literal::
60
62 61 {{ output.text | indent }}
63 62 {% endblock data_text %}
64 63
64 {% block data_html scoped %}
65 .. raw:: html
66
67 {{ output.html | indent }}
68 {% endblock data_html %}
69
65 70 {% block markdowncell scoped %}
66 71 {{ cell.source | markdown2rst }}
67 72 {% endblock markdowncell %}
@@ -1,12 +1,14 b''
1 TPLS := $(patsubst %.tpl,../latex/skeleton/%.tplx,$(wildcard *.tpl))
1 2
3 all: clean $(TPLS)
2 4
3 all: tex/null.tplx tex/display_priority.tplx
4
5 # convert jinja syntax to tex
6 # cf http://flask.pocoo.org/snippets/55/
7 tex/%.tplx: %.tpl
5 # Convert standard Jinja2 syntax to LaTeX safe Jinja2
6 # see http://flask.pocoo.org/snippets/55/ for more info
7 ../latex/skeleton/%.tplx: %.tpl
8 8 @echo 'generating tex equivalent of $^: $@'
9 @echo '((= autogenerated file do not edit =))' > $@
9 @echo '((= Auto-generated template file, DO NOT edit directly!\n' \
10 ' To edit this file, please refer to ../../skeleton/README.md' \
11 '=))\n\n' > $@
10 12 @sed \
11 13 -e 's/{%/((*/g' \
12 14 -e 's/%}/*))/g' \
@@ -17,7 +19,6 b' tex/%.tplx: %.tpl'
17 19 -e "s/tpl'/tplx'/g" \
18 20 $^ >> $@
19 21
20
21 22 clean:
22 23 @echo "cleaning generated tplx files..."
23 @rm tex/*
24 @-rm ../latex/skeleton/*.tplx
@@ -1,6 +1,8 b''
1 1 ## Template skeleton
2 2
3 This contain skeleton template that you probably don't want
4 to inherit directly.
3 This directory contains the template skeleton files.
5 4
6 do not modify the content of the 'tex' folder which is generated by running 'make' in this folder.
5 Do not modify the contents of the `../latex/skeleton` folder. Instead,
6 if you need to, make modifications to the files in this folder and then run
7 `make` to generate the corresponding latex skeleton files in the
8 `../latex/skeleton` folder. No newline at end of file
@@ -29,10 +29,13 b''
29 29 {%- block data_text -%}
30 30 {%- endblock -%}
31 31 {%- endif -%}
32
33 32 {%- if type in ['latex']%}
34 33 {%- block data_latex -%}
35 34 {%- endblock -%}
36 35 {%- endif -%}
36 {%- if type in ['javascript']%}
37 {%- block data_javascript -%}
38 {%- endblock -%}
39 {%- endif -%}
37 40 {%- endfor -%}
38 41 {%- endblock data_priority -%}
@@ -1,10 +1,10 b''
1 1 {#
2 2
3 DO NOT USE THIS AS A BASE WORK,
3 DO NOT USE THIS AS A BASE,
4 4 IF YOU ARE COPY AND PASTING THIS FILE
5 YOU ARE PROBABLY DOING THINGS WRONG.
5 YOU ARE PROBABLY DOING THINGS INCORRECTLY.
6 6
7 Null template, Does nothing except defining a basic structure
7 Null template, does nothing except defining a basic structure
8 8 To layout the different blocks of a notebook.
9 9
10 10 Subtemplates can override blocks to define their custom representation.
@@ -135,7 +135,7 b" transition: Reveal.getQueryHash().transition || 'linear', // default/cube/page/c"
135 135 dependencies: [
136 136 { src: "{{resources.reveal.url_prefix}}/lib/js/classList.js", condition: function() { return !document.body.classList; } },
137 137 { src: "{{resources.reveal.url_prefix}}/plugin/highlight/highlight.js", async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
138 { src: "{{resources.reveal.notes_prefix}}/plugin/notes/notes.js", async: true, condition: function() { return !!document.body.classList; } }
138 { src: "{{resources.reveal.url_prefix}}/plugin/notes/notes.js", async: true, condition: function() { return !!document.body.classList; } }
139 139 // { src: 'http://s7.addthis.com/js/300/addthis_widget.js', async: true},
140 140 ]
141 141 });
@@ -16,12 +16,12 b' Contains base test class for nbconvert'
16 16 import os
17 17 import glob
18 18 import shutil
19 import unittest
19 20
20 21 import IPython
21 22 from IPython.utils.tempdir import TemporaryWorkingDirectory
22 23 from IPython.utils.process import get_output_error_code
23 24 from IPython.testing.tools import get_ipython_cmd
24 from IPython.testing.ipunittest import ParametricTestCase
25 25
26 26 # a trailing space allows for simpler concatenation with the other arguments
27 27 ipy_cmd = get_ipython_cmd(as_string=True) + " "
@@ -31,7 +31,7 b' ipy_cmd = get_ipython_cmd(as_string=True) + " "'
31 31 #-----------------------------------------------------------------------------
32 32
33 33
34 class TestsBase(ParametricTestCase):
34 class TestsBase(unittest.TestCase):
35 35 """Base tests class. Contains useful fuzzy comparison and nbconvert
36 36 functions."""
37 37
@@ -86,7 +86,7 b' class TestsBase(ParametricTestCase):'
86 86
87 87 For example:
88 88 Replace "ii" with "i" in the string "Hiiii" yields "Hii"
89 Another replacement yields "Hi" (the desired output)
89 Another replacement cds "Hi" (the desired output)
90 90
91 91 Parameters:
92 92 -----------
@@ -124,6 +124,14 b''
124 124 "prompt_number": 10
125 125 },
126 126 {
127 "cell_type": "heading",
128 "level": 2,
129 "metadata": {},
130 "source": [
131 "Here is a very long heading that pandoc will wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap"
132 ]
133 },
134 {
127 135 "cell_type": "markdown",
128 136 "metadata": {},
129 137 "source": [
@@ -1,12 +1,10 b''
1 """
2 Contains tests for the nbconvertapp
3 """
1 """Test NbConvertApp"""
2
4 3 #-----------------------------------------------------------------------------
5 #Copyright (c) 2013, the IPython Development Team.
6 #
7 #Distributed under the terms of the Modified BSD License.
4 # Copyright (C) 2013 The IPython Development Team
8 5 #
9 #The full license is in the file COPYING.txt, distributed with this software.
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
10 8 #-----------------------------------------------------------------------------
11 9
12 10 #-----------------------------------------------------------------------------
@@ -15,12 +13,14 b' Contains tests for the nbconvertapp'
15 13
16 14 import os
17 15 import glob
16 import sys
18 17
19 18 from .base import TestsBase
20 19
20 import IPython.testing.tools as tt
21 21 from IPython.testing import decorators as dec
22 22
23
23
24 24 #-----------------------------------------------------------------------------
25 25 # Constants
26 26 #-----------------------------------------------------------------------------
@@ -35,13 +35,14 b' class TestNbConvertApp(TestsBase):'
35 35
36 36
37 37 def test_notebook_help(self):
38 """
39 Will help show if no notebooks are specified?
40 """
38 """Will help show if no notebooks are specified?"""
41 39 with self.create_temp_cwd():
42 40 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
43 assert "see '--help-all'" in out
44
41 self.assertIn("see '--help-all'", out)
42
43 def test_help_output(self):
44 """ipython nbconvert --help-all works"""
45 tt.help_all_output_test('nbconvert')
45 46
46 47 def test_glob(self):
47 48 """
@@ -102,6 +103,30 b' class TestNbConvertApp(TestsBase):'
102 103 assert os.path.isfile('notebook1.tex')
103 104 assert os.path.isfile('notebook1.pdf')
104 105
106 @dec.onlyif_cmds_exist('pandoc')
107 def test_spurious_cr(self):
108 """Check for extra CR characters"""
109 with self.create_temp_cwd(['notebook2.ipynb']):
110 self.call('nbconvert --log-level 0 --to latex notebook2')
111 assert os.path.isfile('notebook2.tex')
112 with open('notebook2.tex') as f:
113 tex = f.read()
114 self.call('nbconvert --log-level 0 --to html notebook2')
115 assert os.path.isfile('notebook2.html')
116 with open('notebook2.html') as f:
117 html = f.read()
118 self.assertEqual(tex.count('\r'), tex.count('\r\n'))
119 self.assertEqual(html.count('\r'), html.count('\r\n'))
120
121 @dec.onlyif_cmds_exist('pandoc')
122 def test_png_base64_html_ok(self):
123 """Is embedded png data well formed in HTML?"""
124 with self.create_temp_cwd(['notebook2.ipynb']):
125 self.call('nbconvert --log-level 0 --to HTML '
126 'notebook2.ipynb --template full')
127 assert os.path.isfile('notebook2.html')
128 with open('notebook2.html') as f:
129 assert "data:image/png;base64,b'" not in f.read()
105 130
106 131 @dec.onlyif_cmds_exist('pandoc')
107 132 def test_template(self):
@@ -15,6 +15,7 b' from __future__ import print_function'
15 15
16 16 # Stdlib imports
17 17 import subprocess
18 from io import TextIOWrapper, BytesIO
18 19
19 20 # IPython imports
20 21 from IPython.utils.py3compat import cast_bytes
@@ -64,6 +65,6 b" def pandoc(source, fmt, to, extra_args=None, encoding='utf-8'):"
64 65 "http://johnmacfarlane.net/pandoc/installing.html"
65 66 )
66 67 out, _ = p.communicate(cast_bytes(source, encoding))
67 out = out.decode(encoding, 'replace')
68 return out[:-1]
68 out = TextIOWrapper(BytesIO(out), encoding, 'replace').read()
69 return out.rstrip('\n')
69 70
@@ -25,6 +25,7 b' import pprint'
25 25 import uuid
26 26
27 27 from IPython.utils.ipstruct import Struct
28 from IPython.utils.py3compat import cast_unicode
28 29
29 30 #-----------------------------------------------------------------------------
30 31 # Code
@@ -67,21 +68,21 b' def new_output(output_type=None, output_text=None, output_png=None,'
67 68
68 69 if output_type != 'pyerr':
69 70 if output_text is not None:
70 output.text = unicode(output_text)
71 output.text = cast_unicode(output_text)
71 72 if output_png is not None:
72 output.png = bytes(output_png)
73 output.png = cast_unicode(output_png)
73 74 if output_jpeg is not None:
74 output.jpeg = bytes(output_jpeg)
75 output.jpeg = cast_unicode(output_jpeg)
75 76 if output_html is not None:
76 output.html = unicode(output_html)
77 output.html = cast_unicode(output_html)
77 78 if output_svg is not None:
78 output.svg = unicode(output_svg)
79 output.svg = cast_unicode(output_svg)
79 80 if output_latex is not None:
80 output.latex = unicode(output_latex)
81 output.latex = cast_unicode(output_latex)
81 82 if output_json is not None:
82 output.json = unicode(output_json)
83 output.json = cast_unicode(output_json)
83 84 if output_javascript is not None:
84 output.javascript = unicode(output_javascript)
85 output.javascript = cast_unicode(output_javascript)
85 86
86 87 if output_type == u'pyout':
87 88 if prompt_number is not None:
@@ -89,14 +90,14 b' def new_output(output_type=None, output_text=None, output_png=None,'
89 90
90 91 if output_type == u'pyerr':
91 92 if ename is not None:
92 output.ename = unicode(ename)
93 output.ename = cast_unicode(ename)
93 94 if evalue is not None:
94 output.evalue = unicode(evalue)
95 output.evalue = cast_unicode(evalue)
95 96 if traceback is not None:
96 output.traceback = [unicode(frame) for frame in list(traceback)]
97 output.traceback = [cast_unicode(frame) for frame in list(traceback)]
97 98
98 99 if output_type == u'stream':
99 output.stream = 'stdout' if stream is None else unicode(stream)
100 output.stream = 'stdout' if stream is None else cast_unicode(stream)
100 101
101 102 return output
102 103
@@ -107,9 +108,9 b' def new_code_cell(input=None, prompt_number=None, outputs=None,'
107 108 cell = NotebookNode()
108 109 cell.cell_type = u'code'
109 110 if language is not None:
110 cell.language = unicode(language)
111 cell.language = cast_unicode(language)
111 112 if input is not None:
112 cell.input = unicode(input)
113 cell.input = cast_unicode(input)
113 114 if prompt_number is not None:
114 115 cell.prompt_number = int(prompt_number)
115 116 if outputs is None:
@@ -130,9 +131,9 b' def new_text_cell(cell_type, source=None, rendered=None, metadata=None):'
130 131 if cell_type == 'plaintext':
131 132 cell_type = 'raw'
132 133 if source is not None:
133 cell.source = unicode(source)
134 cell.source = cast_unicode(source)
134 135 if rendered is not None:
135 cell.rendered = unicode(rendered)
136 cell.rendered = cast_unicode(rendered)
136 137 cell.metadata = NotebookNode(metadata or {})
137 138 cell.cell_type = cell_type
138 139 return cell
@@ -143,9 +144,9 b' def new_heading_cell(source=None, rendered=None, level=1, metadata=None):'
143 144 cell = NotebookNode()
144 145 cell.cell_type = u'heading'
145 146 if source is not None:
146 cell.source = unicode(source)
147 cell.source = cast_unicode(source)
147 148 if rendered is not None:
148 cell.rendered = unicode(rendered)
149 cell.rendered = cast_unicode(rendered)
149 150 cell.level = int(level)
150 151 cell.metadata = NotebookNode(metadata or {})
151 152 return cell
@@ -155,7 +156,7 b' def new_worksheet(name=None, cells=None, metadata=None):'
155 156 """Create a worksheet by name with with a list of cells."""
156 157 ws = NotebookNode()
157 158 if name is not None:
158 ws.name = unicode(name)
159 ws.name = cast_unicode(name)
159 160 if cells is None:
160 161 ws.cells = []
161 162 else:
@@ -178,7 +179,7 b' def new_notebook(name=None, metadata=None, worksheets=None):'
178 179 else:
179 180 nb.metadata = NotebookNode(metadata)
180 181 if name is not None:
181 nb.metadata.name = unicode(name)
182 nb.metadata.name = cast_unicode(name)
182 183 return nb
183 184
184 185
@@ -187,29 +188,29 b' def new_metadata(name=None, authors=None, license=None, created=None,'
187 188 """Create a new metadata node."""
188 189 metadata = NotebookNode()
189 190 if name is not None:
190 metadata.name = unicode(name)
191 metadata.name = cast_unicode(name)
191 192 if authors is not None:
192 193 metadata.authors = list(authors)
193 194 if created is not None:
194 metadata.created = unicode(created)
195 metadata.created = cast_unicode(created)
195 196 if modified is not None:
196 metadata.modified = unicode(modified)
197 metadata.modified = cast_unicode(modified)
197 198 if license is not None:
198 metadata.license = unicode(license)
199 metadata.license = cast_unicode(license)
199 200 if gistid is not None:
200 metadata.gistid = unicode(gistid)
201 metadata.gistid = cast_unicode(gistid)
201 202 return metadata
202 203
203 204 def new_author(name=None, email=None, affiliation=None, url=None):
204 205 """Create a new author."""
205 206 author = NotebookNode()
206 207 if name is not None:
207 author.name = unicode(name)
208 author.name = cast_unicode(name)
208 209 if email is not None:
209 author.email = unicode(email)
210 author.email = cast_unicode(email)
210 211 if affiliation is not None:
211 author.affiliation = unicode(affiliation)
212 author.affiliation = cast_unicode(affiliation)
212 213 if url is not None:
213 author.url = unicode(url)
214 author.url = cast_unicode(url)
214 215 return author
215 216
@@ -46,7 +46,7 b' class JSONReader(NotebookReader):'
46 46 return nb
47 47
48 48 def to_notebook(self, d, **kwargs):
49 return restore_bytes(rejoin_lines(from_dict(d)))
49 return rejoin_lines(from_dict(d))
50 50
51 51
52 52 class JSONWriter(NotebookWriter):
@@ -190,7 +190,7 b' class PyWriter(NotebookWriter):'
190 190 lines.extend([u'# ' + line for line in input.splitlines()])
191 191 lines.append(u'')
192 192 lines.append('')
193 return unicode('\n'.join(lines))
193 return u'\n'.join(lines)
194 194
195 195
196 196 _reader = PyReader()
@@ -32,6 +32,8 b' def restore_bytes(nb):'
32 32
33 33 Base64 encoding is handled elsewhere. Bytes objects in the notebook are
34 34 always b64-encoded. We DO NOT encode/decode around file formats.
35
36 Note: this is never used
35 37 """
36 38 for ws in nb.worksheets:
37 39 for cell in ws.cells:
@@ -9,9 +9,9 b' from ..nbbase import ('
9 9 new_metadata, new_author, new_heading_cell, nbformat, nbformat_minor
10 10 )
11 11
12 # some random base64-encoded *bytes*
13 png = encodestring(os.urandom(5))
14 jpeg = encodestring(os.urandom(6))
12 # some random base64-encoded *text*
13 png = encodestring(os.urandom(5)).decode('ascii')
14 jpeg = encodestring(os.urandom(6)).decode('ascii')
15 15
16 16 ws = new_worksheet(name='worksheet1')
17 17
@@ -1,4 +1,5 b''
1 1 import pprint
2 from base64 import decodestring
2 3 from unittest import TestCase
3 4
4 5 from ..nbjson import reads, writes
@@ -29,5 +30,42 b' class TestJSON(formattest.NBFormatTest, TestCase):'
29 30 s = writes(nb0, split_lines=True)
30 31 self.assertEqual(nbjson.reads(s),nb0)
31 32
33 def test_read_png(self):
34 """PNG output data is b64 unicode"""
35 s = writes(nb0)
36 nb1 = nbjson.reads(s)
37 found_png = False
38 for cell in nb1.worksheets[0].cells:
39 if not 'outputs' in cell:
40 continue
41 for output in cell.outputs:
42 if 'png' in output:
43 found_png = True
44 pngdata = output['png']
45 self.assertEqual(type(pngdata), unicode)
46 # test that it is valid b64 data
47 b64bytes = pngdata.encode('ascii')
48 raw_bytes = decodestring(b64bytes)
49 assert found_png, "never found png output"
50
51 def test_read_jpeg(self):
52 """JPEG output data is b64 unicode"""
53 s = writes(nb0)
54 nb1 = nbjson.reads(s)
55 found_jpeg = False
56 for cell in nb1.worksheets[0].cells:
57 if not 'outputs' in cell:
58 continue
59 for output in cell.outputs:
60 if 'jpeg' in output:
61 found_jpeg = True
62 jpegdata = output['jpeg']
63 self.assertEqual(type(jpegdata), unicode)
64 # test that it is valid b64 data
65 b64bytes = jpegdata.encode('ascii')
66 raw_bytes = decodestring(b64bytes)
67 assert found_jpeg, "never found jpeg output"
68
69
32 70
33 71
@@ -141,3 +141,17 b' class TestMetadata(TestCase):'
141 141 self.assertEqual(md.gistid, u'21341231')
142 142 self.assertEqual(md.authors, authors)
143 143
144 class TestOutputs(TestCase):
145 def test_binary_png(self):
146 out = new_output(output_png=b'\x89PNG\r\n\x1a\n')
147
148 def test_b64b6tes_png(self):
149 out = new_output(output_png=b'iVBORw0KG')
150
151 def test_binary_jpeg(self):
152 out = new_output(output_jpeg=b'\xff\xd8')
153
154 def test_b64b6tes_jpeg(self):
155 out = new_output(output_jpeg=b'/9')
156
157
@@ -1,11 +1,11 b''
1 1 #!/usr/bin/env python
2 2 # -*- coding: utf8 -*-
3 import argparse
4 import traceback
5 import json
3 6
4 7 from IPython.external.jsonschema import Draft3Validator, validate, ValidationError
5 8 import IPython.external.jsonpointer as jsonpointer
6 from IPython.external import argparse
7 import traceback
8 import json
9 9
10 10 def nbvalidate(nbjson, schema='v3.withref.json', key=None,verbose=True):
11 11 v3schema = resolve_ref(json.load(open(schema,'r')))
@@ -71,7 +71,6 b' class ParallelCrashHandler(CrashHandler):'
71 71 base_aliases = {}
72 72 base_aliases.update(base_ip_aliases)
73 73 base_aliases.update({
74 'profile-dir' : 'ProfileDir.location',
75 74 'work-dir' : 'BaseParallelApplication.work_dir',
76 75 'log-to-file' : 'BaseParallelApplication.log_to_file',
77 76 'clean-logs' : 'BaseParallelApplication.clean_logs',
@@ -11,7 +11,7 b' Authors:'
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 # Copyright (C) 2008-2011 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
15 15 #
16 16 # Distributed under the terms of the BSD License. The full license is in
17 17 # the file COPYING, distributed as part of this software.
@@ -44,7 +44,7 b' from IPython.parallel.apps.baseapp import ('
44 44 catch_config_error,
45 45 )
46 46 from IPython.utils.importstring import import_item
47 from IPython.utils.localinterfaces import LOCALHOST, PUBLIC_IPS
47 from IPython.utils.localinterfaces import localhost, public_ips
48 48 from IPython.utils.traitlets import Instance, Unicode, Bool, List, Dict, TraitError
49 49
50 50 from IPython.kernel.zmq.session import (
@@ -224,13 +224,13 b' class IPControllerApp(BaseParallelApplication):'
224 224 location = cdict['location']
225 225
226 226 if not location:
227 if PUBLIC_IPS:
228 location = PUBLIC_IPS[-1]
227 if public_ips():
228 location = public_ips()[-1]
229 229 else:
230 230 self.log.warn("Could not identify this machine's IP, assuming %s."
231 231 " You may need to specify '--location=<external_ip_address>' to help"
232 " IPython decide when to connect via loopback." % LOCALHOST)
233 location = LOCALHOST
232 " IPython decide when to connect via loopback." % localhost() )
233 location = localhost()
234 234 cdict['location'] = location
235 235 fname = os.path.join(self.profile_dir.security_dir, fname)
236 236 self.log.info("writing connection info to %s", fname)
@@ -26,7 +26,7 b' import zmq'
26 26 from zmq.eventloop import ioloop, zmqstream
27 27
28 28 from IPython.config.configurable import LoggingConfigurable
29 from IPython.utils.localinterfaces import LOCALHOST
29 from IPython.utils.localinterfaces import localhost
30 30 from IPython.utils.traitlets import Int, Unicode, Instance, List
31 31
32 32 #-----------------------------------------------------------------------------
@@ -44,8 +44,10 b' class LogWatcher(LoggingConfigurable):'
44 44 # configurables
45 45 topics = List([''], config=True,
46 46 help="The ZMQ topics to subscribe to. Default is to subscribe to all messages")
47 url = Unicode('tcp://%s:20202' % LOCALHOST, config=True,
47 url = Unicode(config=True,
48 48 help="ZMQ url on which to listen for log messages")
49 def _url_default(self):
50 return 'tcp://%s:20202' % localhost()
49 51
50 52 # internals
51 53 stream = Instance('zmq.eventloop.zmqstream.ZMQStream')
@@ -391,7 +391,7 b' class AsyncResult(object):'
391 391 tic = time.time()
392 392 while not self.ready() and (timeout < 0 or time.time() - tic <= timeout):
393 393 self.wait(interval)
394 clear_output()
394 clear_output(wait=True)
395 395 print("%4i/%i tasks finished after %4i s" % (self.progress, N, self.elapsed), end="")
396 396 sys.stdout.flush()
397 397 print()
@@ -37,7 +37,7 b' from IPython.core.profiledir import ProfileDir, ProfileDirError'
37 37 from IPython.utils.capture import RichOutput
38 38 from IPython.utils.coloransi import TermColors
39 39 from IPython.utils.jsonutil import rekey
40 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
40 from IPython.utils.localinterfaces import localhost, is_local_ip
41 41 from IPython.utils.path import get_ipython_dir
42 42 from IPython.utils.py3compat import cast_bytes
43 43 from IPython.utils.traitlets import (HasTraits, Integer, Instance, Unicode,
@@ -433,13 +433,13 b' class Client(HasTraits):'
433 433
434 434 url = cfg['registration']
435 435
436 if location is not None and addr == LOCALHOST:
436 if location is not None and addr == localhost():
437 437 # location specified, and connection is expected to be local
438 if location not in LOCAL_IPS and not sshserver:
438 if not is_local_ip(location) and not sshserver:
439 439 # load ssh from JSON *only* if the controller is not on
440 440 # this machine
441 441 sshserver=cfg['ssh']
442 if location not in LOCAL_IPS and not sshserver:
442 if not is_local_ip(location) and not sshserver:
443 443 # warn if no ssh specified, but SSH is probably needed
444 444 # This is only a warning, because the most likely cause
445 445 # is a local Controller on a laptop whose IP is dynamic
@@ -495,7 +495,12 b' class Client(HasTraits):'
495 495 }
496 496 self._queue_handlers = {'execute_reply' : self._handle_execute_reply,
497 497 'apply_reply' : self._handle_apply_reply}
498 self._connect(sshserver, ssh_kwargs, timeout)
498
499 try:
500 self._connect(sshserver, ssh_kwargs, timeout)
501 except:
502 self.close(linger=0)
503 raise
499 504
500 505 # last step: setup magics, if we are in IPython:
501 506
@@ -599,7 +604,6 b' class Client(HasTraits):'
599 604 self._connected=True
600 605
601 606 def connect_socket(s, url):
602 # url = util.disambiguate_url(url, self._config['location'])
603 607 if self._ssh:
604 608 return tunnel.tunnel_connection(s, url, sshserver, **ssh_kwargs)
605 609 else:
@@ -956,14 +960,23 b' class Client(HasTraits):'
956 960 view.activate(suffix)
957 961 return view
958 962
959 def close(self):
963 def close(self, linger=None):
964 """Close my zmq Sockets
965
966 If `linger`, set the zmq LINGER socket option,
967 which allows discarding of messages.
968 """
960 969 if self._closed:
961 970 return
962 971 self.stop_spin_thread()
963 snames = filter(lambda n: n.endswith('socket'), dir(self))
964 for socket in map(lambda name: getattr(self, name), snames):
965 if isinstance(socket, zmq.Socket) and not socket.closed:
966 socket.close()
972 snames = [ trait for trait in self.trait_names() if trait.endswith("socket") ]
973 for name in snames:
974 socket = getattr(self, name)
975 if socket is not None and not socket.closed:
976 if linger is not None:
977 socket.close(linger=linger)
978 else:
979 socket.close()
967 980 self._closed = True
968 981
969 982 def _spin_every(self, interval=1):
@@ -89,7 +89,6 b' def sync_view_results(f, self, *args, **kwargs):'
89 89 view = self.view
90 90 if view._in_sync_results:
91 91 return f(self, *args, **kwargs)
92 print 'in sync results', f
93 92 view._in_sync_results = True
94 93 try:
95 94 ret = f(self, *args, **kwargs)
@@ -30,7 +30,7 b' from zmq.eventloop.zmqstream import ZMQStream'
30 30
31 31 # internal:
32 32 from IPython.utils.importstring import import_item
33 from IPython.utils.localinterfaces import LOCALHOST
33 from IPython.utils.localinterfaces import localhost
34 34 from IPython.utils.py3compat import cast_bytes
35 35 from IPython.utils.traitlets import (
36 36 HasTraits, Instance, Integer, Unicode, Dict, Set, Tuple, CBytes, DottedObjectName
@@ -177,20 +177,25 b' class HubFactory(RegistrationFactory):'
177 177 def _notifier_port_default(self):
178 178 return util.select_random_ports(1)[0]
179 179
180 engine_ip = Unicode(LOCALHOST, config=True,
180 engine_ip = Unicode(config=True,
181 181 help="IP on which to listen for engine connections. [default: loopback]")
182 def _engine_ip_default(self):
183 return localhost()
182 184 engine_transport = Unicode('tcp', config=True,
183 185 help="0MQ transport for engine connections. [default: tcp]")
184 186
185 client_ip = Unicode(LOCALHOST, config=True,
187 client_ip = Unicode(config=True,
186 188 help="IP on which to listen for client connections. [default: loopback]")
187 189 client_transport = Unicode('tcp', config=True,
188 190 help="0MQ transport for client connections. [default : tcp]")
189 191
190 monitor_ip = Unicode(LOCALHOST, config=True,
192 monitor_ip = Unicode(config=True,
191 193 help="IP on which to listen for monitor messages. [default: loopback]")
192 194 monitor_transport = Unicode('tcp', config=True,
193 195 help="0MQ transport for monitor messages. [default : tcp]")
196
197 _client_ip_default = _monitor_ip_default = _engine_ip_default
198
194 199
195 200 monitor_url = Unicode('')
196 201
@@ -39,7 +39,7 b' class MongoDB(BaseDB):'
39 39 necessary if the default mongodb configuration does not point to your
40 40 mongod instance."""
41 41 )
42 database = Unicode(config=True,
42 database = Unicode("ipython-tasks", config=True,
43 43 help="""The MongoDB database name to use for storing tasks for this session. If unspecified,
44 44 a new database will be created with the Hub's IDENT. Specifying the database will result
45 45 in tasks from previous sessions being available via Clients' db_query and
@@ -358,7 +358,7 b' class TaskScheduler(SessionFactory):'
358 358 # build fake metadata
359 359 md = dict(
360 360 status=u'error',
361 engine=engine,
361 engine=engine.decode('ascii'),
362 362 date=datetime.now(),
363 363 )
364 364 msg = self.session.msg('apply_reply', content, parent=parent, metadata=md)
@@ -96,7 +96,7 b' class SQLiteDB(BaseDB):'
96 96 location = Unicode('', config=True,
97 97 help="""The directory containing the sqlite task database. The default
98 98 is to use the cluster_dir location.""")
99 table = Unicode("", config=True,
99 table = Unicode("ipython-tasks", config=True,
100 100 help="""The SQLite Table to use for storing tasks for this session. If unspecified,
101 101 a new table will be created with the Hub's IDENT. Specifying the table will result
102 102 in tasks from previous sessions being available via Clients' db_query and
@@ -195,7 +195,7 b' class SQLiteDB(BaseDB):'
195 195
196 196 If a bad (old) table does exist, return False
197 197 """
198 cursor = self._db.execute("PRAGMA table_info(%s)"%self.table)
198 cursor = self._db.execute("PRAGMA table_info('%s')"%self.table)
199 199 lines = cursor.fetchall()
200 200 if not lines:
201 201 # table does not exist
@@ -241,7 +241,7 b' class SQLiteDB(BaseDB):'
241 241 )
242 242 previous_table = self.table
243 243
244 self._db.execute("""CREATE TABLE IF NOT EXISTS %s
244 self._db.execute("""CREATE TABLE IF NOT EXISTS '%s'
245 245 (msg_id text PRIMARY KEY,
246 246 header dict text,
247 247 metadata dict text,
@@ -333,12 +333,12 b' class SQLiteDB(BaseDB):'
333 333 d['msg_id'] = msg_id
334 334 line = self._dict_to_list(d)
335 335 tups = '(%s)'%(','.join(['?']*len(line)))
336 self._db.execute("INSERT INTO %s VALUES %s"%(self.table, tups), line)
336 self._db.execute("INSERT INTO '%s' VALUES %s"%(self.table, tups), line)
337 337 # self._db.commit()
338 338
339 339 def get_record(self, msg_id):
340 340 """Get a specific Task Record, by msg_id."""
341 cursor = self._db.execute("""SELECT * FROM %s WHERE msg_id==?"""%self.table, (msg_id,))
341 cursor = self._db.execute("""SELECT * FROM '%s' WHERE msg_id==?"""%self.table, (msg_id,))
342 342 line = cursor.fetchone()
343 343 if line is None:
344 344 raise KeyError("No such msg: %r"%msg_id)
@@ -346,7 +346,7 b' class SQLiteDB(BaseDB):'
346 346
347 347 def update_record(self, msg_id, rec):
348 348 """Update the data in an existing record."""
349 query = "UPDATE %s SET "%self.table
349 query = "UPDATE '%s' SET "%self.table
350 350 sets = []
351 351 keys = sorted(rec.keys())
352 352 values = []
@@ -361,13 +361,13 b' class SQLiteDB(BaseDB):'
361 361
362 362 def drop_record(self, msg_id):
363 363 """Remove a record from the DB."""
364 self._db.execute("""DELETE FROM %s WHERE msg_id==?"""%self.table, (msg_id,))
364 self._db.execute("""DELETE FROM '%s' WHERE msg_id==?"""%self.table, (msg_id,))
365 365 # self._db.commit()
366 366
367 367 def drop_matching_records(self, check):
368 368 """Remove a record from the DB."""
369 369 expr,args = self._render_expression(check)
370 query = "DELETE FROM %s WHERE %s"%(self.table, expr)
370 query = "DELETE FROM '%s' WHERE %s"%(self.table, expr)
371 371 self._db.execute(query,args)
372 372 # self._db.commit()
373 373
@@ -399,7 +399,7 b' class SQLiteDB(BaseDB):'
399 399 else:
400 400 req = '*'
401 401 expr,args = self._render_expression(check)
402 query = """SELECT %s FROM %s WHERE %s"""%(req, self.table, expr)
402 query = """SELECT %s FROM '%s' WHERE %s"""%(req, self.table, expr)
403 403 cursor = self._db.execute(query, args)
404 404 matches = cursor.fetchall()
405 405 records = []
@@ -410,7 +410,7 b' class SQLiteDB(BaseDB):'
410 410
411 411 def get_history(self):
412 412 """get all msg_ids, ordered by time submitted."""
413 query = """SELECT msg_id FROM %s ORDER by submitted ASC"""%self.table
413 query = """SELECT msg_id FROM '%s' ORDER by submitted ASC"""%self.table
414 414 cursor = self._db.execute(query)
415 415 # will be a list of length 1 tuples
416 416 return [ tup[0] for tup in cursor.fetchall()]
@@ -24,7 +24,7 b' from zmq.eventloop import ioloop, zmqstream'
24 24
25 25 from IPython.external.ssh import tunnel
26 26 # internal
27 from IPython.utils.localinterfaces import LOCALHOST
27 from IPython.utils.localinterfaces import localhost
28 28 from IPython.utils.traitlets import (
29 29 Instance, Dict, Integer, Type, Float, Integer, Unicode, CBytes, Bool
30 30 )
@@ -184,13 +184,13 b' class EngineFactory(RegistrationFactory):'
184 184 if self.max_heartbeat_misses > 0:
185 185 # Add a monitor socket which will record the last time a ping was seen
186 186 mon = self.context.socket(zmq.SUB)
187 mport = mon.bind_to_random_port('tcp://%s' % LOCALHOST)
187 mport = mon.bind_to_random_port('tcp://%s' % localhost())
188 188 mon.setsockopt(zmq.SUBSCRIBE, b"")
189 189 self._hb_listener = zmqstream.ZMQStream(mon, self.loop)
190 190 self._hb_listener.on_recv(self._report_ping)
191 191
192 192
193 hb_monitor = "tcp://%s:%i" % (LOCALHOST, mport)
193 hb_monitor = "tcp://%s:%i" % (localhost(), mport)
194 194
195 195 heart = Heart(hb_ping, hb_pong, hb_monitor , heart_id=identity)
196 196 heart.start()
@@ -24,7 +24,7 b' import zmq'
24 24 from zmq.eventloop.ioloop import IOLoop
25 25
26 26 from IPython.config.configurable import Configurable
27 from IPython.utils.localinterfaces import LOCALHOST
27 from IPython.utils.localinterfaces import localhost
28 28 from IPython.utils.traitlets import Integer, Instance, Unicode
29 29
30 30 from IPython.parallel.util import select_random_ports
@@ -40,16 +40,18 b' class RegistrationFactory(SessionFactory):'
40 40
41 41 url = Unicode('', config=True,
42 42 help="""The 0MQ url used for registration. This sets transport, ip, and port
43 in one variable. For example: url='tcp://%s:12345' or
43 in one variable. For example: url='tcp://127.0.0.1:12345' or
44 44 url='epgm://*:90210'"""
45 % LOCALHOST) # url takes precedence over ip,regport,transport
45 ) # url takes precedence over ip,regport,transport
46 46 transport = Unicode('tcp', config=True,
47 47 help="""The 0MQ transport for communications. This will likely be
48 48 the default of 'tcp', but other values include 'ipc', 'epgm', 'inproc'.""")
49 ip = Unicode(LOCALHOST, config=True,
49 ip = Unicode(config=True,
50 50 help="""The IP address for registration. This is generally either
51 51 '127.0.0.1' for loopback only or '*' for all interfaces.
52 [default: '%s']""" % LOCALHOST)
52 """)
53 def _ip_default(self):
54 return localhost()
53 55 regport = Integer(config=True,
54 56 help="""The port on which the Hub listens for registration.""")
55 57 def _regport_default(self):
@@ -63,8 +63,7 b' def setup():'
63 63 tic = time.time()
64 64 while not os.path.exists(engine_json) or not os.path.exists(client_json):
65 65 if cp.poll() is not None:
66 print cp.poll()
67 raise RuntimeError("The test controller failed to start.")
66 raise RuntimeError("The test controller exited with status %s" % cp.poll())
68 67 elif time.time()-tic > 15:
69 68 raise RuntimeError("Timeout waiting for the test controller to start.")
70 69 time.sleep(0.1)
@@ -172,7 +172,6 b' class TestClient(ClusterTestCase):'
172 172 # give the monitor time to notice the message
173 173 time.sleep(.25)
174 174 ahr = self.client.get_result(ar.msg_ids[0])
175 print ar.get(), ahr.get(), ar._single_result, ahr._single_result
176 175 self.assertTrue(isinstance(ahr, AsyncHubResult))
177 176 self.assertEqual(ahr.get().pyout, ar.get().pyout)
178 177 ar2 = self.client.get_result(ar.msg_ids[0])
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (2353 lines changed) Show them Hide them
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now