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 |
|
13 | - if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then cd /tmp; iptest; fi | |
16 |
- if [[ $TRAVIS_PYTHON_VERSION == '3.'* ]]; then |
|
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[ |
|
31 | if sys.version_info[:2] < (2,7): | |
32 |
raise ImportError('Python Version 2. |
|
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 |
|
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', |
|
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 = |
|
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 = |
|
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. |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
511 | """Create a PNG/JPEG image object given raw data. | |
512 |
|
512 | |||
513 |
When this object is returned by an |
|
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 |
|
|
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 |
i |
|
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, |
|
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 |
i |
|
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, |
|
126 | def clear_output(self, wait=False): | |
129 |
super(CapturingDisplayPublisher, self).clear_output( |
|
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 |
|
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 = |
|
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 |
|
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 |
|
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] = |
|
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 = |
|
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. |
|
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. |
|
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. |
|
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 |
|
|
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 |
|
|
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. |
|
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). |
|
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( |
|
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 |
|
|
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 |
|
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 |
|
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 |
|
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 = s |
|
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 |
|
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 |
|
|
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( |
|
181 | gui = CaselessStrEnum(gui_keys, config=True, | |
179 |
help="Enable GUI event loop integration |
|
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: Local |
|
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. |
|
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 |
|
57 | nt.assert_true(len(_ip.alias_manager.aliases) > 10) | |
58 |
for |
|
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('.', |
|
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 |
|
|
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 |
|
|
94 | assert hasattr(magic_foo3, 'has_arguments') | |
98 |
|
95 | |||
99 |
|
|
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 |
|
|
97 | assert_equal(getattr(magic_foo4, 'argcmd_name', None), None) | |
101 |
|
|
98 | assert_equal(real_name(magic_foo4), 'foo4') | |
102 |
|
|
99 | assert_equal(magic_foo4(None, ''), argparse.Namespace()) | |
103 |
|
|
100 | assert hasattr(magic_foo4, 'has_arguments') | |
104 |
|
101 | |||
105 |
|
|
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 |
|
|
103 | assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate') | |
107 |
|
|
104 | assert_equal(real_name(magic_foo5), 'frobnicate') | |
108 |
|
|
105 | assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None)) | |
109 |
|
|
106 | assert hasattr(magic_foo5, 'has_arguments') | |
110 |
|
107 | |||
111 |
|
|
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 |
|
|
109 | assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None) | |
113 |
|
|
110 | assert_equal(real_name(magic_magic_foo), 'magic_foo') | |
114 |
|
|
111 | assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None)) | |
115 |
|
|
112 | assert hasattr(magic_magic_foo, 'has_arguments') | |
116 |
|
113 | |||
117 |
|
|
114 | assert_equal(foo.__doc__, '%foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') | |
118 |
|
|
115 | assert_equal(getattr(foo, 'argcmd_name', None), None) | |
119 |
|
|
116 | assert_equal(real_name(foo), 'foo') | |
120 |
|
|
117 | assert_equal(foo(None, ''), argparse.Namespace(foo=None)) | |
121 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
47 | nt.assert_equal(ip.prefilter('f -1'), 'f(-1)') | |
51 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
219 | staliases = db.get('stored_aliases',{}) | |
206 |
|
|
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 |
|
|
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 |
|
|
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 |
|
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 |
|
|
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 |
|
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( |
|
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 |
|
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(' |
|
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' : ' |
|
24 | * 'icon' : 'icon-terminal', // select your icon from http://fortawesome.github.io/Font-Awesome/icons | |
25 |
* 'callback': function(){ |
|
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 split |
|
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 |
|
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( |
|
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 |
|
|
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 ( |
|
393 | CodeCell.prototype.clear_output = function (wait) { | |
392 |
this.output_area.clear_output( |
|
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( |
|
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_ |
|
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. |
|
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. |
|
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. |
|
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. |
|
614 | this.clear_output(content.wait); | |
603 | }; |
|
615 | }; | |
604 |
|
616 | |||
605 |
|
617 | |||
606 |
OutputArea.prototype.clear_output = function |
|
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 |
|
|
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. |
|
171 | if (force || !this._sticky) { | |
170 |
this. |
|
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 = |
|
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 |
|
|
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 |
|
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= |
|
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( |
|
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( |
|
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 |
|
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 |
|
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 |
|
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"% |
|
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 |
|
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 |
|
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 |
|
|
57 | nt.assert_equal(expected, stripped, message) | |
60 |
|
58 |
@@ -1,7 +1,7 b'' | |||||
1 |
"""Test suite for our zeromq-based messag |
|
1 | """Test suite for our zeromq-based message specification. | |
2 | """ |
|
2 | """ | |
3 | #----------------------------------------------------------------------------- |
|
3 | #----------------------------------------------------------------------------- | |
4 |
# Copyright (C) 2010 |
|
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 |
|
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 |
# M |
|
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 |
|
|
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 |
|
|
84 | Reference.check(self, d) | |
137 | yield tst |
|
|||
138 | if d['status'] == 'ok': |
|
85 | if d['status'] == 'ok': | |
139 |
|
|
86 | ExecuteReplyOkay().check(d) | |
140 | yield tst |
|
|||
141 | elif d['status'] == 'error': |
|
87 | elif d['status'] == 'error': | |
142 |
|
|
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 |
|
|
125 | Reference.check(self, d) | |
181 | yield tst |
|
|||
182 | if d['argspec'] is not None: |
|
126 | if d['argspec'] is not None: | |
183 |
|
|
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 |
|
|
185 | assert mime_pat.match(k) | |
245 |
nt.assert_ |
|
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 |
|
|
194 | assert mime_pat.match(k) | |
254 |
nt.assert_ |
|
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 |
|
|
226 | nt.assert_equal(msg['msg_type'], msg_type) | |
286 | if parent: |
|
227 | if parent: | |
287 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
276 | nt.assert_equal(reply['status'], 'error') | |
343 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
356 | validate_message(reply, 'object_info_reply', msg_id) | |
427 | yield tst |
|
|||
428 | content = reply['content'] |
|
357 | content = reply['content'] | |
429 |
|
|
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 |
|
|
370 | validate_message(reply, 'object_info_reply', msg_id) | |
443 | yield tst |
|
|||
444 | content = reply['content'] |
|
371 | content = reply['content'] | |
445 |
|
|
372 | assert content['found'] | |
446 | argspec = content['argspec'] |
|
373 | argspec = content['argspec'] | |
447 |
|
|
374 | nt.assert_is_instance(argspec, dict, "expected non-empty argspec dict, got %r" % argspec) | |
448 |
|
|
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 |
|
|
383 | validate_message(reply, 'object_info_reply', msg_id) | |
458 | yield tst |
|
|||
459 | content = reply['content'] |
|
384 | content = reply['content'] | |
460 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
418 | validate_message(stdout, 'stream', msg_id) | |
499 | yield tst |
|
|||
500 | content = stdout['content'] |
|
419 | content = stdout['content'] | |
501 |
|
|
420 | nt.assert_equal(content['name'], u'stdout') | |
502 |
|
|
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 |
|
|
430 | validate_message(display, 'display_data', parent=msg_id) | |
513 | yield tst |
|
|||
514 | data = display['content']['data'] |
|
431 | data = display['content']['data'] | |
515 |
|
|
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 |
|
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', |
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
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= |
|
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 |
|
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 |
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
74 | nt.assert_equal(len(bufs), 2) | |
78 | obj2, remainder = unserialize_object(bufs) |
|
75 | obj2, remainder = unserialize_object(bufs) | |
79 |
|
|
76 | nt.assert_equal(remainder, []) | |
80 |
|
|
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 |
|
|
103 | nt.assert_equal(r, []) | |
108 |
|
|
104 | nt.assert_equal(A.shape, B.shape) | |
109 |
|
|
105 | nt.assert_equal(A.dtype, B.dtype) | |
110 |
|
|
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 |
|
|
122 | nt.assert_equal(r, []) | |
128 |
|
|
123 | nt.assert_equal(A.shape, B.shape) | |
129 |
|
|
124 | nt.assert_equal(A.dtype, B.dtype) | |
130 |
|
|
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 |
|
|
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 |
|
|
140 | nt.assert_equal(r, []) | |
147 |
|
|
141 | nt.assert_equal(A.shape, B.shape) | |
148 |
|
|
142 | nt.assert_equal(A.dtype, B.dtype) | |
149 |
|
|
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 |
|
|
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 |
|
|
158 | nt.assert_equal(r, []) | |
166 |
|
|
159 | nt.assert_equal(A.shape, B.shape) | |
167 |
|
|
160 | nt.assert_equal(A.dtype, B.dtype) | |
168 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
190 | nt.assert_is_instance(canned, tuple) | |
201 | t2, r = unserialize_object(bufs) |
|
191 | t2, r = unserialize_object(bufs) | |
202 |
|
|
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 |
|
|
200 | nt.assert_is_instance(canned, point) | |
212 | p2, r = unserialize_object(bufs, globals()) |
|
201 | p2, r = unserialize_object(bufs, globals()) | |
213 |
|
|
202 | nt.assert_equal(p2.x, p.x) | |
214 |
|
|
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 |
|
|
209 | nt.assert_is_instance(canned, list) | |
222 | l2, r = unserialize_object(bufs) |
|
210 | l2, r = unserialize_object(bufs) | |
223 |
|
|
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 |
|
|
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 |
|
|
227 | nt.assert_equal(D2.a, D.a) | |
241 |
|
|
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, |
|
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 |
|
|
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 |
|
|
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, |
|
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 |
|
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. |
|
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, |
|
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 |
|
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( |
|
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. |
|
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. ' |
|
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 . |
|
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 ' |
|
47 | Can a LatexExporter export using 'report' template? | |
48 | """ |
|
48 | """ | |
49 |
(output, resources) = LatexExporter(template_file=' |
|
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 ' |
|
56 | Can a LatexExporter export using 'article' template? | |
57 | """ |
|
57 | """ | |
58 |
(output, resources) = LatexExporter(template_file=' |
|
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 |
|
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 |
|
|
48 | source of the cell to highlight | |
49 | language : str |
|
49 | language : str | |
50 |
|
|
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 |
|
|
65 | source of the cell to highlight | |
64 | language : str |
|
66 | language : str | |
65 |
|
|
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 |
|
|
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 |
|
|
89 | source of the cell to highlight | |
78 | output_formatter : Pygments formatter |
|
90 | output_formatter : Pygments formatter | |
79 | language : str |
|
91 | language : str | |
80 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
' |
|
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 |
|
|
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(" |
|
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(' |
|
27 | url_prefix = Unicode('reveal.js', config=True, | |
28 | config=True, |
|
28 | help="""The URL prefix for reveal.js. | |
29 |
|
|
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 |
|
|
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= |
|
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 |
|
|
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 |
|
|
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 |
|
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 .. |
|
18 | from ..highlightmagics import HighlightMagicsPreprocessor | |
19 |
|
19 | |||
20 |
|
20 | |||
21 | #----------------------------------------------------------------------------- |
|
21 | #----------------------------------------------------------------------------- | |
22 | # Class |
|
22 | # Class | |
23 | #----------------------------------------------------------------------------- |
|
23 | #----------------------------------------------------------------------------- | |
24 |
|
24 | |||
25 |
class Test |
|
25 | class TestHighlightMagics(PreprocessorTestsBase): | |
26 |
"""Contains test functions for |
|
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 = |
|
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 |
|
36 | """Can a HighlightMagicsPreprocessor be constructed?""" | |
38 | self.build_preprocessor() |
|
37 | self.build_preprocessor() | |
39 |
|
||||
40 |
|
38 | |||
41 |
def test_ |
|
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 [{{ 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 |
|
|
16 | \documentclass{article} | |
15 |
((* |
|
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 |
((= |
|
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 |
((= |
|
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 |
|
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 |
|
9 | YOU ARE PROBABLY DOING THINGS INCORRECTLY. | |
7 |
|
10 | |||
8 |
Null template, |
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
.. |
|
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 '((= |
|
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 |
|
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 |
|
5 | YOU ARE PROBABLY DOING THINGS INCORRECTLY. | |
6 |
|
6 | |||
7 |
Null template, |
|
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. |
|
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( |
|
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 |
|
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 ( |
|
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 |
|
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 |
|
68 | out = TextIOWrapper(BytesIO(out), encoding, 'replace').read() | |
68 |
return out |
|
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 = |
|
73 | output.png = cast_unicode(output_png) | |
73 | if output_jpeg is not None: |
|
74 | if output_jpeg is not None: | |
74 |
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 |
|
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 u |
|
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 * |
|
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 |
|
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 |
|
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 |
|
227 | if public_ips(): | |
228 |
location = |
|
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." % |
|
232 | " IPython decide when to connect via loopback." % localhost() ) | |
233 |
location = |
|
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 |
|
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( |
|
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 |
|
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 == |
|
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 |
|
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 |
|
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 |
|
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( |
|
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( |
|
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( |
|
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 |
|
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' % |
|
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" % ( |
|
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 |
|
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:// |
|
43 | in one variable. For example: url='tcp://127.0.0.1:12345' or | |
44 | url='epgm://*:90210'""" |
|
44 | url='epgm://*:90210'""" | |
45 |
|
|
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( |
|
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