##// 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
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
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
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
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
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
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
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
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 - 2.7
4 - 2.7
5 - 3.3
5 - 3.3
6 before_install:
6 before_install:
7 - pip install jinja2
8 - easy_install -q pyzmq
7 - easy_install -q pyzmq
8 - pip install jinja2 sphinx pygments tornado
9 - sudo apt-get install pandoc
9 - sudo apt-get install pandoc
10 - pip install pygments
11 - pip install sphinx
12 install:
10 install:
13 - python setup.py install -q
11 - python setup.py install -q
14 script:
12 script:
15 - if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then iptest -w /tmp; fi
13 - if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then cd /tmp; iptest; fi
16 - if [[ $TRAVIS_PYTHON_VERSION == '3.'* ]]; then iptest3 -w /tmp; fi
14 - if [[ $TRAVIS_PYTHON_VERSION == '3.'* ]]; then cd /tmp; iptest3; fi
@@ -28,8 +28,8 b' import sys'
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 # Don't forget to also update setup.py when this changes!
30 # Don't forget to also update setup.py when this changes!
31 if sys.version[0:3] < '2.6':
31 if sys.version_info[:2] < (2,7):
32 raise ImportError('Python Version 2.6 or above is required for IPython.')
32 raise ImportError('IPython requires Python Version 2.7 or above.')
33
33
34 # Make it easy to import extensions - they are always directly on pythonpath.
34 # Make it easy to import extensions - they are always directly on pythonpath.
35 # Therefore, non-IPython modules can be added to extensions directory.
35 # Therefore, non-IPython modules can be added to extensions directory.
@@ -142,6 +142,9 b' class Application(SingletonConfigurable):'
142
142
143 # The version string of this application.
143 # The version string of this application.
144 version = Unicode(u'0.0')
144 version = Unicode(u'0.0')
145
146 # the argv used to initialize the application
147 argv = List()
145
148
146 # The log level for the application
149 # The log level for the application
147 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
150 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
@@ -454,6 +457,7 b' class Application(SingletonConfigurable):'
454 def parse_command_line(self, argv=None):
457 def parse_command_line(self, argv=None):
455 """Parse the command line arguments."""
458 """Parse the command line arguments."""
456 argv = sys.argv[1:] if argv is None else argv
459 argv = sys.argv[1:] if argv is None else argv
460 self.argv = list(argv)
457
461
458 if argv and argv[0] == 'help':
462 if argv and argv[0] == 'help':
459 # turn `ipython help notebook` into `ipython notebook -h`
463 # turn `ipython help notebook` into `ipython notebook -h`
@@ -24,11 +24,11 b' Authors'
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 import __builtin__ as builtin_mod
26 import __builtin__ as builtin_mod
27 import argparse
27 import os
28 import os
28 import re
29 import re
29 import sys
30 import sys
30
31
31 from IPython.external import argparse
32 from IPython.utils.path import filefind, get_ipython_dir
32 from IPython.utils.path import filefind, get_ipython_dir
33 from IPython.utils import py3compat, warn
33 from IPython.utils import py3compat, warn
34 from IPython.utils.encoding import DEFAULT_ENCODING
34 from IPython.utils.encoding import DEFAULT_ENCODING
@@ -1,3 +1,4 b''
1 # coding: utf-8
1 """
2 """
2 Tests for IPython.config.application.Application
3 Tests for IPython.config.application.Application
3
4
@@ -185,4 +186,8 b' class TestApplication(TestCase):'
185 self.assertEqual(app.bar.b, 5)
186 self.assertEqual(app.bar.b, 5)
186 self.assertEqual(app.extra_args, ['extra', '--disable', 'args'])
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 # Network Constants
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 # Globals
59 # Globals
@@ -254,7 +254,7 b' class IPythonConsoleApp(ConnectionFileMixin):'
254 with open(fname) as f:
254 with open(fname) as f:
255 cfg = json.load(f)
255 cfg = json.load(f)
256 self.transport = cfg.get('transport', 'tcp')
256 self.transport = cfg.get('transport', 'tcp')
257 self.ip = cfg.get('ip', LOCALHOST)
257 self.ip = cfg.get('ip', localhost())
258
258
259 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
259 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
260 name = channel + '_port'
260 name = channel + '_port'
@@ -282,7 +282,7 b' class IPythonConsoleApp(ConnectionFileMixin):'
282 if self.sshkey and not self.sshserver:
282 if self.sshkey and not self.sshserver:
283 # specifying just the key implies that we are connecting directly
283 # specifying just the key implies that we are connecting directly
284 self.sshserver = ip
284 self.sshserver = ip
285 ip = LOCALHOST
285 ip = localhost()
286
286
287 # build connection dict for tunnels:
287 # build connection dict for tunnels:
288 info = dict(ip=ip,
288 info = dict(ip=ip,
@@ -295,7 +295,7 b' class IPythonConsoleApp(ConnectionFileMixin):'
295 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
295 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
296
296
297 # tunnels return a new set of ports, which will be on localhost:
297 # tunnels return a new set of ports, which will be on localhost:
298 self.ip = LOCALHOST
298 self.ip = localhost()
299 try:
299 try:
300 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
300 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
301 except:
301 except:
@@ -20,17 +20,15 b' Authors:'
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 import __builtin__
24 import keyword
25 import os
23 import os
26 import re
24 import re
27 import sys
25 import sys
28
26
29 from IPython.config.configurable import Configurable
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 from IPython.utils.traitlets import List, Instance
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 # Utilities
34 # Utilities
@@ -104,6 +102,70 b' class AliasError(Exception):'
104 class InvalidAliasError(AliasError):
102 class InvalidAliasError(AliasError):
105 pass
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 # Main AliasManager class
170 # Main AliasManager class
109 #-----------------------------------------------------------------------------
171 #-----------------------------------------------------------------------------
@@ -116,35 +178,19 b' class AliasManager(Configurable):'
116
178
117 def __init__(self, shell=None, **kwargs):
179 def __init__(self, shell=None, **kwargs):
118 super(AliasManager, self).__init__(shell=shell, **kwargs)
180 super(AliasManager, self).__init__(shell=shell, **kwargs)
119 self.alias_table = {}
181 # For convenient access
120 self.exclude_aliases()
182 self.linemagics = self.shell.magics_manager.magics['line']
121 self.init_aliases()
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 def init_aliases(self):
185 def init_aliases(self):
138 # Load default aliases
186 # Load default & user aliases
139 for name, cmd in self.default_aliases:
187 for name, cmd in self.default_aliases + self.user_aliases:
140 self.soft_define_alias(name, cmd)
141
142 # Load user aliases
143 for name, cmd in self.user_aliases:
144 self.soft_define_alias(name, cmd)
188 self.soft_define_alias(name, cmd)
145
189
146 def clear_aliases(self):
190 @property
147 self.alias_table.clear()
191 def aliases(self):
192 return [(n, func.cmd) for (n, func) in self.linemagics.items()
193 if isinstance(func, Alias)]
148
194
149 def soft_define_alias(self, name, cmd):
195 def soft_define_alias(self, name, cmd):
150 """Define an alias, but don't raise on an AliasError."""
196 """Define an alias, but don't raise on an AliasError."""
@@ -159,104 +205,33 b' class AliasManager(Configurable):'
159 This will raise an :exc:`AliasError` if there are validation
205 This will raise an :exc:`AliasError` if there are validation
160 problems.
206 problems.
161 """
207 """
162 nargs = self.validate_alias(name, cmd)
208 caller = Alias(shell=self.shell, name=name, cmd=cmd)
163 self.alias_table[name] = (nargs, cmd)
209 self.shell.magics_manager.register_function(caller, magic_kind='line',
210 magic_name=name)
164
211
165 def undefine_alias(self, name):
212 def get_alias(self, name):
166 if name in self.alias_table:
213 """Return an alias, or None if no alias by that name exists."""
167 del self.alias_table[name]
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):
217 def is_alias(self, name):
170 """Validate an alias and return the its number of arguments."""
218 """Return whether or not a given name has been defined as an alias"""
171 if name in self.no_alias:
219 return self.get_alias(name) is not None
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
182
220
183 def call_alias(self, alias, rest=''):
221 def undefine_alias(self, name):
184 """Call an alias given its name and the rest of the line."""
222 if self.is_alias(name):
185 cmd = self.transform_alias(alias, rest)
223 del self.linemagics[name]
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)
205 else:
224 else:
206 # Handle aliases with positional arguments
225 raise ValueError('%s is not an alias' % name)
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
216
226
217 Returns the provided command line, possibly with the first word
227 def clear_aliases(self):
218 (command) translated according to alias expansion rules.
228 for name, cmd in self.aliases:
219
229 self.undefine_alias(name)
220 [ipython]|16> _ip.expand_aliases("np myfile.txt")
230
221 <16> 'q:/opt/np/notepad++.exe myfile.txt'
231 def retrieve_alias(self, name):
222 """
232 """Retrieve the command to which an alias expands."""
223
233 caller = self.get_alias(name)
224 pre,_,fn,rest = split_user_input(line)
234 if caller:
225 res = pre + self.expand_aliases(fn, rest)
235 return caller.cmd
226 return res
236 else:
227
237 raise ValueError('%s is not an alias' % name)
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
@@ -53,6 +53,7 b' from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instan'
53 # aliases and flags
53 # aliases and flags
54
54
55 base_aliases = {
55 base_aliases = {
56 'profile-dir' : 'ProfileDir.location',
56 'profile' : 'BaseIPythonApplication.profile',
57 'profile' : 'BaseIPythonApplication.profile',
57 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
58 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
58 'log-level' : 'Application.log_level',
59 'log-level' : 'Application.log_level',
@@ -24,26 +24,26 b' Tip: to use the tab key as the completion key, call'
24 Notes:
24 Notes:
25
25
26 - Exceptions raised by the completer function are *ignored* (and
26 - Exceptions raised by the completer function are *ignored* (and
27 generally cause the completion to fail). This is a feature -- since
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
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,
29 traceback wouldn't work well without some complicated hoopla to save,
30 reset and restore the tty state.
30 reset and restore the tty state.
31
31
32 - The evaluation of the NAME.NAME... form may cause arbitrary
32 - The evaluation of the NAME.NAME... form may cause arbitrary
33 application defined code to be executed if an object with a
33 application defined code to be executed if an object with a
34 __getattr__ hook is found. Since it is the responsibility of the
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
35 application (or the user) to enable this feature, I consider this an
36 acceptable risk. More complicated expressions (e.g. function calls or
36 acceptable risk. More complicated expressions (e.g. function calls or
37 indexing operations) are *not* evaluated.
37 indexing operations) are *not* evaluated.
38
38
39 - GNU readline is also used by the built-in functions input() and
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
40 raw_input(), and thus these also benefit/suffer from the completer
41 features. Clearly an interactive application can benefit by
41 features. Clearly an interactive application can benefit by
42 specifying its own completer function and using raw_input() for all
42 specifying its own completer function and using raw_input() for all
43 its input.
43 its input.
44
44
45 - When the original stdin is not a tty device, GNU readline is never
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 def __init__(self, shell=None, namespace=None, global_namespace=None,
433 def __init__(self, shell=None, namespace=None, global_namespace=None,
434 alias_table=None, use_readline=True,
434 use_readline=True, config=None, **kwargs):
435 config=None, **kwargs):
436 """IPCompleter() -> completer
435 """IPCompleter() -> completer
437
436
438 Return a completer object suitable for use by the readline library
437 Return a completer object suitable for use by the readline library
@@ -441,17 +440,14 b' class IPCompleter(Completer):'
441 Inputs:
440 Inputs:
442
441
443 - shell: a pointer to the ipython shell itself. This is needed
442 - shell: a pointer to the ipython shell itself. This is needed
444 because this completer knows about magic functions, and those can
443 because this completer knows about magic functions, and those can
445 only be accessed via the ipython instance.
444 only be accessed via the ipython instance.
446
445
447 - namespace: an optional dict where completions are performed.
446 - namespace: an optional dict where completions are performed.
448
447
449 - global_namespace: secondary optional dict for completions, to
448 - global_namespace: secondary optional dict for completions, to
450 handle cases (such as IPython embedded inside functions) where
449 handle cases (such as IPython embedded inside functions) where
451 both Python scopes are visible.
450 both Python scopes are visible.
452
453 - If alias_table is supplied, it should be a dictionary of aliases
454 to complete.
455
451
456 use_readline : bool, optional
452 use_readline : bool, optional
457 If true, use the readline library. This completer can still function
453 If true, use the readline library. This completer can still function
@@ -476,9 +472,6 b' class IPCompleter(Completer):'
476 # List where completion matches will be stored
472 # List where completion matches will be stored
477 self.matches = []
473 self.matches = []
478 self.shell = shell
474 self.shell = shell
479 if alias_table is None:
480 alias_table = {}
481 self.alias_table = alias_table
482 # Regexp to split filenames with spaces in them
475 # Regexp to split filenames with spaces in them
483 self.space_name_re = re.compile(r'([^\\] )')
476 self.space_name_re = re.compile(r'([^\\] )')
484 # Hold a local ref. to glob.glob for speed
477 # Hold a local ref. to glob.glob for speed
@@ -505,7 +498,6 b' class IPCompleter(Completer):'
505 self.matchers = [self.python_matches,
498 self.matchers = [self.python_matches,
506 self.file_matches,
499 self.file_matches,
507 self.magic_matches,
500 self.magic_matches,
508 self.alias_matches,
509 self.python_func_kw_matches,
501 self.python_func_kw_matches,
510 ]
502 ]
511
503
@@ -628,22 +620,6 b' class IPCompleter(Completer):'
628 comp += [ pre+m for m in line_magics if m.startswith(bare_text)]
620 comp += [ pre+m for m in line_magics if m.startswith(bare_text)]
629 return comp
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 def python_matches(self,text):
623 def python_matches(self,text):
648 """Match attributes or global python names"""
624 """Match attributes or global python names"""
649
625
@@ -96,19 +96,24 b' class Tracer(object):'
96 def __init__(self,colors=None):
96 def __init__(self,colors=None):
97 """Create a local debugger instance.
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 colors : str, optional
102 use, it must be one of IPython's valid color schemes. If not given, the
103 The name of the color scheme to use, it must be one of IPython's
103 function will default to the current IPython scheme when running inside
104 valid color schemes. If not given, the function will default to
104 IPython, and to 'NoColor' otherwise.
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
114 Later in your code::
111 debug_here() # -> will open up the debugger at that point.
115
116 debug_here() # -> will open up the debugger at that point.
112
117
113 Once the debugger activates, you can use all of its regular commands to
118 Once the debugger activates, you can use all of its regular commands to
114 step through code, set breakpoints, etc. See the pdb documentation
119 step through code, set breakpoints, etc. See the pdb documentation
@@ -508,9 +508,9 b' class Image(DisplayObject):'
508 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
508 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
509
509
510 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
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 display function, it will result in the image being displayed
514 display function, it will result in the image being displayed
515 in the frontend.
515 in the frontend.
516
516
@@ -657,35 +657,19 b' class Image(DisplayObject):'
657 return unicode(s.split('.')[-1].lower())
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 """Clear the output of the current cell receiving output.
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 Parameters
663 Parameters
669 ----------
664 ----------
670 stdout : bool [default: True]
665 wait : bool [default: false]
671 Whether to clear stdout.
666 Wait to clear the output until new output is available to replace it."""
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 """
678 from IPython.core.interactiveshell import InteractiveShell
667 from IPython.core.interactiveshell import InteractiveShell
679 if InteractiveShell.initialized():
668 if InteractiveShell.initialized():
680 InteractiveShell.instance().display_pub.clear_output(
669 InteractiveShell.instance().display_pub.clear_output(wait)
681 stdout=stdout, stderr=stderr, other=other,
682 )
683 else:
670 else:
684 from IPython.utils import io
671 from IPython.utils import io
685 if stdout:
672 print('\033[2K\r', file=io.stdout, end='')
686 print('\033[2K\r', file=io.stdout, end='')
673 io.stdout.flush()
687 io.stdout.flush()
674 print('\033[2K\r', file=io.stderr, end='')
688 if stderr:
675 io.stderr.flush()
689 print('\033[2K\r', file=io.stderr, end='')
690 io.stderr.flush()
691
@@ -108,14 +108,12 b' class DisplayPublisher(Configurable):'
108 if 'text/plain' in data:
108 if 'text/plain' in data:
109 print(data['text/plain'], file=io.stdout)
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 """Clear the output of the cell receiving output."""
112 """Clear the output of the cell receiving output."""
113 if stdout:
113 print('\033[2K\r', file=io.stdout, end='')
114 print('\033[2K\r', file=io.stdout, end='')
114 io.stdout.flush()
115 io.stdout.flush()
115 print('\033[2K\r', file=io.stderr, end='')
116 if stderr:
116 io.stderr.flush()
117 print('\033[2K\r', file=io.stderr, end='')
118 io.stderr.flush()
119
117
120
118
121 class CapturingDisplayPublisher(DisplayPublisher):
119 class CapturingDisplayPublisher(DisplayPublisher):
@@ -125,8 +123,8 b' class CapturingDisplayPublisher(DisplayPublisher):'
125 def publish(self, source, data, metadata=None):
123 def publish(self, source, data, metadata=None):
126 self.outputs.append((source, data, metadata))
124 self.outputs.append((source, data, metadata))
127
125
128 def clear_output(self, stdout=True, stderr=True, other=True):
126 def clear_output(self, wait=False):
129 super(CapturingDisplayPublisher, self).clear_output(stdout, stderr, other)
127 super(CapturingDisplayPublisher, self).clear_output(wait)
130 if other:
128 if other:
131 # empty the list, *do not* reassign a new list
129 # empty the list, *do not* reassign a new list
132 del self.outputs[:]
130 del self.outputs[:]
@@ -278,6 +278,25 b' _help_end_re = re.compile(r"""(%{0,2}'
278 """,
278 """,
279 re.VERBOSE)
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 def has_comment(src):
300 def has_comment(src):
282 """Indicate whether an input line has (i.e. ends in, or is) a comment.
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 comment : bool
312 comment : bool
294 True if source has a comment.
313 True if source has a comment.
295 """
314 """
296 readline = StringIO(src).readline
315 return (tokenize2.COMMENT in _line_tokens(src))
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)
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 @StatelessInputTransformer.wrap
335 @StatelessInputTransformer.wrap
307 def help_end(line):
336 def help_end(line):
308 """Translate lines with ?/?? at the end"""
337 """Translate lines with ?/?? at the end"""
309 m = _help_end_re.search(line)
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 return line
340 return line
312 target = m.group(1)
341 target = m.group(1)
313 esc = m.group(3)
342 esc = m.group(3)
@@ -359,8 +388,26 b' def cellmagic(end_on_blank_line=False):'
359 line = tpl % (magic_name, first, u'\n'.join(body))
388 line = tpl % (magic_name, first, u'\n'.join(body))
360
389
361
390
362 def _strip_prompts(prompt_re):
391 def _strip_prompts(prompt_re, initial_re=None):
363 """Remove matching input prompts from a block of input."""
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 line = ''
411 line = ''
365 while True:
412 while True:
366 line = (yield line)
413 line = (yield line)
@@ -368,18 +415,19 b' def _strip_prompts(prompt_re):'
368 # First line of cell
415 # First line of cell
369 if line is None:
416 if line is None:
370 continue
417 continue
371 out, n1 = prompt_re.subn('', line, count=1)
418 out, n1 = initial_re.subn('', line, count=1)
372 line = (yield out)
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 if line is None:
421 if line is None:
377 continue
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 out, n2 = prompt_re.subn('', line, count=1)
426 out, n2 = prompt_re.subn('', line, count=1)
379 line = (yield out)
427 line = (yield out)
380
428
381 if n1 or n2:
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 # the rest of the cell as well.
431 # the rest of the cell as well.
384 while line is not None:
432 while line is not None:
385 line = (yield prompt_re.sub('', line, count=1))
433 line = (yield prompt_re.sub('', line, count=1))
@@ -394,7 +442,8 b' def classic_prompt():'
394 """Strip the >>>/... prompts of the Python interactive shell."""
442 """Strip the >>>/... prompts of the Python interactive shell."""
395 # FIXME: non-capturing version (?:...) usable?
443 # FIXME: non-capturing version (?:...) usable?
396 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
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 @CoroutineInputTransformer.wrap
448 @CoroutineInputTransformer.wrap
400 def ipy_prompt():
449 def ipy_prompt():
@@ -47,7 +47,6 b' from IPython.core.displayhook import DisplayHook'
47 from IPython.core.displaypub import DisplayPublisher
47 from IPython.core.displaypub import DisplayPublisher
48 from IPython.core.error import UsageError
48 from IPython.core.error import UsageError
49 from IPython.core.extensions import ExtensionManager
49 from IPython.core.extensions import ExtensionManager
50 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
51 from IPython.core.formatters import DisplayFormatter
50 from IPython.core.formatters import DisplayFormatter
52 from IPython.core.history import HistoryManager
51 from IPython.core.history import HistoryManager
53 from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGIC2
52 from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGIC2
@@ -186,6 +185,13 b' class ReadlineNoRecord(object):'
186 ghi = self.shell.readline.get_history_item
185 ghi = self.shell.readline.get_history_item
187 return [ghi(x) for x in range(start, end)]
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 # Main IPython class
196 # Main IPython class
191 #-----------------------------------------------------------------------------
197 #-----------------------------------------------------------------------------
@@ -464,7 +470,6 b' class InteractiveShell(SingletonConfigurable):'
464 # because it and init_io have to come after init_readline.
470 # because it and init_io have to come after init_readline.
465 self.init_user_ns()
471 self.init_user_ns()
466 self.init_logger()
472 self.init_logger()
467 self.init_alias()
468 self.init_builtins()
473 self.init_builtins()
469
474
470 # The following was in post_config_initialization
475 # The following was in post_config_initialization
@@ -496,6 +501,7 b' class InteractiveShell(SingletonConfigurable):'
496 self.init_displayhook()
501 self.init_displayhook()
497 self.init_latextool()
502 self.init_latextool()
498 self.init_magics()
503 self.init_magics()
504 self.init_alias()
499 self.init_logstart()
505 self.init_logstart()
500 self.init_pdb()
506 self.init_pdb()
501 self.init_extension_manager()
507 self.init_extension_manager()
@@ -819,15 +825,18 b' class InteractiveShell(SingletonConfigurable):'
819 # Things related to the "main" module
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 """Return a new 'main' module object for user code execution.
829 """Return a new 'main' module object for user code execution.
824
830
825 ``filename`` should be the path of the script which will be run in the
831 ``filename`` should be the path of the script which will be run in the
826 module. Requests with the same filename will get the same module, with
832 module. Requests with the same filename will get the same module, with
827 its namespace cleared.
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 When scripts are executed via %run, we must keep a reference to their
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 clear it, rendering references to module globals useless.
840 clear it, rendering references to module globals useless.
832
841
833 This method keeps said reference in a private dict, keyed by the
842 This method keeps said reference in a private dict, keyed by the
@@ -840,9 +849,16 b' class InteractiveShell(SingletonConfigurable):'
840 try:
849 try:
841 main_mod = self._main_mod_cache[filename]
850 main_mod = self._main_mod_cache[filename]
842 except KeyError:
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 else:
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 return main_mod
863 return main_mod
848
864
@@ -856,7 +872,7 b' class InteractiveShell(SingletonConfigurable):'
856
872
857 In [15]: import IPython
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 In [17]: len(_ip._main_mod_cache) > 0
877 In [17]: len(_ip._main_mod_cache) > 0
862 Out[17]: True
878 Out[17]: True
@@ -900,9 +916,9 b' class InteractiveShell(SingletonConfigurable):'
900 Keywords:
916 Keywords:
901
917
902 - force(False): by default, this routine checks the instance call_pdb
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.
919 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
920 The 'force' option forces the debugger to activate even if the flag
905 is false.
921 is false.
906 """
922 """
907
923
908 if not (force or self.call_pdb):
924 if not (force or self.call_pdb):
@@ -970,7 +986,7 b' class InteractiveShell(SingletonConfigurable):'
970
986
971 # A record of hidden variables we have added to the user namespace, so
987 # A record of hidden variables we have added to the user namespace, so
972 # we can list later only variables defined in actual interactive use.
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 # Now that FakeModule produces a real module, we've run into a nasty
991 # Now that FakeModule produces a real module, we've run into a nasty
976 # problem: after script execution (via %run), the module where the user
992 # problem: after script execution (via %run), the module where the user
@@ -1035,9 +1051,6 b' class InteractiveShell(SingletonConfigurable):'
1035 """
1051 """
1036 if user_module is None and user_ns is not None:
1052 if user_module is None and user_ns is not None:
1037 user_ns.setdefault("__name__", "__main__")
1053 user_ns.setdefault("__name__", "__main__")
1038 class DummyMod(object):
1039 "A dummy module used for IPython's interactive namespace."
1040 pass
1041 user_module = DummyMod()
1054 user_module = DummyMod()
1042 user_module.__dict__ = user_ns
1055 user_module.__dict__ = user_ns
1043
1056
@@ -1150,7 +1163,7 b' class InteractiveShell(SingletonConfigurable):'
1150
1163
1151 Note that this does not include the displayhook, which also caches
1164 Note that this does not include the displayhook, which also caches
1152 objects from the output."""
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 [m.__dict__ for m in self._main_mod_cache.values()]
1167 [m.__dict__ for m in self._main_mod_cache.values()]
1155
1168
1156 def reset(self, new_session=True):
1169 def reset(self, new_session=True):
@@ -1301,7 +1314,8 b' class InteractiveShell(SingletonConfigurable):'
1301 # And configure interactive visibility
1314 # And configure interactive visibility
1302 user_ns_hidden = self.user_ns_hidden
1315 user_ns_hidden = self.user_ns_hidden
1303 if interactive:
1316 if interactive:
1304 user_ns_hidden.difference_update(vdict)
1317 for name in vdict:
1318 user_ns_hidden.pop(name, None)
1305 else:
1319 else:
1306 user_ns_hidden.update(vdict)
1320 user_ns_hidden.update(vdict)
1307
1321
@@ -1321,7 +1335,7 b' class InteractiveShell(SingletonConfigurable):'
1321 for name, obj in variables.iteritems():
1335 for name, obj in variables.iteritems():
1322 if name in self.user_ns and self.user_ns[name] is obj:
1336 if name in self.user_ns and self.user_ns[name] is obj:
1323 del self.user_ns[name]
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 # Things related to object introspection
1341 # Things related to object introspection
@@ -1349,9 +1363,7 b' class InteractiveShell(SingletonConfigurable):'
1349 namespaces = [ ('Interactive', self.user_ns),
1363 namespaces = [ ('Interactive', self.user_ns),
1350 ('Interactive (global)', self.user_global_ns),
1364 ('Interactive (global)', self.user_global_ns),
1351 ('Python builtin', builtin_mod.__dict__),
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 # initialize results to 'null'
1368 # initialize results to 'null'
1357 found = False; obj = None; ospace = None; ds = None;
1369 found = False; obj = None; ospace = None; ds = None;
@@ -1390,8 +1402,6 b' class InteractiveShell(SingletonConfigurable):'
1390 # If we finish the for loop (no break), we got all members
1402 # If we finish the for loop (no break), we got all members
1391 found = True
1403 found = True
1392 ospace = nsname
1404 ospace = nsname
1393 if ns == alias_ns:
1394 isalias = True
1395 break # namespace loop
1405 break # namespace loop
1396
1406
1397 # Try to see if it's magic
1407 # Try to see if it's magic
@@ -1926,7 +1936,6 b' class InteractiveShell(SingletonConfigurable):'
1926 self.Completer = IPCompleter(shell=self,
1936 self.Completer = IPCompleter(shell=self,
1927 namespace=self.user_ns,
1937 namespace=self.user_ns,
1928 global_namespace=self.user_global_ns,
1938 global_namespace=self.user_global_ns,
1929 alias_table=self.alias_manager.alias_table,
1930 use_readline=self.has_readline,
1939 use_readline=self.has_readline,
1931 parent=self,
1940 parent=self,
1932 )
1941 )
@@ -2291,7 +2300,6 b' class InteractiveShell(SingletonConfigurable):'
2291 def init_alias(self):
2300 def init_alias(self):
2292 self.alias_manager = AliasManager(shell=self, parent=self)
2301 self.alias_manager = AliasManager(shell=self, parent=self)
2293 self.configurables.append(self.alias_manager)
2302 self.configurables.append(self.alias_manager)
2294 self.ns_table['alias'] = self.alias_manager.alias_table,
2295
2303
2296 #-------------------------------------------------------------------------
2304 #-------------------------------------------------------------------------
2297 # Things related to extensions
2305 # Things related to extensions
@@ -2973,7 +2981,7 b' class InteractiveShell(SingletonConfigurable):'
2973 Optional inputs:
2981 Optional inputs:
2974
2982
2975 - data(None): if data is given, it gets written out to the temp file
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 filename = tempfile.mktemp('.py', prefix)
2986 filename = tempfile.mktemp('.py', prefix)
2979 self.tempfiles.append(filename)
2987 self.tempfiles.append(filename)
@@ -3016,13 +3024,14 b' class InteractiveShell(SingletonConfigurable):'
3016
3024
3017 Optional Parameters:
3025 Optional Parameters:
3018 - raw(False): by default, the processed input is used. If this is
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 Note that slices can be called with two notations:
3029 Note that slices can be called with two notations:
3022
3030
3023 N:M -> standard python form, means including items N...(M-1).
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 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
3035 lines = self.history_manager.get_range_by_str(range_str, raw=raw)
3027 return "\n".join(x for _, _, x in lines)
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 Inputs:
171 Inputs:
172
172
173 - line_mod: possibly modified input, such as the transformations made
173 - line_mod: possibly modified input, such as the transformations made
174 by input prefilters or input handlers of various kinds. This should
174 by input prefilters or input handlers of various kinds. This should
175 always be valid Python.
175 always be valid Python.
176
176
177 - line_ori: unmodified input line from the user. This is not
177 - line_ori: unmodified input line from the user. This is not
178 necessarily valid Python.
178 necessarily valid Python.
179 """
179 """
180
180
181 # Write the log line, but decide which one according to the
181 # Write the log line, but decide which one according to the
@@ -489,11 +489,11 b' class Magics(object):'
489 MUST:
489 MUST:
490
490
491 - Use the method decorators `@line_magic` and `@cell_magic` to decorate
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 - Use the class decorator `@magics_class` to ensure that the magic
494 - Use the class decorator `@magics_class` to ensure that the magic
495 methods are properly registered at the instance level upon instance
495 methods are properly registered at the instance level upon instance
496 initialization.
496 initialization.
497
497
498 See :mod:`magic_functions` for examples of actual implementation classes.
498 See :mod:`magic_functions` for examples of actual implementation classes.
499 """
499 """
@@ -50,9 +50,9 b' Inheritance diagram:'
50 #
50 #
51 # The full license is in the file COPYING.txt, distributed with this software.
51 # The full license is in the file COPYING.txt, distributed with this software.
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53 import argparse
53
54
54 # Our own imports
55 # Our own imports
55 from IPython.external import argparse
56 from IPython.core.error import UsageError
56 from IPython.core.error import UsageError
57 from IPython.utils.process import arg_split
57 from IPython.utils.process import arg_split
58 from IPython.utils.text import dedent
58 from IPython.utils.text import dedent
@@ -56,6 +56,37 b' from IPython.utils.warn import warn, error'
56 # Magic implementation classes
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 @magics_class
90 @magics_class
60 class ExecutionMagics(Magics):
91 class ExecutionMagics(Magics):
61 """Magics related to code execution, debugging, profiling, etc.
92 """Magics related to code execution, debugging, profiling, etc.
@@ -102,75 +133,84 b' python-profiler package from non-free.""")'
102
133
103 Options:
134 Options:
104
135
105 -l <limit>: you can place restrictions on what or how much of the
136 -l <limit>
106 profile gets printed. The limit value can be:
137 you can place restrictions on what or how much of the
107
138 profile gets printed. The limit value can be:
108 * A string: only information for function names containing this string
139
109 is printed.
140 * A string: only information for function names containing this string
110
141 is printed.
111 * An integer: only these many lines are printed.
142
112
143 * An integer: only these many lines are printed.
113 * A float (between 0 and 1): this fraction of the report is printed
144
114 (for example, use a limit of 0.4 to see the topmost 40% only).
145 * A float (between 0 and 1): this fraction of the report is printed
115
146 (for example, use a limit of 0.4 to see the topmost 40% only).
116 You can combine several limits with repeated use of the option. For
147
117 example, '-l __init__ -l 5' will print only the topmost 5 lines of
148 You can combine several limits with repeated use of the option. For
118 information about class constructors.
149 example, ``-l __init__ -l 5`` will print only the topmost 5 lines of
119
150 information about class constructors.
120 -r: return the pstats.Stats object generated by the profiling. This
151
121 object has all the information about the profile in it, and you can
152 -r
122 later use it for further analysis or in other functions.
153 return the pstats.Stats object generated by the profiling. This
123
154 object has all the information about the profile in it, and you can
124 -s <key>: sort profile by given key. You can provide more than one key
155 later use it for further analysis or in other functions.
125 by using the option several times: '-s key1 -s key2 -s key3...'. The
156
126 default sorting key is 'time'.
157 -s <key>
127
158 sort profile by given key. You can provide more than one key
128 The following is copied verbatim from the profile documentation
159 by using the option several times: '-s key1 -s key2 -s key3...'. The
129 referenced below:
160 default sorting key is 'time'.
130
161
131 When more than one key is provided, additional keys are used as
162 The following is copied verbatim from the profile documentation
132 secondary criteria when the there is equality in all keys selected
163 referenced below:
133 before them.
164
134
165 When more than one key is provided, additional keys are used as
135 Abbreviations can be used for any key names, as long as the
166 secondary criteria when the there is equality in all keys selected
136 abbreviation is unambiguous. The following are the keys currently
167 before them.
137 defined:
168
138
169 Abbreviations can be used for any key names, as long as the
139 Valid Arg Meaning
170 abbreviation is unambiguous. The following are the keys currently
140 "calls" call count
171 defined:
141 "cumulative" cumulative time
172
142 "file" file name
173 ============ =====================
143 "module" file name
174 Valid Arg Meaning
144 "pcalls" primitive call count
175 ============ =====================
145 "line" line number
176 "calls" call count
146 "name" function name
177 "cumulative" cumulative time
147 "nfl" name/file/line
178 "file" file name
148 "stdname" standard name
179 "module" file name
149 "time" internal time
180 "pcalls" primitive call count
150
181 "line" line number
151 Note that all sorts on statistics are in descending order (placing
182 "name" function name
152 most time consuming items first), where as name, file, and line number
183 "nfl" name/file/line
153 searches are in ascending order (i.e., alphabetical). The subtle
184 "stdname" standard name
154 distinction between "nfl" and "stdname" is that the standard name is a
185 "time" internal time
155 sort of the name as printed, which means that the embedded line
186 ============ =====================
156 numbers get compared in an odd way. For example, lines 3, 20, and 40
187
157 would (if the file names were the same) appear in the string order
188 Note that all sorts on statistics are in descending order (placing
158 "20" "3" and "40". In contrast, "nfl" does a numeric compare of the
189 most time consuming items first), where as name, file, and line number
159 line numbers. In fact, sort_stats("nfl") is the same as
190 searches are in ascending order (i.e., alphabetical). The subtle
160 sort_stats("name", "file", "line").
191 distinction between "nfl" and "stdname" is that the standard name is a
161
192 sort of the name as printed, which means that the embedded line
162 -T <filename>: save profile results as shown on screen to a text
193 numbers get compared in an odd way. For example, lines 3, 20, and 40
163 file. The profile is still shown on screen.
194 would (if the file names were the same) appear in the string order
164
195 "20" "3" and "40". In contrast, "nfl" does a numeric compare of the
165 -D <filename>: save (via dump_stats) profile statistics to given
196 line numbers. In fact, sort_stats("nfl") is the same as
166 filename. This data is in a format understood by the pstats module, and
197 sort_stats("name", "file", "line").
167 is generated by a call to the dump_stats() method of profile
198
168 objects. The profile is still shown on screen.
199 -T <filename>
169
200 save profile results as shown on screen to a text
170 -q: suppress output to the pager. Best used with -T and/or -D above.
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 If you want to run complete programs under the profiler's control, use
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 contains profiler specific options as described here.
214 contains profiler specific options as described here.
175
215
176 You can read the complete documentation for the profile module with::
216 You can read the complete documentation for the profile module with::
@@ -362,7 +402,8 b' python-profiler package from non-free.""")'
362 file_finder=get_py_filename):
402 file_finder=get_py_filename):
363 """Run the named file inside IPython as a program.
403 """Run the named file inside IPython as a program.
364
404
365 Usage:
405 Usage::
406
366 %run [-n -i -e -G]
407 %run [-n -i -e -G]
367 [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )]
408 [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )]
368 ( -m mod | file ) [args]
409 ( -m mod | file ) [args]
@@ -371,14 +412,13 b' python-profiler package from non-free.""")'
371 the program (put in sys.argv). Then, control returns to IPython's
412 the program (put in sys.argv). Then, control returns to IPython's
372 prompt.
413 prompt.
373
414
374 This is similar to running at a system prompt:\\
415 This is similar to running at a system prompt ``python file args``,
375 $ python file args\\
376 but with the advantage of giving you IPython's tracebacks, and of
416 but with the advantage of giving you IPython's tracebacks, and of
377 loading all variables into your interactive namespace for further use
417 loading all variables into your interactive namespace for further use
378 (unless -p is used, see below).
418 (unless -p is used, see below).
379
419
380 The file is executed in a namespace initially consisting only of
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 sees its environment as if it were being run as a stand-alone program
422 sees its environment as if it were being run as a stand-alone program
383 (except for sharing global objects such as previously imported
423 (except for sharing global objects such as previously imported
384 modules). But after execution, the IPython interactive namespace gets
424 modules). But after execution, the IPython interactive namespace gets
@@ -390,33 +430,37 b' python-profiler package from non-free.""")'
390 '*', '?', '[seq]' and '[!seq]' can be used. Additionally,
430 '*', '?', '[seq]' and '[!seq]' can be used. Additionally,
391 tilde '~' will be expanded into user's home directory. Unlike
431 tilde '~' will be expanded into user's home directory. Unlike
392 real shells, quotation does not suppress expansions. Use
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 To completely disable these expansions, you can use -G flag.
434 To completely disable these expansions, you can use -G flag.
395
435
396 Options:
436 Options:
397
437
398 -n: __name__ is NOT set to '__main__', but to the running file's name
438 -n
399 without extension (as python does under import). This allows running
439 __name__ is NOT set to '__main__', but to the running file's name
400 scripts and reloading the definitions in them without calling code
440 without extension (as python does under import). This allows running
401 protected by an ' if __name__ == "__main__" ' clause.
441 scripts and reloading the definitions in them without calling code
402
442 protected by an ``if __name__ == "__main__"`` clause.
403 -i: run the file in IPython's namespace instead of an empty one. This
443
404 is useful if you are experimenting with code written in a text editor
444 -i
405 which depends on variables defined interactively.
445 run the file in IPython's namespace instead of an empty one. This
406
446 is useful if you are experimenting with code written in a text editor
407 -e: ignore sys.exit() calls or SystemExit exceptions in the script
447 which depends on variables defined interactively.
408 being run. This is particularly useful if IPython is being used to
448
409 run unittests, which always exit with a sys.exit() call. In such
449 -e
410 cases you are interested in the output of the test results, not in
450 ignore sys.exit() calls or SystemExit exceptions in the script
411 seeing a traceback of the unittest module.
451 being run. This is particularly useful if IPython is being used to
412
452 run unittests, which always exit with a sys.exit() call. In such
413 -t: print timing information at the end of the run. IPython will give
453 cases you are interested in the output of the test results, not in
414 you an estimated CPU time consumption for your script, which under
454 seeing a traceback of the unittest module.
415 Unix uses the resource module to avoid the wraparound problems of
455
416 time.clock(). Under Unix, an estimate of time spent on system tasks
456 -t
417 is also given (for Windows platforms this is reported as 0.0).
457 print timing information at the end of the run. IPython will give
418
458 you an estimated CPU time consumption for your script, which under
419 If -t is given, an additional -N<N> option can be given, where <N>
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 must be an integer indicating how many times you want the script to
464 must be an integer indicating how many times you want the script to
421 run. The final timing report will include total and per run results.
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 In [1]: run -t uniq_stable
469 In [1]: run -t uniq_stable
426
470
427 IPython CPU timings (estimated):\\
471 IPython CPU timings (estimated):
428 User : 0.19597 s.\\
472 User : 0.19597 s.
429 System: 0.0 s.\\
473 System: 0.0 s.
430
474
431 In [2]: run -t -N5 uniq_stable
475 In [2]: run -t -N5 uniq_stable
432
476
433 IPython CPU timings (estimated):\\
477 IPython CPU timings (estimated):
434 Total runs performed: 5\\
478 Total runs performed: 5
435 Times : Total Per run\\
479 Times : Total Per run
436 User : 0.910862 s, 0.1821724 s.\\
480 User : 0.910862 s, 0.1821724 s.
437 System: 0.0 s, 0.0 s.
481 System: 0.0 s, 0.0 s.
438
482
439 -d: run your program under the control of pdb, the Python debugger.
483 -d
440 This allows you to execute your program step by step, watch variables,
484 run your program under the control of pdb, the Python debugger.
441 etc. Internally, what IPython does is similar to calling:
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
490 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
491 number for this automatic breakpoint to be <N> by using the -bN option
447 (where N must be an integer). For example::
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
496 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
497 the first breakpoint must be set on a line which actually does
453 something (not a comment or docstring) for it to stop execution.
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
504 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
505 first enter 'c' (without quotes) to start execution up to the first
461 breakpoint.
506 breakpoint.
462
507
463 Entering 'help' gives information about the use of the debugger. You
508 Entering 'help' gives information about the use of the debugger. You
464 can easily see pdb's full documentation with "import pdb;pdb.help()"
509 can easily see pdb's full documentation with "import pdb;pdb.help()"
465 at a prompt.
510 at a prompt.
466
511
467 -p: run program under the control of the Python profiler module (which
512 -p
468 prints a detailed report of execution times, function calls, etc).
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
516 You can pass other options after -p which affect the behavior of the
471 profiler itself. See the docs for %prun for details.
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
519 In this mode, the program's variables do NOT propagate back to the
474 IPython interactive namespace (because they remain in the namespace
520 IPython interactive namespace (because they remain in the namespace
475 where the profiler executes them).
521 where the profiler executes them).
476
522
477 Internally this triggers a call to %prun, see its documentation for
523 Internally this triggers a call to %prun, see its documentation for
478 details on the options available specifically for profiling.
524 details on the options available specifically for profiling.
479
525
480 There is one special usage for which the text above doesn't apply:
526 There is one special usage for which the text above doesn't apply:
481 if the filename ends with .ipy, the file is run as ipython script,
527 if the filename ends with .ipy, the file is run as ipython script,
482 just as if the commands were written on IPython prompt.
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
530 -m
485 the -m option for the python interpreter. Use this option last if you
531 specify module name to load instead of script path. Similar to
486 want to combine with other %run options. Unlike the python interpreter
532 the -m option for the python interpreter. Use this option last if you
487 only source modules are allowed no .pyc or .pyo files.
533 want to combine with other %run options. Unlike the python interpreter
488 For example::
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 __name__save = self.shell.user_ns['__name__']
598 __name__save = self.shell.user_ns['__name__']
551 prog_ns['__name__'] = '__main__'
599 prog_ns['__name__'] = '__main__'
552 main_mod = self.shell.user_module
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 else:
606 else:
554 # Run in a fresh, empty namespace
607 # Run in a fresh, empty namespace
555 if 'n' in opts:
608 if 'n' in opts:
@@ -560,13 +613,8 b' python-profiler package from non-free.""")'
560 # The shell MUST hold a reference to prog_ns so after %run
613 # The shell MUST hold a reference to prog_ns so after %run
561 # exits, the python deletion mechanism doesn't zero it out
614 # exits, the python deletion mechanism doesn't zero it out
562 # (leaving dangling references). See interactiveshell for details
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 prog_ns = main_mod.__dict__
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 # pickle fix. See interactiveshell for an explanation. But we need to
619 # pickle fix. See interactiveshell for an explanation. But we need to
572 # make sure that, if we overwrite __main__, we replace it at the end
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 """Time execution of a Python statement or expression
834 """Time execution of a Python statement or expression
787
835
788 Usage, in line mode:
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 or in cell mode:
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 code
840 code
793 code...
841 code...
794
842
@@ -819,6 +867,11 b' python-profiler package from non-free.""")'
819 -p<P>: use a precision of <P> digits to display the timing result.
867 -p<P>: use a precision of <P> digits to display the timing result.
820 Default: 3
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 Examples
876 Examples
824 --------
877 --------
@@ -851,7 +904,7 b' python-profiler package from non-free.""")'
851
904
852 import timeit
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 posix=False, strict=False)
908 posix=False, strict=False)
856 if stmt == "" and cell is None:
909 if stmt == "" and cell is None:
857 return
910 return
@@ -860,6 +913,8 b' python-profiler package from non-free.""")'
860 number = int(getattr(opts, "n", 0))
913 number = int(getattr(opts, "n", 0))
861 repeat = int(getattr(opts, "r", timeit.default_repeat))
914 repeat = int(getattr(opts, "r", timeit.default_repeat))
862 precision = int(getattr(opts, "p", 3))
915 precision = int(getattr(opts, "p", 3))
916 quiet = 'q' in opts
917 return_result = 'o' in opts
863 if hasattr(opts, "t"):
918 if hasattr(opts, "t"):
864 timefunc = time.time
919 timefunc = time.time
865 if hasattr(opts, "c"):
920 if hasattr(opts, "c"):
@@ -931,13 +986,15 b' python-profiler package from non-free.""")'
931 if timer.timeit(number) >= 0.2:
986 if timer.timeit(number) >= 0.2:
932 break
987 break
933 number *= 10
988 number *= 10
934
989 all_runs = timer.repeat(repeat, number)
935 best = min(timer.repeat(repeat, number)) / number
990 best = min(all_runs) / number
936
991 if not quiet :
937 print u"%d loops, best of %d: %s per loop" % (number, repeat,
992 print u"%d loops, best of %d: %s per loop" % (number, repeat,
938 _format_time(best, precision))
993 _format_time(best, precision))
939 if tc > tc_min:
994 if tc > tc_min:
940 print "Compiler time: %.2f s" % tc
995 print "Compiler time: %.2f s" % tc
996 if return_result:
997 return TimeitResult(number, repeat, best, all_runs, tc, precision)
941
998
942 @skip_doctest
999 @skip_doctest
943 @needs_local_scope
1000 @needs_local_scope
@@ -42,34 +42,48 b' class LoggingMagics(Magics):'
42 history up to that point and then continues logging.
42 history up to that point and then continues logging.
43
43
44 %logstart takes a second optional parameter: logging mode. This can be one
44 %logstart takes a second optional parameter: logging mode. This can be one
45 of (note that the modes are given unquoted):\\
45 of (note that the modes are given unquoted):
46 append: well, that says it.\\
46
47 backup: rename (if exists) to name~ and start name.\\
47 append
48 global: single logfile in your home dir, appended to.\\
48 Keep logging at the end of any existing file.
49 over : overwrite existing log.\\
49
50 rotate: create rotating logs name.1~, name.2~, etc.
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 Options:
62 Options:
53
63
54 -o: log also IPython's output. In this mode, all commands which
64 -o
55 generate an Out[NN] prompt are recorded to the logfile, right after
65 log also IPython's output. In this mode, all commands which
56 their corresponding input line. The output lines are always
66 generate an Out[NN] prompt are recorded to the logfile, right after
57 prepended with a '#[Out]# ' marker, so that the log remains valid
67 their corresponding input line. The output lines are always
58 Python code.
68 prepended with a '#[Out]# ' marker, so that the log remains valid
69 Python code.
59
70
60 Since this marker is always the same, filtering only the output from
71 Since this marker is always the same, filtering only the output from
61 a log is very easy, using for example a simple awk call::
72 a log is very easy, using for example a simple awk call::
62
73
63 awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py
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
76 -r
66 input, so that user lines are logged in their final form, converted
77 log 'raw' input. Normally, IPython's logs contain the processed
67 into valid Python. For example, %Exit is logged as
78 input, so that user lines are logged in their final form, converted
68 _ip.magic("Exit"). If the -r flag is given, all input is logged
79 into valid Python. For example, %Exit is logged as
69 exactly as typed, with no transformations applied.
80 _ip.magic("Exit"). If the -r flag is given, all input is logged
70
81 exactly as typed, with no transformations applied.
71 -t: put timestamps before each input line logged (these are put in
82
72 comments)."""
83 -t
84 put timestamps before each input line logged (these are put in
85 comments).
86 """
73
87
74 opts,par = self.parse_options(parameter_s,'ort')
88 opts,par = self.parse_options(parameter_s,'ort')
75 log_output = 'o' in opts
89 log_output = 'o' in opts
@@ -265,9 +265,10 b' class NamespaceMagics(Magics):'
265
265
266 user_ns = self.shell.user_ns
266 user_ns = self.shell.user_ns
267 user_ns_hidden = self.shell.user_ns_hidden
267 user_ns_hidden = self.shell.user_ns_hidden
268 nonmatching = object() # This can never be in user_ns
268 out = [ i for i in user_ns
269 out = [ i for i in user_ns
269 if not i.startswith('_') \
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 typelist = parameter_s.split()
273 typelist = parameter_s.split()
273 if typelist:
274 if typelist:
@@ -353,10 +354,10 b' class NamespaceMagics(Magics):'
353 - For {},[],(): their length.
354 - For {},[],(): their length.
354
355
355 - For numpy arrays, a summary with shape, number of
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 - Everything else: a string representation, snipping their middle if
359 - Everything else: a string representation, snipping their middle if
359 too long.
360 too long.
360
361
361 Examples
362 Examples
362 --------
363 --------
@@ -26,6 +26,7 b' from pprint import pformat'
26 from IPython.core import magic_arguments
26 from IPython.core import magic_arguments
27 from IPython.core import oinspect
27 from IPython.core import oinspect
28 from IPython.core import page
28 from IPython.core import page
29 from IPython.core.alias import AliasError, Alias
29 from IPython.core.error import UsageError
30 from IPython.core.error import UsageError
30 from IPython.core.magic import (
31 from IPython.core.magic import (
31 Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic
32 Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic
@@ -113,10 +114,14 b' class OSMagics(Magics):'
113 # Now try to define a new one
114 # Now try to define a new one
114 try:
115 try:
115 alias,cmd = par.split(None, 1)
116 alias,cmd = par.split(None, 1)
116 except:
117 except TypeError:
117 print oinspect.getdoc(self.alias)
118 print(oinspect.getdoc(self.alias))
118 else:
119 return
119 self.shell.alias_manager.soft_define_alias(alias, cmd)
120
121 try:
122 self.shell.alias_manager.define_alias(alias, cmd)
123 except AliasError as e:
124 print(e)
120 # end magic_alias
125 # end magic_alias
121
126
122 @line_magic
127 @line_magic
@@ -124,7 +129,12 b' class OSMagics(Magics):'
124 """Remove an alias"""
129 """Remove an alias"""
125
130
126 aname = parameter_s.strip()
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 stored = self.shell.db.get('stored_aliases', {} )
138 stored = self.shell.db.get('stored_aliases', {} )
129 if aname in stored:
139 if aname in stored:
130 print "Removing %stored alias",aname
140 print "Removing %stored alias",aname
@@ -182,7 +192,7 b' class OSMagics(Magics):'
182 try:
192 try:
183 # Removes dots from the name since ipython
193 # Removes dots from the name since ipython
184 # will assume names with dots to be python.
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 self.shell.alias_manager.define_alias(
196 self.shell.alias_manager.define_alias(
187 ff.replace('.',''), ff)
197 ff.replace('.',''), ff)
188 except InvalidAliasError:
198 except InvalidAliasError:
@@ -190,7 +200,7 b' class OSMagics(Magics):'
190 else:
200 else:
191 syscmdlist.append(ff)
201 syscmdlist.append(ff)
192 else:
202 else:
193 no_alias = self.shell.alias_manager.no_alias
203 no_alias = Alias.blacklist
194 for pdir in path:
204 for pdir in path:
195 os.chdir(pdir)
205 os.chdir(pdir)
196 for ff in os.listdir(pdir):
206 for ff in os.listdir(pdir):
@@ -156,8 +156,8 b' def getsource(obj,is_binary=False):'
156 Optional inputs:
156 Optional inputs:
157
157
158 - is_binary: whether the object is known to come from a binary source.
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
159 This implementation will skip returning any output for binary objects, but
160 custom extractors may know how to meaningfully process them."""
160 custom extractors may know how to meaningfully process them."""
161
161
162 if is_binary:
162 if is_binary:
163 return None
163 return None
@@ -545,7 +545,7 b' class Inspector:'
545 - formatter: special formatter for docstrings (see pdoc)
545 - formatter: special formatter for docstrings (see pdoc)
546
546
547 - info: a structure with some information fields which may have been
547 - info: a structure with some information fields which may have been
548 precomputed already.
548 precomputed already.
549
549
550 - detail_level: if set to 1, more information is given.
550 - detail_level: if set to 1, more information is given.
551 """
551 """
@@ -609,7 +609,7 b' class Inspector:'
609 - formatter: special formatter for docstrings (see pdoc)
609 - formatter: special formatter for docstrings (see pdoc)
610
610
611 - info: a structure with some information fields which may have been
611 - info: a structure with some information fields which may have been
612 precomputed already.
612 precomputed already.
613
613
614 - detail_level: if set to 1, more information is given.
614 - detail_level: if set to 1, more information is given.
615 """
615 """
@@ -829,8 +829,8 b' class Inspector:'
829 Arguments:
829 Arguments:
830
830
831 - pattern: string containing shell-like wildcards to use in namespace
831 - pattern: string containing shell-like wildcards to use in namespace
832 searches and optionally a type specification to narrow the search to
832 searches and optionally a type specification to narrow the search to
833 objects of that type.
833 objects of that type.
834
834
835 - ns_table: dict of name->namespaces for search.
835 - ns_table: dict of name->namespaces for search.
836
836
@@ -841,7 +841,7 b' class Inspector:'
841 - ignore_case(False): make the search case-insensitive.
841 - ignore_case(False): make the search case-insensitive.
842
842
843 - show_all(False): show all names, including those starting with
843 - show_all(False): show all names, including those starting with
844 underscores.
844 underscores.
845 """
845 """
846 #print 'ps pattern:<%r>' % pattern # dbg
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 """Print a string snipping the midsection to fit in width.
325 """Print a string snipping the midsection to fit in width.
326
326
327 print_full: mode control:
327 print_full: mode control:
328
328 - 0: only snip long strings
329 - 0: only snip long strings
329 - 1: send to page() directly.
330 - 1: send to page() directly.
330 - 2: snip long strings and ask for full length viewing with page()
331 - 2: snip long strings and ask for full length viewing with page()
332
331 Return 1 if snipping was necessary, 0 otherwise."""
333 Return 1 if snipping was necessary, 0 otherwise."""
332
334
333 if print_full == 1:
335 if print_full == 1:
@@ -488,22 +488,6 b' class AutoMagicChecker(PrefilterChecker):'
488 return self.prefilter_manager.get_handler_by_name('magic')
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 class PythonOpsChecker(PrefilterChecker):
491 class PythonOpsChecker(PrefilterChecker):
508
492
509 priority = Integer(900, config=True)
493 priority = Integer(900, config=True)
@@ -591,20 +575,6 b' class PrefilterHandler(Configurable):'
591 return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name)
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 class MacroHandler(PrefilterHandler):
578 class MacroHandler(PrefilterHandler):
609 handler_name = Unicode("macro")
579 handler_name = Unicode("macro")
610
580
@@ -730,14 +700,12 b' _default_checkers = ['
730 IPyAutocallChecker,
700 IPyAutocallChecker,
731 AssignmentChecker,
701 AssignmentChecker,
732 AutoMagicChecker,
702 AutoMagicChecker,
733 AliasChecker,
734 PythonOpsChecker,
703 PythonOpsChecker,
735 AutocallChecker
704 AutocallChecker
736 ]
705 ]
737
706
738 _default_handlers = [
707 _default_handlers = [
739 PrefilterHandler,
708 PrefilterHandler,
740 AliasHandler,
741 MacroHandler,
709 MacroHandler,
742 MagicHandler,
710 MagicHandler,
743 AutoHandler,
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 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
@@ -28,6 +28,7 b' from IPython.core.application import ('
28 BaseIPythonApplication, base_flags
28 BaseIPythonApplication, base_flags
29 )
29 )
30 from IPython.core.profiledir import ProfileDir
30 from IPython.core.profiledir import ProfileDir
31 from IPython.utils.importstring import import_item
31 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
32 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
32 from IPython.utils.traitlets import Unicode, Bool, Dict
33 from IPython.utils.traitlets import Unicode, Bool, Dict
33
34
@@ -206,6 +207,8 b' class ProfileCreate(BaseIPythonApplication):'
206 description = create_help
207 description = create_help
207 examples = _create_examples
208 examples = _create_examples
208 auto_create = Bool(True, config=False)
209 auto_create = Bool(True, config=False)
210 def _log_format_default(self):
211 return "[%(name)s] %(message)s"
209
212
210 def _copy_config_files_default(self):
213 def _copy_config_files_default(self):
211 return True
214 return True
@@ -234,31 +237,32 b' class ProfileCreate(BaseIPythonApplication):'
234 flags = Dict(create_flags)
237 flags = Dict(create_flags)
235
238
236 classes = [ProfileDir]
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 def init_config_files(self):
253 def init_config_files(self):
239 super(ProfileCreate, self).init_config_files()
254 super(ProfileCreate, self).init_config_files()
240 # use local imports, since these classes may import from here
255 # use local imports, since these classes may import from here
241 from IPython.terminal.ipapp import TerminalIPythonApp
256 from IPython.terminal.ipapp import TerminalIPythonApp
242 apps = [TerminalIPythonApp]
257 apps = [TerminalIPythonApp]
243 try:
258 for app_path in (
244 from IPython.qt.console.qtconsoleapp import IPythonQtConsoleApp
259 'IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp',
245 except Exception:
260 'IPython.html.notebookapp.NotebookApp',
246 # this should be ImportError, but under weird circumstances
261 'IPython.nbconvert.nbconvertapp.NbConvertApp',
247 # this might be an AttributeError, or possibly others
262 ):
248 # in any case, nothing should cause the profile creation to crash.
263 app = self._import_app(app_path)
249 pass
264 if app is not None:
250 else:
265 apps.append(app)
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)
262 if self.parallel:
266 if self.parallel:
263 from IPython.parallel.apps.ipcontrollerapp import IPControllerApp
267 from IPython.parallel.apps.ipcontrollerapp import IPControllerApp
264 from IPython.parallel.apps.ipengineapp import IPEngineApp
268 from IPython.parallel.apps.ipengineapp import IPEngineApp
@@ -214,7 +214,11 b' def find_gui_and_backend(gui=None, gui_select=None):'
214 # select backend based on requested gui
214 # select backend based on requested gui
215 backend = backends[gui]
215 backend = backends[gui]
216 else:
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 # In this case, we need to find what the appropriate gui selection call
222 # In this case, we need to find what the appropriate gui selection call
219 # should be for IPython, so we can activate inputhook accordingly
223 # should be for IPython, so we can activate inputhook accordingly
220 gui = backend2gui.get(backend, None)
224 gui = backend2gui.get(backend, None)
@@ -26,7 +26,7 b" _version_extra = 'dev'"
26 # _version_extra = 'rc1'
26 # _version_extra = 'rc1'
27 # _version_extra = '' # Uncomment this for full releases
27 # _version_extra = '' # Uncomment this for full releases
28
28
29 codename = 'An Afternoon Hack'
29 codename = 'Work in Progress'
30
30
31 # Construct full version string from these.
31 # Construct full version string from these.
32 _ver = [_version_major, _version_minor, _version_patch]
32 _ver = [_version_major, _version_minor, _version_patch]
@@ -141,11 +141,8 b' classifiers = ['
141 'License :: OSI Approved :: BSD License',
141 'License :: OSI Approved :: BSD License',
142 'Programming Language :: Python',
142 'Programming Language :: Python',
143 'Programming Language :: Python :: 2',
143 'Programming Language :: Python :: 2',
144 'Programming Language :: Python :: 2.6',
145 'Programming Language :: Python :: 2.7',
144 'Programming Language :: Python :: 2.7',
146 'Programming Language :: Python :: 3',
145 'Programming Language :: Python :: 3',
147 'Programming Language :: Python :: 3.2',
148 'Programming Language :: Python :: 3.3',
149 'Topic :: System :: Distributed Computing',
146 'Topic :: System :: Distributed Computing',
150 'Topic :: System :: Shells'
147 'Topic :: System :: Shells'
151 ]
148 ]
@@ -36,11 +36,14 b' from IPython.utils.path import filefind'
36 from IPython.utils.traitlets import (
36 from IPython.utils.traitlets import (
37 Unicode, Instance, List, Bool, CaselessStrEnum, Dict
37 Unicode, Instance, List, Bool, CaselessStrEnum, Dict
38 )
38 )
39 from IPython.lib.inputhook import guis
39
40
40 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
41 # Aliases and Flags
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 backend_keys = sorted(pylabtools.backends.keys())
47 backend_keys = sorted(pylabtools.backends.keys())
45 backend_keys.insert(0, 'auto')
48 backend_keys.insert(0, 'auto')
46
49
@@ -175,8 +178,8 b' class InteractiveShellApp(Configurable):'
175 module_to_run = Unicode('', config=True,
178 module_to_run = Unicode('', config=True,
176 help="Run the module as a script."
179 help="Run the module as a script."
177 )
180 )
178 gui = CaselessStrEnum(('qt', 'wx', 'gtk', 'glut', 'pyglet', 'osx'), config=True,
181 gui = CaselessStrEnum(gui_keys, config=True,
179 help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'glut', 'pyglet', 'osx')."
182 help="Enable GUI event loop integration with any of {0}.".format(gui_keys)
180 )
183 )
181 matplotlib = CaselessStrEnum(backend_keys,
184 matplotlib = CaselessStrEnum(backend_keys,
182 config=True,
185 config=True,
@@ -198,7 +201,7 b' class InteractiveShellApp(Configurable):'
198 )
201 )
199 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
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 def _user_ns_changed(self, name, old, new):
205 def _user_ns_changed(self, name, old, new):
203 if self.shell is not None:
206 if self.shell is not None:
204 self.shell.user_ns = new
207 self.shell.user_ns = new
@@ -117,8 +117,7 b' def test_ipdb_magics():'
117 ipdb> pinfo a
117 ipdb> pinfo a
118 Type: ExampleClass
118 Type: ExampleClass
119 String Form:ExampleClass()
119 String Form:ExampleClass()
120 Namespace: Locals
120 Namespace: Local...
121 File: ...
122 Docstring: Docstring for ExampleClass.
121 Docstring: Docstring for ExampleClass.
123 Constructor Docstring:Docstring for ExampleClass.__init__
122 Constructor Docstring:Docstring for ExampleClass.__init__
124 ipdb> continue
123 ipdb> continue
@@ -45,28 +45,6 b' def run(tests):'
45
45
46
46
47 def test_handlers():
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 call_idx = CallableIndexable()
48 call_idx = CallableIndexable()
71 ip.user_ns['call_idx'] = call_idx
49 ip.user_ns['call_idx'] = call_idx
72
50
@@ -9,9 +9,6 b' def test_import_crashhandler():'
9 def test_import_debugger():
9 def test_import_debugger():
10 from IPython.core import debugger
10 from IPython.core import debugger
11
11
12 def test_import_fakemodule():
13 from IPython.core import fakemodule
14
15 def test_import_excolors():
12 def test_import_excolors():
16 from IPython.core import excolors
13 from IPython.core import excolors
17
14
@@ -183,9 +183,18 b' syntax_ml = \\'
183 ('... 456"""','456"""'),
183 ('... 456"""','456"""'),
184 ],
184 ],
185 [('a="""','a="""'),
185 [('a="""','a="""'),
186 ('>>> 123','123'),
187 ('... 456"""','456"""'),
188 ],
189 [('a="""','a="""'),
186 ('123','123'),
190 ('123','123'),
187 ('... 456"""','... 456"""'),
191 ('... 456"""','... 456"""'),
188 ],
192 ],
193 [('....__class__','....__class__'),
194 ],
195 [('a=5', 'a=5'),
196 ('...', ''),
197 ],
189 [('>>> def f(x):', 'def f(x):'),
198 [('>>> def f(x):', 'def f(x):'),
190 ('...', ''),
199 ('...', ''),
191 ('... return x', ' return x'),
200 ('... return x', ' return x'),
@@ -205,6 +214,10 b' syntax_ml = \\'
205 (' ...: 456"""','456"""'),
214 (' ...: 456"""','456"""'),
206 ],
215 ],
207 [('a="""','a="""'),
216 [('a="""','a="""'),
217 ('In [1]: 123','123'),
218 (' ...: 456"""','456"""'),
219 ],
220 [('a="""','a="""'),
208 ('123','123'),
221 ('123','123'),
209 (' ...: 456"""',' ...: 456"""'),
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 leading_indent =
259 leading_indent =
241 [ [(' print "hi"','print "hi"'),
260 [ [(' print "hi"','print "hi"'),
242 ],
261 ],
@@ -106,17 +106,6 b' class InteractiveShellTestCase(unittest.TestCase):'
106 ip.run_cell('a = """\n%exit\n"""')
106 ip.run_cell('a = """\n%exit\n"""')
107 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
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 def test_trailing_newline(self):
109 def test_trailing_newline(self):
121 """test that running !(command) does not raise a SyntaxError"""
110 """test that running !(command) does not raise a SyntaxError"""
122 ip.run_cell('!(true)\n', False)
111 ip.run_cell('!(true)\n', False)
@@ -48,16 +48,16 b' class DummyMagics(magic.Magics): pass'
48 def test_rehashx():
48 def test_rehashx():
49 # clear up everything
49 # clear up everything
50 _ip = get_ipython()
50 _ip = get_ipython()
51 _ip.alias_manager.alias_table.clear()
51 _ip.alias_manager.clear_aliases()
52 del _ip.db['syscmdlist']
52 del _ip.db['syscmdlist']
53
53
54 _ip.magic('rehashx')
54 _ip.magic('rehashx')
55 # Practically ALL ipython development systems will have more than 10 aliases
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)
57 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
58 for key, val in _ip.alias_manager.alias_table.iteritems():
58 for name, cmd in _ip.alias_manager.aliases:
59 # we must strip dots from alias names
59 # we must strip dots from alias names
60 nt.assert_not_in('.', key)
60 nt.assert_not_in('.', name)
61
61
62 # rehashx must fill up syscmdlist
62 # rehashx must fill up syscmdlist
63 scoms = _ip.db['syscmdlist']
63 scoms = _ip.db['syscmdlist']
@@ -487,7 +487,21 b' def test_timeit_special_syntax():'
487 # cell mode test
487 # cell mode test
488 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
488 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
489 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
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 @dec.skipif(execution.profile is None)
506 @dec.skipif(execution.profile is None)
493 def test_prun_special_syntax():
507 def test_prun_special_syntax():
@@ -6,12 +6,11 b''
6 # The full license is in the file COPYING.txt, distributed with this software.
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 from IPython.core.magic_arguments import (argument, argument_group, kwds,
12 from IPython.core.magic_arguments import (argument, argument_group, kwds,
13 magic_arguments, parse_argstring, real_name)
13 magic_arguments, parse_argstring, real_name)
14 from IPython.testing.decorators import parametric
15
14
16
15
17 @magic_arguments()
16 @magic_arguments()
@@ -74,48 +73,46 b' def foo(self, args):'
74 return parse_argstring(foo, args)
73 return parse_argstring(foo, args)
75
74
76
75
77 @parametric
78 def test_magic_arguments():
76 def test_magic_arguments():
79 # Ideally, these would be doctests, but I could not get it to work.
77 assert_equal(magic_foo1.__doc__, '%foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
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')
78 assert_equal(getattr(magic_foo1, 'argcmd_name', None), None)
81 yield assert_equal(getattr(magic_foo1, 'argcmd_name', None), None)
79 assert_equal(real_name(magic_foo1), 'foo1')
82 yield assert_equal(real_name(magic_foo1), 'foo1')
80 assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None))
83 yield assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None))
81 assert hasattr(magic_foo1, 'has_arguments')
84 yield assert_true(hasattr(magic_foo1, 'has_arguments'))
82
85
83 assert_equal(magic_foo2.__doc__, '%foo2\n\n A docstring.\n')
86 yield assert_equal(magic_foo2.__doc__, '%foo2\n\n A docstring.\n')
84 assert_equal(getattr(magic_foo2, 'argcmd_name', None), None)
87 yield assert_equal(getattr(magic_foo2, 'argcmd_name', None), None)
85 assert_equal(real_name(magic_foo2), 'foo2')
88 yield assert_equal(real_name(magic_foo2), 'foo2')
86 assert_equal(magic_foo2(None, ''), argparse.Namespace())
89 yield assert_equal(magic_foo2(None, ''), argparse.Namespace())
87 assert hasattr(magic_foo2, 'has_arguments')
90 yield assert_true(hasattr(magic_foo2, 'has_arguments'))
88
91
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')
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')
90 assert_equal(getattr(magic_foo3, 'argcmd_name', None), None)
93 yield assert_equal(getattr(magic_foo3, 'argcmd_name', None), None)
91 assert_equal(real_name(magic_foo3), 'foo3')
94 yield assert_equal(real_name(magic_foo3), 'foo3')
92 assert_equal(magic_foo3(None, ''),
95 yield assert_equal(magic_foo3(None, ''),
96 argparse.Namespace(bar=None, baz=None, foo=None))
93 argparse.Namespace(bar=None, baz=None, foo=None))
97 yield assert_true(hasattr(magic_foo3, 'has_arguments'))
94 assert hasattr(magic_foo3, 'has_arguments')
98
95
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')
96 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)
97 assert_equal(getattr(magic_foo4, 'argcmd_name', None), None)
101 yield assert_equal(real_name(magic_foo4), 'foo4')
98 assert_equal(real_name(magic_foo4), 'foo4')
102 yield assert_equal(magic_foo4(None, ''), argparse.Namespace())
99 assert_equal(magic_foo4(None, ''), argparse.Namespace())
103 yield assert_true(hasattr(magic_foo4, 'has_arguments'))
100 assert hasattr(magic_foo4, 'has_arguments')
104
101
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')
102 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')
103 assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate')
107 yield assert_equal(real_name(magic_foo5), 'frobnicate')
104 assert_equal(real_name(magic_foo5), 'frobnicate')
108 yield assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None))
105 assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None))
109 yield assert_true(hasattr(magic_foo5, 'has_arguments'))
106 assert hasattr(magic_foo5, 'has_arguments')
110
107
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')
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')
112 yield assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None)
109 assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None)
113 yield assert_equal(real_name(magic_magic_foo), 'magic_foo')
110 assert_equal(real_name(magic_magic_foo), 'magic_foo')
114 yield assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None))
111 assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None))
115 yield assert_true(hasattr(magic_magic_foo, 'has_arguments'))
112 assert hasattr(magic_magic_foo, 'has_arguments')
116
113
117 yield assert_equal(foo.__doc__, '%foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
114 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)
115 assert_equal(getattr(foo, 'argcmd_name', None), None)
119 yield assert_equal(real_name(foo), 'foo')
116 assert_equal(real_name(foo), 'foo')
120 yield assert_equal(foo(None, ''), argparse.Namespace(foo=None))
117 assert_equal(foo(None, ''), argparse.Namespace(foo=None))
121 yield assert_true(hasattr(foo, 'has_arguments'))
118 assert hasattr(foo, 'has_arguments')
@@ -6,7 +6,6 b''
6 import nose.tools as nt
6 import nose.tools as nt
7
7
8 from IPython.core.prefilter import AutocallChecker
8 from IPython.core.prefilter import AutocallChecker
9 from IPython.testing import decorators as dec
10 from IPython.testing.globalipapp import get_ipython
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 ip = get_ipython()
14 ip = get_ipython()
16
15
17 @dec.parametric
18 def test_prefilter():
16 def test_prefilter():
19 """Test user input conversions"""
17 """Test user input conversions"""
20
18
@@ -23,19 +21,18 b' def test_prefilter():'
23 ]
21 ]
24
22
25 for raw, correct in pairs:
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 def test_autocall_binops():
27 def test_autocall_binops():
31 """See https://github.com/ipython/ipython/issues/81"""
28 """See https://github.com/ipython/ipython/issues/81"""
32 ip.magic('autocall 2')
29 ip.magic('autocall 2')
33 f = lambda x: x
30 f = lambda x: x
34 ip.user_ns['f'] = f
31 ip.user_ns['f'] = f
35 try:
32 try:
36 yield nt.assert_equal(ip.prefilter('f 1'),'f(1)')
33 nt.assert_equal(ip.prefilter('f 1'),'f(1)')
37 for t in ['f +1', 'f -1']:
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 # Run tests again with a more permissive exclude_regexp, which will
37 # Run tests again with a more permissive exclude_regexp, which will
41 # allow transformation of binary operations ('f -1' -> 'f(-1)').
38 # allow transformation of binary operations ('f -1' -> 'f(-1)').
@@ -47,8 +44,8 b' def test_autocall_binops():'
47 ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or '
44 ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or '
48 pm.sort_checkers()
45 pm.sort_checkers()
49
46
50 yield nt.assert_equal(ip.prefilter('f -1'), 'f(-1)')
47 nt.assert_equal(ip.prefilter('f -1'), 'f(-1)')
51 yield nt.assert_equal(ip.prefilter('f +1'), 'f(+1)')
48 nt.assert_equal(ip.prefilter('f +1'), 'f(+1)')
52 finally:
49 finally:
53 pm.unregister_checker(ac)
50 pm.unregister_checker(ac)
54 finally:
51 finally:
@@ -56,7 +53,6 b' def test_autocall_binops():'
56 del ip.user_ns['f']
53 del ip.user_ns['f']
57
54
58
55
59 @dec.parametric
60 def test_issue_114():
56 def test_issue_114():
61 """Check that multiline string literals don't expand as magic
57 """Check that multiline string literals don't expand as magic
62 see http://github.com/ipython/ipython/issues/114"""
58 see http://github.com/ipython/ipython/issues/114"""
@@ -70,7 +66,7 b' def test_issue_114():'
70 try:
66 try:
71 for mgk in ip.magics_manager.lsmagic()['line']:
67 for mgk in ip.magics_manager.lsmagic()['line']:
72 raw = template % mgk
68 raw = template % mgk
73 yield nt.assert_equal(ip.prefilter(raw), raw)
69 nt.assert_equal(ip.prefilter(raw), raw)
74 finally:
70 finally:
75 ip.prefilter_manager.multi_line_specials = msp
71 ip.prefilter_manager.multi_line_specials = msp
76
72
@@ -24,7 +24,6 b' import numpy as np'
24
24
25 # Our own imports
25 # Our own imports
26 from IPython.core.interactiveshell import InteractiveShell
26 from IPython.core.interactiveshell import InteractiveShell
27 from IPython.testing import decorators as dec
28 from .. import pylabtools as pt
27 from .. import pylabtools as pt
29
28
30 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
@@ -39,11 +38,10 b' from .. import pylabtools as pt'
39 # Classes and functions
38 # Classes and functions
40 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
41
40
42 @dec.parametric
43 def test_figure_to_svg():
41 def test_figure_to_svg():
44 # simple empty-figure test
42 # simple empty-figure test
45 fig = plt.figure()
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 plt.close('all')
46 plt.close('all')
49
47
@@ -53,7 +51,7 b' def test_figure_to_svg():'
53 ax.plot([1,2,3])
51 ax.plot([1,2,3])
54 plt.draw()
52 plt.draw()
55 svg = pt.print_figure(fig, 'svg')[:100].lower()
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 def test_import_pylab():
57 def test_import_pylab():
@@ -15,6 +15,7 b' from __future__ import absolute_import'
15
15
16 import functools
16 import functools
17 import os
17 import os
18 from os.path import join as pjoin
18 import random
19 import random
19 import sys
20 import sys
20 import tempfile
21 import tempfile
@@ -359,6 +360,17 b' tclass.py: deleting object: C-third'
359 self.mktmp(src)
360 self.mktmp(src)
360 _ip.magic('run -t -N 1 %s' % self.fname)
361 _ip.magic('run -t -N 1 %s' % self.fname)
361 _ip.magic('run -t -N 10 %s' % self.fname)
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 class TestMagicRunWithPackage(unittest.TestCase):
376 class TestMagicRunWithPackage(unittest.TestCase):
@@ -398,6 +410,7 b' class TestMagicRunWithPackage(unittest.TestCase):'
398 self.tempdir.cleanup()
410 self.tempdir.cleanup()
399
411
400 def check_run_submodule(self, submodule, opts=''):
412 def check_run_submodule(self, submodule, opts=''):
413 _ip.user_ns.pop('x', None)
401 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
414 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
402 self.assertEqual(_ip.user_ns['x'], self.value,
415 self.assertEqual(_ip.user_ns['x'], self.value,
403 'Variable `x` is not loaded from module `{0}`.'
416 'Variable `x` is not loaded from module `{0}`.'
@@ -430,3 +443,16 b' class TestMagicRunWithPackage(unittest.TestCase):'
430 @with_fake_debugger
443 @with_fake_debugger
431 def test_debug_run_submodule_with_relative_import(self):
444 def test_debug_run_submodule_with_relative_import(self):
432 self.check_run_submodule('relative', '-d')
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 with tt.AssertPrints("zoon()", suppress=False):
108 with tt.AssertPrints("zoon()", suppress=False):
109 ip.magic('run %s' % fname)
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 class SyntaxErrorTest(unittest.TestCase):
119 class SyntaxErrorTest(unittest.TestCase):
112 def test_syntaxerror_without_lineno(self):
120 def test_syntaxerror_without_lineno(self):
113 with tt.AssertNotPrints("TypeError"):
121 with tt.AssertNotPrints("TypeError"):
114 with tt.AssertPrints("line unknown"):
122 with tt.AssertPrints("line unknown"):
115 ip.run_cell("raise SyntaxError()")
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 traceback in a manner similar to what you would expect from a syntax-highlighting
8 traceback in a manner similar to what you would expect from a syntax-highlighting
9 text editor.
9 text editor.
10
10
11 Installation instructions for ColorTB:
11 Installation instructions for ColorTB::
12
12 import sys,ultratb
13 import sys,ultratb
13 sys.excepthook = ultratb.ColorTB()
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 are bug-free. If a crash *does* occur in that type of program you want details.
22 are bug-free. If a crash *does* occur in that type of program you want details.
22 Give it a shot--you'll love it or you'll hate it.
23 Give it a shot--you'll love it or you'll hate it.
23
24
24 Note:
25 .. note::
25
26
26 The Verbose mode prints the variables currently visible where the exception
27 The Verbose mode prints the variables currently visible where the exception
27 happened (shortening their strings if too long). This can potentially be
28 happened (shortening their strings if too long). This can potentially be
@@ -36,25 +37,28 b' Note:'
36 Verbose).
37 Verbose).
37
38
38
39
39 Installation instructions for ColorTB:
40 Installation instructions for ColorTB::
41
40 import sys,ultratb
42 import sys,ultratb
41 sys.excepthook = ultratb.VerboseTB()
43 sys.excepthook = ultratb.VerboseTB()
42
44
43 Note: Much of the code in this module was lifted verbatim from the standard
45 Note: Much of the code in this module was lifted verbatim from the standard
44 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
46 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
45
47
46 * Color schemes
48 Color schemes
49 -------------
50
47 The colors are defined in the class TBTools through the use of the
51 The colors are defined in the class TBTools through the use of the
48 ColorSchemeTable class. Currently the following exist:
52 ColorSchemeTable class. Currently the following exist:
49
53
50 - NoColor: allows all of this module to be used in any terminal (the color
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 - Linux: is meant to look good in a terminal like the Linux console (black
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 - LightBG: similar to Linux but swaps dark/light colors to be more readable
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 You can implement other color schemes easily, the syntax is fairly
63 You can implement other color schemes easily, the syntax is fairly
60 self-explanatory. Please send back new schemes you develop to the author for
64 self-explanatory. Please send back new schemes you develop to the author for
@@ -359,8 +363,8 b' class TBTools(object):'
359 Valid values are:
363 Valid values are:
360
364
361 - None: the default, which means that IPython will dynamically resolve
365 - None: the default, which means that IPython will dynamically resolve
362 to io.stdout. This ensures compatibility with most tools, including
366 to io.stdout. This ensures compatibility with most tools, including
363 Windows (where plain stdout doesn't recognize ANSI escapes).
367 Windows (where plain stdout doesn't recognize ANSI escapes).
364
368
365 - Any object with 'write' and 'flush' attributes.
369 - Any object with 'write' and 'flush' attributes.
366 """
370 """
@@ -974,9 +978,9 b' class VerboseTB(TBTools):'
974 Keywords:
978 Keywords:
975
979
976 - force(False): by default, this routine checks the instance call_pdb
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.
981 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
982 The 'force' option forces the debugger to activate even if the flag
979 is false.
983 is false.
980
984
981 If the call_pdb flag is set, the pdb interactive debugger is
985 If the call_pdb flag is set, the pdb interactive debugger is
982 invoked. In all cases, the self.tb reference to the current traceback
986 invoked. In all cases, the self.tb reference to the current traceback
@@ -1193,6 +1197,20 b' class SyntaxTB(ListTB):'
1193 self.last_syntax_error = value
1197 self.last_syntax_error = value
1194 ListTB.__call__(self,etype,value,elist)
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 def clear_err_state(self):
1214 def clear_err_state(self):
1197 """Return the current error state and clear it"""
1215 """Return the current error state and clear it"""
1198 e = self.last_syntax_error
1216 e = self.last_syntax_error
@@ -4,9 +4,18 b''
4 Cython related magics
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 Usage
14 Usage
8 =====
15 =====
9
16
17 To enable the magics below, execute ``%load_ext cythonmagic``.
18
10 ``%%cython``
19 ``%%cython``
11
20
12 {CYTHON_DOC}
21 {CYTHON_DOC}
@@ -11,9 +11,13 b' Magics for interacting with Octave via oct2py.'
11 The ``oct2py`` module needs to be installed separately and
11 The ``oct2py`` module needs to be installed separately and
12 can be obtained using ``easy_install`` or ``pip``.
12 can be obtained using ``easy_install`` or ``pip``.
13
13
14 You will also need a working copy of GNU Octave.
15
14 Usage
16 Usage
15 =====
17 =====
16
18
19 To enable the magics below, execute ``%load_ext octavemagic``.
20
17 ``%octave``
21 ``%octave``
18
22
19 {OCTAVE_DOC}
23 {OCTAVE_DOC}
@@ -6,9 +6,18 b' Rmagic'
6
6
7 Magic command interface for interactive work with R via rpy2
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 Usage
16 Usage
10 =====
17 =====
11
18
19 To enable the magics below, execute ``%load_ext rmagic``.
20
12 ``%R``
21 ``%R``
13
22
14 {R_DOC}
23 {R_DOC}
@@ -425,12 +434,12 b' class RMagics(Magics):'
425 is printed if it would printed be when evaluating the same code
434 is printed if it would printed be when evaluating the same code
426 within a standard R REPL.
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 In [10]: %%R
439 In [10]: %%R
431 ....: Y = c(2,4,3,9)
440 ....: Y = c(2,4,3,9)
432 ....: summary(lm(Y~X))
441 ....: summary(lm(Y~X))
433
442
434 Call:
443 Call:
435 lm(formula = Y ~ X)
444 lm(formula = Y ~ X)
436
445
@@ -447,9 +456,9 b' class RMagics(Magics):'
447 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
456 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
448 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
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 will create a scatter plot of X bs Y.
463 will create a scatter plot of X bs Y.
455
464
@@ -475,19 +484,19 b' class RMagics(Magics):'
475 * If the cell is not None, the magic returns None.
484 * If the cell is not None, the magic returns None.
476
485
477 * If the cell evaluates as False, the resulting value is returned
486 * If the cell evaluates as False, the resulting value is returned
478 unless the final line prints something to the console, in
487 unless the final line prints something to the console, in
479 which case None is returned.
488 which case None is returned.
480
489
481 * If the final line results in a NULL value when evaluated
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 * No attempt is made to convert the final value to a structured array.
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 * If the -n flag is present, there is no return value.
496 * If the -n flag is present, there is no return value.
488
497
489 * A trailing ';' will also result in no return value as the last
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 The --dataframe argument will attempt to return structured arrays.
501 The --dataframe argument will attempt to return structured arrays.
493 This is useful for dataframes with
502 This is useful for dataframes with
@@ -25,10 +25,11 b' To automatically restore stored variables at startup, add this to your'
25 import inspect, os, sys, textwrap
25 import inspect, os, sys, textwrap
26
26
27 # Our own
27 # Our own
28 from IPython.config.configurable import Configurable
28 from IPython.core.error import UsageError
29 from IPython.core.error import UsageError
29 from IPython.core.fakemodule import FakeModule
30 from IPython.core.magic import Magics, magics_class, line_magic
30 from IPython.core.magic import Magics, magics_class, line_magic
31 from IPython.testing.skipdoctest import skip_doctest
31 from IPython.testing.skipdoctest import skip_doctest
32 from IPython.utils.traitlets import Bool
32
33
33 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
34 # Functions and classes
35 # Functions and classes
@@ -68,10 +69,23 b' def restore_data(ip):'
68
69
69
70
70 @magics_class
71 @magics_class
71 class StoreMagics(Magics):
72 class StoreMagics(Magics, Configurable):
72 """Lightweight persistence for python variables.
73 """Lightweight persistence for python variables.
73
74
74 Provides the %store magic."""
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 @skip_doctest
90 @skip_doctest
77 @line_magic
91 @line_magic
@@ -196,20 +210,21 b' class StoreMagics(Magics):'
196 obj = ip.user_ns[args[0]]
210 obj = ip.user_ns[args[0]]
197 except KeyError:
211 except KeyError:
198 # it might be an alias
212 # it might be an alias
199 # This needs to be refactored to use the new AliasManager stuff.
213 name = args[0]
200 if args[0] in ip.alias_manager:
214 try:
201 name = args[0]
215 cmd = ip.alias_manager.retrieve_alias(name)
202 nargs, cmd = ip.alias_manager.alias_table[ name ]
216 except ValueError:
203 staliases = db.get('stored_aliases',{})
217 raise UsageError("Unknown variable '%s'" % name)
204 staliases[ name ] = cmd
218
205 db['stored_aliases'] = staliases
219 staliases = db.get('stored_aliases',{})
206 print "Alias stored: %s (%s)" % (name, cmd)
220 staliases[name] = cmd
207 return
221 db['stored_aliases'] = staliases
208 else:
222 print "Alias stored: %s (%s)" % (name, cmd)
209 raise UsageError("Unknown variable '%s'" % args[0])
223 return
210
224
211 else:
225 else:
212 if isinstance(inspect.getmodule(obj), FakeModule):
226 modname = getattr(inspect.getmodule(obj), '__name__', '')
227 if modname == '__main__':
213 print textwrap.dedent("""\
228 print textwrap.dedent("""\
214 Warning:%s is %s
229 Warning:%s is %s
215 Proper storage of interactively declared classes (or instances
230 Proper storage of interactively declared classes (or instances
@@ -225,3 +240,4 b' class StoreMagics(Magics):'
225 def load_ipython_extension(ip):
240 def load_ipython_extension(ip):
226 """Load the extension in IPython."""
241 """Load the extension in IPython."""
227 ip.register_magics(StoreMagics)
242 ip.register_magics(StoreMagics)
243
@@ -1,5 +1,6 b''
1 import tempfile, os
1 import tempfile, os
2
2
3 from IPython.config.loader import Config
3 import nose.tools as nt
4 import nose.tools as nt
4
5
5 ip = get_ipython()
6 ip = get_ipython()
@@ -26,7 +27,24 b' def test_store_restore():'
26 # Check restoring
27 # Check restoring
27 ip.magic('store -r')
28 ip.magic('store -r')
28 nt.assert_equal(ip.user_ns['foo'], 78)
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 nt.assert_in(os.path.realpath(tmpd), ip.user_ns['_dh'])
31 nt.assert_in(os.path.realpath(tmpd), ip.user_ns['_dh'])
31
32
32 os.rmdir(tmpd)
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 $ python -m IPython.external.mathjax
20 $ python -m IPython.external.mathjax
21
21
22 To a specific profile:
23
24 $ python -m IPython.external.mathjax --profile=research
25
22 To install MathJax from a file you have already downloaded:
26 To install MathJax from a file you have already downloaded:
23
27
24 $ python -m IPython.external.mathjax mathjax-xxx.tar.gz
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 # Imports
50 # Imports
47 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
48
52
53 import argparse
49 import os
54 import os
50 import shutil
55 import shutil
51 import sys
56 import sys
@@ -55,7 +60,6 b' import zipfile'
55
60
56
61
57 from IPython.utils.path import locate_profile
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 parser.add_argument(
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 '-i',
213 '-i',
204 '--install-dir',
214 '--install-dir',
205 default=default_dest,
215 help='custom installation directory')
206 help='installation directory (by default : %s)' % (default_dest))
216
207 parser.add_argument(
217 parser.add_argument(
208 '-d',
218 '-d',
209 '--dest',
219 '--dest',
210 action='store_true',
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 parser.add_argument(
222 parser.add_argument(
213 '-r',
223 '-r',
214 '--replace',
224 '--replace',
215 action='store_true',
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 parser.add_argument(
227 parser.add_argument(
218 '-t',
228 '-t',
219 '--test',
229 '--test',
@@ -225,7 +235,13 b' def main() :'
225
235
226 pargs = parser.parse_args()
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 if pargs.dest :
245 if pargs.dest :
230 print dest
246 print dest
231 return
247 return
@@ -259,7 +275,7 b' def main() :'
259 if __name__ == '__main__' :
275 if __name__ == '__main__' :
260 sys.exit(main())
276 sys.exit(main())
261
277
262 __all__ = ['install_mathjax', 'main', 'dest']
278 __all__ = ['install_mathjax', 'main', 'default_dest']
263
279
264 """
280 """
265 Test notes:
281 Test notes:
@@ -78,7 +78,7 b' from IPython.kernel.zmq.kernelapp import ('
78 kernel_aliases,
78 kernel_aliases,
79 )
79 )
80 from IPython.utils.importstring import import_item
80 from IPython.utils.importstring import import_item
81 from IPython.utils.localinterfaces import LOCALHOST
81 from IPython.utils.localinterfaces import localhost
82 from IPython.utils import submodule
82 from IPython.utils import submodule
83 from IPython.utils.traitlets import (
83 from IPython.utils.traitlets import (
84 Dict, Unicode, Integer, List, Bool, Bytes,
84 Dict, Unicode, Integer, List, Bool, Bytes,
@@ -253,7 +253,7 b' aliases.update({'
253 aliases.pop('f', None)
253 aliases.pop('f', None)
254
254
255 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
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 # NotebookApp
259 # NotebookApp
@@ -293,9 +293,11 b' class NotebookApp(BaseIPythonApplication):'
293
293
294 # Network related information.
294 # Network related information.
295
295
296 ip = Unicode(LOCALHOST, config=True,
296 ip = Unicode(config=True,
297 help="The IP address the notebook server will listen on."
297 help="The IP address the notebook server will listen on."
298 )
298 )
299 def _ip_default(self):
300 return localhost()
299
301
300 def _ip_changed(self, name, old, new):
302 def _ip_changed(self, name, old, new):
301 if new == u'*': self.ip = u''
303 if new == u'*': self.ip = u''
@@ -471,17 +473,10 b' class NotebookApp(BaseIPythonApplication):'
471 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
473 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
472 "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL")
474 "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL")
473 )
475 )
474
476
475 def parse_command_line(self, argv=None):
477 def parse_command_line(self, argv=None):
476 super(NotebookApp, self).parse_command_line(argv)
478 super(NotebookApp, self).parse_command_line(argv)
477 if argv is None:
479
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
485 if self.extra_args:
480 if self.extra_args:
486 f = os.path.abspath(self.extra_args[0])
481 f = os.path.abspath(self.extra_args[0])
487 if os.path.isdir(f):
482 if os.path.isdir(f):
@@ -491,6 +486,15 b' class NotebookApp(BaseIPythonApplication):'
491 nbdir = os.path.dirname(f)
486 nbdir = os.path.dirname(f)
492 self.config.NotebookManager.notebook_dir = nbdir
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 def init_configurables(self):
498 def init_configurables(self):
495 # force Session default to be secure
499 # force Session default to be secure
496 default_secure(self.config)
500 default_secure(self.config)
@@ -657,6 +661,7 b' class NotebookApp(BaseIPythonApplication):'
657 def initialize(self, argv=None):
661 def initialize(self, argv=None):
658 self.init_logging()
662 self.init_logging()
659 super(NotebookApp, self).initialize(argv)
663 super(NotebookApp, self).initialize(argv)
664 self.init_kernel_argv()
660 self.init_configurables()
665 self.init_configurables()
661 self.init_components()
666 self.init_components()
662 self.init_webapp()
667 self.init_webapp()
@@ -691,7 +696,7 b' class NotebookApp(BaseIPythonApplication):'
691 info("Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).")
696 info("Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).")
692
697
693 if self.open_browser or self.file_to_run:
698 if self.open_browser or self.file_to_run:
694 ip = self.ip or LOCALHOST
699 ip = self.ip or localhost()
695 try:
700 try:
696 browser = webbrowser.get(self.browser or None)
701 browser = webbrowser.get(self.browser or None)
697 except webbrowser.Error as e:
702 except webbrowser.Error as e:
@@ -1,4 +1,4 b''
1 @import "../base/less/variables.less";
1 @import "../base/less/variables.less";
2 @import "../base/less/mixins.less";
2 @import "../base/less/mixins.less";
3 @import "../base/less/flexbox.less";
3 @import "../base/less/flexbox.less";
4 @import "../base/less/page.less";
4
@@ -17,12 +17,14 b''
17 * Create a custom button in toolbar that execute `%qtconsole` in kernel
17 * Create a custom button in toolbar that execute `%qtconsole` in kernel
18 * and hence open a qtconsole attached to the same kernel as the current notebook
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 * IPython.toolbar.add_buttons_group([
21 * IPython.toolbar.add_buttons_group([
22 * {
22 * {
23 * 'label' : 'run qtconsole',
23 * 'label' : 'run qtconsole',
24 * 'icon' : 'ui-icon-calculator', // select your icon from http://jqueryui.com/themeroller/
24 * 'icon' : 'icon-terminal', // select your icon from http://fortawesome.github.io/Font-Awesome/icons
25 * 'callback': function(){IPython.notebook.kernel.execute('%qtconsole')}
25 * 'callback': function () {
26 * IPython.notebook.kernel.execute('%qtconsole')
27 * }
26 * }
28 * }
27 * // add more button here if needed.
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 * @method is_splittable
211 * @method is_splittable
212 **/
212 **/
213 Cell.prototype.is_splittable = function () {
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 * @return {String} - the text before the cursor
228 * @return {String} - the text before the cursor
220 * @method get_pre_cursor
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)
335 // fallback on default
327 var default_mode = this.default_mode || 'text/plain';
336 var default_mode
337 try {
338 default_mode = this._options.cm_config.mode;
339 } catch(e) {
340 default_mode = 'text/plain';
341 }
328 this.code_mirror.setOption('mode', default_mode);
342 this.code_mirror.setOption('mode', default_mode);
329 };
343 };
330
344
@@ -65,8 +65,8 b' var IPython = (function (IPython) {'
65 this.code_mirror = null;
65 this.code_mirror = null;
66 this.input_prompt_number = null;
66 this.input_prompt_number = null;
67 this.collapsed = false;
67 this.collapsed = false;
68 this.default_mode = 'ipython';
69 this.cell_type = "code";
68 this.cell_type = "code";
69 this.last_msg_id = null;
70
70
71
71
72 var cm_overwrite_options = {
72 var cm_overwrite_options = {
@@ -167,7 +167,7 b' var IPython = (function (IPython) {'
167 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
167 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
168 // browser and keyboard layout !
168 // browser and keyboard layout !
169 // Pressing '(' , request tooltip, don't forget to reappend it
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 // is actually empty
171 // is actually empty
172 IPython.tooltip.pending(that, true);
172 IPython.tooltip.pending(that, true);
173 } else if (event.which === key.UPARROW && event.type === 'keydown') {
173 } else if (event.which === key.UPARROW && event.type === 'keydown') {
@@ -180,8 +180,7 b' var IPython = (function (IPython) {'
180 return true;
180 return true;
181 };
181 };
182 } else if (event.which === key.ESC) {
182 } else if (event.which === key.ESC) {
183 IPython.tooltip.remove_and_cancel_tooltip(true);
183 return IPython.tooltip.remove_and_cancel_tooltip(true);
184 return true;
185 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
184 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
186 // If we are not at the bottom, let CM handle the down arrow and
185 // If we are not at the bottom, let CM handle the down arrow and
187 // prevent the global keydown handler from handling it.
186 // prevent the global keydown handler from handling it.
@@ -242,9 +241,12 b' var IPython = (function (IPython) {'
242 * @method execute
241 * @method execute
243 */
242 */
244 CodeCell.prototype.execute = function () {
243 CodeCell.prototype.execute = function () {
245 this.output_area.clear_output(true, true, true);
244 this.output_area.clear_output();
246 this.set_input_prompt('*');
245 this.set_input_prompt('*');
247 this.element.addClass("running");
246 this.element.addClass("running");
247 if (this.last_msg_id) {
248 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
249 }
248 var callbacks = {
250 var callbacks = {
249 'execute_reply': $.proxy(this._handle_execute_reply, this),
251 'execute_reply': $.proxy(this._handle_execute_reply, this),
250 'output': $.proxy(this.output_area.handle_output, this.output_area),
252 'output': $.proxy(this.output_area.handle_output, this.output_area),
@@ -252,7 +254,7 b' var IPython = (function (IPython) {'
252 'set_next_input': $.proxy(this._handle_set_next_input, this),
254 'set_next_input': $.proxy(this._handle_set_next_input, this),
253 'input_request': $.proxy(this._handle_input_request, this)
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 var data = {'cell': this, 'text': text}
275 var data = {'cell': this, 'text': text}
274 $([IPython.events]).trigger('set_next_input.Notebook', data);
276 $([IPython.events]).trigger('set_next_input.Notebook', data);
275 }
277 }
276
278
277 /**
279 /**
278 * @method _handle_input_request
280 * @method _handle_input_request
279 * @private
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) {
393 CodeCell.prototype.clear_output = function (wait) {
392 this.output_area.clear_output(stdout, stderr, other);
394 this.output_area.clear_output(wait);
393 };
395 };
394
396
395
397
@@ -50,12 +50,13 b' var IPython = (function (IPython) {'
50 * cell_magic_highlight['javascript'] = {'reg':[/^var/]}
50 * cell_magic_highlight['javascript'] = {'reg':[/^var/]}
51 */
51 */
52 cell_magic_highlight : {
52 cell_magic_highlight : {
53 'magic_javascript':{'reg':[/^%%javascript/]}
53 'magic_javascript' :{'reg':[/^%%javascript/]}
54 ,'magic_perl' :{'reg':[/^%%perl/]}
54 ,'magic_perl' :{'reg':[/^%%perl/]}
55 ,'magic_ruby' :{'reg':[/^%%ruby/]}
55 ,'magic_ruby' :{'reg':[/^%%ruby/]}
56 ,'magic_python' :{'reg':[/^%%python3?/]}
56 ,'magic_python' :{'reg':[/^%%python3?/]}
57 ,'magic_shell' :{'reg':[/^%%bash/]}
57 ,'magic_shell' :{'reg':[/^%%bash/]}
58 ,'magic_r' :{'reg':[/^%%R/]}
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 tables: true,
99 tables: true,
100 langPrefix: "language-",
100 langPrefix: "language-",
101 highlight: function(code, lang) {
101 highlight: function(code, lang) {
102 if (!lang) {
103 // no language, no highlight
104 return code;
105 }
102 var highlighted;
106 var highlighted;
103 try {
107 try {
104 highlighted = hljs.highlight(lang, code, false);
108 highlighted = hljs.highlight(lang, code, false);
@@ -1175,8 +1175,14 b' var IPython = (function (IPython) {'
1175 Notebook.prototype.merge_cell_above = function () {
1175 Notebook.prototype.merge_cell_above = function () {
1176 var index = this.get_selected_index();
1176 var index = this.get_selected_index();
1177 var cell = this.get_cell(index);
1177 var cell = this.get_cell(index);
1178 if (!cell.is_mergeable()) {
1179 return;
1180 }
1178 if (index > 0) {
1181 if (index > 0) {
1179 var upper_cell = this.get_cell(index-1);
1182 var upper_cell = this.get_cell(index-1);
1183 if (!upper_cell.is_mergeable()) {
1184 return;
1185 }
1180 var upper_text = upper_cell.get_text();
1186 var upper_text = upper_cell.get_text();
1181 var text = cell.get_text();
1187 var text = cell.get_text();
1182 if (cell instanceof IPython.CodeCell) {
1188 if (cell instanceof IPython.CodeCell) {
@@ -1199,8 +1205,14 b' var IPython = (function (IPython) {'
1199 Notebook.prototype.merge_cell_below = function () {
1205 Notebook.prototype.merge_cell_below = function () {
1200 var index = this.get_selected_index();
1206 var index = this.get_selected_index();
1201 var cell = this.get_cell(index);
1207 var cell = this.get_cell(index);
1208 if (!cell.is_mergeable()) {
1209 return;
1210 }
1202 if (index < this.ncells()-1) {
1211 if (index < this.ncells()-1) {
1203 var lower_cell = this.get_cell(index+1);
1212 var lower_cell = this.get_cell(index+1);
1213 if (!lower_cell.is_mergeable()) {
1214 return;
1215 }
1204 var lower_text = lower_cell.get_text();
1216 var lower_text = lower_cell.get_text();
1205 var text = cell.get_text();
1217 var text = cell.get_text();
1206 if (cell instanceof IPython.CodeCell) {
1218 if (cell instanceof IPython.CodeCell) {
@@ -1327,7 +1339,7 b' var IPython = (function (IPython) {'
1327 var cells = this.get_cells();
1339 var cells = this.get_cells();
1328 for (var i=0; i<ncells; i++) {
1340 for (var i=0; i<ncells; i++) {
1329 if (cells[i] instanceof IPython.CodeCell) {
1341 if (cells[i] instanceof IPython.CodeCell) {
1330 cells[i].clear_output(true,true,true);
1342 cells[i].clear_output();
1331 // Make all In[] prompts blank, as well
1343 // Make all In[] prompts blank, as well
1332 // TODO: make this configurable (via checkbox?)
1344 // TODO: make this configurable (via checkbox?)
1333 cells[i].set_input_prompt();
1345 cells[i].set_input_prompt();
@@ -31,7 +31,7 b' var IPython = (function (IPython) {'
31 this.outputs = [];
31 this.outputs = [];
32 this.collapsed = false;
32 this.collapsed = false;
33 this.scrolled = false;
33 this.scrolled = false;
34 this.clear_out_timeout = null;
34 this.clear_queued = null;
35 if (prompt_area === undefined) {
35 if (prompt_area === undefined) {
36 this.prompt_area = true;
36 this.prompt_area = true;
37 } else {
37 } else {
@@ -289,7 +289,12 b' var IPython = (function (IPython) {'
289 OutputArea.prototype.append_output = function (json, dynamic) {
289 OutputArea.prototype.append_output = function (json, dynamic) {
290 // If dynamic is true, javascript output will be eval'd.
290 // If dynamic is true, javascript output will be eval'd.
291 this.expand();
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 if (json.output_type === 'pyout') {
298 if (json.output_type === 'pyout') {
294 this.append_pyout(json, dynamic);
299 this.append_pyout(json, dynamic);
295 } else if (json.output_type === 'pyerr') {
300 } else if (json.output_type === 'pyerr') {
@@ -300,6 +305,7 b' var IPython = (function (IPython) {'
300 this.append_stream(json);
305 this.append_stream(json);
301 }
306 }
302 this.outputs.push(json);
307 this.outputs.push(json);
308 this.element.height('auto');
303 var that = this;
309 var that = this;
304 setTimeout(function(){that.element.trigger('resize');}, 100);
310 setTimeout(function(){that.element.trigger('resize');}, 100);
305 };
311 };
@@ -312,6 +318,33 b' var IPython = (function (IPython) {'
312 }
318 }
313 return oa;
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 OutputArea.prototype.append_pyout = function (json, dynamic) {
350 OutputArea.prototype.append_pyout = function (json, dynamic) {
@@ -321,19 +354,7 b' var IPython = (function (IPython) {'
321 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
354 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
322 }
355 }
323 this.append_mime_type(json, toinsert, dynamic);
356 this.append_mime_type(json, toinsert, dynamic);
324 try {
357 this._safe_append(toinsert);
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 }
337 // If we just output latex, typeset it.
358 // If we just output latex, typeset it.
338 if ((json.latex !== undefined) || (json.html !== undefined)) {
359 if ((json.latex !== undefined) || (json.html !== undefined)) {
339 this.typeset();
360 this.typeset();
@@ -352,7 +373,7 b' var IPython = (function (IPython) {'
352 s = s + '\n';
373 s = s + '\n';
353 var toinsert = this.create_output_area();
374 var toinsert = this.create_output_area();
354 this.append_text(s, {}, toinsert);
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 // If we got here, attach a new div
410 // If we got here, attach a new div
390 var toinsert = this.create_output_area();
411 var toinsert = this.create_output_area();
391 this.append_text(text, {}, toinsert, "output_stream "+subclass);
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 OutputArea.prototype.append_display_data = function (json, dynamic) {
417 OutputArea.prototype.append_display_data = function (json, dynamic) {
397 var toinsert = this.create_output_area();
418 var toinsert = this.create_output_area();
398 this.append_mime_type(json, toinsert, dynamic);
419 this.append_mime_type(json, toinsert, dynamic);
399 this.element.append(toinsert);
420 this._safe_append(toinsert);
400 // If we just output latex, typeset it.
421 // If we just output latex, typeset it.
401 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
422 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
402 this.typeset();
423 this.typeset();
@@ -444,15 +465,7 b' var IPython = (function (IPython) {'
444 try {
465 try {
445 eval(js);
466 eval(js);
446 } catch(err) {
467 } catch(err) {
447 console.log('Error in Javascript!');
468 this._append_javascript_error(err, container);
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 );
456 }
469 }
457 };
470 };
458
471
@@ -546,7 +559,6 b' var IPython = (function (IPython) {'
546 OutputArea.prototype.append_raw_input = function (content) {
559 OutputArea.prototype.append_raw_input = function (content) {
547 var that = this;
560 var that = this;
548 this.expand();
561 this.expand();
549 this.flush_clear_timeout();
550 var area = this.create_output_area();
562 var area = this.create_output_area();
551
563
552 // disable any other raw_inputs, if they are left around
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 OutputArea.prototype.handle_clear_output = function (content) {
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) {
618 OutputArea.prototype.clear_output = function(wait) {
607 var that = this;
619 if (wait) {
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 };
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) {
626 this.clear_queued = true;
630 var output_div = this.element;
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 // clear all, no need for logic
637 // clear all, no need for logic
634 output_div.html("");
638 this.element.html("");
635 this.outputs = [];
639 this.outputs = [];
636 this.unscroll_area();
640 this.unscroll_area();
637 return;
641 return;
638 }
642 };
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 }
677 };
643 };
678
644
679
645
@@ -52,7 +52,7 b' var IPython = (function (IPython) {'
52 $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) {
52 $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) {
53 that.set_last_checkpoint(data[0]);
53 that.set_last_checkpoint(data[0]);
54 });
54 });
55
55
56 $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) {
56 $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) {
57 that.set_last_checkpoint(data);
57 that.set_last_checkpoint(data);
58 });
58 });
@@ -104,7 +104,7 b' var IPython = (function (IPython) {'
104 return false;
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 return data;
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 * Change heading level of cell, and re-render
503 * Change heading level of cell, and re-render
@@ -161,16 +161,23 b' var IPython = (function (IPython) {'
161 this.code_mirror = null;
161 this.code_mirror = null;
162 }
162 }
163
163
164 // return true on successfully removing a visible tooltip; otherwise return
165 // false.
164 Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
166 Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
165 // note that we don't handle closing directly inside the calltip
167 // note that we don't handle closing directly inside the calltip
166 // as in the completer, because it is not focusable, so won't
168 // as in the completer, because it is not focusable, so won't
167 // get the event.
169 // get the event.
168 if (this._sticky == false || force == true) {
170 if (!this._hidden) {
169 this.cancel_stick();
171 if (force || !this._sticky) {
170 this._hide();
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 // cancel autocall done after '(' for example.
183 // cancel autocall done after '(' for example.
@@ -335,7 +342,7 b' var IPython = (function (IPython) {'
335 if (docstring == null) {
342 if (docstring == null) {
336 docstring = reply.docstring;
343 docstring = reply.docstring;
337 }
344 }
338
345
339 if (docstring == null) {
346 if (docstring == null) {
340 // For reals this time, no docstring
347 // For reals this time, no docstring
341 if (this._hide_if_no_docstring) {
348 if (this._hide_if_no_docstring) {
@@ -1,4 +1,4 b''
1 .cell {
1 div.cell {
2 border: 1px solid transparent;
2 border: 1px solid transparent;
3 .vbox();
3 .vbox();
4
4
@@ -6,9 +6,6 b''
6 .corner-all;
6 .corner-all;
7 border : thin @border_color solid;
7 border : thin @border_color solid;
8 }
8 }
9 }
10
11 div.cell {
12 width: 100%;
9 width: 100%;
13 padding: 5px 5px 5px 0px;
10 padding: 5px 5px 5px 0px;
14 /* This acts as a spacer between cells, that is outside the border */
11 /* This acts as a spacer between cells, that is outside the border */
@@ -22,7 +22,7 b''
22 overflow-x: auto;
22 overflow-x: auto;
23 }
23 }
24
24
25 @-moz-document {
25 @-moz-document url-prefix() {
26 /* Firefox does weird and terrible things (#3549) when overflow-x is auto */
26 /* Firefox does weird and terrible things (#3549) when overflow-x is auto */
27 /* It doesn't respect the overflow setting anyway, so we can workaround it with this */
27 /* It doesn't respect the overflow setting anyway, so we can workaround it with this */
28 .CodeMirror-scroll {
28 .CodeMirror-scroll {
@@ -119,7 +119,6 b' var IPython = (function (IPython) {'
119 this.ws_url = ws_url;
119 this.ws_url = ws_url;
120 this.kernel_url = this.base_url + "/" + this.kernel_id;
120 this.kernel_url = this.base_url + "/" + this.kernel_id;
121 this.start_channels();
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 this.shell_channel = new this.WebSocket(ws_url + "/shell");
143 this.shell_channel = new this.WebSocket(ws_url + "/shell");
145 this.stdin_channel = new this.WebSocket(ws_url + "/stdin");
144 this.stdin_channel = new this.WebSocket(ws_url + "/stdin");
146 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
145 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
147 send_cookie = function(){
146
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 };
152 var already_called_onclose = false; // only alert once
147 var already_called_onclose = false; // only alert once
153 var ws_closed_early = function(evt){
148 var ws_closed_early = function(evt){
154 if (already_called_onclose){
149 if (already_called_onclose){
@@ -170,7 +165,7 b' var IPython = (function (IPython) {'
170 };
165 };
171 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
166 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
172 for (var i=0; i < channels.length; i++) {
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 channels[i].onclose = ws_closed_early;
169 channels[i].onclose = ws_closed_early;
175 }
170 }
176 // switch from early-close to late-close message after 1s
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 * @method stop_channels
206 * @method stop_channels
192 */
207 */
193 Kernel.prototype.stop_channels = function () {
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 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
413 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
392 this._msg_callbacks[msg_id] = callbacks || {};
414 this._msg_callbacks[msg_id] = callbacks || {};
393 }
415 };
394
416
395
417
396 Kernel.prototype._handle_shell_reply = function (e) {
418 Kernel.prototype._handle_shell_reply = function (e) {
@@ -18,20 +18,6 b''
18 .start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;}
18 .start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;}
19 .end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;}
19 .end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;}
20 .center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;}
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 .center-nav{display:inline-block;margin-bottom:-4px;}
21 .center-nav{display:inline-block;margin-bottom:-4px;}
36 .alternate_upload{background-color:none;display:inline;}
22 .alternate_upload{background-color:none;display:inline;}
37 .alternate_upload.form{padding:0;margin:0;}
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 .ansibgpurple{background-color:magenta;}
53 .ansibgpurple{background-color:magenta;}
68 .ansibgcyan{background-color:cyan;}
54 .ansibgcyan{background-color:cyan;}
69 .ansibggray{background-color:gray;}
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;}
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;}
71 div.cell{width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 7px;outline:none;}
72 div.prompt{width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
57 div.prompt{width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
73 .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;}
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 .no_input_radius{border-top-right-radius:0px;border-top-left-radius:0px;}
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 div.output_prompt{color:darkred;}
83 div.output_prompt{color:darkred;}
99 .CodeMirror{line-height:1.231em;height:auto;background:none;}
84 .CodeMirror{line-height:1.231em;height:auto;background:none;}
100 .CodeMirror-scroll{overflow-y:hidden;overflow-x:auto;}
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 .CodeMirror-linenumber{padding:0 8px 0 4px;}
87 .CodeMirror-linenumber{padding:0 8px 0 4px;}
103 .CodeMirror-gutters{border-bottom-left-radius:4px;border-top-left-radius:4px;}
88 .CodeMirror-gutters{border-bottom-left-radius:4px;border-top-left-radius:4px;}
104 .CodeMirror pre{padding:0;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
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 // base
9 // base
10 @import "../base/less/style.less";
10 @import "../base/less/style.less";
11 @import "../base/less/page.less";
11
12
12 // auth
13 // auth
13 @import "../auth/less/style.less";
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 .ansibgpurple{background-color:magenta;}
1434 .ansibgpurple{background-color:magenta;}
1435 .ansibgcyan{background-color:cyan;}
1435 .ansibgcyan{background-color:cyan;}
1436 .ansibggray{background-color:gray;}
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;}
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;}
1438 div.cell{width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 7px;outline:none;}
1439 div.prompt{width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
1438 div.prompt{width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
1440 .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;}
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 .no_input_radius{border-top-right-radius:0px;border-top-left-radius:0px;}
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 div.output_prompt{color:darkred;}
1464 div.output_prompt{color:darkred;}
1466 .CodeMirror{line-height:1.231em;height:auto;background:none;}
1465 .CodeMirror{line-height:1.231em;height:auto;background:none;}
1467 .CodeMirror-scroll{overflow-y:hidden;overflow-x:auto;}
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 .CodeMirror-linenumber{padding:0 8px 0 4px;}
1468 .CodeMirror-linenumber{padding:0 8px 0 4px;}
1470 .CodeMirror-gutters{border-bottom-left-radius:4px;border-top-left-radius:4px;}
1469 .CodeMirror-gutters{border-bottom-left-radius:4px;border-top-left-radius:4px;}
1471 .CodeMirror pre{padding:0;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
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 from IPython.external.ssh import tunnel
34 from IPython.external.ssh import tunnel
35
35
36 # IPython imports
36 # IPython imports
37 # from IPython.config import Configurable
37 from IPython.config import Configurable
38 from IPython.core.profiledir import ProfileDir
38 from IPython.core.profiledir import ProfileDir
39 from IPython.utils.localinterfaces import LOCALHOST
39 from IPython.utils.localinterfaces import localhost
40 from IPython.utils.path import filefind, get_ipython_dir
40 from IPython.utils.path import filefind, get_ipython_dir
41 from IPython.utils.py3compat import str_to_bytes, bytes_to_str
41 from IPython.utils.py3compat import str_to_bytes, bytes_to_str
42 from IPython.utils.traitlets import (
42 from IPython.utils.traitlets import (
43 Bool, Integer, Unicode, CaselessStrEnum,
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 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
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 signature_scheme='hmac-sha256',
53 signature_scheme='hmac-sha256',
55 ):
54 ):
56 """Generates a JSON config file, including the selection of random ports.
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 and 'sha256' is the default hash function.
90 and 'sha256' is the default hash function.
92
91
93 """
92 """
93 if not ip:
94 ip = localhost()
94 # default to temporary connector file
95 # default to temporary connector file
95 if not fname:
96 if not fname:
96 fname = tempfile.mktemp('.json')
97 fname = tempfile.mktemp('.json')
@@ -383,7 +384,7 b' channel_socket_types = {'
383
384
384 port_names = [ "%s_port" % channel for channel in ('shell', 'stdin', 'iopub', 'hb', 'control')]
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 """Mixin for configurable classes that work with connection files"""
388 """Mixin for configurable classes that work with connection files"""
388
389
389 # The addresses for the communication channels
390 # The addresses for the communication channels
@@ -392,7 +393,7 b' class ConnectionFileMixin(HasTraits):'
392
393
393 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
394 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
394
395
395 ip = Unicode(LOCALHOST, config=True,
396 ip = Unicode(config=True,
396 help="""Set the kernel\'s IP address [default localhost].
397 help="""Set the kernel\'s IP address [default localhost].
397 If the IP address is something other than localhost, then
398 If the IP address is something other than localhost, then
398 Consoles on other machines will be able to connect
399 Consoles on other machines will be able to connect
@@ -406,7 +407,7 b' class ConnectionFileMixin(HasTraits):'
406 else:
407 else:
407 return 'kernel-ipc'
408 return 'kernel-ipc'
408 else:
409 else:
409 return LOCALHOST
410 return localhost()
410
411
411 def _ip_changed(self, name, old, new):
412 def _ip_changed(self, name, old, new):
412 if new == '*':
413 if new == '*':
@@ -1,5 +1,4 b''
1 """Base class to manage a running kernel
1 """Base class to manage a running kernel"""
2 """
3
2
4 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
5 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 The IPython Development Team
@@ -24,7 +23,7 b' import zmq'
24 # Local imports
23 # Local imports
25 from IPython.config.configurable import LoggingConfigurable
24 from IPython.config.configurable import LoggingConfigurable
26 from IPython.utils.importstring import import_item
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 from IPython.utils.traitlets import (
27 from IPython.utils.traitlets import (
29 Any, Instance, Unicode, List, Bool, Type, DottedObjectName
28 Any, Instance, Unicode, List, Bool, Type, DottedObjectName
30 )
29 )
@@ -185,11 +184,11 b' class KernelManager(LoggingConfigurable, ConnectionFileMixin):'
185 keyword arguments that are passed down to build the kernel_cmd
184 keyword arguments that are passed down to build the kernel_cmd
186 and launching the kernel (e.g. Popen kwargs).
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 raise RuntimeError("Can only launch a kernel on a local interface. "
188 raise RuntimeError("Can only launch a kernel on a local interface. "
190 "Make sure that the '*_address' attributes are "
189 "Make sure that the '*_address' attributes are "
191 "configured properly. "
190 "configured properly. "
192 "Currently valid addresses are: %s"%LOCAL_IPS
191 "Currently valid addresses are: %s" % local_ips()
193 )
192 )
194
193
195 # write connection file / get default ports
194 # write connection file / get default ports
@@ -171,7 +171,7 b' class MultiKernelManager(LoggingConfigurable):'
171 self.log.info("Signaled Kernel %s with %s" % (kernel_id, signum))
171 self.log.info("Signaled Kernel %s with %s" % (kernel_id, signum))
172
172
173 @kernel_method
173 @kernel_method
174 def restart_kernel(self, kernel_id):
174 def restart_kernel(self, kernel_id, now=False):
175 """Restart a kernel by its uuid, keeping the same ports.
175 """Restart a kernel by its uuid, keeping the same ports.
176
176
177 Parameters
177 Parameters
@@ -11,101 +11,18 b''
11 # Imports
11 # Imports
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 import os
15 import shutil
16 import sys
14 import sys
17 import tempfile
18
19 from contextlib import contextmanager
20 from subprocess import PIPE
21
15
22 import nose.tools as nt
16 import nose.tools as nt
23
17
24 from IPython.kernel import KernelManager
18 from IPython.testing import decorators as dec, tools as tt
25 from IPython.kernel.tests.test_message_spec import execute, flush_channels
19 from IPython.utils import py3compat
26 from IPython.testing import decorators as dec
20
27 from IPython.utils import path
21 from .utils import new_kernel, kernel, TIMEOUT, assemble_output, execute, flush_channels
28
22
29 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
30 # Tests
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 def _check_mp_mode(kc, expected=False, stream="stdout"):
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 nt.assert_equal(eval(stdout.strip()), expected)
33 nt.assert_equal(eval(stdout.strip()), expected)
117
34
118
35
36 # printing tests
37
119 def test_simple_print():
38 def test_simple_print():
120 """simple print statement in kernel"""
39 """simple print statement in kernel"""
121 with new_kernel() as kc:
40 with kernel() as kc:
122 iopub = kc.iopub_channel
41 iopub = kc.iopub_channel
123 msg_id, content = execute(kc=kc, code="print ('hi')")
42 msg_id, content = execute(kc=kc, code="print ('hi')")
124 stdout, stderr = assemble_output(iopub)
43 stdout, stderr = assemble_output(iopub)
125 nt.assert_equal(stdout, 'hi\n')
44 nt.assert_equal(stdout, 'hi\n')
126 nt.assert_equal(stderr, '')
45 nt.assert_equal(stderr, '')
127 _check_mp_mode(kc, expected=False)
46 _check_mp_mode(kc, expected=False)
128 print ('hello')
129
47
130
48
131 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
49 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
@@ -161,7 +79,7 b' def test_subprocess_print():'
161
79
162 def test_subprocess_noprint():
80 def test_subprocess_noprint():
163 """mp.Process without print doesn't trigger iostream mp_mode"""
81 """mp.Process without print doesn't trigger iostream mp_mode"""
164 with new_kernel() as kc:
82 with kernel() as kc:
165 iopub = kc.iopub_channel
83 iopub = kc.iopub_channel
166
84
167 np = 5
85 np = 5
@@ -202,3 +120,51 b' def test_subprocess_error():'
202 _check_mp_mode(kc, expected=False)
120 _check_mp_mode(kc, expected=False)
203 _check_mp_mode(kc, expected=False, stream="stderr")
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 def _run_lifecycle(self, km):
26 def _run_lifecycle(self, km):
27 km.start_kernel(stdout=PIPE, stderr=PIPE)
27 km.start_kernel(stdout=PIPE, stderr=PIPE)
28 self.assertTrue(km.is_alive())
28 self.assertTrue(km.is_alive())
29 km.restart_kernel()
29 km.restart_kernel(now=True)
30 self.assertTrue(km.is_alive())
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 km.interrupt_kernel()
31 km.interrupt_kernel()
39 self.assertTrue(isinstance(km, KernelManager))
32 self.assertTrue(isinstance(km, KernelManager))
40 km.shutdown_kernel()
33 km.shutdown_kernel(now=True)
41
34
42 def test_tcp_lifecycle(self):
35 def test_tcp_lifecycle(self):
43 km = self._get_tcp_km()
36 km = self._get_tcp_km()
@@ -20,14 +20,12 b' Authors'
20 import nose.tools as nt
20 import nose.tools as nt
21
21
22 # Our own imports
22 # Our own imports
23 from IPython.testing import decorators as dec
24 from IPython.kernel.launcher import swallow_argv
23 from IPython.kernel.launcher import swallow_argv
25
24
26 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
27 # Classes and functions
26 # Classes and functions
28 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
29
28
30 @dec.parametric
31 def test_swallow_argv():
29 def test_swallow_argv():
32 tests = [
30 tests = [
33 # expected , argv , aliases, flags
31 # expected , argv , aliases, flags
@@ -56,5 +54,5 b' def test_swallow_argv():'
56 "expected : %r" % expected,
54 "expected : %r" % expected,
57 "returned : %r" % stripped,
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 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING.txt, distributed as part of this software.
7 # the file COPYING.txt, distributed as part of this software.
@@ -15,77 +15,25 b' import nose.tools as nt'
15
15
16 from IPython.kernel import KernelManager
16 from IPython.kernel import KernelManager
17
17
18 from IPython.testing import decorators as dec
19 from IPython.utils.traitlets import (
18 from IPython.utils.traitlets import (
20 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
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 KC = None
27 STARTUP_TIMEOUT = 60
28 TIMEOUT = 15
29
28
30 def setup():
29 def setup():
31 global KM, KC
30 global KC
32 KM = KernelManager()
31 KC = start_global_kernel()
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']
83
32
84 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
85 # MSG Spec References
34 # Message Spec References
86 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
87
36
88
89 class Reference(HasTraits):
37 class Reference(HasTraits):
90
38
91 """
39 """
@@ -101,14 +49,14 b' class Reference(HasTraits):'
101 def check(self, d):
49 def check(self, d):
102 """validate a dict against our traits"""
50 """validate a dict against our traits"""
103 for key in self.trait_names():
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 # FIXME: always allow None, probably not a good idea
53 # FIXME: always allow None, probably not a good idea
106 if d[key] is None:
54 if d[key] is None:
107 continue
55 continue
108 try:
56 try:
109 setattr(self, key, d[key])
57 setattr(self, key, d[key])
110 except TraitError as e:
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 class RMessage(Reference):
62 class RMessage(Reference):
@@ -133,14 +81,11 b' class ExecuteReply(Reference):'
133 status = Enum((u'ok', u'error'))
81 status = Enum((u'ok', u'error'))
134
82
135 def check(self, d):
83 def check(self, d):
136 for tst in Reference.check(self, d):
84 Reference.check(self, d)
137 yield tst
138 if d['status'] == 'ok':
85 if d['status'] == 'ok':
139 for tst in ExecuteReplyOkay().check(d):
86 ExecuteReplyOkay().check(d)
140 yield tst
141 elif d['status'] == 'error':
87 elif d['status'] == 'error':
142 for tst in ExecuteReplyError().check(d):
88 ExecuteReplyError().check(d)
143 yield tst
144
89
145
90
146 class ExecuteReplyOkay(Reference):
91 class ExecuteReplyOkay(Reference):
@@ -177,11 +122,9 b' class OInfoReply(Reference):'
177 source = Unicode()
122 source = Unicode()
178
123
179 def check(self, d):
124 def check(self, d):
180 for tst in Reference.check(self, d):
125 Reference.check(self, d)
181 yield tst
182 if d['argspec'] is not None:
126 if d['argspec'] is not None:
183 for tst in ArgSpec().check(d['argspec']):
127 ArgSpec().check(d['argspec'])
184 yield tst
185
128
186
129
187 class ArgSpec(Reference):
130 class ArgSpec(Reference):
@@ -212,10 +155,8 b' class KernelInfoReply(Reference):'
212
155
213 def _ipython_version_changed(self, name, old, new):
156 def _ipython_version_changed(self, name, old, new):
214 for v in new:
157 for v in new:
215 nt.assert_true(
158 assert isinstance(v, int) or isinstance(v, basestring), \
216 isinstance(v, int) or isinstance(v, basestring),
159 'expected int or string as version component, got {0!r}'.format(v)
217 'expected int or string as version component, got {0!r}'
218 .format(v))
219
160
220
161
221 # IOPub messages
162 # IOPub messages
@@ -241,8 +182,8 b' class DisplayData(Reference):'
241 data = Dict()
182 data = Dict()
242 def _data_changed(self, name, old, new):
183 def _data_changed(self, name, old, new):
243 for k,v in new.iteritems():
184 for k,v in new.iteritems():
244 nt.assert_true(mime_pat.match(k))
185 assert mime_pat.match(k)
245 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
186 nt.assert_is_instance(v, basestring)
246
187
247
188
248 class PyOut(Reference):
189 class PyOut(Reference):
@@ -250,8 +191,8 b' class PyOut(Reference):'
250 data = Dict()
191 data = Dict()
251 def _data_changed(self, name, old, new):
192 def _data_changed(self, name, old, new):
252 for k,v in new.iteritems():
193 for k,v in new.iteritems():
253 nt.assert_true(mime_pat.match(k))
194 assert mime_pat.match(k)
254 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
195 nt.assert_is_instance(v, basestring)
255
196
256
197
257 references = {
198 references = {
@@ -282,13 +223,12 b' def validate_message(msg, msg_type=None, parent=None):'
282 """
223 """
283 RMessage().check(msg)
224 RMessage().check(msg)
284 if msg_type:
225 if msg_type:
285 yield nt.assert_equal(msg['msg_type'], msg_type)
226 nt.assert_equal(msg['msg_type'], msg_type)
286 if parent:
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 content = msg['content']
229 content = msg['content']
289 ref = references[msg['msg_type']]
230 ref = references[msg['msg_type']]
290 for tst in ref.check(content):
231 ref.check(content)
291 yield tst
292
232
293
233
294 #-----------------------------------------------------------------------------
234 #-----------------------------------------------------------------------------
@@ -297,54 +237,47 b' def validate_message(msg, msg_type=None, parent=None):'
297
237
298 # Shell channel
238 # Shell channel
299
239
300 @dec.parametric
301 def test_execute():
240 def test_execute():
302 flush_channels()
241 flush_channels()
303
242
304 msg_id = KC.execute(code='x=1')
243 msg_id = KC.execute(code='x=1')
305 reply = KC.get_shell_msg(timeout=TIMEOUT)
244 reply = KC.get_shell_msg(timeout=TIMEOUT)
306 for tst in validate_message(reply, 'execute_reply', msg_id):
245 validate_message(reply, 'execute_reply', msg_id)
307 yield tst
308
246
309
247
310 @dec.parametric
311 def test_execute_silent():
248 def test_execute_silent():
312 flush_channels()
249 flush_channels()
313 msg_id, reply = execute(code='x=1', silent=True)
250 msg_id, reply = execute(code='x=1', silent=True)
314
251
315 # flush status=idle
252 # flush status=idle
316 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
253 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
317 for tst in validate_message(status, 'status', msg_id):
254 validate_message(status, 'status', msg_id)
318 yield tst
319 nt.assert_equal(status['content']['execution_state'], 'idle')
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 count = reply['execution_count']
258 count = reply['execution_count']
323
259
324 msg_id, reply = execute(code='x=2', silent=True)
260 msg_id, reply = execute(code='x=2', silent=True)
325
261
326 # flush status=idle
262 # flush status=idle
327 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
263 status = KC.iopub_channel.get_msg(timeout=TIMEOUT)
328 for tst in validate_message(status, 'status', msg_id):
264 validate_message(status, 'status', msg_id)
329 yield tst
265 nt.assert_equal(status['content']['execution_state'], 'idle')
330 yield 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 count_2 = reply['execution_count']
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 def test_execute_error():
272 def test_execute_error():
339 flush_channels()
273 flush_channels()
340
274
341 msg_id, reply = execute(code='1/0')
275 msg_id, reply = execute(code='1/0')
342 yield nt.assert_equal(reply['status'], 'error')
276 nt.assert_equal(reply['status'], 'error')
343 yield nt.assert_equal(reply['ename'], 'ZeroDivisionError')
277 nt.assert_equal(reply['ename'], 'ZeroDivisionError')
344
278
345 pyerr = KC.iopub_channel.get_msg(timeout=TIMEOUT)
279 pyerr = KC.iopub_channel.get_msg(timeout=TIMEOUT)
346 for tst in validate_message(pyerr, 'pyerr', msg_id):
280 validate_message(pyerr, 'pyerr', msg_id)
347 yield tst
348
281
349
282
350 def test_execute_inc():
283 def test_execute_inc():
@@ -405,17 +338,14 b' def test_user_expressions_fail():'
405 nt.assert_equal(foo['ename'], 'NameError')
338 nt.assert_equal(foo['ename'], 'NameError')
406
339
407
340
408 @dec.parametric
409 def test_oinfo():
341 def test_oinfo():
410 flush_channels()
342 flush_channels()
411
343
412 msg_id = KC.object_info('a')
344 msg_id = KC.object_info('a')
413 reply = KC.get_shell_msg(timeout=TIMEOUT)
345 reply = KC.get_shell_msg(timeout=TIMEOUT)
414 for tst in validate_message(reply, 'object_info_reply', msg_id):
346 validate_message(reply, 'object_info_reply', msg_id)
415 yield tst
416
347
417
348
418 @dec.parametric
419 def test_oinfo_found():
349 def test_oinfo_found():
420 flush_channels()
350 flush_channels()
421
351
@@ -423,15 +353,13 b' def test_oinfo_found():'
423
353
424 msg_id = KC.object_info('a')
354 msg_id = KC.object_info('a')
425 reply = KC.get_shell_msg(timeout=TIMEOUT)
355 reply = KC.get_shell_msg(timeout=TIMEOUT)
426 for tst in validate_message(reply, 'object_info_reply', msg_id):
356 validate_message(reply, 'object_info_reply', msg_id)
427 yield tst
428 content = reply['content']
357 content = reply['content']
429 yield nt.assert_true(content['found'])
358 assert content['found']
430 argspec = content['argspec']
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 def test_oinfo_detail():
363 def test_oinfo_detail():
436 flush_channels()
364 flush_channels()
437
365
@@ -439,28 +367,24 b' def test_oinfo_detail():'
439
367
440 msg_id = KC.object_info('ip.object_inspect', detail_level=2)
368 msg_id = KC.object_info('ip.object_inspect', detail_level=2)
441 reply = KC.get_shell_msg(timeout=TIMEOUT)
369 reply = KC.get_shell_msg(timeout=TIMEOUT)
442 for tst in validate_message(reply, 'object_info_reply', msg_id):
370 validate_message(reply, 'object_info_reply', msg_id)
443 yield tst
444 content = reply['content']
371 content = reply['content']
445 yield nt.assert_true(content['found'])
372 assert content['found']
446 argspec = content['argspec']
373 argspec = content['argspec']
447 yield nt.assert_true(isinstance(argspec, dict), "expected non-empty argspec dict, got %r" % argspec)
374 nt.assert_is_instance(argspec, dict, "expected non-empty argspec dict, got %r" % argspec)
448 yield nt.assert_equal(argspec['defaults'], [0])
375 nt.assert_equal(argspec['defaults'], [0])
449
376
450
377
451 @dec.parametric
452 def test_oinfo_not_found():
378 def test_oinfo_not_found():
453 flush_channels()
379 flush_channels()
454
380
455 msg_id = KC.object_info('dne')
381 msg_id = KC.object_info('dne')
456 reply = KC.get_shell_msg(timeout=TIMEOUT)
382 reply = KC.get_shell_msg(timeout=TIMEOUT)
457 for tst in validate_message(reply, 'object_info_reply', msg_id):
383 validate_message(reply, 'object_info_reply', msg_id)
458 yield tst
459 content = reply['content']
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 def test_complete():
388 def test_complete():
465 flush_channels()
389 flush_channels()
466
390
@@ -468,49 +392,42 b' def test_complete():'
468
392
469 msg_id = KC.complete('al', 'al', 2)
393 msg_id = KC.complete('al', 'al', 2)
470 reply = KC.get_shell_msg(timeout=TIMEOUT)
394 reply = KC.get_shell_msg(timeout=TIMEOUT)
471 for tst in validate_message(reply, 'complete_reply', msg_id):
395 validate_message(reply, 'complete_reply', msg_id)
472 yield tst
473 matches = reply['content']['matches']
396 matches = reply['content']['matches']
474 for name in ('alpha', 'albert'):
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 def test_kernel_info_request():
401 def test_kernel_info_request():
480 flush_channels()
402 flush_channels()
481
403
482 msg_id = KC.kernel_info()
404 msg_id = KC.kernel_info()
483 reply = KC.get_shell_msg(timeout=TIMEOUT)
405 reply = KC.get_shell_msg(timeout=TIMEOUT)
484 for tst in validate_message(reply, 'kernel_info_reply', msg_id):
406 validate_message(reply, 'kernel_info_reply', msg_id)
485 yield tst
486
407
487
408
488 # IOPub channel
409 # IOPub channel
489
410
490
411
491 @dec.parametric
492 def test_stream():
412 def test_stream():
493 flush_channels()
413 flush_channels()
494
414
495 msg_id, reply = execute("print('hi')")
415 msg_id, reply = execute("print('hi')")
496
416
497 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
417 stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT)
498 for tst in validate_message(stdout, 'stream', msg_id):
418 validate_message(stdout, 'stream', msg_id)
499 yield tst
500 content = stdout['content']
419 content = stdout['content']
501 yield nt.assert_equal(content['name'], u'stdout')
420 nt.assert_equal(content['name'], u'stdout')
502 yield nt.assert_equal(content['data'], u'hi\n')
421 nt.assert_equal(content['data'], u'hi\n')
503
422
504
423
505 @dec.parametric
506 def test_display_data():
424 def test_display_data():
507 flush_channels()
425 flush_channels()
508
426
509 msg_id, reply = execute("from IPython.core.display import display; display(1)")
427 msg_id, reply = execute("from IPython.core.display import display; display(1)")
510
428
511 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
429 display = KC.iopub_channel.get_msg(timeout=TIMEOUT)
512 for tst in validate_message(display, 'display_data', parent=msg_id):
430 validate_message(display, 'display_data', parent=msg_id)
513 yield tst
514 data = display['content']['data']
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 from IPython.testing import decorators as dec
7 from IPython.testing import decorators as dec
8
8
9 from IPython.config.loader import Config
9 from IPython.config.loader import Config
10 from IPython.utils.localinterfaces import LOCALHOST
10 from IPython.utils.localinterfaces import localhost
11 from IPython.kernel import KernelManager
11 from IPython.kernel import KernelManager
12 from IPython.kernel.multikernelmanager import MultiKernelManager
12 from IPython.kernel.multikernelmanager import MultiKernelManager
13
13
@@ -31,20 +31,13 b' class TestKernelManager(TestCase):'
31 self.assertTrue(kid in km)
31 self.assertTrue(kid in km)
32 self.assertTrue(kid in km.list_kernel_ids())
32 self.assertTrue(kid in km.list_kernel_ids())
33 self.assertEqual(len(km),1)
33 self.assertEqual(len(km),1)
34 km.restart_kernel(kid)
34 km.restart_kernel(kid, now=True)
35 self.assertTrue(km.is_alive(kid))
35 self.assertTrue(km.is_alive(kid))
36 self.assertTrue(kid in km.list_kernel_ids())
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 km.interrupt_kernel(kid)
37 km.interrupt_kernel(kid)
45 k = km.get_kernel(kid)
38 k = km.get_kernel(kid)
46 self.assertTrue(isinstance(k, KernelManager))
39 self.assertTrue(isinstance(k, KernelManager))
47 km.shutdown_kernel(kid)
40 km.shutdown_kernel(kid, now=True)
48 self.assertTrue(not kid in km)
41 self.assertTrue(not kid in km)
49
42
50 def _run_cinfo(self, km, transport, ip):
43 def _run_cinfo(self, km, transport, ip):
@@ -63,7 +56,7 b' class TestKernelManager(TestCase):'
63 self.assertTrue('hb_port' in cinfo)
56 self.assertTrue('hb_port' in cinfo)
64 stream = km.connect_hb(kid)
57 stream = km.connect_hb(kid)
65 stream.close()
58 stream.close()
66 km.shutdown_kernel(kid)
59 km.shutdown_kernel(kid, now=True)
67
60
68 def test_tcp_lifecycle(self):
61 def test_tcp_lifecycle(self):
69 km = self._get_tcp_km()
62 km = self._get_tcp_km()
@@ -71,7 +64,7 b' class TestKernelManager(TestCase):'
71
64
72 def test_tcp_cinfo(self):
65 def test_tcp_cinfo(self):
73 km = self._get_tcp_km()
66 km = self._get_tcp_km()
74 self._run_cinfo(km, 'tcp', LOCALHOST)
67 self._run_cinfo(km, 'tcp', localhost())
75
68
76 @dec.skip_win32
69 @dec.skip_win32
77 def test_ipc_lifecycle(self):
70 def test_ipc_lifecycle(self):
@@ -14,8 +14,6 b' Authors'
14
14
15 import nose.tools as nt
15 import nose.tools as nt
16
16
17 from IPython.testing import decorators as dec
18
19 from IPython.kernel import launcher, connect
17 from IPython.kernel import launcher, connect
20 from IPython import kernel
18 from IPython import kernel
21
19
@@ -23,25 +21,21 b' from IPython import kernel'
23 # Classes and functions
21 # Classes and functions
24 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
25
23
26 @dec.parametric
27 def test_kms():
24 def test_kms():
28 for base in ("", "Multi"):
25 for base in ("", "Multi"):
29 KM = base + "KernelManager"
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 def test_kcs():
29 def test_kcs():
34 for base in ("", "Blocking"):
30 for base in ("", "Blocking"):
35 KM = base + "KernelClient"
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 def test_launcher():
34 def test_launcher():
40 for name in launcher.__all__:
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 def test_connect():
38 def test_connect():
45 for name in connect.__all__:
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 import zmq
20 import zmq
21
21
22 from IPython.utils.localinterfaces import LOCALHOST
22 from IPython.utils.localinterfaces import localhost
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Code
25 # Code
@@ -29,7 +29,9 b' from IPython.utils.localinterfaces import LOCALHOST'
29 class Heartbeat(Thread):
29 class Heartbeat(Thread):
30 "A simple ping-pong style heartbeat that runs in a thread."
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 Thread.__init__(self)
35 Thread.__init__(self)
34 self.context = context
36 self.context = context
35 self.transport, self.ip, self.port = addr
37 self.transport, self.ip, self.port = addr
@@ -87,7 +87,7 b' class Kernel(Configurable):'
87 if self.shell is not None:
87 if self.shell is not None:
88 self.shell.user_module = new
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 def _user_ns_changed(self, name, old, new):
91 def _user_ns_changed(self, name, old, new):
92 if self.shell is not None:
92 if self.shell is not None:
93 self.shell.user_ns = new
93 self.shell.user_ns = new
@@ -131,6 +131,7 b' class Kernel(Configurable):'
131 # A reference to the Python builtin 'raw_input' function.
131 # A reference to the Python builtin 'raw_input' function.
132 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
132 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
133 _sys_raw_input = Any()
133 _sys_raw_input = Any()
134 _sys_eval_input = Any()
134
135
135 # set of aborted msg_ids
136 # set of aborted msg_ids
136 aborted = Set()
137 aborted = Set()
@@ -352,15 +353,18 b' class Kernel(Configurable):'
352 # raw_input in the user namespace.
353 # raw_input in the user namespace.
353 if content.get('allow_stdin', False):
354 if content.get('allow_stdin', False):
354 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
355 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
356 input = lambda prompt='': eval(raw_input(prompt))
355 else:
357 else:
356 raw_input = lambda prompt='' : self._no_raw_input()
358 raw_input = input = lambda prompt='' : self._no_raw_input()
357
359
358 if py3compat.PY3:
360 if py3compat.PY3:
359 self._sys_raw_input = __builtin__.input
361 self._sys_raw_input = __builtin__.input
360 __builtin__.input = raw_input
362 __builtin__.input = raw_input
361 else:
363 else:
362 self._sys_raw_input = __builtin__.raw_input
364 self._sys_raw_input = __builtin__.raw_input
365 self._sys_eval_input = __builtin__.input
363 __builtin__.raw_input = raw_input
366 __builtin__.raw_input = raw_input
367 __builtin__.input = input
364
368
365 # Set the parent message of the display hook and out streams.
369 # Set the parent message of the display hook and out streams.
366 shell.displayhook.set_parent(parent)
370 shell.displayhook.set_parent(parent)
@@ -403,6 +407,7 b' class Kernel(Configurable):'
403 __builtin__.input = self._sys_raw_input
407 __builtin__.input = self._sys_raw_input
404 else:
408 else:
405 __builtin__.raw_input = self._sys_raw_input
409 __builtin__.raw_input = self._sys_raw_input
410 __builtin__.input = self._sys_eval_input
406
411
407 reply_content[u'status'] = status
412 reply_content[u'status'] = status
408
413
@@ -39,7 +39,7 b' from IPython.core.shellapp import ('
39 InteractiveShellApp, shell_flags, shell_aliases
39 InteractiveShellApp, shell_flags, shell_aliases
40 )
40 )
41 from IPython.utils import io
41 from IPython.utils import io
42 from IPython.utils.localinterfaces import LOCALHOST
42 from IPython.utils.localinterfaces import localhost
43 from IPython.utils.path import filefind
43 from IPython.utils.path import filefind
44 from IPython.utils.py3compat import str_to_bytes
44 from IPython.utils.py3compat import str_to_bytes
45 from IPython.utils.traitlets import (
45 from IPython.utils.traitlets import (
@@ -156,7 +156,8 b' class IPKernelApp(BaseIPythonApplication, InteractiveShellApp):'
156 else:
156 else:
157 return 'kernel-ipc'
157 return 'kernel-ipc'
158 else:
158 else:
159 return LOCALHOST
159 return localhost()
160
160 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
161 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
161 shell_port = Integer(0, config=True, help="set the shell (ROUTER) port [default: random]")
162 shell_port = Integer(0, config=True, help="set the shell (ROUTER) port [default: random]")
162 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
163 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
@@ -436,8 +436,15 b' class Session(Configurable):'
436 msg = dict(a=[1,'hi'])
436 msg = dict(a=[1,'hi'])
437 try:
437 try:
438 packed = pack(msg)
438 packed = pack(msg)
439 except Exception:
439 except Exception as e:
440 raise ValueError("packer could not serialize a simple message")
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 # ensure packed message is bytes
449 # ensure packed message is bytes
443 if not isinstance(packed, bytes):
450 if not isinstance(packed, bytes):
@@ -446,8 +453,16 b' class Session(Configurable):'
446 # check that unpack is pack's inverse
453 # check that unpack is pack's inverse
447 try:
454 try:
448 unpacked = unpack(packed)
455 unpacked = unpack(packed)
449 except Exception:
456 assert unpacked == msg
450 raise ValueError("unpacker could not handle the packer's output")
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 # check datetime support
467 # check datetime support
453 msg = dict(t=datetime.now())
468 msg = dict(t=datetime.now())
@@ -46,7 +46,6 b" DTYPES = ('uint8', 'float64', 'int32', [('g', 'float32')], '|S10')"
46 # Tests
46 # Tests
47 #-------------------------------------------------------------------------------
47 #-------------------------------------------------------------------------------
48
48
49 @dec.parametric
50 def test_roundtrip_simple():
49 def test_roundtrip_simple():
51 for obj in [
50 for obj in [
52 'hello',
51 'hello',
@@ -55,18 +54,16 b' def test_roundtrip_simple():'
55 (b'123', 'hello'),
54 (b'123', 'hello'),
56 ]:
55 ]:
57 obj2 = roundtrip(obj)
56 obj2 = roundtrip(obj)
58 yield nt.assert_equals(obj, obj2)
57 nt.assert_equal(obj, obj2)
59
58
60 @dec.parametric
61 def test_roundtrip_nested():
59 def test_roundtrip_nested():
62 for obj in [
60 for obj in [
63 dict(a=range(5), b={1:b'hello'}),
61 dict(a=range(5), b={1:b'hello'}),
64 [range(5),[range(3),(1,[b'whoda'])]],
62 [range(5),[range(3),(1,[b'whoda'])]],
65 ]:
63 ]:
66 obj2 = roundtrip(obj)
64 obj2 = roundtrip(obj)
67 yield nt.assert_equals(obj, obj2)
65 nt.assert_equal(obj, obj2)
68
66
69 @dec.parametric
70 def test_roundtrip_buffered():
67 def test_roundtrip_buffered():
71 for obj in [
68 for obj in [
72 dict(a=b"x"*1025),
69 dict(a=b"x"*1025),
@@ -74,10 +71,10 b' def test_roundtrip_buffered():'
74 [b"hello"*501, 1,2,3]
71 [b"hello"*501, 1,2,3]
75 ]:
72 ]:
76 bufs = serialize_object(obj)
73 bufs = serialize_object(obj)
77 yield nt.assert_equals(len(bufs), 2)
74 nt.assert_equal(len(bufs), 2)
78 obj2, remainder = unserialize_object(bufs)
75 obj2, remainder = unserialize_object(bufs)
79 yield nt.assert_equals(remainder, [])
76 nt.assert_equal(remainder, [])
80 yield nt.assert_equals(obj, obj2)
77 nt.assert_equal(obj, obj2)
81
78
82 def _scrub_nan(A):
79 def _scrub_nan(A):
83 """scrub nans out of empty arrays
80 """scrub nans out of empty arrays
@@ -93,7 +90,6 b' def _scrub_nan(A):'
93 # e.g. str dtype
90 # e.g. str dtype
94 pass
91 pass
95
92
96 @dec.parametric
97 @dec.skip_without('numpy')
93 @dec.skip_without('numpy')
98 def test_numpy():
94 def test_numpy():
99 import numpy
95 import numpy
@@ -104,12 +100,11 b' def test_numpy():'
104 _scrub_nan(A)
100 _scrub_nan(A)
105 bufs = serialize_object(A)
101 bufs = serialize_object(A)
106 B, r = unserialize_object(bufs)
102 B, r = unserialize_object(bufs)
107 yield nt.assert_equals(r, [])
103 nt.assert_equal(r, [])
108 yield nt.assert_equals(A.shape, B.shape)
104 nt.assert_equal(A.shape, B.shape)
109 yield nt.assert_equals(A.dtype, B.dtype)
105 nt.assert_equal(A.dtype, B.dtype)
110 yield assert_array_equal(A,B)
106 assert_array_equal(A,B)
111
107
112 @dec.parametric
113 @dec.skip_without('numpy')
108 @dec.skip_without('numpy')
114 def test_recarray():
109 def test_recarray():
115 import numpy
110 import numpy
@@ -124,12 +119,11 b' def test_recarray():'
124
119
125 bufs = serialize_object(A)
120 bufs = serialize_object(A)
126 B, r = unserialize_object(bufs)
121 B, r = unserialize_object(bufs)
127 yield nt.assert_equals(r, [])
122 nt.assert_equal(r, [])
128 yield nt.assert_equals(A.shape, B.shape)
123 nt.assert_equal(A.shape, B.shape)
129 yield nt.assert_equals(A.dtype, B.dtype)
124 nt.assert_equal(A.dtype, B.dtype)
130 yield assert_array_equal(A,B)
125 assert_array_equal(A,B)
131
126
132 @dec.parametric
133 @dec.skip_without('numpy')
127 @dec.skip_without('numpy')
134 def test_numpy_in_seq():
128 def test_numpy_in_seq():
135 import numpy
129 import numpy
@@ -140,15 +134,14 b' def test_numpy_in_seq():'
140 _scrub_nan(A)
134 _scrub_nan(A)
141 bufs = serialize_object((A,1,2,b'hello'))
135 bufs = serialize_object((A,1,2,b'hello'))
142 canned = pickle.loads(bufs[0])
136 canned = pickle.loads(bufs[0])
143 yield nt.assert_true(canned[0], CannedArray)
137 nt.assert_is_instance(canned[0], CannedArray)
144 tup, r = unserialize_object(bufs)
138 tup, r = unserialize_object(bufs)
145 B = tup[0]
139 B = tup[0]
146 yield nt.assert_equals(r, [])
140 nt.assert_equal(r, [])
147 yield nt.assert_equals(A.shape, B.shape)
141 nt.assert_equal(A.shape, B.shape)
148 yield nt.assert_equals(A.dtype, B.dtype)
142 nt.assert_equal(A.dtype, B.dtype)
149 yield assert_array_equal(A,B)
143 assert_array_equal(A,B)
150
144
151 @dec.parametric
152 @dec.skip_without('numpy')
145 @dec.skip_without('numpy')
153 def test_numpy_in_dict():
146 def test_numpy_in_dict():
154 import numpy
147 import numpy
@@ -159,27 +152,25 b' def test_numpy_in_dict():'
159 _scrub_nan(A)
152 _scrub_nan(A)
160 bufs = serialize_object(dict(a=A,b=1,c=range(20)))
153 bufs = serialize_object(dict(a=A,b=1,c=range(20)))
161 canned = pickle.loads(bufs[0])
154 canned = pickle.loads(bufs[0])
162 yield nt.assert_true(canned['a'], CannedArray)
155 nt.assert_is_instance(canned['a'], CannedArray)
163 d, r = unserialize_object(bufs)
156 d, r = unserialize_object(bufs)
164 B = d['a']
157 B = d['a']
165 yield nt.assert_equals(r, [])
158 nt.assert_equal(r, [])
166 yield nt.assert_equals(A.shape, B.shape)
159 nt.assert_equal(A.shape, B.shape)
167 yield nt.assert_equals(A.dtype, B.dtype)
160 nt.assert_equal(A.dtype, B.dtype)
168 yield assert_array_equal(A,B)
161 assert_array_equal(A,B)
169
162
170 @dec.parametric
171 def test_class():
163 def test_class():
172 @interactive
164 @interactive
173 class C(object):
165 class C(object):
174 a=5
166 a=5
175 bufs = serialize_object(dict(C=C))
167 bufs = serialize_object(dict(C=C))
176 canned = pickle.loads(bufs[0])
168 canned = pickle.loads(bufs[0])
177 yield nt.assert_true(canned['C'], CannedClass)
169 nt.assert_is_instance(canned['C'], CannedClass)
178 d, r = unserialize_object(bufs)
170 d, r = unserialize_object(bufs)
179 C2 = d['C']
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 def test_class_oldstyle():
174 def test_class_oldstyle():
184 @interactive
175 @interactive
185 class C:
176 class C:
@@ -187,42 +178,38 b' def test_class_oldstyle():'
187
178
188 bufs = serialize_object(dict(C=C))
179 bufs = serialize_object(dict(C=C))
189 canned = pickle.loads(bufs[0])
180 canned = pickle.loads(bufs[0])
190 yield nt.assert_true(isinstance(canned['C'], CannedClass))
181 nt.assert_is_instance(canned['C'], CannedClass)
191 d, r = unserialize_object(bufs)
182 d, r = unserialize_object(bufs)
192 C2 = d['C']
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 def test_tuple():
186 def test_tuple():
197 tup = (lambda x:x, 1)
187 tup = (lambda x:x, 1)
198 bufs = serialize_object(tup)
188 bufs = serialize_object(tup)
199 canned = pickle.loads(bufs[0])
189 canned = pickle.loads(bufs[0])
200 yield nt.assert_true(isinstance(canned, tuple))
190 nt.assert_is_instance(canned, tuple)
201 t2, r = unserialize_object(bufs)
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 point = namedtuple('point', 'x y')
194 point = namedtuple('point', 'x y')
205
195
206 @dec.parametric
207 def test_namedtuple():
196 def test_namedtuple():
208 p = point(1,2)
197 p = point(1,2)
209 bufs = serialize_object(p)
198 bufs = serialize_object(p)
210 canned = pickle.loads(bufs[0])
199 canned = pickle.loads(bufs[0])
211 yield nt.assert_true(isinstance(canned, point))
200 nt.assert_is_instance(canned, point)
212 p2, r = unserialize_object(bufs, globals())
201 p2, r = unserialize_object(bufs, globals())
213 yield nt.assert_equal(p2.x, p.x)
202 nt.assert_equal(p2.x, p.x)
214 yield nt.assert_equal(p2.y, p.y)
203 nt.assert_equal(p2.y, p.y)
215
204
216 @dec.parametric
217 def test_list():
205 def test_list():
218 lis = [lambda x:x, 1]
206 lis = [lambda x:x, 1]
219 bufs = serialize_object(lis)
207 bufs = serialize_object(lis)
220 canned = pickle.loads(bufs[0])
208 canned = pickle.loads(bufs[0])
221 yield nt.assert_true(isinstance(canned, list))
209 nt.assert_is_instance(canned, list)
222 l2, r = unserialize_object(bufs)
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 def test_class_inheritance():
213 def test_class_inheritance():
227 @interactive
214 @interactive
228 class C(object):
215 class C(object):
@@ -234,8 +221,8 b' def test_class_inheritance():'
234
221
235 bufs = serialize_object(dict(D=D))
222 bufs = serialize_object(dict(D=D))
236 canned = pickle.loads(bufs[0])
223 canned = pickle.loads(bufs[0])
237 yield nt.assert_true(canned['D'], CannedClass)
224 nt.assert_is_instance(canned['D'], CannedClass)
238 d, r = unserialize_object(bufs)
225 d, r = unserialize_object(bufs)
239 D2 = d['D']
226 D2 = d['D']
240 yield nt.assert_equal(D2.a, D.a)
227 nt.assert_equal(D2.a, D.a)
241 yield nt.assert_equal(D2.b, D.b)
228 nt.assert_equal(D2.b, D.b)
@@ -20,6 +20,12 b' from zmq.eventloop.zmqstream import ZMQStream'
20
20
21 from IPython.kernel.zmq import session as ss
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 class SessionTestCase(BaseZMQTestCase):
29 class SessionTestCase(BaseZMQTestCase):
24
30
25 def setUp(self):
31 def setUp(self):
@@ -222,4 +228,44 b' class TestSession(SessionTestCase):'
222 self.assertTrue(len(session.digest_history) == 100)
228 self.assertTrue(len(session.digest_history) == 100)
223 session._add_digest(uuid.uuid4().bytes)
229 session._add_digest(uuid.uuid4().bytes)
224 self.assertTrue(len(session.digest_history) == 91)
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 cmd = ('from IPython import start_kernel\n'
8 cmd = ('from IPython import start_kernel\n'
9 'ns = {"tre": 123}\n'
9 'ns = {"tre": 123}\n'
10 'start_kernel(user_ns=ns)')
10 'start_kernel(user_ns=ns)')
11
11
12 with setup_kernel(cmd) as client:
12 with setup_kernel(cmd) as client:
13 msg_id = client.object_info('tre')
13 msg_id = client.object_info('tre')
14 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
14 msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
15 content = msg['content']
15 content = msg['content']
16 assert content['found']
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 parent=self.parent_header, ident=self.topic,
86 parent=self.parent_header, ident=self.topic,
87 )
87 )
88
88
89 def clear_output(self, stdout=True, stderr=True, other=True):
89 def clear_output(self, wait=False):
90 content = dict(stdout=stdout, stderr=stderr, other=other)
90 content = dict(wait=wait)
91
91
92 if stdout:
92 print('\r', file=sys.stdout, end='')
93 print('\r', file=sys.stdout, end='')
93 print('\r', file=sys.stderr, end='')
94 if stderr:
95 print('\r', file=sys.stderr, end='')
96
97 self._flush_streams()
94 self._flush_streams()
98
95
99 self.session.send(
96 self.session.send(
@@ -14,14 +14,14 b' The classes are (see their docstrings for further details):'
14 - Demo: pure python demos
14 - Demo: pure python demos
15
15
16 - IPythonDemo: demos with input to be processed by IPython as if it had been
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
17 typed interactively (so magics work, as well as any other special syntax you
18 may have added via input prefilters).
18 may have added via input prefilters).
19
19
20 - LineDemo: single-line version of the Demo class. These demos are executed
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 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
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 - ClearMixin: mixin to make Demo classes with less visual clutter. It
26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
27 declares an empty marquee and a pre_cmd that clears the screen before each
27 declares an empty marquee and a pre_cmd that clears the screen before each
@@ -214,18 +214,18 b' class Demo(object):'
214 Optional inputs:
214 Optional inputs:
215
215
216 - title: a string to use as the demo name. Of most use when the demo
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
217 you are making comes from an object that has no filename, or if you
218 want an alternate denotation distinct from the filename.
218 want an alternate denotation distinct from the filename.
219
219
220 - arg_str(''): a string of arguments, internally converted to a list
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
221 just like sys.argv, so the demo script can see a similar
222 environment.
222 environment.
223
223
224 - auto_all(None): global flag to run all blocks automatically without
224 - auto_all(None): global flag to run all blocks automatically without
225 confirmation. This attribute overrides the block-level tags and
225 confirmation. This attribute overrides the block-level tags and
226 applies to the whole demo. It is an attribute of the object, 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
227 can be changed at runtime simply by reassigning it to a boolean
228 value.
228 value.
229 """
229 """
230 if hasattr(src, "read"):
230 if hasattr(src, "read"):
231 # It seems to be a file or a file-like object
231 # It seems to be a file or a file-like object
@@ -481,6 +481,19 b' set_inputhook = inputhook_manager.set_inputhook'
481 current_gui = inputhook_manager.current_gui
481 current_gui = inputhook_manager.current_gui
482 clear_app_refs = inputhook_manager.clear_app_refs
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 # Convenience function to switch amongst them
498 # Convenience function to switch amongst them
486 def enable_gui(gui=None, app=None):
499 def enable_gui(gui=None, app=None):
@@ -507,18 +520,6 b' def enable_gui(gui=None, app=None):'
507 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
520 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
508 one.
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 try:
523 try:
523 gui_hook = guis[gui]
524 gui_hook = guis[gui]
524 except KeyError:
525 except KeyError:
@@ -93,9 +93,9 b' class InteractiveRunner(object):'
93 - program: command to execute the given program.
93 - program: command to execute the given program.
94
94
95 - prompts: a list of patterns to match as valid prompts, in the
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
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
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).
98 (it must be a true list, as pexpect does type checks).
99
99
100 If more than one prompt is given, the first is treated as the main
100 If more than one prompt is given, the first is treated as the main
101 program prompt and the others as 'continuation' prompts, like
101 program prompt and the others as 'continuation' prompts, like
@@ -107,19 +107,19 b' class InteractiveRunner(object):'
107 Optional inputs:
107 Optional inputs:
108
108
109 - args(None): optional list of strings to pass as arguments to the
109 - args(None): optional list of strings to pass as arguments to the
110 child program.
110 child program.
111
111
112 - out(sys.stdout): if given, an output stream to be used when writing
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 Public members not parameterized in the constructor:
115 Public members not parameterized in the constructor:
116
116
117 - delaybeforesend(0): Newer versions of pexpect have a delay before
117 - delaybeforesend(0): Newer versions of pexpect have a delay before
118 sending each new input. For our purposes here, it's typically best
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
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
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
121 increase this value (it is measured in seconds). Note that this
122 variable is not honored at all by older versions of pexpect.
122 variable is not honored at all by older versions of pexpect.
123 """
123 """
124
124
125 self.program = program
125 self.program = program
@@ -154,7 +154,7 b' class InteractiveRunner(object):'
154
154
155 Inputs:
155 Inputs:
156
156
157 -fname: name of the file to execute.
157 - fname: name of the file to execute.
158
158
159 See the run_source docstring for the meaning of the optional
159 See the run_source docstring for the meaning of the optional
160 arguments."""
160 arguments."""
@@ -173,15 +173,15 b' class InteractiveRunner(object):'
173 Inputs:
173 Inputs:
174
174
175 - source: a string of code to be executed, or an open file object we
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 Optional inputs:
178 Optional inputs:
179
179
180 - interact(False): if true, start to interact with the running
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 - get_output(False): if true, capture the output of the child process
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 Returns:
186 Returns:
187 A string containing the process output, but only if requested.
187 A string containing the process output, but only if requested.
@@ -229,7 +229,17 b' class PrettyPrinter(_PrettyPrinterBase):'
229 self.buffer.append(Breakable(sep, width, self))
229 self.buffer.append(Breakable(sep, width, self))
230 self.buffer_width += width
230 self.buffer_width += width
231 self._break_outer_groups()
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 def begin_group(self, indent=0, open=''):
244 def begin_group(self, indent=0, open=''):
235 """
245 """
@@ -478,8 +488,12 b' def _default_pprint(obj, p, cycle):'
478 """
488 """
479 klass = getattr(obj, '__class__', None) or type(obj)
489 klass = getattr(obj, '__class__', None) or type(obj)
480 if getattr(klass, '__repr__', None) not in _baseclass_reprs:
490 if getattr(klass, '__repr__', None) not in _baseclass_reprs:
481 # A user-provided repr.
491 # A user-provided repr. Find newlines and replace them with p.break_()
482 p.text(repr(obj))
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 return
497 return
484 p.begin_group(1, '<')
498 p.begin_group(1, '<')
485 p.pretty(klass)
499 p.pretty(klass)
@@ -58,6 +58,23 b' class NoModule(object):'
58
58
59 NoModule.__module__ = None
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 def test_indentation():
79 def test_indentation():
63 """Test correct indentation in groups"""
80 """Test correct indentation in groups"""
@@ -118,3 +135,19 b' def test_pprint_nomod():'
118 """
135 """
119 output = pretty.pretty(NoModule)
136 output = pretty.pretty(NoModule)
120 nt.assert_equal(output, 'NoModule')
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 from .export import *
1 from .export import *
2 from .html import HTMLExporter
2 from .html import HTMLExporter
3 from .slides import SlidesExporter
3 from .slides import SlidesExporter
4 from .exporter import Exporter
4 from .templateexporter import TemplateExporter
5 from .latex import LatexExporter
5 from .latex import LatexExporter
6 from .markdown import MarkdownExporter
6 from .markdown import MarkdownExporter
7 from .python import PythonExporter
7 from .python import PythonExporter
8 from .rst import RSTExporter
8 from .rst import RSTExporter
9 from .exporter import Exporter
@@ -19,6 +19,7 b' from IPython.nbformat.v3.nbbase import NotebookNode'
19 from IPython.config import Config
19 from IPython.config import Config
20
20
21 from .exporter import Exporter
21 from .exporter import Exporter
22 from .templateexporter import TemplateExporter
22 from .html import HTMLExporter
23 from .html import HTMLExporter
23 from .slides import SlidesExporter
24 from .slides import SlidesExporter
24 from .latex import LatexExporter
25 from .latex import LatexExporter
@@ -122,7 +123,7 b' def export(exporter, nb, **kw):'
122 return output, resources
123 return output, resources
123
124
124 exporter_map = dict(
125 exporter_map = dict(
125 custom=Exporter,
126 custom=TemplateExporter,
126 html=HTMLExporter,
127 html=HTMLExporter,
127 slides=SlidesExporter,
128 slides=SlidesExporter,
128 latex=LatexExporter,
129 latex=LatexExporter,
@@ -19,56 +19,21 b' from __future__ import print_function, absolute_import'
19 # Stdlib imports
19 # Stdlib imports
20 import io
20 import io
21 import os
21 import os
22 import inspect
23 import copy
22 import copy
24 import collections
23 import collections
25 import datetime
24 import datetime
26
25
27 # other libs/dependencies
28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader, TemplateNotFound
29
26
30 # IPython imports
27 # IPython imports
31 from IPython.config.configurable import LoggingConfigurable
28 from IPython.config.configurable import LoggingConfigurable
32 from IPython.config import Config
29 from IPython.config import Config
33 from IPython.nbformat import current as nbformat
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 from IPython.utils.importstring import import_item
32 from IPython.utils.importstring import import_item
36 from IPython.utils.text import indent
33 from IPython.utils import text, py3compat
37 from IPython.utils import py3compat
38
34
39 from IPython.nbconvert import preprocessors as nbpreprocessors
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 # Class
39 # Class
@@ -81,70 +46,21 b' class ResourcesDict(collections.defaultdict):'
81
46
82 class Exporter(LoggingConfigurable):
47 class Exporter(LoggingConfigurable):
83 """
48 """
84 Exports notebooks into other file formats. Uses Jinja 2 templating engine
49 Class containing methods that sequentially run a list of preprocessors on a
85 to output new formats. Inherit from this class if you are creating a new
50 NotebookNode object and then return the modified NotebookNode object and
86 template type along with new filters/preprocessors. If the filters/
51 accompanying resources dict.
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}
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 file_extension = Unicode(
54 file_extension = Unicode(
114 'txt', config=True,
55 'txt', config=True,
115 help="Extension of the file that should be written to disk"
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 #Configurability, allows the user to easily add filters and preprocessors.
59 #Configurability, allows the user to easily add filters and preprocessors.
142 preprocessors = List(config=True,
60 preprocessors = List(config=True,
143 help="""List of preprocessors, by name or namespace, to enable.""")
61 help="""List of preprocessors, by name or namespace, to enable.""")
144
62
145 filters = Dict(config=True,
63 _preprocessors = None
146 help="""Dictionary of filters, by name and namespace, to add to the Jinja
147 environment.""")
148
64
149 default_preprocessors = List([nbpreprocessors.coalesce_streams,
65 default_preprocessors = List([nbpreprocessors.coalesce_streams,
150 nbpreprocessors.SVG2PDFPreprocessor,
66 nbpreprocessors.SVG2PDFPreprocessor,
@@ -152,42 +68,34 b' class Exporter(LoggingConfigurable):'
152 nbpreprocessors.CSSHTMLHeaderPreprocessor,
68 nbpreprocessors.CSSHTMLHeaderPreprocessor,
153 nbpreprocessors.RevealHelpPreprocessor,
69 nbpreprocessors.RevealHelpPreprocessor,
154 nbpreprocessors.LatexPreprocessor,
70 nbpreprocessors.LatexPreprocessor,
155 nbpreprocessors.SphinxPreprocessor],
71 nbpreprocessors.HighlightMagicsPreprocessor],
156 config=True,
72 config=True,
157 help="""List of preprocessors available by default, by name, namespace,
73 help="""List of preprocessors available by default, by name, namespace,
158 instance, or type.""")
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 Public constructor
79 Public constructor
164
80
165 Parameters
81 Parameters
166 ----------
82 ----------
167 config : config
83 config : config
168 User configuration instance.
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 if not config:
86 if not config:
176 config = self.default_config
87 config = self.default_config
177
88
178 super(Exporter, self).__init__(config=config, **kw)
89 super(Exporter, self).__init__(config=config, **kw)
179
90
180 #Init
91 #Init
181 self._init_template()
182 self._init_environment(extra_loaders=extra_loaders)
183 self._init_preprocessors()
92 self._init_preprocessors()
184 self._init_filters()
185
93
186
94
187 @property
95 @property
188 def default_config(self):
96 def default_config(self):
189 return Config()
97 return Config()
190
98
191 def _config_changed(self, name, old, new):
99 def _config_changed(self, name, old, new):
192 """When setting config, make sure to start with our default_config"""
100 """When setting config, make sure to start with our default_config"""
193 c = self.default_config
101 c = self.default_config
@@ -196,56 +104,18 b' class Exporter(LoggingConfigurable):'
196 if c != old:
104 if c != old:
197 self.config = c
105 self.config = c
198 super(Exporter, self)._config_changed(name, old, c)
106 super(Exporter, self)._config_changed(name, old, c)
199
107
200
108
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
239 def from_notebook_node(self, nb, resources=None, **kw):
109 def from_notebook_node(self, nb, resources=None, **kw):
240 """
110 """
241 Convert a notebook from a notebook node instance.
111 Convert a notebook from a notebook node instance.
242
112
243 Parameters
113 Parameters
244 ----------
114 ----------
245 nb : Notebook node
115 nb : Notebook node
246 resources : dict (**kw)
116 resources : dict (**kw)
247 of additional resources that can be accessed read/write by
117 of additional resources that can be accessed read/write by
248 preprocessors and filters.
118 preprocessors.
249 """
119 """
250 nb_copy = copy.deepcopy(nb)
120 nb_copy = copy.deepcopy(nb)
251 resources = self._init_resources(resources)
121 resources = self._init_resources(resources)
@@ -253,26 +123,20 b' class Exporter(LoggingConfigurable):'
253 # Preprocess
123 # Preprocess
254 nb_copy, resources = self._preprocess(nb_copy, resources)
124 nb_copy, resources = self._preprocess(nb_copy, resources)
255
125
256 self._load_template()
126 return nb_copy, resources
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
263
127
264
128
265 def from_filename(self, filename, resources=None, **kw):
129 def from_filename(self, filename, resources=None, **kw):
266 """
130 """
267 Convert a notebook from a notebook file.
131 Convert a notebook from a notebook file.
268
132
269 Parameters
133 Parameters
270 ----------
134 ----------
271 filename : str
135 filename : str
272 Full filename of the notebook file to open and convert.
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 if resources is None:
140 if resources is None:
277 resources = ResourcesDict()
141 resources = ResourcesDict()
278 if not 'metadata' in resources or resources['metadata'] == '':
142 if not 'metadata' in resources or resources['metadata'] == '':
@@ -282,16 +146,16 b' class Exporter(LoggingConfigurable):'
282 resources['metadata']['name'] = notebook_name
146 resources['metadata']['name'] = notebook_name
283
147
284 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
148 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
285 resources['metadata']['modified_date'] = modified_date.strftime("%B %d, %Y")
149 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
286
150
287 with io.open(filename) as f:
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 def from_file(self, file_stream, resources=None, **kw):
155 def from_file(self, file_stream, resources=None, **kw):
292 """
156 """
293 Convert a notebook from a notebook file.
157 Convert a notebook from a notebook file.
294
158
295 Parameters
159 Parameters
296 ----------
160 ----------
297 file_stream : file-like object
161 file_stream : file-like object
@@ -304,10 +168,10 b' class Exporter(LoggingConfigurable):'
304 """
168 """
305 Register a preprocessor.
169 Register a preprocessor.
306 Preprocessors are classes that act upon the notebook before it is
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 capable of passing additional information to the Jinja
172 capable of passing additional information to the Jinja
309 templating engine.
173 templating engine.
310
174
311 Parameters
175 Parameters
312 ----------
176 ----------
313 preprocessor : preprocessor
177 preprocessor : preprocessor
@@ -317,130 +181,43 b' class Exporter(LoggingConfigurable):'
317 isclass = isinstance(preprocessor, type)
181 isclass = isinstance(preprocessor, type)
318 constructed = not isclass
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 if constructed and isinstance(preprocessor, py3compat.string_types):
185 if constructed and isinstance(preprocessor, py3compat.string_types):
322 #Preprocessor is a string, import the namespace and recursively call
186 # Preprocessor is a string, import the namespace and recursively call
323 #this register_preprocessor method
187 # this register_preprocessor method
324 preprocessor_cls = import_item(preprocessor)
188 preprocessor_cls = import_item(preprocessor)
325 return self.register_preprocessor(preprocessor_cls, enabled)
189 return self.register_preprocessor(preprocessor_cls, enabled)
326
190
327 if constructed and hasattr(preprocessor, '__call__'):
191 if constructed and hasattr(preprocessor, '__call__'):
328 #Preprocessor is a function, no need to construct it.
192 # Preprocessor is a function, no need to construct it.
329 #Register and return the preprocessor.
193 # Register and return the preprocessor.
330 if enabled:
194 if enabled:
331 preprocessor.enabled = True
195 preprocessor.enabled = True
332 self._preprocessors.append(preprocessor)
196 self._preprocessors.append(preprocessor)
333 return preprocessor
197 return preprocessor
334
198
335 elif isclass and isinstance(preprocessor, MetaHasTraits):
199 elif isclass and isinstance(preprocessor, MetaHasTraits):
336 #Preprocessor is configurable. Make sure to pass in new default for
200 # Preprocessor is configurable. Make sure to pass in new default for
337 #the enabled flag if one was specified.
201 # the enabled flag if one was specified.
338 self.register_preprocessor(preprocessor(parent=self), enabled)
202 self.register_preprocessor(preprocessor(parent=self), enabled)
339
203
340 elif isclass:
204 elif isclass:
341 #Preprocessor is not configurable, construct it
205 # Preprocessor is not configurable, construct it
342 self.register_preprocessor(preprocessor(), enabled)
206 self.register_preprocessor(preprocessor(), enabled)
343
207
344 else:
208 else:
345 #Preprocessor is an instance of something without a __call__
209 # Preprocessor is an instance of something without a __call__
346 #attribute.
210 # attribute.
347 raise TypeError('preprocessor')
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 def _init_preprocessors(self):
214 def _init_preprocessors(self):
439 """
215 """
440 Register all of the preprocessors needed for this exporter, disabled
216 Register all of the preprocessors needed for this exporter, disabled
441 unless specified explicitly.
217 unless specified explicitly.
442 """
218 """
443 self._preprocessors = []
219 if self._preprocessors is None:
220 self._preprocessors = []
444
221
445 #Load default preprocessors (not necessarly enabled by default).
222 #Load default preprocessors (not necessarly enabled by default).
446 if self.default_preprocessors:
223 if self.default_preprocessors:
@@ -451,21 +228,6 b' class Exporter(LoggingConfigurable):'
451 if self.preprocessors:
228 if self.preprocessors:
452 for preprocessor in self.preprocessors:
229 for preprocessor in self.preprocessors:
453 self.register_preprocessor(preprocessor, enabled=True)
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 def _init_resources(self, resources):
233 def _init_resources(self, resources):
@@ -476,7 +238,7 b' class Exporter(LoggingConfigurable):'
476 if not isinstance(resources, ResourcesDict):
238 if not isinstance(resources, ResourcesDict):
477 new_resources = ResourcesDict()
239 new_resources = ResourcesDict()
478 new_resources.update(resources)
240 new_resources.update(resources)
479 resources = new_resources
241 resources = new_resources
480
242
481 #Make sure the metadata extension exists in resources
243 #Make sure the metadata extension exists in resources
482 if 'metadata' in resources:
244 if 'metadata' in resources:
@@ -484,29 +246,28 b' class Exporter(LoggingConfigurable):'
484 resources['metadata'] = ResourcesDict(resources['metadata'])
246 resources['metadata'] = ResourcesDict(resources['metadata'])
485 else:
247 else:
486 resources['metadata'] = ResourcesDict()
248 resources['metadata'] = ResourcesDict()
487 if not resources['metadata']['name']:
249 if not resources['metadata']['name']:
488 resources['metadata']['name'] = 'Notebook'
250 resources['metadata']['name'] = 'Notebook'
489
251
490 #Set the output extension
252 #Set the output extension
491 resources['output_extension'] = self.file_extension
253 resources['output_extension'] = self.file_extension
492 return resources
254 return resources
493
255
494
256
495 def _preprocess(self, nb, resources):
257 def _preprocess(self, nb, resources):
496 """
258 """
497 Preprocess the notebook before passing it into the Jinja engine.
259 Preprocess the notebook before passing it into the Jinja engine.
498 To preprocess the notebook is to apply all of the
260 To preprocess the notebook is to apply all of the
499
261
500 Parameters
262 Parameters
501 ----------
263 ----------
502 nb : notebook node
264 nb : notebook node
503 notebook that is being exported.
265 notebook that is being exported.
504 resources : a dict of additional resources that
266 resources : a dict of additional resources that
505 can be accessed read/write by preprocessors
267 can be accessed read/write by preprocessors
506 and filters.
507 """
268 """
508
269
509 # Do a copy.deepcopy first,
270 # Do a copy.deepcopy first,
510 # we are never safe enough with what the preprocessors could do.
271 # we are never safe enough with what the preprocessors could do.
511 nbc = copy.deepcopy(nb)
272 nbc = copy.deepcopy(nb)
512 resc = copy.deepcopy(resources)
273 resc = copy.deepcopy(resources)
@@ -19,13 +19,13 b' from IPython.utils.traitlets import Unicode, List'
19 from IPython.nbconvert import preprocessors
19 from IPython.nbconvert import preprocessors
20 from IPython.config import Config
20 from IPython.config import Config
21
21
22 from .exporter import Exporter
22 from .templateexporter import TemplateExporter
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Classes
25 # Classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 class HTMLExporter(Exporter):
28 class HTMLExporter(TemplateExporter):
29 """
29 """
30 Exports a basic HTML document. This exporter assists with the export of
30 Exports a basic HTML document. This exporter assists with the export of
31 HTML. Inherit from it if you are writing your own HTML template and need
31 HTML. Inherit from it if you are writing your own HTML template and need
@@ -46,7 +46,10 b' class HTMLExporter(Exporter):'
46 c = Config({
46 c = Config({
47 'CSSHTMLHeaderPreprocessor':{
47 'CSSHTMLHeaderPreprocessor':{
48 'enabled':True
48 'enabled':True
49 }
49 },
50 'HighlightMagicsPreprocessor': {
51 'enabled':True
52 }
50 })
53 })
51 c.merge(super(HTMLExporter,self).default_config)
54 c.merge(super(HTMLExporter,self).default_config)
52 return c
55 return c
@@ -24,13 +24,13 b' from IPython.utils.traitlets import Unicode, List'
24 from IPython.config import Config
24 from IPython.config import Config
25
25
26 from IPython.nbconvert import filters, preprocessors
26 from IPython.nbconvert import filters, preprocessors
27 from .exporter import Exporter
27 from .templateexporter import TemplateExporter
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Classes and functions
30 # Classes and functions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 class LatexExporter(Exporter):
33 class LatexExporter(TemplateExporter):
34 """
34 """
35 Exports to a Latex template. Inherit from this class if your template is
35 Exports to a Latex template. Inherit from this class if your template is
36 LaTeX based and you need custom tranformers/filters. Inherit from it if
36 LaTeX based and you need custom tranformers/filters. Inherit from it if
@@ -45,7 +45,7 b' class LatexExporter(Exporter):'
45 help="Extension of the file that should be written to disk")
45 help="Extension of the file that should be written to disk")
46
46
47 default_template = Unicode('article', config=True, help="""Template of the
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 #Latex constants
50 #Latex constants
51 default_template_path = Unicode(
51 default_template_path = Unicode(
@@ -85,6 +85,9 b' class LatexExporter(Exporter):'
85 },
85 },
86 'SphinxPreprocessor': {
86 'SphinxPreprocessor': {
87 'enabled':True
87 'enabled':True
88 },
89 'HighlightMagicsPreprocessor': {
90 'enabled':True
88 }
91 }
89 })
92 })
90 c.merge(super(LatexExporter,self).default_config)
93 c.merge(super(LatexExporter,self).default_config)
@@ -13,15 +13,16 b' Exporter that will export your ipynb to Markdown.'
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 from IPython.config import Config
16 from IPython.utils.traitlets import Unicode
17 from IPython.utils.traitlets import Unicode
17
18
18 from .exporter import Exporter
19 from .templateexporter import TemplateExporter
19
20
20 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
21 # Classes
22 # Classes
22 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
23
24
24 class MarkdownExporter(Exporter):
25 class MarkdownExporter(TemplateExporter):
25 """
26 """
26 Exports to a markdown document (.md)
27 Exports to a markdown document (.md)
27 """
28 """
@@ -29,3 +30,9 b' class MarkdownExporter(Exporter):'
29 file_extension = Unicode(
30 file_extension = Unicode(
30 'md', config=True,
31 'md', config=True,
31 help="Extension of the file that should be written to disk")
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 from IPython.utils.traitlets import Unicode
16 from IPython.utils.traitlets import Unicode
17
17
18 from .exporter import Exporter
18 from .templateexporter import TemplateExporter
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Classes
21 # Classes
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 class PythonExporter(Exporter):
24 class PythonExporter(TemplateExporter):
25 """
25 """
26 Exports a Python code file.
26 Exports a Python code file.
27 """
27 """
@@ -16,13 +16,13 b' Exporter for exporting notebooks to restructured text.'
16 from IPython.utils.traitlets import Unicode
16 from IPython.utils.traitlets import Unicode
17 from IPython.config import Config
17 from IPython.config import Config
18
18
19 from .exporter import Exporter
19 from .templateexporter import TemplateExporter
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Classes
22 # Classes
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 class RSTExporter(Exporter):
25 class RSTExporter(TemplateExporter):
26 """
26 """
27 Exports restructured text documents.
27 Exports restructured text documents.
28 """
28 """
@@ -19,13 +19,13 b' from IPython.utils.traitlets import Unicode'
19 from IPython.nbconvert import preprocessors
19 from IPython.nbconvert import preprocessors
20 from IPython.config import Config
20 from IPython.config import Config
21
21
22 from .exporter import Exporter
22 from .templateexporter import TemplateExporter
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Classes
25 # Classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 class SlidesExporter(Exporter):
28 class SlidesExporter(TemplateExporter):
29 """
29 """
30 Exports slides
30 Exports slides
31 """
31 """
@@ -47,6 +47,9 b' class SlidesExporter(Exporter):'
47 'RevealHelpPreprocessor':{
47 'RevealHelpPreprocessor':{
48 'enabled':True,
48 'enabled':True,
49 },
49 },
50 'HighlightMagicsPreprocessor': {
51 'enabled':True
52 }
50 })
53 })
51 c.merge(super(SlidesExporter,self).default_config)
54 c.merge(super(SlidesExporter,self).default_config)
52 return c
55 return c
@@ -27,4 +27,4 b' class ExportersTestsBase(TestsBase):'
27
27
28 def _get_notebook(self):
28 def _get_notebook(self):
29 return os.path.join(self._get_files_path(), 'notebook2.ipynb')
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 (output, resources) = export(None, self._get_notebook())
99 (output, resources) = export(None, self._get_notebook())
100 except TypeError:
100 except TypeError:
101 pass
101 pass
102 No newline at end of file
102
@@ -17,7 +17,7 b' Module with tests for exporter.py'
17 from IPython.config import Config
17 from IPython.config import Config
18
18
19 from .base import ExportersTestsBase
19 from .base import ExportersTestsBase
20 from .cheese import CheesePreprocessor
20 from ...preprocessors.base import Preprocessor
21 from ..exporter import Exporter
21 from ..exporter import Exporter
22
22
23
23
@@ -25,84 +25,35 b' from ..exporter import Exporter'
25 # Class
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 class TestExporter(ExportersTestsBase):
38 class TestExporter(ExportersTestsBase):
29 """Contains test functions for exporter.py"""
39 """Contains test functions for exporter.py"""
30
40
31
41
32 def test_constructor(self):
42 def test_constructor(self):
33 """
43 """Can an Exporter be constructed?"""
34 Can an Exporter be constructed?
35 """
36 Exporter()
44 Exporter()
37
45
38
46
39 def test_export(self):
47 def test_export(self):
40 """
48 """Can an Exporter export something?"""
41 Can an Exporter export something?
49 exporter = Exporter()
42 """
50 (notebook, resources) = exporter.from_filename(self._get_notebook())
43 exporter = self._make_exporter()
51 assert isinstance(notebook, dict)
44 (output, resources) = exporter.from_filename(self._get_notebook())
52
45 assert len(output) > 0
53
46
54 def test_preprocessor(self):
47
55 """Do preprocessors work?"""
48 def test_extract_outputs(self):
56 config = Config({'Exporter': {'preprocessors': [PizzaPreprocessor()]}})
49 """
57 exporter = Exporter(config=config)
50 If the ExtractOutputPreprocessor is enabled, are outputs extracted?
58 (notebook, resources) = exporter.from_filename(self._get_notebook())
51 """
59 self.assertEqual(notebook['pizza'], 'cheese')
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
@@ -44,18 +44,18 b' class TestLatexExporter(ExportersTestsBase):'
44 @onlyif_cmds_exist('pandoc')
44 @onlyif_cmds_exist('pandoc')
45 def test_export_book(self):
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 assert len(output) > 0
50 assert len(output) > 0
51
51
52
52
53 @onlyif_cmds_exist('pandoc')
53 @onlyif_cmds_exist('pandoc')
54 def test_export_basic(self):
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 assert len(output) > 0
59 assert len(output) > 0
60
60
61
61
@@ -1,6 +1,7 b''
1 from .ansi import *
1 from .ansi import *
2 from .citation import *
2 from .datatypefilter import *
3 from .datatypefilter import *
3 from .highlight import *
4 from .highlight import *
4 from .latex import *
5 from .latex import *
5 from .markdown import *
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 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 from pygments import highlight as pygements_highlight
17 from pygments import highlight as pygements_highlight
18 from pygments.lexers import get_lexer_by_name
18 from pygments.lexers import get_lexer_by_name
19 from pygments.formatters import HtmlFormatter
19 from pygments.formatters import HtmlFormatter
20 from pygments.formatters import LatexFormatter
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 Return a syntax-highlighted version of the input source as html output.
43 Return a syntax-highlighted version of the input source as html output.
44
44
45 Parameters
45 Parameters
46 ----------
46 ----------
47 source : str
47 source : str
48 Source code to highlight the syntax of.
48 source of the cell to highlight
49 language : str
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
54
53 return _pygment_highlight(source, HtmlFormatter(), language)
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 Return a syntax-highlighted version of the input source as latex output.
60 Return a syntax-highlighted version of the input source as latex output.
59
61
60 Parameters
62 Parameters
61 ----------
63 ----------
62 source : str
64 source : str
63 Source code to highlight the syntax of.
65 source of the cell to highlight
64 language : str
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 Return a syntax-highlighted version of the input source
84 Return a syntax-highlighted version of the input source
73
85
74 Parameters
86 Parameters
75 ----------
87 ----------
76 source : str
88 source : str
77 Source code to highlight the syntax of.
89 source of the cell to highlight
78 output_formatter : Pygments formatter
90 output_formatter : Pygments formatter
79 language : str
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 if language == 'ipython':
105 if language == 'ipython':
84 lexer = IPythonLexer()
106 lexer = IPythonLexer()
85 else:
107 else:
86 lexer = get_lexer_by_name(language, stripall=True)
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 import os
19 import os
20 import re
20 import re
21 import textwrap
21 import textwrap
22 from urllib2 import quote
22 from xml.etree import ElementTree
23 from xml.etree import ElementTree
23
24
24 from IPython.core.interactiveshell import InteractiveShell
25 from IPython.core.interactiveshell import InteractiveShell
@@ -38,6 +39,8 b' __all__ = ['
38 'get_lines',
39 'get_lines',
39 'ipython2python',
40 'ipython2python',
40 'posix_path',
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 For use in heading cells
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 link = html2text(h).replace(' ', '-')
87 link = html2text(h).replace(' ', '-')
85 h.set('id', link)
88 h.set('id', link)
86 a = ElementTree.Element("a", {"class" : "anchor-link", "href" : "#" + link})
89 a = ElementTree.Element("a", {"class" : "anchor-link", "href" : "#" + link})
@@ -93,6 +96,16 b' def add_anchor(html):'
93 return py3compat.decode(ElementTree.tostring(h), 'utf-8')
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 def strip_dollars(text):
109 def strip_dollars(text):
97 """
110 """
98 Remove all dollar symbols from text
111 Remove all dollar symbols from text
@@ -181,3 +194,8 b' def posix_path(path):'
181 if os.path.sep != '/':
194 if os.path.sep != '/':
182 return path.replace(os.path.sep, '/')
195 return path.replace(os.path.sep, '/')
183 return path
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 'hello' : 'hello'}
39 'hello' : 'hello'}
40
40
41 for inval, outval in correct_outputs.items():
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 def _try_strip_ansi(self, inval, outval):
45 def _try_strip_ansi(self, inval, outval):
@@ -58,7 +58,7 b' class TestAnsi(TestsBase):'
58 'hello' : 'hello'}
58 'hello' : 'hello'}
59
59
60 for inval, outval in correct_outputs.items():
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 def _try_ansi2html(self, inval, outval):
64 def _try_ansi2html(self, inval, outval):
@@ -77,7 +77,7 b' class TestAnsi(TestsBase):'
77 'hello' : 'hello'}
77 'hello' : 'hello'}
78
78
79 for inval, outval in correct_outputs.items():
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 def _try_ansi2latex(self, inval, outval):
83 def _try_ansi2latex(self, inval, outval):
@@ -49,13 +49,13 b' class TestHighlight(TestsBase):'
49 def test_highlight2html(self):
49 def test_highlight2html(self):
50 """highlight2html test"""
50 """highlight2html test"""
51 for index, test in enumerate(self.tests):
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 def test_highlight2latex(self):
55 def test_highlight2latex(self):
56 """highlight2latex test"""
56 """highlight2latex test"""
57 for index, test in enumerate(self.tests):
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 def _try_highlight(self, method, test, tokens):
61 def _try_highlight(self, method, test, tokens):
@@ -35,7 +35,7 b' class TestLatex(TestsBase):'
35 ('','')]
35 ('','')]
36
36
37 for test in tests:
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 def _try_escape_latex(self, test, result):
41 def _try_escape_latex(self, test, result):
@@ -56,7 +56,7 b' class TestLatex(TestsBase):'
56 ('','')]
56 ('','')]
57
57
58 for test in tests:
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 def _try_strip_math_space(self, test, result):
62 def _try_strip_math_space(self, test, result):
@@ -61,14 +61,14 b' class TestMarkdown(TestsBase):'
61 def test_markdown2latex(self):
61 def test_markdown2latex(self):
62 """markdown2latex test"""
62 """markdown2latex test"""
63 for index, test in enumerate(self.tests):
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 @dec.onlyif_cmds_exist('pandoc')
67 @dec.onlyif_cmds_exist('pandoc')
68 def test_markdown2html(self):
68 def test_markdown2html(self):
69 """markdown2html test"""
69 """markdown2html test"""
70 for index, test in enumerate(self.tests):
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 @dec.onlyif_cmds_exist('pandoc')
74 @dec.onlyif_cmds_exist('pandoc')
@@ -81,7 +81,7 b' class TestMarkdown(TestsBase):'
81 tokens[1] = r'\*\*test'
81 tokens[1] = r'\*\*test'
82
82
83 for index, test in enumerate(self.tests):
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 def _try_markdown(self, method, test, tokens):
87 def _try_markdown(self, method, test, tokens):
@@ -15,10 +15,10 b' Module with tests for Strings'
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 import os
16 import os
17
17
18 from IPython.testing import decorators as dec
19 from ...tests.base import TestsBase
18 from ...tests.base import TestsBase
20 from ..strings import (wrap_text, html2text, add_anchor, strip_dollars,
19 from ..strings import (wrap_text, html2text, add_anchor, strip_dollars,
21 strip_files_prefix, get_lines, comment_lines, ipython2python, posix_path,
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 As if the strings were thine, shouldst know of this.
36 As if the strings were thine, shouldst know of this.
37 """
37 """
38 for length in [30,5,1]:
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 def _confirm_wrap_text(self, text, length):
42 def _confirm_wrap_text(self, text, length):
@@ -73,7 +73,7 b' class TestStrings(TestsBase):'
73 ('Hello', 'Hello'),
73 ('Hello', 'Hello'),
74 ('W$o$rld', 'W$o$rld')]
74 ('W$o$rld', 'W$o$rld')]
75 for test in tests:
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 def _try_strip_dollars(self, test, result):
79 def _try_strip_dollars(self, test, result):
@@ -89,7 +89,7 b' class TestStrings(TestsBase):'
89 ('My files are in `files/`', 'My files are in `files/`'),
89 ('My files are in `files/`', 'My files are in `files/`'),
90 ('<a href="files/test.html">files/test.html</a>', '<a href="test.html">files/test.html</a>')]
90 ('<a href="files/test.html">files/test.html</a>', '<a href="test.html">files/test.html</a>')]
91 for test in tests:
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 def _try_files_prefix(self, test, result):
95 def _try_files_prefix(self, test, result):
@@ -121,8 +121,15 b' class TestStrings(TestsBase):'
121 ignore_spaces=True, ignore_newlines=True)
121 ignore_spaces=True, ignore_newlines=True)
122
122
123 def test_posix_path(self):
123 def test_posix_path(self):
124 """posix_path test"""
124 path_list = ['foo', 'bar']
125 path_list = ['foo', 'bar']
125 expected = '/'.join(path_list)
126 expected = '/'.join(path_list)
126 native = os.path.join(*path_list)
127 native = os.path.join(*path_list)
127 filtered = posix_path(native)
128 filtered = posix_path(native)
128 self.assertEqual(filtered, expected)
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 # From IPython
26 # From IPython
27 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
27 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
28 from IPython.core.profiledir import ProfileDir
28 from IPython.config import catch_config_error, Configurable
29 from IPython.config import catch_config_error, Configurable
29 from IPython.utils.traitlets import (
30 from IPython.utils.traitlets import (
30 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
31 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
@@ -58,12 +59,11 b' nbconvert_aliases = {}'
58 nbconvert_aliases.update(base_aliases)
59 nbconvert_aliases.update(base_aliases)
59 nbconvert_aliases.update({
60 nbconvert_aliases.update({
60 'to' : 'NbConvertApp.export_format',
61 'to' : 'NbConvertApp.export_format',
61 'template' : 'Exporter.template_file',
62 'template' : 'TemplateExporter.template_file',
62 'writer' : 'NbConvertApp.writer_class',
63 'writer' : 'NbConvertApp.writer_class',
63 'post': 'NbConvertApp.postprocessor_class',
64 'post': 'NbConvertApp.postprocessor_class',
64 'output': 'NbConvertApp.output_base',
65 'output': 'NbConvertApp.output_base',
65 'offline-slides': 'RevealHelpTransformer.url_prefix',
66 'reveal-prefix': 'RevealHelpPreprocessor.url_prefix',
66 'slide-notes': 'RevealHelpTransformer.speaker_notes'
67 })
67 })
68
68
69 nbconvert_flags = {}
69 nbconvert_flags = {}
@@ -87,12 +87,13 b' class NbConvertApp(BaseIPythonApplication):'
87 return logging.INFO
87 return logging.INFO
88
88
89 def _classes_default(self):
89 def _classes_default(self):
90 classes = [NbConvertBase]
90 classes = [NbConvertBase, ProfileDir]
91 for pkg in (exporters, preprocessors, writers):
91 for pkg in (exporters, preprocessors, writers):
92 for name in dir(pkg):
92 for name in dir(pkg):
93 cls = getattr(pkg, name)
93 cls = getattr(pkg, name)
94 if isinstance(cls, type) and issubclass(cls, Configurable):
94 if isinstance(cls, type) and issubclass(cls, Configurable):
95 classes.append(cls)
95 classes.append(cls)
96
96 return classes
97 return classes
97
98
98 description = Unicode(
99 description = Unicode(
@@ -1,3 +1,8 b''
1 from .base import PostProcessorBase
1 from .base import PostProcessorBase
2 from .pdf import PDFPostProcessor
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 class PDFPostProcessor(PostProcessorBase):
26 class PDFPostProcessor(PostProcessorBase):
27 """Writer designed to write to PDF files"""
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 How many times pdflatex will be called.
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 Shell command used to compile PDF.""")
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 verbose = Bool(False, config=True, help="""
39 verbose = Bool(False, config=True, help="""
37 Whether or not to display the output of the compile call.
40 Whether or not to display the output of the compile call.
38 """)
41 """)
39
42
40 def postprocess(self, input):
43 temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'],
41 """
44 config=True, help="""
42 Consume and write Jinja output a PDF.
45 Filename extensions of temp files to remove after running.
43 See files.py for more...
46 """)
44 """
47
45 command = [c.format(filename=input) for c in self.command]
48 def run_command(self, command_list, filename, count, log_function):
46 self.log.info("Building PDF: %s", command)
49 """Run command_list count times.
47 with open(os.devnull, 'rb') as null:
50
48 stdout = subprocess.PIPE if not self.verbose else None
51 Parameters
49 for index in range(self.iteration_count):
52 ----------
50 p = subprocess.Popen(command, stdout=stdout, stdin=null)
53 command_list : list
51 out, err = p.communicate()
54 A list of args to provide to Popen. Each element of this
52 if p.returncode:
55 list will be interpolated with the filename to convert.
53 if self.verbose:
56 filename : unicode
54 # verbose means I didn't capture stdout with PIPE,
57 The name of the file to convert.
55 # so it's already been displayed and `out` is None.
58 count : int
56 out = u''
59 How many times to run the command.
57 else:
60
58 out = out.decode('utf-8', 'replace')
61 Returns
59 self.log.critical(u"PDF conversion failed: %s\n%s", command, out)
62 -------
60 return
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 """
1 """PostProcessor for serving reveal.js HTML slideshows."""
2 Contains postprocessor for serving nbconvert output.
3 """
4 #-----------------------------------------------------------------------------
2 #-----------------------------------------------------------------------------
5 #Copyright (c) 2013, the IPython Development Team.
3 #Copyright (c) 2013, the IPython Development Team.
6 #
4 #
@@ -16,40 +14,94 b' Contains postprocessor for serving nbconvert output.'
16 import os
14 import os
17 import webbrowser
15 import webbrowser
18
16
19 from BaseHTTPServer import HTTPServer
17 from tornado import web, ioloop, httpserver
20 from SimpleHTTPServer import SimpleHTTPRequestHandler
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 from .base import PostProcessorBase
22 from .base import PostProcessorBase
25
23
26 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
27 # Classes
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 class ServePostProcessor(PostProcessorBase):
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 open_in_browser = Bool(True, config=True,
52 open_in_browser = Bool(True, config=True,
34 help="""Set to False to deactivate
53 help="""Should the browser be opened automatically?"""
35 the opening of the browser""")
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 def postprocess(self, input):
62 def postprocess(self, input):
38 """
63 """Serve the build directory with a webserver."""
39 Simple implementation to serve the build directory.
64 dirname, filename = os.path.split(input)
40 """
65 handlers = [
41
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 try:
95 try:
43 dirname, filename = os.path.split(input)
96 ioloop.IOLoop.instance().start()
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()
54 except KeyboardInterrupt:
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 # Check that the PDF was created.
63 # Check that the PDF was created.
64 assert os.path.isfile('a.pdf')
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 from .extractoutput import ExtractOutputPreprocessor
5 from .extractoutput import ExtractOutputPreprocessor
6 from .revealhelp import RevealHelpPreprocessor
6 from .revealhelp import RevealHelpPreprocessor
7 from .latex import LatexPreprocessor
7 from .latex import LatexPreprocessor
8 from .sphinx import SphinxPreprocessor
9 from .csshtmlheader import CSSHTMLHeaderPreprocessor
8 from .csshtmlheader import CSSHTMLHeaderPreprocessor
9 from .highlightmagics import HighlightMagicsPreprocessor
10
10
11 # decorated function Preprocessors
11 # decorated function Preprocessors
12 from .coalescestreams import coalesce_streams
12 from .coalescestreams import coalesce_streams
@@ -19,7 +19,8 b' def cell_preprocessor(function):'
19 Wrap a function to be executed on all cells of a notebook
19 Wrap a function to be executed on all cells of a notebook
20
20
21 Wrapped Parameters
21 Wrapped Parameters
22 ----------
22 ------------------
23
23 cell : NotebookNode cell
24 cell : NotebookNode cell
24 Notebook cell being processed
25 Notebook cell being processed
25 resources : dictionary
26 resources : dictionary
@@ -69,7 +70,7 b' def coalesce_streams(cell, resources, index):'
69 last.text += output.text
70 last.text += output.text
70 else:
71 else:
71 new_outputs.append(output)
72 new_outputs.append(output)
72 last = output
73 last = output
73
74
74 cell.outputs = new_outputs
75 cell.outputs = new_outputs
75 return cell, resources
76 return cell, resources
@@ -14,9 +14,12 b' they are converted.'
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 from __future__ import print_function, absolute_import
16 from __future__ import print_function, absolute_import
17 import os
17
18
18 # Our own imports
19 # Third-party import, needed for Pygments latex definitions.
19 # Needed to override preprocessor
20 from pygments.formatters import LatexFormatter
21
22 # ipy imports
20 from .base import (Preprocessor)
23 from .base import (Preprocessor)
21 from IPython.nbconvert import filters
24 from IPython.nbconvert import filters
22
25
@@ -29,6 +32,24 b' class LatexPreprocessor(Preprocessor):'
29 Converter for latex destined documents.
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 def preprocess_cell(self, cell, resources, index):
53 def preprocess_cell(self, cell, resources, index):
33 """
54 """
34 Apply a transformation on each cell,
55 Apply a transformation on each cell,
@@ -24,15 +24,14 b' from IPython.utils.traitlets import Unicode, Bool'
24
24
25 class RevealHelpPreprocessor(Preprocessor):
25 class RevealHelpPreprocessor(Preprocessor):
26
26
27 url_prefix = Unicode('//cdn.jsdelivr.net/reveal.js/2.4.0',
27 url_prefix = Unicode('reveal.js', config=True,
28 config=True,
28 help="""The URL prefix for reveal.js.
29 help="""If you want to use a local reveal.js library,
29 This can be a a relative URL for a local copy of reveal.js,
30 use 'url_prefix':'reveal.js' in your config object.""")
30 or point to a CDN.
31
31
32 speaker_notes = Bool(False,
32 For speaker notes to work, a local reveal.js prefix must be used.
33 config=True,
33 """
34 help="""If you want to use the speaker notes
34 )
35 set this to True.""")
36
35
37 def preprocess(self, nb, resources):
36 def preprocess(self, nb, resources):
38 """
37 """
@@ -65,30 +64,4 b' class RevealHelpPreprocessor(Preprocessor):'
65 if not isinstance(resources['reveal'], dict):
64 if not isinstance(resources['reveal'], dict):
66 resources['reveal'] = {}
65 resources['reveal'] = {}
67 resources['reveal']['url_prefix'] = self.url_prefix
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 return nb, resources
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 nbformat.new_output(output_type="stream", stream="stdout", output_text="d"),
36 nbformat.new_output(output_type="stream", stream="stdout", output_text="d"),
37 nbformat.new_output(output_type="stream", stream="stderr", output_text="e"),
37 nbformat.new_output(output_type="stream", stream="stderr", output_text="e"),
38 nbformat.new_output(output_type="stream", stream="stderr", output_text="f"),
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 cells=[nbformat.new_code_cell(input="$ e $", prompt_number=1,outputs=outputs),
41 cells=[nbformat.new_code_cell(input="$ e $", prompt_number=1,outputs=outputs),
42 nbformat.new_text_cell('markdown', source="$ e $")]
42 nbformat.new_text_cell('markdown', source="$ e $")]
@@ -14,6 +14,8 b' Module with tests for the coalescestreams preprocessor'
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 from IPython.nbformat import current as nbformat
18
17 from .base import PreprocessorTestsBase
19 from .base import PreprocessorTestsBase
18 from ..coalescestreams import coalesce_streams
20 from ..coalescestreams import coalesce_streams
19
21
@@ -35,4 +37,24 b' class TestCoalesceStreams(PreprocessorTestsBase):'
35 self.assertEqual(outputs[1].output_type, "text")
37 self.assertEqual(outputs[1].output_type, "text")
36 self.assertEqual(outputs[2].text, "cd")
38 self.assertEqual(outputs[2].text, "cd")
37 self.assertEqual(outputs[3].text, "ef")
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 nb, res = preprocessor(nb, res)
46 nb, res = preprocessor(nb, res)
47
47
48 # Check if text was extracted.
48 # Check if text was extracted.
49 assert 'text_filename' in nb.worksheets[0].cells[0].outputs[1]
49 output = nb.worksheets[0].cells[0].outputs[1]
50 text_filename = nb.worksheets[0].cells[0].outputs[1]['text_filename']
50 assert 'text_filename' in output
51 text_filename = output['text_filename']
51
52
52 # Check if png was extracted.
53 # Check if png was extracted.
53 assert 'png_filename' in nb.worksheets[0].cells[0].outputs[6]
54 output = nb.worksheets[0].cells[0].outputs[6]
54 png_filename = nb.worksheets[0].cells[0].outputs[6]['png_filename']
55 assert 'png_filename' in output
56 png_filename = output['png_filename']
55
57
56 # Verify text output
58 # Verify text output
57 assert text_filename in res['outputs']
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 from .base import PreprocessorTestsBase
17 from .base import PreprocessorTestsBase
18 from ..sphinx import SphinxPreprocessor
18 from ..highlightmagics import HighlightMagicsPreprocessor
19
19
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Class
22 # Class
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 class TestSphinx(PreprocessorTestsBase):
25 class TestHighlightMagics(PreprocessorTestsBase):
26 """Contains test functions for sphinx.py"""
26 """Contains test functions for highlightmagics.py"""
27
27
28
28
29 def build_preprocessor(self):
29 def build_preprocessor(self):
30 """Make an instance of a preprocessor"""
30 """Make an instance of a preprocessor"""
31 preprocessor = SphinxPreprocessor()
31 preprocessor = HighlightMagicsPreprocessor()
32 preprocessor.enabled = True
32 preprocessor.enabled = True
33 return preprocessor
33 return preprocessor
34
34
35
36 def test_constructor(self):
35 def test_constructor(self):
37 """Can a SphinxPreprocessor be constructed?"""
36 """Can a HighlightMagicsPreprocessor be constructed?"""
38 self.build_preprocessor()
37 self.build_preprocessor()
39
40
38
41 def test_resources(self):
39 def test_tagging(self):
42 """Make sure the SphinxPreprocessor adds the appropriate resources to the
40 """Test the HighlightMagicsPreprocessor tagging"""
43 resources dict."""
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 nb = self.build_notebook()
59 nb = self.build_notebook()
45 res = self.build_resources()
60 res = self.build_resources()
46 preprocessor = self.build_preprocessor()
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 nb, res = preprocessor(nb, res)
66 nb, res = preprocessor(nb, res)
48 assert "author" in res['sphinx']
67
49 assert "version" in res['sphinx']
68 assert('magics_language' not in nb.worksheets[0].cells[0]['metadata']) No newline at end of file
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']
@@ -36,7 +36,7 b' In&nbsp;[{{ cell.prompt_number }}]:'
36
36
37 {% block input %}
37 {% block input %}
38 <div class="input_area box-flex1">
38 <div class="input_area box-flex1">
39 {{ cell.input | highlight2html }}
39 {{ cell.input | highlight2html(metadata=cell.metadata) }}
40 </div>
40 </div>
41 {%- endblock input %}
41 {%- endblock input %}
42
42
@@ -135,6 +135,12 b' unknown type {{ cell.type }}'
135 </pre>
135 </pre>
136 {%- endblock -%}
136 {%- endblock -%}
137
137
138 {%- block data_javascript %}
139 <script type="text/javascript">
140 {{ output.javascript }}
141 </script>
142 {%- endblock -%}
143
138 {%- block display_data scoped -%}
144 {%- block display_data scoped -%}
139 <div class="box-flex1 output_subarea output_display_data">
145 <div class="box-flex1 output_subarea output_display_data">
140 {{ super() }}
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
2 % Default to the notebook output style
5 document style. Most of the is derived directly from Sphinx source.
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
15 ((* block docclass *))
14 ((* set parentdocumentclass = 'article' *))
16 \documentclass{article}
15 ((* set documentclass = 'howto' *))
17 ((* endblock docclass *)) No newline at end of file
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 *))
@@ -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 ((*- extends 'null.tplx' -*))
5 ((*- extends 'null.tplx' -*))
3
6
4 ((=display data priority=))
7 ((=display data priority=))
@@ -30,10 +33,13 b''
30 ((*- block data_text -*))
33 ((*- block data_text -*))
31 ((*- endblock -*))
34 ((*- endblock -*))
32 ((*- endif -*))
35 ((*- endif -*))
33
34 ((*- if type in ['latex']*))
36 ((*- if type in ['latex']*))
35 ((*- block data_latex -*))
37 ((*- block data_latex -*))
36 ((*- endblock -*))
38 ((*- endblock -*))
37 ((*- endif -*))
39 ((*- endif -*))
40 ((*- if type in ['javascript']*))
41 ((*- block data_javascript -*))
42 ((*- endblock -*))
43 ((*- endif -*))
38 ((*- endfor -*))
44 ((*- endfor -*))
39 ((*- endblock data_priority -*))
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 IF YOU ARE COPY AND PASTING THIS FILE
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 To layout the different blocks of a notebook.
12 To layout the different blocks of a notebook.
10
13
11 Subtemplates can override blocks to define their custom representation.
14 Subtemplates can override blocks to define their custom representation.
@@ -2,19 +2,13 b''
2
2
3
3
4 {% block in_prompt %}
4 {% block in_prompt %}
5 In[{{ cell.prompt_number if cell.prompt_number else ' ' }}]:
6 {% endblock in_prompt %}
5 {% endblock in_prompt %}
7
6
8 {% block output_prompt %}
7 {% block output_prompt %}
9 {% if cell.haspyout %}
10 Out[{{ cell.prompt_number }}]:
11 {%- endif %}
12 {%- endblock output_prompt %}
8 {%- endblock output_prompt %}
13
9
14 {% block input %}
10 {% block input %}
15 ```
11 {{ cell.input | indent(4)}}
16 {{ cell.input }}
17 ```
18 {% endblock input %}
12 {% endblock input %}
19
13
20 {% block pyerr %}
14 {% block pyerr %}
@@ -26,6 +20,7 b' Out[{{ cell.prompt_number }}]:'
26 {% endblock traceback_line %}
20 {% endblock traceback_line %}
27
21
28 {% block pyout %}
22 {% block pyout %}
23
29 {% block data_priority scoped %}
24 {% block data_priority scoped %}
30 {{ super() }}
25 {{ super() }}
31 {% endblock %}
26 {% endblock %}
@@ -36,23 +31,25 b' Out[{{ cell.prompt_number }}]:'
36 {% endblock stream %}
31 {% endblock stream %}
37
32
38 {% block data_svg %}
33 {% block data_svg %}
39 [!image]({{ output.svg_filename }})
34 ![svg]({{ output.svg_filename | path2url }})
40 {% endblock data_svg %}
35 {% endblock data_svg %}
41
36
42 {% block data_png %}
37 {% block data_png %}
43 [!image]({{ output.png_filename }})
38 ![png]({{ output.png_filename | path2url }})
44 {% endblock data_png %}
39 {% endblock data_png %}
45
40
46 {% block data_jpg %}
41 {% block data_jpg %}
47 [!image]({{ output.jpg_filename }})
42 ![jpeg]({{ output.jpeg_filename | path2url }})
48 {% endblock data_jpg %}
43 {% endblock data_jpg %}
49
44
50 {% block data_latex %}
45 {% block data_latex %}
51 $$
52 {{ output.latex }}
46 {{ output.latex }}
53 $$
54 {% endblock data_latex %}
47 {% endblock data_latex %}
55
48
49 {% block data_html scoped %}
50 {{ output.html }}
51 {% endblock data_html %}
52
56 {% block data_text scoped %}
53 {% block data_text scoped %}
57 {{ output.text | indent }}
54 {{ output.text | indent }}
58 {% endblock data_text %}
55 {% endblock data_text %}
@@ -61,6 +58,7 b' $$'
61 {{ cell.source | wrap_text(80) }}
58 {{ cell.source | wrap_text(80) }}
62 {% endblock markdowncell %}
59 {% endblock markdowncell %}
63
60
61
64 {% block headingcell scoped %}
62 {% block headingcell scoped %}
65 {{ '#' * cell.level }} {{ cell.source | replace('\n', ' ') }}
63 {{ '#' * cell.level }} {{ cell.source | replace('\n', ' ') }}
66 {% endblock headingcell %}
64 {% endblock headingcell %}
@@ -2,25 +2,22 b''
2
2
3
3
4 {% block in_prompt %}
4 {% block in_prompt %}
5
6 In[{{ cell.prompt_number if cell.prompt_number else ' ' }}]:
7
8 .. code:: python
9
10 {% endblock in_prompt %}
5 {% endblock in_prompt %}
11
6
12 {% block output_prompt %}
7 {% block output_prompt %}
13 {% if cell.haspyout -%}
14 Out[{{ cell.prompt_number }}]:
15 {% endif %}
16 {% endblock output_prompt %}
8 {% endblock output_prompt %}
17
9
18 {% block input %}
10 {% block input %}
11 {%- if not cell.input.isspace() -%}
12 .. code:: python
13
19 {{ cell.input | indent}}
14 {{ cell.input | indent}}
15 {%- endif -%}
20 {% endblock input %}
16 {% endblock input %}
21
17
22 {% block pyerr %}
18 {% block pyerr %}
23 ::
19 ::
20
24 {{ super() }}
21 {{ super() }}
25 {% endblock pyerr %}
22 {% endblock pyerr %}
26
23
@@ -49,19 +46,27 b" In[{{ cell.prompt_number if cell.prompt_number else ' ' }}]:"
49 {% endblock data_png %}
46 {% endblock data_png %}
50
47
51 {% block data_jpg %}
48 {% block data_jpg %}
52 ..jpg image:: {{ output.jpg_filename }}
49 .. image:: {{ output.jpeg_filename }}
53 {% endblock data_jpg %}
50 {% endblock data_jpg %}
54
51
55 {% block data_latex %}
52 {% block data_latex %}
56 .. math::
53 .. math::
57 {{ output.latex | indent }}
54
55 {{ output.latex | strip_dollars | indent }}
58 {% endblock data_latex %}
56 {% endblock data_latex %}
59
57
60 {% block data_text scoped %}
58 {% block data_text scoped %}
61 .. parsed-literal::
59 .. parsed-literal::
60
62 {{ output.text | indent }}
61 {{ output.text | indent }}
63 {% endblock data_text %}
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 {% block markdowncell scoped %}
70 {% block markdowncell scoped %}
66 {{ cell.source | markdown2rst }}
71 {{ cell.source | markdown2rst }}
67 {% endblock markdowncell %}
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
5 # Convert standard Jinja2 syntax to LaTeX safe Jinja2
4
6 # see http://flask.pocoo.org/snippets/55/ for more info
5 # convert jinja syntax to tex
7 ../latex/skeleton/%.tplx: %.tpl
6 # cf http://flask.pocoo.org/snippets/55/
7 tex/%.tplx: %.tpl
8 @echo 'generating tex equivalent of $^: $@'
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 @sed \
12 @sed \
11 -e 's/{%/((*/g' \
13 -e 's/{%/((*/g' \
12 -e 's/%}/*))/g' \
14 -e 's/%}/*))/g' \
@@ -17,7 +19,6 b' tex/%.tplx: %.tpl'
17 -e "s/tpl'/tplx'/g" \
19 -e "s/tpl'/tplx'/g" \
18 $^ >> $@
20 $^ >> $@
19
21
20
21 clean:
22 clean:
22 @echo "cleaning generated tplx files..."
23 @echo "cleaning generated tplx files..."
23 @rm tex/*
24 @-rm ../latex/skeleton/*.tplx
@@ -1,6 +1,8 b''
1 ## Template skeleton
1 ## Template skeleton
2
2
3 This contain skeleton template that you probably don't want
3 This directory contains the template skeleton files.
4 to inherit directly.
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 {%- block data_text -%}
29 {%- block data_text -%}
30 {%- endblock -%}
30 {%- endblock -%}
31 {%- endif -%}
31 {%- endif -%}
32
33 {%- if type in ['latex']%}
32 {%- if type in ['latex']%}
34 {%- block data_latex -%}
33 {%- block data_latex -%}
35 {%- endblock -%}
34 {%- endblock -%}
36 {%- endif -%}
35 {%- endif -%}
36 {%- if type in ['javascript']%}
37 {%- block data_javascript -%}
38 {%- endblock -%}
39 {%- endif -%}
37 {%- endfor -%}
40 {%- endfor -%}
38 {%- endblock data_priority -%}
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 IF YOU ARE COPY AND PASTING THIS FILE
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 To layout the different blocks of a notebook.
8 To layout the different blocks of a notebook.
9
9
10 Subtemplates can override blocks to define their custom representation.
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 dependencies: [
135 dependencies: [
136 { src: "{{resources.reveal.url_prefix}}/lib/js/classList.js", condition: function() { return !document.body.classList; } },
136 { src: "{{resources.reveal.url_prefix}}/lib/js/classList.js", condition: function() { return !document.body.classList; } },
137 { src: "{{resources.reveal.url_prefix}}/plugin/highlight/highlight.js", async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
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 // { src: 'http://s7.addthis.com/js/300/addthis_widget.js', async: true},
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 import os
16 import os
17 import glob
17 import glob
18 import shutil
18 import shutil
19 import unittest
19
20
20 import IPython
21 import IPython
21 from IPython.utils.tempdir import TemporaryWorkingDirectory
22 from IPython.utils.tempdir import TemporaryWorkingDirectory
22 from IPython.utils.process import get_output_error_code
23 from IPython.utils.process import get_output_error_code
23 from IPython.testing.tools import get_ipython_cmd
24 from IPython.testing.tools import get_ipython_cmd
24 from IPython.testing.ipunittest import ParametricTestCase
25
25
26 # a trailing space allows for simpler concatenation with the other arguments
26 # a trailing space allows for simpler concatenation with the other arguments
27 ipy_cmd = get_ipython_cmd(as_string=True) + " "
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 """Base tests class. Contains useful fuzzy comparison and nbconvert
35 """Base tests class. Contains useful fuzzy comparison and nbconvert
36 functions."""
36 functions."""
37
37
@@ -86,7 +86,7 b' class TestsBase(ParametricTestCase):'
86
86
87 For example:
87 For example:
88 Replace "ii" with "i" in the string "Hiiii" yields "Hii"
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 Parameters:
91 Parameters:
92 -----------
92 -----------
@@ -124,6 +124,14 b''
124 "prompt_number": 10
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 "cell_type": "markdown",
135 "cell_type": "markdown",
128 "metadata": {},
136 "metadata": {},
129 "source": [
137 "source": [
@@ -1,12 +1,10 b''
1 """
1 """Test NbConvertApp"""
2 Contains tests for the nbconvertapp
2
3 """
4 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
5 #Copyright (c) 2013, the IPython Development Team.
4 # Copyright (C) 2013 The IPython Development Team
6 #
7 #Distributed under the terms of the Modified BSD License.
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 import os
14 import os
17 import glob
15 import glob
16 import sys
18
17
19 from .base import TestsBase
18 from .base import TestsBase
20
19
20 import IPython.testing.tools as tt
21 from IPython.testing import decorators as dec
21 from IPython.testing import decorators as dec
22
22
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Constants
25 # Constants
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
@@ -35,13 +35,14 b' class TestNbConvertApp(TestsBase):'
35
35
36
36
37 def test_notebook_help(self):
37 def test_notebook_help(self):
38 """
38 """Will help show if no notebooks are specified?"""
39 Will help show if no notebooks are specified?
40 """
41 with self.create_temp_cwd():
39 with self.create_temp_cwd():
42 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
40 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
43 assert "see '--help-all'" in out
41 self.assertIn("see '--help-all'", out)
44
42
43 def test_help_output(self):
44 """ipython nbconvert --help-all works"""
45 tt.help_all_output_test('nbconvert')
45
46
46 def test_glob(self):
47 def test_glob(self):
47 """
48 """
@@ -102,6 +103,30 b' class TestNbConvertApp(TestsBase):'
102 assert os.path.isfile('notebook1.tex')
103 assert os.path.isfile('notebook1.tex')
103 assert os.path.isfile('notebook1.pdf')
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 @dec.onlyif_cmds_exist('pandoc')
131 @dec.onlyif_cmds_exist('pandoc')
107 def test_template(self):
132 def test_template(self):
@@ -15,6 +15,7 b' from __future__ import print_function'
15
15
16 # Stdlib imports
16 # Stdlib imports
17 import subprocess
17 import subprocess
18 from io import TextIOWrapper, BytesIO
18
19
19 # IPython imports
20 # IPython imports
20 from IPython.utils.py3compat import cast_bytes
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 "http://johnmacfarlane.net/pandoc/installing.html"
65 "http://johnmacfarlane.net/pandoc/installing.html"
65 )
66 )
66 out, _ = p.communicate(cast_bytes(source, encoding))
67 out, _ = p.communicate(cast_bytes(source, encoding))
67 out = out.decode(encoding, 'replace')
68 out = TextIOWrapper(BytesIO(out), encoding, 'replace').read()
68 return out[:-1]
69 return out.rstrip('\n')
69
70
@@ -25,6 +25,7 b' import pprint'
25 import uuid
25 import uuid
26
26
27 from IPython.utils.ipstruct import Struct
27 from IPython.utils.ipstruct import Struct
28 from IPython.utils.py3compat import cast_unicode
28
29
29 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
30 # Code
31 # Code
@@ -67,21 +68,21 b' def new_output(output_type=None, output_text=None, output_png=None,'
67
68
68 if output_type != 'pyerr':
69 if output_type != 'pyerr':
69 if output_text is not None:
70 if output_text is not None:
70 output.text = unicode(output_text)
71 output.text = cast_unicode(output_text)
71 if output_png is not None:
72 if output_png is not None:
72 output.png = bytes(output_png)
73 output.png = cast_unicode(output_png)
73 if output_jpeg is not None:
74 if output_jpeg is not None:
74 output.jpeg = bytes(output_jpeg)
75 output.jpeg = cast_unicode(output_jpeg)
75 if output_html is not None:
76 if output_html is not None:
76 output.html = unicode(output_html)
77 output.html = cast_unicode(output_html)
77 if output_svg is not None:
78 if output_svg is not None:
78 output.svg = unicode(output_svg)
79 output.svg = cast_unicode(output_svg)
79 if output_latex is not None:
80 if output_latex is not None:
80 output.latex = unicode(output_latex)
81 output.latex = cast_unicode(output_latex)
81 if output_json is not None:
82 if output_json is not None:
82 output.json = unicode(output_json)
83 output.json = cast_unicode(output_json)
83 if output_javascript is not None:
84 if output_javascript is not None:
84 output.javascript = unicode(output_javascript)
85 output.javascript = cast_unicode(output_javascript)
85
86
86 if output_type == u'pyout':
87 if output_type == u'pyout':
87 if prompt_number is not None:
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 if output_type == u'pyerr':
91 if output_type == u'pyerr':
91 if ename is not None:
92 if ename is not None:
92 output.ename = unicode(ename)
93 output.ename = cast_unicode(ename)
93 if evalue is not None:
94 if evalue is not None:
94 output.evalue = unicode(evalue)
95 output.evalue = cast_unicode(evalue)
95 if traceback is not None:
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 if output_type == u'stream':
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 return output
102 return output
102
103
@@ -107,9 +108,9 b' def new_code_cell(input=None, prompt_number=None, outputs=None,'
107 cell = NotebookNode()
108 cell = NotebookNode()
108 cell.cell_type = u'code'
109 cell.cell_type = u'code'
109 if language is not None:
110 if language is not None:
110 cell.language = unicode(language)
111 cell.language = cast_unicode(language)
111 if input is not None:
112 if input is not None:
112 cell.input = unicode(input)
113 cell.input = cast_unicode(input)
113 if prompt_number is not None:
114 if prompt_number is not None:
114 cell.prompt_number = int(prompt_number)
115 cell.prompt_number = int(prompt_number)
115 if outputs is None:
116 if outputs is None:
@@ -130,9 +131,9 b' def new_text_cell(cell_type, source=None, rendered=None, metadata=None):'
130 if cell_type == 'plaintext':
131 if cell_type == 'plaintext':
131 cell_type = 'raw'
132 cell_type = 'raw'
132 if source is not None:
133 if source is not None:
133 cell.source = unicode(source)
134 cell.source = cast_unicode(source)
134 if rendered is not None:
135 if rendered is not None:
135 cell.rendered = unicode(rendered)
136 cell.rendered = cast_unicode(rendered)
136 cell.metadata = NotebookNode(metadata or {})
137 cell.metadata = NotebookNode(metadata or {})
137 cell.cell_type = cell_type
138 cell.cell_type = cell_type
138 return cell
139 return cell
@@ -143,9 +144,9 b' def new_heading_cell(source=None, rendered=None, level=1, metadata=None):'
143 cell = NotebookNode()
144 cell = NotebookNode()
144 cell.cell_type = u'heading'
145 cell.cell_type = u'heading'
145 if source is not None:
146 if source is not None:
146 cell.source = unicode(source)
147 cell.source = cast_unicode(source)
147 if rendered is not None:
148 if rendered is not None:
148 cell.rendered = unicode(rendered)
149 cell.rendered = cast_unicode(rendered)
149 cell.level = int(level)
150 cell.level = int(level)
150 cell.metadata = NotebookNode(metadata or {})
151 cell.metadata = NotebookNode(metadata or {})
151 return cell
152 return cell
@@ -155,7 +156,7 b' def new_worksheet(name=None, cells=None, metadata=None):'
155 """Create a worksheet by name with with a list of cells."""
156 """Create a worksheet by name with with a list of cells."""
156 ws = NotebookNode()
157 ws = NotebookNode()
157 if name is not None:
158 if name is not None:
158 ws.name = unicode(name)
159 ws.name = cast_unicode(name)
159 if cells is None:
160 if cells is None:
160 ws.cells = []
161 ws.cells = []
161 else:
162 else:
@@ -178,7 +179,7 b' def new_notebook(name=None, metadata=None, worksheets=None):'
178 else:
179 else:
179 nb.metadata = NotebookNode(metadata)
180 nb.metadata = NotebookNode(metadata)
180 if name is not None:
181 if name is not None:
181 nb.metadata.name = unicode(name)
182 nb.metadata.name = cast_unicode(name)
182 return nb
183 return nb
183
184
184
185
@@ -187,29 +188,29 b' def new_metadata(name=None, authors=None, license=None, created=None,'
187 """Create a new metadata node."""
188 """Create a new metadata node."""
188 metadata = NotebookNode()
189 metadata = NotebookNode()
189 if name is not None:
190 if name is not None:
190 metadata.name = unicode(name)
191 metadata.name = cast_unicode(name)
191 if authors is not None:
192 if authors is not None:
192 metadata.authors = list(authors)
193 metadata.authors = list(authors)
193 if created is not None:
194 if created is not None:
194 metadata.created = unicode(created)
195 metadata.created = cast_unicode(created)
195 if modified is not None:
196 if modified is not None:
196 metadata.modified = unicode(modified)
197 metadata.modified = cast_unicode(modified)
197 if license is not None:
198 if license is not None:
198 metadata.license = unicode(license)
199 metadata.license = cast_unicode(license)
199 if gistid is not None:
200 if gistid is not None:
200 metadata.gistid = unicode(gistid)
201 metadata.gistid = cast_unicode(gistid)
201 return metadata
202 return metadata
202
203
203 def new_author(name=None, email=None, affiliation=None, url=None):
204 def new_author(name=None, email=None, affiliation=None, url=None):
204 """Create a new author."""
205 """Create a new author."""
205 author = NotebookNode()
206 author = NotebookNode()
206 if name is not None:
207 if name is not None:
207 author.name = unicode(name)
208 author.name = cast_unicode(name)
208 if email is not None:
209 if email is not None:
209 author.email = unicode(email)
210 author.email = cast_unicode(email)
210 if affiliation is not None:
211 if affiliation is not None:
211 author.affiliation = unicode(affiliation)
212 author.affiliation = cast_unicode(affiliation)
212 if url is not None:
213 if url is not None:
213 author.url = unicode(url)
214 author.url = cast_unicode(url)
214 return author
215 return author
215
216
@@ -46,7 +46,7 b' class JSONReader(NotebookReader):'
46 return nb
46 return nb
47
47
48 def to_notebook(self, d, **kwargs):
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 class JSONWriter(NotebookWriter):
52 class JSONWriter(NotebookWriter):
@@ -190,7 +190,7 b' class PyWriter(NotebookWriter):'
190 lines.extend([u'# ' + line for line in input.splitlines()])
190 lines.extend([u'# ' + line for line in input.splitlines()])
191 lines.append(u'')
191 lines.append(u'')
192 lines.append('')
192 lines.append('')
193 return unicode('\n'.join(lines))
193 return u'\n'.join(lines)
194
194
195
195
196 _reader = PyReader()
196 _reader = PyReader()
@@ -32,6 +32,8 b' def restore_bytes(nb):'
32
32
33 Base64 encoding is handled elsewhere. Bytes objects in the notebook are
33 Base64 encoding is handled elsewhere. Bytes objects in the notebook are
34 always b64-encoded. We DO NOT encode/decode around file formats.
34 always b64-encoded. We DO NOT encode/decode around file formats.
35
36 Note: this is never used
35 """
37 """
36 for ws in nb.worksheets:
38 for ws in nb.worksheets:
37 for cell in ws.cells:
39 for cell in ws.cells:
@@ -9,9 +9,9 b' from ..nbbase import ('
9 new_metadata, new_author, new_heading_cell, nbformat, nbformat_minor
9 new_metadata, new_author, new_heading_cell, nbformat, nbformat_minor
10 )
10 )
11
11
12 # some random base64-encoded *bytes*
12 # some random base64-encoded *text*
13 png = encodestring(os.urandom(5))
13 png = encodestring(os.urandom(5)).decode('ascii')
14 jpeg = encodestring(os.urandom(6))
14 jpeg = encodestring(os.urandom(6)).decode('ascii')
15
15
16 ws = new_worksheet(name='worksheet1')
16 ws = new_worksheet(name='worksheet1')
17
17
@@ -1,4 +1,5 b''
1 import pprint
1 import pprint
2 from base64 import decodestring
2 from unittest import TestCase
3 from unittest import TestCase
3
4
4 from ..nbjson import reads, writes
5 from ..nbjson import reads, writes
@@ -29,5 +30,42 b' class TestJSON(formattest.NBFormatTest, TestCase):'
29 s = writes(nb0, split_lines=True)
30 s = writes(nb0, split_lines=True)
30 self.assertEqual(nbjson.reads(s),nb0)
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 self.assertEqual(md.gistid, u'21341231')
141 self.assertEqual(md.gistid, u'21341231')
142 self.assertEqual(md.authors, authors)
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 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # -*- coding: utf8 -*-
2 # -*- coding: utf8 -*-
3 import argparse
4 import traceback
5 import json
3
6
4 from IPython.external.jsonschema import Draft3Validator, validate, ValidationError
7 from IPython.external.jsonschema import Draft3Validator, validate, ValidationError
5 import IPython.external.jsonpointer as jsonpointer
8 import IPython.external.jsonpointer as jsonpointer
6 from IPython.external import argparse
7 import traceback
8 import json
9
9
10 def nbvalidate(nbjson, schema='v3.withref.json', key=None,verbose=True):
10 def nbvalidate(nbjson, schema='v3.withref.json', key=None,verbose=True):
11 v3schema = resolve_ref(json.load(open(schema,'r')))
11 v3schema = resolve_ref(json.load(open(schema,'r')))
@@ -71,7 +71,6 b' class ParallelCrashHandler(CrashHandler):'
71 base_aliases = {}
71 base_aliases = {}
72 base_aliases.update(base_ip_aliases)
72 base_aliases.update(base_ip_aliases)
73 base_aliases.update({
73 base_aliases.update({
74 'profile-dir' : 'ProfileDir.location',
75 'work-dir' : 'BaseParallelApplication.work_dir',
74 'work-dir' : 'BaseParallelApplication.work_dir',
76 'log-to-file' : 'BaseParallelApplication.log_to_file',
75 'log-to-file' : 'BaseParallelApplication.log_to_file',
77 'clean-logs' : 'BaseParallelApplication.clean_logs',
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 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
@@ -44,7 +44,7 b' from IPython.parallel.apps.baseapp import ('
44 catch_config_error,
44 catch_config_error,
45 )
45 )
46 from IPython.utils.importstring import import_item
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 from IPython.utils.traitlets import Instance, Unicode, Bool, List, Dict, TraitError
48 from IPython.utils.traitlets import Instance, Unicode, Bool, List, Dict, TraitError
49
49
50 from IPython.kernel.zmq.session import (
50 from IPython.kernel.zmq.session import (
@@ -224,13 +224,13 b' class IPControllerApp(BaseParallelApplication):'
224 location = cdict['location']
224 location = cdict['location']
225
225
226 if not location:
226 if not location:
227 if PUBLIC_IPS:
227 if public_ips():
228 location = PUBLIC_IPS[-1]
228 location = public_ips()[-1]
229 else:
229 else:
230 self.log.warn("Could not identify this machine's IP, assuming %s."
230 self.log.warn("Could not identify this machine's IP, assuming %s."
231 " You may need to specify '--location=<external_ip_address>' to help"
231 " You may need to specify '--location=<external_ip_address>' to help"
232 " IPython decide when to connect via loopback." % LOCALHOST)
232 " IPython decide when to connect via loopback." % localhost() )
233 location = LOCALHOST
233 location = localhost()
234 cdict['location'] = location
234 cdict['location'] = location
235 fname = os.path.join(self.profile_dir.security_dir, fname)
235 fname = os.path.join(self.profile_dir.security_dir, fname)
236 self.log.info("writing connection info to %s", fname)
236 self.log.info("writing connection info to %s", fname)
@@ -26,7 +26,7 b' import zmq'
26 from zmq.eventloop import ioloop, zmqstream
26 from zmq.eventloop import ioloop, zmqstream
27
27
28 from IPython.config.configurable import LoggingConfigurable
28 from IPython.config.configurable import LoggingConfigurable
29 from IPython.utils.localinterfaces import LOCALHOST
29 from IPython.utils.localinterfaces import localhost
30 from IPython.utils.traitlets import Int, Unicode, Instance, List
30 from IPython.utils.traitlets import Int, Unicode, Instance, List
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
@@ -44,8 +44,10 b' class LogWatcher(LoggingConfigurable):'
44 # configurables
44 # configurables
45 topics = List([''], config=True,
45 topics = List([''], config=True,
46 help="The ZMQ topics to subscribe to. Default is to subscribe to all messages")
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 help="ZMQ url on which to listen for log messages")
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 # internals
52 # internals
51 stream = Instance('zmq.eventloop.zmqstream.ZMQStream')
53 stream = Instance('zmq.eventloop.zmqstream.ZMQStream')
@@ -391,7 +391,7 b' class AsyncResult(object):'
391 tic = time.time()
391 tic = time.time()
392 while not self.ready() and (timeout < 0 or time.time() - tic <= timeout):
392 while not self.ready() and (timeout < 0 or time.time() - tic <= timeout):
393 self.wait(interval)
393 self.wait(interval)
394 clear_output()
394 clear_output(wait=True)
395 print("%4i/%i tasks finished after %4i s" % (self.progress, N, self.elapsed), end="")
395 print("%4i/%i tasks finished after %4i s" % (self.progress, N, self.elapsed), end="")
396 sys.stdout.flush()
396 sys.stdout.flush()
397 print()
397 print()
@@ -37,7 +37,7 b' from IPython.core.profiledir import ProfileDir, ProfileDirError'
37 from IPython.utils.capture import RichOutput
37 from IPython.utils.capture import RichOutput
38 from IPython.utils.coloransi import TermColors
38 from IPython.utils.coloransi import TermColors
39 from IPython.utils.jsonutil import rekey
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 from IPython.utils.path import get_ipython_dir
41 from IPython.utils.path import get_ipython_dir
42 from IPython.utils.py3compat import cast_bytes
42 from IPython.utils.py3compat import cast_bytes
43 from IPython.utils.traitlets import (HasTraits, Integer, Instance, Unicode,
43 from IPython.utils.traitlets import (HasTraits, Integer, Instance, Unicode,
@@ -433,13 +433,13 b' class Client(HasTraits):'
433
433
434 url = cfg['registration']
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 # location specified, and connection is expected to be local
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 # load ssh from JSON *only* if the controller is not on
439 # load ssh from JSON *only* if the controller is not on
440 # this machine
440 # this machine
441 sshserver=cfg['ssh']
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 # warn if no ssh specified, but SSH is probably needed
443 # warn if no ssh specified, but SSH is probably needed
444 # This is only a warning, because the most likely cause
444 # This is only a warning, because the most likely cause
445 # is a local Controller on a laptop whose IP is dynamic
445 # is a local Controller on a laptop whose IP is dynamic
@@ -495,7 +495,12 b' class Client(HasTraits):'
495 }
495 }
496 self._queue_handlers = {'execute_reply' : self._handle_execute_reply,
496 self._queue_handlers = {'execute_reply' : self._handle_execute_reply,
497 'apply_reply' : self._handle_apply_reply}
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 # last step: setup magics, if we are in IPython:
505 # last step: setup magics, if we are in IPython:
501
506
@@ -599,7 +604,6 b' class Client(HasTraits):'
599 self._connected=True
604 self._connected=True
600
605
601 def connect_socket(s, url):
606 def connect_socket(s, url):
602 # url = util.disambiguate_url(url, self._config['location'])
603 if self._ssh:
607 if self._ssh:
604 return tunnel.tunnel_connection(s, url, sshserver, **ssh_kwargs)
608 return tunnel.tunnel_connection(s, url, sshserver, **ssh_kwargs)
605 else:
609 else:
@@ -956,14 +960,23 b' class Client(HasTraits):'
956 view.activate(suffix)
960 view.activate(suffix)
957 return view
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 if self._closed:
969 if self._closed:
961 return
970 return
962 self.stop_spin_thread()
971 self.stop_spin_thread()
963 snames = filter(lambda n: n.endswith('socket'), dir(self))
972 snames = [ trait for trait in self.trait_names() if trait.endswith("socket") ]
964 for socket in map(lambda name: getattr(self, name), snames):
973 for name in snames:
965 if isinstance(socket, zmq.Socket) and not socket.closed:
974 socket = getattr(self, name)
966 socket.close()
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 self._closed = True
980 self._closed = True
968
981
969 def _spin_every(self, interval=1):
982 def _spin_every(self, interval=1):
@@ -89,7 +89,6 b' def sync_view_results(f, self, *args, **kwargs):'
89 view = self.view
89 view = self.view
90 if view._in_sync_results:
90 if view._in_sync_results:
91 return f(self, *args, **kwargs)
91 return f(self, *args, **kwargs)
92 print 'in sync results', f
93 view._in_sync_results = True
92 view._in_sync_results = True
94 try:
93 try:
95 ret = f(self, *args, **kwargs)
94 ret = f(self, *args, **kwargs)
@@ -30,7 +30,7 b' from zmq.eventloop.zmqstream import ZMQStream'
30
30
31 # internal:
31 # internal:
32 from IPython.utils.importstring import import_item
32 from IPython.utils.importstring import import_item
33 from IPython.utils.localinterfaces import LOCALHOST
33 from IPython.utils.localinterfaces import localhost
34 from IPython.utils.py3compat import cast_bytes
34 from IPython.utils.py3compat import cast_bytes
35 from IPython.utils.traitlets import (
35 from IPython.utils.traitlets import (
36 HasTraits, Instance, Integer, Unicode, Dict, Set, Tuple, CBytes, DottedObjectName
36 HasTraits, Instance, Integer, Unicode, Dict, Set, Tuple, CBytes, DottedObjectName
@@ -177,20 +177,25 b' class HubFactory(RegistrationFactory):'
177 def _notifier_port_default(self):
177 def _notifier_port_default(self):
178 return util.select_random_ports(1)[0]
178 return util.select_random_ports(1)[0]
179
179
180 engine_ip = Unicode(LOCALHOST, config=True,
180 engine_ip = Unicode(config=True,
181 help="IP on which to listen for engine connections. [default: loopback]")
181 help="IP on which to listen for engine connections. [default: loopback]")
182 def _engine_ip_default(self):
183 return localhost()
182 engine_transport = Unicode('tcp', config=True,
184 engine_transport = Unicode('tcp', config=True,
183 help="0MQ transport for engine connections. [default: tcp]")
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 help="IP on which to listen for client connections. [default: loopback]")
188 help="IP on which to listen for client connections. [default: loopback]")
187 client_transport = Unicode('tcp', config=True,
189 client_transport = Unicode('tcp', config=True,
188 help="0MQ transport for client connections. [default : tcp]")
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 help="IP on which to listen for monitor messages. [default: loopback]")
193 help="IP on which to listen for monitor messages. [default: loopback]")
192 monitor_transport = Unicode('tcp', config=True,
194 monitor_transport = Unicode('tcp', config=True,
193 help="0MQ transport for monitor messages. [default : tcp]")
195 help="0MQ transport for monitor messages. [default : tcp]")
196
197 _client_ip_default = _monitor_ip_default = _engine_ip_default
198
194
199
195 monitor_url = Unicode('')
200 monitor_url = Unicode('')
196
201
@@ -39,7 +39,7 b' class MongoDB(BaseDB):'
39 necessary if the default mongodb configuration does not point to your
39 necessary if the default mongodb configuration does not point to your
40 mongod instance."""
40 mongod instance."""
41 )
41 )
42 database = Unicode(config=True,
42 database = Unicode("ipython-tasks", config=True,
43 help="""The MongoDB database name to use for storing tasks for this session. If unspecified,
43 help="""The MongoDB database name to use for storing tasks for this session. If unspecified,
44 a new database will be created with the Hub's IDENT. Specifying the database will result
44 a new database will be created with the Hub's IDENT. Specifying the database will result
45 in tasks from previous sessions being available via Clients' db_query and
45 in tasks from previous sessions being available via Clients' db_query and
@@ -358,7 +358,7 b' class TaskScheduler(SessionFactory):'
358 # build fake metadata
358 # build fake metadata
359 md = dict(
359 md = dict(
360 status=u'error',
360 status=u'error',
361 engine=engine,
361 engine=engine.decode('ascii'),
362 date=datetime.now(),
362 date=datetime.now(),
363 )
363 )
364 msg = self.session.msg('apply_reply', content, parent=parent, metadata=md)
364 msg = self.session.msg('apply_reply', content, parent=parent, metadata=md)
@@ -96,7 +96,7 b' class SQLiteDB(BaseDB):'
96 location = Unicode('', config=True,
96 location = Unicode('', config=True,
97 help="""The directory containing the sqlite task database. The default
97 help="""The directory containing the sqlite task database. The default
98 is to use the cluster_dir location.""")
98 is to use the cluster_dir location.""")
99 table = Unicode("", config=True,
99 table = Unicode("ipython-tasks", config=True,
100 help="""The SQLite Table to use for storing tasks for this session. If unspecified,
100 help="""The SQLite Table to use for storing tasks for this session. If unspecified,
101 a new table will be created with the Hub's IDENT. Specifying the table will result
101 a new table will be created with the Hub's IDENT. Specifying the table will result
102 in tasks from previous sessions being available via Clients' db_query and
102 in tasks from previous sessions being available via Clients' db_query and
@@ -195,7 +195,7 b' class SQLiteDB(BaseDB):'
195
195
196 If a bad (old) table does exist, return False
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 lines = cursor.fetchall()
199 lines = cursor.fetchall()
200 if not lines:
200 if not lines:
201 # table does not exist
201 # table does not exist
@@ -241,7 +241,7 b' class SQLiteDB(BaseDB):'
241 )
241 )
242 previous_table = self.table
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 (msg_id text PRIMARY KEY,
245 (msg_id text PRIMARY KEY,
246 header dict text,
246 header dict text,
247 metadata dict text,
247 metadata dict text,
@@ -333,12 +333,12 b' class SQLiteDB(BaseDB):'
333 d['msg_id'] = msg_id
333 d['msg_id'] = msg_id
334 line = self._dict_to_list(d)
334 line = self._dict_to_list(d)
335 tups = '(%s)'%(','.join(['?']*len(line)))
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 # self._db.commit()
337 # self._db.commit()
338
338
339 def get_record(self, msg_id):
339 def get_record(self, msg_id):
340 """Get a specific Task Record, by msg_id."""
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 line = cursor.fetchone()
342 line = cursor.fetchone()
343 if line is None:
343 if line is None:
344 raise KeyError("No such msg: %r"%msg_id)
344 raise KeyError("No such msg: %r"%msg_id)
@@ -346,7 +346,7 b' class SQLiteDB(BaseDB):'
346
346
347 def update_record(self, msg_id, rec):
347 def update_record(self, msg_id, rec):
348 """Update the data in an existing record."""
348 """Update the data in an existing record."""
349 query = "UPDATE %s SET "%self.table
349 query = "UPDATE '%s' SET "%self.table
350 sets = []
350 sets = []
351 keys = sorted(rec.keys())
351 keys = sorted(rec.keys())
352 values = []
352 values = []
@@ -361,13 +361,13 b' class SQLiteDB(BaseDB):'
361
361
362 def drop_record(self, msg_id):
362 def drop_record(self, msg_id):
363 """Remove a record from the DB."""
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 # self._db.commit()
365 # self._db.commit()
366
366
367 def drop_matching_records(self, check):
367 def drop_matching_records(self, check):
368 """Remove a record from the DB."""
368 """Remove a record from the DB."""
369 expr,args = self._render_expression(check)
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 self._db.execute(query,args)
371 self._db.execute(query,args)
372 # self._db.commit()
372 # self._db.commit()
373
373
@@ -399,7 +399,7 b' class SQLiteDB(BaseDB):'
399 else:
399 else:
400 req = '*'
400 req = '*'
401 expr,args = self._render_expression(check)
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 cursor = self._db.execute(query, args)
403 cursor = self._db.execute(query, args)
404 matches = cursor.fetchall()
404 matches = cursor.fetchall()
405 records = []
405 records = []
@@ -410,7 +410,7 b' class SQLiteDB(BaseDB):'
410
410
411 def get_history(self):
411 def get_history(self):
412 """get all msg_ids, ordered by time submitted."""
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 cursor = self._db.execute(query)
414 cursor = self._db.execute(query)
415 # will be a list of length 1 tuples
415 # will be a list of length 1 tuples
416 return [ tup[0] for tup in cursor.fetchall()]
416 return [ tup[0] for tup in cursor.fetchall()]
@@ -24,7 +24,7 b' from zmq.eventloop import ioloop, zmqstream'
24
24
25 from IPython.external.ssh import tunnel
25 from IPython.external.ssh import tunnel
26 # internal
26 # internal
27 from IPython.utils.localinterfaces import LOCALHOST
27 from IPython.utils.localinterfaces import localhost
28 from IPython.utils.traitlets import (
28 from IPython.utils.traitlets import (
29 Instance, Dict, Integer, Type, Float, Integer, Unicode, CBytes, Bool
29 Instance, Dict, Integer, Type, Float, Integer, Unicode, CBytes, Bool
30 )
30 )
@@ -184,13 +184,13 b' class EngineFactory(RegistrationFactory):'
184 if self.max_heartbeat_misses > 0:
184 if self.max_heartbeat_misses > 0:
185 # Add a monitor socket which will record the last time a ping was seen
185 # Add a monitor socket which will record the last time a ping was seen
186 mon = self.context.socket(zmq.SUB)
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 mon.setsockopt(zmq.SUBSCRIBE, b"")
188 mon.setsockopt(zmq.SUBSCRIBE, b"")
189 self._hb_listener = zmqstream.ZMQStream(mon, self.loop)
189 self._hb_listener = zmqstream.ZMQStream(mon, self.loop)
190 self._hb_listener.on_recv(self._report_ping)
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 heart = Heart(hb_ping, hb_pong, hb_monitor , heart_id=identity)
195 heart = Heart(hb_ping, hb_pong, hb_monitor , heart_id=identity)
196 heart.start()
196 heart.start()
@@ -24,7 +24,7 b' import zmq'
24 from zmq.eventloop.ioloop import IOLoop
24 from zmq.eventloop.ioloop import IOLoop
25
25
26 from IPython.config.configurable import Configurable
26 from IPython.config.configurable import Configurable
27 from IPython.utils.localinterfaces import LOCALHOST
27 from IPython.utils.localinterfaces import localhost
28 from IPython.utils.traitlets import Integer, Instance, Unicode
28 from IPython.utils.traitlets import Integer, Instance, Unicode
29
29
30 from IPython.parallel.util import select_random_ports
30 from IPython.parallel.util import select_random_ports
@@ -40,16 +40,18 b' class RegistrationFactory(SessionFactory):'
40
40
41 url = Unicode('', config=True,
41 url = Unicode('', config=True,
42 help="""The 0MQ url used for registration. This sets transport, ip, and port
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 url='epgm://*:90210'"""
44 url='epgm://*:90210'"""
45 % LOCALHOST) # url takes precedence over ip,regport,transport
45 ) # url takes precedence over ip,regport,transport
46 transport = Unicode('tcp', config=True,
46 transport = Unicode('tcp', config=True,
47 help="""The 0MQ transport for communications. This will likely be
47 help="""The 0MQ transport for communications. This will likely be
48 the default of 'tcp', but other values include 'ipc', 'epgm', 'inproc'.""")
48 the default of 'tcp', but other values include 'ipc', 'epgm', 'inproc'.""")
49 ip = Unicode(LOCALHOST, config=True,
49 ip = Unicode(config=True,
50 help="""The IP address for registration. This is generally either
50 help="""The IP address for registration. This is generally either
51 '127.0.0.1' for loopback only or '*' for all interfaces.
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 regport = Integer(config=True,
55 regport = Integer(config=True,
54 help="""The port on which the Hub listens for registration.""")
56 help="""The port on which the Hub listens for registration.""")
55 def _regport_default(self):
57 def _regport_default(self):
@@ -63,8 +63,7 b' def setup():'
63 tic = time.time()
63 tic = time.time()
64 while not os.path.exists(engine_json) or not os.path.exists(client_json):
64 while not os.path.exists(engine_json) or not os.path.exists(client_json):
65 if cp.poll() is not None:
65 if cp.poll() is not None:
66 print cp.poll()
66 raise RuntimeError("The test controller exited with status %s" % cp.poll())
67 raise RuntimeError("The test controller failed to start.")
68 elif time.time()-tic > 15:
67 elif time.time()-tic > 15:
69 raise RuntimeError("Timeout waiting for the test controller to start.")
68 raise RuntimeError("Timeout waiting for the test controller to start.")
70 time.sleep(0.1)
69 time.sleep(0.1)
@@ -172,7 +172,6 b' class TestClient(ClusterTestCase):'
172 # give the monitor time to notice the message
172 # give the monitor time to notice the message
173 time.sleep(.25)
173 time.sleep(.25)
174 ahr = self.client.get_result(ar.msg_ids[0])
174 ahr = self.client.get_result(ar.msg_ids[0])
175 print ar.get(), ahr.get(), ar._single_result, ahr._single_result
176 self.assertTrue(isinstance(ahr, AsyncHubResult))
175 self.assertTrue(isinstance(ahr, AsyncHubResult))
177 self.assertEqual(ahr.get().pyout, ar.get().pyout)
176 self.assertEqual(ahr.get().pyout, ar.get().pyout)
178 ar2 = self.client.get_result(ar.msg_ids[0])
177 ar2 = self.client.get_result(ar.msg_ids[0])
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
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
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
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
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
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
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