Show More
The requested changes are too big and content was truncated. Show full diff
@@ -0,0 +1,41 b'' | |||
|
1 | from IPython.utils.capture import capture_output | |
|
2 | ||
|
3 | import nose.tools as nt | |
|
4 | ||
|
5 | def test_alias_lifecycle(): | |
|
6 | name = 'test_alias1' | |
|
7 | cmd = 'echo "Hello"' | |
|
8 | am = _ip.alias_manager | |
|
9 | am.clear_aliases() | |
|
10 | am.define_alias(name, cmd) | |
|
11 | assert am.is_alias(name) | |
|
12 | nt.assert_equal(am.retrieve_alias(name), cmd) | |
|
13 | nt.assert_in((name, cmd), am.aliases) | |
|
14 | ||
|
15 | # Test running the alias | |
|
16 | orig_system = _ip.system | |
|
17 | result = [] | |
|
18 | _ip.system = result.append | |
|
19 | try: | |
|
20 | _ip.run_cell('%{}'.format(name)) | |
|
21 | result = [c.strip() for c in result] | |
|
22 | nt.assert_equal(result, [cmd]) | |
|
23 | finally: | |
|
24 | _ip.system = orig_system | |
|
25 | ||
|
26 | # Test removing the alias | |
|
27 | am.undefine_alias(name) | |
|
28 | assert not am.is_alias(name) | |
|
29 | with nt.assert_raises(ValueError): | |
|
30 | am.retrieve_alias(name) | |
|
31 | nt.assert_not_in((name, cmd), am.aliases) | |
|
32 | ||
|
33 | ||
|
34 | def test_alias_args_error(): | |
|
35 | """Error expanding with wrong number of arguments""" | |
|
36 | _ip.alias_manager.define_alias('parts', 'echo first %s second %s') | |
|
37 | # capture stderr: | |
|
38 | with capture_output() as cap: | |
|
39 | _ip.run_cell('parts 1') | |
|
40 | ||
|
41 | nt.assert_equal(cap.stderr.split(':')[0], 'UsageError') No newline at end of file |
@@ -0,0 +1,25 b'' | |||
|
1 | """Test NotebookApp""" | |
|
2 | ||
|
3 | #----------------------------------------------------------------------------- | |
|
4 | # Copyright (C) 2013 The IPython Development Team | |
|
5 | # | |
|
6 | # Distributed under the terms of the BSD License. The full license is in | |
|
7 | # the file COPYING, distributed as part of this software. | |
|
8 | #----------------------------------------------------------------------------- | |
|
9 | ||
|
10 | #----------------------------------------------------------------------------- | |
|
11 | # Imports | |
|
12 | #----------------------------------------------------------------------------- | |
|
13 | ||
|
14 | import nose.tools as nt | |
|
15 | ||
|
16 | import IPython.testing.tools as tt | |
|
17 | ||
|
18 | #----------------------------------------------------------------------------- | |
|
19 | # Test functions | |
|
20 | #----------------------------------------------------------------------------- | |
|
21 | ||
|
22 | def test_help_output(): | |
|
23 | """ipython notebook --help-all works""" | |
|
24 | tt.help_all_output_test('notebook') | |
|
25 |
@@ -0,0 +1,167 b'' | |||
|
1 | """utilities for testing IPython kernels""" | |
|
2 | ||
|
3 | #------------------------------------------------------------------------------- | |
|
4 | # Copyright (C) 2013 The IPython Development Team | |
|
5 | # | |
|
6 | # Distributed under the terms of the BSD License. The full license is in | |
|
7 | # the file COPYING, distributed as part of this software. | |
|
8 | #------------------------------------------------------------------------------- | |
|
9 | ||
|
10 | #------------------------------------------------------------------------------- | |
|
11 | # Imports | |
|
12 | #------------------------------------------------------------------------------- | |
|
13 | ||
|
14 | import atexit | |
|
15 | ||
|
16 | from contextlib import contextmanager | |
|
17 | from subprocess import PIPE | |
|
18 | from Queue import Empty | |
|
19 | ||
|
20 | import nose.tools as nt | |
|
21 | ||
|
22 | from IPython.kernel import KernelManager | |
|
23 | ||
|
24 | #------------------------------------------------------------------------------- | |
|
25 | # Globals | |
|
26 | #------------------------------------------------------------------------------- | |
|
27 | ||
|
28 | STARTUP_TIMEOUT = 60 | |
|
29 | TIMEOUT = 15 | |
|
30 | ||
|
31 | KM = None | |
|
32 | KC = None | |
|
33 | ||
|
34 | #------------------------------------------------------------------------------- | |
|
35 | # code | |
|
36 | #------------------------------------------------------------------------------- | |
|
37 | ||
|
38 | ||
|
39 | def start_new_kernel(): | |
|
40 | """start a new kernel, and return its Manager and Client""" | |
|
41 | km = KernelManager() | |
|
42 | km.start_kernel(stdout=PIPE, stderr=PIPE) | |
|
43 | kc = km.client() | |
|
44 | kc.start_channels() | |
|
45 | ||
|
46 | msg_id = kc.kernel_info() | |
|
47 | kc.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT) | |
|
48 | flush_channels(kc) | |
|
49 | return km, kc | |
|
50 | ||
|
51 | def flush_channels(kc=None): | |
|
52 | """flush any messages waiting on the queue""" | |
|
53 | from .test_message_spec import validate_message | |
|
54 | ||
|
55 | if kc is None: | |
|
56 | kc = KC | |
|
57 | for channel in (kc.shell_channel, kc.iopub_channel): | |
|
58 | while True: | |
|
59 | try: | |
|
60 | msg = channel.get_msg(block=True, timeout=0.1) | |
|
61 | except Empty: | |
|
62 | break | |
|
63 | else: | |
|
64 | validate_message(msg) | |
|
65 | ||
|
66 | ||
|
67 | def execute(code='', kc=None, **kwargs): | |
|
68 | """wrapper for doing common steps for validating an execution request""" | |
|
69 | from .test_message_spec import validate_message | |
|
70 | if kc is None: | |
|
71 | kc = KC | |
|
72 | msg_id = kc.execute(code=code, **kwargs) | |
|
73 | reply = kc.get_shell_msg(timeout=TIMEOUT) | |
|
74 | validate_message(reply, 'execute_reply', msg_id) | |
|
75 | busy = kc.get_iopub_msg(timeout=TIMEOUT) | |
|
76 | validate_message(busy, 'status', msg_id) | |
|
77 | nt.assert_equal(busy['content']['execution_state'], 'busy') | |
|
78 | ||
|
79 | if not kwargs.get('silent'): | |
|
80 | pyin = kc.get_iopub_msg(timeout=TIMEOUT) | |
|
81 | validate_message(pyin, 'pyin', msg_id) | |
|
82 | nt.assert_equal(pyin['content']['code'], code) | |
|
83 | ||
|
84 | return msg_id, reply['content'] | |
|
85 | ||
|
86 | def start_global_kernel(): | |
|
87 | """start the global kernel (if it isn't running) and return its client""" | |
|
88 | global KM, KC | |
|
89 | if KM is None: | |
|
90 | KM, KC = start_new_kernel() | |
|
91 | atexit.register(stop_global_kernel) | |
|
92 | return KC | |
|
93 | ||
|
94 | @contextmanager | |
|
95 | def kernel(): | |
|
96 | """Context manager for the global kernel instance | |
|
97 | ||
|
98 | Should be used for most kernel tests | |
|
99 | ||
|
100 | Returns | |
|
101 | ------- | |
|
102 | kernel_client: connected KernelClient instance | |
|
103 | """ | |
|
104 | yield start_global_kernel() | |
|
105 | ||
|
106 | def uses_kernel(test_f): | |
|
107 | """Decorator for tests that use the global kernel""" | |
|
108 | def wrapped_test(): | |
|
109 | with kernel() as kc: | |
|
110 | test_f(kc) | |
|
111 | wrapped_test.__doc__ = test_f.__doc__ | |
|
112 | wrapped_test.__name__ = test_f.__name__ | |
|
113 | return wrapped_test | |
|
114 | ||
|
115 | def stop_global_kernel(): | |
|
116 | """Stop the global shared kernel instance, if it exists""" | |
|
117 | global KM, KC | |
|
118 | KC.stop_channels() | |
|
119 | KC = None | |
|
120 | if KM is None: | |
|
121 | return | |
|
122 | KM.shutdown_kernel(now=True) | |
|
123 | KM = None | |
|
124 | ||
|
125 | @contextmanager | |
|
126 | def new_kernel(): | |
|
127 | """Context manager for a new kernel in a subprocess | |
|
128 | ||
|
129 | Should only be used for tests where the kernel must not be re-used. | |
|
130 | ||
|
131 | Returns | |
|
132 | ------- | |
|
133 | kernel_client: connected KernelClient instance | |
|
134 | """ | |
|
135 | km, kc = start_new_kernel() | |
|
136 | try: | |
|
137 | yield kc | |
|
138 | finally: | |
|
139 | kc.stop_channels() | |
|
140 | km.shutdown_kernel(now=True) | |
|
141 | ||
|
142 | ||
|
143 | def assemble_output(iopub): | |
|
144 | """assemble stdout/err from an execution""" | |
|
145 | stdout = '' | |
|
146 | stderr = '' | |
|
147 | while True: | |
|
148 | msg = iopub.get_msg(block=True, timeout=1) | |
|
149 | msg_type = msg['msg_type'] | |
|
150 | content = msg['content'] | |
|
151 | if msg_type == 'status' and content['execution_state'] == 'idle': | |
|
152 | # idle message signals end of output | |
|
153 | break | |
|
154 | elif msg['msg_type'] == 'stream': | |
|
155 | if content['name'] == 'stdout': | |
|
156 | stdout += content['data'] | |
|
157 | elif content['name'] == 'stderr': | |
|
158 | stderr += content['data'] | |
|
159 | else: | |
|
160 | raise KeyError("bad stream: %r" % content['name']) | |
|
161 | else: | |
|
162 | # other output, ignored | |
|
163 | pass | |
|
164 | return stdout, stderr | |
|
165 | ||
|
166 | ||
|
167 |
@@ -0,0 +1,316 b'' | |||
|
1 | """This module defines Exporter, a highly configurable converter | |
|
2 | that uses Jinja2 to export notebook files into different formats. | |
|
3 | """ | |
|
4 | ||
|
5 | #----------------------------------------------------------------------------- | |
|
6 | # Copyright (c) 2013, the IPython Development Team. | |
|
7 | # | |
|
8 | # Distributed under the terms of the Modified BSD License. | |
|
9 | # | |
|
10 | # The full license is in the file COPYING.txt, distributed with this software. | |
|
11 | #----------------------------------------------------------------------------- | |
|
12 | ||
|
13 | #----------------------------------------------------------------------------- | |
|
14 | # Imports | |
|
15 | #----------------------------------------------------------------------------- | |
|
16 | ||
|
17 | from __future__ import print_function, absolute_import | |
|
18 | ||
|
19 | # Stdlib imports | |
|
20 | import os | |
|
21 | ||
|
22 | # other libs/dependencies | |
|
23 | from jinja2 import Environment, FileSystemLoader, ChoiceLoader, TemplateNotFound | |
|
24 | ||
|
25 | # IPython imports | |
|
26 | from IPython.utils.traitlets import MetaHasTraits, Unicode, List, Dict, Any | |
|
27 | from IPython.utils.importstring import import_item | |
|
28 | from IPython.utils import py3compat, text | |
|
29 | ||
|
30 | from IPython.nbconvert import filters | |
|
31 | from .exporter import Exporter | |
|
32 | ||
|
33 | #----------------------------------------------------------------------------- | |
|
34 | # Globals and constants | |
|
35 | #----------------------------------------------------------------------------- | |
|
36 | ||
|
37 | #Jinja2 extensions to load. | |
|
38 | JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols'] | |
|
39 | ||
|
40 | default_filters = { | |
|
41 | 'indent': text.indent, | |
|
42 | 'markdown2html': filters.markdown2html, | |
|
43 | 'ansi2html': filters.ansi2html, | |
|
44 | 'filter_data_type': filters.DataTypeFilter, | |
|
45 | 'get_lines': filters.get_lines, | |
|
46 | 'highlight2html': filters.highlight2html, | |
|
47 | 'highlight2latex': filters.highlight2latex, | |
|
48 | 'ipython2python': filters.ipython2python, | |
|
49 | 'posix_path': filters.posix_path, | |
|
50 | 'markdown2latex': filters.markdown2latex, | |
|
51 | 'markdown2rst': filters.markdown2rst, | |
|
52 | 'comment_lines': filters.comment_lines, | |
|
53 | 'strip_ansi': filters.strip_ansi, | |
|
54 | 'strip_dollars': filters.strip_dollars, | |
|
55 | 'strip_files_prefix': filters.strip_files_prefix, | |
|
56 | 'html2text' : filters.html2text, | |
|
57 | 'add_anchor': filters.add_anchor, | |
|
58 | 'ansi2latex': filters.ansi2latex, | |
|
59 | 'strip_math_space': filters.strip_math_space, | |
|
60 | 'wrap_text': filters.wrap_text, | |
|
61 | 'escape_latex': filters.escape_latex, | |
|
62 | 'citation2latex': filters.citation2latex, | |
|
63 | 'path2url': filters.path2url, | |
|
64 | 'add_prompts': filters.add_prompts, | |
|
65 | } | |
|
66 | ||
|
67 | #----------------------------------------------------------------------------- | |
|
68 | # Class | |
|
69 | #----------------------------------------------------------------------------- | |
|
70 | ||
|
71 | class TemplateExporter(Exporter): | |
|
72 | """ | |
|
73 | Exports notebooks into other file formats. Uses Jinja 2 templating engine | |
|
74 | to output new formats. Inherit from this class if you are creating a new | |
|
75 | template type along with new filters/preprocessors. If the filters/ | |
|
76 | preprocessors provided by default suffice, there is no need to inherit from | |
|
77 | this class. Instead, override the template_file and file_extension | |
|
78 | traits via a config file. | |
|
79 | ||
|
80 | {filters} | |
|
81 | """ | |
|
82 | ||
|
83 | # finish the docstring | |
|
84 | __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys())) | |
|
85 | ||
|
86 | ||
|
87 | template_file = Unicode(u'default', | |
|
88 | config=True, | |
|
89 | help="Name of the template file to use") | |
|
90 | def _template_file_changed(self, name, old, new): | |
|
91 | if new == 'default': | |
|
92 | self.template_file = self.default_template | |
|
93 | else: | |
|
94 | self.template_file = new | |
|
95 | self.template = None | |
|
96 | self._load_template() | |
|
97 | ||
|
98 | default_template = Unicode(u'') | |
|
99 | template = Any() | |
|
100 | environment = Any() | |
|
101 | ||
|
102 | template_path = List(['.'], config=True) | |
|
103 | def _template_path_changed(self, name, old, new): | |
|
104 | self._load_template() | |
|
105 | ||
|
106 | default_template_path = Unicode( | |
|
107 | os.path.join("..", "templates"), | |
|
108 | help="Path where the template files are located.") | |
|
109 | ||
|
110 | template_skeleton_path = Unicode( | |
|
111 | os.path.join("..", "templates", "skeleton"), | |
|
112 | help="Path where the template skeleton files are located.") | |
|
113 | ||
|
114 | #Jinja block definitions | |
|
115 | jinja_comment_block_start = Unicode("", config=True) | |
|
116 | jinja_comment_block_end = Unicode("", config=True) | |
|
117 | jinja_variable_block_start = Unicode("", config=True) | |
|
118 | jinja_variable_block_end = Unicode("", config=True) | |
|
119 | jinja_logic_block_start = Unicode("", config=True) | |
|
120 | jinja_logic_block_end = Unicode("", config=True) | |
|
121 | ||
|
122 | #Extension that the template files use. | |
|
123 | template_extension = Unicode(".tpl", config=True) | |
|
124 | ||
|
125 | filters = Dict(config=True, | |
|
126 | help="""Dictionary of filters, by name and namespace, to add to the Jinja | |
|
127 | environment.""") | |
|
128 | ||
|
129 | ||
|
130 | def __init__(self, config=None, extra_loaders=None, **kw): | |
|
131 | """ | |
|
132 | Public constructor | |
|
133 | ||
|
134 | Parameters | |
|
135 | ---------- | |
|
136 | config : config | |
|
137 | User configuration instance. | |
|
138 | extra_loaders : list[of Jinja Loaders] | |
|
139 | ordered list of Jinja loader to find templates. Will be tried in order | |
|
140 | before the default FileSystem ones. | |
|
141 | template : str (optional, kw arg) | |
|
142 | Template to use when exporting. | |
|
143 | """ | |
|
144 | if not config: | |
|
145 | config = self.default_config | |
|
146 | ||
|
147 | super(Exporter, self).__init__(config=config, **kw) | |
|
148 | ||
|
149 | #Init | |
|
150 | self._init_template() | |
|
151 | self._init_environment(extra_loaders=extra_loaders) | |
|
152 | self._init_preprocessors() | |
|
153 | self._init_filters() | |
|
154 | ||
|
155 | ||
|
156 | def _load_template(self): | |
|
157 | """Load the Jinja template object from the template file | |
|
158 | ||
|
159 | This is a no-op if the template attribute is already defined, | |
|
160 | or the Jinja environment is not setup yet. | |
|
161 | ||
|
162 | This is triggered by various trait changes that would change the template. | |
|
163 | """ | |
|
164 | if self.template is not None: | |
|
165 | return | |
|
166 | # called too early, do nothing | |
|
167 | if self.environment is None: | |
|
168 | return | |
|
169 | # Try different template names during conversion. First try to load the | |
|
170 | # template by name with extension added, then try loading the template | |
|
171 | # as if the name is explicitly specified, then try the name as a | |
|
172 | # 'flavor', and lastly just try to load the template by module name. | |
|
173 | module_name = self.__module__.rsplit('.', 1)[-1] | |
|
174 | try_names = [] | |
|
175 | if self.template_file: | |
|
176 | try_names.extend([ | |
|
177 | self.template_file + self.template_extension, | |
|
178 | self.template_file, | |
|
179 | module_name + '_' + self.template_file + self.template_extension, | |
|
180 | ]) | |
|
181 | try_names.append(module_name + self.template_extension) | |
|
182 | for try_name in try_names: | |
|
183 | self.log.debug("Attempting to load template %s", try_name) | |
|
184 | try: | |
|
185 | self.template = self.environment.get_template(try_name) | |
|
186 | except (TemplateNotFound, IOError): | |
|
187 | pass | |
|
188 | except Exception as e: | |
|
189 | self.log.warn("Unexpected exception loading template: %s", try_name, exc_info=True) | |
|
190 | else: | |
|
191 | self.log.info("Loaded template %s", try_name) | |
|
192 | break | |
|
193 | ||
|
194 | def from_notebook_node(self, nb, resources=None, **kw): | |
|
195 | """ | |
|
196 | Convert a notebook from a notebook node instance. | |
|
197 | ||
|
198 | Parameters | |
|
199 | ---------- | |
|
200 | nb : Notebook node | |
|
201 | resources : dict (**kw) | |
|
202 | of additional resources that can be accessed read/write by | |
|
203 | preprocessors and filters. | |
|
204 | """ | |
|
205 | nb_copy, resources = super(TemplateExporter, self).from_notebook_node(nb, resources, **kw) | |
|
206 | ||
|
207 | self._load_template() | |
|
208 | ||
|
209 | if self.template is not None: | |
|
210 | output = self.template.render(nb=nb_copy, resources=resources) | |
|
211 | else: | |
|
212 | raise IOError('template file "%s" could not be found' % self.template_file) | |
|
213 | return output, resources | |
|
214 | ||
|
215 | ||
|
216 | def register_filter(self, name, jinja_filter): | |
|
217 | """ | |
|
218 | Register a filter. | |
|
219 | A filter is a function that accepts and acts on one string. | |
|
220 | The filters are accesible within the Jinja templating engine. | |
|
221 | ||
|
222 | Parameters | |
|
223 | ---------- | |
|
224 | name : str | |
|
225 | name to give the filter in the Jinja engine | |
|
226 | filter : filter | |
|
227 | """ | |
|
228 | if jinja_filter is None: | |
|
229 | raise TypeError('filter') | |
|
230 | isclass = isinstance(jinja_filter, type) | |
|
231 | constructed = not isclass | |
|
232 | ||
|
233 | #Handle filter's registration based on it's type | |
|
234 | if constructed and isinstance(jinja_filter, py3compat.string_types): | |
|
235 | #filter is a string, import the namespace and recursively call | |
|
236 | #this register_filter method | |
|
237 | filter_cls = import_item(jinja_filter) | |
|
238 | return self.register_filter(name, filter_cls) | |
|
239 | ||
|
240 | if constructed and hasattr(jinja_filter, '__call__'): | |
|
241 | #filter is a function, no need to construct it. | |
|
242 | self.environment.filters[name] = jinja_filter | |
|
243 | return jinja_filter | |
|
244 | ||
|
245 | elif isclass and isinstance(jinja_filter, MetaHasTraits): | |
|
246 | #filter is configurable. Make sure to pass in new default for | |
|
247 | #the enabled flag if one was specified. | |
|
248 | filter_instance = jinja_filter(parent=self) | |
|
249 | self.register_filter(name, filter_instance ) | |
|
250 | ||
|
251 | elif isclass: | |
|
252 | #filter is not configurable, construct it | |
|
253 | filter_instance = jinja_filter() | |
|
254 | self.register_filter(name, filter_instance) | |
|
255 | ||
|
256 | else: | |
|
257 | #filter is an instance of something without a __call__ | |
|
258 | #attribute. | |
|
259 | raise TypeError('filter') | |
|
260 | ||
|
261 | ||
|
262 | def _init_template(self): | |
|
263 | """ | |
|
264 | Make sure a template name is specified. If one isn't specified, try to | |
|
265 | build one from the information we know. | |
|
266 | """ | |
|
267 | self._template_file_changed('template_file', self.template_file, self.template_file) | |
|
268 | ||
|
269 | ||
|
270 | def _init_environment(self, extra_loaders=None): | |
|
271 | """ | |
|
272 | Create the Jinja templating environment. | |
|
273 | """ | |
|
274 | here = os.path.dirname(os.path.realpath(__file__)) | |
|
275 | loaders = [] | |
|
276 | if extra_loaders: | |
|
277 | loaders.extend(extra_loaders) | |
|
278 | ||
|
279 | paths = self.template_path | |
|
280 | paths.extend([os.path.join(here, self.default_template_path), | |
|
281 | os.path.join(here, self.template_skeleton_path)]) | |
|
282 | loaders.append(FileSystemLoader(paths)) | |
|
283 | ||
|
284 | self.environment = Environment( | |
|
285 | loader= ChoiceLoader(loaders), | |
|
286 | extensions=JINJA_EXTENSIONS | |
|
287 | ) | |
|
288 | ||
|
289 | #Set special Jinja2 syntax that will not conflict with latex. | |
|
290 | if self.jinja_logic_block_start: | |
|
291 | self.environment.block_start_string = self.jinja_logic_block_start | |
|
292 | if self.jinja_logic_block_end: | |
|
293 | self.environment.block_end_string = self.jinja_logic_block_end | |
|
294 | if self.jinja_variable_block_start: | |
|
295 | self.environment.variable_start_string = self.jinja_variable_block_start | |
|
296 | if self.jinja_variable_block_end: | |
|
297 | self.environment.variable_end_string = self.jinja_variable_block_end | |
|
298 | if self.jinja_comment_block_start: | |
|
299 | self.environment.comment_start_string = self.jinja_comment_block_start | |
|
300 | if self.jinja_comment_block_end: | |
|
301 | self.environment.comment_end_string = self.jinja_comment_block_end | |
|
302 | ||
|
303 | ||
|
304 | def _init_filters(self): | |
|
305 | """ | |
|
306 | Register all of the filters required for the exporter. | |
|
307 | """ | |
|
308 | ||
|
309 | #Add default filters to the Jinja2 environment | |
|
310 | for key, value in default_filters.items(): | |
|
311 | self.register_filter(key, value) | |
|
312 | ||
|
313 | #Load user filters. Overwrite existing filters if need be. | |
|
314 | if self.filters: | |
|
315 | for key, user_filter in self.filters.items(): | |
|
316 | self.register_filter(key, user_filter) |
@@ -0,0 +1,108 b'' | |||
|
1 | """ | |
|
2 | Module with tests for templateexporter.py | |
|
3 | """ | |
|
4 | ||
|
5 | #----------------------------------------------------------------------------- | |
|
6 | # Copyright (c) 2013, the IPython Development Team. | |
|
7 | # | |
|
8 | # Distributed under the terms of the Modified BSD License. | |
|
9 | # | |
|
10 | # The full license is in the file COPYING.txt, distributed with this software. | |
|
11 | #----------------------------------------------------------------------------- | |
|
12 | ||
|
13 | #----------------------------------------------------------------------------- | |
|
14 | # Imports | |
|
15 | #----------------------------------------------------------------------------- | |
|
16 | ||
|
17 | from IPython.config import Config | |
|
18 | ||
|
19 | from .base import ExportersTestsBase | |
|
20 | from .cheese import CheesePreprocessor | |
|
21 | from ..templateexporter import TemplateExporter | |
|
22 | ||
|
23 | ||
|
24 | #----------------------------------------------------------------------------- | |
|
25 | # Class | |
|
26 | #----------------------------------------------------------------------------- | |
|
27 | ||
|
28 | class TestExporter(ExportersTestsBase): | |
|
29 | """Contains test functions for exporter.py""" | |
|
30 | ||
|
31 | ||
|
32 | def test_constructor(self): | |
|
33 | """ | |
|
34 | Can a TemplateExporter be constructed? | |
|
35 | """ | |
|
36 | TemplateExporter() | |
|
37 | ||
|
38 | ||
|
39 | def test_export(self): | |
|
40 | """ | |
|
41 | Can a TemplateExporter export something? | |
|
42 | """ | |
|
43 | exporter = self._make_exporter() | |
|
44 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
45 | assert len(output) > 0 | |
|
46 | ||
|
47 | ||
|
48 | def test_extract_outputs(self): | |
|
49 | """ | |
|
50 | If the ExtractOutputPreprocessor is enabled, are outputs extracted? | |
|
51 | """ | |
|
52 | config = Config({'ExtractOutputPreprocessor': {'enabled': True}}) | |
|
53 | exporter = self._make_exporter(config=config) | |
|
54 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
55 | assert resources is not None | |
|
56 | assert isinstance(resources['outputs'], dict) | |
|
57 | assert len(resources['outputs']) > 0 | |
|
58 | ||
|
59 | ||
|
60 | def test_preprocessor_class(self): | |
|
61 | """ | |
|
62 | Can a preprocessor be added to the preprocessors list by class type? | |
|
63 | """ | |
|
64 | config = Config({'Exporter': {'preprocessors': [CheesePreprocessor]}}) | |
|
65 | exporter = self._make_exporter(config=config) | |
|
66 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
67 | assert resources is not None | |
|
68 | assert resources['cheese'] == 'real' | |
|
69 | ||
|
70 | ||
|
71 | def test_preprocessor_instance(self): | |
|
72 | """ | |
|
73 | Can a preprocessor be added to the preprocessors list by instance? | |
|
74 | """ | |
|
75 | config = Config({'Exporter': {'preprocessors': [CheesePreprocessor()]}}) | |
|
76 | exporter = self._make_exporter(config=config) | |
|
77 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
78 | assert resources is not None | |
|
79 | assert resources['cheese'] == 'real' | |
|
80 | ||
|
81 | ||
|
82 | def test_preprocessor_dottedobjectname(self): | |
|
83 | """ | |
|
84 | Can a preprocessor be added to the preprocessors list by dotted object name? | |
|
85 | """ | |
|
86 | config = Config({'Exporter': {'preprocessors': ['IPython.nbconvert.exporters.tests.cheese.CheesePreprocessor']}}) | |
|
87 | exporter = self._make_exporter(config=config) | |
|
88 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
89 | assert resources is not None | |
|
90 | assert resources['cheese'] == 'real' | |
|
91 | ||
|
92 | ||
|
93 | def test_preprocessor_via_method(self): | |
|
94 | """ | |
|
95 | Can a preprocessor be added via the Exporter convenience method? | |
|
96 | """ | |
|
97 | exporter = self._make_exporter() | |
|
98 | exporter.register_preprocessor(CheesePreprocessor, enabled=True) | |
|
99 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
100 | assert resources is not None | |
|
101 | assert resources['cheese'] == 'real' | |
|
102 | ||
|
103 | ||
|
104 | def _make_exporter(self, config=None): | |
|
105 | # Create the exporter instance, make sure to set a template name since | |
|
106 | # the base TemplateExporter doesn't have a template associated with it. | |
|
107 | exporter = TemplateExporter(config=config, template_file='python') | |
|
108 | return exporter |
@@ -0,0 +1,72 b'' | |||
|
1 | """Citation handling for LaTeX output.""" | |
|
2 | ||
|
3 | #----------------------------------------------------------------------------- | |
|
4 | # Copyright (c) 2013, the IPython Development Team. | |
|
5 | # | |
|
6 | # Distributed under the terms of the Modified BSD License. | |
|
7 | # | |
|
8 | # The full license is in the file COPYING.txt, distributed with this software. | |
|
9 | #----------------------------------------------------------------------------- | |
|
10 | ||
|
11 | #----------------------------------------------------------------------------- | |
|
12 | # Code | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | ||
|
16 | __all__ = ['citation2latex'] | |
|
17 | ||
|
18 | ||
|
19 | def citation2latex(s): | |
|
20 | """Parse citations in Markdown cells. | |
|
21 | ||
|
22 | This looks for HTML tags having a data attribute names `data-cite` | |
|
23 | and replaces it by the call to LaTeX cite command. The tranformation | |
|
24 | looks like this: | |
|
25 | ||
|
26 | `<cite data-cite="granger">(Granger, 2013)</cite>` | |
|
27 | ||
|
28 | Becomes | |
|
29 | ||
|
30 | `\\cite{granger}` | |
|
31 | ||
|
32 | Any HTML tag can be used, which allows the citations to be formatted | |
|
33 | in HTML in any manner. | |
|
34 | """ | |
|
35 | try: | |
|
36 | from lxml import html | |
|
37 | except ImportError: | |
|
38 | return s | |
|
39 | ||
|
40 | tree = html.fragment_fromstring(s, create_parent='div') | |
|
41 | _process_node_cite(tree) | |
|
42 | s = html.tostring(tree, encoding='unicode') | |
|
43 | if s.endswith('</div>'): | |
|
44 | s = s[:-6] | |
|
45 | if s.startswith('<div>'): | |
|
46 | s = s[5:] | |
|
47 | return s | |
|
48 | ||
|
49 | ||
|
50 | def _process_node_cite(node): | |
|
51 | """Do the citation replacement as we walk the lxml tree.""" | |
|
52 | ||
|
53 | def _get(o, name): | |
|
54 | value = getattr(o, name, None) | |
|
55 | return '' if value is None else value | |
|
56 | ||
|
57 | if 'data-cite' in node.attrib: | |
|
58 | cite = '\cite{%(ref)s}' % {'ref': node.attrib['data-cite']} | |
|
59 | prev = node.getprevious() | |
|
60 | if prev is not None: | |
|
61 | prev.tail = _get(prev, 'tail') + cite + _get(node, 'tail') | |
|
62 | else: | |
|
63 | parent = node.getparent() | |
|
64 | if parent is not None: | |
|
65 | parent.text = _get(parent, 'text') + cite + _get(node, 'tail') | |
|
66 | try: | |
|
67 | node.getparent().remove(node) | |
|
68 | except AttributeError: | |
|
69 | pass | |
|
70 | else: | |
|
71 | for child in node: | |
|
72 | _process_node_cite(child) |
@@ -0,0 +1,58 b'' | |||
|
1 | #----------------------------------------------------------------------------- | |
|
2 | # Copyright (c) 2013, the IPython Development Team. | |
|
3 | # | |
|
4 | # Distributed under the terms of the Modified BSD License. | |
|
5 | # | |
|
6 | # The full license is in the file COPYING.txt, distributed with this software. | |
|
7 | #----------------------------------------------------------------------------- | |
|
8 | ||
|
9 | #----------------------------------------------------------------------------- | |
|
10 | # Imports | |
|
11 | #----------------------------------------------------------------------------- | |
|
12 | ||
|
13 | from ..citation import citation2latex | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Tests | |
|
17 | #----------------------------------------------------------------------------- | |
|
18 | ||
|
19 | test_md = """ | |
|
20 | # My Heading | |
|
21 | ||
|
22 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ac magna non augue | |
|
23 | porttitor scelerisque ac id diam <cite data-cite="granger">Granger</cite>. Mauris elit | |
|
24 | velit, lobortis sed interdum at, vestibulum vitae libero <strong data-cite="fperez">Perez</strong>. | |
|
25 | Lorem ipsum dolor sit amet, consectetur adipiscing elit | |
|
26 | <em data-cite="takluyver">Thomas</em>. Quisque iaculis ligula ut ipsum mattis viverra. | |
|
27 | ||
|
28 | <p>Here is a plain paragraph that should be unaffected.</p> | |
|
29 | ||
|
30 | * One <cite data-cite="jdfreder">Jonathan</cite>. | |
|
31 | * Two <cite data-cite="carreau">Matthias</cite>. | |
|
32 | * Three <cite data-cite="ivanov">Paul</cite>. | |
|
33 | """ | |
|
34 | ||
|
35 | test_md_parsed = """ | |
|
36 | # My Heading | |
|
37 | ||
|
38 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ac magna non augue | |
|
39 | porttitor scelerisque ac id diam \cite{granger}. Mauris elit | |
|
40 | velit, lobortis sed interdum at, vestibulum vitae libero \cite{fperez}. | |
|
41 | Lorem ipsum dolor sit amet, consectetur adipiscing elit | |
|
42 | \cite{takluyver}. Quisque iaculis ligula ut ipsum mattis viverra. | |
|
43 | ||
|
44 | <p>Here is a plain paragraph that should be unaffected.</p> | |
|
45 | ||
|
46 | * One \cite{jdfreder}. | |
|
47 | * Two \cite{carreau}. | |
|
48 | * Three \cite{ivanov}. | |
|
49 | """ | |
|
50 | ||
|
51 | def test_citation2latex(): | |
|
52 | """Are citations parsed properly?""" | |
|
53 | try: | |
|
54 | import lxml | |
|
55 | except ImportError: | |
|
56 | assert test_md == citation2latex(test_md) | |
|
57 | else: | |
|
58 | assert test_md_parsed == citation2latex(test_md) |
@@ -0,0 +1,113 b'' | |||
|
1 | """This preprocessor detect cells using a different language through | |
|
2 | magic extensions such as `%%R` or `%%octave`. Cell's metadata is marked | |
|
3 | so that the appropriate highlighter can be used in the `highlight` | |
|
4 | filter. | |
|
5 | """ | |
|
6 | ||
|
7 | #----------------------------------------------------------------------------- | |
|
8 | # Copyright (c) 2013, the IPython Development Team. | |
|
9 | # | |
|
10 | # Distributed under the terms of the Modified BSD License. | |
|
11 | # | |
|
12 | # The full license is in the file COPYING.txt, distributed with this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
18 | ||
|
19 | from __future__ import print_function, absolute_import | |
|
20 | ||
|
21 | import re | |
|
22 | ||
|
23 | # Our own imports | |
|
24 | from .base import Preprocessor | |
|
25 | from IPython.utils.traitlets import Dict | |
|
26 | ||
|
27 | #----------------------------------------------------------------------------- | |
|
28 | # Classes | |
|
29 | #----------------------------------------------------------------------------- | |
|
30 | ||
|
31 | ||
|
32 | class HighlightMagicsPreprocessor(Preprocessor): | |
|
33 | """ | |
|
34 | Detects and tags code cells that use a different languages than Python. | |
|
35 | """ | |
|
36 | ||
|
37 | # list of magic language extensions and their associated pygment lexers | |
|
38 | default_languages = Dict( | |
|
39 | default_value={ | |
|
40 | '%%R': 'r', | |
|
41 | '%%bash': 'bash', | |
|
42 | '%%cython': 'cython', | |
|
43 | '%%javascript': 'javascript', | |
|
44 | '%%julia': 'julia', | |
|
45 | '%%latex': 'latex', | |
|
46 | '%%octave': 'octave', | |
|
47 | '%%perl': 'perl', | |
|
48 | '%%ruby': 'ruby', | |
|
49 | '%%sh': 'sh'}) | |
|
50 | ||
|
51 | # user defined language extensions | |
|
52 | languages = Dict( | |
|
53 | config=True, | |
|
54 | help=("Syntax highlighting for magic's extension languages. " | |
|
55 | "Each item associates a language magic extension such as %%R, " | |
|
56 | "with a pygments lexer such as r.")) | |
|
57 | ||
|
58 | def __init__(self, config=None, **kw): | |
|
59 | """Public constructor""" | |
|
60 | ||
|
61 | super(HighlightMagicsPreprocessor, self).__init__(config=config, **kw) | |
|
62 | ||
|
63 | # Update the default languages dict with the user configured ones | |
|
64 | self.default_languages.update(self.languages) | |
|
65 | ||
|
66 | # build a regular expression to catch language extensions and choose | |
|
67 | # an adequate pygments lexer | |
|
68 | any_language = "|".join(self.default_languages.keys()) | |
|
69 | self.re_magic_language = re.compile( | |
|
70 | r'^\s*({0})\s+'.format(any_language)) | |
|
71 | ||
|
72 | def which_magic_language(self, source): | |
|
73 | """ | |
|
74 | When a cell uses another language through a magic extension, | |
|
75 | the other language is returned. | |
|
76 | If no language magic is detected, this function returns None. | |
|
77 | ||
|
78 | Parameters | |
|
79 | ---------- | |
|
80 | source: str | |
|
81 | Source code of the cell to highlight | |
|
82 | """ | |
|
83 | ||
|
84 | m = self.re_magic_language.match(source) | |
|
85 | ||
|
86 | if m: | |
|
87 | # By construction of the re, the matched language must be in the | |
|
88 | # languages dictionary | |
|
89 | return self.default_languages[m.group(1)] | |
|
90 | else: | |
|
91 | return None | |
|
92 | ||
|
93 | def preprocess_cell(self, cell, resources, cell_index): | |
|
94 | """ | |
|
95 | Tags cells using a magic extension language | |
|
96 | ||
|
97 | Parameters | |
|
98 | ---------- | |
|
99 | cell : NotebookNode cell | |
|
100 | Notebook cell being processed | |
|
101 | resources : dictionary | |
|
102 | Additional resources used in the conversion process. Allows | |
|
103 | preprocessors to pass variables into the Jinja engine. | |
|
104 | cell_index : int | |
|
105 | Index of the cell being processed (see base.py) | |
|
106 | """ | |
|
107 | ||
|
108 | # Only tag code cells | |
|
109 | if hasattr(cell, "input") and cell.cell_type == "code": | |
|
110 | magic_language = self.which_magic_language(cell.input) | |
|
111 | if magic_language: | |
|
112 | cell['metadata']['magics_language'] = magic_language | |
|
113 | return cell, resources |
@@ -0,0 +1,197 b'' | |||
|
1 | ((= Latex base template (must inherit) | |
|
2 | This template builds upon the abstract template, adding common latex output | |
|
3 | functions. Figures, data_text, | |
|
4 | This template does not define a docclass, the inheriting class must define this.=)) | |
|
5 | ||
|
6 | ((*- extends 'display_priority.tplx' -*)) | |
|
7 | ||
|
8 | %=============================================================================== | |
|
9 | % Abstract overrides | |
|
10 | %=============================================================================== | |
|
11 | ||
|
12 | ((* block header *)) | |
|
13 | ((* block docclass *))((* endblock docclass *)) | |
|
14 | ||
|
15 | ((* block packages *)) | |
|
16 | \usepackage{graphicx} % Used to insert images | |
|
17 | \usepackage{adjustbox} % Used to constrain images to a maximum size | |
|
18 | \usepackage{color} % Allow colors to be defined | |
|
19 | \usepackage{enumerate} % Needed for markdown enumerations to work | |
|
20 | \usepackage{geometry} % Used to adjust the document margins | |
|
21 | \usepackage{amsmath} % Equations | |
|
22 | \usepackage{amssymb} % Equations | |
|
23 | \usepackage[utf8]{inputenc} % Allow utf-8 characters in the tex document | |
|
24 | \usepackage{ucs} % Extended unicode (utf-8) support | |
|
25 | \usepackage{fancyvrb} % verbatim replacement that allows latex | |
|
26 | \usepackage{grffile} % extends the file name processing of package graphics | |
|
27 | % to support a larger range | |
|
28 | % The hyperref package gives us a pdf with properly built | |
|
29 | % internal navigation ('pdf bookmarks' for the table of contents, | |
|
30 | % internal cross-reference links, web links for URLs, etc.) | |
|
31 | \usepackage{hyperref} | |
|
32 | ((* endblock packages *)) | |
|
33 | ||
|
34 | ((* block definitions *)) | |
|
35 | \definecolor{orange}{cmyk}{0,0.4,0.8,0.2} | |
|
36 | \definecolor{darkorange}{rgb}{.71,0.21,0.01} | |
|
37 | \definecolor{darkgreen}{rgb}{.12,.54,.11} | |
|
38 | \definecolor{myteal}{rgb}{.26, .44, .56} | |
|
39 | \definecolor{gray}{gray}{0.45} | |
|
40 | \definecolor{lightgray}{gray}{.95} | |
|
41 | \definecolor{mediumgray}{gray}{.8} | |
|
42 | \definecolor{inputbackground}{rgb}{.95, .95, .85} | |
|
43 | \definecolor{outputbackground}{rgb}{.95, .95, .95} | |
|
44 | \definecolor{traceback}{rgb}{1, .95, .95} | |
|
45 | % new ansi colors | |
|
46 | \definecolor{brown}{rgb}{0.54,0.27,0.07} | |
|
47 | \definecolor{purple}{rgb}{0.5,0.0,0.5} | |
|
48 | \definecolor{darkgray}{gray}{0.25} | |
|
49 | \definecolor{lightred}{rgb}{1.0,0.39,0.28} | |
|
50 | \definecolor{lightgreen}{rgb}{0.48,0.99,0.0} | |
|
51 | \definecolor{lightblue}{rgb}{0.53,0.81,0.92} | |
|
52 | \definecolor{lightpurple}{rgb}{0.87,0.63,0.87} | |
|
53 | \definecolor{lightcyan}{rgb}{0.5,1.0,0.83} | |
|
54 | % Define a nice break command that doesn't care if a line doesn't already | |
|
55 | % exist. | |
|
56 | \def\br{\hspace*{\fill} \\* } | |
|
57 | % Math Jax compatability definitions | |
|
58 | \def\gt{>} | |
|
59 | \def\lt{<} | |
|
60 | % Document parameters | |
|
61 | ((* block title *))\title{((( resources.metadata.name | escape_latex )))}((* endblock title *)) | |
|
62 | ((* block date *))((* endblock date *)) | |
|
63 | ((* block author *))((* endblock author *)) | |
|
64 | ((* endblock definitions *)) | |
|
65 | ||
|
66 | ((* block commands *)) | |
|
67 | % Prevent overflowing lines due to hard-to-break entities | |
|
68 | \sloppy | |
|
69 | % Setup hyperref package | |
|
70 | \hypersetup{ | |
|
71 | breaklinks=true, % so long urls are correctly broken across lines | |
|
72 | colorlinks=true, | |
|
73 | urlcolor=blue, | |
|
74 | linkcolor=darkorange, | |
|
75 | citecolor=darkgreen, | |
|
76 | } | |
|
77 | % Slightly bigger margins than the latex defaults | |
|
78 | ((* block margins *)) | |
|
79 | \geometry{verbose,tmargin=1in,bmargin=1in,lmargin=1in,rmargin=1in} | |
|
80 | ((* endblock margins *)) | |
|
81 | ((* endblock commands *)) | |
|
82 | ((* endblock header *)) | |
|
83 | ||
|
84 | ((* block body *)) | |
|
85 | \begin{document} | |
|
86 | ||
|
87 | ((* block predoc *)) | |
|
88 | ((* block maketitle *))\maketitle((* endblock maketitle *)) | |
|
89 | ((* block abstract *))((* endblock abstract *)) | |
|
90 | ((* endblock predoc *)) | |
|
91 | ||
|
92 | ((( super() ))) | |
|
93 | ||
|
94 | % Add a bibliography block to the postdoc | |
|
95 | ((* block postdoc *)) | |
|
96 | ((* block bibliography *))((* endblock bibliography *)) | |
|
97 | ((* endblock postdoc *)) | |
|
98 | \end{document} | |
|
99 | ((* endblock body *)) | |
|
100 | ||
|
101 | %=============================================================================== | |
|
102 | % Support blocks | |
|
103 | %=============================================================================== | |
|
104 | ||
|
105 | % Displaying simple data text | |
|
106 | ((* block data_text *)) | |
|
107 | \begin{verbatim} | |
|
108 | ((( output.text ))) | |
|
109 | \end{verbatim} | |
|
110 | ((* endblock data_text *)) | |
|
111 | ||
|
112 | % Display python error text as-is | |
|
113 | ((* block pyerr *)) | |
|
114 | \begin{Verbatim}[commandchars=\\\{\}] | |
|
115 | ((( super() ))) | |
|
116 | \end{Verbatim} | |
|
117 | ((* endblock pyerr *)) | |
|
118 | ((* block traceback_line *)) | |
|
119 | ((( line | indent | strip_ansi | escape_latex ))) | |
|
120 | ((* endblock traceback_line *)) | |
|
121 | ||
|
122 | % Display stream ouput with coloring | |
|
123 | ((* block stream *)) | |
|
124 | \begin{Verbatim}[commandchars=\\\{\}] | |
|
125 | ((( output.text | escape_latex | ansi2latex ))) | |
|
126 | \end{Verbatim} | |
|
127 | ((* endblock stream *)) | |
|
128 | ||
|
129 | % Display latex | |
|
130 | ((* block data_latex -*)) | |
|
131 | ((*- if output.latex.startswith('$'): -*)) | |
|
132 | ((= Replace $ symbols with more explicit, equation block. =)) | |
|
133 | \begin{equation*} | |
|
134 | ((( output.latex | strip_dollars ))) | |
|
135 | \end{equation*} | |
|
136 | ((*- else -*)) | |
|
137 | ((( output.latex ))) | |
|
138 | ((*- endif *)) | |
|
139 | ((* endblock data_latex *)) | |
|
140 | ||
|
141 | % Default mechanism for rendering figures | |
|
142 | ((*- block data_png -*))((( draw_figure(output.png_filename) )))((*- endblock -*)) | |
|
143 | ((*- block data_jpg -*))((( draw_figure(output.jpeg_filename) )))((*- endblock -*)) | |
|
144 | ((*- block data_svg -*))((( draw_figure(output.svg_filename) )))((*- endblock -*)) | |
|
145 | ((*- block data_pdf -*))((( draw_figure(output.pdf_filename) )))((*- endblock -*)) | |
|
146 | ||
|
147 | % Draw a figure using the graphicx package. | |
|
148 | ((* macro draw_figure(filename) -*)) | |
|
149 | ((* set filename = filename | posix_path *)) | |
|
150 | ((*- block figure scoped -*)) | |
|
151 | \begin{center} | |
|
152 | \adjustimage{max size={0.9\linewidth}{0.9\paperheight}}{((( filename )))} | |
|
153 | \end{center} | |
|
154 | { \hspace*{\fill} \\} | |
|
155 | ((*- endblock figure -*)) | |
|
156 | ((*- endmacro *)) | |
|
157 | ||
|
158 | % Draw heading cell. Explicitly map different cell levels. | |
|
159 | ((* block headingcell scoped *)) | |
|
160 | ||
|
161 | ((* if cell.level == 1 -*)) | |
|
162 | ((* block h1 -*))\section((* endblock h1 -*)) | |
|
163 | ((* elif cell.level == 2 -*)) | |
|
164 | ((* block h2 -*))\subsection((* endblock h2 -*)) | |
|
165 | ((* elif cell.level == 3 -*)) | |
|
166 | ((* block h3 -*))\subsubsection((* endblock h3 -*)) | |
|
167 | ((* elif cell.level == 4 -*)) | |
|
168 | ((* block h4 -*))\paragraph((* endblock h4 -*)) | |
|
169 | ((* elif cell.level == 5 -*)) | |
|
170 | ((* block h5 -*))\subparagraph((* endblock h5 -*)) | |
|
171 | ((* elif cell.level == 6 -*)) | |
|
172 | ((* block h6 -*))\\*\textit((* endblock h6 -*)) | |
|
173 | ((*- endif -*)) | |
|
174 | {((( cell.source | replace('\n', ' ') | citation2latex | markdown2latex )))} | |
|
175 | ||
|
176 | ((* endblock headingcell *)) | |
|
177 | ||
|
178 | % Redirect pyout to display data priority. | |
|
179 | ((* block pyout scoped *)) | |
|
180 | ((* block data_priority scoped *)) | |
|
181 | ((( super() ))) | |
|
182 | ((* endblock *)) | |
|
183 | ((* endblock pyout *)) | |
|
184 | ||
|
185 | % Render markdown | |
|
186 | ((* block markdowncell scoped *)) | |
|
187 | ((( cell.source | citation2latex | markdown2latex ))) | |
|
188 | ((* endblock markdowncell *)) | |
|
189 | ||
|
190 | % Spit out the contents of raw cells unmodified | |
|
191 | ((* block rawcell scoped *)) | |
|
192 | ((( cell.source ))) | |
|
193 | ((* endblock rawcell *)) | |
|
194 | ||
|
195 | % Don't display unknown types | |
|
196 | ((* block unknowncell scoped *)) | |
|
197 | ((* endblock unknowncell *)) |
@@ -0,0 +1,22 b'' | |||
|
1 | ||
|
2 | % Default to the notebook output style | |
|
3 | ((* if not cell_style is defined *)) | |
|
4 | ((* set cell_style = 'style_ipython.tplx' *)) | |
|
5 | ((* endif *)) | |
|
6 | ||
|
7 | % Inherit from the specified cell style. | |
|
8 | ((* extends cell_style *)) | |
|
9 | ||
|
10 | ||
|
11 | %=============================================================================== | |
|
12 | % Latex Book | |
|
13 | %=============================================================================== | |
|
14 | ||
|
15 | ((* block predoc *)) | |
|
16 | ((( super() ))) | |
|
17 | ((* block tableofcontents *))\tableofcontents((* endblock tableofcontents *)) | |
|
18 | ((* endblock predoc *)) | |
|
19 | ||
|
20 | ((* block docclass *)) | |
|
21 | \documentclass{report} | |
|
22 | ((* endblock docclass *)) |
@@ -0,0 +1,41 b'' | |||
|
1 | ((= Black&white ipython input/output style =)) | |
|
2 | ||
|
3 | ((*- extends 'latex_base.tplx' -*)) | |
|
4 | ||
|
5 | %=============================================================================== | |
|
6 | % Input | |
|
7 | %=============================================================================== | |
|
8 | ||
|
9 | ((* block input scoped *)) | |
|
10 | ((( add_prompt(cell.input, cell, 'In ') ))) | |
|
11 | ((* endblock input *)) | |
|
12 | ||
|
13 | ||
|
14 | %=============================================================================== | |
|
15 | % Output | |
|
16 | %=============================================================================== | |
|
17 | ||
|
18 | ((* block pyout scoped *)) | |
|
19 | ((*- for type in output | filter_data_type -*)) | |
|
20 | ((*- if type in ['text']*)) | |
|
21 | ((( add_prompt(output.text, cell, 'Out') ))) | |
|
22 | ((*- else -*)) | |
|
23 | \verb+Out[((( cell.prompt_number )))]:+((( super() ))) | |
|
24 | ((*- endif -*)) | |
|
25 | ((*- endfor -*)) | |
|
26 | ((* endblock pyout *)) | |
|
27 | ||
|
28 | ||
|
29 | %============================================================================== | |
|
30 | % Support Macros | |
|
31 | %============================================================================== | |
|
32 | ||
|
33 | % Name: draw_prompt | |
|
34 | % Purpose: Renders an output/input prompt | |
|
35 | ((* macro add_prompt(text, cell, prompt) -*)) | |
|
36 | ((*- set prompt_number = "" ~ cell.prompt_number -*)) | |
|
37 | ((*- set indentation = " " * (prompt_number | length + 7) -*)) | |
|
38 | \begin{verbatim} | |
|
39 | (((- text | add_prompts(first=prompt ~ '[' ~ prompt_number ~ ']: ', cont=indentation) -))) | |
|
40 | \end{verbatim} | |
|
41 | ((*- endmacro *)) |
@@ -0,0 +1,13 b'' | |||
|
1 | ((= Black&white Python input/output style =)) | |
|
2 | ||
|
3 | ((*- extends 'latex_base.tplx' -*)) | |
|
4 | ||
|
5 | %=============================================================================== | |
|
6 | % Input | |
|
7 | %=============================================================================== | |
|
8 | ||
|
9 | ((* block input scoped *)) | |
|
10 | \begin{verbatim} | |
|
11 | ((( cell.input | add_prompts ))) | |
|
12 | \end{verbatim} | |
|
13 | ((* endblock input *)) |
@@ -0,0 +1,54 b'' | |||
|
1 | ((= IPython input/output style =)) | |
|
2 | ||
|
3 | ((*- extends 'latex_base.tplx' -*)) | |
|
4 | ||
|
5 | % Custom definitions | |
|
6 | ((* block definitions *)) | |
|
7 | ((( super() ))) | |
|
8 | ||
|
9 | % Pygments definitions | |
|
10 | ((( resources.latex.pygments_definitions ))) | |
|
11 | ||
|
12 | % Exact colors from NB | |
|
13 | \definecolor{incolor}{rgb}{0.0, 0.0, 0.5} | |
|
14 | \definecolor{outcolor}{rgb}{0.545, 0.0, 0.0} | |
|
15 | ||
|
16 | ((* endblock definitions *)) | |
|
17 | ||
|
18 | %=============================================================================== | |
|
19 | % Input | |
|
20 | %=============================================================================== | |
|
21 | ||
|
22 | ((* block input scoped *)) | |
|
23 | ((( add_prompt(cell.input | highlight2latex(strip_verbatim=True), cell, 'In ', 'incolor') ))) | |
|
24 | ((* endblock input *)) | |
|
25 | ||
|
26 | ||
|
27 | %=============================================================================== | |
|
28 | % Output | |
|
29 | %=============================================================================== | |
|
30 | ||
|
31 | ((* block pyout scoped *)) | |
|
32 | ((*- for type in output | filter_data_type -*)) | |
|
33 | ((*- if type in ['text']*)) | |
|
34 | ((( add_prompt(output.text | escape_latex, cell, 'Out', 'outcolor') ))) | |
|
35 | ((* else -*)) | |
|
36 | \texttt{\color{outcolor}Out[{\color{outcolor}((( cell.prompt_number )))}]:}((( super() ))) | |
|
37 | ((*- endif -*)) | |
|
38 | ((*- endfor -*)) | |
|
39 | ((* endblock pyout *)) | |
|
40 | ||
|
41 | ||
|
42 | %============================================================================== | |
|
43 | % Support Macros | |
|
44 | %============================================================================== | |
|
45 | ||
|
46 | % Name: draw_prompt | |
|
47 | % Purpose: Renders an output/input prompt | |
|
48 | ((* macro add_prompt(text, cell, prompt, prompt_color) -*)) | |
|
49 | ((*- set prompt_number = "" ~ cell.prompt_number -*)) | |
|
50 | ((*- set indention = " " * (prompt_number | length + 7) -*)) | |
|
51 | \begin{Verbatim}[commandchars=\\\{\}] | |
|
52 | ((( text | add_prompts(first='{\color{' ~ prompt_color ~ '}' ~ prompt ~ '[{\\color{' ~ prompt_color ~ '}' ~ prompt_number ~ '}]:} ', cont=indention) ))) | |
|
53 | \end{Verbatim} | |
|
54 | ((*- endmacro *)) |
@@ -0,0 +1,21 b'' | |||
|
1 | ((= Python input/output style =)) | |
|
2 | ||
|
3 | ((*- extends 'latex_base.tplx' -*)) | |
|
4 | ||
|
5 | % Custom definitions | |
|
6 | ((* block definitions *)) | |
|
7 | ((( super() ))) | |
|
8 | ||
|
9 | % Pygments definitions | |
|
10 | ((( resources.latex.pygments_definitions ))) | |
|
11 | ((* endblock definitions *)) | |
|
12 | ||
|
13 | %=============================================================================== | |
|
14 | % Input | |
|
15 | %=============================================================================== | |
|
16 | ||
|
17 | ((* block input scoped *)) | |
|
18 | \begin{Verbatim}[commandchars=\\\{\}] | |
|
19 | ((( cell.input | highlight2latex(strip_verbatim=True) | add_prompts ))) | |
|
20 | \end{Verbatim} | |
|
21 | ((* endblock input *)) |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: new file 100644 | |
The requested commit or file is too big and content was truncated. Show full diff |
@@ -4,13 +4,11 b' python:' | |||
|
4 | 4 | - 2.7 |
|
5 | 5 | - 3.3 |
|
6 | 6 | before_install: |
|
7 | - pip install jinja2 | |
|
8 | 7 | - easy_install -q pyzmq |
|
8 | - pip install jinja2 sphinx pygments tornado | |
|
9 | 9 | - sudo apt-get install pandoc |
|
10 | - pip install pygments | |
|
11 | - pip install sphinx | |
|
12 | 10 | install: |
|
13 | 11 | - python setup.py install -q |
|
14 | 12 | script: |
|
15 |
- if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then |
|
|
16 |
- if [[ $TRAVIS_PYTHON_VERSION == '3.'* ]]; then |
|
|
13 | - if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then cd /tmp; iptest; fi | |
|
14 | - if [[ $TRAVIS_PYTHON_VERSION == '3.'* ]]; then cd /tmp; iptest3; fi |
@@ -28,8 +28,8 b' import sys' | |||
|
28 | 28 | #----------------------------------------------------------------------------- |
|
29 | 29 | |
|
30 | 30 | # Don't forget to also update setup.py when this changes! |
|
31 |
if sys.version[ |
|
|
32 |
raise ImportError('Python Version 2. |
|
|
31 | if sys.version_info[:2] < (2,7): | |
|
32 | raise ImportError('IPython requires Python Version 2.7 or above.') | |
|
33 | 33 | |
|
34 | 34 | # Make it easy to import extensions - they are always directly on pythonpath. |
|
35 | 35 | # Therefore, non-IPython modules can be added to extensions directory. |
@@ -142,6 +142,9 b' class Application(SingletonConfigurable):' | |||
|
142 | 142 | |
|
143 | 143 | # The version string of this application. |
|
144 | 144 | version = Unicode(u'0.0') |
|
145 | ||
|
146 | # the argv used to initialize the application | |
|
147 | argv = List() | |
|
145 | 148 | |
|
146 | 149 | # The log level for the application |
|
147 | 150 | log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'), |
@@ -454,6 +457,7 b' class Application(SingletonConfigurable):' | |||
|
454 | 457 | def parse_command_line(self, argv=None): |
|
455 | 458 | """Parse the command line arguments.""" |
|
456 | 459 | argv = sys.argv[1:] if argv is None else argv |
|
460 | self.argv = list(argv) | |
|
457 | 461 | |
|
458 | 462 | if argv and argv[0] == 'help': |
|
459 | 463 | # turn `ipython help notebook` into `ipython notebook -h` |
@@ -24,11 +24,11 b' Authors' | |||
|
24 | 24 | #----------------------------------------------------------------------------- |
|
25 | 25 | |
|
26 | 26 | import __builtin__ as builtin_mod |
|
27 | import argparse | |
|
27 | 28 | import os |
|
28 | 29 | import re |
|
29 | 30 | import sys |
|
30 | 31 | |
|
31 | from IPython.external import argparse | |
|
32 | 32 | from IPython.utils.path import filefind, get_ipython_dir |
|
33 | 33 | from IPython.utils import py3compat, warn |
|
34 | 34 | from IPython.utils.encoding import DEFAULT_ENCODING |
@@ -1,3 +1,4 b'' | |||
|
1 | # coding: utf-8 | |
|
1 | 2 | """ |
|
2 | 3 | Tests for IPython.config.application.Application |
|
3 | 4 | |
@@ -185,4 +186,8 b' class TestApplication(TestCase):' | |||
|
185 | 186 | self.assertEqual(app.bar.b, 5) |
|
186 | 187 | self.assertEqual(app.extra_args, ['extra', '--disable', 'args']) |
|
187 | 188 | |
|
189 | def test_unicode_argv(self): | |
|
190 | app = MyApp() | |
|
191 | app.parse_command_line(['ünîcødé']) | |
|
192 | ||
|
188 | 193 |
@@ -53,7 +53,7 b' from IPython.kernel.connect import ConnectionFileMixin' | |||
|
53 | 53 | # Network Constants |
|
54 | 54 | #----------------------------------------------------------------------------- |
|
55 | 55 | |
|
56 |
from IPython.utils.localinterfaces import |
|
|
56 | from IPython.utils.localinterfaces import localhost | |
|
57 | 57 | |
|
58 | 58 | #----------------------------------------------------------------------------- |
|
59 | 59 | # Globals |
@@ -254,7 +254,7 b' class IPythonConsoleApp(ConnectionFileMixin):' | |||
|
254 | 254 | with open(fname) as f: |
|
255 | 255 | cfg = json.load(f) |
|
256 | 256 | self.transport = cfg.get('transport', 'tcp') |
|
257 |
self.ip = cfg.get('ip', |
|
|
257 | self.ip = cfg.get('ip', localhost()) | |
|
258 | 258 | |
|
259 | 259 | for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'): |
|
260 | 260 | name = channel + '_port' |
@@ -282,7 +282,7 b' class IPythonConsoleApp(ConnectionFileMixin):' | |||
|
282 | 282 | if self.sshkey and not self.sshserver: |
|
283 | 283 | # specifying just the key implies that we are connecting directly |
|
284 | 284 | self.sshserver = ip |
|
285 |
ip = |
|
|
285 | ip = localhost() | |
|
286 | 286 | |
|
287 | 287 | # build connection dict for tunnels: |
|
288 | 288 | info = dict(ip=ip, |
@@ -295,7 +295,7 b' class IPythonConsoleApp(ConnectionFileMixin):' | |||
|
295 | 295 | self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver)) |
|
296 | 296 | |
|
297 | 297 | # tunnels return a new set of ports, which will be on localhost: |
|
298 |
self.ip = |
|
|
298 | self.ip = localhost() | |
|
299 | 299 | try: |
|
300 | 300 | newports = tunnel_to_kernel(info, self.sshserver, self.sshkey) |
|
301 | 301 | except: |
@@ -20,17 +20,15 b' Authors:' | |||
|
20 | 20 | # Imports |
|
21 | 21 | #----------------------------------------------------------------------------- |
|
22 | 22 | |
|
23 | import __builtin__ | |
|
24 | import keyword | |
|
25 | 23 | import os |
|
26 | 24 | import re |
|
27 | 25 | import sys |
|
28 | 26 | |
|
29 | 27 | from IPython.config.configurable import Configurable |
|
30 |
from IPython.core. |
|
|
28 | from IPython.core.error import UsageError | |
|
31 | 29 | |
|
32 | 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 | 34 | # Utilities |
@@ -104,6 +102,70 b' class AliasError(Exception):' | |||
|
104 | 102 | class InvalidAliasError(AliasError): |
|
105 | 103 | pass |
|
106 | 104 | |
|
105 | class Alias(object): | |
|
106 | """Callable object storing the details of one alias. | |
|
107 | ||
|
108 | Instances are registered as magic functions to allow use of aliases. | |
|
109 | """ | |
|
110 | ||
|
111 | # Prepare blacklist | |
|
112 | blacklist = {'cd','popd','pushd','dhist','alias','unalias'} | |
|
113 | ||
|
114 | def __init__(self, shell, name, cmd): | |
|
115 | self.shell = shell | |
|
116 | self.name = name | |
|
117 | self.cmd = cmd | |
|
118 | self.nargs = self.validate() | |
|
119 | ||
|
120 | def validate(self): | |
|
121 | """Validate the alias, and return the number of arguments.""" | |
|
122 | if self.name in self.blacklist: | |
|
123 | raise InvalidAliasError("The name %s can't be aliased " | |
|
124 | "because it is a keyword or builtin." % self.name) | |
|
125 | try: | |
|
126 | caller = self.shell.magics_manager.magics['line'][self.name] | |
|
127 | except KeyError: | |
|
128 | pass | |
|
129 | else: | |
|
130 | if not isinstance(caller, Alias): | |
|
131 | raise InvalidAliasError("The name %s can't be aliased " | |
|
132 | "because it is another magic command." % self.name) | |
|
133 | ||
|
134 | if not (isinstance(self.cmd, basestring)): | |
|
135 | raise InvalidAliasError("An alias command must be a string, " | |
|
136 | "got: %r" % self.cmd) | |
|
137 | ||
|
138 | nargs = self.cmd.count('%s') | |
|
139 | ||
|
140 | if (nargs > 0) and (self.cmd.find('%l') >= 0): | |
|
141 | raise InvalidAliasError('The %s and %l specifiers are mutually ' | |
|
142 | 'exclusive in alias definitions.') | |
|
143 | ||
|
144 | return nargs | |
|
145 | ||
|
146 | def __repr__(self): | |
|
147 | return "<alias {} for {!r}>".format(self.name, self.cmd) | |
|
148 | ||
|
149 | def __call__(self, rest=''): | |
|
150 | cmd = self.cmd | |
|
151 | nargs = self.nargs | |
|
152 | # Expand the %l special to be the user's input line | |
|
153 | if cmd.find('%l') >= 0: | |
|
154 | cmd = cmd.replace('%l', rest) | |
|
155 | rest = '' | |
|
156 | if nargs==0: | |
|
157 | # Simple, argument-less aliases | |
|
158 | cmd = '%s %s' % (cmd, rest) | |
|
159 | else: | |
|
160 | # Handle aliases with positional arguments | |
|
161 | args = rest.split(None, nargs) | |
|
162 | if len(args) < nargs: | |
|
163 | raise UsageError('Alias <%s> requires %s arguments, %s given.' % | |
|
164 | (self.name, nargs, len(args))) | |
|
165 | cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:])) | |
|
166 | ||
|
167 | self.shell.system(cmd) | |
|
168 | ||
|
107 | 169 | #----------------------------------------------------------------------------- |
|
108 | 170 | # Main AliasManager class |
|
109 | 171 | #----------------------------------------------------------------------------- |
@@ -116,35 +178,19 b' class AliasManager(Configurable):' | |||
|
116 | 178 | |
|
117 | 179 | def __init__(self, shell=None, **kwargs): |
|
118 | 180 | super(AliasManager, self).__init__(shell=shell, **kwargs) |
|
119 | self.alias_table = {} | |
|
120 | self.exclude_aliases() | |
|
181 | # For convenient access | |
|
182 | self.linemagics = self.shell.magics_manager.magics['line'] | |
|
121 | 183 | self.init_aliases() |
|
122 | 184 | |
|
123 | def __contains__(self, name): | |
|
124 | return name in self.alias_table | |
|
125 | ||
|
126 | @property | |
|
127 | def aliases(self): | |
|
128 | return [(item[0], item[1][1]) for item in self.alias_table.iteritems()] | |
|
129 | ||
|
130 | def exclude_aliases(self): | |
|
131 | # set of things NOT to alias (keywords, builtins and some magics) | |
|
132 | no_alias = set(['cd','popd','pushd','dhist','alias','unalias']) | |
|
133 | no_alias.update(set(keyword.kwlist)) | |
|
134 | no_alias.update(set(__builtin__.__dict__.keys())) | |
|
135 | self.no_alias = no_alias | |
|
136 | ||
|
137 | 185 | def init_aliases(self): |
|
138 | # Load default aliases | |
|
139 | for name, cmd in self.default_aliases: | |
|
140 | self.soft_define_alias(name, cmd) | |
|
141 | ||
|
142 | # Load user aliases | |
|
143 | for name, cmd in self.user_aliases: | |
|
186 | # Load default & user aliases | |
|
187 | for name, cmd in self.default_aliases + self.user_aliases: | |
|
144 | 188 | self.soft_define_alias(name, cmd) |
|
145 | 189 | |
|
146 | def clear_aliases(self): | |
|
147 | self.alias_table.clear() | |
|
190 | @property | |
|
191 | def aliases(self): | |
|
192 | return [(n, func.cmd) for (n, func) in self.linemagics.items() | |
|
193 | if isinstance(func, Alias)] | |
|
148 | 194 | |
|
149 | 195 | def soft_define_alias(self, name, cmd): |
|
150 | 196 | """Define an alias, but don't raise on an AliasError.""" |
@@ -159,104 +205,33 b' class AliasManager(Configurable):' | |||
|
159 | 205 | This will raise an :exc:`AliasError` if there are validation |
|
160 | 206 | problems. |
|
161 | 207 | """ |
|
162 | nargs = self.validate_alias(name, cmd) | |
|
163 | self.alias_table[name] = (nargs, cmd) | |
|
208 | caller = Alias(shell=self.shell, name=name, cmd=cmd) | |
|
209 | self.shell.magics_manager.register_function(caller, magic_kind='line', | |
|
210 | magic_name=name) | |
|
164 | 211 | |
|
165 |
def |
|
|
166 | if name in self.alias_table: | |
|
167 | del self.alias_table[name] | |
|
212 | def get_alias(self, name): | |
|
213 | """Return an alias, or None if no alias by that name exists.""" | |
|
214 | aname = self.linemagics.get(name, None) | |
|
215 | return aname if isinstance(aname, Alias) else None | |
|
168 | 216 | |
|
169 |
def |
|
|
170 | """Validate an alias and return the its number of arguments.""" | |
|
171 | if name in self.no_alias: | |
|
172 | raise InvalidAliasError("The name %s can't be aliased " | |
|
173 | "because it is a keyword or builtin." % name) | |
|
174 | if not (isinstance(cmd, basestring)): | |
|
175 | raise InvalidAliasError("An alias command must be a string, " | |
|
176 | "got: %r" % cmd) | |
|
177 | nargs = cmd.count('%s') | |
|
178 | if nargs>0 and cmd.find('%l')>=0: | |
|
179 | raise InvalidAliasError('The %s and %l specifiers are mutually ' | |
|
180 | 'exclusive in alias definitions.') | |
|
181 | return nargs | |
|
217 | def is_alias(self, name): | |
|
218 | """Return whether or not a given name has been defined as an alias""" | |
|
219 | return self.get_alias(name) is not None | |
|
182 | 220 | |
|
183 |
def |
|
|
184 | """Call an alias given its name and the rest of the line.""" | |
|
185 | cmd = self.transform_alias(alias, rest) | |
|
186 | try: | |
|
187 | self.shell.system(cmd) | |
|
188 | except: | |
|
189 | self.shell.showtraceback() | |
|
190 | ||
|
191 | def transform_alias(self, alias,rest=''): | |
|
192 | """Transform alias to system command string.""" | |
|
193 | nargs, cmd = self.alias_table[alias] | |
|
194 | ||
|
195 | if ' ' in cmd and os.path.isfile(cmd): | |
|
196 | cmd = '"%s"' % cmd | |
|
197 | ||
|
198 | # Expand the %l special to be the user's input line | |
|
199 | if cmd.find('%l') >= 0: | |
|
200 | cmd = cmd.replace('%l', rest) | |
|
201 | rest = '' | |
|
202 | if nargs==0: | |
|
203 | # Simple, argument-less aliases | |
|
204 | cmd = '%s %s' % (cmd, rest) | |
|
221 | def undefine_alias(self, name): | |
|
222 | if self.is_alias(name): | |
|
223 | del self.linemagics[name] | |
|
205 | 224 | else: |
|
206 | # Handle aliases with positional arguments | |
|
207 | args = rest.split(None, nargs) | |
|
208 | if len(args) < nargs: | |
|
209 | raise AliasError('Alias <%s> requires %s arguments, %s given.' % | |
|
210 | (alias, nargs, len(args))) | |
|
211 | cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:])) | |
|
212 | return cmd | |
|
213 | ||
|
214 | def expand_alias(self, line): | |
|
215 | """ Expand an alias in the command line | |
|
225 | raise ValueError('%s is not an alias' % name) | |
|
216 | 226 | |
|
217 | Returns the provided command line, possibly with the first word | |
|
218 | (command) translated according to alias expansion rules. | |
|
219 | ||
|
220 | [ipython]|16> _ip.expand_aliases("np myfile.txt") | |
|
221 | <16> 'q:/opt/np/notepad++.exe myfile.txt' | |
|
222 | """ | |
|
223 | ||
|
224 | pre,_,fn,rest = split_user_input(line) | |
|
225 | res = pre + self.expand_aliases(fn, rest) | |
|
226 |
|
|
|
227 | ||
|
228 | def expand_aliases(self, fn, rest): | |
|
229 | """Expand multiple levels of aliases: | |
|
230 | ||
|
231 | if: | |
|
232 | ||
|
233 | alias foo bar /tmp | |
|
234 | alias baz foo | |
|
235 | ||
|
236 | then: | |
|
237 | ||
|
238 | baz huhhahhei -> bar /tmp huhhahhei | |
|
239 | """ | |
|
240 | line = fn + " " + rest | |
|
241 | ||
|
242 | done = set() | |
|
243 | while 1: | |
|
244 | pre,_,fn,rest = split_user_input(line, shell_line_split) | |
|
245 | if fn in self.alias_table: | |
|
246 | if fn in done: | |
|
247 | warn("Cyclic alias definition, repeated '%s'" % fn) | |
|
248 | return "" | |
|
249 | done.add(fn) | |
|
250 | ||
|
251 | l2 = self.transform_alias(fn, rest) | |
|
252 | if l2 == line: | |
|
253 | break | |
|
254 | # ls -> ls -F should not recurse forever | |
|
255 | if l2.split(None,1)[0] == line.split(None,1)[0]: | |
|
256 | line = l2 | |
|
257 | break | |
|
258 | line = l2 | |
|
259 | else: | |
|
260 | break | |
|
261 | ||
|
262 | return line | |
|
227 | def clear_aliases(self): | |
|
228 | for name, cmd in self.aliases: | |
|
229 | self.undefine_alias(name) | |
|
230 | ||
|
231 | def retrieve_alias(self, name): | |
|
232 | """Retrieve the command to which an alias expands.""" | |
|
233 | caller = self.get_alias(name) | |
|
234 | if caller: | |
|
235 | return caller.cmd | |
|
236 | else: | |
|
237 | raise ValueError('%s is not an alias' % name) |
@@ -53,6 +53,7 b' from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instan' | |||
|
53 | 53 | # aliases and flags |
|
54 | 54 | |
|
55 | 55 | base_aliases = { |
|
56 | 'profile-dir' : 'ProfileDir.location', | |
|
56 | 57 | 'profile' : 'BaseIPythonApplication.profile', |
|
57 | 58 | 'ipython-dir' : 'BaseIPythonApplication.ipython_dir', |
|
58 | 59 | 'log-level' : 'Application.log_level', |
@@ -24,26 +24,26 b' Tip: to use the tab key as the completion key, call' | |||
|
24 | 24 | Notes: |
|
25 | 25 | |
|
26 | 26 | - Exceptions raised by the completer function are *ignored* (and |
|
27 | generally cause the completion to fail). This is a feature -- since | |
|
28 | readline sets the tty device in raw (or cbreak) mode, printing a | |
|
29 | traceback wouldn't work well without some complicated hoopla to save, | |
|
30 | reset and restore the tty state. | |
|
27 | generally cause the completion to fail). This is a feature -- since | |
|
28 | readline sets the tty device in raw (or cbreak) mode, printing a | |
|
29 | traceback wouldn't work well without some complicated hoopla to save, | |
|
30 | reset and restore the tty state. | |
|
31 | 31 | |
|
32 | 32 | - The evaluation of the NAME.NAME... form may cause arbitrary |
|
33 | application defined code to be executed if an object with a | |
|
34 | __getattr__ hook is found. Since it is the responsibility of the | |
|
35 | application (or the user) to enable this feature, I consider this an | |
|
36 | acceptable risk. More complicated expressions (e.g. function calls or | |
|
37 | indexing operations) are *not* evaluated. | |
|
33 | application defined code to be executed if an object with a | |
|
34 | ``__getattr__`` hook is found. Since it is the responsibility of the | |
|
35 | application (or the user) to enable this feature, I consider this an | |
|
36 | acceptable risk. More complicated expressions (e.g. function calls or | |
|
37 | indexing operations) are *not* evaluated. | |
|
38 | 38 | |
|
39 | 39 | - GNU readline is also used by the built-in functions input() and |
|
40 | raw_input(), and thus these also benefit/suffer from the completer | |
|
41 | features. Clearly an interactive application can benefit by | |
|
42 | specifying its own completer function and using raw_input() for all | |
|
43 | its input. | |
|
40 | raw_input(), and thus these also benefit/suffer from the completer | |
|
41 | features. Clearly an interactive application can benefit by | |
|
42 | specifying its own completer function and using raw_input() for all | |
|
43 | its input. | |
|
44 | 44 | |
|
45 | 45 | - When the original stdin is not a tty device, GNU readline is never |
|
46 | used, and this module (and the readline module) are silently inactive. | |
|
46 | used, and this module (and the readline module) are silently inactive. | |
|
47 | 47 | """ |
|
48 | 48 | |
|
49 | 49 | #***************************************************************************** |
@@ -431,8 +431,7 b' class IPCompleter(Completer):' | |||
|
431 | 431 | ) |
|
432 | 432 | |
|
433 | 433 | def __init__(self, shell=None, namespace=None, global_namespace=None, |
|
434 |
|
|
|
435 | config=None, **kwargs): | |
|
434 | use_readline=True, config=None, **kwargs): | |
|
436 | 435 | """IPCompleter() -> completer |
|
437 | 436 | |
|
438 | 437 | Return a completer object suitable for use by the readline library |
@@ -441,17 +440,14 b' class IPCompleter(Completer):' | |||
|
441 | 440 | Inputs: |
|
442 | 441 | |
|
443 | 442 | - shell: a pointer to the ipython shell itself. This is needed |
|
444 | because this completer knows about magic functions, and those can | |
|
445 | only be accessed via the ipython instance. | |
|
443 | because this completer knows about magic functions, and those can | |
|
444 | only be accessed via the ipython instance. | |
|
446 | 445 | |
|
447 | 446 | - namespace: an optional dict where completions are performed. |
|
448 | 447 | |
|
449 | 448 | - global_namespace: secondary optional dict for completions, to |
|
450 | handle cases (such as IPython embedded inside functions) where | |
|
451 | both Python scopes are visible. | |
|
452 | ||
|
453 | - If alias_table is supplied, it should be a dictionary of aliases | |
|
454 | to complete. | |
|
449 | handle cases (such as IPython embedded inside functions) where | |
|
450 | both Python scopes are visible. | |
|
455 | 451 | |
|
456 | 452 | use_readline : bool, optional |
|
457 | 453 | If true, use the readline library. This completer can still function |
@@ -476,9 +472,6 b' class IPCompleter(Completer):' | |||
|
476 | 472 | # List where completion matches will be stored |
|
477 | 473 | self.matches = [] |
|
478 | 474 | self.shell = shell |
|
479 | if alias_table is None: | |
|
480 | alias_table = {} | |
|
481 | self.alias_table = alias_table | |
|
482 | 475 | # Regexp to split filenames with spaces in them |
|
483 | 476 | self.space_name_re = re.compile(r'([^\\] )') |
|
484 | 477 | # Hold a local ref. to glob.glob for speed |
@@ -505,7 +498,6 b' class IPCompleter(Completer):' | |||
|
505 | 498 | self.matchers = [self.python_matches, |
|
506 | 499 | self.file_matches, |
|
507 | 500 | self.magic_matches, |
|
508 | self.alias_matches, | |
|
509 | 501 | self.python_func_kw_matches, |
|
510 | 502 | ] |
|
511 | 503 | |
@@ -628,22 +620,6 b' class IPCompleter(Completer):' | |||
|
628 | 620 | comp += [ pre+m for m in line_magics if m.startswith(bare_text)] |
|
629 | 621 | return comp |
|
630 | 622 | |
|
631 | def alias_matches(self, text): | |
|
632 | """Match internal system aliases""" | |
|
633 | #print 'Completer->alias_matches:',text,'lb',self.text_until_cursor # dbg | |
|
634 | ||
|
635 | # if we are not in the first 'item', alias matching | |
|
636 | # doesn't make sense - unless we are starting with 'sudo' command. | |
|
637 | main_text = self.text_until_cursor.lstrip() | |
|
638 | if ' ' in main_text and not main_text.startswith('sudo'): | |
|
639 | return [] | |
|
640 | text = os.path.expanduser(text) | |
|
641 | aliases = self.alias_table.keys() | |
|
642 | if text == '': | |
|
643 | return aliases | |
|
644 | else: | |
|
645 | return [a for a in aliases if a.startswith(text)] | |
|
646 | ||
|
647 | 623 | def python_matches(self,text): |
|
648 | 624 | """Match attributes or global python names""" |
|
649 | 625 |
@@ -96,19 +96,24 b' class Tracer(object):' | |||
|
96 | 96 | def __init__(self,colors=None): |
|
97 | 97 | """Create a local debugger instance. |
|
98 | 98 | |
|
99 |
|
|
|
99 | Parameters | |
|
100 | ---------- | |
|
100 | 101 | |
|
101 | - `colors` (None): a string containing the name of the color scheme to | |
|
102 | use, it must be one of IPython's valid color schemes. If not given, the | |
|
103 | function will default to the current IPython scheme when running inside | |
|
104 | IPython, and to 'NoColor' otherwise. | |
|
102 | colors : str, optional | |
|
103 | The name of the color scheme to use, it must be one of IPython's | |
|
104 | valid color schemes. If not given, the function will default to | |
|
105 | the current IPython scheme when running inside IPython, and to | |
|
106 | 'NoColor' otherwise. | |
|
105 | 107 | |
|
106 |
|
|
|
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 |
|
|
|
111 | debug_here() # -> will open up the debugger at that point. | |
|
114 | Later in your code:: | |
|
115 | ||
|
116 | debug_here() # -> will open up the debugger at that point. | |
|
112 | 117 | |
|
113 | 118 | Once the debugger activates, you can use all of its regular commands to |
|
114 | 119 | step through code, set breakpoints, etc. See the pdb documentation |
@@ -508,9 +508,9 b' class Image(DisplayObject):' | |||
|
508 | 508 | _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG] |
|
509 | 509 | |
|
510 | 510 | def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False): |
|
511 |
"""Create a |
|
|
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 | 514 | display function, it will result in the image being displayed |
|
515 | 515 | in the frontend. |
|
516 | 516 | |
@@ -657,35 +657,19 b' class Image(DisplayObject):' | |||
|
657 | 657 | return unicode(s.split('.')[-1].lower()) |
|
658 | 658 | |
|
659 | 659 | |
|
660 | def clear_output(stdout=True, stderr=True, other=True): | |
|
660 | def clear_output(wait=False): | |
|
661 | 661 | """Clear the output of the current cell receiving output. |
|
662 | 662 | |
|
663 | Optionally, each of stdout/stderr or other non-stream data (e.g. anything | |
|
664 | produced by display()) can be excluded from the clear event. | |
|
665 | ||
|
666 | By default, everything is cleared. | |
|
667 | ||
|
668 | 663 | Parameters |
|
669 | 664 | ---------- |
|
670 |
|
|
|
671 | Whether to clear stdout. | |
|
672 | stderr : bool [default: True] | |
|
673 | Whether to clear stderr. | |
|
674 | other : bool [default: True] | |
|
675 | Whether to clear everything else that is not stdout/stderr | |
|
676 | (e.g. figures,images,HTML, any result of display()). | |
|
677 | """ | |
|
665 | wait : bool [default: false] | |
|
666 | Wait to clear the output until new output is available to replace it.""" | |
|
678 | 667 | from IPython.core.interactiveshell import InteractiveShell |
|
679 | 668 | if InteractiveShell.initialized(): |
|
680 | InteractiveShell.instance().display_pub.clear_output( | |
|
681 | stdout=stdout, stderr=stderr, other=other, | |
|
682 | ) | |
|
669 | InteractiveShell.instance().display_pub.clear_output(wait) | |
|
683 | 670 | else: |
|
684 | 671 | from IPython.utils import io |
|
685 | if stdout: | |
|
686 | print('\033[2K\r', file=io.stdout, end='') | |
|
687 | io.stdout.flush() | |
|
688 |
i |
|
|
689 | print('\033[2K\r', file=io.stderr, end='') | |
|
690 | io.stderr.flush() | |
|
691 | ||
|
672 | print('\033[2K\r', file=io.stdout, end='') | |
|
673 | io.stdout.flush() | |
|
674 | print('\033[2K\r', file=io.stderr, end='') | |
|
675 | io.stderr.flush() |
@@ -108,14 +108,12 b' class DisplayPublisher(Configurable):' | |||
|
108 | 108 | if 'text/plain' in data: |
|
109 | 109 | print(data['text/plain'], file=io.stdout) |
|
110 | 110 | |
|
111 |
def clear_output(self, |
|
|
111 | def clear_output(self, wait=False): | |
|
112 | 112 | """Clear the output of the cell receiving output.""" |
|
113 | if stdout: | |
|
114 | print('\033[2K\r', file=io.stdout, end='') | |
|
115 | io.stdout.flush() | |
|
116 |
i |
|
|
117 | print('\033[2K\r', file=io.stderr, end='') | |
|
118 | io.stderr.flush() | |
|
113 | print('\033[2K\r', file=io.stdout, end='') | |
|
114 | io.stdout.flush() | |
|
115 | print('\033[2K\r', file=io.stderr, end='') | |
|
116 | io.stderr.flush() | |
|
119 | 117 | |
|
120 | 118 | |
|
121 | 119 | class CapturingDisplayPublisher(DisplayPublisher): |
@@ -125,8 +123,8 b' class CapturingDisplayPublisher(DisplayPublisher):' | |||
|
125 | 123 | def publish(self, source, data, metadata=None): |
|
126 | 124 | self.outputs.append((source, data, metadata)) |
|
127 | 125 | |
|
128 |
def clear_output(self, |
|
|
129 |
super(CapturingDisplayPublisher, self).clear_output( |
|
|
126 | def clear_output(self, wait=False): | |
|
127 | super(CapturingDisplayPublisher, self).clear_output(wait) | |
|
130 | 128 | if other: |
|
131 | 129 | # empty the list, *do not* reassign a new list |
|
132 | 130 | del self.outputs[:] |
@@ -278,6 +278,25 b' _help_end_re = re.compile(r"""(%{0,2}' | |||
|
278 | 278 | """, |
|
279 | 279 | re.VERBOSE) |
|
280 | 280 | |
|
281 | # Extra pseudotokens for multiline strings and data structures | |
|
282 | _MULTILINE_STRING = object() | |
|
283 | _MULTILINE_STRUCTURE = object() | |
|
284 | ||
|
285 | def _line_tokens(line): | |
|
286 | """Helper for has_comment and ends_in_comment_or_string.""" | |
|
287 | readline = StringIO(line).readline | |
|
288 | toktypes = set() | |
|
289 | try: | |
|
290 | for t in generate_tokens(readline): | |
|
291 | toktypes.add(t[0]) | |
|
292 | except TokenError as e: | |
|
293 | # There are only two cases where a TokenError is raised. | |
|
294 | if 'multi-line string' in e.args[0]: | |
|
295 | toktypes.add(_MULTILINE_STRING) | |
|
296 | else: | |
|
297 | toktypes.add(_MULTILINE_STRUCTURE) | |
|
298 | return toktypes | |
|
299 | ||
|
281 | 300 | def has_comment(src): |
|
282 | 301 | """Indicate whether an input line has (i.e. ends in, or is) a comment. |
|
283 | 302 | |
@@ -293,21 +312,31 b' def has_comment(src):' | |||
|
293 | 312 | comment : bool |
|
294 | 313 | True if source has a comment. |
|
295 | 314 | """ |
|
296 | readline = StringIO(src).readline | |
|
297 | toktypes = set() | |
|
298 | try: | |
|
299 | for t in generate_tokens(readline): | |
|
300 | toktypes.add(t[0]) | |
|
301 | except TokenError: | |
|
302 | pass | |
|
303 | return(tokenize2.COMMENT in toktypes) | |
|
315 | return (tokenize2.COMMENT in _line_tokens(src)) | |
|
304 | 316 | |
|
317 | def ends_in_comment_or_string(src): | |
|
318 | """Indicates whether or not an input line ends in a comment or within | |
|
319 | a multiline string. | |
|
320 | ||
|
321 | Parameters | |
|
322 | ---------- | |
|
323 | src : string | |
|
324 | A single line input string. | |
|
325 | ||
|
326 | Returns | |
|
327 | ------- | |
|
328 | comment : bool | |
|
329 | True if source ends in a comment or multiline string. | |
|
330 | """ | |
|
331 | toktypes = _line_tokens(src) | |
|
332 | return (tokenize2.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes) | |
|
333 | ||
|
305 | 334 | |
|
306 | 335 | @StatelessInputTransformer.wrap |
|
307 | 336 | def help_end(line): |
|
308 | 337 | """Translate lines with ?/?? at the end""" |
|
309 | 338 | m = _help_end_re.search(line) |
|
310 |
if m is None or |
|
|
339 | if m is None or ends_in_comment_or_string(line): | |
|
311 | 340 | return line |
|
312 | 341 | target = m.group(1) |
|
313 | 342 | esc = m.group(3) |
@@ -359,8 +388,26 b' def cellmagic(end_on_blank_line=False):' | |||
|
359 | 388 | line = tpl % (magic_name, first, u'\n'.join(body)) |
|
360 | 389 | |
|
361 | 390 | |
|
362 | def _strip_prompts(prompt_re): | |
|
363 |
"""Remove matching input prompts from a block of input. |
|
|
391 | def _strip_prompts(prompt_re, initial_re=None): | |
|
392 | """Remove matching input prompts from a block of input. | |
|
393 | ||
|
394 | Parameters | |
|
395 | ---------- | |
|
396 | prompt_re : regular expression | |
|
397 | A regular expression matching any input prompt (including continuation) | |
|
398 | initial_re : regular expression, optional | |
|
399 | A regular expression matching only the initial prompt, but not continuation. | |
|
400 | If no initial expression is given, prompt_re will be used everywhere. | |
|
401 | Used mainly for plain Python prompts, where the continuation prompt | |
|
402 | ``...`` is a valid Python expression in Python 3, so shouldn't be stripped. | |
|
403 | ||
|
404 | If initial_re and prompt_re differ, | |
|
405 | only initial_re will be tested against the first line. | |
|
406 | If any prompt is found on the first two lines, | |
|
407 | prompts will be stripped from the rest of the block. | |
|
408 | """ | |
|
409 | if initial_re is None: | |
|
410 | initial_re = prompt_re | |
|
364 | 411 | line = '' |
|
365 | 412 | while True: |
|
366 | 413 | line = (yield line) |
@@ -368,18 +415,19 b' def _strip_prompts(prompt_re):' | |||
|
368 | 415 | # First line of cell |
|
369 | 416 | if line is None: |
|
370 | 417 | continue |
|
371 |
out, n1 = |
|
|
418 | out, n1 = initial_re.subn('', line, count=1) | |
|
372 | 419 | line = (yield out) |
|
373 | 420 | |
|
374 | # Second line of cell, because people often copy from just after the | |
|
375 | # first prompt, so we might not see it in the first line. | |
|
376 | 421 | if line is None: |
|
377 | 422 | continue |
|
423 | # check for any prompt on the second line of the cell, | |
|
424 | # because people often copy from just after the first prompt, | |
|
425 | # so we might not see it in the first line. | |
|
378 | 426 | out, n2 = prompt_re.subn('', line, count=1) |
|
379 | 427 | line = (yield out) |
|
380 | 428 | |
|
381 | 429 | if n1 or n2: |
|
382 |
# Found |
|
|
430 | # Found a prompt in the first two lines - check for it in | |
|
383 | 431 | # the rest of the cell as well. |
|
384 | 432 | while line is not None: |
|
385 | 433 | line = (yield prompt_re.sub('', line, count=1)) |
@@ -394,7 +442,8 b' def classic_prompt():' | |||
|
394 | 442 | """Strip the >>>/... prompts of the Python interactive shell.""" |
|
395 | 443 | # FIXME: non-capturing version (?:...) usable? |
|
396 | 444 | prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)') |
|
397 | return _strip_prompts(prompt_re) | |
|
445 | initial_re = re.compile(r'^(>>> ?)') | |
|
446 | return _strip_prompts(prompt_re, initial_re) | |
|
398 | 447 | |
|
399 | 448 | @CoroutineInputTransformer.wrap |
|
400 | 449 | def ipy_prompt(): |
@@ -47,7 +47,6 b' from IPython.core.displayhook import DisplayHook' | |||
|
47 | 47 | from IPython.core.displaypub import DisplayPublisher |
|
48 | 48 | from IPython.core.error import UsageError |
|
49 | 49 | from IPython.core.extensions import ExtensionManager |
|
50 | from IPython.core.fakemodule import FakeModule, init_fakemod_dict | |
|
51 | 50 | from IPython.core.formatters import DisplayFormatter |
|
52 | 51 | from IPython.core.history import HistoryManager |
|
53 | 52 | from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGIC2 |
@@ -186,6 +185,13 b' class ReadlineNoRecord(object):' | |||
|
186 | 185 | ghi = self.shell.readline.get_history_item |
|
187 | 186 | return [ghi(x) for x in range(start, end)] |
|
188 | 187 | |
|
188 | ||
|
189 | @undoc | |
|
190 | class DummyMod(object): | |
|
191 | """A dummy module used for IPython's interactive module when | |
|
192 | a namespace must be assigned to the module's __dict__.""" | |
|
193 | pass | |
|
194 | ||
|
189 | 195 | #----------------------------------------------------------------------------- |
|
190 | 196 | # Main IPython class |
|
191 | 197 | #----------------------------------------------------------------------------- |
@@ -464,7 +470,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
464 | 470 | # because it and init_io have to come after init_readline. |
|
465 | 471 | self.init_user_ns() |
|
466 | 472 | self.init_logger() |
|
467 | self.init_alias() | |
|
468 | 473 | self.init_builtins() |
|
469 | 474 | |
|
470 | 475 | # The following was in post_config_initialization |
@@ -496,6 +501,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
496 | 501 | self.init_displayhook() |
|
497 | 502 | self.init_latextool() |
|
498 | 503 | self.init_magics() |
|
504 | self.init_alias() | |
|
499 | 505 | self.init_logstart() |
|
500 | 506 | self.init_pdb() |
|
501 | 507 | self.init_extension_manager() |
@@ -819,15 +825,18 b' class InteractiveShell(SingletonConfigurable):' | |||
|
819 | 825 | # Things related to the "main" module |
|
820 | 826 | #------------------------------------------------------------------------- |
|
821 | 827 | |
|
822 | def new_main_mod(self, filename): | |
|
828 | def new_main_mod(self, filename, modname): | |
|
823 | 829 | """Return a new 'main' module object for user code execution. |
|
824 | 830 | |
|
825 | 831 | ``filename`` should be the path of the script which will be run in the |
|
826 | 832 | module. Requests with the same filename will get the same module, with |
|
827 | 833 | its namespace cleared. |
|
828 | 834 | |
|
835 | ``modname`` should be the module name - normally either '__main__' or | |
|
836 | the basename of the file without the extension. | |
|
837 | ||
|
829 | 838 | When scripts are executed via %run, we must keep a reference to their |
|
830 |
__main__ module |
|
|
839 | __main__ module around so that Python doesn't | |
|
831 | 840 | clear it, rendering references to module globals useless. |
|
832 | 841 | |
|
833 | 842 | This method keeps said reference in a private dict, keyed by the |
@@ -840,9 +849,16 b' class InteractiveShell(SingletonConfigurable):' | |||
|
840 | 849 | try: |
|
841 | 850 | main_mod = self._main_mod_cache[filename] |
|
842 | 851 | except KeyError: |
|
843 |
main_mod = self._main_mod_cache[filename] = |
|
|
852 | main_mod = self._main_mod_cache[filename] = types.ModuleType(modname, | |
|
853 | doc="Module created for script run in IPython") | |
|
844 | 854 | else: |
|
845 | init_fakemod_dict(main_mod) | |
|
855 | main_mod.__dict__.clear() | |
|
856 | main_mod.__name__ = modname | |
|
857 | ||
|
858 | main_mod.__file__ = filename | |
|
859 | # It seems pydoc (and perhaps others) needs any module instance to | |
|
860 | # implement a __nonzero__ method | |
|
861 | main_mod.__nonzero__ = lambda : True | |
|
846 | 862 | |
|
847 | 863 | return main_mod |
|
848 | 864 | |
@@ -856,7 +872,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
856 | 872 | |
|
857 | 873 | In [15]: import IPython |
|
858 | 874 | |
|
859 | In [16]: m = _ip.new_main_mod(IPython.__file__) | |
|
875 | In [16]: m = _ip.new_main_mod(IPython.__file__, 'IPython') | |
|
860 | 876 | |
|
861 | 877 | In [17]: len(_ip._main_mod_cache) > 0 |
|
862 | 878 | Out[17]: True |
@@ -900,9 +916,9 b' class InteractiveShell(SingletonConfigurable):' | |||
|
900 | 916 | Keywords: |
|
901 | 917 | |
|
902 | 918 | - force(False): by default, this routine checks the instance call_pdb |
|
903 | flag and does not actually invoke the debugger if the flag is false. | |
|
904 | The 'force' option forces the debugger to activate even if the flag | |
|
905 | is false. | |
|
919 | flag and does not actually invoke the debugger if the flag is false. | |
|
920 | The 'force' option forces the debugger to activate even if the flag | |
|
921 | is false. | |
|
906 | 922 | """ |
|
907 | 923 | |
|
908 | 924 | if not (force or self.call_pdb): |
@@ -970,7 +986,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
970 | 986 | |
|
971 | 987 | # A record of hidden variables we have added to the user namespace, so |
|
972 | 988 | # we can list later only variables defined in actual interactive use. |
|
973 |
self.user_ns_hidden = |
|
|
989 | self.user_ns_hidden = {} | |
|
974 | 990 | |
|
975 | 991 | # Now that FakeModule produces a real module, we've run into a nasty |
|
976 | 992 | # problem: after script execution (via %run), the module where the user |
@@ -1035,9 +1051,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1035 | 1051 | """ |
|
1036 | 1052 | if user_module is None and user_ns is not None: |
|
1037 | 1053 | user_ns.setdefault("__name__", "__main__") |
|
1038 | class DummyMod(object): | |
|
1039 | "A dummy module used for IPython's interactive namespace." | |
|
1040 | pass | |
|
1041 | 1054 | user_module = DummyMod() |
|
1042 | 1055 | user_module.__dict__ = user_ns |
|
1043 | 1056 | |
@@ -1150,7 +1163,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1150 | 1163 | |
|
1151 | 1164 | Note that this does not include the displayhook, which also caches |
|
1152 | 1165 | objects from the output.""" |
|
1153 | return [self.user_ns, self.user_global_ns] + \ | |
|
1166 | return [self.user_ns, self.user_global_ns, self.user_ns_hidden] + \ | |
|
1154 | 1167 | [m.__dict__ for m in self._main_mod_cache.values()] |
|
1155 | 1168 | |
|
1156 | 1169 | def reset(self, new_session=True): |
@@ -1301,7 +1314,8 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1301 | 1314 | # And configure interactive visibility |
|
1302 | 1315 | user_ns_hidden = self.user_ns_hidden |
|
1303 | 1316 | if interactive: |
|
1304 | user_ns_hidden.difference_update(vdict) | |
|
1317 | for name in vdict: | |
|
1318 | user_ns_hidden.pop(name, None) | |
|
1305 | 1319 | else: |
|
1306 | 1320 | user_ns_hidden.update(vdict) |
|
1307 | 1321 | |
@@ -1321,7 +1335,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1321 | 1335 | for name, obj in variables.iteritems(): |
|
1322 | 1336 | if name in self.user_ns and self.user_ns[name] is obj: |
|
1323 | 1337 | del self.user_ns[name] |
|
1324 |
self.user_ns_hidden. |
|
|
1338 | self.user_ns_hidden.pop(name, None) | |
|
1325 | 1339 | |
|
1326 | 1340 | #------------------------------------------------------------------------- |
|
1327 | 1341 | # Things related to object introspection |
@@ -1349,9 +1363,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1349 | 1363 | namespaces = [ ('Interactive', self.user_ns), |
|
1350 | 1364 | ('Interactive (global)', self.user_global_ns), |
|
1351 | 1365 | ('Python builtin', builtin_mod.__dict__), |
|
1352 | ('Alias', self.alias_manager.alias_table), | |
|
1353 | 1366 | ] |
|
1354 | alias_ns = self.alias_manager.alias_table | |
|
1355 | 1367 | |
|
1356 | 1368 | # initialize results to 'null' |
|
1357 | 1369 | found = False; obj = None; ospace = None; ds = None; |
@@ -1390,8 +1402,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1390 | 1402 | # If we finish the for loop (no break), we got all members |
|
1391 | 1403 | found = True |
|
1392 | 1404 | ospace = nsname |
|
1393 | if ns == alias_ns: | |
|
1394 | isalias = True | |
|
1395 | 1405 | break # namespace loop |
|
1396 | 1406 | |
|
1397 | 1407 | # Try to see if it's magic |
@@ -1926,7 +1936,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1926 | 1936 | self.Completer = IPCompleter(shell=self, |
|
1927 | 1937 | namespace=self.user_ns, |
|
1928 | 1938 | global_namespace=self.user_global_ns, |
|
1929 | alias_table=self.alias_manager.alias_table, | |
|
1930 | 1939 | use_readline=self.has_readline, |
|
1931 | 1940 | parent=self, |
|
1932 | 1941 | ) |
@@ -2291,7 +2300,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2291 | 2300 | def init_alias(self): |
|
2292 | 2301 | self.alias_manager = AliasManager(shell=self, parent=self) |
|
2293 | 2302 | self.configurables.append(self.alias_manager) |
|
2294 | self.ns_table['alias'] = self.alias_manager.alias_table, | |
|
2295 | 2303 | |
|
2296 | 2304 | #------------------------------------------------------------------------- |
|
2297 | 2305 | # Things related to extensions |
@@ -2973,7 +2981,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2973 | 2981 | Optional inputs: |
|
2974 | 2982 | |
|
2975 | 2983 | - data(None): if data is given, it gets written out to the temp file |
|
2976 | immediately, and the file is closed again.""" | |
|
2984 | immediately, and the file is closed again.""" | |
|
2977 | 2985 | |
|
2978 | 2986 | filename = tempfile.mktemp('.py', prefix) |
|
2979 | 2987 | self.tempfiles.append(filename) |
@@ -3016,13 +3024,14 b' class InteractiveShell(SingletonConfigurable):' | |||
|
3016 | 3024 | |
|
3017 | 3025 | Optional Parameters: |
|
3018 | 3026 | - raw(False): by default, the processed input is used. If this is |
|
3019 | true, the raw input history is used instead. | |
|
3027 | true, the raw input history is used instead. | |
|
3020 | 3028 | |
|
3021 | 3029 | Note that slices can be called with two notations: |
|
3022 | 3030 | |
|
3023 | 3031 | N:M -> standard python form, means including items N...(M-1). |
|
3024 | 3032 | |
|
3025 |
N-M -> include items N..M (closed endpoint). |
|
|
3033 | N-M -> include items N..M (closed endpoint). | |
|
3034 | """ | |
|
3026 | 3035 | lines = self.history_manager.get_range_by_str(range_str, raw=raw) |
|
3027 | 3036 | return "\n".join(x for _, _, x in lines) |
|
3028 | 3037 |
@@ -171,11 +171,11 b' which already exists. But you must first start the logging process with' | |||
|
171 | 171 | Inputs: |
|
172 | 172 | |
|
173 | 173 | - line_mod: possibly modified input, such as the transformations made |
|
174 |
by input prefilters or input handlers of various kinds. |
|
|
175 | always be valid Python. | |
|
174 | by input prefilters or input handlers of various kinds. This should | |
|
175 | always be valid Python. | |
|
176 | 176 | |
|
177 |
- line_ori: unmodified input line from the user. |
|
|
178 | necessarily valid Python. | |
|
177 | - line_ori: unmodified input line from the user. This is not | |
|
178 | necessarily valid Python. | |
|
179 | 179 | """ |
|
180 | 180 | |
|
181 | 181 | # Write the log line, but decide which one according to the |
@@ -489,11 +489,11 b' class Magics(object):' | |||
|
489 | 489 | MUST: |
|
490 | 490 | |
|
491 | 491 | - Use the method decorators `@line_magic` and `@cell_magic` to decorate |
|
492 | individual methods as magic functions, AND | |
|
492 | individual methods as magic functions, AND | |
|
493 | 493 | |
|
494 | 494 | - Use the class decorator `@magics_class` to ensure that the magic |
|
495 | methods are properly registered at the instance level upon instance | |
|
496 | initialization. | |
|
495 | methods are properly registered at the instance level upon instance | |
|
496 | initialization. | |
|
497 | 497 | |
|
498 | 498 | See :mod:`magic_functions` for examples of actual implementation classes. |
|
499 | 499 | """ |
@@ -50,9 +50,9 b' Inheritance diagram:' | |||
|
50 | 50 | # |
|
51 | 51 | # The full license is in the file COPYING.txt, distributed with this software. |
|
52 | 52 | #----------------------------------------------------------------------------- |
|
53 | import argparse | |
|
53 | 54 | |
|
54 | 55 | # Our own imports |
|
55 | from IPython.external import argparse | |
|
56 | 56 | from IPython.core.error import UsageError |
|
57 | 57 | from IPython.utils.process import arg_split |
|
58 | 58 | from IPython.utils.text import dedent |
@@ -56,6 +56,37 b' from IPython.utils.warn import warn, error' | |||
|
56 | 56 | # Magic implementation classes |
|
57 | 57 | #----------------------------------------------------------------------------- |
|
58 | 58 | |
|
59 | ||
|
60 | class TimeitResult(object): | |
|
61 | """ | |
|
62 | Object returned by the timeit magic with info about the run. | |
|
63 | ||
|
64 | Contain the following attributes : | |
|
65 | ||
|
66 | loops: (int) number of loop done per measurement | |
|
67 | repeat: (int) number of time the mesurement has been repeated | |
|
68 | best: (float) best execusion time / number | |
|
69 | all_runs: (list of float) execusion time of each run (in s) | |
|
70 | compile_time: (float) time of statement compilation (s) | |
|
71 | ||
|
72 | """ | |
|
73 | ||
|
74 | def __init__(self, loops, repeat, best, all_runs, compile_time, precision): | |
|
75 | self.loops = loops | |
|
76 | self.repeat = repeat | |
|
77 | self.best = best | |
|
78 | self.all_runs = all_runs | |
|
79 | self.compile_time = compile_time | |
|
80 | self._precision = precision | |
|
81 | ||
|
82 | def _repr_pretty_(self, p , cycle): | |
|
83 | unic = u"%d loops, best of %d: %s per loop" % (self.loops, self.repeat, | |
|
84 | _format_time(self.best, self._precision)) | |
|
85 | p.text(u'<TimeitResult : '+unic+u'>') | |
|
86 | ||
|
87 | ||
|
88 | ||
|
89 | ||
|
59 | 90 | @magics_class |
|
60 | 91 | class ExecutionMagics(Magics): |
|
61 | 92 | """Magics related to code execution, debugging, profiling, etc. |
@@ -102,75 +133,84 b' python-profiler package from non-free.""")' | |||
|
102 | 133 | |
|
103 | 134 | Options: |
|
104 | 135 | |
|
105 | -l <limit>: you can place restrictions on what or how much of the | |
|
106 | profile gets printed. The limit value can be: | |
|
107 | ||
|
108 | * A string: only information for function names containing this string | |
|
109 | is printed. | |
|
110 | ||
|
111 | * An integer: only these many lines are printed. | |
|
112 | ||
|
113 | * A float (between 0 and 1): this fraction of the report is printed | |
|
114 | (for example, use a limit of 0.4 to see the topmost 40% only). | |
|
115 | ||
|
116 | You can combine several limits with repeated use of the option. For | |
|
117 | example, '-l __init__ -l 5' will print only the topmost 5 lines of | |
|
118 | information about class constructors. | |
|
119 | ||
|
120 | -r: return the pstats.Stats object generated by the profiling. This | |
|
121 | object has all the information about the profile in it, and you can | |
|
122 | later use it for further analysis or in other functions. | |
|
123 | ||
|
124 | -s <key>: sort profile by given key. You can provide more than one key | |
|
125 | by using the option several times: '-s key1 -s key2 -s key3...'. The | |
|
126 | default sorting key is 'time'. | |
|
127 | ||
|
128 | The following is copied verbatim from the profile documentation | |
|
129 | referenced below: | |
|
130 | ||
|
131 | When more than one key is provided, additional keys are used as | |
|
132 | secondary criteria when the there is equality in all keys selected | |
|
133 | before them. | |
|
134 | ||
|
135 | Abbreviations can be used for any key names, as long as the | |
|
136 | abbreviation is unambiguous. The following are the keys currently | |
|
137 | defined: | |
|
138 | ||
|
139 | Valid Arg Meaning | |
|
140 | "calls" call count | |
|
141 | "cumulative" cumulative time | |
|
142 | "file" file name | |
|
143 | "module" file name | |
|
144 | "pcalls" primitive call count | |
|
145 | "line" line number | |
|
146 | "name" function name | |
|
147 |
|
|
|
148 | "stdname" standard name | |
|
149 | "time" internal time | |
|
150 | ||
|
151 | Note that all sorts on statistics are in descending order (placing | |
|
152 | most time consuming items first), where as name, file, and line number | |
|
153 | searches are in ascending order (i.e., alphabetical). The subtle | |
|
154 | distinction between "nfl" and "stdname" is that the standard name is a | |
|
155 | sort of the name as printed, which means that the embedded line | |
|
156 | numbers get compared in an odd way. For example, lines 3, 20, and 40 | |
|
157 | would (if the file names were the same) appear in the string order | |
|
158 | "20" "3" and "40". In contrast, "nfl" does a numeric compare of the | |
|
159 | line numbers. In fact, sort_stats("nfl") is the same as | |
|
160 | sort_stats("name", "file", "line"). | |
|
161 | ||
|
162 | -T <filename>: save profile results as shown on screen to a text | |
|
163 | file. The profile is still shown on screen. | |
|
164 | ||
|
165 | -D <filename>: save (via dump_stats) profile statistics to given | |
|
166 | filename. This data is in a format understood by the pstats module, and | |
|
167 | is generated by a call to the dump_stats() method of profile | |
|
168 | objects. The profile is still shown on screen. | |
|
169 | ||
|
170 | -q: suppress output to the pager. Best used with -T and/or -D above. | |
|
136 | -l <limit> | |
|
137 | you can place restrictions on what or how much of the | |
|
138 | profile gets printed. The limit value can be: | |
|
139 | ||
|
140 | * A string: only information for function names containing this string | |
|
141 | is printed. | |
|
142 | ||
|
143 | * An integer: only these many lines are printed. | |
|
144 | ||
|
145 | * A float (between 0 and 1): this fraction of the report is printed | |
|
146 | (for example, use a limit of 0.4 to see the topmost 40% only). | |
|
147 | ||
|
148 | You can combine several limits with repeated use of the option. For | |
|
149 | example, ``-l __init__ -l 5`` will print only the topmost 5 lines of | |
|
150 | information about class constructors. | |
|
151 | ||
|
152 | -r | |
|
153 | return the pstats.Stats object generated by the profiling. This | |
|
154 | object has all the information about the profile in it, and you can | |
|
155 | later use it for further analysis or in other functions. | |
|
156 | ||
|
157 | -s <key> | |
|
158 | sort profile by given key. You can provide more than one key | |
|
159 | by using the option several times: '-s key1 -s key2 -s key3...'. The | |
|
160 | default sorting key is 'time'. | |
|
161 | ||
|
162 | The following is copied verbatim from the profile documentation | |
|
163 | referenced below: | |
|
164 | ||
|
165 | When more than one key is provided, additional keys are used as | |
|
166 | secondary criteria when the there is equality in all keys selected | |
|
167 | before them. | |
|
168 | ||
|
169 | Abbreviations can be used for any key names, as long as the | |
|
170 | abbreviation is unambiguous. The following are the keys currently | |
|
171 | defined: | |
|
172 | ||
|
173 | ============ ===================== | |
|
174 | Valid Arg Meaning | |
|
175 | ============ ===================== | |
|
176 | "calls" call count | |
|
177 | "cumulative" cumulative time | |
|
178 | "file" file name | |
|
179 | "module" file name | |
|
180 | "pcalls" primitive call count | |
|
181 | "line" line number | |
|
182 | "name" function name | |
|
183 | "nfl" name/file/line | |
|
184 | "stdname" standard name | |
|
185 | "time" internal time | |
|
186 | ============ ===================== | |
|
187 | ||
|
188 | Note that all sorts on statistics are in descending order (placing | |
|
189 | most time consuming items first), where as name, file, and line number | |
|
190 | searches are in ascending order (i.e., alphabetical). The subtle | |
|
191 | distinction between "nfl" and "stdname" is that the standard name is a | |
|
192 | sort of the name as printed, which means that the embedded line | |
|
193 | numbers get compared in an odd way. For example, lines 3, 20, and 40 | |
|
194 | would (if the file names were the same) appear in the string order | |
|
195 | "20" "3" and "40". In contrast, "nfl" does a numeric compare of the | |
|
196 | line numbers. In fact, sort_stats("nfl") is the same as | |
|
197 | sort_stats("name", "file", "line"). | |
|
198 | ||
|
199 | -T <filename> | |
|
200 | save profile results as shown on screen to a text | |
|
201 | file. The profile is still shown on screen. | |
|
202 | ||
|
203 | -D <filename> | |
|
204 | save (via dump_stats) profile statistics to given | |
|
205 | filename. This data is in a format understood by the pstats module, and | |
|
206 | is generated by a call to the dump_stats() method of profile | |
|
207 | objects. The profile is still shown on screen. | |
|
208 | ||
|
209 | -q | |
|
210 | suppress output to the pager. Best used with -T and/or -D above. | |
|
171 | 211 | |
|
172 | 212 | If you want to run complete programs under the profiler's control, use |
|
173 |
|
|
|
213 | ``%run -p [prof_opts] filename.py [args to program]`` where prof_opts | |
|
174 | 214 | contains profiler specific options as described here. |
|
175 | 215 | |
|
176 | 216 | You can read the complete documentation for the profile module with:: |
@@ -362,7 +402,8 b' python-profiler package from non-free.""")' | |||
|
362 | 402 | file_finder=get_py_filename): |
|
363 | 403 | """Run the named file inside IPython as a program. |
|
364 | 404 | |
|
365 | Usage: | |
|
405 | Usage:: | |
|
406 | ||
|
366 | 407 | %run [-n -i -e -G] |
|
367 | 408 | [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )] |
|
368 | 409 | ( -m mod | file ) [args] |
@@ -371,14 +412,13 b' python-profiler package from non-free.""")' | |||
|
371 | 412 | the program (put in sys.argv). Then, control returns to IPython's |
|
372 | 413 | prompt. |
|
373 | 414 | |
|
374 |
This is similar to running at a system prompt |
|
|
375 | $ python file args\\ | |
|
415 | This is similar to running at a system prompt ``python file args``, | |
|
376 | 416 | but with the advantage of giving you IPython's tracebacks, and of |
|
377 | 417 | loading all variables into your interactive namespace for further use |
|
378 | 418 | (unless -p is used, see below). |
|
379 | 419 | |
|
380 | 420 | The file is executed in a namespace initially consisting only of |
|
381 | __name__=='__main__' and sys.argv constructed as indicated. It thus | |
|
421 | ``__name__=='__main__'`` and sys.argv constructed as indicated. It thus | |
|
382 | 422 | sees its environment as if it were being run as a stand-alone program |
|
383 | 423 | (except for sharing global objects such as previously imported |
|
384 | 424 | modules). But after execution, the IPython interactive namespace gets |
@@ -390,33 +430,37 b' python-profiler package from non-free.""")' | |||
|
390 | 430 | '*', '?', '[seq]' and '[!seq]' can be used. Additionally, |
|
391 | 431 | tilde '~' will be expanded into user's home directory. Unlike |
|
392 | 432 | real shells, quotation does not suppress expansions. Use |
|
393 |
*two* back slashes (e.g. |
|
|
433 | *two* back slashes (e.g. ``\\\\*``) to suppress expansions. | |
|
394 | 434 | To completely disable these expansions, you can use -G flag. |
|
395 | 435 | |
|
396 | 436 | Options: |
|
397 | 437 | |
|
398 | -n: __name__ is NOT set to '__main__', but to the running file's name | |
|
399 | without extension (as python does under import). This allows running | |
|
400 | scripts and reloading the definitions in them without calling code | |
|
401 | protected by an ' if __name__ == "__main__" ' clause. | |
|
402 | ||
|
403 | -i: run the file in IPython's namespace instead of an empty one. This | |
|
404 | is useful if you are experimenting with code written in a text editor | |
|
405 | which depends on variables defined interactively. | |
|
406 | ||
|
407 | -e: ignore sys.exit() calls or SystemExit exceptions in the script | |
|
408 | being run. This is particularly useful if IPython is being used to | |
|
409 | run unittests, which always exit with a sys.exit() call. In such | |
|
410 | cases you are interested in the output of the test results, not in | |
|
411 | seeing a traceback of the unittest module. | |
|
412 | ||
|
413 | -t: print timing information at the end of the run. IPython will give | |
|
414 | you an estimated CPU time consumption for your script, which under | |
|
415 | Unix uses the resource module to avoid the wraparound problems of | |
|
416 | time.clock(). Under Unix, an estimate of time spent on system tasks | |
|
417 | is also given (for Windows platforms this is reported as 0.0). | |
|
418 | ||
|
419 | If -t is given, an additional -N<N> option can be given, where <N> | |
|
438 | -n | |
|
439 | __name__ is NOT set to '__main__', but to the running file's name | |
|
440 | without extension (as python does under import). This allows running | |
|
441 | scripts and reloading the definitions in them without calling code | |
|
442 | protected by an ``if __name__ == "__main__"`` clause. | |
|
443 | ||
|
444 | -i | |
|
445 | run the file in IPython's namespace instead of an empty one. This | |
|
446 | is useful if you are experimenting with code written in a text editor | |
|
447 | which depends on variables defined interactively. | |
|
448 | ||
|
449 | -e | |
|
450 | ignore sys.exit() calls or SystemExit exceptions in the script | |
|
451 | being run. This is particularly useful if IPython is being used to | |
|
452 | run unittests, which always exit with a sys.exit() call. In such | |
|
453 | cases you are interested in the output of the test results, not in | |
|
454 | seeing a traceback of the unittest module. | |
|
455 | ||
|
456 | -t | |
|
457 | print timing information at the end of the run. IPython will give | |
|
458 | you an estimated CPU time consumption for your script, which under | |
|
459 | Unix uses the resource module to avoid the wraparound problems of | |
|
460 | time.clock(). Under Unix, an estimate of time spent on system tasks | |
|
461 | is also given (for Windows platforms this is reported as 0.0). | |
|
462 | ||
|
463 | If -t is given, an additional ``-N<N>`` option can be given, where <N> | |
|
420 | 464 | must be an integer indicating how many times you want the script to |
|
421 | 465 | run. The final timing report will include total and per run results. |
|
422 | 466 | |
@@ -424,74 +468,78 b' python-profiler package from non-free.""")' | |||
|
424 | 468 | |
|
425 | 469 | In [1]: run -t uniq_stable |
|
426 | 470 | |
|
427 |
IPython CPU timings (estimated): |
|
|
428 |
User : 0.19597 s. |
|
|
429 |
System: 0.0 s. |
|
|
471 | IPython CPU timings (estimated): | |
|
472 | User : 0.19597 s. | |
|
473 | System: 0.0 s. | |
|
430 | 474 | |
|
431 | 475 | In [2]: run -t -N5 uniq_stable |
|
432 | 476 | |
|
433 |
IPython CPU timings (estimated): |
|
|
434 |
Total runs performed: 5 |
|
|
435 |
Times : Total Per run |
|
|
436 |
User : 0.910862 s, 0.1821724 s. |
|
|
477 | IPython CPU timings (estimated): | |
|
478 | Total runs performed: 5 | |
|
479 | Times : Total Per run | |
|
480 | User : 0.910862 s, 0.1821724 s. | |
|
437 | 481 | System: 0.0 s, 0.0 s. |
|
438 | 482 | |
|
439 | -d: run your program under the control of pdb, the Python debugger. | |
|
440 | This allows you to execute your program step by step, watch variables, | |
|
441 | etc. Internally, what IPython does is similar to calling: | |
|
483 | -d | |
|
484 | run your program under the control of pdb, the Python debugger. | |
|
485 | This allows you to execute your program step by step, watch variables, | |
|
486 | etc. Internally, what IPython does is similar to calling:: | |
|
442 | 487 | |
|
443 | pdb.run('execfile("YOURFILENAME")') | |
|
488 | pdb.run('execfile("YOURFILENAME")') | |
|
444 | 489 | |
|
445 | with a breakpoint set on line 1 of your file. You can change the line | |
|
446 | number for this automatic breakpoint to be <N> by using the -bN option | |
|
447 |
(where N must be an integer). |
|
|
490 | with a breakpoint set on line 1 of your file. You can change the line | |
|
491 | number for this automatic breakpoint to be <N> by using the -bN option | |
|
492 | (where N must be an integer). For example:: | |
|
448 | 493 | |
|
449 | %run -d -b40 myscript | |
|
494 | %run -d -b40 myscript | |
|
450 | 495 | |
|
451 | will set the first breakpoint at line 40 in myscript.py. Note that | |
|
452 | the first breakpoint must be set on a line which actually does | |
|
453 | something (not a comment or docstring) for it to stop execution. | |
|
496 | will set the first breakpoint at line 40 in myscript.py. Note that | |
|
497 | the first breakpoint must be set on a line which actually does | |
|
498 | something (not a comment or docstring) for it to stop execution. | |
|
454 | 499 | |
|
455 | Or you can specify a breakpoint in a different file:: | |
|
500 | Or you can specify a breakpoint in a different file:: | |
|
456 | 501 | |
|
457 | %run -d -b myotherfile.py:20 myscript | |
|
502 | %run -d -b myotherfile.py:20 myscript | |
|
458 | 503 | |
|
459 | When the pdb debugger starts, you will see a (Pdb) prompt. You must | |
|
460 | first enter 'c' (without quotes) to start execution up to the first | |
|
461 | breakpoint. | |
|
504 | When the pdb debugger starts, you will see a (Pdb) prompt. You must | |
|
505 | first enter 'c' (without quotes) to start execution up to the first | |
|
506 | breakpoint. | |
|
462 | 507 | |
|
463 | Entering 'help' gives information about the use of the debugger. You | |
|
464 | can easily see pdb's full documentation with "import pdb;pdb.help()" | |
|
465 | at a prompt. | |
|
508 | Entering 'help' gives information about the use of the debugger. You | |
|
509 | can easily see pdb's full documentation with "import pdb;pdb.help()" | |
|
510 | at a prompt. | |
|
466 | 511 | |
|
467 | -p: run program under the control of the Python profiler module (which | |
|
468 | prints a detailed report of execution times, function calls, etc). | |
|
512 | -p | |
|
513 | run program under the control of the Python profiler module (which | |
|
514 | prints a detailed report of execution times, function calls, etc). | |
|
469 | 515 | |
|
470 | You can pass other options after -p which affect the behavior of the | |
|
471 | profiler itself. See the docs for %prun for details. | |
|
516 | You can pass other options after -p which affect the behavior of the | |
|
517 | profiler itself. See the docs for %prun for details. | |
|
472 | 518 | |
|
473 | In this mode, the program's variables do NOT propagate back to the | |
|
474 | IPython interactive namespace (because they remain in the namespace | |
|
475 | where the profiler executes them). | |
|
519 | In this mode, the program's variables do NOT propagate back to the | |
|
520 | IPython interactive namespace (because they remain in the namespace | |
|
521 | where the profiler executes them). | |
|
476 | 522 | |
|
477 | Internally this triggers a call to %prun, see its documentation for | |
|
478 | details on the options available specifically for profiling. | |
|
523 | Internally this triggers a call to %prun, see its documentation for | |
|
524 | details on the options available specifically for profiling. | |
|
479 | 525 | |
|
480 | 526 | There is one special usage for which the text above doesn't apply: |
|
481 | 527 | if the filename ends with .ipy, the file is run as ipython script, |
|
482 | 528 | just as if the commands were written on IPython prompt. |
|
483 | 529 | |
|
484 | -m: specify module name to load instead of script path. Similar to | |
|
485 | the -m option for the python interpreter. Use this option last if you | |
|
486 | want to combine with other %run options. Unlike the python interpreter | |
|
487 | only source modules are allowed no .pyc or .pyo files. | |
|
488 | For example:: | |
|
530 | -m | |
|
531 | specify module name to load instead of script path. Similar to | |
|
532 | the -m option for the python interpreter. Use this option last if you | |
|
533 | want to combine with other %run options. Unlike the python interpreter | |
|
534 | only source modules are allowed no .pyc or .pyo files. | |
|
535 | For example:: | |
|
489 | 536 | |
|
490 | %run -m example | |
|
537 | %run -m example | |
|
491 | 538 | |
|
492 | will run the example module. | |
|
539 | will run the example module. | |
|
493 | 540 | |
|
494 | -G: disable shell-like glob expansion of arguments. | |
|
541 | -G | |
|
542 | disable shell-like glob expansion of arguments. | |
|
495 | 543 | |
|
496 | 544 | """ |
|
497 | 545 | |
@@ -550,6 +598,11 b' python-profiler package from non-free.""")' | |||
|
550 | 598 | __name__save = self.shell.user_ns['__name__'] |
|
551 | 599 | prog_ns['__name__'] = '__main__' |
|
552 | 600 | main_mod = self.shell.user_module |
|
601 | ||
|
602 | # Since '%run foo' emulates 'python foo.py' at the cmd line, we must | |
|
603 | # set the __file__ global in the script's namespace | |
|
604 | # TK: Is this necessary in interactive mode? | |
|
605 | prog_ns['__file__'] = filename | |
|
553 | 606 | else: |
|
554 | 607 | # Run in a fresh, empty namespace |
|
555 | 608 | if 'n' in opts: |
@@ -560,13 +613,8 b' python-profiler package from non-free.""")' | |||
|
560 | 613 | # The shell MUST hold a reference to prog_ns so after %run |
|
561 | 614 | # exits, the python deletion mechanism doesn't zero it out |
|
562 | 615 | # (leaving dangling references). See interactiveshell for details |
|
563 | main_mod = self.shell.new_main_mod(filename) | |
|
616 | main_mod = self.shell.new_main_mod(filename, name) | |
|
564 | 617 | prog_ns = main_mod.__dict__ |
|
565 | prog_ns['__name__'] = name | |
|
566 | ||
|
567 | # Since '%run foo' emulates 'python foo.py' at the cmd line, we must | |
|
568 | # set the __file__ global in the script's namespace | |
|
569 | prog_ns['__file__'] = filename | |
|
570 | 618 | |
|
571 | 619 | # pickle fix. See interactiveshell for an explanation. But we need to |
|
572 | 620 | # make sure that, if we overwrite __main__, we replace it at the end |
@@ -786,9 +834,9 b' python-profiler package from non-free.""")' | |||
|
786 | 834 | """Time execution of a Python statement or expression |
|
787 | 835 | |
|
788 | 836 | Usage, in line mode: |
|
789 | %timeit [-n<N> -r<R> [-t|-c]] statement | |
|
837 | %timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] statement | |
|
790 | 838 | or in cell mode: |
|
791 | %%timeit [-n<N> -r<R> [-t|-c]] setup_code | |
|
839 | %%timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] setup_code | |
|
792 | 840 | code |
|
793 | 841 | code... |
|
794 | 842 | |
@@ -819,6 +867,11 b' python-profiler package from non-free.""")' | |||
|
819 | 867 | -p<P>: use a precision of <P> digits to display the timing result. |
|
820 | 868 | Default: 3 |
|
821 | 869 | |
|
870 | -q: Quiet, do not print result. | |
|
871 | ||
|
872 | -o: return a TimeitResult that can be stored in a variable to inspect | |
|
873 | the result in more details. | |
|
874 | ||
|
822 | 875 | |
|
823 | 876 | Examples |
|
824 | 877 | -------- |
@@ -851,7 +904,7 b' python-profiler package from non-free.""")' | |||
|
851 | 904 | |
|
852 | 905 | import timeit |
|
853 | 906 | |
|
854 | opts, stmt = self.parse_options(line,'n:r:tcp:', | |
|
907 | opts, stmt = self.parse_options(line,'n:r:tcp:qo', | |
|
855 | 908 | posix=False, strict=False) |
|
856 | 909 | if stmt == "" and cell is None: |
|
857 | 910 | return |
@@ -860,6 +913,8 b' python-profiler package from non-free.""")' | |||
|
860 | 913 | number = int(getattr(opts, "n", 0)) |
|
861 | 914 | repeat = int(getattr(opts, "r", timeit.default_repeat)) |
|
862 | 915 | precision = int(getattr(opts, "p", 3)) |
|
916 | quiet = 'q' in opts | |
|
917 | return_result = 'o' in opts | |
|
863 | 918 | if hasattr(opts, "t"): |
|
864 | 919 | timefunc = time.time |
|
865 | 920 | if hasattr(opts, "c"): |
@@ -931,13 +986,15 b' python-profiler package from non-free.""")' | |||
|
931 | 986 | if timer.timeit(number) >= 0.2: |
|
932 | 987 | break |
|
933 | 988 | number *= 10 |
|
934 | ||
|
935 |
best = min( |
|
|
936 | ||
|
937 | print u"%d loops, best of %d: %s per loop" % (number, repeat, | |
|
938 | _format_time(best, precision)) | |
|
939 | if tc > tc_min: | |
|
940 | print "Compiler time: %.2f s" % tc | |
|
989 | all_runs = timer.repeat(repeat, number) | |
|
990 | best = min(all_runs) / number | |
|
991 | if not quiet : | |
|
992 | print u"%d loops, best of %d: %s per loop" % (number, repeat, | |
|
993 | _format_time(best, precision)) | |
|
994 | if tc > tc_min: | |
|
995 | print "Compiler time: %.2f s" % tc | |
|
996 | if return_result: | |
|
997 | return TimeitResult(number, repeat, best, all_runs, tc, precision) | |
|
941 | 998 | |
|
942 | 999 | @skip_doctest |
|
943 | 1000 | @needs_local_scope |
@@ -42,34 +42,48 b' class LoggingMagics(Magics):' | |||
|
42 | 42 | history up to that point and then continues logging. |
|
43 | 43 | |
|
44 | 44 | %logstart takes a second optional parameter: logging mode. This can be one |
|
45 |
of (note that the modes are given unquoted): |
|
|
46 | append: well, that says it.\\ | |
|
47 | backup: rename (if exists) to name~ and start name.\\ | |
|
48 | global: single logfile in your home dir, appended to.\\ | |
|
49 | over : overwrite existing log.\\ | |
|
50 | rotate: create rotating logs name.1~, name.2~, etc. | |
|
45 | of (note that the modes are given unquoted): | |
|
46 | ||
|
47 | append | |
|
48 | Keep logging at the end of any existing file. | |
|
49 | ||
|
50 | backup | |
|
51 | Rename any existing file to name~ and start name. | |
|
52 | ||
|
53 | global | |
|
54 | Append to a single logfile in your home directory. | |
|
55 | ||
|
56 | over | |
|
57 | Overwrite any existing log. | |
|
58 | ||
|
59 | rotate | |
|
60 | Create rotating logs: name.1~, name.2~, etc. | |
|
51 | 61 | |
|
52 | 62 | Options: |
|
53 | 63 | |
|
54 | -o: log also IPython's output. In this mode, all commands which | |
|
55 | generate an Out[NN] prompt are recorded to the logfile, right after | |
|
56 | their corresponding input line. The output lines are always | |
|
57 | prepended with a '#[Out]# ' marker, so that the log remains valid | |
|
58 | Python code. | |
|
64 | -o | |
|
65 | log also IPython's output. In this mode, all commands which | |
|
66 | generate an Out[NN] prompt are recorded to the logfile, right after | |
|
67 | their corresponding input line. The output lines are always | |
|
68 | prepended with a '#[Out]# ' marker, so that the log remains valid | |
|
69 | Python code. | |
|
59 | 70 | |
|
60 | 71 | Since this marker is always the same, filtering only the output from |
|
61 | 72 | a log is very easy, using for example a simple awk call:: |
|
62 | 73 | |
|
63 | 74 | awk -F'#\\[Out\\]# ' '{if($2) {print $2}}' ipython_log.py |
|
64 | 75 | |
|
65 | -r: log 'raw' input. Normally, IPython's logs contain the processed | |
|
66 | input, so that user lines are logged in their final form, converted | |
|
67 | into valid Python. For example, %Exit is logged as | |
|
68 | _ip.magic("Exit"). If the -r flag is given, all input is logged | |
|
69 | exactly as typed, with no transformations applied. | |
|
70 | ||
|
71 | -t: put timestamps before each input line logged (these are put in | |
|
72 |
|
|
|
76 | -r | |
|
77 | log 'raw' input. Normally, IPython's logs contain the processed | |
|
78 | input, so that user lines are logged in their final form, converted | |
|
79 | into valid Python. For example, %Exit is logged as | |
|
80 | _ip.magic("Exit"). If the -r flag is given, all input is logged | |
|
81 | exactly as typed, with no transformations applied. | |
|
82 | ||
|
83 | -t | |
|
84 | put timestamps before each input line logged (these are put in | |
|
85 | comments). | |
|
86 | """ | |
|
73 | 87 | |
|
74 | 88 | opts,par = self.parse_options(parameter_s,'ort') |
|
75 | 89 | log_output = 'o' in opts |
@@ -265,9 +265,10 b' class NamespaceMagics(Magics):' | |||
|
265 | 265 | |
|
266 | 266 | user_ns = self.shell.user_ns |
|
267 | 267 | user_ns_hidden = self.shell.user_ns_hidden |
|
268 | nonmatching = object() # This can never be in user_ns | |
|
268 | 269 | out = [ i for i in user_ns |
|
269 | 270 | if not i.startswith('_') \ |
|
270 |
and not i |
|
|
271 | and (user_ns[i] is not user_ns_hidden.get(i, nonmatching)) ] | |
|
271 | 272 | |
|
272 | 273 | typelist = parameter_s.split() |
|
273 | 274 | if typelist: |
@@ -353,10 +354,10 b' class NamespaceMagics(Magics):' | |||
|
353 | 354 | - For {},[],(): their length. |
|
354 | 355 | |
|
355 | 356 | - For numpy arrays, a summary with shape, number of |
|
356 | elements, typecode and size in memory. | |
|
357 | elements, typecode and size in memory. | |
|
357 | 358 | |
|
358 | 359 | - Everything else: a string representation, snipping their middle if |
|
359 | too long. | |
|
360 | too long. | |
|
360 | 361 | |
|
361 | 362 | Examples |
|
362 | 363 | -------- |
@@ -26,6 +26,7 b' from pprint import pformat' | |||
|
26 | 26 | from IPython.core import magic_arguments |
|
27 | 27 | from IPython.core import oinspect |
|
28 | 28 | from IPython.core import page |
|
29 | from IPython.core.alias import AliasError, Alias | |
|
29 | 30 | from IPython.core.error import UsageError |
|
30 | 31 | from IPython.core.magic import ( |
|
31 | 32 | Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic |
@@ -113,10 +114,14 b' class OSMagics(Magics):' | |||
|
113 | 114 | # Now try to define a new one |
|
114 | 115 | try: |
|
115 | 116 | alias,cmd = par.split(None, 1) |
|
116 | except: | |
|
117 |
print |
|
|
118 | else: | |
|
119 | self.shell.alias_manager.soft_define_alias(alias, cmd) | |
|
117 | except TypeError: | |
|
118 | print(oinspect.getdoc(self.alias)) | |
|
119 | return | |
|
120 | ||
|
121 | try: | |
|
122 | self.shell.alias_manager.define_alias(alias, cmd) | |
|
123 | except AliasError as e: | |
|
124 | print(e) | |
|
120 | 125 | # end magic_alias |
|
121 | 126 | |
|
122 | 127 | @line_magic |
@@ -124,7 +129,12 b' class OSMagics(Magics):' | |||
|
124 | 129 | """Remove an alias""" |
|
125 | 130 | |
|
126 | 131 | aname = parameter_s.strip() |
|
127 | self.shell.alias_manager.undefine_alias(aname) | |
|
132 | try: | |
|
133 | self.shell.alias_manager.undefine_alias(aname) | |
|
134 | except ValueError as e: | |
|
135 | print(e) | |
|
136 | return | |
|
137 | ||
|
128 | 138 | stored = self.shell.db.get('stored_aliases', {} ) |
|
129 | 139 | if aname in stored: |
|
130 | 140 | print "Removing %stored alias",aname |
@@ -182,7 +192,7 b' class OSMagics(Magics):' | |||
|
182 | 192 | try: |
|
183 | 193 | # Removes dots from the name since ipython |
|
184 | 194 | # will assume names with dots to be python. |
|
185 |
if |
|
|
195 | if not self.shell.alias_manager.is_alias(ff): | |
|
186 | 196 | self.shell.alias_manager.define_alias( |
|
187 | 197 | ff.replace('.',''), ff) |
|
188 | 198 | except InvalidAliasError: |
@@ -190,7 +200,7 b' class OSMagics(Magics):' | |||
|
190 | 200 | else: |
|
191 | 201 | syscmdlist.append(ff) |
|
192 | 202 | else: |
|
193 |
no_alias = s |
|
|
203 | no_alias = Alias.blacklist | |
|
194 | 204 | for pdir in path: |
|
195 | 205 | os.chdir(pdir) |
|
196 | 206 | for ff in os.listdir(pdir): |
@@ -156,8 +156,8 b' def getsource(obj,is_binary=False):' | |||
|
156 | 156 | Optional inputs: |
|
157 | 157 | |
|
158 | 158 | - is_binary: whether the object is known to come from a binary source. |
|
159 | This implementation will skip returning any output for binary objects, but | |
|
160 | custom extractors may know how to meaningfully process them.""" | |
|
159 | This implementation will skip returning any output for binary objects, but | |
|
160 | custom extractors may know how to meaningfully process them.""" | |
|
161 | 161 | |
|
162 | 162 | if is_binary: |
|
163 | 163 | return None |
@@ -545,7 +545,7 b' class Inspector:' | |||
|
545 | 545 | - formatter: special formatter for docstrings (see pdoc) |
|
546 | 546 | |
|
547 | 547 | - info: a structure with some information fields which may have been |
|
548 | precomputed already. | |
|
548 | precomputed already. | |
|
549 | 549 | |
|
550 | 550 | - detail_level: if set to 1, more information is given. |
|
551 | 551 | """ |
@@ -609,7 +609,7 b' class Inspector:' | |||
|
609 | 609 | - formatter: special formatter for docstrings (see pdoc) |
|
610 | 610 | |
|
611 | 611 | - info: a structure with some information fields which may have been |
|
612 | precomputed already. | |
|
612 | precomputed already. | |
|
613 | 613 | |
|
614 | 614 | - detail_level: if set to 1, more information is given. |
|
615 | 615 | """ |
@@ -829,8 +829,8 b' class Inspector:' | |||
|
829 | 829 | Arguments: |
|
830 | 830 | |
|
831 | 831 | - pattern: string containing shell-like wildcards to use in namespace |
|
832 | searches and optionally a type specification to narrow the search to | |
|
833 | objects of that type. | |
|
832 | searches and optionally a type specification to narrow the search to | |
|
833 | objects of that type. | |
|
834 | 834 | |
|
835 | 835 | - ns_table: dict of name->namespaces for search. |
|
836 | 836 | |
@@ -841,7 +841,7 b' class Inspector:' | |||
|
841 | 841 | - ignore_case(False): make the search case-insensitive. |
|
842 | 842 | |
|
843 | 843 | - show_all(False): show all names, including those starting with |
|
844 | underscores. | |
|
844 | underscores. | |
|
845 | 845 | """ |
|
846 | 846 | #print 'ps pattern:<%r>' % pattern # dbg |
|
847 | 847 |
@@ -325,9 +325,11 b" def snip_print(str,width = 75,print_full = 0,header = ''):" | |||
|
325 | 325 | """Print a string snipping the midsection to fit in width. |
|
326 | 326 | |
|
327 | 327 | print_full: mode control: |
|
328 | ||
|
328 | 329 | - 0: only snip long strings |
|
329 | 330 | - 1: send to page() directly. |
|
330 | 331 | - 2: snip long strings and ask for full length viewing with page() |
|
332 | ||
|
331 | 333 | Return 1 if snipping was necessary, 0 otherwise.""" |
|
332 | 334 | |
|
333 | 335 | if print_full == 1: |
@@ -488,22 +488,6 b' class AutoMagicChecker(PrefilterChecker):' | |||
|
488 | 488 | return self.prefilter_manager.get_handler_by_name('magic') |
|
489 | 489 | |
|
490 | 490 | |
|
491 | class AliasChecker(PrefilterChecker): | |
|
492 | ||
|
493 | priority = Integer(800, config=True) | |
|
494 | ||
|
495 | def check(self, line_info): | |
|
496 | "Check if the initital identifier on the line is an alias." | |
|
497 | # Note: aliases can not contain '.' | |
|
498 | head = line_info.ifun.split('.',1)[0] | |
|
499 | if line_info.ifun not in self.shell.alias_manager \ | |
|
500 | or head not in self.shell.alias_manager \ | |
|
501 | or is_shadowed(head, self.shell): | |
|
502 | return None | |
|
503 | ||
|
504 | return self.prefilter_manager.get_handler_by_name('alias') | |
|
505 | ||
|
506 | ||
|
507 | 491 | class PythonOpsChecker(PrefilterChecker): |
|
508 | 492 | |
|
509 | 493 | priority = Integer(900, config=True) |
@@ -591,20 +575,6 b' class PrefilterHandler(Configurable):' | |||
|
591 | 575 | return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name) |
|
592 | 576 | |
|
593 | 577 | |
|
594 | class AliasHandler(PrefilterHandler): | |
|
595 | ||
|
596 | handler_name = Unicode('alias') | |
|
597 | ||
|
598 | def handle(self, line_info): | |
|
599 | """Handle alias input lines. """ | |
|
600 | transformed = self.shell.alias_manager.expand_aliases(line_info.ifun,line_info.the_rest) | |
|
601 | # pre is needed, because it carries the leading whitespace. Otherwise | |
|
602 | # aliases won't work in indented sections. | |
|
603 | line_out = '%sget_ipython().system(%r)' % (line_info.pre_whitespace, transformed) | |
|
604 | ||
|
605 | return line_out | |
|
606 | ||
|
607 | ||
|
608 | 578 | class MacroHandler(PrefilterHandler): |
|
609 | 579 | handler_name = Unicode("macro") |
|
610 | 580 | |
@@ -730,14 +700,12 b' _default_checkers = [' | |||
|
730 | 700 | IPyAutocallChecker, |
|
731 | 701 | AssignmentChecker, |
|
732 | 702 | AutoMagicChecker, |
|
733 | AliasChecker, | |
|
734 | 703 | PythonOpsChecker, |
|
735 | 704 | AutocallChecker |
|
736 | 705 | ] |
|
737 | 706 | |
|
738 | 707 | _default_handlers = [ |
|
739 | 708 | PrefilterHandler, |
|
740 | AliasHandler, | |
|
741 | 709 | MacroHandler, |
|
742 | 710 | MagicHandler, |
|
743 | 711 | AutoHandler, |
@@ -11,7 +11,7 b' Authors:' | |||
|
11 | 11 | """ |
|
12 | 12 | |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 |
# Copyright (C) 2008 |
|
|
14 | # Copyright (C) 2008 The IPython Development Team | |
|
15 | 15 | # |
|
16 | 16 | # Distributed under the terms of the BSD License. The full license is in |
|
17 | 17 | # the file COPYING, distributed as part of this software. |
@@ -28,6 +28,7 b' from IPython.core.application import (' | |||
|
28 | 28 | BaseIPythonApplication, base_flags |
|
29 | 29 | ) |
|
30 | 30 | from IPython.core.profiledir import ProfileDir |
|
31 | from IPython.utils.importstring import import_item | |
|
31 | 32 | from IPython.utils.path import get_ipython_dir, get_ipython_package_dir |
|
32 | 33 | from IPython.utils.traitlets import Unicode, Bool, Dict |
|
33 | 34 | |
@@ -206,6 +207,8 b' class ProfileCreate(BaseIPythonApplication):' | |||
|
206 | 207 | description = create_help |
|
207 | 208 | examples = _create_examples |
|
208 | 209 | auto_create = Bool(True, config=False) |
|
210 | def _log_format_default(self): | |
|
211 | return "[%(name)s] %(message)s" | |
|
209 | 212 | |
|
210 | 213 | def _copy_config_files_default(self): |
|
211 | 214 | return True |
@@ -234,31 +237,32 b' class ProfileCreate(BaseIPythonApplication):' | |||
|
234 | 237 | flags = Dict(create_flags) |
|
235 | 238 | |
|
236 | 239 | classes = [ProfileDir] |
|
240 | ||
|
241 | def _import_app(self, app_path): | |
|
242 | """import an app class""" | |
|
243 | app = None | |
|
244 | name = app_path.rsplit('.', 1)[-1] | |
|
245 | try: | |
|
246 | app = import_item(app_path) | |
|
247 | except ImportError as e: | |
|
248 | self.log.info("Couldn't import %s, config file will be excluded", name) | |
|
249 | except Exception: | |
|
250 | self.log.warn('Unexpected error importing %s', name, exc_info=True) | |
|
251 | return app | |
|
237 | 252 | |
|
238 | 253 | def init_config_files(self): |
|
239 | 254 | super(ProfileCreate, self).init_config_files() |
|
240 | 255 | # use local imports, since these classes may import from here |
|
241 | 256 | from IPython.terminal.ipapp import TerminalIPythonApp |
|
242 | 257 | apps = [TerminalIPythonApp] |
|
243 | try: | |
|
244 |
|
|
|
245 | except Exception: | |
|
246 | # this should be ImportError, but under weird circumstances | |
|
247 | # this might be an AttributeError, or possibly others | |
|
248 | # in any case, nothing should cause the profile creation to crash. | |
|
249 | pass | |
|
250 | else: | |
|
251 | apps.append(IPythonQtConsoleApp) | |
|
252 | try: | |
|
253 | from IPython.html.notebookapp import NotebookApp | |
|
254 | except ImportError: | |
|
255 | pass | |
|
256 | except Exception: | |
|
257 | self.log.debug('Unexpected error when importing NotebookApp', | |
|
258 | exc_info=True | |
|
259 | ) | |
|
260 | else: | |
|
261 | apps.append(NotebookApp) | |
|
258 | for app_path in ( | |
|
259 | 'IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp', | |
|
260 | 'IPython.html.notebookapp.NotebookApp', | |
|
261 | 'IPython.nbconvert.nbconvertapp.NbConvertApp', | |
|
262 | ): | |
|
263 | app = self._import_app(app_path) | |
|
264 | if app is not None: | |
|
265 | apps.append(app) | |
|
262 | 266 | if self.parallel: |
|
263 | 267 | from IPython.parallel.apps.ipcontrollerapp import IPControllerApp |
|
264 | 268 | from IPython.parallel.apps.ipengineapp import IPEngineApp |
@@ -214,7 +214,11 b' def find_gui_and_backend(gui=None, gui_select=None):' | |||
|
214 | 214 | # select backend based on requested gui |
|
215 | 215 | backend = backends[gui] |
|
216 | 216 | else: |
|
217 | backend = matplotlib.rcParams['backend'] | |
|
217 | # We need to read the backend from the original data structure, *not* | |
|
218 | # from mpl.rcParams, since a prior invocation of %matplotlib may have | |
|
219 | # overwritten that. | |
|
220 | # WARNING: this assumes matplotlib 1.1 or newer!! | |
|
221 | backend = matplotlib.rcParamsOrig['backend'] | |
|
218 | 222 | # In this case, we need to find what the appropriate gui selection call |
|
219 | 223 | # should be for IPython, so we can activate inputhook accordingly |
|
220 | 224 | gui = backend2gui.get(backend, None) |
@@ -26,7 +26,7 b" _version_extra = 'dev'" | |||
|
26 | 26 | # _version_extra = 'rc1' |
|
27 | 27 | # _version_extra = '' # Uncomment this for full releases |
|
28 | 28 | |
|
29 | codename = 'An Afternoon Hack' | |
|
29 | codename = 'Work in Progress' | |
|
30 | 30 | |
|
31 | 31 | # Construct full version string from these. |
|
32 | 32 | _ver = [_version_major, _version_minor, _version_patch] |
@@ -141,11 +141,8 b' classifiers = [' | |||
|
141 | 141 | 'License :: OSI Approved :: BSD License', |
|
142 | 142 | 'Programming Language :: Python', |
|
143 | 143 | 'Programming Language :: Python :: 2', |
|
144 | 'Programming Language :: Python :: 2.6', | |
|
145 | 144 | 'Programming Language :: Python :: 2.7', |
|
146 | 145 | 'Programming Language :: Python :: 3', |
|
147 | 'Programming Language :: Python :: 3.2', | |
|
148 | 'Programming Language :: Python :: 3.3', | |
|
149 | 146 | 'Topic :: System :: Distributed Computing', |
|
150 | 147 | 'Topic :: System :: Shells' |
|
151 | 148 | ] |
@@ -36,11 +36,14 b' from IPython.utils.path import filefind' | |||
|
36 | 36 | from IPython.utils.traitlets import ( |
|
37 | 37 | Unicode, Instance, List, Bool, CaselessStrEnum, Dict |
|
38 | 38 | ) |
|
39 | from IPython.lib.inputhook import guis | |
|
39 | 40 | |
|
40 | 41 | #----------------------------------------------------------------------------- |
|
41 | 42 | # Aliases and Flags |
|
42 | 43 | #----------------------------------------------------------------------------- |
|
43 | 44 | |
|
45 | gui_keys = tuple(sorted([ key for key in guis if key is not None ])) | |
|
46 | ||
|
44 | 47 | backend_keys = sorted(pylabtools.backends.keys()) |
|
45 | 48 | backend_keys.insert(0, 'auto') |
|
46 | 49 | |
@@ -175,8 +178,8 b' class InteractiveShellApp(Configurable):' | |||
|
175 | 178 | module_to_run = Unicode('', config=True, |
|
176 | 179 | help="Run the module as a script." |
|
177 | 180 | ) |
|
178 |
gui = CaselessStrEnum( |
|
|
179 |
help="Enable GUI event loop integration |
|
|
181 | gui = CaselessStrEnum(gui_keys, config=True, | |
|
182 | help="Enable GUI event loop integration with any of {0}.".format(gui_keys) | |
|
180 | 183 | ) |
|
181 | 184 | matplotlib = CaselessStrEnum(backend_keys, |
|
182 | 185 | config=True, |
@@ -198,7 +201,7 b' class InteractiveShellApp(Configurable):' | |||
|
198 | 201 | ) |
|
199 | 202 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') |
|
200 | 203 | |
|
201 | user_ns = Dict(default_value=None) | |
|
204 | user_ns = Instance(dict, args=None, allow_none=True) | |
|
202 | 205 | def _user_ns_changed(self, name, old, new): |
|
203 | 206 | if self.shell is not None: |
|
204 | 207 | self.shell.user_ns = new |
@@ -117,8 +117,7 b' def test_ipdb_magics():' | |||
|
117 | 117 | ipdb> pinfo a |
|
118 | 118 | Type: ExampleClass |
|
119 | 119 | String Form:ExampleClass() |
|
120 |
Namespace: Local |
|
|
121 | File: ... | |
|
120 | Namespace: Local... | |
|
122 | 121 | Docstring: Docstring for ExampleClass. |
|
123 | 122 | Constructor Docstring:Docstring for ExampleClass.__init__ |
|
124 | 123 | ipdb> continue |
@@ -45,28 +45,6 b' def run(tests):' | |||
|
45 | 45 | |
|
46 | 46 | |
|
47 | 47 | def test_handlers(): |
|
48 | # alias expansion | |
|
49 | ||
|
50 | # We're using 'true' as our syscall of choice because it doesn't | |
|
51 | # write anything to stdout. | |
|
52 | ||
|
53 | # Turn off actual execution of aliases, because it's noisy | |
|
54 | old_system_cmd = ip.system | |
|
55 | ip.system = lambda cmd: None | |
|
56 | ||
|
57 | ||
|
58 | ip.alias_manager.alias_table['an_alias'] = (0, 'true') | |
|
59 | # These are useful for checking a particular recursive alias issue | |
|
60 | ip.alias_manager.alias_table['top'] = (0, 'd:/cygwin/top') | |
|
61 | ip.alias_manager.alias_table['d'] = (0, 'true') | |
|
62 | run([(i,py3compat.u_format(o)) for i,o in \ | |
|
63 | [("an_alias", "get_ipython().system({u}'true ')"), # alias | |
|
64 | # Below: recursive aliases should expand whitespace-surrounded | |
|
65 | # chars, *not* initial chars which happen to be aliases: | |
|
66 | ("top", "get_ipython().system({u}'d:/cygwin/top ')"), | |
|
67 | ]]) | |
|
68 | ip.system = old_system_cmd | |
|
69 | ||
|
70 | 48 | call_idx = CallableIndexable() |
|
71 | 49 | ip.user_ns['call_idx'] = call_idx |
|
72 | 50 |
@@ -9,9 +9,6 b' def test_import_crashhandler():' | |||
|
9 | 9 | def test_import_debugger(): |
|
10 | 10 | from IPython.core import debugger |
|
11 | 11 | |
|
12 | def test_import_fakemodule(): | |
|
13 | from IPython.core import fakemodule | |
|
14 | ||
|
15 | 12 | def test_import_excolors(): |
|
16 | 13 | from IPython.core import excolors |
|
17 | 14 |
@@ -183,9 +183,18 b' syntax_ml = \\' | |||
|
183 | 183 | ('... 456"""','456"""'), |
|
184 | 184 | ], |
|
185 | 185 | [('a="""','a="""'), |
|
186 | ('>>> 123','123'), | |
|
187 | ('... 456"""','456"""'), | |
|
188 | ], | |
|
189 | [('a="""','a="""'), | |
|
186 | 190 | ('123','123'), |
|
187 | 191 | ('... 456"""','... 456"""'), |
|
188 | 192 | ], |
|
193 | [('....__class__','....__class__'), | |
|
194 | ], | |
|
195 | [('a=5', 'a=5'), | |
|
196 | ('...', ''), | |
|
197 | ], | |
|
189 | 198 | [('>>> def f(x):', 'def f(x):'), |
|
190 | 199 | ('...', ''), |
|
191 | 200 | ('... return x', ' return x'), |
@@ -205,6 +214,10 b' syntax_ml = \\' | |||
|
205 | 214 | (' ...: 456"""','456"""'), |
|
206 | 215 | ], |
|
207 | 216 | [('a="""','a="""'), |
|
217 | ('In [1]: 123','123'), | |
|
218 | (' ...: 456"""','456"""'), | |
|
219 | ], | |
|
220 | [('a="""','a="""'), | |
|
208 | 221 | ('123','123'), |
|
209 | 222 | (' ...: 456"""',' ...: 456"""'), |
|
210 | 223 | ], |
@@ -237,6 +250,12 b' syntax_ml = \\' | |||
|
237 | 250 | ], |
|
238 | 251 | ], |
|
239 | 252 | |
|
253 | multiline_string = | |
|
254 | [ [("'''foo?", None), | |
|
255 | ("bar'''", "'''foo?\nbar'''"), | |
|
256 | ], | |
|
257 | ], | |
|
258 | ||
|
240 | 259 | leading_indent = |
|
241 | 260 | [ [(' print "hi"','print "hi"'), |
|
242 | 261 | ], |
@@ -106,17 +106,6 b' class InteractiveShellTestCase(unittest.TestCase):' | |||
|
106 | 106 | ip.run_cell('a = """\n%exit\n"""') |
|
107 | 107 | self.assertEqual(ip.user_ns['a'], '\n%exit\n') |
|
108 | 108 | |
|
109 | def test_alias_crash(self): | |
|
110 | """Errors in prefilter can't crash IPython""" | |
|
111 | ip.run_cell('%alias parts echo first %s second %s') | |
|
112 | # capture stderr: | |
|
113 | save_err = io.stderr | |
|
114 | io.stderr = StringIO() | |
|
115 | ip.run_cell('parts 1') | |
|
116 | err = io.stderr.getvalue() | |
|
117 | io.stderr = save_err | |
|
118 | self.assertEqual(err.split(':')[0], 'ERROR') | |
|
119 | ||
|
120 | 109 | def test_trailing_newline(self): |
|
121 | 110 | """test that running !(command) does not raise a SyntaxError""" |
|
122 | 111 | ip.run_cell('!(true)\n', False) |
@@ -48,16 +48,16 b' class DummyMagics(magic.Magics): pass' | |||
|
48 | 48 | def test_rehashx(): |
|
49 | 49 | # clear up everything |
|
50 | 50 | _ip = get_ipython() |
|
51 |
_ip.alias_manager. |
|
|
51 | _ip.alias_manager.clear_aliases() | |
|
52 | 52 | del _ip.db['syscmdlist'] |
|
53 | 53 | |
|
54 | 54 | _ip.magic('rehashx') |
|
55 | 55 | # Practically ALL ipython development systems will have more than 10 aliases |
|
56 | 56 | |
|
57 |
nt.assert_true(len(_ip.alias_manager.alias |
|
|
58 |
for |
|
|
57 | nt.assert_true(len(_ip.alias_manager.aliases) > 10) | |
|
58 | for name, cmd in _ip.alias_manager.aliases: | |
|
59 | 59 | # we must strip dots from alias names |
|
60 |
nt.assert_not_in('.', |
|
|
60 | nt.assert_not_in('.', name) | |
|
61 | 61 | |
|
62 | 62 | # rehashx must fill up syscmdlist |
|
63 | 63 | scoms = _ip.db['syscmdlist'] |
@@ -487,7 +487,21 b' def test_timeit_special_syntax():' | |||
|
487 | 487 | # cell mode test |
|
488 | 488 | _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2') |
|
489 | 489 | nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2') |
|
490 | ||
|
490 | ||
|
491 | def test_timeit_return(): | |
|
492 | """ | |
|
493 | test wether timeit -o return object | |
|
494 | """ | |
|
495 | ||
|
496 | res = _ip.run_line_magic('timeit','-n10 -r10 -o 1') | |
|
497 | assert(res is not None) | |
|
498 | ||
|
499 | def test_timeit_quiet(): | |
|
500 | """ | |
|
501 | test quiet option of timeit magic | |
|
502 | """ | |
|
503 | with tt.AssertNotPrints("loops"): | |
|
504 | _ip.run_cell("%timeit -n1 -r1 -q 1") | |
|
491 | 505 | |
|
492 | 506 | @dec.skipif(execution.profile is None) |
|
493 | 507 | def test_prun_special_syntax(): |
@@ -6,12 +6,11 b'' | |||
|
6 | 6 | # The full license is in the file COPYING.txt, distributed with this software. |
|
7 | 7 | #----------------------------------------------------------------------------- |
|
8 | 8 | |
|
9 | from nose.tools import assert_equal, assert_true | |
|
9 | import argparse | |
|
10 | from nose.tools import assert_equal | |
|
10 | 11 | |
|
11 | from IPython.external import argparse | |
|
12 | 12 | from IPython.core.magic_arguments import (argument, argument_group, kwds, |
|
13 | 13 | magic_arguments, parse_argstring, real_name) |
|
14 | from IPython.testing.decorators import parametric | |
|
15 | 14 | |
|
16 | 15 | |
|
17 | 16 | @magic_arguments() |
@@ -74,48 +73,46 b' def foo(self, args):' | |||
|
74 | 73 | return parse_argstring(foo, args) |
|
75 | 74 | |
|
76 | 75 | |
|
77 | @parametric | |
|
78 | 76 | def test_magic_arguments(): |
|
79 | # Ideally, these would be doctests, but I could not get it to work. | |
|
80 | yield assert_equal(magic_foo1.__doc__, '%foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') | |
|
81 | yield assert_equal(getattr(magic_foo1, 'argcmd_name', None), None) | |
|
82 | yield assert_equal(real_name(magic_foo1), 'foo1') | |
|
83 | yield assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None)) | |
|
84 | yield assert_true(hasattr(magic_foo1, 'has_arguments')) | |
|
85 | ||
|
86 | yield assert_equal(magic_foo2.__doc__, '%foo2\n\n A docstring.\n') | |
|
87 | yield assert_equal(getattr(magic_foo2, 'argcmd_name', None), None) | |
|
88 | yield assert_equal(real_name(magic_foo2), 'foo2') | |
|
89 | yield assert_equal(magic_foo2(None, ''), argparse.Namespace()) | |
|
90 | yield assert_true(hasattr(magic_foo2, 'has_arguments')) | |
|
91 | ||
|
92 | yield assert_equal(magic_foo3.__doc__, '%foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n') | |
|
93 | yield assert_equal(getattr(magic_foo3, 'argcmd_name', None), None) | |
|
94 |
|
|
|
95 | yield assert_equal(magic_foo3(None, ''), | |
|
77 | assert_equal(magic_foo1.__doc__, '%foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') | |
|
78 | assert_equal(getattr(magic_foo1, 'argcmd_name', None), None) | |
|
79 | assert_equal(real_name(magic_foo1), 'foo1') | |
|
80 | assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None)) | |
|
81 | assert hasattr(magic_foo1, 'has_arguments') | |
|
82 | ||
|
83 | assert_equal(magic_foo2.__doc__, '%foo2\n\n A docstring.\n') | |
|
84 | assert_equal(getattr(magic_foo2, 'argcmd_name', None), None) | |
|
85 | assert_equal(real_name(magic_foo2), 'foo2') | |
|
86 | assert_equal(magic_foo2(None, ''), argparse.Namespace()) | |
|
87 | assert hasattr(magic_foo2, 'has_arguments') | |
|
88 | ||
|
89 | assert_equal(magic_foo3.__doc__, '%foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n') | |
|
90 | assert_equal(getattr(magic_foo3, 'argcmd_name', None), None) | |
|
91 | assert_equal(real_name(magic_foo3), 'foo3') | |
|
92 | assert_equal(magic_foo3(None, ''), | |
|
96 | 93 | argparse.Namespace(bar=None, baz=None, foo=None)) |
|
97 |
|
|
|
98 | ||
|
99 |
|
|
|
100 |
|
|
|
101 |
|
|
|
102 |
|
|
|
103 |
|
|
|
104 | ||
|
105 |
|
|
|
106 |
|
|
|
107 |
|
|
|
108 |
|
|
|
109 |
|
|
|
110 | ||
|
111 |
|
|
|
112 |
|
|
|
113 |
|
|
|
114 |
|
|
|
115 |
|
|
|
116 | ||
|
117 |
|
|
|
118 |
|
|
|
119 |
|
|
|
120 |
|
|
|
121 |
|
|
|
94 | assert hasattr(magic_foo3, 'has_arguments') | |
|
95 | ||
|
96 | assert_equal(magic_foo4.__doc__, '%foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') | |
|
97 | assert_equal(getattr(magic_foo4, 'argcmd_name', None), None) | |
|
98 | assert_equal(real_name(magic_foo4), 'foo4') | |
|
99 | assert_equal(magic_foo4(None, ''), argparse.Namespace()) | |
|
100 | assert hasattr(magic_foo4, 'has_arguments') | |
|
101 | ||
|
102 | assert_equal(magic_foo5.__doc__, '%frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') | |
|
103 | assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate') | |
|
104 | assert_equal(real_name(magic_foo5), 'frobnicate') | |
|
105 | assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None)) | |
|
106 | assert hasattr(magic_foo5, 'has_arguments') | |
|
107 | ||
|
108 | assert_equal(magic_magic_foo.__doc__, '%magic_foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') | |
|
109 | assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None) | |
|
110 | assert_equal(real_name(magic_magic_foo), 'magic_foo') | |
|
111 | assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None)) | |
|
112 | assert hasattr(magic_magic_foo, 'has_arguments') | |
|
113 | ||
|
114 | assert_equal(foo.__doc__, '%foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') | |
|
115 | assert_equal(getattr(foo, 'argcmd_name', None), None) | |
|
116 | assert_equal(real_name(foo), 'foo') | |
|
117 | assert_equal(foo(None, ''), argparse.Namespace(foo=None)) | |
|
118 | assert hasattr(foo, 'has_arguments') |
@@ -6,7 +6,6 b'' | |||
|
6 | 6 | import nose.tools as nt |
|
7 | 7 | |
|
8 | 8 | from IPython.core.prefilter import AutocallChecker |
|
9 | from IPython.testing import decorators as dec | |
|
10 | 9 | from IPython.testing.globalipapp import get_ipython |
|
11 | 10 | |
|
12 | 11 | #----------------------------------------------------------------------------- |
@@ -14,7 +13,6 b' from IPython.testing.globalipapp import get_ipython' | |||
|
14 | 13 | #----------------------------------------------------------------------------- |
|
15 | 14 | ip = get_ipython() |
|
16 | 15 | |
|
17 | @dec.parametric | |
|
18 | 16 | def test_prefilter(): |
|
19 | 17 | """Test user input conversions""" |
|
20 | 18 | |
@@ -23,19 +21,18 b' def test_prefilter():' | |||
|
23 | 21 | ] |
|
24 | 22 | |
|
25 | 23 | for raw, correct in pairs: |
|
26 |
|
|
|
24 | nt.assert_equal(ip.prefilter(raw), correct) | |
|
27 | 25 | |
|
28 | 26 | |
|
29 | @dec.parametric | |
|
30 | 27 | def test_autocall_binops(): |
|
31 | 28 | """See https://github.com/ipython/ipython/issues/81""" |
|
32 | 29 | ip.magic('autocall 2') |
|
33 | 30 | f = lambda x: x |
|
34 | 31 | ip.user_ns['f'] = f |
|
35 | 32 | try: |
|
36 |
|
|
|
33 | nt.assert_equal(ip.prefilter('f 1'),'f(1)') | |
|
37 | 34 | for t in ['f +1', 'f -1']: |
|
38 |
|
|
|
35 | nt.assert_equal(ip.prefilter(t), t) | |
|
39 | 36 | |
|
40 | 37 | # Run tests again with a more permissive exclude_regexp, which will |
|
41 | 38 | # allow transformation of binary operations ('f -1' -> 'f(-1)'). |
@@ -47,8 +44,8 b' def test_autocall_binops():' | |||
|
47 | 44 | ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or ' |
|
48 | 45 | pm.sort_checkers() |
|
49 | 46 | |
|
50 |
|
|
|
51 |
|
|
|
47 | nt.assert_equal(ip.prefilter('f -1'), 'f(-1)') | |
|
48 | nt.assert_equal(ip.prefilter('f +1'), 'f(+1)') | |
|
52 | 49 | finally: |
|
53 | 50 | pm.unregister_checker(ac) |
|
54 | 51 | finally: |
@@ -56,7 +53,6 b' def test_autocall_binops():' | |||
|
56 | 53 | del ip.user_ns['f'] |
|
57 | 54 | |
|
58 | 55 | |
|
59 | @dec.parametric | |
|
60 | 56 | def test_issue_114(): |
|
61 | 57 | """Check that multiline string literals don't expand as magic |
|
62 | 58 | see http://github.com/ipython/ipython/issues/114""" |
@@ -70,7 +66,7 b' def test_issue_114():' | |||
|
70 | 66 | try: |
|
71 | 67 | for mgk in ip.magics_manager.lsmagic()['line']: |
|
72 | 68 | raw = template % mgk |
|
73 |
|
|
|
69 | nt.assert_equal(ip.prefilter(raw), raw) | |
|
74 | 70 | finally: |
|
75 | 71 | ip.prefilter_manager.multi_line_specials = msp |
|
76 | 72 |
@@ -24,7 +24,6 b' import numpy as np' | |||
|
24 | 24 | |
|
25 | 25 | # Our own imports |
|
26 | 26 | from IPython.core.interactiveshell import InteractiveShell |
|
27 | from IPython.testing import decorators as dec | |
|
28 | 27 | from .. import pylabtools as pt |
|
29 | 28 | |
|
30 | 29 | #----------------------------------------------------------------------------- |
@@ -39,11 +38,10 b' from .. import pylabtools as pt' | |||
|
39 | 38 | # Classes and functions |
|
40 | 39 | #----------------------------------------------------------------------------- |
|
41 | 40 | |
|
42 | @dec.parametric | |
|
43 | 41 | def test_figure_to_svg(): |
|
44 | 42 | # simple empty-figure test |
|
45 | 43 | fig = plt.figure() |
|
46 |
|
|
|
44 | nt.assert_equal(pt.print_figure(fig, 'svg'), None) | |
|
47 | 45 | |
|
48 | 46 | plt.close('all') |
|
49 | 47 | |
@@ -53,7 +51,7 b' def test_figure_to_svg():' | |||
|
53 | 51 | ax.plot([1,2,3]) |
|
54 | 52 | plt.draw() |
|
55 | 53 | svg = pt.print_figure(fig, 'svg')[:100].lower() |
|
56 |
|
|
|
54 | nt.assert_in(b'doctype svg', svg) | |
|
57 | 55 | |
|
58 | 56 | |
|
59 | 57 | def test_import_pylab(): |
@@ -15,6 +15,7 b' from __future__ import absolute_import' | |||
|
15 | 15 | |
|
16 | 16 | import functools |
|
17 | 17 | import os |
|
18 | from os.path import join as pjoin | |
|
18 | 19 | import random |
|
19 | 20 | import sys |
|
20 | 21 | import tempfile |
@@ -359,6 +360,17 b' tclass.py: deleting object: C-third' | |||
|
359 | 360 | self.mktmp(src) |
|
360 | 361 | _ip.magic('run -t -N 1 %s' % self.fname) |
|
361 | 362 | _ip.magic('run -t -N 10 %s' % self.fname) |
|
363 | ||
|
364 | def test_ignore_sys_exit(self): | |
|
365 | """Test the -e option to ignore sys.exit()""" | |
|
366 | src = "import sys; sys.exit(1)" | |
|
367 | self.mktmp(src) | |
|
368 | with tt.AssertPrints('SystemExit'): | |
|
369 | _ip.magic('run %s' % self.fname) | |
|
370 | ||
|
371 | with tt.AssertNotPrints('SystemExit'): | |
|
372 | _ip.magic('run -e %s' % self.fname) | |
|
373 | ||
|
362 | 374 | |
|
363 | 375 | |
|
364 | 376 | class TestMagicRunWithPackage(unittest.TestCase): |
@@ -398,6 +410,7 b' class TestMagicRunWithPackage(unittest.TestCase):' | |||
|
398 | 410 | self.tempdir.cleanup() |
|
399 | 411 | |
|
400 | 412 | def check_run_submodule(self, submodule, opts=''): |
|
413 | _ip.user_ns.pop('x', None) | |
|
401 | 414 | _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts)) |
|
402 | 415 | self.assertEqual(_ip.user_ns['x'], self.value, |
|
403 | 416 | 'Variable `x` is not loaded from module `{0}`.' |
@@ -430,3 +443,16 b' class TestMagicRunWithPackage(unittest.TestCase):' | |||
|
430 | 443 | @with_fake_debugger |
|
431 | 444 | def test_debug_run_submodule_with_relative_import(self): |
|
432 | 445 | self.check_run_submodule('relative', '-d') |
|
446 | ||
|
447 | def test_run__name__(): | |
|
448 | with TemporaryDirectory() as td: | |
|
449 | path = pjoin(td, 'foo.py') | |
|
450 | with open(path, 'w') as f: | |
|
451 | f.write("q = __name__") | |
|
452 | ||
|
453 | _ip.user_ns.pop('q', None) | |
|
454 | _ip.magic('run {}'.format(path)) | |
|
455 | nt.assert_equal(_ip.user_ns.pop('q'), '__main__') | |
|
456 | ||
|
457 | _ip.magic('run -n {}'.format(path)) | |
|
458 | nt.assert_equal(_ip.user_ns.pop('q'), 'foo') |
@@ -108,8 +108,33 b' class IndentationErrorTest(unittest.TestCase):' | |||
|
108 | 108 | with tt.AssertPrints("zoon()", suppress=False): |
|
109 | 109 | ip.magic('run %s' % fname) |
|
110 | 110 | |
|
111 | se_file_1 = """1 | |
|
112 | 2 | |
|
113 | 7/ | |
|
114 | """ | |
|
115 | ||
|
116 | se_file_2 = """7/ | |
|
117 | """ | |
|
118 | ||
|
111 | 119 | class SyntaxErrorTest(unittest.TestCase): |
|
112 | 120 | def test_syntaxerror_without_lineno(self): |
|
113 | 121 | with tt.AssertNotPrints("TypeError"): |
|
114 | 122 | with tt.AssertPrints("line unknown"): |
|
115 | 123 | ip.run_cell("raise SyntaxError()") |
|
124 | ||
|
125 | def test_changing_py_file(self): | |
|
126 | with TemporaryDirectory() as td: | |
|
127 | fname = os.path.join(td, "foo.py") | |
|
128 | with open(fname, 'w') as f: | |
|
129 | f.write(se_file_1) | |
|
130 | ||
|
131 | with tt.AssertPrints(["7/", "SyntaxError"]): | |
|
132 | ip.magic("run " + fname) | |
|
133 | ||
|
134 | # Modify the file | |
|
135 | with open(fname, 'w') as f: | |
|
136 | f.write(se_file_2) | |
|
137 | ||
|
138 | # The SyntaxError should point to the correct line | |
|
139 | with tt.AssertPrints(["7/", "SyntaxError"]): | |
|
140 | ip.magic("run " + fname) |
@@ -8,7 +8,8 b' ColorTB class is a solution to that problem. It colors the different parts of a' | |||
|
8 | 8 | traceback in a manner similar to what you would expect from a syntax-highlighting |
|
9 | 9 | text editor. |
|
10 | 10 | |
|
11 | Installation instructions for ColorTB: | |
|
11 | Installation instructions for ColorTB:: | |
|
12 | ||
|
12 | 13 | import sys,ultratb |
|
13 | 14 | sys.excepthook = ultratb.ColorTB() |
|
14 | 15 | |
@@ -21,7 +22,7 b' but kind of neat, and maybe useful for long-running programs that you believe' | |||
|
21 | 22 | are bug-free. If a crash *does* occur in that type of program you want details. |
|
22 | 23 | Give it a shot--you'll love it or you'll hate it. |
|
23 | 24 | |
|
24 | Note: | |
|
25 | .. note:: | |
|
25 | 26 | |
|
26 | 27 | The Verbose mode prints the variables currently visible where the exception |
|
27 | 28 | happened (shortening their strings if too long). This can potentially be |
@@ -36,25 +37,28 b' Note:' | |||
|
36 | 37 | Verbose). |
|
37 | 38 | |
|
38 | 39 | |
|
39 | Installation instructions for ColorTB: | |
|
40 | Installation instructions for ColorTB:: | |
|
41 | ||
|
40 | 42 | import sys,ultratb |
|
41 | 43 | sys.excepthook = ultratb.VerboseTB() |
|
42 | 44 | |
|
43 | 45 | Note: Much of the code in this module was lifted verbatim from the standard |
|
44 | 46 | library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'. |
|
45 | 47 | |
|
46 |
|
|
|
48 | Color schemes | |
|
49 | ------------- | |
|
50 | ||
|
47 | 51 | The colors are defined in the class TBTools through the use of the |
|
48 | 52 | ColorSchemeTable class. Currently the following exist: |
|
49 | 53 | |
|
50 | 54 | - NoColor: allows all of this module to be used in any terminal (the color |
|
51 | escapes are just dummy blank strings). | |
|
55 | escapes are just dummy blank strings). | |
|
52 | 56 | |
|
53 | 57 | - Linux: is meant to look good in a terminal like the Linux console (black |
|
54 | or very dark background). | |
|
58 | or very dark background). | |
|
55 | 59 | |
|
56 | 60 | - LightBG: similar to Linux but swaps dark/light colors to be more readable |
|
57 | in light background terminals. | |
|
61 | in light background terminals. | |
|
58 | 62 | |
|
59 | 63 | You can implement other color schemes easily, the syntax is fairly |
|
60 | 64 | self-explanatory. Please send back new schemes you develop to the author for |
@@ -359,8 +363,8 b' class TBTools(object):' | |||
|
359 | 363 | Valid values are: |
|
360 | 364 | |
|
361 | 365 | - None: the default, which means that IPython will dynamically resolve |
|
362 | to io.stdout. This ensures compatibility with most tools, including | |
|
363 | Windows (where plain stdout doesn't recognize ANSI escapes). | |
|
366 | to io.stdout. This ensures compatibility with most tools, including | |
|
367 | Windows (where plain stdout doesn't recognize ANSI escapes). | |
|
364 | 368 | |
|
365 | 369 | - Any object with 'write' and 'flush' attributes. |
|
366 | 370 | """ |
@@ -974,9 +978,9 b' class VerboseTB(TBTools):' | |||
|
974 | 978 | Keywords: |
|
975 | 979 | |
|
976 | 980 | - force(False): by default, this routine checks the instance call_pdb |
|
977 | flag and does not actually invoke the debugger if the flag is false. | |
|
978 | The 'force' option forces the debugger to activate even if the flag | |
|
979 | is false. | |
|
981 | flag and does not actually invoke the debugger if the flag is false. | |
|
982 | The 'force' option forces the debugger to activate even if the flag | |
|
983 | is false. | |
|
980 | 984 | |
|
981 | 985 | If the call_pdb flag is set, the pdb interactive debugger is |
|
982 | 986 | invoked. In all cases, the self.tb reference to the current traceback |
@@ -1193,6 +1197,20 b' class SyntaxTB(ListTB):' | |||
|
1193 | 1197 | self.last_syntax_error = value |
|
1194 | 1198 | ListTB.__call__(self,etype,value,elist) |
|
1195 | 1199 | |
|
1200 | def structured_traceback(self, etype, value, elist, tb_offset=None, | |
|
1201 | context=5): | |
|
1202 | # If the source file has been edited, the line in the syntax error can | |
|
1203 | # be wrong (retrieved from an outdated cache). This replaces it with | |
|
1204 | # the current value. | |
|
1205 | if isinstance(value.filename, py3compat.string_types) \ | |
|
1206 | and isinstance(value.lineno, int): | |
|
1207 | linecache.checkcache(value.filename) | |
|
1208 | newtext = ulinecache.getline(value.filename, value.lineno) | |
|
1209 | if newtext: | |
|
1210 | value.text = newtext | |
|
1211 | return super(SyntaxTB, self).structured_traceback(etype, value, elist, | |
|
1212 | tb_offset=tb_offset, context=context) | |
|
1213 | ||
|
1196 | 1214 | def clear_err_state(self): |
|
1197 | 1215 | """Return the current error state and clear it""" |
|
1198 | 1216 | e = self.last_syntax_error |
@@ -4,9 +4,18 b'' | |||
|
4 | 4 | Cython related magics |
|
5 | 5 | ===================== |
|
6 | 6 | |
|
7 | Magic command interface for interactive work with Cython | |
|
8 | ||
|
9 | .. note:: | |
|
10 | ||
|
11 | The ``Cython`` package needs to be installed separately. It | |
|
12 | can be obtained using ``easy_install`` or ``pip``. | |
|
13 | ||
|
7 | 14 | Usage |
|
8 | 15 | ===== |
|
9 | 16 | |
|
17 | To enable the magics below, execute ``%load_ext cythonmagic``. | |
|
18 | ||
|
10 | 19 | ``%%cython`` |
|
11 | 20 | |
|
12 | 21 | {CYTHON_DOC} |
@@ -11,9 +11,13 b' Magics for interacting with Octave via oct2py.' | |||
|
11 | 11 | The ``oct2py`` module needs to be installed separately and |
|
12 | 12 | can be obtained using ``easy_install`` or ``pip``. |
|
13 | 13 | |
|
14 | You will also need a working copy of GNU Octave. | |
|
15 | ||
|
14 | 16 | Usage |
|
15 | 17 | ===== |
|
16 | 18 | |
|
19 | To enable the magics below, execute ``%load_ext octavemagic``. | |
|
20 | ||
|
17 | 21 | ``%octave`` |
|
18 | 22 | |
|
19 | 23 | {OCTAVE_DOC} |
@@ -6,9 +6,18 b' Rmagic' | |||
|
6 | 6 | |
|
7 | 7 | Magic command interface for interactive work with R via rpy2 |
|
8 | 8 | |
|
9 | .. note:: | |
|
10 | ||
|
11 | The ``rpy2`` package needs to be installed separately. It | |
|
12 | can be obtained using ``easy_install`` or ``pip``. | |
|
13 | ||
|
14 | You will also need a working copy of R. | |
|
15 | ||
|
9 | 16 | Usage |
|
10 | 17 | ===== |
|
11 | 18 | |
|
19 | To enable the magics below, execute ``%load_ext rmagic``. | |
|
20 | ||
|
12 | 21 | ``%R`` |
|
13 | 22 | |
|
14 | 23 | {R_DOC} |
@@ -425,12 +434,12 b' class RMagics(Magics):' | |||
|
425 | 434 | is printed if it would printed be when evaluating the same code |
|
426 | 435 | within a standard R REPL. |
|
427 | 436 | |
|
428 |
Nothing is returned to python by default in cell mode |
|
|
437 | Nothing is returned to python by default in cell mode:: | |
|
429 | 438 | |
|
430 | 439 | In [10]: %%R |
|
431 | 440 | ....: Y = c(2,4,3,9) |
|
432 | 441 | ....: summary(lm(Y~X)) |
|
433 | ||
|
442 | ||
|
434 | 443 | Call: |
|
435 | 444 | lm(formula = Y ~ X) |
|
436 | 445 | |
@@ -447,9 +456,9 b' class RMagics(Magics):' | |||
|
447 | 456 | Multiple R-squared: 0.6993,Adjusted R-squared: 0.549 |
|
448 | 457 | F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638 |
|
449 | 458 | |
|
450 |
In the notebook, plots are published as the output of the cell |
|
|
459 | In the notebook, plots are published as the output of the cell:: | |
|
451 | 460 | |
|
452 | %R plot(X, Y) | |
|
461 | %R plot(X, Y) | |
|
453 | 462 | |
|
454 | 463 | will create a scatter plot of X bs Y. |
|
455 | 464 | |
@@ -475,19 +484,19 b' class RMagics(Magics):' | |||
|
475 | 484 | * If the cell is not None, the magic returns None. |
|
476 | 485 | |
|
477 | 486 | * If the cell evaluates as False, the resulting value is returned |
|
478 | unless the final line prints something to the console, in | |
|
479 | which case None is returned. | |
|
487 | unless the final line prints something to the console, in | |
|
488 | which case None is returned. | |
|
480 | 489 | |
|
481 | 490 | * If the final line results in a NULL value when evaluated |
|
482 | by rpy2, then None is returned. | |
|
491 | by rpy2, then None is returned. | |
|
483 | 492 | |
|
484 | 493 | * No attempt is made to convert the final value to a structured array. |
|
485 | Use the --dataframe flag or %Rget to push / return a structured array. | |
|
494 | Use the --dataframe flag or %Rget to push / return a structured array. | |
|
486 | 495 | |
|
487 | 496 | * If the -n flag is present, there is no return value. |
|
488 | 497 | |
|
489 | 498 | * A trailing ';' will also result in no return value as the last |
|
490 | value in the line is an empty string. | |
|
499 | value in the line is an empty string. | |
|
491 | 500 | |
|
492 | 501 | The --dataframe argument will attempt to return structured arrays. |
|
493 | 502 | This is useful for dataframes with |
@@ -25,10 +25,11 b' To automatically restore stored variables at startup, add this to your' | |||
|
25 | 25 | import inspect, os, sys, textwrap |
|
26 | 26 | |
|
27 | 27 | # Our own |
|
28 | from IPython.config.configurable import Configurable | |
|
28 | 29 | from IPython.core.error import UsageError |
|
29 | from IPython.core.fakemodule import FakeModule | |
|
30 | 30 | from IPython.core.magic import Magics, magics_class, line_magic |
|
31 | 31 | from IPython.testing.skipdoctest import skip_doctest |
|
32 | from IPython.utils.traitlets import Bool | |
|
32 | 33 | |
|
33 | 34 | #----------------------------------------------------------------------------- |
|
34 | 35 | # Functions and classes |
@@ -68,10 +69,23 b' def restore_data(ip):' | |||
|
68 | 69 | |
|
69 | 70 | |
|
70 | 71 | @magics_class |
|
71 | class StoreMagics(Magics): | |
|
72 | class StoreMagics(Magics, Configurable): | |
|
72 | 73 | """Lightweight persistence for python variables. |
|
73 | 74 | |
|
74 | 75 | Provides the %store magic.""" |
|
76 | ||
|
77 | autorestore = Bool(False, config=True, help= | |
|
78 | """If True, any %store-d variables will be automatically restored | |
|
79 | when IPython starts. | |
|
80 | """ | |
|
81 | ) | |
|
82 | ||
|
83 | def __init__(self, shell): | |
|
84 | Configurable.__init__(self, config=shell.config) | |
|
85 | Magics.__init__(self, shell=shell) | |
|
86 | self.shell.configurables.append(self) | |
|
87 | if self.autorestore: | |
|
88 | restore_data(self.shell) | |
|
75 | 89 | |
|
76 | 90 | @skip_doctest |
|
77 | 91 | @line_magic |
@@ -196,20 +210,21 b' class StoreMagics(Magics):' | |||
|
196 | 210 | obj = ip.user_ns[args[0]] |
|
197 | 211 | except KeyError: |
|
198 | 212 | # it might be an alias |
|
199 | # This needs to be refactored to use the new AliasManager stuff. | |
|
200 | if args[0] in ip.alias_manager: | |
|
201 | name = args[0] | |
|
202 | nargs, cmd = ip.alias_manager.alias_table[ name ] | |
|
203 | staliases = db.get('stored_aliases',{}) | |
|
204 | staliases[ name ] = cmd | |
|
205 |
|
|
|
206 |
|
|
|
207 | return | |
|
208 | else: | |
|
209 | raise UsageError("Unknown variable '%s'" % args[0]) | |
|
213 | name = args[0] | |
|
214 | try: | |
|
215 | cmd = ip.alias_manager.retrieve_alias(name) | |
|
216 | except ValueError: | |
|
217 | raise UsageError("Unknown variable '%s'" % name) | |
|
218 | ||
|
219 | staliases = db.get('stored_aliases',{}) | |
|
220 | staliases[name] = cmd | |
|
221 | db['stored_aliases'] = staliases | |
|
222 | print "Alias stored: %s (%s)" % (name, cmd) | |
|
223 | return | |
|
210 | 224 | |
|
211 | 225 | else: |
|
212 |
|
|
|
226 | modname = getattr(inspect.getmodule(obj), '__name__', '') | |
|
227 | if modname == '__main__': | |
|
213 | 228 | print textwrap.dedent("""\ |
|
214 | 229 | Warning:%s is %s |
|
215 | 230 | Proper storage of interactively declared classes (or instances |
@@ -225,3 +240,4 b' class StoreMagics(Magics):' | |||
|
225 | 240 | def load_ipython_extension(ip): |
|
226 | 241 | """Load the extension in IPython.""" |
|
227 | 242 | ip.register_magics(StoreMagics) |
|
243 |
@@ -1,5 +1,6 b'' | |||
|
1 | 1 | import tempfile, os |
|
2 | 2 | |
|
3 | from IPython.config.loader import Config | |
|
3 | 4 | import nose.tools as nt |
|
4 | 5 | |
|
5 | 6 | ip = get_ipython() |
@@ -26,7 +27,24 b' def test_store_restore():' | |||
|
26 | 27 | # Check restoring |
|
27 | 28 | ip.magic('store -r') |
|
28 | 29 | nt.assert_equal(ip.user_ns['foo'], 78) |
|
29 |
|
|
|
30 | assert ip.alias_manager.is_alias('bar') | |
|
30 | 31 | nt.assert_in(os.path.realpath(tmpd), ip.user_ns['_dh']) |
|
31 | 32 | |
|
32 | 33 | os.rmdir(tmpd) |
|
34 | ||
|
35 | def test_autorestore(): | |
|
36 | ip.user_ns['foo'] = 95 | |
|
37 | ip.magic('store foo') | |
|
38 | del ip.user_ns['foo'] | |
|
39 | c = Config() | |
|
40 | c.StoreMagics.autorestore = False | |
|
41 | orig_config = ip.config | |
|
42 | try: | |
|
43 | ip.config = c | |
|
44 | ip.extension_manager.reload_extension('storemagic') | |
|
45 | nt.assert_not_in('foo', ip.user_ns) | |
|
46 | c.StoreMagics.autorestore = True | |
|
47 | ip.extension_manager.reload_extension('storemagic') | |
|
48 | nt.assert_equal(ip.user_ns['foo'], 95) | |
|
49 | finally: | |
|
50 | ip.config = orig_config |
@@ -19,6 +19,10 b' From the command line:' | |||
|
19 | 19 | |
|
20 | 20 | $ python -m IPython.external.mathjax |
|
21 | 21 | |
|
22 | To a specific profile: | |
|
23 | ||
|
24 | $ python -m IPython.external.mathjax --profile=research | |
|
25 | ||
|
22 | 26 | To install MathJax from a file you have already downloaded: |
|
23 | 27 | |
|
24 | 28 | $ python -m IPython.external.mathjax mathjax-xxx.tar.gz |
@@ -46,6 +50,7 b' To find the directory where IPython would like MathJax installed:' | |||
|
46 | 50 | # Imports |
|
47 | 51 | #----------------------------------------------------------------------------- |
|
48 | 52 | |
|
53 | import argparse | |
|
49 | 54 | import os |
|
50 | 55 | import shutil |
|
51 | 56 | import sys |
@@ -55,7 +60,6 b' import zipfile' | |||
|
55 | 60 | |
|
56 | 61 | |
|
57 | 62 | from IPython.utils.path import locate_profile |
|
58 | from IPython.external import argparse | |
|
59 | 63 | #----------------------------------------------------------------------------- |
|
60 | 64 | # |
|
61 | 65 | #----------------------------------------------------------------------------- |
@@ -200,20 +204,26 b' def main() :' | |||
|
200 | 204 | ) |
|
201 | 205 | |
|
202 | 206 | parser.add_argument( |
|
207 | '-p', | |
|
208 | '--profile', | |
|
209 | default='default', | |
|
210 | help='profile to install MathJax to (default is default)') | |
|
211 | ||
|
212 | parser.add_argument( | |
|
203 | 213 | '-i', |
|
204 | 214 | '--install-dir', |
|
205 | default=default_dest, | |
|
206 | help='installation directory (by default : %s)' % (default_dest)) | |
|
215 | help='custom installation directory') | |
|
216 | ||
|
207 | 217 | parser.add_argument( |
|
208 | 218 | '-d', |
|
209 | 219 | '--dest', |
|
210 | 220 | action='store_true', |
|
211 |
help='print where |
|
|
221 | help='print where current mathjax would be installed and exit') | |
|
212 | 222 | parser.add_argument( |
|
213 | 223 | '-r', |
|
214 | 224 | '--replace', |
|
215 | 225 | action='store_true', |
|
216 | help='Wether to replace current mathjax if already exist') | |
|
226 | help='Whether to replace current mathjax if it already exists') | |
|
217 | 227 | parser.add_argument( |
|
218 | 228 | '-t', |
|
219 | 229 | '--test', |
@@ -225,7 +235,13 b' def main() :' | |||
|
225 | 235 | |
|
226 | 236 | pargs = parser.parse_args() |
|
227 | 237 | |
|
228 |
|
|
|
238 | if pargs.install_dir: | |
|
239 | # Explicit install_dir overrides profile | |
|
240 | dest = pargs.install_dir | |
|
241 | else: | |
|
242 | profile = pargs.profile | |
|
243 | dest = os.path.join(locate_profile(profile), 'static', 'mathjax') | |
|
244 | ||
|
229 | 245 | if pargs.dest : |
|
230 | 246 | print dest |
|
231 | 247 | return |
@@ -259,7 +275,7 b' def main() :' | |||
|
259 | 275 | if __name__ == '__main__' : |
|
260 | 276 | sys.exit(main()) |
|
261 | 277 | |
|
262 | __all__ = ['install_mathjax', 'main', 'dest'] | |
|
278 | __all__ = ['install_mathjax', 'main', 'default_dest'] | |
|
263 | 279 | |
|
264 | 280 | """ |
|
265 | 281 | Test notes: |
@@ -78,7 +78,7 b' from IPython.kernel.zmq.kernelapp import (' | |||
|
78 | 78 | kernel_aliases, |
|
79 | 79 | ) |
|
80 | 80 | from IPython.utils.importstring import import_item |
|
81 |
from IPython.utils.localinterfaces import |
|
|
81 | from IPython.utils.localinterfaces import localhost | |
|
82 | 82 | from IPython.utils import submodule |
|
83 | 83 | from IPython.utils.traitlets import ( |
|
84 | 84 | Dict, Unicode, Integer, List, Bool, Bytes, |
@@ -253,7 +253,7 b' aliases.update({' | |||
|
253 | 253 | aliases.pop('f', None) |
|
254 | 254 | |
|
255 | 255 | notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile', |
|
256 | u'notebook-dir'] | |
|
256 | u'notebook-dir', u'profile', u'profile-dir'] | |
|
257 | 257 | |
|
258 | 258 | #----------------------------------------------------------------------------- |
|
259 | 259 | # NotebookApp |
@@ -293,9 +293,11 b' class NotebookApp(BaseIPythonApplication):' | |||
|
293 | 293 | |
|
294 | 294 | # Network related information. |
|
295 | 295 | |
|
296 |
ip = Unicode( |
|
|
296 | ip = Unicode(config=True, | |
|
297 | 297 | help="The IP address the notebook server will listen on." |
|
298 | 298 | ) |
|
299 | def _ip_default(self): | |
|
300 | return localhost() | |
|
299 | 301 | |
|
300 | 302 | def _ip_changed(self, name, old, new): |
|
301 | 303 | if new == u'*': self.ip = u'' |
@@ -471,17 +473,10 b' class NotebookApp(BaseIPythonApplication):' | |||
|
471 | 473 | help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers" |
|
472 | 474 | "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL") |
|
473 | 475 | ) |
|
474 | ||
|
476 | ||
|
475 | 477 | def parse_command_line(self, argv=None): |
|
476 | 478 | super(NotebookApp, self).parse_command_line(argv) |
|
477 | if argv is None: | |
|
478 | argv = sys.argv[1:] | |
|
479 | ||
|
480 | # Scrub frontend-specific flags | |
|
481 | self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags) | |
|
482 | # Kernel should inherit default config file from frontend | |
|
483 | self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name) | |
|
484 | ||
|
479 | ||
|
485 | 480 | if self.extra_args: |
|
486 | 481 | f = os.path.abspath(self.extra_args[0]) |
|
487 | 482 | if os.path.isdir(f): |
@@ -491,6 +486,15 b' class NotebookApp(BaseIPythonApplication):' | |||
|
491 | 486 | nbdir = os.path.dirname(f) |
|
492 | 487 | self.config.NotebookManager.notebook_dir = nbdir |
|
493 | 488 | |
|
489 | def init_kernel_argv(self): | |
|
490 | """construct the kernel arguments""" | |
|
491 | # Scrub frontend-specific flags | |
|
492 | self.kernel_argv = swallow_argv(self.argv, notebook_aliases, notebook_flags) | |
|
493 | # Kernel should inherit default config file from frontend | |
|
494 | self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name) | |
|
495 | # Kernel should get *absolute* path to profile directory | |
|
496 | self.kernel_argv.extend(["--profile-dir", self.profile_dir.location]) | |
|
497 | ||
|
494 | 498 | def init_configurables(self): |
|
495 | 499 | # force Session default to be secure |
|
496 | 500 | default_secure(self.config) |
@@ -657,6 +661,7 b' class NotebookApp(BaseIPythonApplication):' | |||
|
657 | 661 | def initialize(self, argv=None): |
|
658 | 662 | self.init_logging() |
|
659 | 663 | super(NotebookApp, self).initialize(argv) |
|
664 | self.init_kernel_argv() | |
|
660 | 665 | self.init_configurables() |
|
661 | 666 | self.init_components() |
|
662 | 667 | self.init_webapp() |
@@ -691,7 +696,7 b' class NotebookApp(BaseIPythonApplication):' | |||
|
691 | 696 | info("Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).") |
|
692 | 697 | |
|
693 | 698 | if self.open_browser or self.file_to_run: |
|
694 |
ip = self.ip or |
|
|
699 | ip = self.ip or localhost() | |
|
695 | 700 | try: |
|
696 | 701 | browser = webbrowser.get(self.browser or None) |
|
697 | 702 | except webbrowser.Error as e: |
@@ -1,4 +1,4 b'' | |||
|
1 | 1 | @import "../base/less/variables.less"; |
|
2 | 2 | @import "../base/less/mixins.less"; |
|
3 | 3 | @import "../base/less/flexbox.less"; |
|
4 | @import "../base/less/page.less"; | |
|
4 |
@@ -17,12 +17,14 b'' | |||
|
17 | 17 | * Create a custom button in toolbar that execute `%qtconsole` in kernel |
|
18 | 18 | * and hence open a qtconsole attached to the same kernel as the current notebook |
|
19 | 19 | * |
|
20 |
* $([IPython.events]).on(' |
|
|
20 | * $([IPython.events]).on('app_initialized.NotebookApp', function(){ | |
|
21 | 21 | * IPython.toolbar.add_buttons_group([ |
|
22 | 22 | * { |
|
23 | 23 | * 'label' : 'run qtconsole', |
|
24 |
* 'icon' : ' |
|
|
25 |
* 'callback': function(){ |
|
|
24 | * 'icon' : 'icon-terminal', // select your icon from http://fortawesome.github.io/Font-Awesome/icons | |
|
25 | * 'callback': function () { | |
|
26 | * IPython.notebook.kernel.execute('%qtconsole') | |
|
27 | * } | |
|
26 | 28 | * } |
|
27 | 29 | * // add more button here if needed. |
|
28 | 30 | * ]); |
@@ -207,7 +207,7 b' var IPython = (function (IPython) {' | |||
|
207 | 207 | |
|
208 | 208 | |
|
209 | 209 | /** |
|
210 |
* can the cell be split |
|
|
210 | * can the cell be split into two cells | |
|
211 | 211 | * @method is_splittable |
|
212 | 212 | **/ |
|
213 | 213 | Cell.prototype.is_splittable = function () { |
@@ -216,6 +216,15 b' var IPython = (function (IPython) {' | |||
|
216 | 216 | |
|
217 | 217 | |
|
218 | 218 | /** |
|
219 | * can the cell be merged with other cells | |
|
220 | * @method is_mergeable | |
|
221 | **/ | |
|
222 | Cell.prototype.is_mergeable = function () { | |
|
223 | return true; | |
|
224 | }; | |
|
225 | ||
|
226 | ||
|
227 | /** | |
|
219 | 228 | * @return {String} - the text before the cursor |
|
220 | 229 | * @method get_pre_cursor |
|
221 | 230 | **/ |
@@ -323,8 +332,13 b' var IPython = (function (IPython) {' | |||
|
323 | 332 | } |
|
324 | 333 | } |
|
325 | 334 | } |
|
326 |
// fallback on default |
|
|
327 | var default_mode = this.default_mode || 'text/plain'; | |
|
335 | // fallback on default | |
|
336 | var default_mode | |
|
337 | try { | |
|
338 | default_mode = this._options.cm_config.mode; | |
|
339 | } catch(e) { | |
|
340 | default_mode = 'text/plain'; | |
|
341 | } | |
|
328 | 342 | this.code_mirror.setOption('mode', default_mode); |
|
329 | 343 | }; |
|
330 | 344 |
@@ -65,8 +65,8 b' var IPython = (function (IPython) {' | |||
|
65 | 65 | this.code_mirror = null; |
|
66 | 66 | this.input_prompt_number = null; |
|
67 | 67 | this.collapsed = false; |
|
68 | this.default_mode = 'ipython'; | |
|
69 | 68 | this.cell_type = "code"; |
|
69 | this.last_msg_id = null; | |
|
70 | 70 | |
|
71 | 71 | |
|
72 | 72 | var cm_overwrite_options = { |
@@ -167,7 +167,7 b' var IPython = (function (IPython) {' | |||
|
167 | 167 | // triger on keypress (!) otherwise inconsistent event.which depending on plateform |
|
168 | 168 | // browser and keyboard layout ! |
|
169 | 169 | // Pressing '(' , request tooltip, don't forget to reappend it |
|
170 |
// The second argument says to hide the tooltip if the docstring |
|
|
170 | // The second argument says to hide the tooltip if the docstring | |
|
171 | 171 | // is actually empty |
|
172 | 172 | IPython.tooltip.pending(that, true); |
|
173 | 173 | } else if (event.which === key.UPARROW && event.type === 'keydown') { |
@@ -180,8 +180,7 b' var IPython = (function (IPython) {' | |||
|
180 | 180 | return true; |
|
181 | 181 | }; |
|
182 | 182 | } else if (event.which === key.ESC) { |
|
183 | IPython.tooltip.remove_and_cancel_tooltip(true); | |
|
184 | return true; | |
|
183 | return IPython.tooltip.remove_and_cancel_tooltip(true); | |
|
185 | 184 | } else if (event.which === key.DOWNARROW && event.type === 'keydown') { |
|
186 | 185 | // If we are not at the bottom, let CM handle the down arrow and |
|
187 | 186 | // prevent the global keydown handler from handling it. |
@@ -242,9 +241,12 b' var IPython = (function (IPython) {' | |||
|
242 | 241 | * @method execute |
|
243 | 242 | */ |
|
244 | 243 | CodeCell.prototype.execute = function () { |
|
245 |
this.output_area.clear_output( |
|
|
244 | this.output_area.clear_output(); | |
|
246 | 245 | this.set_input_prompt('*'); |
|
247 | 246 | this.element.addClass("running"); |
|
247 | if (this.last_msg_id) { | |
|
248 | this.kernel.clear_callbacks_for_msg(this.last_msg_id); | |
|
249 | } | |
|
248 | 250 | var callbacks = { |
|
249 | 251 | 'execute_reply': $.proxy(this._handle_execute_reply, this), |
|
250 | 252 | 'output': $.proxy(this.output_area.handle_output, this.output_area), |
@@ -252,7 +254,7 b' var IPython = (function (IPython) {' | |||
|
252 | 254 | 'set_next_input': $.proxy(this._handle_set_next_input, this), |
|
253 | 255 | 'input_request': $.proxy(this._handle_input_request, this) |
|
254 | 256 | }; |
|
255 |
|
|
|
257 | this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true}); | |
|
256 | 258 | }; |
|
257 | 259 | |
|
258 | 260 | /** |
@@ -273,7 +275,7 b' var IPython = (function (IPython) {' | |||
|
273 | 275 | var data = {'cell': this, 'text': text} |
|
274 | 276 | $([IPython.events]).trigger('set_next_input.Notebook', data); |
|
275 | 277 | } |
|
276 | ||
|
278 | ||
|
277 | 279 | /** |
|
278 | 280 | * @method _handle_input_request |
|
279 | 281 | * @private |
@@ -388,8 +390,8 b' var IPython = (function (IPython) {' | |||
|
388 | 390 | }; |
|
389 | 391 | |
|
390 | 392 | |
|
391 |
CodeCell.prototype.clear_output = function ( |
|
|
392 |
this.output_area.clear_output( |
|
|
393 | CodeCell.prototype.clear_output = function (wait) { | |
|
394 | this.output_area.clear_output(wait); | |
|
393 | 395 | }; |
|
394 | 396 | |
|
395 | 397 |
@@ -50,12 +50,13 b' var IPython = (function (IPython) {' | |||
|
50 | 50 | * cell_magic_highlight['javascript'] = {'reg':[/^var/]} |
|
51 | 51 | */ |
|
52 | 52 | cell_magic_highlight : { |
|
53 | 'magic_javascript':{'reg':[/^%%javascript/]} | |
|
54 | ,'magic_perl' :{'reg':[/^%%perl/]} | |
|
55 | ,'magic_ruby' :{'reg':[/^%%ruby/]} | |
|
56 | ,'magic_python' :{'reg':[/^%%python3?/]} | |
|
57 | ,'magic_shell' :{'reg':[/^%%bash/]} | |
|
58 | ,'magic_r' :{'reg':[/^%%R/]} | |
|
53 | 'magic_javascript' :{'reg':[/^%%javascript/]} | |
|
54 | ,'magic_perl' :{'reg':[/^%%perl/]} | |
|
55 | ,'magic_ruby' :{'reg':[/^%%ruby/]} | |
|
56 | ,'magic_python' :{'reg':[/^%%python3?/]} | |
|
57 | ,'magic_shell' :{'reg':[/^%%bash/]} | |
|
58 | ,'magic_r' :{'reg':[/^%%R/]} | |
|
59 | ,'magic_text/x-cython' :{'reg':[/^%%cython/]} | |
|
59 | 60 | }, |
|
60 | 61 | |
|
61 | 62 | /** |
@@ -99,6 +99,10 b' function (marked) {' | |||
|
99 | 99 | tables: true, |
|
100 | 100 | langPrefix: "language-", |
|
101 | 101 | highlight: function(code, lang) { |
|
102 | if (!lang) { | |
|
103 | // no language, no highlight | |
|
104 | return code; | |
|
105 | } | |
|
102 | 106 | var highlighted; |
|
103 | 107 | try { |
|
104 | 108 | highlighted = hljs.highlight(lang, code, false); |
@@ -1175,8 +1175,14 b' var IPython = (function (IPython) {' | |||
|
1175 | 1175 | Notebook.prototype.merge_cell_above = function () { |
|
1176 | 1176 | var index = this.get_selected_index(); |
|
1177 | 1177 | var cell = this.get_cell(index); |
|
1178 | if (!cell.is_mergeable()) { | |
|
1179 | return; | |
|
1180 | } | |
|
1178 | 1181 | if (index > 0) { |
|
1179 | 1182 | var upper_cell = this.get_cell(index-1); |
|
1183 | if (!upper_cell.is_mergeable()) { | |
|
1184 | return; | |
|
1185 | } | |
|
1180 | 1186 | var upper_text = upper_cell.get_text(); |
|
1181 | 1187 | var text = cell.get_text(); |
|
1182 | 1188 | if (cell instanceof IPython.CodeCell) { |
@@ -1199,8 +1205,14 b' var IPython = (function (IPython) {' | |||
|
1199 | 1205 | Notebook.prototype.merge_cell_below = function () { |
|
1200 | 1206 | var index = this.get_selected_index(); |
|
1201 | 1207 | var cell = this.get_cell(index); |
|
1208 | if (!cell.is_mergeable()) { | |
|
1209 | return; | |
|
1210 | } | |
|
1202 | 1211 | if (index < this.ncells()-1) { |
|
1203 | 1212 | var lower_cell = this.get_cell(index+1); |
|
1213 | if (!lower_cell.is_mergeable()) { | |
|
1214 | return; | |
|
1215 | } | |
|
1204 | 1216 | var lower_text = lower_cell.get_text(); |
|
1205 | 1217 | var text = cell.get_text(); |
|
1206 | 1218 | if (cell instanceof IPython.CodeCell) { |
@@ -1327,7 +1339,7 b' var IPython = (function (IPython) {' | |||
|
1327 | 1339 | var cells = this.get_cells(); |
|
1328 | 1340 | for (var i=0; i<ncells; i++) { |
|
1329 | 1341 | if (cells[i] instanceof IPython.CodeCell) { |
|
1330 |
cells[i].clear_output( |
|
|
1342 | cells[i].clear_output(); | |
|
1331 | 1343 | // Make all In[] prompts blank, as well |
|
1332 | 1344 | // TODO: make this configurable (via checkbox?) |
|
1333 | 1345 | cells[i].set_input_prompt(); |
@@ -31,7 +31,7 b' var IPython = (function (IPython) {' | |||
|
31 | 31 | this.outputs = []; |
|
32 | 32 | this.collapsed = false; |
|
33 | 33 | this.scrolled = false; |
|
34 |
this.clear_ |
|
|
34 | this.clear_queued = null; | |
|
35 | 35 | if (prompt_area === undefined) { |
|
36 | 36 | this.prompt_area = true; |
|
37 | 37 | } else { |
@@ -289,7 +289,12 b' var IPython = (function (IPython) {' | |||
|
289 | 289 | OutputArea.prototype.append_output = function (json, dynamic) { |
|
290 | 290 | // If dynamic is true, javascript output will be eval'd. |
|
291 | 291 | this.expand(); |
|
292 | this.flush_clear_timeout(); | |
|
292 | ||
|
293 | // Clear the output if clear is queued. | |
|
294 | if (this.clear_queued) { | |
|
295 | this.clear_output(false); | |
|
296 | } | |
|
297 | ||
|
293 | 298 | if (json.output_type === 'pyout') { |
|
294 | 299 | this.append_pyout(json, dynamic); |
|
295 | 300 | } else if (json.output_type === 'pyerr') { |
@@ -300,6 +305,7 b' var IPython = (function (IPython) {' | |||
|
300 | 305 | this.append_stream(json); |
|
301 | 306 | } |
|
302 | 307 | this.outputs.push(json); |
|
308 | this.element.height('auto'); | |
|
303 | 309 | var that = this; |
|
304 | 310 | setTimeout(function(){that.element.trigger('resize');}, 100); |
|
305 | 311 | }; |
@@ -312,6 +318,33 b' var IPython = (function (IPython) {' | |||
|
312 | 318 | } |
|
313 | 319 | return oa; |
|
314 | 320 | }; |
|
321 | ||
|
322 | OutputArea.prototype._append_javascript_error = function (err, container) { | |
|
323 | // display a message when a javascript error occurs in display output | |
|
324 | var msg = "Javascript error adding output!" | |
|
325 | console.log(msg, err); | |
|
326 | if ( container === undefined ) return; | |
|
327 | container.append( | |
|
328 | $('<div/>').html(msg + "<br/>" + | |
|
329 | err.toString() + | |
|
330 | '<br/>See your browser Javascript console for more details.' | |
|
331 | ).addClass('js-error') | |
|
332 | ); | |
|
333 | container.show(); | |
|
334 | }; | |
|
335 | ||
|
336 | OutputArea.prototype._safe_append = function (toinsert) { | |
|
337 | // safely append an item to the document | |
|
338 | // this is an object created by user code, | |
|
339 | // and may have errors, which should not be raised | |
|
340 | // under any circumstances. | |
|
341 | try { | |
|
342 | this.element.append(toinsert); | |
|
343 | } catch(err) { | |
|
344 | console.log(err); | |
|
345 | this._append_javascript_error(err, this.element); | |
|
346 | } | |
|
347 | }; | |
|
315 | 348 | |
|
316 | 349 | |
|
317 | 350 | OutputArea.prototype.append_pyout = function (json, dynamic) { |
@@ -321,19 +354,7 b' var IPython = (function (IPython) {' | |||
|
321 | 354 | toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:'); |
|
322 | 355 | } |
|
323 | 356 | this.append_mime_type(json, toinsert, dynamic); |
|
324 | try { | |
|
325 | this.element.append(toinsert); | |
|
326 | } catch(err) { | |
|
327 | console.log("Error attaching output!"); | |
|
328 | console.log(err); | |
|
329 | this.element.show(); | |
|
330 | toinsert.html($('<div/>') | |
|
331 | .html("Javascript error adding output!<br/>" + | |
|
332 | err.toString() + | |
|
333 | '<br/>See your browser Javascript console for more details.') | |
|
334 | .addClass('js-error') | |
|
335 | ); | |
|
336 | } | |
|
357 | this._safe_append(toinsert); | |
|
337 | 358 | // If we just output latex, typeset it. |
|
338 | 359 | if ((json.latex !== undefined) || (json.html !== undefined)) { |
|
339 | 360 | this.typeset(); |
@@ -352,7 +373,7 b' var IPython = (function (IPython) {' | |||
|
352 | 373 | s = s + '\n'; |
|
353 | 374 | var toinsert = this.create_output_area(); |
|
354 | 375 | this.append_text(s, {}, toinsert); |
|
355 |
this. |
|
|
376 | this._safe_append(toinsert); | |
|
356 | 377 | } |
|
357 | 378 | }; |
|
358 | 379 | |
@@ -389,14 +410,14 b' var IPython = (function (IPython) {' | |||
|
389 | 410 | // If we got here, attach a new div |
|
390 | 411 | var toinsert = this.create_output_area(); |
|
391 | 412 | this.append_text(text, {}, toinsert, "output_stream "+subclass); |
|
392 |
this. |
|
|
413 | this._safe_append(toinsert); | |
|
393 | 414 | }; |
|
394 | 415 | |
|
395 | 416 | |
|
396 | 417 | OutputArea.prototype.append_display_data = function (json, dynamic) { |
|
397 | 418 | var toinsert = this.create_output_area(); |
|
398 | 419 | this.append_mime_type(json, toinsert, dynamic); |
|
399 |
this. |
|
|
420 | this._safe_append(toinsert); | |
|
400 | 421 | // If we just output latex, typeset it. |
|
401 | 422 | if ( (json.latex !== undefined) || (json.html !== undefined) ) { |
|
402 | 423 | this.typeset(); |
@@ -444,15 +465,7 b' var IPython = (function (IPython) {' | |||
|
444 | 465 | try { |
|
445 | 466 | eval(js); |
|
446 | 467 | } catch(err) { |
|
447 | console.log('Error in Javascript!'); | |
|
448 | console.log(err); | |
|
449 | container.show(); | |
|
450 | element.append($('<div/>') | |
|
451 | .html("Error in Javascript !<br/>"+ | |
|
452 | err.toString()+ | |
|
453 | '<br/>See your browser Javascript console for more details.') | |
|
454 | .addClass('js-error') | |
|
455 | ); | |
|
468 | this._append_javascript_error(err, container); | |
|
456 | 469 | } |
|
457 | 470 | }; |
|
458 | 471 | |
@@ -546,7 +559,6 b' var IPython = (function (IPython) {' | |||
|
546 | 559 | OutputArea.prototype.append_raw_input = function (content) { |
|
547 | 560 | var that = this; |
|
548 | 561 | this.expand(); |
|
549 | this.flush_clear_timeout(); | |
|
550 | 562 | var area = this.create_output_area(); |
|
551 | 563 | |
|
552 | 564 | // disable any other raw_inputs, if they are left around |
@@ -599,81 +611,35 b' var IPython = (function (IPython) {' | |||
|
599 | 611 | |
|
600 | 612 | |
|
601 | 613 | OutputArea.prototype.handle_clear_output = function (content) { |
|
602 |
this.clear_output(content. |
|
|
614 | this.clear_output(content.wait); | |
|
603 | 615 | }; |
|
604 | 616 | |
|
605 | 617 | |
|
606 |
OutputArea.prototype.clear_output = function |
|
|
607 | var that = this; | |
|
608 | if (this.clear_out_timeout != null){ | |
|
609 | // fire previous pending clear *immediately* | |
|
610 | clearTimeout(this.clear_out_timeout); | |
|
611 | this.clear_out_timeout = null; | |
|
612 | this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other); | |
|
613 | } | |
|
614 | // store flags for flushing the timeout | |
|
615 | this._clear_stdout = stdout; | |
|
616 | this._clear_stderr = stderr; | |
|
617 | this._clear_other = other; | |
|
618 | this.clear_out_timeout = setTimeout(function() { | |
|
619 | // really clear timeout only after a short delay | |
|
620 | // this reduces flicker in 'clear_output; print' cases | |
|
621 | that.clear_out_timeout = null; | |
|
622 | that._clear_stdout = that._clear_stderr = that._clear_other = null; | |
|
623 | that.clear_output_callback(stdout, stderr, other); | |
|
624 | }, 500 | |
|
625 | ); | |
|
626 | }; | |
|
618 | OutputArea.prototype.clear_output = function(wait) { | |
|
619 | if (wait) { | |
|
627 | 620 | |
|
621 | // If a clear is queued, clear before adding another to the queue. | |
|
622 | if (this.clear_queued) { | |
|
623 | this.clear_output(false); | |
|
624 | }; | |
|
628 | 625 | |
|
629 | OutputArea.prototype.clear_output_callback = function (stdout, stderr, other) { | |
|
630 | var output_div = this.element; | |
|
626 | this.clear_queued = true; | |
|
627 | } else { | |
|
631 | 628 | |
|
632 | if (stdout && stderr && other){ | |
|
629 | // Fix the output div's height if the clear_output is waiting for | |
|
630 | // new output (it is being used in an animation). | |
|
631 | if (this.clear_queued) { | |
|
632 | var height = this.element.height(); | |
|
633 | this.element.height(height); | |
|
634 | this.clear_queued = false; | |
|
635 | } | |
|
636 | ||
|
633 | 637 | // clear all, no need for logic |
|
634 |
|
|
|
638 | this.element.html(""); | |
|
635 | 639 | this.outputs = []; |
|
636 | 640 | this.unscroll_area(); |
|
637 | 641 | return; |
|
638 | } | |
|
639 | // remove html output | |
|
640 | // each output_subarea that has an identifying class is in an output_area | |
|
641 | // which is the element to be removed. | |
|
642 | if (stdout) { | |
|
643 | output_div.find("div.output_stdout").parent().remove(); | |
|
644 | } | |
|
645 | if (stderr) { | |
|
646 | output_div.find("div.output_stderr").parent().remove(); | |
|
647 | } | |
|
648 | if (other) { | |
|
649 | output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove(); | |
|
650 | } | |
|
651 | this.unscroll_area(); | |
|
652 | ||
|
653 | // remove cleared outputs from JSON list: | |
|
654 | for (var i = this.outputs.length - 1; i >= 0; i--) { | |
|
655 | var out = this.outputs[i]; | |
|
656 | var output_type = out.output_type; | |
|
657 | if (output_type == "display_data" && other) { | |
|
658 | this.outputs.splice(i,1); | |
|
659 | } else if (output_type == "stream") { | |
|
660 | if (stdout && out.stream == "stdout") { | |
|
661 | this.outputs.splice(i,1); | |
|
662 | } else if (stderr && out.stream == "stderr") { | |
|
663 | this.outputs.splice(i,1); | |
|
664 | } | |
|
665 | } | |
|
666 | } | |
|
667 | }; | |
|
668 | ||
|
669 | ||
|
670 | OutputArea.prototype.flush_clear_timeout = function() { | |
|
671 | var output_div = this.element; | |
|
672 | if (this.clear_out_timeout){ | |
|
673 | clearTimeout(this.clear_out_timeout); | |
|
674 | this.clear_out_timeout = null; | |
|
675 | this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other); | |
|
676 | } | |
|
642 | }; | |
|
677 | 643 | }; |
|
678 | 644 | |
|
679 | 645 |
@@ -52,7 +52,7 b' var IPython = (function (IPython) {' | |||
|
52 | 52 | $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) { |
|
53 | 53 | that.set_last_checkpoint(data[0]); |
|
54 | 54 | }); |
|
55 | ||
|
55 | ||
|
56 | 56 | $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) { |
|
57 | 57 | that.set_last_checkpoint(data); |
|
58 | 58 | }); |
@@ -104,7 +104,7 b' var IPython = (function (IPython) {' | |||
|
104 | 104 | return false; |
|
105 | 105 | } |
|
106 | 106 | }); |
|
107 | that.find('input[type="text"]').focus(); | |
|
107 | that.find('input[type="text"]').focus().select(); | |
|
108 | 108 | } |
|
109 | 109 | }); |
|
110 | 110 | } |
@@ -482,6 +482,22 b' var IPython = (function (IPython) {' | |||
|
482 | 482 | return data; |
|
483 | 483 | }; |
|
484 | 484 | |
|
485 | /** | |
|
486 | * can the cell be split into two cells | |
|
487 | * @method is_splittable | |
|
488 | **/ | |
|
489 | HeadingCell.prototype.is_splittable = function () { | |
|
490 | return false; | |
|
491 | }; | |
|
492 | ||
|
493 | ||
|
494 | /** | |
|
495 | * can the cell be merged with other cells | |
|
496 | * @method is_mergeable | |
|
497 | **/ | |
|
498 | HeadingCell.prototype.is_mergeable = function () { | |
|
499 | return false; | |
|
500 | }; | |
|
485 | 501 | |
|
486 | 502 | /** |
|
487 | 503 | * Change heading level of cell, and re-render |
@@ -161,16 +161,23 b' var IPython = (function (IPython) {' | |||
|
161 | 161 | this.code_mirror = null; |
|
162 | 162 | } |
|
163 | 163 | |
|
164 | // return true on successfully removing a visible tooltip; otherwise return | |
|
165 | // false. | |
|
164 | 166 | Tooltip.prototype.remove_and_cancel_tooltip = function (force) { |
|
165 | 167 | // note that we don't handle closing directly inside the calltip |
|
166 | 168 | // as in the completer, because it is not focusable, so won't |
|
167 | 169 | // get the event. |
|
168 | if (this._sticky == false || force == true) { | |
|
169 |
this. |
|
|
170 |
this. |
|
|
170 | if (!this._hidden) { | |
|
171 | if (force || !this._sticky) { | |
|
172 | this.cancel_stick(); | |
|
173 | this._hide(); | |
|
174 | } | |
|
175 | this.cancel_pending(); | |
|
176 | this.reset_tabs_function(); | |
|
177 | return true; | |
|
178 | } else { | |
|
179 | return false; | |
|
171 | 180 | } |
|
172 | this.cancel_pending(); | |
|
173 | this.reset_tabs_function(); | |
|
174 | 181 | } |
|
175 | 182 | |
|
176 | 183 | // cancel autocall done after '(' for example. |
@@ -335,7 +342,7 b' var IPython = (function (IPython) {' | |||
|
335 | 342 | if (docstring == null) { |
|
336 | 343 | docstring = reply.docstring; |
|
337 | 344 | } |
|
338 | ||
|
345 | ||
|
339 | 346 | if (docstring == null) { |
|
340 | 347 | // For reals this time, no docstring |
|
341 | 348 | if (this._hide_if_no_docstring) { |
@@ -1,4 +1,4 b'' | |||
|
1 | .cell { | |
|
1 | div.cell { | |
|
2 | 2 | border: 1px solid transparent; |
|
3 | 3 | .vbox(); |
|
4 | 4 | |
@@ -6,9 +6,6 b'' | |||
|
6 | 6 | .corner-all; |
|
7 | 7 | border : thin @border_color solid; |
|
8 | 8 | } |
|
9 | } | |
|
10 | ||
|
11 | div.cell { | |
|
12 | 9 | width: 100%; |
|
13 | 10 | padding: 5px 5px 5px 0px; |
|
14 | 11 | /* This acts as a spacer between cells, that is outside the border */ |
@@ -22,7 +22,7 b'' | |||
|
22 | 22 | overflow-x: auto; |
|
23 | 23 | } |
|
24 | 24 | |
|
25 | @-moz-document { | |
|
25 | @-moz-document url-prefix() { | |
|
26 | 26 | /* Firefox does weird and terrible things (#3549) when overflow-x is auto */ |
|
27 | 27 | /* It doesn't respect the overflow setting anyway, so we can workaround it with this */ |
|
28 | 28 | .CodeMirror-scroll { |
@@ -119,7 +119,6 b' var IPython = (function (IPython) {' | |||
|
119 | 119 | this.ws_url = ws_url; |
|
120 | 120 | this.kernel_url = this.base_url + "/" + this.kernel_id; |
|
121 | 121 | this.start_channels(); |
|
122 | $([IPython.events]).trigger('status_started.Kernel', {kernel: this}); | |
|
123 | 122 | }; |
|
124 | 123 | |
|
125 | 124 | |
@@ -144,11 +143,7 b' var IPython = (function (IPython) {' | |||
|
144 | 143 | this.shell_channel = new this.WebSocket(ws_url + "/shell"); |
|
145 | 144 | this.stdin_channel = new this.WebSocket(ws_url + "/stdin"); |
|
146 | 145 | this.iopub_channel = new this.WebSocket(ws_url + "/iopub"); |
|
147 | send_cookie = function(){ | |
|
148 | // send the session id so the Session object Python-side | |
|
149 | // has the same identity | |
|
150 | this.send(that.session_id + ':' + document.cookie); | |
|
151 | }; | |
|
146 | ||
|
152 | 147 | var already_called_onclose = false; // only alert once |
|
153 | 148 | var ws_closed_early = function(evt){ |
|
154 | 149 | if (already_called_onclose){ |
@@ -170,7 +165,7 b' var IPython = (function (IPython) {' | |||
|
170 | 165 | }; |
|
171 | 166 | var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel]; |
|
172 | 167 | for (var i=0; i < channels.length; i++) { |
|
173 |
channels[i].onopen = |
|
|
168 | channels[i].onopen = $.proxy(this._ws_opened, this); | |
|
174 | 169 | channels[i].onclose = ws_closed_early; |
|
175 | 170 | } |
|
176 | 171 | // switch from early-close to late-close message after 1s |
@@ -187,7 +182,27 b' var IPython = (function (IPython) {' | |||
|
187 | 182 | }; |
|
188 | 183 | |
|
189 | 184 | /** |
|
190 | * Stop the `shell`and `iopub` channels. | |
|
185 | * Handle a websocket entering the open state | |
|
186 | * sends session and cookie authentication info as first message. | |
|
187 | * Once all sockets are open, signal the Kernel.status_started event. | |
|
188 | * @method _ws_opened | |
|
189 | */ | |
|
190 | Kernel.prototype._ws_opened = function (evt) { | |
|
191 | // send the session id so the Session object Python-side | |
|
192 | // has the same identity | |
|
193 | evt.target.send(this.session_id + ':' + document.cookie); | |
|
194 | ||
|
195 | var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel]; | |
|
196 | for (var i=0; i < channels.length; i++) { | |
|
197 | // if any channel is not ready, don't trigger event. | |
|
198 | if ( !channels[i].readyState ) return; | |
|
199 | } | |
|
200 | // all events ready, trigger started event. | |
|
201 | $([IPython.events]).trigger('status_started.Kernel', {kernel: this}); | |
|
202 | }; | |
|
203 | ||
|
204 | /** | |
|
205 | * Stop the websocket channels. | |
|
191 | 206 | * @method stop_channels |
|
192 | 207 | */ |
|
193 | 208 | Kernel.prototype.stop_channels = function () { |
@@ -388,9 +403,16 b' var IPython = (function (IPython) {' | |||
|
388 | 403 | }; |
|
389 | 404 | |
|
390 | 405 | |
|
406 | Kernel.prototype.clear_callbacks_for_msg = function (msg_id) { | |
|
407 | if (this._msg_callbacks[msg_id] !== undefined ) { | |
|
408 | delete this._msg_callbacks[msg_id]; | |
|
409 | } | |
|
410 | }; | |
|
411 | ||
|
412 | ||
|
391 | 413 | Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) { |
|
392 | 414 | this._msg_callbacks[msg_id] = callbacks || {}; |
|
393 | } | |
|
415 | }; | |
|
394 | 416 | |
|
395 | 417 | |
|
396 | 418 | Kernel.prototype._handle_shell_reply = function (e) { |
@@ -18,20 +18,6 b'' | |||
|
18 | 18 | .start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;} |
|
19 | 19 | .end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;} |
|
20 | 20 | .center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;} |
|
21 | body{background-color:white;position:absolute;left:0px;right:0px;top:0px;bottom:0px;overflow:visible;} | |
|
22 | div#header{display:none;} | |
|
23 | #ipython_notebook{padding-left:16px;} | |
|
24 | #noscript{width:auto;padding-top:16px;padding-bottom:16px;text-align:center;font-size:22px;color:red;font-weight:bold;} | |
|
25 | #ipython_notebook img{font-family:Verdana,"Helvetica Neue",Arial,Helvetica,Geneva,sans-serif;height:24px;text-decoration:none;color:black;} | |
|
26 | #site{width:100%;display:none;} | |
|
27 | .ui-button .ui-button-text{padding:0.2em 0.8em;font-size:77%;} | |
|
28 | input.ui-button{padding:0.3em 0.9em;} | |
|
29 | .navbar span{margin-top:3px;} | |
|
30 | span#login_widget{float:right;} | |
|
31 | .nav-header{text-transform:none;} | |
|
32 | .navbar-nobg{background-color:transparent;background-image:none;} | |
|
33 | #header>span{margin-top:10px;} | |
|
34 | .modal-body{max-height:500px;} | |
|
35 | 21 | .center-nav{display:inline-block;margin-bottom:-4px;} |
|
36 | 22 | .alternate_upload{background-color:none;display:inline;} |
|
37 | 23 | .alternate_upload.form{padding:0;margin:0;} |
@@ -67,8 +53,7 b' input.engine_num_input{height:20px;margin-bottom:2px;padding-top:0;padding-botto' | |||
|
67 | 53 | .ansibgpurple{background-color:magenta;} |
|
68 | 54 | .ansibgcyan{background-color:cyan;} |
|
69 | 55 | .ansibggray{background-color:gray;} |
|
70 | .cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}.cell.selected{border-radius:4px;border:thin #ababab solid;} | |
|
71 | div.cell{width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 7px;outline:none;} | |
|
56 | div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 7px;outline:none;}div.cell.selected{border-radius:4px;border:thin #ababab solid;} | |
|
72 | 57 | div.prompt{width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;} |
|
73 | 58 | .celltoolbar{border:thin solid #CFCFCF;border-bottom:none;background:#EEE;border-top-right-radius:3px;border-top-left-radius:3px;width:100%;-webkit-box-pack:end;height:22px;} |
|
74 | 59 | .no_input_radius{border-top-right-radius:0px;border-top-left-radius:0px;} |
@@ -98,7 +83,7 b' div.out_prompt_overlay:hover{-webkit-box-shadow:inset 0 0 1px #000000;-moz-box-s' | |||
|
98 | 83 | div.output_prompt{color:darkred;} |
|
99 | 84 | .CodeMirror{line-height:1.231em;height:auto;background:none;} |
|
100 | 85 | .CodeMirror-scroll{overflow-y:hidden;overflow-x:auto;} |
|
101 | @-moz-document {.CodeMirror-scroll{overflow-x:hidden;}}.CodeMirror-lines{padding:0.4em;} | |
|
86 | @-moz-document url-prefix(){.CodeMirror-scroll{overflow-x:hidden;}}.CodeMirror-lines{padding:0.4em;} | |
|
102 | 87 | .CodeMirror-linenumber{padding:0 8px 0 4px;} |
|
103 | 88 | .CodeMirror-gutters{border-bottom-left-radius:4px;border-top-left-radius:4px;} |
|
104 | 89 | .CodeMirror pre{padding:0;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} |
@@ -8,6 +8,7 b'' | |||
|
8 | 8 | |
|
9 | 9 | // base |
|
10 | 10 | @import "../base/less/style.less"; |
|
11 | @import "../base/less/page.less"; | |
|
11 | 12 | |
|
12 | 13 | // auth |
|
13 | 14 | @import "../auth/less/style.less"; |
@@ -1434,8 +1434,7 b' input.engine_num_input{height:20px;margin-bottom:2px;padding-top:0;padding-botto' | |||
|
1434 | 1434 | .ansibgpurple{background-color:magenta;} |
|
1435 | 1435 | .ansibgcyan{background-color:cyan;} |
|
1436 | 1436 | .ansibggray{background-color:gray;} |
|
1437 | .cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}.cell.selected{border-radius:4px;border:thin #ababab solid;} | |
|
1438 | div.cell{width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 7px;outline:none;} | |
|
1437 | div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 7px;outline:none;}div.cell.selected{border-radius:4px;border:thin #ababab solid;} | |
|
1439 | 1438 | div.prompt{width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;} |
|
1440 | 1439 | .celltoolbar{border:thin solid #CFCFCF;border-bottom:none;background:#EEE;border-top-right-radius:3px;border-top-left-radius:3px;width:100%;-webkit-box-pack:end;height:22px;} |
|
1441 | 1440 | .no_input_radius{border-top-right-radius:0px;border-top-left-radius:0px;} |
@@ -1465,7 +1464,7 b' div.out_prompt_overlay:hover{-webkit-box-shadow:inset 0 0 1px #000000;-moz-box-s' | |||
|
1465 | 1464 | div.output_prompt{color:darkred;} |
|
1466 | 1465 | .CodeMirror{line-height:1.231em;height:auto;background:none;} |
|
1467 | 1466 | .CodeMirror-scroll{overflow-y:hidden;overflow-x:auto;} |
|
1468 | @-moz-document {.CodeMirror-scroll{overflow-x:hidden;}}.CodeMirror-lines{padding:0.4em;} | |
|
1467 | @-moz-document url-prefix(){.CodeMirror-scroll{overflow-x:hidden;}}.CodeMirror-lines{padding:0.4em;} | |
|
1469 | 1468 | .CodeMirror-linenumber{padding:0 8px 0 4px;} |
|
1470 | 1469 | .CodeMirror-gutters{border-bottom-left-radius:4px;border-top-left-radius:4px;} |
|
1471 | 1470 | .CodeMirror pre{padding:0;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} |
@@ -34,14 +34,13 b' import zmq' | |||
|
34 | 34 | from IPython.external.ssh import tunnel |
|
35 | 35 | |
|
36 | 36 | # IPython imports |
|
37 |
|
|
|
37 | from IPython.config import Configurable | |
|
38 | 38 | from IPython.core.profiledir import ProfileDir |
|
39 |
from IPython.utils.localinterfaces import |
|
|
39 | from IPython.utils.localinterfaces import localhost | |
|
40 | 40 | from IPython.utils.path import filefind, get_ipython_dir |
|
41 | 41 | from IPython.utils.py3compat import str_to_bytes, bytes_to_str |
|
42 | 42 | from IPython.utils.traitlets import ( |
|
43 | 43 | Bool, Integer, Unicode, CaselessStrEnum, |
|
44 | HasTraits, | |
|
45 | 44 | ) |
|
46 | 45 | |
|
47 | 46 | |
@@ -50,7 +49,7 b' from IPython.utils.traitlets import (' | |||
|
50 | 49 | #----------------------------------------------------------------------------- |
|
51 | 50 | |
|
52 | 51 | def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0, |
|
53 |
control_port=0, ip= |
|
|
52 | control_port=0, ip='', key=b'', transport='tcp', | |
|
54 | 53 | signature_scheme='hmac-sha256', |
|
55 | 54 | ): |
|
56 | 55 | """Generates a JSON config file, including the selection of random ports. |
@@ -91,6 +90,8 b' def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, ' | |||
|
91 | 90 | and 'sha256' is the default hash function. |
|
92 | 91 | |
|
93 | 92 | """ |
|
93 | if not ip: | |
|
94 | ip = localhost() | |
|
94 | 95 | # default to temporary connector file |
|
95 | 96 | if not fname: |
|
96 | 97 | fname = tempfile.mktemp('.json') |
@@ -383,7 +384,7 b' channel_socket_types = {' | |||
|
383 | 384 | |
|
384 | 385 | port_names = [ "%s_port" % channel for channel in ('shell', 'stdin', 'iopub', 'hb', 'control')] |
|
385 | 386 | |
|
386 |
class ConnectionFileMixin( |
|
|
387 | class ConnectionFileMixin(Configurable): | |
|
387 | 388 | """Mixin for configurable classes that work with connection files""" |
|
388 | 389 | |
|
389 | 390 | # The addresses for the communication channels |
@@ -392,7 +393,7 b' class ConnectionFileMixin(HasTraits):' | |||
|
392 | 393 | |
|
393 | 394 | transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True) |
|
394 | 395 | |
|
395 |
ip = Unicode( |
|
|
396 | ip = Unicode(config=True, | |
|
396 | 397 | help="""Set the kernel\'s IP address [default localhost]. |
|
397 | 398 | If the IP address is something other than localhost, then |
|
398 | 399 | Consoles on other machines will be able to connect |
@@ -406,7 +407,7 b' class ConnectionFileMixin(HasTraits):' | |||
|
406 | 407 | else: |
|
407 | 408 | return 'kernel-ipc' |
|
408 | 409 | else: |
|
409 |
return |
|
|
410 | return localhost() | |
|
410 | 411 | |
|
411 | 412 | def _ip_changed(self, name, old, new): |
|
412 | 413 | if new == '*': |
@@ -1,5 +1,4 b'' | |||
|
1 | """Base class to manage a running kernel | |
|
2 | """ | |
|
1 | """Base class to manage a running kernel""" | |
|
3 | 2 | |
|
4 | 3 | #----------------------------------------------------------------------------- |
|
5 | 4 | # Copyright (C) 2013 The IPython Development Team |
@@ -24,7 +23,7 b' import zmq' | |||
|
24 | 23 | # Local imports |
|
25 | 24 | from IPython.config.configurable import LoggingConfigurable |
|
26 | 25 | from IPython.utils.importstring import import_item |
|
27 |
from IPython.utils.localinterfaces import |
|
|
26 | from IPython.utils.localinterfaces import is_local_ip, local_ips | |
|
28 | 27 | from IPython.utils.traitlets import ( |
|
29 | 28 | Any, Instance, Unicode, List, Bool, Type, DottedObjectName |
|
30 | 29 | ) |
@@ -185,11 +184,11 b' class KernelManager(LoggingConfigurable, ConnectionFileMixin):' | |||
|
185 | 184 | keyword arguments that are passed down to build the kernel_cmd |
|
186 | 185 | and launching the kernel (e.g. Popen kwargs). |
|
187 | 186 | """ |
|
188 |
if self.transport == 'tcp' and self.ip |
|
|
187 | if self.transport == 'tcp' and not is_local_ip(self.ip): | |
|
189 | 188 | raise RuntimeError("Can only launch a kernel on a local interface. " |
|
190 | 189 | "Make sure that the '*_address' attributes are " |
|
191 | 190 | "configured properly. " |
|
192 |
"Currently valid addresses are: %s"% |
|
|
191 | "Currently valid addresses are: %s" % local_ips() | |
|
193 | 192 | ) |
|
194 | 193 | |
|
195 | 194 | # write connection file / get default ports |
@@ -171,7 +171,7 b' class MultiKernelManager(LoggingConfigurable):' | |||
|
171 | 171 | self.log.info("Signaled Kernel %s with %s" % (kernel_id, signum)) |
|
172 | 172 | |
|
173 | 173 | @kernel_method |
|
174 | def restart_kernel(self, kernel_id): | |
|
174 | def restart_kernel(self, kernel_id, now=False): | |
|
175 | 175 | """Restart a kernel by its uuid, keeping the same ports. |
|
176 | 176 | |
|
177 | 177 | Parameters |
@@ -11,101 +11,18 b'' | |||
|
11 | 11 | # Imports |
|
12 | 12 | #------------------------------------------------------------------------------- |
|
13 | 13 | |
|
14 | import os | |
|
15 | import shutil | |
|
16 | 14 | import sys |
|
17 | import tempfile | |
|
18 | ||
|
19 | from contextlib import contextmanager | |
|
20 | from subprocess import PIPE | |
|
21 | 15 | |
|
22 | 16 | import nose.tools as nt |
|
23 | 17 | |
|
24 | from IPython.kernel import KernelManager | |
|
25 | from IPython.kernel.tests.test_message_spec import execute, flush_channels | |
|
26 | from IPython.testing import decorators as dec | |
|
27 | from IPython.utils import path | |
|
18 | from IPython.testing import decorators as dec, tools as tt | |
|
19 | from IPython.utils import py3compat | |
|
20 | ||
|
21 | from .utils import new_kernel, kernel, TIMEOUT, assemble_output, execute, flush_channels | |
|
28 | 22 | |
|
29 | 23 | #------------------------------------------------------------------------------- |
|
30 | 24 | # Tests |
|
31 | 25 | #------------------------------------------------------------------------------- |
|
32 | IPYTHONDIR = None | |
|
33 | save_env = None | |
|
34 | save_get_ipython_dir = None | |
|
35 | ||
|
36 | def setup(): | |
|
37 | """setup temporary IPYTHONDIR for tests""" | |
|
38 | global IPYTHONDIR | |
|
39 | global save_env | |
|
40 | global save_get_ipython_dir | |
|
41 | ||
|
42 | IPYTHONDIR = tempfile.mkdtemp() | |
|
43 | ||
|
44 | save_env = os.environ.copy() | |
|
45 | os.environ["IPYTHONDIR"] = IPYTHONDIR | |
|
46 | ||
|
47 | save_get_ipython_dir = path.get_ipython_dir | |
|
48 | path.get_ipython_dir = lambda : IPYTHONDIR | |
|
49 | ||
|
50 | ||
|
51 | def teardown(): | |
|
52 | path.get_ipython_dir = save_get_ipython_dir | |
|
53 | os.environ = save_env | |
|
54 | ||
|
55 | try: | |
|
56 | shutil.rmtree(IPYTHONDIR) | |
|
57 | except (OSError, IOError): | |
|
58 | # no such file | |
|
59 | pass | |
|
60 | ||
|
61 | ||
|
62 | @contextmanager | |
|
63 | def new_kernel(): | |
|
64 | """start a kernel in a subprocess, and wait for it to be ready | |
|
65 | ||
|
66 | Returns | |
|
67 | ------- | |
|
68 | kernel_manager: connected KernelManager instance | |
|
69 | """ | |
|
70 | KM = KernelManager() | |
|
71 | ||
|
72 | KM.start_kernel(stdout=PIPE, stderr=PIPE) | |
|
73 | KC = KM.client() | |
|
74 | KC.start_channels() | |
|
75 | ||
|
76 | # wait for kernel to be ready | |
|
77 | KC.shell_channel.execute("import sys") | |
|
78 | KC.shell_channel.get_msg(block=True, timeout=5) | |
|
79 | flush_channels(KC) | |
|
80 | try: | |
|
81 | yield KC | |
|
82 | finally: | |
|
83 | KC.stop_channels() | |
|
84 | KM.shutdown_kernel() | |
|
85 | ||
|
86 | ||
|
87 | def assemble_output(iopub): | |
|
88 | """assemble stdout/err from an execution""" | |
|
89 | stdout = '' | |
|
90 | stderr = '' | |
|
91 | while True: | |
|
92 | msg = iopub.get_msg(block=True, timeout=1) | |
|
93 | msg_type = msg['msg_type'] | |
|
94 | content = msg['content'] | |
|
95 | if msg_type == 'status' and content['execution_state'] == 'idle': | |
|
96 | # idle message signals end of output | |
|
97 | break | |
|
98 | elif msg['msg_type'] == 'stream': | |
|
99 | if content['name'] == 'stdout': | |
|
100 | stdout = stdout + content['data'] | |
|
101 | elif content['name'] == 'stderr': | |
|
102 | stderr = stderr + content['data'] | |
|
103 | else: | |
|
104 | raise KeyError("bad stream: %r" % content['name']) | |
|
105 | else: | |
|
106 | # other output, ignored | |
|
107 | pass | |
|
108 | return stdout, stderr | |
|
109 | 26 | |
|
110 | 27 | |
|
111 | 28 | def _check_mp_mode(kc, expected=False, stream="stdout"): |
@@ -116,16 +33,17 b' def _check_mp_mode(kc, expected=False, stream="stdout"):' | |||
|
116 | 33 | nt.assert_equal(eval(stdout.strip()), expected) |
|
117 | 34 | |
|
118 | 35 | |
|
36 | # printing tests | |
|
37 | ||
|
119 | 38 | def test_simple_print(): |
|
120 | 39 | """simple print statement in kernel""" |
|
121 |
with |
|
|
40 | with kernel() as kc: | |
|
122 | 41 | iopub = kc.iopub_channel |
|
123 | 42 | msg_id, content = execute(kc=kc, code="print ('hi')") |
|
124 | 43 | stdout, stderr = assemble_output(iopub) |
|
125 | 44 | nt.assert_equal(stdout, 'hi\n') |
|
126 | 45 | nt.assert_equal(stderr, '') |
|
127 | 46 | _check_mp_mode(kc, expected=False) |
|
128 | print ('hello') | |
|
129 | 47 | |
|
130 | 48 | |
|
131 | 49 | @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows") |
@@ -161,7 +79,7 b' def test_subprocess_print():' | |||
|
161 | 79 | |
|
162 | 80 | def test_subprocess_noprint(): |
|
163 | 81 | """mp.Process without print doesn't trigger iostream mp_mode""" |
|
164 |
with |
|
|
82 | with kernel() as kc: | |
|
165 | 83 | iopub = kc.iopub_channel |
|
166 | 84 | |
|
167 | 85 | np = 5 |
@@ -202,3 +120,51 b' def test_subprocess_error():' | |||
|
202 | 120 | _check_mp_mode(kc, expected=False) |
|
203 | 121 | _check_mp_mode(kc, expected=False, stream="stderr") |
|
204 | 122 | |
|
123 | # raw_input tests | |
|
124 | ||
|
125 | def test_raw_input(): | |
|
126 | """test [raw_]input""" | |
|
127 | with kernel() as kc: | |
|
128 | iopub = kc.iopub_channel | |
|
129 | ||
|
130 | input_f = "input" if py3compat.PY3 else "raw_input" | |
|
131 | theprompt = "prompt> " | |
|
132 | code = 'print({input_f}("{theprompt}"))'.format(**locals()) | |
|
133 | msg_id = kc.execute(code, allow_stdin=True) | |
|
134 | msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT) | |
|
135 | nt.assert_equal(msg['header']['msg_type'], u'input_request') | |
|
136 | content = msg['content'] | |
|
137 | nt.assert_equal(content['prompt'], theprompt) | |
|
138 | text = "some text" | |
|
139 | kc.input(text) | |
|
140 | reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) | |
|
141 | nt.assert_equal(reply['content']['status'], 'ok') | |
|
142 | stdout, stderr = assemble_output(iopub) | |
|
143 | nt.assert_equal(stdout, text + "\n") | |
|
144 | ||
|
145 | ||
|
146 | @dec.skipif(py3compat.PY3) | |
|
147 | def test_eval_input(): | |
|
148 | """test input() on Python 2""" | |
|
149 | with kernel() as kc: | |
|
150 | iopub = kc.iopub_channel | |
|
151 | ||
|
152 | input_f = "input" if py3compat.PY3 else "raw_input" | |
|
153 | theprompt = "prompt> " | |
|
154 | code = 'print(input("{theprompt}"))'.format(**locals()) | |
|
155 | msg_id = kc.execute(code, allow_stdin=True) | |
|
156 | msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT) | |
|
157 | nt.assert_equal(msg['header']['msg_type'], u'input_request') | |
|
158 | content = msg['content'] | |
|
159 | nt.assert_equal(content['prompt'], theprompt) | |
|
160 | kc.input("1+1") | |
|
161 | reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) | |
|
162 | nt.assert_equal(reply['content']['status'], 'ok') | |
|
163 | stdout, stderr = assemble_output(iopub) | |
|
164 | nt.assert_equal(stdout, "2\n") | |
|
165 | ||
|
166 | ||
|
167 | def test_help_output(): | |
|
168 | """ipython kernel --help-all works""" | |
|
169 | tt.help_all_output_test('kernel') | |
|
170 |
@@ -26,18 +26,11 b' class TestKernelManager(TestCase):' | |||
|
26 | 26 | def _run_lifecycle(self, km): |
|
27 | 27 | km.start_kernel(stdout=PIPE, stderr=PIPE) |
|
28 | 28 | self.assertTrue(km.is_alive()) |
|
29 | km.restart_kernel() | |
|
29 | km.restart_kernel(now=True) | |
|
30 | 30 | self.assertTrue(km.is_alive()) |
|
31 | # We need a delay here to give the restarting kernel a chance to | |
|
32 | # restart. Otherwise, the interrupt will kill it, causing the test | |
|
33 | # suite to hang. The reason it *hangs* is that the shutdown | |
|
34 | # message for the restart sometimes hasn't been sent to the kernel. | |
|
35 | # Because linger is oo on the shell channel, the context can't | |
|
36 | # close until the message is sent to the kernel, which is not dead. | |
|
37 | time.sleep(1.0) | |
|
38 | 31 | km.interrupt_kernel() |
|
39 | 32 | self.assertTrue(isinstance(km, KernelManager)) |
|
40 | km.shutdown_kernel() | |
|
33 | km.shutdown_kernel(now=True) | |
|
41 | 34 | |
|
42 | 35 | def test_tcp_lifecycle(self): |
|
43 | 36 | km = self._get_tcp_km() |
@@ -20,14 +20,12 b' Authors' | |||
|
20 | 20 | import nose.tools as nt |
|
21 | 21 | |
|
22 | 22 | # Our own imports |
|
23 | from IPython.testing import decorators as dec | |
|
24 | 23 | from IPython.kernel.launcher import swallow_argv |
|
25 | 24 | |
|
26 | 25 | #----------------------------------------------------------------------------- |
|
27 | 26 | # Classes and functions |
|
28 | 27 | #----------------------------------------------------------------------------- |
|
29 | 28 | |
|
30 | @dec.parametric | |
|
31 | 29 | def test_swallow_argv(): |
|
32 | 30 | tests = [ |
|
33 | 31 | # expected , argv , aliases, flags |
@@ -56,5 +54,5 b' def test_swallow_argv():' | |||
|
56 | 54 | "expected : %r" % expected, |
|
57 | 55 | "returned : %r" % stripped, |
|
58 | 56 | ]) |
|
59 |
|
|
|
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 | 6 | # Distributed under the terms of the BSD License. The full license is in |
|
7 | 7 | # the file COPYING.txt, distributed as part of this software. |
@@ -15,77 +15,25 b' import nose.tools as nt' | |||
|
15 | 15 | |
|
16 | 16 | from IPython.kernel import KernelManager |
|
17 | 17 | |
|
18 | from IPython.testing import decorators as dec | |
|
19 | 18 | from IPython.utils.traitlets import ( |
|
20 | 19 | HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any, |
|
21 | 20 | ) |
|
22 | 21 | |
|
22 | from .utils import TIMEOUT, start_global_kernel, flush_channels, execute | |
|
23 | ||
|
23 | 24 | #----------------------------------------------------------------------------- |
|
24 | # Global setup and utilities | |
|
25 | # Globals | |
|
25 | 26 | #----------------------------------------------------------------------------- |
|
26 | ||
|
27 | STARTUP_TIMEOUT = 60 | |
|
28 | TIMEOUT = 15 | |
|
27 | KC = None | |
|
29 | 28 | |
|
30 | 29 | def setup(): |
|
31 |
global |
|
|
32 | KM = KernelManager() | |
|
33 | KM.start_kernel(stdout=PIPE, stderr=PIPE) | |
|
34 | KC = KM.client() | |
|
35 | KC.start_channels() | |
|
36 | ||
|
37 | # wait for kernel to be ready | |
|
38 | try: | |
|
39 | msg = KC.iopub_channel.get_msg(block=True, timeout=STARTUP_TIMEOUT) | |
|
40 | except Empty: | |
|
41 | pass | |
|
42 | msg_id = KC.kernel_info() | |
|
43 | KC.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT) | |
|
44 | flush_channels() | |
|
45 | ||
|
46 | ||
|
47 | def teardown(): | |
|
48 | KC.stop_channels() | |
|
49 | KM.shutdown_kernel() | |
|
50 | ||
|
51 | ||
|
52 | def flush_channels(kc=None): | |
|
53 | """flush any messages waiting on the queue""" | |
|
54 | if kc is None: | |
|
55 | kc = KC | |
|
56 | for channel in (kc.shell_channel, kc.iopub_channel): | |
|
57 | while True: | |
|
58 | try: | |
|
59 | msg = channel.get_msg(block=True, timeout=0.1) | |
|
60 | except Empty: | |
|
61 | break | |
|
62 | else: | |
|
63 | list(validate_message(msg)) | |
|
64 | ||
|
65 | ||
|
66 | def execute(code='', kc=None, **kwargs): | |
|
67 | """wrapper for doing common steps for validating an execution request""" | |
|
68 | if kc is None: | |
|
69 | kc = KC | |
|
70 | msg_id = kc.execute(code=code, **kwargs) | |
|
71 | reply = kc.get_shell_msg(timeout=TIMEOUT) | |
|
72 | list(validate_message(reply, 'execute_reply', msg_id)) | |
|
73 | busy = kc.get_iopub_msg(timeout=TIMEOUT) | |
|
74 | list(validate_message(busy, 'status', msg_id)) | |
|
75 | nt.assert_equal(busy['content']['execution_state'], 'busy') | |
|
76 | ||
|
77 | if not kwargs.get('silent'): | |
|
78 | pyin = kc.get_iopub_msg(timeout=TIMEOUT) | |
|
79 | list(validate_message(pyin, 'pyin', msg_id)) | |
|
80 | nt.assert_equal(pyin['content']['code'], code) | |
|
81 | ||
|
82 | return msg_id, reply['content'] | |
|
30 | global KC | |
|
31 | KC = start_global_kernel() | |
|
83 | 32 | |
|
84 | 33 | #----------------------------------------------------------------------------- |
|
85 |
# M |
|
|
34 | # Message Spec References | |
|
86 | 35 | #----------------------------------------------------------------------------- |
|
87 | 36 | |
|
88 | ||
|
89 | 37 | class Reference(HasTraits): |
|
90 | 38 | |
|
91 | 39 | """ |
@@ -101,14 +49,14 b' class Reference(HasTraits):' | |||
|
101 | 49 | def check(self, d): |
|
102 | 50 | """validate a dict against our traits""" |
|
103 | 51 | for key in self.trait_names(): |
|
104 | yield nt.assert_true(key in d, "Missing key: %r, should be found in %s" % (key, d)) | |
|
52 | nt.assert_in(key, d) | |
|
105 | 53 | # FIXME: always allow None, probably not a good idea |
|
106 | 54 | if d[key] is None: |
|
107 | 55 | continue |
|
108 | 56 | try: |
|
109 | 57 | setattr(self, key, d[key]) |
|
110 | 58 | except TraitError as e: |
|
111 |
|
|
|
59 | nt.assert_true(False, str(e)) | |
|
112 | 60 | |
|
113 | 61 | |
|
114 | 62 | class RMessage(Reference): |
@@ -133,14 +81,11 b' class ExecuteReply(Reference):' | |||
|
133 | 81 | status = Enum((u'ok', u'error')) |
|
134 | 82 | |
|
135 | 83 | def check(self, d): |
|
136 |
|
|
|
137 | yield tst | |
|
84 | Reference.check(self, d) | |
|
138 | 85 | if d['status'] == 'ok': |
|
139 |
|
|
|
140 | yield tst | |
|
86 | ExecuteReplyOkay().check(d) | |
|
141 | 87 | elif d['status'] == 'error': |
|
142 |
|
|
|
143 | yield tst | |
|
88 | ExecuteReplyError().check(d) | |
|
144 | 89 | |
|
145 | 90 | |
|
146 | 91 | class ExecuteReplyOkay(Reference): |
@@ -177,11 +122,9 b' class OInfoReply(Reference):' | |||
|
177 | 122 | source = Unicode() |
|
178 | 123 | |
|
179 | 124 | def check(self, d): |
|
180 |
|
|
|
181 | yield tst | |
|
125 | Reference.check(self, d) | |
|
182 | 126 | if d['argspec'] is not None: |
|
183 |
|
|
|
184 | yield tst | |
|
127 | ArgSpec().check(d['argspec']) | |
|
185 | 128 | |
|
186 | 129 | |
|
187 | 130 | class ArgSpec(Reference): |
@@ -212,10 +155,8 b' class KernelInfoReply(Reference):' | |||
|
212 | 155 | |
|
213 | 156 | def _ipython_version_changed(self, name, old, new): |
|
214 | 157 | for v in new: |
|
215 | nt.assert_true( | |
|
216 | isinstance(v, int) or isinstance(v, basestring), | |
|
217 | 'expected int or string as version component, got {0!r}' | |
|
218 | .format(v)) | |
|
158 | assert isinstance(v, int) or isinstance(v, basestring), \ | |
|
159 | 'expected int or string as version component, got {0!r}'.format(v) | |
|
219 | 160 | |
|
220 | 161 | |
|
221 | 162 | # IOPub messages |
@@ -241,8 +182,8 b' class DisplayData(Reference):' | |||
|
241 | 182 | data = Dict() |
|
242 | 183 | def _data_changed(self, name, old, new): |
|
243 | 184 | for k,v in new.iteritems(): |
|
244 |
|
|
|
245 |
nt.assert_ |
|
|
185 | assert mime_pat.match(k) | |
|
186 | nt.assert_is_instance(v, basestring) | |
|
246 | 187 | |
|
247 | 188 | |
|
248 | 189 | class PyOut(Reference): |
@@ -250,8 +191,8 b' class PyOut(Reference):' | |||
|
250 | 191 | data = Dict() |
|
251 | 192 | def _data_changed(self, name, old, new): |
|
252 | 193 | for k,v in new.iteritems(): |
|
253 |
|
|
|
254 |
nt.assert_ |
|
|
194 | assert mime_pat.match(k) | |
|
195 | nt.assert_is_instance(v, basestring) | |
|
255 | 196 | |
|
256 | 197 | |
|
257 | 198 | references = { |
@@ -282,13 +223,12 b' def validate_message(msg, msg_type=None, parent=None):' | |||
|
282 | 223 | """ |
|
283 | 224 | RMessage().check(msg) |
|
284 | 225 | if msg_type: |
|
285 |
|
|
|
226 | nt.assert_equal(msg['msg_type'], msg_type) | |
|
286 | 227 | if parent: |
|
287 |
|
|
|
228 | nt.assert_equal(msg['parent_header']['msg_id'], parent) | |
|
288 | 229 | content = msg['content'] |
|
289 | 230 | ref = references[msg['msg_type']] |
|
290 |
|
|
|
291 | yield tst | |
|
231 | ref.check(content) | |
|
292 | 232 | |
|
293 | 233 | |
|
294 | 234 | #----------------------------------------------------------------------------- |
@@ -297,54 +237,47 b' def validate_message(msg, msg_type=None, parent=None):' | |||
|
297 | 237 | |
|
298 | 238 | # Shell channel |
|
299 | 239 | |
|
300 | @dec.parametric | |
|
301 | 240 | def test_execute(): |
|
302 | 241 | flush_channels() |
|
303 | 242 | |
|
304 | 243 | msg_id = KC.execute(code='x=1') |
|
305 | 244 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
306 |
|
|
|
307 | yield tst | |
|
245 | validate_message(reply, 'execute_reply', msg_id) | |
|
308 | 246 | |
|
309 | 247 | |
|
310 | @dec.parametric | |
|
311 | 248 | def test_execute_silent(): |
|
312 | 249 | flush_channels() |
|
313 | 250 | msg_id, reply = execute(code='x=1', silent=True) |
|
314 | 251 | |
|
315 | 252 | # flush status=idle |
|
316 | 253 | status = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
317 |
|
|
|
318 | yield tst | |
|
254 | validate_message(status, 'status', msg_id) | |
|
319 | 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 | 258 | count = reply['execution_count'] |
|
323 | 259 | |
|
324 | 260 | msg_id, reply = execute(code='x=2', silent=True) |
|
325 | 261 | |
|
326 | 262 | # flush status=idle |
|
327 | 263 | status = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
328 |
|
|
|
329 | yield tst | |
|
330 | yield nt.assert_equal(status['content']['execution_state'], 'idle') | |
|
264 | validate_message(status, 'status', msg_id) | |
|
265 | nt.assert_equal(status['content']['execution_state'], 'idle') | |
|
331 | 266 | |
|
332 |
|
|
|
267 | nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1) | |
|
333 | 268 | count_2 = reply['execution_count'] |
|
334 |
|
|
|
269 | nt.assert_equal(count_2, count) | |
|
335 | 270 | |
|
336 | 271 | |
|
337 | @dec.parametric | |
|
338 | 272 | def test_execute_error(): |
|
339 | 273 | flush_channels() |
|
340 | 274 | |
|
341 | 275 | msg_id, reply = execute(code='1/0') |
|
342 |
|
|
|
343 |
|
|
|
276 | nt.assert_equal(reply['status'], 'error') | |
|
277 | nt.assert_equal(reply['ename'], 'ZeroDivisionError') | |
|
344 | 278 | |
|
345 | 279 | pyerr = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
346 |
|
|
|
347 | yield tst | |
|
280 | validate_message(pyerr, 'pyerr', msg_id) | |
|
348 | 281 | |
|
349 | 282 | |
|
350 | 283 | def test_execute_inc(): |
@@ -405,17 +338,14 b' def test_user_expressions_fail():' | |||
|
405 | 338 | nt.assert_equal(foo['ename'], 'NameError') |
|
406 | 339 | |
|
407 | 340 | |
|
408 | @dec.parametric | |
|
409 | 341 | def test_oinfo(): |
|
410 | 342 | flush_channels() |
|
411 | 343 | |
|
412 | 344 | msg_id = KC.object_info('a') |
|
413 | 345 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
414 |
|
|
|
415 | yield tst | |
|
346 | validate_message(reply, 'object_info_reply', msg_id) | |
|
416 | 347 | |
|
417 | 348 | |
|
418 | @dec.parametric | |
|
419 | 349 | def test_oinfo_found(): |
|
420 | 350 | flush_channels() |
|
421 | 351 | |
@@ -423,15 +353,13 b' def test_oinfo_found():' | |||
|
423 | 353 | |
|
424 | 354 | msg_id = KC.object_info('a') |
|
425 | 355 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
426 |
|
|
|
427 | yield tst | |
|
356 | validate_message(reply, 'object_info_reply', msg_id) | |
|
428 | 357 | content = reply['content'] |
|
429 |
|
|
|
358 | assert content['found'] | |
|
430 | 359 | argspec = content['argspec'] |
|
431 | yield nt.assert_true(argspec is None, "didn't expect argspec dict, got %r" % argspec) | |
|
360 | nt.assert_is(argspec, None) | |
|
432 | 361 | |
|
433 | 362 | |
|
434 | @dec.parametric | |
|
435 | 363 | def test_oinfo_detail(): |
|
436 | 364 | flush_channels() |
|
437 | 365 | |
@@ -439,28 +367,24 b' def test_oinfo_detail():' | |||
|
439 | 367 | |
|
440 | 368 | msg_id = KC.object_info('ip.object_inspect', detail_level=2) |
|
441 | 369 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
442 |
|
|
|
443 | yield tst | |
|
370 | validate_message(reply, 'object_info_reply', msg_id) | |
|
444 | 371 | content = reply['content'] |
|
445 |
|
|
|
372 | assert content['found'] | |
|
446 | 373 | argspec = content['argspec'] |
|
447 |
|
|
|
448 |
|
|
|
374 | nt.assert_is_instance(argspec, dict, "expected non-empty argspec dict, got %r" % argspec) | |
|
375 | nt.assert_equal(argspec['defaults'], [0]) | |
|
449 | 376 | |
|
450 | 377 | |
|
451 | @dec.parametric | |
|
452 | 378 | def test_oinfo_not_found(): |
|
453 | 379 | flush_channels() |
|
454 | 380 | |
|
455 | 381 | msg_id = KC.object_info('dne') |
|
456 | 382 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
457 |
|
|
|
458 | yield tst | |
|
383 | validate_message(reply, 'object_info_reply', msg_id) | |
|
459 | 384 | content = reply['content'] |
|
460 |
|
|
|
385 | nt.assert_false(content['found']) | |
|
461 | 386 | |
|
462 | 387 | |
|
463 | @dec.parametric | |
|
464 | 388 | def test_complete(): |
|
465 | 389 | flush_channels() |
|
466 | 390 | |
@@ -468,49 +392,42 b' def test_complete():' | |||
|
468 | 392 | |
|
469 | 393 | msg_id = KC.complete('al', 'al', 2) |
|
470 | 394 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
471 |
|
|
|
472 | yield tst | |
|
395 | validate_message(reply, 'complete_reply', msg_id) | |
|
473 | 396 | matches = reply['content']['matches'] |
|
474 | 397 | for name in ('alpha', 'albert'): |
|
475 | yield nt.assert_true(name in matches, "Missing match: %r" % name) | |
|
398 | nt.assert_in(name, matches) | |
|
476 | 399 | |
|
477 | 400 | |
|
478 | @dec.parametric | |
|
479 | 401 | def test_kernel_info_request(): |
|
480 | 402 | flush_channels() |
|
481 | 403 | |
|
482 | 404 | msg_id = KC.kernel_info() |
|
483 | 405 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
484 |
|
|
|
485 | yield tst | |
|
406 | validate_message(reply, 'kernel_info_reply', msg_id) | |
|
486 | 407 | |
|
487 | 408 | |
|
488 | 409 | # IOPub channel |
|
489 | 410 | |
|
490 | 411 | |
|
491 | @dec.parametric | |
|
492 | 412 | def test_stream(): |
|
493 | 413 | flush_channels() |
|
494 | 414 | |
|
495 | 415 | msg_id, reply = execute("print('hi')") |
|
496 | 416 | |
|
497 | 417 | stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
498 |
|
|
|
499 | yield tst | |
|
418 | validate_message(stdout, 'stream', msg_id) | |
|
500 | 419 | content = stdout['content'] |
|
501 |
|
|
|
502 |
|
|
|
420 | nt.assert_equal(content['name'], u'stdout') | |
|
421 | nt.assert_equal(content['data'], u'hi\n') | |
|
503 | 422 | |
|
504 | 423 | |
|
505 | @dec.parametric | |
|
506 | 424 | def test_display_data(): |
|
507 | 425 | flush_channels() |
|
508 | 426 | |
|
509 | 427 | msg_id, reply = execute("from IPython.core.display import display; display(1)") |
|
510 | 428 | |
|
511 | 429 | display = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
512 |
|
|
|
513 | yield tst | |
|
430 | validate_message(display, 'display_data', parent=msg_id) | |
|
514 | 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 | 7 | from IPython.testing import decorators as dec |
|
8 | 8 | |
|
9 | 9 | from IPython.config.loader import Config |
|
10 |
from IPython.utils.localinterfaces import |
|
|
10 | from IPython.utils.localinterfaces import localhost | |
|
11 | 11 | from IPython.kernel import KernelManager |
|
12 | 12 | from IPython.kernel.multikernelmanager import MultiKernelManager |
|
13 | 13 | |
@@ -31,20 +31,13 b' class TestKernelManager(TestCase):' | |||
|
31 | 31 | self.assertTrue(kid in km) |
|
32 | 32 | self.assertTrue(kid in km.list_kernel_ids()) |
|
33 | 33 | self.assertEqual(len(km),1) |
|
34 | km.restart_kernel(kid) | |
|
34 | km.restart_kernel(kid, now=True) | |
|
35 | 35 | self.assertTrue(km.is_alive(kid)) |
|
36 | 36 | self.assertTrue(kid in km.list_kernel_ids()) |
|
37 | # We need a delay here to give the restarting kernel a chance to | |
|
38 | # restart. Otherwise, the interrupt will kill it, causing the test | |
|
39 | # suite to hang. The reason it *hangs* is that the shutdown | |
|
40 | # message for the restart sometimes hasn't been sent to the kernel. | |
|
41 | # Because linger is oo on the shell channel, the context can't | |
|
42 | # close until the message is sent to the kernel, which is not dead. | |
|
43 | time.sleep(1.0) | |
|
44 | 37 | km.interrupt_kernel(kid) |
|
45 | 38 | k = km.get_kernel(kid) |
|
46 | 39 | self.assertTrue(isinstance(k, KernelManager)) |
|
47 | km.shutdown_kernel(kid) | |
|
40 | km.shutdown_kernel(kid, now=True) | |
|
48 | 41 | self.assertTrue(not kid in km) |
|
49 | 42 | |
|
50 | 43 | def _run_cinfo(self, km, transport, ip): |
@@ -63,7 +56,7 b' class TestKernelManager(TestCase):' | |||
|
63 | 56 | self.assertTrue('hb_port' in cinfo) |
|
64 | 57 | stream = km.connect_hb(kid) |
|
65 | 58 | stream.close() |
|
66 | km.shutdown_kernel(kid) | |
|
59 | km.shutdown_kernel(kid, now=True) | |
|
67 | 60 | |
|
68 | 61 | def test_tcp_lifecycle(self): |
|
69 | 62 | km = self._get_tcp_km() |
@@ -71,7 +64,7 b' class TestKernelManager(TestCase):' | |||
|
71 | 64 | |
|
72 | 65 | def test_tcp_cinfo(self): |
|
73 | 66 | km = self._get_tcp_km() |
|
74 |
self._run_cinfo(km, 'tcp', |
|
|
67 | self._run_cinfo(km, 'tcp', localhost()) | |
|
75 | 68 | |
|
76 | 69 | @dec.skip_win32 |
|
77 | 70 | def test_ipc_lifecycle(self): |
@@ -14,8 +14,6 b' Authors' | |||
|
14 | 14 | |
|
15 | 15 | import nose.tools as nt |
|
16 | 16 | |
|
17 | from IPython.testing import decorators as dec | |
|
18 | ||
|
19 | 17 | from IPython.kernel import launcher, connect |
|
20 | 18 | from IPython import kernel |
|
21 | 19 | |
@@ -23,25 +21,21 b' from IPython import kernel' | |||
|
23 | 21 | # Classes and functions |
|
24 | 22 | #----------------------------------------------------------------------------- |
|
25 | 23 | |
|
26 | @dec.parametric | |
|
27 | 24 | def test_kms(): |
|
28 | 25 | for base in ("", "Multi"): |
|
29 | 26 | KM = base + "KernelManager" |
|
30 |
|
|
|
27 | nt.assert_in(KM, dir(kernel)) | |
|
31 | 28 | |
|
32 | @dec.parametric | |
|
33 | 29 | def test_kcs(): |
|
34 | 30 | for base in ("", "Blocking"): |
|
35 | 31 | KM = base + "KernelClient" |
|
36 |
|
|
|
32 | nt.assert_in(KM, dir(kernel)) | |
|
37 | 33 | |
|
38 | @dec.parametric | |
|
39 | 34 | def test_launcher(): |
|
40 | 35 | for name in launcher.__all__: |
|
41 |
|
|
|
36 | nt.assert_in(name, dir(kernel)) | |
|
42 | 37 | |
|
43 | @dec.parametric | |
|
44 | 38 | def test_connect(): |
|
45 | 39 | for name in connect.__all__: |
|
46 |
|
|
|
40 | nt.assert_in(name, dir(kernel)) | |
|
47 | 41 |
@@ -19,7 +19,7 b' from threading import Thread' | |||
|
19 | 19 | |
|
20 | 20 | import zmq |
|
21 | 21 | |
|
22 |
from IPython.utils.localinterfaces import |
|
|
22 | from IPython.utils.localinterfaces import localhost | |
|
23 | 23 | |
|
24 | 24 | #----------------------------------------------------------------------------- |
|
25 | 25 | # Code |
@@ -29,7 +29,9 b' from IPython.utils.localinterfaces import LOCALHOST' | |||
|
29 | 29 | class Heartbeat(Thread): |
|
30 | 30 | "A simple ping-pong style heartbeat that runs in a thread." |
|
31 | 31 | |
|
32 |
def __init__(self, context, addr= |
|
|
32 | def __init__(self, context, addr=None): | |
|
33 | if addr is None: | |
|
34 | addr = ('tcp', localhost(), 0) | |
|
33 | 35 | Thread.__init__(self) |
|
34 | 36 | self.context = context |
|
35 | 37 | self.transport, self.ip, self.port = addr |
@@ -87,7 +87,7 b' class Kernel(Configurable):' | |||
|
87 | 87 | if self.shell is not None: |
|
88 | 88 | self.shell.user_module = new |
|
89 | 89 | |
|
90 | user_ns = Dict(default_value=None) | |
|
90 | user_ns = Instance(dict, args=None, allow_none=True) | |
|
91 | 91 | def _user_ns_changed(self, name, old, new): |
|
92 | 92 | if self.shell is not None: |
|
93 | 93 | self.shell.user_ns = new |
@@ -131,6 +131,7 b' class Kernel(Configurable):' | |||
|
131 | 131 | # A reference to the Python builtin 'raw_input' function. |
|
132 | 132 | # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3) |
|
133 | 133 | _sys_raw_input = Any() |
|
134 | _sys_eval_input = Any() | |
|
134 | 135 | |
|
135 | 136 | # set of aborted msg_ids |
|
136 | 137 | aborted = Set() |
@@ -352,15 +353,18 b' class Kernel(Configurable):' | |||
|
352 | 353 | # raw_input in the user namespace. |
|
353 | 354 | if content.get('allow_stdin', False): |
|
354 | 355 | raw_input = lambda prompt='': self._raw_input(prompt, ident, parent) |
|
356 | input = lambda prompt='': eval(raw_input(prompt)) | |
|
355 | 357 | else: |
|
356 | raw_input = lambda prompt='' : self._no_raw_input() | |
|
358 | raw_input = input = lambda prompt='' : self._no_raw_input() | |
|
357 | 359 | |
|
358 | 360 | if py3compat.PY3: |
|
359 | 361 | self._sys_raw_input = __builtin__.input |
|
360 | 362 | __builtin__.input = raw_input |
|
361 | 363 | else: |
|
362 | 364 | self._sys_raw_input = __builtin__.raw_input |
|
365 | self._sys_eval_input = __builtin__.input | |
|
363 | 366 | __builtin__.raw_input = raw_input |
|
367 | __builtin__.input = input | |
|
364 | 368 | |
|
365 | 369 | # Set the parent message of the display hook and out streams. |
|
366 | 370 | shell.displayhook.set_parent(parent) |
@@ -403,6 +407,7 b' class Kernel(Configurable):' | |||
|
403 | 407 | __builtin__.input = self._sys_raw_input |
|
404 | 408 | else: |
|
405 | 409 | __builtin__.raw_input = self._sys_raw_input |
|
410 | __builtin__.input = self._sys_eval_input | |
|
406 | 411 | |
|
407 | 412 | reply_content[u'status'] = status |
|
408 | 413 |
@@ -39,7 +39,7 b' from IPython.core.shellapp import (' | |||
|
39 | 39 | InteractiveShellApp, shell_flags, shell_aliases |
|
40 | 40 | ) |
|
41 | 41 | from IPython.utils import io |
|
42 |
from IPython.utils.localinterfaces import |
|
|
42 | from IPython.utils.localinterfaces import localhost | |
|
43 | 43 | from IPython.utils.path import filefind |
|
44 | 44 | from IPython.utils.py3compat import str_to_bytes |
|
45 | 45 | from IPython.utils.traitlets import ( |
@@ -156,7 +156,8 b' class IPKernelApp(BaseIPythonApplication, InteractiveShellApp):' | |||
|
156 | 156 | else: |
|
157 | 157 | return 'kernel-ipc' |
|
158 | 158 | else: |
|
159 |
return |
|
|
159 | return localhost() | |
|
160 | ||
|
160 | 161 | hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]") |
|
161 | 162 | shell_port = Integer(0, config=True, help="set the shell (ROUTER) port [default: random]") |
|
162 | 163 | iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]") |
@@ -436,8 +436,15 b' class Session(Configurable):' | |||
|
436 | 436 | msg = dict(a=[1,'hi']) |
|
437 | 437 | try: |
|
438 | 438 | packed = pack(msg) |
|
439 | except Exception: | |
|
440 |
|
|
|
439 | except Exception as e: | |
|
440 | msg = "packer '{packer}' could not serialize a simple message: {e}{jsonmsg}" | |
|
441 | if self.packer == 'json': | |
|
442 | jsonmsg = "\nzmq.utils.jsonapi.jsonmod = %s" % jsonapi.jsonmod | |
|
443 | else: | |
|
444 | jsonmsg = "" | |
|
445 | raise ValueError( | |
|
446 | msg.format(packer=self.packer, e=e, jsonmsg=jsonmsg) | |
|
447 | ) | |
|
441 | 448 | |
|
442 | 449 | # ensure packed message is bytes |
|
443 | 450 | if not isinstance(packed, bytes): |
@@ -446,8 +453,16 b' class Session(Configurable):' | |||
|
446 | 453 | # check that unpack is pack's inverse |
|
447 | 454 | try: |
|
448 | 455 | unpacked = unpack(packed) |
|
449 | except Exception: | |
|
450 | raise ValueError("unpacker could not handle the packer's output") | |
|
456 | assert unpacked == msg | |
|
457 | except Exception as e: | |
|
458 | msg = "unpacker '{unpacker}' could not handle output from packer '{packer}': {e}{jsonmsg}" | |
|
459 | if self.packer == 'json': | |
|
460 | jsonmsg = "\nzmq.utils.jsonapi.jsonmod = %s" % jsonapi.jsonmod | |
|
461 | else: | |
|
462 | jsonmsg = "" | |
|
463 | raise ValueError( | |
|
464 | msg.format(packer=self.packer, unpacker=self.unpacker, e=e, jsonmsg=jsonmsg) | |
|
465 | ) | |
|
451 | 466 | |
|
452 | 467 | # check datetime support |
|
453 | 468 | msg = dict(t=datetime.now()) |
@@ -46,7 +46,6 b" DTYPES = ('uint8', 'float64', 'int32', [('g', 'float32')], '|S10')" | |||
|
46 | 46 | # Tests |
|
47 | 47 | #------------------------------------------------------------------------------- |
|
48 | 48 | |
|
49 | @dec.parametric | |
|
50 | 49 | def test_roundtrip_simple(): |
|
51 | 50 | for obj in [ |
|
52 | 51 | 'hello', |
@@ -55,18 +54,16 b' def test_roundtrip_simple():' | |||
|
55 | 54 | (b'123', 'hello'), |
|
56 | 55 | ]: |
|
57 | 56 | obj2 = roundtrip(obj) |
|
58 |
|
|
|
57 | nt.assert_equal(obj, obj2) | |
|
59 | 58 | |
|
60 | @dec.parametric | |
|
61 | 59 | def test_roundtrip_nested(): |
|
62 | 60 | for obj in [ |
|
63 | 61 | dict(a=range(5), b={1:b'hello'}), |
|
64 | 62 | [range(5),[range(3),(1,[b'whoda'])]], |
|
65 | 63 | ]: |
|
66 | 64 | obj2 = roundtrip(obj) |
|
67 |
|
|
|
65 | nt.assert_equal(obj, obj2) | |
|
68 | 66 | |
|
69 | @dec.parametric | |
|
70 | 67 | def test_roundtrip_buffered(): |
|
71 | 68 | for obj in [ |
|
72 | 69 | dict(a=b"x"*1025), |
@@ -74,10 +71,10 b' def test_roundtrip_buffered():' | |||
|
74 | 71 | [b"hello"*501, 1,2,3] |
|
75 | 72 | ]: |
|
76 | 73 | bufs = serialize_object(obj) |
|
77 |
|
|
|
74 | nt.assert_equal(len(bufs), 2) | |
|
78 | 75 | obj2, remainder = unserialize_object(bufs) |
|
79 |
|
|
|
80 |
|
|
|
76 | nt.assert_equal(remainder, []) | |
|
77 | nt.assert_equal(obj, obj2) | |
|
81 | 78 | |
|
82 | 79 | def _scrub_nan(A): |
|
83 | 80 | """scrub nans out of empty arrays |
@@ -93,7 +90,6 b' def _scrub_nan(A):' | |||
|
93 | 90 | # e.g. str dtype |
|
94 | 91 | pass |
|
95 | 92 | |
|
96 | @dec.parametric | |
|
97 | 93 | @dec.skip_without('numpy') |
|
98 | 94 | def test_numpy(): |
|
99 | 95 | import numpy |
@@ -104,12 +100,11 b' def test_numpy():' | |||
|
104 | 100 | _scrub_nan(A) |
|
105 | 101 | bufs = serialize_object(A) |
|
106 | 102 | B, r = unserialize_object(bufs) |
|
107 |
|
|
|
108 |
|
|
|
109 |
|
|
|
110 |
|
|
|
103 | nt.assert_equal(r, []) | |
|
104 | nt.assert_equal(A.shape, B.shape) | |
|
105 | nt.assert_equal(A.dtype, B.dtype) | |
|
106 | assert_array_equal(A,B) | |
|
111 | 107 | |
|
112 | @dec.parametric | |
|
113 | 108 | @dec.skip_without('numpy') |
|
114 | 109 | def test_recarray(): |
|
115 | 110 | import numpy |
@@ -124,12 +119,11 b' def test_recarray():' | |||
|
124 | 119 | |
|
125 | 120 | bufs = serialize_object(A) |
|
126 | 121 | B, r = unserialize_object(bufs) |
|
127 |
|
|
|
128 |
|
|
|
129 |
|
|
|
130 |
|
|
|
122 | nt.assert_equal(r, []) | |
|
123 | nt.assert_equal(A.shape, B.shape) | |
|
124 | nt.assert_equal(A.dtype, B.dtype) | |
|
125 | assert_array_equal(A,B) | |
|
131 | 126 | |
|
132 | @dec.parametric | |
|
133 | 127 | @dec.skip_without('numpy') |
|
134 | 128 | def test_numpy_in_seq(): |
|
135 | 129 | import numpy |
@@ -140,15 +134,14 b' def test_numpy_in_seq():' | |||
|
140 | 134 | _scrub_nan(A) |
|
141 | 135 | bufs = serialize_object((A,1,2,b'hello')) |
|
142 | 136 | canned = pickle.loads(bufs[0]) |
|
143 |
|
|
|
137 | nt.assert_is_instance(canned[0], CannedArray) | |
|
144 | 138 | tup, r = unserialize_object(bufs) |
|
145 | 139 | B = tup[0] |
|
146 |
|
|
|
147 |
|
|
|
148 |
|
|
|
149 |
|
|
|
140 | nt.assert_equal(r, []) | |
|
141 | nt.assert_equal(A.shape, B.shape) | |
|
142 | nt.assert_equal(A.dtype, B.dtype) | |
|
143 | assert_array_equal(A,B) | |
|
150 | 144 | |
|
151 | @dec.parametric | |
|
152 | 145 | @dec.skip_without('numpy') |
|
153 | 146 | def test_numpy_in_dict(): |
|
154 | 147 | import numpy |
@@ -159,27 +152,25 b' def test_numpy_in_dict():' | |||
|
159 | 152 | _scrub_nan(A) |
|
160 | 153 | bufs = serialize_object(dict(a=A,b=1,c=range(20))) |
|
161 | 154 | canned = pickle.loads(bufs[0]) |
|
162 |
|
|
|
155 | nt.assert_is_instance(canned['a'], CannedArray) | |
|
163 | 156 | d, r = unserialize_object(bufs) |
|
164 | 157 | B = d['a'] |
|
165 |
|
|
|
166 |
|
|
|
167 |
|
|
|
168 |
|
|
|
158 | nt.assert_equal(r, []) | |
|
159 | nt.assert_equal(A.shape, B.shape) | |
|
160 | nt.assert_equal(A.dtype, B.dtype) | |
|
161 | assert_array_equal(A,B) | |
|
169 | 162 | |
|
170 | @dec.parametric | |
|
171 | 163 | def test_class(): |
|
172 | 164 | @interactive |
|
173 | 165 | class C(object): |
|
174 | 166 | a=5 |
|
175 | 167 | bufs = serialize_object(dict(C=C)) |
|
176 | 168 | canned = pickle.loads(bufs[0]) |
|
177 |
|
|
|
169 | nt.assert_is_instance(canned['C'], CannedClass) | |
|
178 | 170 | d, r = unserialize_object(bufs) |
|
179 | 171 | C2 = d['C'] |
|
180 |
|
|
|
172 | nt.assert_equal(C2.a, C.a) | |
|
181 | 173 | |
|
182 | @dec.parametric | |
|
183 | 174 | def test_class_oldstyle(): |
|
184 | 175 | @interactive |
|
185 | 176 | class C: |
@@ -187,42 +178,38 b' def test_class_oldstyle():' | |||
|
187 | 178 | |
|
188 | 179 | bufs = serialize_object(dict(C=C)) |
|
189 | 180 | canned = pickle.loads(bufs[0]) |
|
190 |
|
|
|
181 | nt.assert_is_instance(canned['C'], CannedClass) | |
|
191 | 182 | d, r = unserialize_object(bufs) |
|
192 | 183 | C2 = d['C'] |
|
193 |
|
|
|
184 | nt.assert_equal(C2.a, C.a) | |
|
194 | 185 | |
|
195 | @dec.parametric | |
|
196 | 186 | def test_tuple(): |
|
197 | 187 | tup = (lambda x:x, 1) |
|
198 | 188 | bufs = serialize_object(tup) |
|
199 | 189 | canned = pickle.loads(bufs[0]) |
|
200 |
|
|
|
190 | nt.assert_is_instance(canned, tuple) | |
|
201 | 191 | t2, r = unserialize_object(bufs) |
|
202 |
|
|
|
192 | nt.assert_equal(t2[0](t2[1]), tup[0](tup[1])) | |
|
203 | 193 | |
|
204 | 194 | point = namedtuple('point', 'x y') |
|
205 | 195 | |
|
206 | @dec.parametric | |
|
207 | 196 | def test_namedtuple(): |
|
208 | 197 | p = point(1,2) |
|
209 | 198 | bufs = serialize_object(p) |
|
210 | 199 | canned = pickle.loads(bufs[0]) |
|
211 |
|
|
|
200 | nt.assert_is_instance(canned, point) | |
|
212 | 201 | p2, r = unserialize_object(bufs, globals()) |
|
213 |
|
|
|
214 |
|
|
|
202 | nt.assert_equal(p2.x, p.x) | |
|
203 | nt.assert_equal(p2.y, p.y) | |
|
215 | 204 | |
|
216 | @dec.parametric | |
|
217 | 205 | def test_list(): |
|
218 | 206 | lis = [lambda x:x, 1] |
|
219 | 207 | bufs = serialize_object(lis) |
|
220 | 208 | canned = pickle.loads(bufs[0]) |
|
221 |
|
|
|
209 | nt.assert_is_instance(canned, list) | |
|
222 | 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 | 213 | def test_class_inheritance(): |
|
227 | 214 | @interactive |
|
228 | 215 | class C(object): |
@@ -234,8 +221,8 b' def test_class_inheritance():' | |||
|
234 | 221 | |
|
235 | 222 | bufs = serialize_object(dict(D=D)) |
|
236 | 223 | canned = pickle.loads(bufs[0]) |
|
237 |
|
|
|
224 | nt.assert_is_instance(canned['D'], CannedClass) | |
|
238 | 225 | d, r = unserialize_object(bufs) |
|
239 | 226 | D2 = d['D'] |
|
240 |
|
|
|
241 |
|
|
|
227 | nt.assert_equal(D2.a, D.a) | |
|
228 | nt.assert_equal(D2.b, D.b) |
@@ -20,6 +20,12 b' from zmq.eventloop.zmqstream import ZMQStream' | |||
|
20 | 20 | |
|
21 | 21 | from IPython.kernel.zmq import session as ss |
|
22 | 22 | |
|
23 | def _bad_packer(obj): | |
|
24 | raise TypeError("I don't work") | |
|
25 | ||
|
26 | def _bad_unpacker(bytes): | |
|
27 | raise TypeError("I don't work either") | |
|
28 | ||
|
23 | 29 | class SessionTestCase(BaseZMQTestCase): |
|
24 | 30 | |
|
25 | 31 | def setUp(self): |
@@ -222,4 +228,44 b' class TestSession(SessionTestCase):' | |||
|
222 | 228 | self.assertTrue(len(session.digest_history) == 100) |
|
223 | 229 | session._add_digest(uuid.uuid4().bytes) |
|
224 | 230 | self.assertTrue(len(session.digest_history) == 91) |
|
225 | ||
|
231 | ||
|
232 | def test_bad_pack(self): | |
|
233 | try: | |
|
234 | session = ss.Session(pack=_bad_packer) | |
|
235 | except ValueError as e: | |
|
236 | self.assertIn("could not serialize", str(e)) | |
|
237 | self.assertIn("don't work", str(e)) | |
|
238 | else: | |
|
239 | self.fail("Should have raised ValueError") | |
|
240 | ||
|
241 | def test_bad_unpack(self): | |
|
242 | try: | |
|
243 | session = ss.Session(unpack=_bad_unpacker) | |
|
244 | except ValueError as e: | |
|
245 | self.assertIn("could not handle output", str(e)) | |
|
246 | self.assertIn("don't work either", str(e)) | |
|
247 | else: | |
|
248 | self.fail("Should have raised ValueError") | |
|
249 | ||
|
250 | def test_bad_packer(self): | |
|
251 | try: | |
|
252 | session = ss.Session(packer=__name__ + '._bad_packer') | |
|
253 | except ValueError as e: | |
|
254 | self.assertIn("could not serialize", str(e)) | |
|
255 | self.assertIn("don't work", str(e)) | |
|
256 | else: | |
|
257 | self.fail("Should have raised ValueError") | |
|
258 | ||
|
259 | def test_bad_unpacker(self): | |
|
260 | try: | |
|
261 | session = ss.Session(unpacker=__name__ + '._bad_unpacker') | |
|
262 | except ValueError as e: | |
|
263 | self.assertIn("could not handle output", str(e)) | |
|
264 | self.assertIn("don't work either", str(e)) | |
|
265 | else: | |
|
266 | self.fail("Should have raised ValueError") | |
|
267 | ||
|
268 | def test_bad_roundtrip(self): | |
|
269 | with self.assertRaises(ValueError): | |
|
270 | session= ss.Session(unpack=lambda b: 5) | |
|
271 |
@@ -8,10 +8,38 b' def test_ipython_start_kernel_userns():' | |||
|
8 | 8 | cmd = ('from IPython import start_kernel\n' |
|
9 | 9 | 'ns = {"tre": 123}\n' |
|
10 | 10 | 'start_kernel(user_ns=ns)') |
|
11 | ||
|
11 | ||
|
12 | 12 | with setup_kernel(cmd) as client: |
|
13 | 13 | msg_id = client.object_info('tre') |
|
14 | 14 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
15 | 15 | content = msg['content'] |
|
16 | 16 | assert content['found'] |
|
17 | nt.assert_equal(content['string_form'], u'123') No newline at end of file | |
|
17 | nt.assert_equal(content['string_form'], u'123') | |
|
18 | ||
|
19 | # user_module should be an instance of DummyMod | |
|
20 | msg_id = client.execute("usermod = get_ipython().user_module") | |
|
21 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
|
22 | content = msg['content'] | |
|
23 | nt.assert_equal(content['status'], u'ok') | |
|
24 | msg_id = client.object_info('usermod') | |
|
25 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
|
26 | content = msg['content'] | |
|
27 | assert content['found'] | |
|
28 | nt.assert_in('DummyMod', content['string_form']) | |
|
29 | ||
|
30 | def test_ipython_start_kernel_no_userns(): | |
|
31 | # Issue #4188 - user_ns should be passed to shell as None, not {} | |
|
32 | cmd = ('from IPython import start_kernel\n' | |
|
33 | 'start_kernel()') | |
|
34 | ||
|
35 | with setup_kernel(cmd) as client: | |
|
36 | # user_module should not be an instance of DummyMod | |
|
37 | msg_id = client.execute("usermod = get_ipython().user_module") | |
|
38 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
|
39 | content = msg['content'] | |
|
40 | nt.assert_equal(content['status'], u'ok') | |
|
41 | msg_id = client.object_info('usermod') | |
|
42 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
|
43 | content = msg['content'] | |
|
44 | assert content['found'] | |
|
45 | nt.assert_not_in('DummyMod', content['string_form']) |
@@ -86,14 +86,11 b' class ZMQDisplayPublisher(DisplayPublisher):' | |||
|
86 | 86 | parent=self.parent_header, ident=self.topic, |
|
87 | 87 | ) |
|
88 | 88 | |
|
89 |
def clear_output(self, |
|
|
90 | content = dict(stdout=stdout, stderr=stderr, other=other) | |
|
91 | ||
|
92 | if stdout: | |
|
93 |
|
|
|
94 | if stderr: | |
|
95 | print('\r', file=sys.stderr, end='') | |
|
96 | ||
|
89 | def clear_output(self, wait=False): | |
|
90 | content = dict(wait=wait) | |
|
91 | ||
|
92 | print('\r', file=sys.stdout, end='') | |
|
93 | print('\r', file=sys.stderr, end='') | |
|
97 | 94 | self._flush_streams() |
|
98 | 95 | |
|
99 | 96 | self.session.send( |
@@ -14,14 +14,14 b' The classes are (see their docstrings for further details):' | |||
|
14 | 14 | - Demo: pure python demos |
|
15 | 15 | |
|
16 | 16 | - IPythonDemo: demos with input to be processed by IPython as if it had been |
|
17 | typed interactively (so magics work, as well as any other special syntax you | |
|
18 | may have added via input prefilters). | |
|
17 | typed interactively (so magics work, as well as any other special syntax you | |
|
18 | may have added via input prefilters). | |
|
19 | 19 | |
|
20 | 20 | - LineDemo: single-line version of the Demo class. These demos are executed |
|
21 | one line at a time, and require no markup. | |
|
21 | one line at a time, and require no markup. | |
|
22 | 22 | |
|
23 | 23 | - IPythonLineDemo: IPython version of the LineDemo class (the demo is |
|
24 | executed a line at a time, but processed via IPython). | |
|
24 | executed a line at a time, but processed via IPython). | |
|
25 | 25 | |
|
26 | 26 | - ClearMixin: mixin to make Demo classes with less visual clutter. It |
|
27 | 27 | declares an empty marquee and a pre_cmd that clears the screen before each |
@@ -214,18 +214,18 b' class Demo(object):' | |||
|
214 | 214 | Optional inputs: |
|
215 | 215 | |
|
216 | 216 | - title: a string to use as the demo name. Of most use when the demo |
|
217 | you are making comes from an object that has no filename, or if you | |
|
218 | want an alternate denotation distinct from the filename. | |
|
217 | you are making comes from an object that has no filename, or if you | |
|
218 | want an alternate denotation distinct from the filename. | |
|
219 | 219 | |
|
220 | 220 | - arg_str(''): a string of arguments, internally converted to a list |
|
221 | just like sys.argv, so the demo script can see a similar | |
|
222 | environment. | |
|
221 | just like sys.argv, so the demo script can see a similar | |
|
222 | environment. | |
|
223 | 223 | |
|
224 | 224 | - auto_all(None): global flag to run all blocks automatically without |
|
225 | confirmation. This attribute overrides the block-level tags and | |
|
226 | applies to the whole demo. It is an attribute of the object, and | |
|
227 | can be changed at runtime simply by reassigning it to a boolean | |
|
228 | value. | |
|
225 | confirmation. This attribute overrides the block-level tags and | |
|
226 | applies to the whole demo. It is an attribute of the object, and | |
|
227 | can be changed at runtime simply by reassigning it to a boolean | |
|
228 | value. | |
|
229 | 229 | """ |
|
230 | 230 | if hasattr(src, "read"): |
|
231 | 231 | # It seems to be a file or a file-like object |
@@ -481,6 +481,19 b' set_inputhook = inputhook_manager.set_inputhook' | |||
|
481 | 481 | current_gui = inputhook_manager.current_gui |
|
482 | 482 | clear_app_refs = inputhook_manager.clear_app_refs |
|
483 | 483 | |
|
484 | guis = {None: clear_inputhook, | |
|
485 | GUI_NONE: clear_inputhook, | |
|
486 | GUI_OSX: lambda app=False: None, | |
|
487 | GUI_TK: enable_tk, | |
|
488 | GUI_GTK: enable_gtk, | |
|
489 | GUI_WX: enable_wx, | |
|
490 | GUI_QT: enable_qt4, # qt3 not supported | |
|
491 | GUI_QT4: enable_qt4, | |
|
492 | GUI_GLUT: enable_glut, | |
|
493 | GUI_PYGLET: enable_pyglet, | |
|
494 | GUI_GTK3: enable_gtk3, | |
|
495 | } | |
|
496 | ||
|
484 | 497 | |
|
485 | 498 | # Convenience function to switch amongst them |
|
486 | 499 | def enable_gui(gui=None, app=None): |
@@ -507,18 +520,6 b' def enable_gui(gui=None, app=None):' | |||
|
507 | 520 | PyOS_InputHook wrapper object or the GUI toolkit app created, if there was |
|
508 | 521 | one. |
|
509 | 522 | """ |
|
510 | guis = {None: clear_inputhook, | |
|
511 | GUI_NONE: clear_inputhook, | |
|
512 | GUI_OSX: lambda app=False: None, | |
|
513 | GUI_TK: enable_tk, | |
|
514 | GUI_GTK: enable_gtk, | |
|
515 | GUI_WX: enable_wx, | |
|
516 | GUI_QT: enable_qt4, # qt3 not supported | |
|
517 | GUI_QT4: enable_qt4, | |
|
518 | GUI_GLUT: enable_glut, | |
|
519 | GUI_PYGLET: enable_pyglet, | |
|
520 | GUI_GTK3: enable_gtk3, | |
|
521 | } | |
|
522 | 523 | try: |
|
523 | 524 | gui_hook = guis[gui] |
|
524 | 525 | except KeyError: |
@@ -93,9 +93,9 b' class InteractiveRunner(object):' | |||
|
93 | 93 | - program: command to execute the given program. |
|
94 | 94 | |
|
95 | 95 | - prompts: a list of patterns to match as valid prompts, in the |
|
96 | format used by pexpect. This basically means that it can be either | |
|
97 | a string (to be compiled as a regular expression) or a list of such | |
|
98 | (it must be a true list, as pexpect does type checks). | |
|
96 | format used by pexpect. This basically means that it can be either | |
|
97 | a string (to be compiled as a regular expression) or a list of such | |
|
98 | (it must be a true list, as pexpect does type checks). | |
|
99 | 99 | |
|
100 | 100 | If more than one prompt is given, the first is treated as the main |
|
101 | 101 | program prompt and the others as 'continuation' prompts, like |
@@ -107,19 +107,19 b' class InteractiveRunner(object):' | |||
|
107 | 107 | Optional inputs: |
|
108 | 108 | |
|
109 | 109 | - args(None): optional list of strings to pass as arguments to the |
|
110 | child program. | |
|
110 | child program. | |
|
111 | 111 | |
|
112 | 112 | - out(sys.stdout): if given, an output stream to be used when writing |
|
113 | output. The only requirement is that it must have a .write() method. | |
|
113 | output. The only requirement is that it must have a .write() method. | |
|
114 | 114 | |
|
115 | 115 | Public members not parameterized in the constructor: |
|
116 | 116 | |
|
117 | 117 | - delaybeforesend(0): Newer versions of pexpect have a delay before |
|
118 | sending each new input. For our purposes here, it's typically best | |
|
119 | to just set this to zero, but if you encounter reliability problems | |
|
120 | or want an interactive run to pause briefly at each prompt, just | |
|
121 | increase this value (it is measured in seconds). Note that this | |
|
122 | variable is not honored at all by older versions of pexpect. | |
|
118 | sending each new input. For our purposes here, it's typically best | |
|
119 | to just set this to zero, but if you encounter reliability problems | |
|
120 | or want an interactive run to pause briefly at each prompt, just | |
|
121 | increase this value (it is measured in seconds). Note that this | |
|
122 | variable is not honored at all by older versions of pexpect. | |
|
123 | 123 | """ |
|
124 | 124 | |
|
125 | 125 | self.program = program |
@@ -154,7 +154,7 b' class InteractiveRunner(object):' | |||
|
154 | 154 | |
|
155 | 155 | Inputs: |
|
156 | 156 | |
|
157 | -fname: name of the file to execute. | |
|
157 | - fname: name of the file to execute. | |
|
158 | 158 | |
|
159 | 159 | See the run_source docstring for the meaning of the optional |
|
160 | 160 | arguments.""" |
@@ -173,15 +173,15 b' class InteractiveRunner(object):' | |||
|
173 | 173 | Inputs: |
|
174 | 174 | |
|
175 | 175 | - source: a string of code to be executed, or an open file object we |
|
176 | can iterate over. | |
|
176 | can iterate over. | |
|
177 | 177 | |
|
178 | 178 | Optional inputs: |
|
179 | 179 | |
|
180 | 180 | - interact(False): if true, start to interact with the running |
|
181 | program at the end of the script. Otherwise, just exit. | |
|
181 | program at the end of the script. Otherwise, just exit. | |
|
182 | 182 | |
|
183 | 183 | - get_output(False): if true, capture the output of the child process |
|
184 | (filtering the input commands out) and return it as a string. | |
|
184 | (filtering the input commands out) and return it as a string. | |
|
185 | 185 | |
|
186 | 186 | Returns: |
|
187 | 187 | A string containing the process output, but only if requested. |
@@ -229,7 +229,17 b' class PrettyPrinter(_PrettyPrinterBase):' | |||
|
229 | 229 | self.buffer.append(Breakable(sep, width, self)) |
|
230 | 230 | self.buffer_width += width |
|
231 | 231 | self._break_outer_groups() |
|
232 | ||
|
232 | ||
|
233 | def break_(self): | |
|
234 | """ | |
|
235 | Explicitly insert a newline into the output, maintaining correct indentation. | |
|
236 | """ | |
|
237 | self.flush() | |
|
238 | self.output.write(self.newline) | |
|
239 | self.output.write(' ' * self.indentation) | |
|
240 | self.output_width = self.indentation | |
|
241 | self.buffer_width = 0 | |
|
242 | ||
|
233 | 243 | |
|
234 | 244 | def begin_group(self, indent=0, open=''): |
|
235 | 245 | """ |
@@ -478,8 +488,12 b' def _default_pprint(obj, p, cycle):' | |||
|
478 | 488 | """ |
|
479 | 489 | klass = getattr(obj, '__class__', None) or type(obj) |
|
480 | 490 | if getattr(klass, '__repr__', None) not in _baseclass_reprs: |
|
481 | # A user-provided repr. | |
|
482 |
|
|
|
491 | # A user-provided repr. Find newlines and replace them with p.break_() | |
|
492 | output = repr(obj) | |
|
493 | for idx,output_line in enumerate(output.splitlines()): | |
|
494 | if idx: | |
|
495 | p.break_() | |
|
496 | p.text(output_line) | |
|
483 | 497 | return |
|
484 | 498 | p.begin_group(1, '<') |
|
485 | 499 | p.pretty(klass) |
@@ -58,6 +58,23 b' class NoModule(object):' | |||
|
58 | 58 | |
|
59 | 59 | NoModule.__module__ = None |
|
60 | 60 | |
|
61 | class Breaking(object): | |
|
62 | def _repr_pretty_(self, p, cycle): | |
|
63 | with p.group(4,"TG: ",":"): | |
|
64 | p.text("Breaking(") | |
|
65 | p.break_() | |
|
66 | p.text(")") | |
|
67 | ||
|
68 | class BreakingRepr(object): | |
|
69 | def __repr__(self): | |
|
70 | return "Breaking(\n)" | |
|
71 | ||
|
72 | class BreakingReprParent(object): | |
|
73 | def _repr_pretty_(self, p, cycle): | |
|
74 | with p.group(4,"TG: ",":"): | |
|
75 | p.pretty(BreakingRepr()) | |
|
76 | ||
|
77 | ||
|
61 | 78 | |
|
62 | 79 | def test_indentation(): |
|
63 | 80 | """Test correct indentation in groups""" |
@@ -118,3 +135,19 b' def test_pprint_nomod():' | |||
|
118 | 135 | """ |
|
119 | 136 | output = pretty.pretty(NoModule) |
|
120 | 137 | nt.assert_equal(output, 'NoModule') |
|
138 | ||
|
139 | def test_pprint_break(): | |
|
140 | """ | |
|
141 | Test that p.break_ produces expected output | |
|
142 | """ | |
|
143 | output = pretty.pretty(Breaking()) | |
|
144 | expected = "TG: Breaking(\n ):" | |
|
145 | nt.assert_equal(output, expected) | |
|
146 | ||
|
147 | def test_pprint_break_repr(): | |
|
148 | """ | |
|
149 | Test that p.break_ is used in repr | |
|
150 | """ | |
|
151 | output = pretty.pretty(BreakingReprParent()) | |
|
152 | expected = "TG: Breaking(\n ):" | |
|
153 | nt.assert_equal(output, expected) No newline at end of file |
@@ -1,8 +1,9 b'' | |||
|
1 | 1 | from .export import * |
|
2 | 2 | from .html import HTMLExporter |
|
3 | 3 | from .slides import SlidesExporter |
|
4 | from .exporter import Exporter | |
|
4 | from .templateexporter import TemplateExporter | |
|
5 | 5 | from .latex import LatexExporter |
|
6 | 6 | from .markdown import MarkdownExporter |
|
7 | 7 | from .python import PythonExporter |
|
8 | 8 | from .rst import RSTExporter |
|
9 | from .exporter import Exporter |
@@ -19,6 +19,7 b' from IPython.nbformat.v3.nbbase import NotebookNode' | |||
|
19 | 19 | from IPython.config import Config |
|
20 | 20 | |
|
21 | 21 | from .exporter import Exporter |
|
22 | from .templateexporter import TemplateExporter | |
|
22 | 23 | from .html import HTMLExporter |
|
23 | 24 | from .slides import SlidesExporter |
|
24 | 25 | from .latex import LatexExporter |
@@ -122,7 +123,7 b' def export(exporter, nb, **kw):' | |||
|
122 | 123 | return output, resources |
|
123 | 124 | |
|
124 | 125 | exporter_map = dict( |
|
125 | custom=Exporter, | |
|
126 | custom=TemplateExporter, | |
|
126 | 127 | html=HTMLExporter, |
|
127 | 128 | slides=SlidesExporter, |
|
128 | 129 | latex=LatexExporter, |
@@ -19,56 +19,21 b' from __future__ import print_function, absolute_import' | |||
|
19 | 19 | # Stdlib imports |
|
20 | 20 | import io |
|
21 | 21 | import os |
|
22 | import inspect | |
|
23 | 22 | import copy |
|
24 | 23 | import collections |
|
25 | 24 | import datetime |
|
26 | 25 | |
|
27 | # other libs/dependencies | |
|
28 | from jinja2 import Environment, FileSystemLoader, ChoiceLoader, TemplateNotFound | |
|
29 | 26 | |
|
30 | 27 | # IPython imports |
|
31 | 28 | from IPython.config.configurable import LoggingConfigurable |
|
32 | 29 | from IPython.config import Config |
|
33 | 30 | from IPython.nbformat import current as nbformat |
|
34 |
from IPython.utils.traitlets import MetaHasTraits, |
|
|
31 | from IPython.utils.traitlets import MetaHasTraits, Unicode, List | |
|
35 | 32 | from IPython.utils.importstring import import_item |
|
36 |
from IPython.utils |
|
|
37 | from IPython.utils import py3compat | |
|
33 | from IPython.utils import text, py3compat | |
|
38 | 34 | |
|
39 | 35 | from IPython.nbconvert import preprocessors as nbpreprocessors |
|
40 | from IPython.nbconvert import filters | |
|
41 | 36 | |
|
42 | #----------------------------------------------------------------------------- | |
|
43 | # Globals and constants | |
|
44 | #----------------------------------------------------------------------------- | |
|
45 | ||
|
46 | #Jinja2 extensions to load. | |
|
47 | JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols'] | |
|
48 | ||
|
49 | default_filters = { | |
|
50 | 'indent': indent, | |
|
51 | 'markdown2html': filters.markdown2html, | |
|
52 | 'ansi2html': filters.ansi2html, | |
|
53 | 'filter_data_type': filters.DataTypeFilter, | |
|
54 | 'get_lines': filters.get_lines, | |
|
55 | 'highlight2html': filters.highlight2html, | |
|
56 | 'highlight2latex': filters.highlight2latex, | |
|
57 | 'ipython2python': filters.ipython2python, | |
|
58 | 'posix_path': filters.posix_path, | |
|
59 | 'markdown2latex': filters.markdown2latex, | |
|
60 | 'markdown2rst': filters.markdown2rst, | |
|
61 | 'comment_lines': filters.comment_lines, | |
|
62 | 'strip_ansi': filters.strip_ansi, | |
|
63 | 'strip_dollars': filters.strip_dollars, | |
|
64 | 'strip_files_prefix': filters.strip_files_prefix, | |
|
65 | 'html2text' : filters.html2text, | |
|
66 | 'add_anchor': filters.add_anchor, | |
|
67 | 'ansi2latex': filters.ansi2latex, | |
|
68 | 'strip_math_space': filters.strip_math_space, | |
|
69 | 'wrap_text': filters.wrap_text, | |
|
70 | 'escape_latex': filters.escape_latex, | |
|
71 | } | |
|
72 | 37 | |
|
73 | 38 | #----------------------------------------------------------------------------- |
|
74 | 39 | # Class |
@@ -81,70 +46,21 b' class ResourcesDict(collections.defaultdict):' | |||
|
81 | 46 | |
|
82 | 47 | class Exporter(LoggingConfigurable): |
|
83 | 48 | """ |
|
84 | Exports notebooks into other file formats. Uses Jinja 2 templating engine | |
|
85 | to output new formats. Inherit from this class if you are creating a new | |
|
86 | template type along with new filters/preprocessors. If the filters/ | |
|
87 | preprocessors provided by default suffice, there is no need to inherit from | |
|
88 | this class. Instead, override the template_file and file_extension | |
|
89 | traits via a config file. | |
|
90 | ||
|
91 | {filters} | |
|
49 | Class containing methods that sequentially run a list of preprocessors on a | |
|
50 | NotebookNode object and then return the modified NotebookNode object and | |
|
51 | accompanying resources dict. | |
|
92 | 52 | """ |
|
93 | ||
|
94 | # finish the docstring | |
|
95 | __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys())) | |
|
96 | ||
|
97 | ||
|
98 | template_file = Unicode(u'default', | |
|
99 | config=True, | |
|
100 | help="Name of the template file to use") | |
|
101 | def _template_file_changed(self, name, old, new): | |
|
102 | if new=='default': | |
|
103 | self.template_file = self.default_template | |
|
104 | else: | |
|
105 | self.template_file = new | |
|
106 | self.template = None | |
|
107 | self._load_template() | |
|
108 | ||
|
109 | default_template = Unicode(u'') | |
|
110 | template = Any() | |
|
111 | environment = Any() | |
|
112 | 53 | |
|
113 | 54 | file_extension = Unicode( |
|
114 |
'txt', config=True, |
|
|
55 | 'txt', config=True, | |
|
115 | 56 | help="Extension of the file that should be written to disk" |
|
116 | 57 | ) |
|
117 | 58 | |
|
118 | template_path = List(['.'], config=True) | |
|
119 | def _template_path_changed(self, name, old, new): | |
|
120 | self._load_template() | |
|
121 | ||
|
122 | default_template_path = Unicode( | |
|
123 | os.path.join("..", "templates"), | |
|
124 | help="Path where the template files are located.") | |
|
125 | ||
|
126 | template_skeleton_path = Unicode( | |
|
127 | os.path.join("..", "templates", "skeleton"), | |
|
128 | help="Path where the template skeleton files are located.") | |
|
129 | ||
|
130 | #Jinja block definitions | |
|
131 | jinja_comment_block_start = Unicode("", config=True) | |
|
132 | jinja_comment_block_end = Unicode("", config=True) | |
|
133 | jinja_variable_block_start = Unicode("", config=True) | |
|
134 | jinja_variable_block_end = Unicode("", config=True) | |
|
135 | jinja_logic_block_start = Unicode("", config=True) | |
|
136 | jinja_logic_block_end = Unicode("", config=True) | |
|
137 | ||
|
138 | #Extension that the template files use. | |
|
139 | template_extension = Unicode(".tpl", config=True) | |
|
140 | ||
|
141 | 59 | #Configurability, allows the user to easily add filters and preprocessors. |
|
142 | 60 | preprocessors = List(config=True, |
|
143 | 61 | help="""List of preprocessors, by name or namespace, to enable.""") |
|
144 | 62 | |
|
145 | filters = Dict(config=True, | |
|
146 | help="""Dictionary of filters, by name and namespace, to add to the Jinja | |
|
147 | environment.""") | |
|
63 | _preprocessors = None | |
|
148 | 64 | |
|
149 | 65 | default_preprocessors = List([nbpreprocessors.coalesce_streams, |
|
150 | 66 | nbpreprocessors.SVG2PDFPreprocessor, |
@@ -152,42 +68,34 b' class Exporter(LoggingConfigurable):' | |||
|
152 | 68 | nbpreprocessors.CSSHTMLHeaderPreprocessor, |
|
153 | 69 | nbpreprocessors.RevealHelpPreprocessor, |
|
154 | 70 | nbpreprocessors.LatexPreprocessor, |
|
155 |
nbpreprocessors. |
|
|
71 | nbpreprocessors.HighlightMagicsPreprocessor], | |
|
156 | 72 | config=True, |
|
157 | 73 | help="""List of preprocessors available by default, by name, namespace, |
|
158 | 74 | instance, or type.""") |
|
159 | 75 | |
|
160 | 76 | |
|
161 |
def __init__(self, config=None, |
|
|
77 | def __init__(self, config=None, **kw): | |
|
162 | 78 | """ |
|
163 | 79 | Public constructor |
|
164 | ||
|
80 | ||
|
165 | 81 | Parameters |
|
166 | 82 | ---------- |
|
167 | 83 | config : config |
|
168 | 84 | User configuration instance. |
|
169 | extra_loaders : list[of Jinja Loaders] | |
|
170 | ordered list of Jinja loader to find templates. Will be tried in order | |
|
171 | before the default FileSystem ones. | |
|
172 | template : str (optional, kw arg) | |
|
173 | Template to use when exporting. | |
|
174 | 85 | """ |
|
175 | 86 | if not config: |
|
176 | 87 | config = self.default_config |
|
177 | ||
|
88 | ||
|
178 | 89 | super(Exporter, self).__init__(config=config, **kw) |
|
179 | 90 | |
|
180 | 91 | #Init |
|
181 | self._init_template() | |
|
182 | self._init_environment(extra_loaders=extra_loaders) | |
|
183 | 92 | self._init_preprocessors() |
|
184 | self._init_filters() | |
|
185 | 93 | |
|
186 | 94 | |
|
187 | 95 | @property |
|
188 | 96 | def default_config(self): |
|
189 | 97 | return Config() |
|
190 | ||
|
98 | ||
|
191 | 99 | def _config_changed(self, name, old, new): |
|
192 | 100 | """When setting config, make sure to start with our default_config""" |
|
193 | 101 | c = self.default_config |
@@ -196,56 +104,18 b' class Exporter(LoggingConfigurable):' | |||
|
196 | 104 | if c != old: |
|
197 | 105 | self.config = c |
|
198 | 106 | super(Exporter, self)._config_changed(name, old, c) |
|
199 | ||
|
200 | ||
|
201 | def _load_template(self): | |
|
202 | """Load the Jinja template object from the template file | |
|
203 | ||
|
204 | This is a no-op if the template attribute is already defined, | |
|
205 | or the Jinja environment is not setup yet. | |
|
206 | ||
|
207 | This is triggered by various trait changes that would change the template. | |
|
208 | """ | |
|
209 | if self.template is not None: | |
|
210 | return | |
|
211 | # called too early, do nothing | |
|
212 | if self.environment is None: | |
|
213 | return | |
|
214 | # Try different template names during conversion. First try to load the | |
|
215 | # template by name with extension added, then try loading the template | |
|
216 | # as if the name is explicitly specified, then try the name as a | |
|
217 | # 'flavor', and lastly just try to load the template by module name. | |
|
218 | module_name = self.__module__.rsplit('.', 1)[-1] | |
|
219 | try_names = [] | |
|
220 | if self.template_file: | |
|
221 | try_names.extend([ | |
|
222 | self.template_file + self.template_extension, | |
|
223 | self.template_file, | |
|
224 | module_name + '_' + self.template_file + self.template_extension, | |
|
225 | ]) | |
|
226 | try_names.append(module_name + self.template_extension) | |
|
227 | for try_name in try_names: | |
|
228 | self.log.debug("Attempting to load template %s", try_name) | |
|
229 | try: | |
|
230 | self.template = self.environment.get_template(try_name) | |
|
231 | except (TemplateNotFound, IOError): | |
|
232 | pass | |
|
233 | except Exception as e: | |
|
234 | self.log.warn("Unexpected exception loading template: %s", try_name, exc_info=True) | |
|
235 | else: | |
|
236 | self.log.info("Loaded template %s", try_name) | |
|
237 | break | |
|
238 | ||
|
107 | ||
|
108 | ||
|
239 | 109 | def from_notebook_node(self, nb, resources=None, **kw): |
|
240 | 110 | """ |
|
241 | 111 | Convert a notebook from a notebook node instance. |
|
242 | ||
|
112 | ||
|
243 | 113 | Parameters |
|
244 | 114 | ---------- |
|
245 | 115 | nb : Notebook node |
|
246 |
resources : dict (**kw) |
|
|
247 |
of additional resources that can be accessed read/write by |
|
|
248 |
preprocessors |
|
|
116 | resources : dict (**kw) | |
|
117 | of additional resources that can be accessed read/write by | |
|
118 | preprocessors. | |
|
249 | 119 | """ |
|
250 | 120 | nb_copy = copy.deepcopy(nb) |
|
251 | 121 | resources = self._init_resources(resources) |
@@ -253,26 +123,20 b' class Exporter(LoggingConfigurable):' | |||
|
253 | 123 | # Preprocess |
|
254 | 124 | nb_copy, resources = self._preprocess(nb_copy, resources) |
|
255 | 125 | |
|
256 | self._load_template() | |
|
257 | ||
|
258 | if self.template is not None: | |
|
259 | output = self.template.render(nb=nb_copy, resources=resources) | |
|
260 | else: | |
|
261 | raise IOError('template file "%s" could not be found' % self.template_file) | |
|
262 | return output, resources | |
|
126 | return nb_copy, resources | |
|
263 | 127 | |
|
264 | 128 | |
|
265 | 129 | def from_filename(self, filename, resources=None, **kw): |
|
266 | 130 | """ |
|
267 | 131 | Convert a notebook from a notebook file. |
|
268 | ||
|
132 | ||
|
269 | 133 | Parameters |
|
270 | 134 | ---------- |
|
271 | 135 | filename : str |
|
272 | 136 | Full filename of the notebook file to open and convert. |
|
273 | 137 | """ |
|
274 | 138 | |
|
275 | #Pull the metadata from the filesystem. | |
|
139 | # Pull the metadata from the filesystem. | |
|
276 | 140 | if resources is None: |
|
277 | 141 | resources = ResourcesDict() |
|
278 | 142 | if not 'metadata' in resources or resources['metadata'] == '': |
@@ -282,16 +146,16 b' class Exporter(LoggingConfigurable):' | |||
|
282 | 146 | resources['metadata']['name'] = notebook_name |
|
283 | 147 | |
|
284 | 148 | modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename)) |
|
285 |
resources['metadata']['modified_date'] = modified_date.strftime( |
|
|
286 | ||
|
149 | resources['metadata']['modified_date'] = modified_date.strftime(text.date_format) | |
|
150 | ||
|
287 | 151 | with io.open(filename) as f: |
|
288 | return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw) | |
|
152 | return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources, **kw) | |
|
289 | 153 | |
|
290 | 154 | |
|
291 | 155 | def from_file(self, file_stream, resources=None, **kw): |
|
292 | 156 | """ |
|
293 | 157 | Convert a notebook from a notebook file. |
|
294 | ||
|
158 | ||
|
295 | 159 | Parameters |
|
296 | 160 | ---------- |
|
297 | 161 | file_stream : file-like object |
@@ -304,10 +168,10 b' class Exporter(LoggingConfigurable):' | |||
|
304 | 168 | """ |
|
305 | 169 | Register a preprocessor. |
|
306 | 170 | Preprocessors are classes that act upon the notebook before it is |
|
307 |
passed into the Jinja templating engine. |
|
|
171 | passed into the Jinja templating engine. preprocessors are also | |
|
308 | 172 | capable of passing additional information to the Jinja |
|
309 | 173 | templating engine. |
|
310 | ||
|
174 | ||
|
311 | 175 | Parameters |
|
312 | 176 | ---------- |
|
313 | 177 | preprocessor : preprocessor |
@@ -317,130 +181,43 b' class Exporter(LoggingConfigurable):' | |||
|
317 | 181 | isclass = isinstance(preprocessor, type) |
|
318 | 182 | constructed = not isclass |
|
319 | 183 | |
|
320 | #Handle preprocessor's registration based on it's type | |
|
184 | # Handle preprocessor's registration based on it's type | |
|
321 | 185 | if constructed and isinstance(preprocessor, py3compat.string_types): |
|
322 | #Preprocessor is a string, import the namespace and recursively call | |
|
323 | #this register_preprocessor method | |
|
186 | # Preprocessor is a string, import the namespace and recursively call | |
|
187 | # this register_preprocessor method | |
|
324 | 188 | preprocessor_cls = import_item(preprocessor) |
|
325 | 189 | return self.register_preprocessor(preprocessor_cls, enabled) |
|
326 | ||
|
190 | ||
|
327 | 191 | if constructed and hasattr(preprocessor, '__call__'): |
|
328 | #Preprocessor is a function, no need to construct it. | |
|
329 | #Register and return the preprocessor. | |
|
192 | # Preprocessor is a function, no need to construct it. | |
|
193 | # Register and return the preprocessor. | |
|
330 | 194 | if enabled: |
|
331 | 195 | preprocessor.enabled = True |
|
332 | 196 | self._preprocessors.append(preprocessor) |
|
333 | 197 | return preprocessor |
|
334 | 198 | |
|
335 | 199 | elif isclass and isinstance(preprocessor, MetaHasTraits): |
|
336 | #Preprocessor is configurable. Make sure to pass in new default for | |
|
337 | #the enabled flag if one was specified. | |
|
200 | # Preprocessor is configurable. Make sure to pass in new default for | |
|
201 | # the enabled flag if one was specified. | |
|
338 | 202 | self.register_preprocessor(preprocessor(parent=self), enabled) |
|
339 | 203 | |
|
340 | 204 | elif isclass: |
|
341 | #Preprocessor is not configurable, construct it | |
|
205 | # Preprocessor is not configurable, construct it | |
|
342 | 206 | self.register_preprocessor(preprocessor(), enabled) |
|
343 | 207 | |
|
344 | 208 | else: |
|
345 | #Preprocessor is an instance of something without a __call__ | |
|
346 | #attribute. | |
|
209 | # Preprocessor is an instance of something without a __call__ | |
|
210 | # attribute. | |
|
347 | 211 | raise TypeError('preprocessor') |
|
348 | 212 | |
|
349 | 213 | |
|
350 | def register_filter(self, name, jinja_filter): | |
|
351 | """ | |
|
352 | Register a filter. | |
|
353 | A filter is a function that accepts and acts on one string. | |
|
354 | The filters are accesible within the Jinja templating engine. | |
|
355 | ||
|
356 | Parameters | |
|
357 | ---------- | |
|
358 | name : str | |
|
359 | name to give the filter in the Jinja engine | |
|
360 | filter : filter | |
|
361 | """ | |
|
362 | if jinja_filter is None: | |
|
363 | raise TypeError('filter') | |
|
364 | isclass = isinstance(jinja_filter, type) | |
|
365 | constructed = not isclass | |
|
366 | ||
|
367 | #Handle filter's registration based on it's type | |
|
368 | if constructed and isinstance(jinja_filter, py3compat.string_types): | |
|
369 | #filter is a string, import the namespace and recursively call | |
|
370 | #this register_filter method | |
|
371 | filter_cls = import_item(jinja_filter) | |
|
372 | return self.register_filter(name, filter_cls) | |
|
373 | ||
|
374 | if constructed and hasattr(jinja_filter, '__call__'): | |
|
375 | #filter is a function, no need to construct it. | |
|
376 | self.environment.filters[name] = jinja_filter | |
|
377 | return jinja_filter | |
|
378 | ||
|
379 | elif isclass and isinstance(jinja_filter, MetaHasTraits): | |
|
380 | #filter is configurable. Make sure to pass in new default for | |
|
381 | #the enabled flag if one was specified. | |
|
382 | filter_instance = jinja_filter(parent=self) | |
|
383 | self.register_filter(name, filter_instance ) | |
|
384 | ||
|
385 | elif isclass: | |
|
386 | #filter is not configurable, construct it | |
|
387 | filter_instance = jinja_filter() | |
|
388 | self.register_filter(name, filter_instance) | |
|
389 | ||
|
390 | else: | |
|
391 | #filter is an instance of something without a __call__ | |
|
392 | #attribute. | |
|
393 | raise TypeError('filter') | |
|
394 | ||
|
395 | ||
|
396 | def _init_template(self): | |
|
397 | """ | |
|
398 | Make sure a template name is specified. If one isn't specified, try to | |
|
399 | build one from the information we know. | |
|
400 | """ | |
|
401 | self._template_file_changed('template_file', self.template_file, self.template_file) | |
|
402 | ||
|
403 | ||
|
404 | def _init_environment(self, extra_loaders=None): | |
|
405 | """ | |
|
406 | Create the Jinja templating environment. | |
|
407 | """ | |
|
408 | here = os.path.dirname(os.path.realpath(__file__)) | |
|
409 | loaders = [] | |
|
410 | if extra_loaders: | |
|
411 | loaders.extend(extra_loaders) | |
|
412 | ||
|
413 | paths = self.template_path | |
|
414 | paths.extend([os.path.join(here, self.default_template_path), | |
|
415 | os.path.join(here, self.template_skeleton_path)]) | |
|
416 | loaders.append(FileSystemLoader(paths)) | |
|
417 | ||
|
418 | self.environment = Environment( | |
|
419 | loader= ChoiceLoader(loaders), | |
|
420 | extensions=JINJA_EXTENSIONS | |
|
421 | ) | |
|
422 | ||
|
423 | #Set special Jinja2 syntax that will not conflict with latex. | |
|
424 | if self.jinja_logic_block_start: | |
|
425 | self.environment.block_start_string = self.jinja_logic_block_start | |
|
426 | if self.jinja_logic_block_end: | |
|
427 | self.environment.block_end_string = self.jinja_logic_block_end | |
|
428 | if self.jinja_variable_block_start: | |
|
429 | self.environment.variable_start_string = self.jinja_variable_block_start | |
|
430 | if self.jinja_variable_block_end: | |
|
431 | self.environment.variable_end_string = self.jinja_variable_block_end | |
|
432 | if self.jinja_comment_block_start: | |
|
433 | self.environment.comment_start_string = self.jinja_comment_block_start | |
|
434 | if self.jinja_comment_block_end: | |
|
435 | self.environment.comment_end_string = self.jinja_comment_block_end | |
|
436 | ||
|
437 | ||
|
438 | 214 | def _init_preprocessors(self): |
|
439 | 215 | """ |
|
440 | 216 | Register all of the preprocessors needed for this exporter, disabled |
|
441 | 217 | unless specified explicitly. |
|
442 | 218 | """ |
|
443 |
self._preprocessors |
|
|
219 | if self._preprocessors is None: | |
|
220 | self._preprocessors = [] | |
|
444 | 221 | |
|
445 | 222 | #Load default preprocessors (not necessarly enabled by default). |
|
446 | 223 | if self.default_preprocessors: |
@@ -451,21 +228,6 b' class Exporter(LoggingConfigurable):' | |||
|
451 | 228 | if self.preprocessors: |
|
452 | 229 | for preprocessor in self.preprocessors: |
|
453 | 230 | self.register_preprocessor(preprocessor, enabled=True) |
|
454 | ||
|
455 | ||
|
456 | def _init_filters(self): | |
|
457 | """ | |
|
458 | Register all of the filters required for the exporter. | |
|
459 | """ | |
|
460 | ||
|
461 | #Add default filters to the Jinja2 environment | |
|
462 | for key, value in default_filters.items(): | |
|
463 | self.register_filter(key, value) | |
|
464 | ||
|
465 | #Load user filters. Overwrite existing filters if need be. | |
|
466 | if self.filters: | |
|
467 | for key, user_filter in self.filters.items(): | |
|
468 | self.register_filter(key, user_filter) | |
|
469 | 231 | |
|
470 | 232 | |
|
471 | 233 | def _init_resources(self, resources): |
@@ -476,7 +238,7 b' class Exporter(LoggingConfigurable):' | |||
|
476 | 238 | if not isinstance(resources, ResourcesDict): |
|
477 | 239 | new_resources = ResourcesDict() |
|
478 | 240 | new_resources.update(resources) |
|
479 |
resources = new_resources |
|
|
241 | resources = new_resources | |
|
480 | 242 | |
|
481 | 243 | #Make sure the metadata extension exists in resources |
|
482 | 244 | if 'metadata' in resources: |
@@ -484,29 +246,28 b' class Exporter(LoggingConfigurable):' | |||
|
484 | 246 | resources['metadata'] = ResourcesDict(resources['metadata']) |
|
485 | 247 | else: |
|
486 | 248 | resources['metadata'] = ResourcesDict() |
|
487 |
if not resources['metadata']['name']: |
|
|
249 | if not resources['metadata']['name']: | |
|
488 | 250 | resources['metadata']['name'] = 'Notebook' |
|
489 | 251 | |
|
490 | 252 | #Set the output extension |
|
491 | 253 | resources['output_extension'] = self.file_extension |
|
492 | 254 | return resources |
|
493 | ||
|
255 | ||
|
494 | 256 | |
|
495 | 257 | def _preprocess(self, nb, resources): |
|
496 | 258 | """ |
|
497 | 259 | Preprocess the notebook before passing it into the Jinja engine. |
|
498 |
To preprocess the notebook is to apply all of the |
|
|
499 | ||
|
260 | To preprocess the notebook is to apply all of the | |
|
261 | ||
|
500 | 262 | Parameters |
|
501 | 263 | ---------- |
|
502 | 264 | nb : notebook node |
|
503 | 265 | notebook that is being exported. |
|
504 | 266 | resources : a dict of additional resources that |
|
505 | 267 | can be accessed read/write by preprocessors |
|
506 | and filters. | |
|
507 | 268 | """ |
|
508 | ||
|
509 |
# Do a copy.deepcopy first, |
|
|
269 | ||
|
270 | # Do a copy.deepcopy first, | |
|
510 | 271 | # we are never safe enough with what the preprocessors could do. |
|
511 | 272 | nbc = copy.deepcopy(nb) |
|
512 | 273 | resc = copy.deepcopy(resources) |
@@ -19,13 +19,13 b' from IPython.utils.traitlets import Unicode, List' | |||
|
19 | 19 | from IPython.nbconvert import preprocessors |
|
20 | 20 | from IPython.config import Config |
|
21 | 21 | |
|
22 | from .exporter import Exporter | |
|
22 | from .templateexporter import TemplateExporter | |
|
23 | 23 | |
|
24 | 24 | #----------------------------------------------------------------------------- |
|
25 | 25 | # Classes |
|
26 | 26 | #----------------------------------------------------------------------------- |
|
27 | 27 | |
|
28 | class HTMLExporter(Exporter): | |
|
28 | class HTMLExporter(TemplateExporter): | |
|
29 | 29 | """ |
|
30 | 30 | Exports a basic HTML document. This exporter assists with the export of |
|
31 | 31 | HTML. Inherit from it if you are writing your own HTML template and need |
@@ -46,7 +46,10 b' class HTMLExporter(Exporter):' | |||
|
46 | 46 | c = Config({ |
|
47 | 47 | 'CSSHTMLHeaderPreprocessor':{ |
|
48 | 48 | 'enabled':True |
|
49 |
} |
|
|
49 | }, | |
|
50 | 'HighlightMagicsPreprocessor': { | |
|
51 | 'enabled':True | |
|
52 | } | |
|
50 | 53 | }) |
|
51 | 54 | c.merge(super(HTMLExporter,self).default_config) |
|
52 | 55 | return c |
@@ -24,13 +24,13 b' from IPython.utils.traitlets import Unicode, List' | |||
|
24 | 24 | from IPython.config import Config |
|
25 | 25 | |
|
26 | 26 | from IPython.nbconvert import filters, preprocessors |
|
27 | from .exporter import Exporter | |
|
27 | from .templateexporter import TemplateExporter | |
|
28 | 28 | |
|
29 | 29 | #----------------------------------------------------------------------------- |
|
30 | 30 | # Classes and functions |
|
31 | 31 | #----------------------------------------------------------------------------- |
|
32 | 32 | |
|
33 | class LatexExporter(Exporter): | |
|
33 | class LatexExporter(TemplateExporter): | |
|
34 | 34 | """ |
|
35 | 35 | Exports to a Latex template. Inherit from this class if your template is |
|
36 | 36 | LaTeX based and you need custom tranformers/filters. Inherit from it if |
@@ -45,7 +45,7 b' class LatexExporter(Exporter):' | |||
|
45 | 45 | help="Extension of the file that should be written to disk") |
|
46 | 46 | |
|
47 | 47 | default_template = Unicode('article', config=True, help="""Template of the |
|
48 |
data format to use. I.E. ' |
|
|
48 | data format to use. I.E. 'article' or 'report'""") | |
|
49 | 49 | |
|
50 | 50 | #Latex constants |
|
51 | 51 | default_template_path = Unicode( |
@@ -85,6 +85,9 b' class LatexExporter(Exporter):' | |||
|
85 | 85 | }, |
|
86 | 86 | 'SphinxPreprocessor': { |
|
87 | 87 | 'enabled':True |
|
88 | }, | |
|
89 | 'HighlightMagicsPreprocessor': { | |
|
90 | 'enabled':True | |
|
88 | 91 | } |
|
89 | 92 | }) |
|
90 | 93 | c.merge(super(LatexExporter,self).default_config) |
@@ -13,15 +13,16 b' Exporter that will export your ipynb to Markdown.' | |||
|
13 | 13 | # Imports |
|
14 | 14 | #----------------------------------------------------------------------------- |
|
15 | 15 | |
|
16 | from IPython.config import Config | |
|
16 | 17 | from IPython.utils.traitlets import Unicode |
|
17 | 18 | |
|
18 | from .exporter import Exporter | |
|
19 | from .templateexporter import TemplateExporter | |
|
19 | 20 | |
|
20 | 21 | #----------------------------------------------------------------------------- |
|
21 | 22 | # Classes |
|
22 | 23 | #----------------------------------------------------------------------------- |
|
23 | 24 | |
|
24 | class MarkdownExporter(Exporter): | |
|
25 | class MarkdownExporter(TemplateExporter): | |
|
25 | 26 | """ |
|
26 | 27 | Exports to a markdown document (.md) |
|
27 | 28 | """ |
@@ -29,3 +30,9 b' class MarkdownExporter(Exporter):' | |||
|
29 | 30 | file_extension = Unicode( |
|
30 | 31 | 'md', config=True, |
|
31 | 32 | help="Extension of the file that should be written to disk") |
|
33 | ||
|
34 | @property | |
|
35 | def default_config(self): | |
|
36 | c = Config({'ExtractOutputPreprocessor':{'enabled':True}}) | |
|
37 | c.merge(super(MarkdownExporter,self).default_config) | |
|
38 | return c |
@@ -15,13 +15,13 b' Python exporter which exports Notebook code into a PY file.' | |||
|
15 | 15 | |
|
16 | 16 | from IPython.utils.traitlets import Unicode |
|
17 | 17 | |
|
18 | from .exporter import Exporter | |
|
18 | from .templateexporter import TemplateExporter | |
|
19 | 19 | |
|
20 | 20 | #----------------------------------------------------------------------------- |
|
21 | 21 | # Classes |
|
22 | 22 | #----------------------------------------------------------------------------- |
|
23 | 23 | |
|
24 | class PythonExporter(Exporter): | |
|
24 | class PythonExporter(TemplateExporter): | |
|
25 | 25 | """ |
|
26 | 26 | Exports a Python code file. |
|
27 | 27 | """ |
@@ -16,13 +16,13 b' Exporter for exporting notebooks to restructured text.' | |||
|
16 | 16 | from IPython.utils.traitlets import Unicode |
|
17 | 17 | from IPython.config import Config |
|
18 | 18 | |
|
19 | from .exporter import Exporter | |
|
19 | from .templateexporter import TemplateExporter | |
|
20 | 20 | |
|
21 | 21 | #----------------------------------------------------------------------------- |
|
22 | 22 | # Classes |
|
23 | 23 | #----------------------------------------------------------------------------- |
|
24 | 24 | |
|
25 | class RSTExporter(Exporter): | |
|
25 | class RSTExporter(TemplateExporter): | |
|
26 | 26 | """ |
|
27 | 27 | Exports restructured text documents. |
|
28 | 28 | """ |
@@ -19,13 +19,13 b' from IPython.utils.traitlets import Unicode' | |||
|
19 | 19 | from IPython.nbconvert import preprocessors |
|
20 | 20 | from IPython.config import Config |
|
21 | 21 | |
|
22 | from .exporter import Exporter | |
|
22 | from .templateexporter import TemplateExporter | |
|
23 | 23 | |
|
24 | 24 | #----------------------------------------------------------------------------- |
|
25 | 25 | # Classes |
|
26 | 26 | #----------------------------------------------------------------------------- |
|
27 | 27 | |
|
28 | class SlidesExporter(Exporter): | |
|
28 | class SlidesExporter(TemplateExporter): | |
|
29 | 29 | """ |
|
30 | 30 | Exports slides |
|
31 | 31 | """ |
@@ -47,6 +47,9 b' class SlidesExporter(Exporter):' | |||
|
47 | 47 | 'RevealHelpPreprocessor':{ |
|
48 | 48 | 'enabled':True, |
|
49 | 49 | }, |
|
50 | 'HighlightMagicsPreprocessor': { | |
|
51 | 'enabled':True | |
|
52 | } | |
|
50 | 53 | }) |
|
51 | 54 | c.merge(super(SlidesExporter,self).default_config) |
|
52 | 55 | return c |
@@ -27,4 +27,4 b' class ExportersTestsBase(TestsBase):' | |||
|
27 | 27 | |
|
28 | 28 | def _get_notebook(self): |
|
29 | 29 | return os.path.join(self._get_files_path(), 'notebook2.ipynb') |
|
30 | No newline at end of file | |
|
30 |
@@ -99,4 +99,4 b' class TestExport(ExportersTestsBase):' | |||
|
99 | 99 | (output, resources) = export(None, self._get_notebook()) |
|
100 | 100 | except TypeError: |
|
101 | 101 | pass |
|
102 | No newline at end of file | |
|
102 |
@@ -17,7 +17,7 b' Module with tests for exporter.py' | |||
|
17 | 17 | from IPython.config import Config |
|
18 | 18 | |
|
19 | 19 | from .base import ExportersTestsBase |
|
20 |
from . |
|
|
20 | from ...preprocessors.base import Preprocessor | |
|
21 | 21 | from ..exporter import Exporter |
|
22 | 22 | |
|
23 | 23 | |
@@ -25,84 +25,35 b' from ..exporter import Exporter' | |||
|
25 | 25 | # Class |
|
26 | 26 | #----------------------------------------------------------------------------- |
|
27 | 27 | |
|
28 | class PizzaPreprocessor(Preprocessor): | |
|
29 | """Simple preprocessor that adds a 'pizza' entry to the NotebookNode. Used | |
|
30 | to test Exporter. | |
|
31 | """ | |
|
32 | ||
|
33 | def preprocess(self, nb, resources): | |
|
34 | nb['pizza'] = 'cheese' | |
|
35 | return nb, resources | |
|
36 | ||
|
37 | ||
|
28 | 38 | class TestExporter(ExportersTestsBase): |
|
29 | 39 | """Contains test functions for exporter.py""" |
|
30 | 40 | |
|
31 | 41 | |
|
32 | 42 | def test_constructor(self): |
|
33 | """ | |
|
34 | Can an Exporter be constructed? | |
|
35 | """ | |
|
43 | """Can an Exporter be constructed?""" | |
|
36 | 44 | Exporter() |
|
37 | 45 | |
|
38 | 46 | |
|
39 | 47 | def test_export(self): |
|
40 | """ | |
|
41 | Can an Exporter export something? | |
|
42 | """ | |
|
43 | exporter = self._make_exporter() | |
|
44 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
45 | assert len(output) > 0 | |
|
46 | ||
|
47 | ||
|
48 | def test_extract_outputs(self): | |
|
49 | """ | |
|
50 | If the ExtractOutputPreprocessor is enabled, are outputs extracted? | |
|
51 | """ | |
|
52 | config = Config({'ExtractOutputPreprocessor': {'enabled': True}}) | |
|
53 | exporter = self._make_exporter(config=config) | |
|
54 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
55 | assert resources is not None | |
|
56 | assert isinstance(resources['outputs'], dict) | |
|
57 | assert len(resources['outputs']) > 0 | |
|
58 | ||
|
59 | ||
|
60 | def test_preprocessor_class(self): | |
|
61 | """ | |
|
62 | Can a preprocessor be added to the preprocessors list by class type? | |
|
63 | """ | |
|
64 | config = Config({'Exporter': {'preprocessors': [CheesePreprocessor]}}) | |
|
65 | exporter = self._make_exporter(config=config) | |
|
66 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
67 | assert resources is not None | |
|
68 | assert resources['cheese'] == 'real' | |
|
69 | ||
|
70 | ||
|
71 | def test_preprocessor_instance(self): | |
|
72 | """ | |
|
73 | Can a preprocessor be added to the preprocessors list by instance? | |
|
74 | """ | |
|
75 | config = Config({'Exporter': {'preprocessors': [CheesePreprocessor()]}}) | |
|
76 | exporter = self._make_exporter(config=config) | |
|
77 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
78 | assert resources is not None | |
|
79 | assert resources['cheese'] == 'real' | |
|
80 | ||
|
81 | ||
|
82 | def test_preprocessor_dottedobjectname(self): | |
|
83 | """ | |
|
84 | Can a preprocessor be added to the preprocessors list by dotted object name? | |
|
85 | """ | |
|
86 | config = Config({'Exporter': {'preprocessors': ['IPython.nbconvert.exporters.tests.cheese.CheesePreprocessor']}}) | |
|
87 | exporter = self._make_exporter(config=config) | |
|
88 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
89 | assert resources is not None | |
|
90 | assert resources['cheese'] == 'real' | |
|
91 | ||
|
92 | ||
|
93 | def test_preprocessor_via_method(self): | |
|
94 | """ | |
|
95 | Can a preprocessor be added via the Exporter convenience method? | |
|
96 | """ | |
|
97 | exporter = self._make_exporter() | |
|
98 | exporter.register_preprocessor(CheesePreprocessor, enabled=True) | |
|
99 | (output, resources) = exporter.from_filename(self._get_notebook()) | |
|
100 | assert resources is not None | |
|
101 | assert resources['cheese'] == 'real' | |
|
102 | ||
|
103 | ||
|
104 | def _make_exporter(self, config=None): | |
|
105 | #Create the exporter instance, make sure to set a template name since | |
|
106 | #the base Exporter doesn't have a template associated with it. | |
|
107 | exporter = Exporter(config=config, template_file='python') | |
|
108 | return exporter | |
|
48 | """Can an Exporter export something?""" | |
|
49 | exporter = Exporter() | |
|
50 | (notebook, resources) = exporter.from_filename(self._get_notebook()) | |
|
51 | assert isinstance(notebook, dict) | |
|
52 | ||
|
53 | ||
|
54 | def test_preprocessor(self): | |
|
55 | """Do preprocessors work?""" | |
|
56 | config = Config({'Exporter': {'preprocessors': [PizzaPreprocessor()]}}) | |
|
57 | exporter = Exporter(config=config) | |
|
58 | (notebook, resources) = exporter.from_filename(self._get_notebook()) | |
|
59 | self.assertEqual(notebook['pizza'], 'cheese') |
@@ -44,18 +44,18 b' class TestLatexExporter(ExportersTestsBase):' | |||
|
44 | 44 | @onlyif_cmds_exist('pandoc') |
|
45 | 45 | def test_export_book(self): |
|
46 | 46 | """ |
|
47 |
Can a LatexExporter export using ' |
|
|
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 | 50 | assert len(output) > 0 |
|
51 | 51 | |
|
52 | 52 | |
|
53 | 53 | @onlyif_cmds_exist('pandoc') |
|
54 | 54 | def test_export_basic(self): |
|
55 | 55 | """ |
|
56 |
Can a LatexExporter export using ' |
|
|
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 | 59 | assert len(output) > 0 |
|
60 | 60 | |
|
61 | 61 |
@@ -1,6 +1,7 b'' | |||
|
1 | 1 | from .ansi import * |
|
2 | from .citation import * | |
|
2 | 3 | from .datatypefilter import * |
|
3 | 4 | from .highlight import * |
|
4 | 5 | from .latex import * |
|
5 | 6 | from .markdown import * |
|
6 | from .strings import * No newline at end of file | |
|
7 | from .strings import * |
@@ -14,7 +14,7 b' from within Jinja templates.' | |||
|
14 | 14 | # Imports |
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | |
|
17 |
from |
|
|
17 | from pygments import highlight as pygements_highlight | |
|
18 | 18 | from pygments.lexers import get_lexer_by_name |
|
19 | 19 | from pygments.formatters import HtmlFormatter |
|
20 | 20 | from pygments.formatters import LatexFormatter |
@@ -38,51 +38,73 b' __all__ = [' | |||
|
38 | 38 | ] |
|
39 | 39 | |
|
40 | 40 | |
|
41 | def highlight2html(source, language='ipython'): | |
|
41 | def highlight2html(source, language='ipython', metadata=None): | |
|
42 | 42 | """ |
|
43 | 43 | Return a syntax-highlighted version of the input source as html output. |
|
44 | ||
|
44 | ||
|
45 | 45 | Parameters |
|
46 | 46 | ---------- |
|
47 | 47 | source : str |
|
48 |
|
|
|
48 | source of the cell to highlight | |
|
49 | 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 | ||
|
53 | return _pygment_highlight(source, HtmlFormatter(), language) | |
|
54 | ||
|
55 | return _pygment_highlight(source, HtmlFormatter(), language, metadata) | |
|
54 | 56 | |
|
55 | 57 | |
|
56 | def highlight2latex(source, language='ipython'): | |
|
58 | def highlight2latex(source, language='ipython', metadata=None, strip_verbatim=False): | |
|
57 | 59 | """ |
|
58 | 60 | Return a syntax-highlighted version of the input source as latex output. |
|
59 | ||
|
61 | ||
|
60 | 62 | Parameters |
|
61 | 63 | ---------- |
|
62 | 64 | source : str |
|
63 |
|
|
|
65 | source of the cell to highlight | |
|
64 | 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 | 84 | Return a syntax-highlighted version of the input source |
|
73 | ||
|
85 | ||
|
74 | 86 | Parameters |
|
75 | 87 | ---------- |
|
76 | 88 | source : str |
|
77 |
|
|
|
89 | source of the cell to highlight | |
|
78 | 90 | output_formatter : Pygments formatter |
|
79 | 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 | 105 | if language == 'ipython': |
|
84 | 106 | lexer = IPythonLexer() |
|
85 | 107 | else: |
|
86 | 108 | lexer = get_lexer_by_name(language, stripall=True) |
|
87 | 109 | |
|
88 |
return pygements_highlight(source, lexer, output_formatter) |
|
|
110 | return pygements_highlight(source, lexer, output_formatter) |
@@ -19,6 +19,7 b' templates.' | |||
|
19 | 19 | import os |
|
20 | 20 | import re |
|
21 | 21 | import textwrap |
|
22 | from urllib2 import quote | |
|
22 | 23 | from xml.etree import ElementTree |
|
23 | 24 | |
|
24 | 25 | from IPython.core.interactiveshell import InteractiveShell |
@@ -38,6 +39,8 b' __all__ = [' | |||
|
38 | 39 | 'get_lines', |
|
39 | 40 | 'ipython2python', |
|
40 | 41 | 'posix_path', |
|
42 | 'path2url', | |
|
43 | 'add_prompts' | |
|
41 | 44 | ] |
|
42 | 45 | |
|
43 | 46 | |
@@ -80,7 +83,7 b' def add_anchor(html):' | |||
|
80 | 83 | |
|
81 | 84 | For use in heading cells |
|
82 | 85 | """ |
|
83 | h = ElementTree.fromstring(py3compat.cast_bytes_py2(html)) | |
|
86 | h = ElementTree.fromstring(py3compat.cast_bytes_py2(html, encoding='utf-8')) | |
|
84 | 87 | link = html2text(h).replace(' ', '-') |
|
85 | 88 | h.set('id', link) |
|
86 | 89 | a = ElementTree.Element("a", {"class" : "anchor-link", "href" : "#" + link}) |
@@ -93,6 +96,16 b' def add_anchor(html):' | |||
|
93 | 96 | return py3compat.decode(ElementTree.tostring(h), 'utf-8') |
|
94 | 97 | |
|
95 | 98 | |
|
99 | def add_prompts(code, first='>>> ', cont='... '): | |
|
100 | """Add prompts to code snippets""" | |
|
101 | new_code = [] | |
|
102 | code_list = code.split('\n') | |
|
103 | new_code.append(first + code_list[0]) | |
|
104 | for line in code_list[1:]: | |
|
105 | new_code.append(cont + line) | |
|
106 | return '\n'.join(new_code) | |
|
107 | ||
|
108 | ||
|
96 | 109 | def strip_dollars(text): |
|
97 | 110 | """ |
|
98 | 111 | Remove all dollar symbols from text |
@@ -181,3 +194,8 b' def posix_path(path):' | |||
|
181 | 194 | if os.path.sep != '/': |
|
182 | 195 | return path.replace(os.path.sep, '/') |
|
183 | 196 | return path |
|
197 | ||
|
198 | def path2url(path): | |
|
199 | """Turn a file path into a URL""" | |
|
200 | parts = path.split(os.path.sep) | |
|
201 | return '/'.join(quote(part) for part in parts) |
@@ -39,7 +39,7 b' class TestAnsi(TestsBase):' | |||
|
39 | 39 | 'hello' : 'hello'} |
|
40 | 40 | |
|
41 | 41 | for inval, outval in correct_outputs.items(): |
|
42 |
|
|
|
42 | self._try_strip_ansi(inval, outval) | |
|
43 | 43 | |
|
44 | 44 | |
|
45 | 45 | def _try_strip_ansi(self, inval, outval): |
@@ -58,7 +58,7 b' class TestAnsi(TestsBase):' | |||
|
58 | 58 | 'hello' : 'hello'} |
|
59 | 59 | |
|
60 | 60 | for inval, outval in correct_outputs.items(): |
|
61 |
|
|
|
61 | self._try_ansi2html(inval, outval) | |
|
62 | 62 | |
|
63 | 63 | |
|
64 | 64 | def _try_ansi2html(self, inval, outval): |
@@ -77,7 +77,7 b' class TestAnsi(TestsBase):' | |||
|
77 | 77 | 'hello' : 'hello'} |
|
78 | 78 | |
|
79 | 79 | for inval, outval in correct_outputs.items(): |
|
80 |
|
|
|
80 | self._try_ansi2latex(inval, outval) | |
|
81 | 81 | |
|
82 | 82 | |
|
83 | 83 | def _try_ansi2latex(self, inval, outval): |
@@ -49,13 +49,13 b' class TestHighlight(TestsBase):' | |||
|
49 | 49 | def test_highlight2html(self): |
|
50 | 50 | """highlight2html test""" |
|
51 | 51 | for index, test in enumerate(self.tests): |
|
52 |
|
|
|
52 | self._try_highlight(highlight2html, test, self.tokens[index]) | |
|
53 | 53 | |
|
54 | 54 | |
|
55 | 55 | def test_highlight2latex(self): |
|
56 | 56 | """highlight2latex test""" |
|
57 | 57 | for index, test in enumerate(self.tests): |
|
58 |
|
|
|
58 | self._try_highlight(highlight2latex, test, self.tokens[index]) | |
|
59 | 59 | |
|
60 | 60 | |
|
61 | 61 | def _try_highlight(self, method, test, tokens): |
@@ -35,7 +35,7 b' class TestLatex(TestsBase):' | |||
|
35 | 35 | ('','')] |
|
36 | 36 | |
|
37 | 37 | for test in tests: |
|
38 |
|
|
|
38 | self._try_escape_latex(test[0], test[1]) | |
|
39 | 39 | |
|
40 | 40 | |
|
41 | 41 | def _try_escape_latex(self, test, result): |
@@ -56,7 +56,7 b' class TestLatex(TestsBase):' | |||
|
56 | 56 | ('','')] |
|
57 | 57 | |
|
58 | 58 | for test in tests: |
|
59 |
|
|
|
59 | self._try_strip_math_space(test[0], test[1]) | |
|
60 | 60 | |
|
61 | 61 | |
|
62 | 62 | def _try_strip_math_space(self, test, result): |
@@ -61,14 +61,14 b' class TestMarkdown(TestsBase):' | |||
|
61 | 61 | def test_markdown2latex(self): |
|
62 | 62 | """markdown2latex test""" |
|
63 | 63 | for index, test in enumerate(self.tests): |
|
64 |
|
|
|
64 | self._try_markdown(markdown2latex, test, self.tokens[index]) | |
|
65 | 65 | |
|
66 | 66 | |
|
67 | 67 | @dec.onlyif_cmds_exist('pandoc') |
|
68 | 68 | def test_markdown2html(self): |
|
69 | 69 | """markdown2html test""" |
|
70 | 70 | for index, test in enumerate(self.tests): |
|
71 |
|
|
|
71 | self._try_markdown(markdown2html, test, self.tokens[index]) | |
|
72 | 72 | |
|
73 | 73 | |
|
74 | 74 | @dec.onlyif_cmds_exist('pandoc') |
@@ -81,7 +81,7 b' class TestMarkdown(TestsBase):' | |||
|
81 | 81 | tokens[1] = r'\*\*test' |
|
82 | 82 | |
|
83 | 83 | for index, test in enumerate(self.tests): |
|
84 |
|
|
|
84 | self._try_markdown(markdown2rst, test, tokens[index]) | |
|
85 | 85 | |
|
86 | 86 | |
|
87 | 87 | def _try_markdown(self, method, test, tokens): |
@@ -15,10 +15,10 b' Module with tests for Strings' | |||
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | import os |
|
17 | 17 | |
|
18 | from IPython.testing import decorators as dec | |
|
19 | 18 | from ...tests.base import TestsBase |
|
20 | 19 | from ..strings import (wrap_text, html2text, add_anchor, strip_dollars, |
|
21 | 20 | strip_files_prefix, get_lines, comment_lines, ipython2python, posix_path, |
|
21 | add_prompts | |
|
22 | 22 | ) |
|
23 | 23 | |
|
24 | 24 | |
@@ -36,7 +36,7 b' class TestStrings(TestsBase):' | |||
|
36 | 36 | As if the strings were thine, shouldst know of this. |
|
37 | 37 | """ |
|
38 | 38 | for length in [30,5,1]: |
|
39 |
|
|
|
39 | self._confirm_wrap_text(test_text, length) | |
|
40 | 40 | |
|
41 | 41 | |
|
42 | 42 | def _confirm_wrap_text(self, text, length): |
@@ -73,7 +73,7 b' class TestStrings(TestsBase):' | |||
|
73 | 73 | ('Hello', 'Hello'), |
|
74 | 74 | ('W$o$rld', 'W$o$rld')] |
|
75 | 75 | for test in tests: |
|
76 |
|
|
|
76 | self._try_strip_dollars(test[0], test[1]) | |
|
77 | 77 | |
|
78 | 78 | |
|
79 | 79 | def _try_strip_dollars(self, test, result): |
@@ -89,7 +89,7 b' class TestStrings(TestsBase):' | |||
|
89 | 89 | ('My files are in `files/`', 'My files are in `files/`'), |
|
90 | 90 | ('<a href="files/test.html">files/test.html</a>', '<a href="test.html">files/test.html</a>')] |
|
91 | 91 | for test in tests: |
|
92 |
|
|
|
92 | self._try_files_prefix(test[0], test[1]) | |
|
93 | 93 | |
|
94 | 94 | |
|
95 | 95 | def _try_files_prefix(self, test, result): |
@@ -121,8 +121,15 b' class TestStrings(TestsBase):' | |||
|
121 | 121 | ignore_spaces=True, ignore_newlines=True) |
|
122 | 122 | |
|
123 | 123 | def test_posix_path(self): |
|
124 | """posix_path test""" | |
|
124 | 125 | path_list = ['foo', 'bar'] |
|
125 | 126 | expected = '/'.join(path_list) |
|
126 | 127 | native = os.path.join(*path_list) |
|
127 | 128 | filtered = posix_path(native) |
|
128 | 129 | self.assertEqual(filtered, expected) |
|
130 | ||
|
131 | def test_add_prompts(self): | |
|
132 | """add_prompts test""" | |
|
133 | text1 = """for i in range(10):\n i += 1\n print i""" | |
|
134 | text2 = """>>> for i in range(10):\n... i += 1\n... print i""" | |
|
135 | self.assertEqual(text2, add_prompts(text1)) |
@@ -25,6 +25,7 b' import glob' | |||
|
25 | 25 | |
|
26 | 26 | # From IPython |
|
27 | 27 | from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags |
|
28 | from IPython.core.profiledir import ProfileDir | |
|
28 | 29 | from IPython.config import catch_config_error, Configurable |
|
29 | 30 | from IPython.utils.traitlets import ( |
|
30 | 31 | Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum, |
@@ -58,12 +59,11 b' nbconvert_aliases = {}' | |||
|
58 | 59 | nbconvert_aliases.update(base_aliases) |
|
59 | 60 | nbconvert_aliases.update({ |
|
60 | 61 | 'to' : 'NbConvertApp.export_format', |
|
61 | 'template' : 'Exporter.template_file', | |
|
62 | 'template' : 'TemplateExporter.template_file', | |
|
62 | 63 | 'writer' : 'NbConvertApp.writer_class', |
|
63 | 64 | 'post': 'NbConvertApp.postprocessor_class', |
|
64 | 65 | 'output': 'NbConvertApp.output_base', |
|
65 |
' |
|
|
66 | 'slide-notes': 'RevealHelpTransformer.speaker_notes' | |
|
66 | 'reveal-prefix': 'RevealHelpPreprocessor.url_prefix', | |
|
67 | 67 | }) |
|
68 | 68 | |
|
69 | 69 | nbconvert_flags = {} |
@@ -87,12 +87,13 b' class NbConvertApp(BaseIPythonApplication):' | |||
|
87 | 87 | return logging.INFO |
|
88 | 88 | |
|
89 | 89 | def _classes_default(self): |
|
90 | classes = [NbConvertBase] | |
|
90 | classes = [NbConvertBase, ProfileDir] | |
|
91 | 91 | for pkg in (exporters, preprocessors, writers): |
|
92 | 92 | for name in dir(pkg): |
|
93 | 93 | cls = getattr(pkg, name) |
|
94 | 94 | if isinstance(cls, type) and issubclass(cls, Configurable): |
|
95 | 95 | classes.append(cls) |
|
96 | ||
|
96 | 97 | return classes |
|
97 | 98 | |
|
98 | 99 | description = Unicode( |
@@ -1,3 +1,8 b'' | |||
|
1 | 1 | from .base import PostProcessorBase |
|
2 | 2 | from .pdf import PDFPostProcessor |
|
3 | from .serve import ServePostProcessor | |
|
3 | ||
|
4 | # protect against unavailable tornado | |
|
5 | try: | |
|
6 | from .serve import ServePostProcessor | |
|
7 | except ImportError: | |
|
8 | pass |
@@ -26,35 +26,107 b' from .base import PostProcessorBase' | |||
|
26 | 26 | class PDFPostProcessor(PostProcessorBase): |
|
27 | 27 | """Writer designed to write to PDF files""" |
|
28 | 28 | |
|
29 |
|
|
|
29 | latex_count = Integer(3, config=True, help=""" | |
|
30 | 30 | How many times pdflatex will be called. |
|
31 | 31 | """) |
|
32 | 32 | |
|
33 | command = List(["pdflatex", "{filename}"], config=True, help=""" | |
|
33 | latex_command = List(["pdflatex", "{filename}"], config=True, help=""" | |
|
34 | 34 | Shell command used to compile PDF.""") |
|
35 | 35 | |
|
36 | bib_command = List(["bibtex", "{filename}"], config=True, help=""" | |
|
37 | Shell command used to run bibtex.""") | |
|
38 | ||
|
36 | 39 | verbose = Bool(False, config=True, help=""" |
|
37 | 40 | Whether or not to display the output of the compile call. |
|
38 | 41 | """) |
|
39 | 42 | |
|
40 | def postprocess(self, input): | |
|
41 | """ | |
|
42 | Consume and write Jinja output a PDF. | |
|
43 | See files.py for more... | |
|
44 | """ | |
|
45 | command = [c.format(filename=input) for c in self.command] | |
|
46 | self.log.info("Building PDF: %s", command) | |
|
47 | with open(os.devnull, 'rb') as null: | |
|
48 | stdout = subprocess.PIPE if not self.verbose else None | |
|
49 | for index in range(self.iteration_count): | |
|
50 | p = subprocess.Popen(command, stdout=stdout, stdin=null) | |
|
51 | out, err = p.communicate() | |
|
52 | if p.returncode: | |
|
53 | if self.verbose: | |
|
54 | # verbose means I didn't capture stdout with PIPE, | |
|
55 | # so it's already been displayed and `out` is None. | |
|
56 | out = u'' | |
|
57 | else: | |
|
58 | out = out.decode('utf-8', 'replace') | |
|
59 | self.log.critical(u"PDF conversion failed: %s\n%s", command, out) | |
|
60 | return | |
|
43 | temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'], | |
|
44 | config=True, help=""" | |
|
45 | Filename extensions of temp files to remove after running. | |
|
46 | """) | |
|
47 | ||
|
48 | def run_command(self, command_list, filename, count, log_function): | |
|
49 | """Run command_list count times. | |
|
50 | ||
|
51 | Parameters | |
|
52 | ---------- | |
|
53 | command_list : list | |
|
54 | A list of args to provide to Popen. Each element of this | |
|
55 | list will be interpolated with the filename to convert. | |
|
56 | filename : unicode | |
|
57 | The name of the file to convert. | |
|
58 | count : int | |
|
59 | How many times to run the command. | |
|
60 | ||
|
61 | Returns | |
|
62 | ------- | |
|
63 | continue : bool | |
|
64 | A boolean indicating if the command was successful (True) | |
|
65 | or failed (False). | |
|
66 | """ | |
|
67 | command = [c.format(filename=filename) for c in command_list] | |
|
68 | times = 'time' if count == 1 else 'times' | |
|
69 | self.log.info("Running %s %i %s: %s", command_list[0], count, times, command) | |
|
70 | with open(os.devnull, 'rb') as null: | |
|
71 | stdout = subprocess.PIPE if not self.verbose else None | |
|
72 | for index in range(count): | |
|
73 | p = subprocess.Popen(command, stdout=stdout, stdin=null) | |
|
74 | out, err = p.communicate() | |
|
75 | if p.returncode: | |
|
76 | if self.verbose: | |
|
77 | # verbose means I didn't capture stdout with PIPE, | |
|
78 | # so it's already been displayed and `out` is None. | |
|
79 | out = u'' | |
|
80 | else: | |
|
81 | out = out.decode('utf-8', 'replace') | |
|
82 | log_function(command, out) | |
|
83 | return False # failure | |
|
84 | return True # success | |
|
85 | ||
|
86 | def run_latex(self, filename): | |
|
87 | """Run pdflatex self.latex_count times.""" | |
|
88 | ||
|
89 | def log_error(command, out): | |
|
90 | self.log.critical(u"%s failed: %s\n%s", command[0], command, out) | |
|
91 | ||
|
92 | return self.run_command(self.latex_command, filename, | |
|
93 | self.latex_count, log_error) | |
|
94 | ||
|
95 | def run_bib(self, filename): | |
|
96 | """Run bibtex self.latex_count times.""" | |
|
97 | filename = os.path.splitext(filename)[0] | |
|
98 | ||
|
99 | def log_error(command, out): | |
|
100 | self.log.warn('%s had problems, most likely because there were no citations', | |
|
101 | command[0]) | |
|
102 | self.log.debug(u"%s output: %s\n%s", command[0], command, out) | |
|
103 | ||
|
104 | return self.run_command(self.bib_command, filename, 1, log_error) | |
|
105 | ||
|
106 | def clean_temp_files(self, filename): | |
|
107 | """Remove temporary files created by pdflatex/bibtext.""" | |
|
108 | self.log.info("Removing temporary LaTeX files") | |
|
109 | filename = os.path.splitext(filename)[0] | |
|
110 | for ext in self.temp_file_exts: | |
|
111 | try: | |
|
112 | os.remove(filename+ext) | |
|
113 | except OSError: | |
|
114 | pass | |
|
115 | ||
|
116 | def postprocess(self, filename): | |
|
117 | """Build a PDF by running pdflatex and bibtex""" | |
|
118 | self.log.info("Building PDF") | |
|
119 | cont = self.run_latex(filename) | |
|
120 | if cont: | |
|
121 | cont = self.run_bib(filename) | |
|
122 | else: | |
|
123 | self.clean_temp_files(filename) | |
|
124 | return | |
|
125 | if cont: | |
|
126 | cont = self.run_latex(filename) | |
|
127 | self.clean_temp_files(filename) | |
|
128 | filename = os.path.splitext(filename)[0] | |
|
129 | if os.path.isfile(filename+'.pdf'): | |
|
130 | self.log.info('PDF successfully created') | |
|
131 | return | |
|
132 |
@@ -1,6 +1,4 b'' | |||
|
1 | """ | |
|
2 | Contains postprocessor for serving nbconvert output. | |
|
3 | """ | |
|
1 | """PostProcessor for serving reveal.js HTML slideshows.""" | |
|
4 | 2 | #----------------------------------------------------------------------------- |
|
5 | 3 | #Copyright (c) 2013, the IPython Development Team. |
|
6 | 4 | # |
@@ -16,40 +14,94 b' Contains postprocessor for serving nbconvert output.' | |||
|
16 | 14 | import os |
|
17 | 15 | import webbrowser |
|
18 | 16 | |
|
19 | from BaseHTTPServer import HTTPServer | |
|
20 | from SimpleHTTPServer import SimpleHTTPRequestHandler | |
|
17 | from tornado import web, ioloop, httpserver | |
|
18 | from tornado.httpclient import AsyncHTTPClient | |
|
21 | 19 | |
|
22 | from IPython.utils.traitlets import Bool | |
|
20 | from IPython.utils.traitlets import Bool, Unicode, Int | |
|
23 | 21 | |
|
24 | 22 | from .base import PostProcessorBase |
|
25 | 23 | |
|
26 | 24 | #----------------------------------------------------------------------------- |
|
27 | 25 | # Classes |
|
28 | 26 | #----------------------------------------------------------------------------- |
|
27 | ||
|
28 | class ProxyHandler(web.RequestHandler): | |
|
29 | """handler the proxies requests from a local prefix to a CDN""" | |
|
30 | @web.asynchronous | |
|
31 | def get(self, prefix, url): | |
|
32 | """proxy a request to a CDN""" | |
|
33 | proxy_url = "/".join([self.settings['cdn'], url]) | |
|
34 | client = self.settings['client'] | |
|
35 | client.fetch(proxy_url, callback=self.finish_get) | |
|
36 | ||
|
37 | def finish_get(self, response): | |
|
38 | """finish the request""" | |
|
39 | # copy potentially relevant headers | |
|
40 | for header in ["Content-Type", "Cache-Control", "Date", "Last-Modified", "Expires"]: | |
|
41 | if header in response.headers: | |
|
42 | self.set_header(header, response.headers[header]) | |
|
43 | self.finish(response.body) | |
|
44 | ||
|
29 | 45 | class ServePostProcessor(PostProcessorBase): |
|
30 |
"""Post processor designed to serve files |
|
|
46 | """Post processor designed to serve files | |
|
47 | ||
|
48 | Proxies reveal.js requests to a CDN if no local reveal.js is present | |
|
49 | """ | |
|
31 | 50 | |
|
32 | 51 | |
|
33 | 52 | open_in_browser = Bool(True, config=True, |
|
34 | help="""Set to False to deactivate | |
|
35 | the opening of the browser""") | |
|
53 | help="""Should the browser be opened automatically?""" | |
|
54 | ) | |
|
55 | reveal_cdn = Unicode("https://cdn.jsdelivr.net/reveal.js/2.4.0", config=True, | |
|
56 | help="""URL for reveal.js CDN.""" | |
|
57 | ) | |
|
58 | reveal_prefix = Unicode("reveal.js", config=True, help="URL prefix for reveal.js") | |
|
59 | ip = Unicode("127.0.0.1", config=True, help="The IP address to listen on.") | |
|
60 | port = Int(8000, config=True, help="port for the server to listen on.") | |
|
36 | 61 | |
|
37 | 62 | def postprocess(self, input): |
|
38 | """ | |
|
39 | Simple implementation to serve the build directory. | |
|
40 | """ | |
|
41 | ||
|
63 | """Serve the build directory with a webserver.""" | |
|
64 | dirname, filename = os.path.split(input) | |
|
65 | handlers = [ | |
|
66 | (r"/(.+)", web.StaticFileHandler, {'path' : dirname}), | |
|
67 | (r"/", web.RedirectHandler, {"url": "/%s" % filename}) | |
|
68 | ] | |
|
69 | ||
|
70 | if ('://' in self.reveal_prefix or self.reveal_prefix.startswith("//")): | |
|
71 | # reveal specifically from CDN, nothing to do | |
|
72 | pass | |
|
73 | elif os.path.isdir(os.path.join(dirname, self.reveal_prefix)): | |
|
74 | # reveal prefix exists | |
|
75 | self.log.info("Serving local %s", self.reveal_prefix) | |
|
76 | else: | |
|
77 | self.log.info("Redirecting %s requests to %s", self.reveal_prefix, self.reveal_cdn) | |
|
78 | handlers.insert(0, (r"/(%s)/(.*)" % self.reveal_prefix, ProxyHandler)) | |
|
79 | ||
|
80 | app = web.Application(handlers, | |
|
81 | cdn=self.reveal_cdn, | |
|
82 | client=AsyncHTTPClient(), | |
|
83 | ) | |
|
84 | # hook up tornado logging to our logger | |
|
85 | from tornado import log | |
|
86 | log.app_log = self.log | |
|
87 | ||
|
88 | http_server = httpserver.HTTPServer(app) | |
|
89 | http_server.listen(self.port, address=self.ip) | |
|
90 | url = "http://%s:%i/%s" % (self.ip, self.port, filename) | |
|
91 | print("Serving your slides at %s" % url) | |
|
92 | print("Use Control-C to stop this server") | |
|
93 | if self.open_in_browser: | |
|
94 | webbrowser.open(url, new=2) | |
|
42 | 95 | try: |
|
43 | dirname, filename = os.path.split(input) | |
|
44 | if dirname: | |
|
45 | os.chdir(dirname) | |
|
46 | httpd = HTTPServer(('127.0.0.1', 8000), SimpleHTTPRequestHandler) | |
|
47 | sa = httpd.socket.getsockname() | |
|
48 | url = "http://" + sa[0] + ":" + str(sa[1]) + "/" + filename | |
|
49 | if self.open_in_browser: | |
|
50 | webbrowser.open(url, new=2) | |
|
51 | print("Serving your slides on " + url) | |
|
52 | print("Use Control-C to stop this server.") | |
|
53 | httpd.serve_forever() | |
|
96 | ioloop.IOLoop.instance().start() | |
|
54 | 97 | except KeyboardInterrupt: |
|
55 |
print(" |
|
|
98 | print("\nInterrupted") | |
|
99 | ||
|
100 | def main(path): | |
|
101 | """allow running this module to serve the slides""" | |
|
102 | server = ServePostProcessor() | |
|
103 | server(path) | |
|
104 | ||
|
105 | if __name__ == '__main__': | |
|
106 | import sys | |
|
107 | main(sys.argv[1]) |
@@ -62,3 +62,7 b' class TestPDF(TestsBase):' | |||
|
62 | 62 | |
|
63 | 63 | # Check that the PDF was created. |
|
64 | 64 | assert os.path.isfile('a.pdf') |
|
65 | ||
|
66 | # Make sure that temp files are cleaned up | |
|
67 | for ext in processor.temp_file_exts: | |
|
68 | assert not os.path.isfile('a'+ext) |
@@ -5,8 +5,8 b' from .svg2pdf import SVG2PDFPreprocessor' | |||
|
5 | 5 | from .extractoutput import ExtractOutputPreprocessor |
|
6 | 6 | from .revealhelp import RevealHelpPreprocessor |
|
7 | 7 | from .latex import LatexPreprocessor |
|
8 | from .sphinx import SphinxPreprocessor | |
|
9 | 8 | from .csshtmlheader import CSSHTMLHeaderPreprocessor |
|
9 | from .highlightmagics import HighlightMagicsPreprocessor | |
|
10 | 10 | |
|
11 | 11 | # decorated function Preprocessors |
|
12 | 12 | from .coalescestreams import coalesce_streams |
@@ -19,7 +19,8 b' def cell_preprocessor(function):' | |||
|
19 | 19 | Wrap a function to be executed on all cells of a notebook |
|
20 | 20 | |
|
21 | 21 | Wrapped Parameters |
|
22 | ---------- | |
|
22 | ------------------ | |
|
23 | ||
|
23 | 24 | cell : NotebookNode cell |
|
24 | 25 | Notebook cell being processed |
|
25 | 26 | resources : dictionary |
@@ -69,7 +70,7 b' def coalesce_streams(cell, resources, index):' | |||
|
69 | 70 | last.text += output.text |
|
70 | 71 | else: |
|
71 | 72 | new_outputs.append(output) |
|
72 | last = output | |
|
73 | last = output | |
|
73 | 74 | |
|
74 | 75 | cell.outputs = new_outputs |
|
75 | 76 | return cell, resources |
@@ -14,9 +14,12 b' they are converted.' | |||
|
14 | 14 | #----------------------------------------------------------------------------- |
|
15 | 15 | |
|
16 | 16 | from __future__ import print_function, absolute_import |
|
17 | import os | |
|
17 | 18 | |
|
18 | # Our own imports | |
|
19 | # Needed to override preprocessor | |
|
19 | # Third-party import, needed for Pygments latex definitions. | |
|
20 | from pygments.formatters import LatexFormatter | |
|
21 | ||
|
22 | # ipy imports | |
|
20 | 23 | from .base import (Preprocessor) |
|
21 | 24 | from IPython.nbconvert import filters |
|
22 | 25 | |
@@ -29,6 +32,24 b' class LatexPreprocessor(Preprocessor):' | |||
|
29 | 32 | Converter for latex destined documents. |
|
30 | 33 | """ |
|
31 | 34 | |
|
35 | def preprocess(self, nb, resources): | |
|
36 | """ | |
|
37 | Preprocessing to apply on each notebook. | |
|
38 | ||
|
39 | Parameters | |
|
40 | ---------- | |
|
41 | nb : NotebookNode | |
|
42 | Notebook being converted | |
|
43 | resources : dictionary | |
|
44 | Additional resources used in the conversion process. Allows | |
|
45 | preprocessors to pass variables into the Jinja engine. | |
|
46 | """ | |
|
47 | # Generate Pygments definitions for Latex | |
|
48 | resources["latex"] = {} | |
|
49 | resources["latex"]["pygments_definitions"] = LatexFormatter().get_style_defs() | |
|
50 | return super(LatexPreprocessor, self).preprocess(nb, resources) | |
|
51 | ||
|
52 | ||
|
32 | 53 | def preprocess_cell(self, cell, resources, index): |
|
33 | 54 | """ |
|
34 | 55 | Apply a transformation on each cell, |
@@ -24,15 +24,14 b' from IPython.utils.traitlets import Unicode, Bool' | |||
|
24 | 24 | |
|
25 | 25 | class RevealHelpPreprocessor(Preprocessor): |
|
26 | 26 | |
|
27 |
url_prefix = Unicode(' |
|
|
28 | config=True, | |
|
29 |
|
|
|
30 | use 'url_prefix':'reveal.js' in your config object.""") | |
|
31 | ||
|
32 | speaker_notes = Bool(False, | |
|
33 |
|
|
|
34 | help="""If you want to use the speaker notes | |
|
35 | set this to True.""") | |
|
27 | url_prefix = Unicode('reveal.js', config=True, | |
|
28 | help="""The URL prefix for reveal.js. | |
|
29 | This can be a a relative URL for a local copy of reveal.js, | |
|
30 | or point to a CDN. | |
|
31 | ||
|
32 | For speaker notes to work, a local reveal.js prefix must be used. | |
|
33 | """ | |
|
34 | ) | |
|
36 | 35 | |
|
37 | 36 | def preprocess(self, nb, resources): |
|
38 | 37 | """ |
@@ -65,30 +64,4 b' class RevealHelpPreprocessor(Preprocessor):' | |||
|
65 | 64 | if not isinstance(resources['reveal'], dict): |
|
66 | 65 | resources['reveal'] = {} |
|
67 | 66 | resources['reveal']['url_prefix'] = self.url_prefix |
|
68 | resources['reveal']['notes_prefix'] = self.url_prefix | |
|
69 | ||
|
70 | cdn = 'http://cdn.jsdelivr.net/reveal.js/2.4.0' | |
|
71 | local = 'local' | |
|
72 | html_path = 'plugin/notes/notes.html' | |
|
73 | js_path = 'plugin/notes/notes.js' | |
|
74 | ||
|
75 | html_infile = os.path.join(cdn, html_path) | |
|
76 | js_infile = os.path.join(cdn, js_path) | |
|
77 | html_outfile = os.path.join(local, html_path) | |
|
78 | js_outfile = os.path.join(local, js_path) | |
|
79 | ||
|
80 | if self.speaker_notes: | |
|
81 | if 'outputs' not in resources: | |
|
82 | resources['outputs'] = {} | |
|
83 | resources['outputs'][html_outfile] = self.notes_helper(html_infile) | |
|
84 | resources['outputs'][js_outfile] = self.notes_helper(js_infile) | |
|
85 | resources['reveal']['notes_prefix'] = local | |
|
86 | ||
|
87 | 67 | return nb, resources |
|
88 | ||
|
89 | def notes_helper(self, infile): | |
|
90 | """Helper function to get the content from an url.""" | |
|
91 | ||
|
92 | content = urllib2.urlopen(infile).read() | |
|
93 | ||
|
94 | return content |
@@ -36,7 +36,7 b' class PreprocessorTestsBase(TestsBase):' | |||
|
36 | 36 | nbformat.new_output(output_type="stream", stream="stdout", output_text="d"), |
|
37 | 37 | nbformat.new_output(output_type="stream", stream="stderr", output_text="e"), |
|
38 | 38 | nbformat.new_output(output_type="stream", stream="stderr", output_text="f"), |
|
39 |
nbformat.new_output(output_type="png", output_png= |
|
|
39 | nbformat.new_output(output_type="png", output_png='Zw==')] #g | |
|
40 | 40 | |
|
41 | 41 | cells=[nbformat.new_code_cell(input="$ e $", prompt_number=1,outputs=outputs), |
|
42 | 42 | nbformat.new_text_cell('markdown', source="$ e $")] |
@@ -14,6 +14,8 b' Module with tests for the coalescestreams preprocessor' | |||
|
14 | 14 | # Imports |
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | |
|
17 | from IPython.nbformat import current as nbformat | |
|
18 | ||
|
17 | 19 | from .base import PreprocessorTestsBase |
|
18 | 20 | from ..coalescestreams import coalesce_streams |
|
19 | 21 | |
@@ -35,4 +37,24 b' class TestCoalesceStreams(PreprocessorTestsBase):' | |||
|
35 | 37 | self.assertEqual(outputs[1].output_type, "text") |
|
36 | 38 | self.assertEqual(outputs[2].text, "cd") |
|
37 | 39 | self.assertEqual(outputs[3].text, "ef") |
|
38 | ||
|
40 | ||
|
41 | ||
|
42 | def test_coalesce_sequenced_streams(self): | |
|
43 | """Can the coalesce streams preprocessor merge a sequence of streams?""" | |
|
44 | ||
|
45 | outputs = [nbformat.new_output(output_type="stream", stream="stdout", output_text="0"), | |
|
46 | nbformat.new_output(output_type="stream", stream="stdout", output_text="1"), | |
|
47 | nbformat.new_output(output_type="stream", stream="stdout", output_text="2"), | |
|
48 | nbformat.new_output(output_type="stream", stream="stdout", output_text="3"), | |
|
49 | nbformat.new_output(output_type="stream", stream="stdout", output_text="4"), | |
|
50 | nbformat.new_output(output_type="stream", stream="stdout", output_text="5"), | |
|
51 | nbformat.new_output(output_type="stream", stream="stdout", output_text="6"), | |
|
52 | nbformat.new_output(output_type="stream", stream="stdout", output_text="7")] | |
|
53 | cells=[nbformat.new_code_cell(input="# None", prompt_number=1,outputs=outputs)] | |
|
54 | worksheets = [nbformat.new_worksheet(name="worksheet1", cells=cells)] | |
|
55 | ||
|
56 | nb = nbformat.new_notebook(name="notebook1", worksheets=worksheets) | |
|
57 | res = self.build_resources() | |
|
58 | nb, res = coalesce_streams(nb, res) | |
|
59 | outputs = nb.worksheets[0].cells[0].outputs | |
|
60 | self.assertEqual(outputs[0].text, u'01234567') |
@@ -46,12 +46,14 b' class TestExtractOutput(PreprocessorTestsBase):' | |||
|
46 | 46 | nb, res = preprocessor(nb, res) |
|
47 | 47 | |
|
48 | 48 | # Check if text was extracted. |
|
49 |
|
|
|
50 | text_filename = nb.worksheets[0].cells[0].outputs[1]['text_filename'] | |
|
49 | output = nb.worksheets[0].cells[0].outputs[1] | |
|
50 | assert 'text_filename' in output | |
|
51 | text_filename = output['text_filename'] | |
|
51 | 52 | |
|
52 | 53 | # Check if png was extracted. |
|
53 |
|
|
|
54 | png_filename = nb.worksheets[0].cells[0].outputs[6]['png_filename'] | |
|
54 | output = nb.worksheets[0].cells[0].outputs[6] | |
|
55 | assert 'png_filename' in output | |
|
56 | png_filename = output['png_filename'] | |
|
55 | 57 | |
|
56 | 58 | # Verify text output |
|
57 | 59 | assert text_filename in res['outputs'] |
@@ -1,5 +1,5 b'' | |||
|
1 | 1 | """ |
|
2 |
Module with tests for the |
|
|
2 | Module with tests for the HighlightMagics preprocessor | |
|
3 | 3 | """ |
|
4 | 4 | |
|
5 | 5 | #----------------------------------------------------------------------------- |
@@ -15,43 +15,54 b' Module with tests for the sphinx preprocessor' | |||
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | |
|
17 | 17 | from .base import PreprocessorTestsBase |
|
18 |
from .. |
|
|
18 | from ..highlightmagics import HighlightMagicsPreprocessor | |
|
19 | 19 | |
|
20 | 20 | |
|
21 | 21 | #----------------------------------------------------------------------------- |
|
22 | 22 | # Class |
|
23 | 23 | #----------------------------------------------------------------------------- |
|
24 | 24 | |
|
25 |
class Test |
|
|
26 |
"""Contains test functions for |
|
|
25 | class TestHighlightMagics(PreprocessorTestsBase): | |
|
26 | """Contains test functions for highlightmagics.py""" | |
|
27 | 27 | |
|
28 | 28 | |
|
29 | 29 | def build_preprocessor(self): |
|
30 | 30 | """Make an instance of a preprocessor""" |
|
31 |
preprocessor = |
|
|
31 | preprocessor = HighlightMagicsPreprocessor() | |
|
32 | 32 | preprocessor.enabled = True |
|
33 | 33 | return preprocessor |
|
34 | 34 | |
|
35 | ||
|
36 | 35 | def test_constructor(self): |
|
37 |
"""Can a |
|
|
36 | """Can a HighlightMagicsPreprocessor be constructed?""" | |
|
38 | 37 | self.build_preprocessor() |
|
39 | ||
|
40 | 38 | |
|
41 |
def test_ |
|
|
42 | """Make sure the SphinxPreprocessor adds the appropriate resources to the | |
|
43 | resources dict.""" | |
|
39 | def test_tagging(self): | |
|
40 | """Test the HighlightMagicsPreprocessor tagging""" | |
|
41 | nb = self.build_notebook() | |
|
42 | res = self.build_resources() | |
|
43 | preprocessor = self.build_preprocessor() | |
|
44 | nb.worksheets[0].cells[0].input = """%%R -i x,y -o XYcoef | |
|
45 | lm.fit <- lm(y~x) | |
|
46 | par(mfrow=c(2,2)) | |
|
47 | print(summary(lm.fit)) | |
|
48 | plot(lm.fit) | |
|
49 | XYcoef <- coef(lm.fit)""" | |
|
50 | ||
|
51 | nb, res = preprocessor(nb, res) | |
|
52 | ||
|
53 | assert('magics_language' in nb.worksheets[0].cells[0]['metadata']) | |
|
54 | ||
|
55 | self.assertEqual(nb.worksheets[0].cells[0]['metadata']['magics_language'], 'r') | |
|
56 | ||
|
57 | def test_no_false_positive(self): | |
|
58 | """Test that HighlightMagicsPreprocessor does not tag false positives""" | |
|
44 | 59 | nb = self.build_notebook() |
|
45 | 60 | res = self.build_resources() |
|
46 | 61 | preprocessor = self.build_preprocessor() |
|
62 | nb.worksheets[0].cells[0].input = """# this should not be detected | |
|
63 | print(\""" | |
|
64 | %%R -i x, y | |
|
65 | \""")""" | |
|
47 | 66 | nb, res = preprocessor(nb, res) |
|
48 | assert "author" in res['sphinx'] | |
|
49 | assert "version" in res['sphinx'] | |
|
50 | assert "release" in res['sphinx'] | |
|
51 | assert "date" in res['sphinx'] | |
|
52 | assert "chapterstyle" in res['sphinx'] | |
|
53 | assert "outputstyle" in res['sphinx'] | |
|
54 | assert "centeroutput" in res['sphinx'] | |
|
55 | assert "header" in res['sphinx'] | |
|
56 | assert "texinputs" in res['sphinx'] | |
|
57 | assert "pygment_definitions" in res['sphinx'] | |
|
67 | ||
|
68 | assert('magics_language' not in nb.worksheets[0].cells[0]['metadata']) No newline at end of file |
@@ -36,7 +36,7 b' In [{{ cell.prompt_number }}]:' | |||
|
36 | 36 | |
|
37 | 37 | {% block input %} |
|
38 | 38 | <div class="input_area box-flex1"> |
|
39 | {{ cell.input | highlight2html }} | |
|
39 | {{ cell.input | highlight2html(metadata=cell.metadata) }} | |
|
40 | 40 | </div> |
|
41 | 41 | {%- endblock input %} |
|
42 | 42 | |
@@ -135,6 +135,12 b' unknown type {{ cell.type }}' | |||
|
135 | 135 | </pre> |
|
136 | 136 | {%- endblock -%} |
|
137 | 137 | |
|
138 | {%- block data_javascript %} | |
|
139 | <script type="text/javascript"> | |
|
140 | {{ output.javascript }} | |
|
141 | </script> | |
|
142 | {%- endblock -%} | |
|
143 | ||
|
138 | 144 | {%- block display_data scoped -%} |
|
139 | 145 | <div class="box-flex1 output_subarea output_display_data"> |
|
140 | 146 | {{ super() }} |
@@ -1,26 +1,17 b'' | |||
|
1 | ((============================================================================ | |
|
2 | NBConvert Sphinx-Latex HowTo Template | |
|
3 | 1 | |
|
4 | Purpose: Allow export of PDF friendly Latex inspired by Sphinx HowTo | |
|
5 | document style. Most of the is derived directly from Sphinx source. | |
|
2 | % Default to the notebook output style | |
|
3 | ((* if not cell_style is defined *)) | |
|
4 | ((* set cell_style = 'style_ipython.tplx' *)) | |
|
5 | ((* endif *)) | |
|
6 | 6 | |
|
7 | Inheritance: null>display_priority>sphinx | |
|
7 | % Inherit from the specified cell style. | |
|
8 | ((* extends cell_style *)) | |
|
8 | 9 | |
|
9 | ==========================================================================)) | |
|
10 | 10 | |
|
11 | ((*- extends 'sphinx.tplx' -*)) | |
|
11 | %=============================================================================== | |
|
12 | % Latex Article | |
|
13 | %=============================================================================== | |
|
12 | 14 | |
|
13 | ||
|
14 |
|
|
|
15 |
((* |
|
|
16 | ||
|
17 | ((* block h1 -*))part((* endblock h1 -*)) | |
|
18 | ((* block h2 -*))section((* endblock h2 -*)) | |
|
19 | ((* block h3 -*))subsection((* endblock h3 -*)) | |
|
20 | ((* block h4 -*))subsubsection((* endblock h4 -*)) | |
|
21 | ((* block h5 -*))paragraph((* endblock h5 -*)) | |
|
22 | ((* block h6 -*))subparagraph((* endblock h6 -*)) | |
|
23 | ||
|
24 | % Diasble table of contents for howto | |
|
25 | ((* block toc *)) | |
|
26 | ((* endblock toc *)) | |
|
15 | ((* block docclass *)) | |
|
16 | \documentclass{article} | |
|
17 | ((* endblock docclass *)) No newline at end of file |
@@ -1,4 +1,7 b'' | |||
|
1 |
((= |
|
|
1 | ((= Auto-generated template file, DO NOT edit directly! | |
|
2 | To edit this file, please refer to ../../skeleton/README.md =)) | |
|
3 | ||
|
4 | ||
|
2 | 5 | ((*- extends 'null.tplx' -*)) |
|
3 | 6 | |
|
4 | 7 | ((=display data priority=)) |
@@ -30,10 +33,13 b'' | |||
|
30 | 33 | ((*- block data_text -*)) |
|
31 | 34 | ((*- endblock -*)) |
|
32 | 35 | ((*- endif -*)) |
|
33 | ||
|
34 | 36 | ((*- if type in ['latex']*)) |
|
35 | 37 | ((*- block data_latex -*)) |
|
36 | 38 | ((*- endblock -*)) |
|
37 | 39 | ((*- endif -*)) |
|
40 | ((*- if type in ['javascript']*)) | |
|
41 | ((*- block data_javascript -*)) | |
|
42 | ((*- endblock -*)) | |
|
43 | ((*- endif -*)) | |
|
38 | 44 | ((*- endfor -*)) |
|
39 | 45 | ((*- endblock data_priority -*)) |
@@ -1,11 +1,14 b'' | |||
|
1 |
((= |
|
|
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 | 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 | 12 | To layout the different blocks of a notebook. |
|
10 | 13 | |
|
11 | 14 | Subtemplates can override blocks to define their custom representation. |
@@ -2,19 +2,13 b'' | |||
|
2 | 2 | |
|
3 | 3 | |
|
4 | 4 | {% block in_prompt %} |
|
5 | In[{{ cell.prompt_number if cell.prompt_number else ' ' }}]: | |
|
6 | 5 | {% endblock in_prompt %} |
|
7 | 6 | |
|
8 | 7 | {% block output_prompt %} |
|
9 | {% if cell.haspyout %} | |
|
10 | Out[{{ cell.prompt_number }}]: | |
|
11 | {%- endif %} | |
|
12 | 8 | {%- endblock output_prompt %} |
|
13 | 9 | |
|
14 | 10 | {% block input %} |
|
15 | ``` | |
|
16 | {{ cell.input }} | |
|
17 | ``` | |
|
11 | {{ cell.input | indent(4)}} | |
|
18 | 12 | {% endblock input %} |
|
19 | 13 | |
|
20 | 14 | {% block pyerr %} |
@@ -26,6 +20,7 b' Out[{{ cell.prompt_number }}]:' | |||
|
26 | 20 | {% endblock traceback_line %} |
|
27 | 21 | |
|
28 | 22 | {% block pyout %} |
|
23 | ||
|
29 | 24 | {% block data_priority scoped %} |
|
30 | 25 | {{ super() }} |
|
31 | 26 | {% endblock %} |
@@ -36,23 +31,25 b' Out[{{ cell.prompt_number }}]:' | |||
|
36 | 31 | {% endblock stream %} |
|
37 | 32 | |
|
38 | 33 | {% block data_svg %} |
|
39 |
|
|
|
34 | ![svg]({{ output.svg_filename | path2url }}) | |
|
40 | 35 | {% endblock data_svg %} |
|
41 | 36 | |
|
42 | 37 | {% block data_png %} |
|
43 |
|
|
|
38 | ![png]({{ output.png_filename | path2url }}) | |
|
44 | 39 | {% endblock data_png %} |
|
45 | 40 | |
|
46 | 41 | {% block data_jpg %} |
|
47 |
|
|
|
42 | ![jpeg]({{ output.jpeg_filename | path2url }}) | |
|
48 | 43 | {% endblock data_jpg %} |
|
49 | 44 | |
|
50 | 45 | {% block data_latex %} |
|
51 | $$ | |
|
52 | 46 | {{ output.latex }} |
|
53 | $$ | |
|
54 | 47 | {% endblock data_latex %} |
|
55 | 48 | |
|
49 | {% block data_html scoped %} | |
|
50 | {{ output.html }} | |
|
51 | {% endblock data_html %} | |
|
52 | ||
|
56 | 53 | {% block data_text scoped %} |
|
57 | 54 | {{ output.text | indent }} |
|
58 | 55 | {% endblock data_text %} |
@@ -61,6 +58,7 b' $$' | |||
|
61 | 58 | {{ cell.source | wrap_text(80) }} |
|
62 | 59 | {% endblock markdowncell %} |
|
63 | 60 | |
|
61 | ||
|
64 | 62 | {% block headingcell scoped %} |
|
65 | 63 | {{ '#' * cell.level }} {{ cell.source | replace('\n', ' ') }} |
|
66 | 64 | {% endblock headingcell %} |
@@ -2,25 +2,22 b'' | |||
|
2 | 2 | |
|
3 | 3 | |
|
4 | 4 | {% block in_prompt %} |
|
5 | ||
|
6 | In[{{ cell.prompt_number if cell.prompt_number else ' ' }}]: | |
|
7 | ||
|
8 | .. code:: python | |
|
9 | ||
|
10 | 5 | {% endblock in_prompt %} |
|
11 | 6 | |
|
12 | 7 | {% block output_prompt %} |
|
13 | {% if cell.haspyout -%} | |
|
14 | Out[{{ cell.prompt_number }}]: | |
|
15 | {% endif %} | |
|
16 | 8 | {% endblock output_prompt %} |
|
17 | 9 | |
|
18 | 10 | {% block input %} |
|
11 | {%- if not cell.input.isspace() -%} | |
|
12 | .. code:: python | |
|
13 | ||
|
19 | 14 | {{ cell.input | indent}} |
|
15 | {%- endif -%} | |
|
20 | 16 | {% endblock input %} |
|
21 | 17 | |
|
22 | 18 | {% block pyerr %} |
|
23 | 19 | :: |
|
20 | ||
|
24 | 21 | {{ super() }} |
|
25 | 22 | {% endblock pyerr %} |
|
26 | 23 | |
@@ -49,19 +46,27 b" In[{{ cell.prompt_number if cell.prompt_number else ' ' }}]:" | |||
|
49 | 46 | {% endblock data_png %} |
|
50 | 47 | |
|
51 | 48 | {% block data_jpg %} |
|
52 |
.. |
|
|
49 | .. image:: {{ output.jpeg_filename }} | |
|
53 | 50 | {% endblock data_jpg %} |
|
54 | 51 | |
|
55 | 52 | {% block data_latex %} |
|
56 | 53 | .. math:: |
|
57 | {{ output.latex | indent }} | |
|
54 | ||
|
55 | {{ output.latex | strip_dollars | indent }} | |
|
58 | 56 | {% endblock data_latex %} |
|
59 | 57 | |
|
60 | 58 | {% block data_text scoped %} |
|
61 | 59 | .. parsed-literal:: |
|
60 | ||
|
62 | 61 | {{ output.text | indent }} |
|
63 | 62 | {% endblock data_text %} |
|
64 | 63 | |
|
64 | {% block data_html scoped %} | |
|
65 | .. raw:: html | |
|
66 | ||
|
67 | {{ output.html | indent }} | |
|
68 | {% endblock data_html %} | |
|
69 | ||
|
65 | 70 | {% block markdowncell scoped %} |
|
66 | 71 | {{ cell.source | markdown2rst }} |
|
67 | 72 | {% endblock markdowncell %} |
@@ -1,12 +1,14 b'' | |||
|
1 | TPLS := $(patsubst %.tpl,../latex/skeleton/%.tplx,$(wildcard *.tpl)) | |
|
1 | 2 | |
|
3 | all: clean $(TPLS) | |
|
2 | 4 | |
|
3 | all: tex/null.tplx tex/display_priority.tplx | |
|
4 | ||
|
5 | # convert jinja syntax to tex | |
|
6 | # cf http://flask.pocoo.org/snippets/55/ | |
|
7 | tex/%.tplx: %.tpl | |
|
5 | # Convert standard Jinja2 syntax to LaTeX safe Jinja2 | |
|
6 | # see http://flask.pocoo.org/snippets/55/ for more info | |
|
7 | ../latex/skeleton/%.tplx: %.tpl | |
|
8 | 8 | @echo 'generating tex equivalent of $^: $@' |
|
9 |
@echo '((= |
|
|
9 | @echo '((= Auto-generated template file, DO NOT edit directly!\n' \ | |
|
10 | ' To edit this file, please refer to ../../skeleton/README.md' \ | |
|
11 | '=))\n\n' > $@ | |
|
10 | 12 | @sed \ |
|
11 | 13 | -e 's/{%/((*/g' \ |
|
12 | 14 | -e 's/%}/*))/g' \ |
@@ -17,7 +19,6 b' tex/%.tplx: %.tpl' | |||
|
17 | 19 | -e "s/tpl'/tplx'/g" \ |
|
18 | 20 | $^ >> $@ |
|
19 | 21 | |
|
20 | ||
|
21 | 22 | clean: |
|
22 | 23 | @echo "cleaning generated tplx files..." |
|
23 | @rm tex/* | |
|
24 | @-rm ../latex/skeleton/*.tplx |
@@ -1,6 +1,8 b'' | |||
|
1 | 1 | ## Template skeleton |
|
2 | 2 | |
|
3 | This contain skeleton template that you probably don't want | |
|
4 | to inherit directly. | |
|
3 | This directory contains the template skeleton files. | |
|
5 | 4 | |
|
6 | do not modify the content of the 'tex' folder which is generated by running 'make' in this folder. | |
|
5 | Do not modify the contents of the `../latex/skeleton` folder. Instead, | |
|
6 | if you need to, make modifications to the files in this folder and then run | |
|
7 | `make` to generate the corresponding latex skeleton files in the | |
|
8 | `../latex/skeleton` folder. No newline at end of file |
@@ -29,10 +29,13 b'' | |||
|
29 | 29 | {%- block data_text -%} |
|
30 | 30 | {%- endblock -%} |
|
31 | 31 | {%- endif -%} |
|
32 | ||
|
33 | 32 | {%- if type in ['latex']%} |
|
34 | 33 | {%- block data_latex -%} |
|
35 | 34 | {%- endblock -%} |
|
36 | 35 | {%- endif -%} |
|
36 | {%- if type in ['javascript']%} | |
|
37 | {%- block data_javascript -%} | |
|
38 | {%- endblock -%} | |
|
39 | {%- endif -%} | |
|
37 | 40 | {%- endfor -%} |
|
38 | 41 | {%- endblock data_priority -%} |
@@ -1,10 +1,10 b'' | |||
|
1 | 1 | {# |
|
2 | 2 | |
|
3 |
DO NOT USE THIS AS A BASE |
|
|
3 | DO NOT USE THIS AS A BASE, | |
|
4 | 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 | 8 | To layout the different blocks of a notebook. |
|
9 | 9 | |
|
10 | 10 | Subtemplates can override blocks to define their custom representation. |
@@ -135,7 +135,7 b" transition: Reveal.getQueryHash().transition || 'linear', // default/cube/page/c" | |||
|
135 | 135 | dependencies: [ |
|
136 | 136 | { src: "{{resources.reveal.url_prefix}}/lib/js/classList.js", condition: function() { return !document.body.classList; } }, |
|
137 | 137 | { src: "{{resources.reveal.url_prefix}}/plugin/highlight/highlight.js", async: true, callback: function() { hljs.initHighlightingOnLoad(); } }, |
|
138 |
{ src: "{{resources.reveal. |
|
|
138 | { src: "{{resources.reveal.url_prefix}}/plugin/notes/notes.js", async: true, condition: function() { return !!document.body.classList; } } | |
|
139 | 139 | // { src: 'http://s7.addthis.com/js/300/addthis_widget.js', async: true}, |
|
140 | 140 | ] |
|
141 | 141 | }); |
@@ -16,12 +16,12 b' Contains base test class for nbconvert' | |||
|
16 | 16 | import os |
|
17 | 17 | import glob |
|
18 | 18 | import shutil |
|
19 | import unittest | |
|
19 | 20 | |
|
20 | 21 | import IPython |
|
21 | 22 | from IPython.utils.tempdir import TemporaryWorkingDirectory |
|
22 | 23 | from IPython.utils.process import get_output_error_code |
|
23 | 24 | from IPython.testing.tools import get_ipython_cmd |
|
24 | from IPython.testing.ipunittest import ParametricTestCase | |
|
25 | 25 | |
|
26 | 26 | # a trailing space allows for simpler concatenation with the other arguments |
|
27 | 27 | ipy_cmd = get_ipython_cmd(as_string=True) + " " |
@@ -31,7 +31,7 b' ipy_cmd = get_ipython_cmd(as_string=True) + " "' | |||
|
31 | 31 | #----------------------------------------------------------------------------- |
|
32 | 32 | |
|
33 | 33 | |
|
34 |
class TestsBase( |
|
|
34 | class TestsBase(unittest.TestCase): | |
|
35 | 35 | """Base tests class. Contains useful fuzzy comparison and nbconvert |
|
36 | 36 | functions.""" |
|
37 | 37 | |
@@ -86,7 +86,7 b' class TestsBase(ParametricTestCase):' | |||
|
86 | 86 | |
|
87 | 87 | For example: |
|
88 | 88 | Replace "ii" with "i" in the string "Hiiii" yields "Hii" |
|
89 |
Another replacement |
|
|
89 | Another replacement cds "Hi" (the desired output) | |
|
90 | 90 | |
|
91 | 91 | Parameters: |
|
92 | 92 | ----------- |
@@ -124,6 +124,14 b'' | |||
|
124 | 124 | "prompt_number": 10 |
|
125 | 125 | }, |
|
126 | 126 | { |
|
127 | "cell_type": "heading", | |
|
128 | "level": 2, | |
|
129 | "metadata": {}, | |
|
130 | "source": [ | |
|
131 | "Here is a very long heading that pandoc will wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap and wrap" | |
|
132 | ] | |
|
133 | }, | |
|
134 | { | |
|
127 | 135 | "cell_type": "markdown", |
|
128 | 136 | "metadata": {}, |
|
129 | 137 | "source": [ |
@@ -1,12 +1,10 b'' | |||
|
1 | """ | |
|
2 | Contains tests for the nbconvertapp | |
|
3 | """ | |
|
1 | """Test NbConvertApp""" | |
|
2 | ||
|
4 | 3 | #----------------------------------------------------------------------------- |
|
5 |
#Copyright ( |
|
|
6 | # | |
|
7 | #Distributed under the terms of the Modified BSD License. | |
|
4 | # Copyright (C) 2013 The IPython Development Team | |
|
8 | 5 | # |
|
9 | #The full license is in the file COPYING.txt, distributed with this software. | |
|
6 | # Distributed under the terms of the BSD License. The full license is in | |
|
7 | # the file COPYING, distributed as part of this software. | |
|
10 | 8 | #----------------------------------------------------------------------------- |
|
11 | 9 | |
|
12 | 10 | #----------------------------------------------------------------------------- |
@@ -15,12 +13,14 b' Contains tests for the nbconvertapp' | |||
|
15 | 13 | |
|
16 | 14 | import os |
|
17 | 15 | import glob |
|
16 | import sys | |
|
18 | 17 | |
|
19 | 18 | from .base import TestsBase |
|
20 | 19 | |
|
20 | import IPython.testing.tools as tt | |
|
21 | 21 | from IPython.testing import decorators as dec |
|
22 | 22 | |
|
23 | ||
|
23 | ||
|
24 | 24 | #----------------------------------------------------------------------------- |
|
25 | 25 | # Constants |
|
26 | 26 | #----------------------------------------------------------------------------- |
@@ -35,13 +35,14 b' class TestNbConvertApp(TestsBase):' | |||
|
35 | 35 | |
|
36 | 36 | |
|
37 | 37 | def test_notebook_help(self): |
|
38 | """ | |
|
39 | Will help show if no notebooks are specified? | |
|
40 | """ | |
|
38 | """Will help show if no notebooks are specified?""" | |
|
41 | 39 | with self.create_temp_cwd(): |
|
42 | 40 | out, err = self.call('nbconvert --log-level 0', ignore_return_code=True) |
|
43 |
assert |
|
|
44 | ||
|
41 | self.assertIn("see '--help-all'", out) | |
|
42 | ||
|
43 | def test_help_output(self): | |
|
44 | """ipython nbconvert --help-all works""" | |
|
45 | tt.help_all_output_test('nbconvert') | |
|
45 | 46 | |
|
46 | 47 | def test_glob(self): |
|
47 | 48 | """ |
@@ -102,6 +103,30 b' class TestNbConvertApp(TestsBase):' | |||
|
102 | 103 | assert os.path.isfile('notebook1.tex') |
|
103 | 104 | assert os.path.isfile('notebook1.pdf') |
|
104 | 105 | |
|
106 | @dec.onlyif_cmds_exist('pandoc') | |
|
107 | def test_spurious_cr(self): | |
|
108 | """Check for extra CR characters""" | |
|
109 | with self.create_temp_cwd(['notebook2.ipynb']): | |
|
110 | self.call('nbconvert --log-level 0 --to latex notebook2') | |
|
111 | assert os.path.isfile('notebook2.tex') | |
|
112 | with open('notebook2.tex') as f: | |
|
113 | tex = f.read() | |
|
114 | self.call('nbconvert --log-level 0 --to html notebook2') | |
|
115 | assert os.path.isfile('notebook2.html') | |
|
116 | with open('notebook2.html') as f: | |
|
117 | html = f.read() | |
|
118 | self.assertEqual(tex.count('\r'), tex.count('\r\n')) | |
|
119 | self.assertEqual(html.count('\r'), html.count('\r\n')) | |
|
120 | ||
|
121 | @dec.onlyif_cmds_exist('pandoc') | |
|
122 | def test_png_base64_html_ok(self): | |
|
123 | """Is embedded png data well formed in HTML?""" | |
|
124 | with self.create_temp_cwd(['notebook2.ipynb']): | |
|
125 | self.call('nbconvert --log-level 0 --to HTML ' | |
|
126 | 'notebook2.ipynb --template full') | |
|
127 | assert os.path.isfile('notebook2.html') | |
|
128 | with open('notebook2.html') as f: | |
|
129 | assert "data:image/png;base64,b'" not in f.read() | |
|
105 | 130 | |
|
106 | 131 | @dec.onlyif_cmds_exist('pandoc') |
|
107 | 132 | def test_template(self): |
@@ -15,6 +15,7 b' from __future__ import print_function' | |||
|
15 | 15 | |
|
16 | 16 | # Stdlib imports |
|
17 | 17 | import subprocess |
|
18 | from io import TextIOWrapper, BytesIO | |
|
18 | 19 | |
|
19 | 20 | # IPython imports |
|
20 | 21 | from IPython.utils.py3compat import cast_bytes |
@@ -64,6 +65,6 b" def pandoc(source, fmt, to, extra_args=None, encoding='utf-8'):" | |||
|
64 | 65 | "http://johnmacfarlane.net/pandoc/installing.html" |
|
65 | 66 | ) |
|
66 | 67 | out, _ = p.communicate(cast_bytes(source, encoding)) |
|
67 |
out = out |
|
|
68 |
return out |
|
|
68 | out = TextIOWrapper(BytesIO(out), encoding, 'replace').read() | |
|
69 | return out.rstrip('\n') | |
|
69 | 70 |
@@ -25,6 +25,7 b' import pprint' | |||
|
25 | 25 | import uuid |
|
26 | 26 | |
|
27 | 27 | from IPython.utils.ipstruct import Struct |
|
28 | from IPython.utils.py3compat import cast_unicode | |
|
28 | 29 | |
|
29 | 30 | #----------------------------------------------------------------------------- |
|
30 | 31 | # Code |
@@ -67,21 +68,21 b' def new_output(output_type=None, output_text=None, output_png=None,' | |||
|
67 | 68 | |
|
68 | 69 | if output_type != 'pyerr': |
|
69 | 70 | if output_text is not None: |
|
70 | output.text = unicode(output_text) | |
|
71 | output.text = cast_unicode(output_text) | |
|
71 | 72 | if output_png is not None: |
|
72 |
output.png = |
|
|
73 | output.png = cast_unicode(output_png) | |
|
73 | 74 | if output_jpeg is not None: |
|
74 |
output.jpeg = |
|
|
75 | output.jpeg = cast_unicode(output_jpeg) | |
|
75 | 76 | if output_html is not None: |
|
76 | output.html = unicode(output_html) | |
|
77 | output.html = cast_unicode(output_html) | |
|
77 | 78 | if output_svg is not None: |
|
78 | output.svg = unicode(output_svg) | |
|
79 | output.svg = cast_unicode(output_svg) | |
|
79 | 80 | if output_latex is not None: |
|
80 | output.latex = unicode(output_latex) | |
|
81 | output.latex = cast_unicode(output_latex) | |
|
81 | 82 | if output_json is not None: |
|
82 | output.json = unicode(output_json) | |
|
83 | output.json = cast_unicode(output_json) | |
|
83 | 84 | if output_javascript is not None: |
|
84 | output.javascript = unicode(output_javascript) | |
|
85 | output.javascript = cast_unicode(output_javascript) | |
|
85 | 86 | |
|
86 | 87 | if output_type == u'pyout': |
|
87 | 88 | if prompt_number is not None: |
@@ -89,14 +90,14 b' def new_output(output_type=None, output_text=None, output_png=None,' | |||
|
89 | 90 | |
|
90 | 91 | if output_type == u'pyerr': |
|
91 | 92 | if ename is not None: |
|
92 | output.ename = unicode(ename) | |
|
93 | output.ename = cast_unicode(ename) | |
|
93 | 94 | if evalue is not None: |
|
94 | output.evalue = unicode(evalue) | |
|
95 | output.evalue = cast_unicode(evalue) | |
|
95 | 96 | if traceback is not None: |
|
96 | output.traceback = [unicode(frame) for frame in list(traceback)] | |
|
97 | output.traceback = [cast_unicode(frame) for frame in list(traceback)] | |
|
97 | 98 | |
|
98 | 99 | if output_type == u'stream': |
|
99 | output.stream = 'stdout' if stream is None else unicode(stream) | |
|
100 | output.stream = 'stdout' if stream is None else cast_unicode(stream) | |
|
100 | 101 | |
|
101 | 102 | return output |
|
102 | 103 | |
@@ -107,9 +108,9 b' def new_code_cell(input=None, prompt_number=None, outputs=None,' | |||
|
107 | 108 | cell = NotebookNode() |
|
108 | 109 | cell.cell_type = u'code' |
|
109 | 110 | if language is not None: |
|
110 | cell.language = unicode(language) | |
|
111 | cell.language = cast_unicode(language) | |
|
111 | 112 | if input is not None: |
|
112 | cell.input = unicode(input) | |
|
113 | cell.input = cast_unicode(input) | |
|
113 | 114 | if prompt_number is not None: |
|
114 | 115 | cell.prompt_number = int(prompt_number) |
|
115 | 116 | if outputs is None: |
@@ -130,9 +131,9 b' def new_text_cell(cell_type, source=None, rendered=None, metadata=None):' | |||
|
130 | 131 | if cell_type == 'plaintext': |
|
131 | 132 | cell_type = 'raw' |
|
132 | 133 | if source is not None: |
|
133 | cell.source = unicode(source) | |
|
134 | cell.source = cast_unicode(source) | |
|
134 | 135 | if rendered is not None: |
|
135 | cell.rendered = unicode(rendered) | |
|
136 | cell.rendered = cast_unicode(rendered) | |
|
136 | 137 | cell.metadata = NotebookNode(metadata or {}) |
|
137 | 138 | cell.cell_type = cell_type |
|
138 | 139 | return cell |
@@ -143,9 +144,9 b' def new_heading_cell(source=None, rendered=None, level=1, metadata=None):' | |||
|
143 | 144 | cell = NotebookNode() |
|
144 | 145 | cell.cell_type = u'heading' |
|
145 | 146 | if source is not None: |
|
146 | cell.source = unicode(source) | |
|
147 | cell.source = cast_unicode(source) | |
|
147 | 148 | if rendered is not None: |
|
148 | cell.rendered = unicode(rendered) | |
|
149 | cell.rendered = cast_unicode(rendered) | |
|
149 | 150 | cell.level = int(level) |
|
150 | 151 | cell.metadata = NotebookNode(metadata or {}) |
|
151 | 152 | return cell |
@@ -155,7 +156,7 b' def new_worksheet(name=None, cells=None, metadata=None):' | |||
|
155 | 156 | """Create a worksheet by name with with a list of cells.""" |
|
156 | 157 | ws = NotebookNode() |
|
157 | 158 | if name is not None: |
|
158 | ws.name = unicode(name) | |
|
159 | ws.name = cast_unicode(name) | |
|
159 | 160 | if cells is None: |
|
160 | 161 | ws.cells = [] |
|
161 | 162 | else: |
@@ -178,7 +179,7 b' def new_notebook(name=None, metadata=None, worksheets=None):' | |||
|
178 | 179 | else: |
|
179 | 180 | nb.metadata = NotebookNode(metadata) |
|
180 | 181 | if name is not None: |
|
181 | nb.metadata.name = unicode(name) | |
|
182 | nb.metadata.name = cast_unicode(name) | |
|
182 | 183 | return nb |
|
183 | 184 | |
|
184 | 185 | |
@@ -187,29 +188,29 b' def new_metadata(name=None, authors=None, license=None, created=None,' | |||
|
187 | 188 | """Create a new metadata node.""" |
|
188 | 189 | metadata = NotebookNode() |
|
189 | 190 | if name is not None: |
|
190 | metadata.name = unicode(name) | |
|
191 | metadata.name = cast_unicode(name) | |
|
191 | 192 | if authors is not None: |
|
192 | 193 | metadata.authors = list(authors) |
|
193 | 194 | if created is not None: |
|
194 | metadata.created = unicode(created) | |
|
195 | metadata.created = cast_unicode(created) | |
|
195 | 196 | if modified is not None: |
|
196 | metadata.modified = unicode(modified) | |
|
197 | metadata.modified = cast_unicode(modified) | |
|
197 | 198 | if license is not None: |
|
198 | metadata.license = unicode(license) | |
|
199 | metadata.license = cast_unicode(license) | |
|
199 | 200 | if gistid is not None: |
|
200 | metadata.gistid = unicode(gistid) | |
|
201 | metadata.gistid = cast_unicode(gistid) | |
|
201 | 202 | return metadata |
|
202 | 203 | |
|
203 | 204 | def new_author(name=None, email=None, affiliation=None, url=None): |
|
204 | 205 | """Create a new author.""" |
|
205 | 206 | author = NotebookNode() |
|
206 | 207 | if name is not None: |
|
207 | author.name = unicode(name) | |
|
208 | author.name = cast_unicode(name) | |
|
208 | 209 | if email is not None: |
|
209 | author.email = unicode(email) | |
|
210 | author.email = cast_unicode(email) | |
|
210 | 211 | if affiliation is not None: |
|
211 | author.affiliation = unicode(affiliation) | |
|
212 | author.affiliation = cast_unicode(affiliation) | |
|
212 | 213 | if url is not None: |
|
213 | author.url = unicode(url) | |
|
214 | author.url = cast_unicode(url) | |
|
214 | 215 | return author |
|
215 | 216 |
@@ -46,7 +46,7 b' class JSONReader(NotebookReader):' | |||
|
46 | 46 | return nb |
|
47 | 47 | |
|
48 | 48 | def to_notebook(self, d, **kwargs): |
|
49 |
return |
|
|
49 | return rejoin_lines(from_dict(d)) | |
|
50 | 50 | |
|
51 | 51 | |
|
52 | 52 | class JSONWriter(NotebookWriter): |
@@ -190,7 +190,7 b' class PyWriter(NotebookWriter):' | |||
|
190 | 190 | lines.extend([u'# ' + line for line in input.splitlines()]) |
|
191 | 191 | lines.append(u'') |
|
192 | 192 | lines.append('') |
|
193 |
return u |
|
|
193 | return u'\n'.join(lines) | |
|
194 | 194 | |
|
195 | 195 | |
|
196 | 196 | _reader = PyReader() |
@@ -32,6 +32,8 b' def restore_bytes(nb):' | |||
|
32 | 32 | |
|
33 | 33 | Base64 encoding is handled elsewhere. Bytes objects in the notebook are |
|
34 | 34 | always b64-encoded. We DO NOT encode/decode around file formats. |
|
35 | ||
|
36 | Note: this is never used | |
|
35 | 37 | """ |
|
36 | 38 | for ws in nb.worksheets: |
|
37 | 39 | for cell in ws.cells: |
@@ -9,9 +9,9 b' from ..nbbase import (' | |||
|
9 | 9 | new_metadata, new_author, new_heading_cell, nbformat, nbformat_minor |
|
10 | 10 | ) |
|
11 | 11 | |
|
12 |
# some random base64-encoded * |
|
|
13 | png = encodestring(os.urandom(5)) | |
|
14 | jpeg = encodestring(os.urandom(6)) | |
|
12 | # some random base64-encoded *text* | |
|
13 | png = encodestring(os.urandom(5)).decode('ascii') | |
|
14 | jpeg = encodestring(os.urandom(6)).decode('ascii') | |
|
15 | 15 | |
|
16 | 16 | ws = new_worksheet(name='worksheet1') |
|
17 | 17 |
@@ -1,4 +1,5 b'' | |||
|
1 | 1 | import pprint |
|
2 | from base64 import decodestring | |
|
2 | 3 | from unittest import TestCase |
|
3 | 4 | |
|
4 | 5 | from ..nbjson import reads, writes |
@@ -29,5 +30,42 b' class TestJSON(formattest.NBFormatTest, TestCase):' | |||
|
29 | 30 | s = writes(nb0, split_lines=True) |
|
30 | 31 | self.assertEqual(nbjson.reads(s),nb0) |
|
31 | 32 | |
|
33 | def test_read_png(self): | |
|
34 | """PNG output data is b64 unicode""" | |
|
35 | s = writes(nb0) | |
|
36 | nb1 = nbjson.reads(s) | |
|
37 | found_png = False | |
|
38 | for cell in nb1.worksheets[0].cells: | |
|
39 | if not 'outputs' in cell: | |
|
40 | continue | |
|
41 | for output in cell.outputs: | |
|
42 | if 'png' in output: | |
|
43 | found_png = True | |
|
44 | pngdata = output['png'] | |
|
45 | self.assertEqual(type(pngdata), unicode) | |
|
46 | # test that it is valid b64 data | |
|
47 | b64bytes = pngdata.encode('ascii') | |
|
48 | raw_bytes = decodestring(b64bytes) | |
|
49 | assert found_png, "never found png output" | |
|
50 | ||
|
51 | def test_read_jpeg(self): | |
|
52 | """JPEG output data is b64 unicode""" | |
|
53 | s = writes(nb0) | |
|
54 | nb1 = nbjson.reads(s) | |
|
55 | found_jpeg = False | |
|
56 | for cell in nb1.worksheets[0].cells: | |
|
57 | if not 'outputs' in cell: | |
|
58 | continue | |
|
59 | for output in cell.outputs: | |
|
60 | if 'jpeg' in output: | |
|
61 | found_jpeg = True | |
|
62 | jpegdata = output['jpeg'] | |
|
63 | self.assertEqual(type(jpegdata), unicode) | |
|
64 | # test that it is valid b64 data | |
|
65 | b64bytes = jpegdata.encode('ascii') | |
|
66 | raw_bytes = decodestring(b64bytes) | |
|
67 | assert found_jpeg, "never found jpeg output" | |
|
68 | ||
|
69 | ||
|
32 | 70 | |
|
33 | 71 |
@@ -141,3 +141,17 b' class TestMetadata(TestCase):' | |||
|
141 | 141 | self.assertEqual(md.gistid, u'21341231') |
|
142 | 142 | self.assertEqual(md.authors, authors) |
|
143 | 143 | |
|
144 | class TestOutputs(TestCase): | |
|
145 | def test_binary_png(self): | |
|
146 | out = new_output(output_png=b'\x89PNG\r\n\x1a\n') | |
|
147 | ||
|
148 | def test_b64b6tes_png(self): | |
|
149 | out = new_output(output_png=b'iVBORw0KG') | |
|
150 | ||
|
151 | def test_binary_jpeg(self): | |
|
152 | out = new_output(output_jpeg=b'\xff\xd8') | |
|
153 | ||
|
154 | def test_b64b6tes_jpeg(self): | |
|
155 | out = new_output(output_jpeg=b'/9') | |
|
156 | ||
|
157 |
@@ -1,11 +1,11 b'' | |||
|
1 | 1 | #!/usr/bin/env python |
|
2 | 2 | # -*- coding: utf8 -*- |
|
3 | import argparse | |
|
4 | import traceback | |
|
5 | import json | |
|
3 | 6 | |
|
4 | 7 | from IPython.external.jsonschema import Draft3Validator, validate, ValidationError |
|
5 | 8 | import IPython.external.jsonpointer as jsonpointer |
|
6 | from IPython.external import argparse | |
|
7 | import traceback | |
|
8 | import json | |
|
9 | 9 | |
|
10 | 10 | def nbvalidate(nbjson, schema='v3.withref.json', key=None,verbose=True): |
|
11 | 11 | v3schema = resolve_ref(json.load(open(schema,'r'))) |
@@ -71,7 +71,6 b' class ParallelCrashHandler(CrashHandler):' | |||
|
71 | 71 | base_aliases = {} |
|
72 | 72 | base_aliases.update(base_ip_aliases) |
|
73 | 73 | base_aliases.update({ |
|
74 | 'profile-dir' : 'ProfileDir.location', | |
|
75 | 74 | 'work-dir' : 'BaseParallelApplication.work_dir', |
|
76 | 75 | 'log-to-file' : 'BaseParallelApplication.log_to_file', |
|
77 | 76 | 'clean-logs' : 'BaseParallelApplication.clean_logs', |
@@ -11,7 +11,7 b' Authors:' | |||
|
11 | 11 | """ |
|
12 | 12 | |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 |
# Copyright (C) 2008 |
|
|
14 | # Copyright (C) 2008 The IPython Development Team | |
|
15 | 15 | # |
|
16 | 16 | # Distributed under the terms of the BSD License. The full license is in |
|
17 | 17 | # the file COPYING, distributed as part of this software. |
@@ -44,7 +44,7 b' from IPython.parallel.apps.baseapp import (' | |||
|
44 | 44 | catch_config_error, |
|
45 | 45 | ) |
|
46 | 46 | from IPython.utils.importstring import import_item |
|
47 |
from IPython.utils.localinterfaces import |
|
|
47 | from IPython.utils.localinterfaces import localhost, public_ips | |
|
48 | 48 | from IPython.utils.traitlets import Instance, Unicode, Bool, List, Dict, TraitError |
|
49 | 49 | |
|
50 | 50 | from IPython.kernel.zmq.session import ( |
@@ -224,13 +224,13 b' class IPControllerApp(BaseParallelApplication):' | |||
|
224 | 224 | location = cdict['location'] |
|
225 | 225 | |
|
226 | 226 | if not location: |
|
227 |
if |
|
|
228 |
location = |
|
|
227 | if public_ips(): | |
|
228 | location = public_ips()[-1] | |
|
229 | 229 | else: |
|
230 | 230 | self.log.warn("Could not identify this machine's IP, assuming %s." |
|
231 | 231 | " You may need to specify '--location=<external_ip_address>' to help" |
|
232 |
" IPython decide when to connect via loopback." % |
|
|
233 |
location = |
|
|
232 | " IPython decide when to connect via loopback." % localhost() ) | |
|
233 | location = localhost() | |
|
234 | 234 | cdict['location'] = location |
|
235 | 235 | fname = os.path.join(self.profile_dir.security_dir, fname) |
|
236 | 236 | self.log.info("writing connection info to %s", fname) |
@@ -26,7 +26,7 b' import zmq' | |||
|
26 | 26 | from zmq.eventloop import ioloop, zmqstream |
|
27 | 27 | |
|
28 | 28 | from IPython.config.configurable import LoggingConfigurable |
|
29 |
from IPython.utils.localinterfaces import |
|
|
29 | from IPython.utils.localinterfaces import localhost | |
|
30 | 30 | from IPython.utils.traitlets import Int, Unicode, Instance, List |
|
31 | 31 | |
|
32 | 32 | #----------------------------------------------------------------------------- |
@@ -44,8 +44,10 b' class LogWatcher(LoggingConfigurable):' | |||
|
44 | 44 | # configurables |
|
45 | 45 | topics = List([''], config=True, |
|
46 | 46 | help="The ZMQ topics to subscribe to. Default is to subscribe to all messages") |
|
47 |
url = Unicode( |
|
|
47 | url = Unicode(config=True, | |
|
48 | 48 | help="ZMQ url on which to listen for log messages") |
|
49 | def _url_default(self): | |
|
50 | return 'tcp://%s:20202' % localhost() | |
|
49 | 51 | |
|
50 | 52 | # internals |
|
51 | 53 | stream = Instance('zmq.eventloop.zmqstream.ZMQStream') |
@@ -391,7 +391,7 b' class AsyncResult(object):' | |||
|
391 | 391 | tic = time.time() |
|
392 | 392 | while not self.ready() and (timeout < 0 or time.time() - tic <= timeout): |
|
393 | 393 | self.wait(interval) |
|
394 | clear_output() | |
|
394 | clear_output(wait=True) | |
|
395 | 395 | print("%4i/%i tasks finished after %4i s" % (self.progress, N, self.elapsed), end="") |
|
396 | 396 | sys.stdout.flush() |
|
397 | 397 | print() |
@@ -37,7 +37,7 b' from IPython.core.profiledir import ProfileDir, ProfileDirError' | |||
|
37 | 37 | from IPython.utils.capture import RichOutput |
|
38 | 38 | from IPython.utils.coloransi import TermColors |
|
39 | 39 | from IPython.utils.jsonutil import rekey |
|
40 |
from IPython.utils.localinterfaces import |
|
|
40 | from IPython.utils.localinterfaces import localhost, is_local_ip | |
|
41 | 41 | from IPython.utils.path import get_ipython_dir |
|
42 | 42 | from IPython.utils.py3compat import cast_bytes |
|
43 | 43 | from IPython.utils.traitlets import (HasTraits, Integer, Instance, Unicode, |
@@ -433,13 +433,13 b' class Client(HasTraits):' | |||
|
433 | 433 | |
|
434 | 434 | url = cfg['registration'] |
|
435 | 435 | |
|
436 |
if location is not None and addr == |
|
|
436 | if location is not None and addr == localhost(): | |
|
437 | 437 | # location specified, and connection is expected to be local |
|
438 |
if |
|
|
438 | if not is_local_ip(location) and not sshserver: | |
|
439 | 439 | # load ssh from JSON *only* if the controller is not on |
|
440 | 440 | # this machine |
|
441 | 441 | sshserver=cfg['ssh'] |
|
442 |
if |
|
|
442 | if not is_local_ip(location) and not sshserver: | |
|
443 | 443 | # warn if no ssh specified, but SSH is probably needed |
|
444 | 444 | # This is only a warning, because the most likely cause |
|
445 | 445 | # is a local Controller on a laptop whose IP is dynamic |
@@ -495,7 +495,12 b' class Client(HasTraits):' | |||
|
495 | 495 | } |
|
496 | 496 | self._queue_handlers = {'execute_reply' : self._handle_execute_reply, |
|
497 | 497 | 'apply_reply' : self._handle_apply_reply} |
|
498 | self._connect(sshserver, ssh_kwargs, timeout) | |
|
498 | ||
|
499 | try: | |
|
500 | self._connect(sshserver, ssh_kwargs, timeout) | |
|
501 | except: | |
|
502 | self.close(linger=0) | |
|
503 | raise | |
|
499 | 504 | |
|
500 | 505 | # last step: setup magics, if we are in IPython: |
|
501 | 506 | |
@@ -599,7 +604,6 b' class Client(HasTraits):' | |||
|
599 | 604 | self._connected=True |
|
600 | 605 | |
|
601 | 606 | def connect_socket(s, url): |
|
602 | # url = util.disambiguate_url(url, self._config['location']) | |
|
603 | 607 | if self._ssh: |
|
604 | 608 | return tunnel.tunnel_connection(s, url, sshserver, **ssh_kwargs) |
|
605 | 609 | else: |
@@ -956,14 +960,23 b' class Client(HasTraits):' | |||
|
956 | 960 | view.activate(suffix) |
|
957 | 961 | return view |
|
958 | 962 | |
|
959 | def close(self): | |
|
963 | def close(self, linger=None): | |
|
964 | """Close my zmq Sockets | |
|
965 | ||
|
966 | If `linger`, set the zmq LINGER socket option, | |
|
967 | which allows discarding of messages. | |
|
968 | """ | |
|
960 | 969 | if self._closed: |
|
961 | 970 | return |
|
962 | 971 | self.stop_spin_thread() |
|
963 | snames = filter(lambda n: n.endswith('socket'), dir(self)) | |
|
964 | for socket in map(lambda name: getattr(self, name), snames): | |
|
965 | if isinstance(socket, zmq.Socket) and not socket.closed: | |
|
966 |
socket.close |
|
|
972 | snames = [ trait for trait in self.trait_names() if trait.endswith("socket") ] | |
|
973 | for name in snames: | |
|
974 | socket = getattr(self, name) | |
|
975 | if socket is not None and not socket.closed: | |
|
976 | if linger is not None: | |
|
977 | socket.close(linger=linger) | |
|
978 | else: | |
|
979 | socket.close() | |
|
967 | 980 | self._closed = True |
|
968 | 981 | |
|
969 | 982 | def _spin_every(self, interval=1): |
@@ -89,7 +89,6 b' def sync_view_results(f, self, *args, **kwargs):' | |||
|
89 | 89 | view = self.view |
|
90 | 90 | if view._in_sync_results: |
|
91 | 91 | return f(self, *args, **kwargs) |
|
92 | print 'in sync results', f | |
|
93 | 92 | view._in_sync_results = True |
|
94 | 93 | try: |
|
95 | 94 | ret = f(self, *args, **kwargs) |
@@ -30,7 +30,7 b' from zmq.eventloop.zmqstream import ZMQStream' | |||
|
30 | 30 | |
|
31 | 31 | # internal: |
|
32 | 32 | from IPython.utils.importstring import import_item |
|
33 |
from IPython.utils.localinterfaces import |
|
|
33 | from IPython.utils.localinterfaces import localhost | |
|
34 | 34 | from IPython.utils.py3compat import cast_bytes |
|
35 | 35 | from IPython.utils.traitlets import ( |
|
36 | 36 | HasTraits, Instance, Integer, Unicode, Dict, Set, Tuple, CBytes, DottedObjectName |
@@ -177,20 +177,25 b' class HubFactory(RegistrationFactory):' | |||
|
177 | 177 | def _notifier_port_default(self): |
|
178 | 178 | return util.select_random_ports(1)[0] |
|
179 | 179 | |
|
180 |
engine_ip = Unicode( |
|
|
180 | engine_ip = Unicode(config=True, | |
|
181 | 181 | help="IP on which to listen for engine connections. [default: loopback]") |
|
182 | def _engine_ip_default(self): | |
|
183 | return localhost() | |
|
182 | 184 | engine_transport = Unicode('tcp', config=True, |
|
183 | 185 | help="0MQ transport for engine connections. [default: tcp]") |
|
184 | 186 | |
|
185 |
client_ip = Unicode( |
|
|
187 | client_ip = Unicode(config=True, | |
|
186 | 188 | help="IP on which to listen for client connections. [default: loopback]") |
|
187 | 189 | client_transport = Unicode('tcp', config=True, |
|
188 | 190 | help="0MQ transport for client connections. [default : tcp]") |
|
189 | 191 | |
|
190 |
monitor_ip = Unicode( |
|
|
192 | monitor_ip = Unicode(config=True, | |
|
191 | 193 | help="IP on which to listen for monitor messages. [default: loopback]") |
|
192 | 194 | monitor_transport = Unicode('tcp', config=True, |
|
193 | 195 | help="0MQ transport for monitor messages. [default : tcp]") |
|
196 | ||
|
197 | _client_ip_default = _monitor_ip_default = _engine_ip_default | |
|
198 | ||
|
194 | 199 | |
|
195 | 200 | monitor_url = Unicode('') |
|
196 | 201 |
@@ -39,7 +39,7 b' class MongoDB(BaseDB):' | |||
|
39 | 39 | necessary if the default mongodb configuration does not point to your |
|
40 | 40 | mongod instance.""" |
|
41 | 41 | ) |
|
42 | database = Unicode(config=True, | |
|
42 | database = Unicode("ipython-tasks", config=True, | |
|
43 | 43 | help="""The MongoDB database name to use for storing tasks for this session. If unspecified, |
|
44 | 44 | a new database will be created with the Hub's IDENT. Specifying the database will result |
|
45 | 45 | in tasks from previous sessions being available via Clients' db_query and |
@@ -358,7 +358,7 b' class TaskScheduler(SessionFactory):' | |||
|
358 | 358 | # build fake metadata |
|
359 | 359 | md = dict( |
|
360 | 360 | status=u'error', |
|
361 | engine=engine, | |
|
361 | engine=engine.decode('ascii'), | |
|
362 | 362 | date=datetime.now(), |
|
363 | 363 | ) |
|
364 | 364 | msg = self.session.msg('apply_reply', content, parent=parent, metadata=md) |
@@ -96,7 +96,7 b' class SQLiteDB(BaseDB):' | |||
|
96 | 96 | location = Unicode('', config=True, |
|
97 | 97 | help="""The directory containing the sqlite task database. The default |
|
98 | 98 | is to use the cluster_dir location.""") |
|
99 | table = Unicode("", config=True, | |
|
99 | table = Unicode("ipython-tasks", config=True, | |
|
100 | 100 | help="""The SQLite Table to use for storing tasks for this session. If unspecified, |
|
101 | 101 | a new table will be created with the Hub's IDENT. Specifying the table will result |
|
102 | 102 | in tasks from previous sessions being available via Clients' db_query and |
@@ -195,7 +195,7 b' class SQLiteDB(BaseDB):' | |||
|
195 | 195 | |
|
196 | 196 | If a bad (old) table does exist, return False |
|
197 | 197 | """ |
|
198 | cursor = self._db.execute("PRAGMA table_info(%s)"%self.table) | |
|
198 | cursor = self._db.execute("PRAGMA table_info('%s')"%self.table) | |
|
199 | 199 | lines = cursor.fetchall() |
|
200 | 200 | if not lines: |
|
201 | 201 | # table does not exist |
@@ -241,7 +241,7 b' class SQLiteDB(BaseDB):' | |||
|
241 | 241 | ) |
|
242 | 242 | previous_table = self.table |
|
243 | 243 | |
|
244 | self._db.execute("""CREATE TABLE IF NOT EXISTS %s | |
|
244 | self._db.execute("""CREATE TABLE IF NOT EXISTS '%s' | |
|
245 | 245 | (msg_id text PRIMARY KEY, |
|
246 | 246 | header dict text, |
|
247 | 247 | metadata dict text, |
@@ -333,12 +333,12 b' class SQLiteDB(BaseDB):' | |||
|
333 | 333 | d['msg_id'] = msg_id |
|
334 | 334 | line = self._dict_to_list(d) |
|
335 | 335 | tups = '(%s)'%(','.join(['?']*len(line))) |
|
336 | self._db.execute("INSERT INTO %s VALUES %s"%(self.table, tups), line) | |
|
336 | self._db.execute("INSERT INTO '%s' VALUES %s"%(self.table, tups), line) | |
|
337 | 337 | # self._db.commit() |
|
338 | 338 | |
|
339 | 339 | def get_record(self, msg_id): |
|
340 | 340 | """Get a specific Task Record, by msg_id.""" |
|
341 | cursor = self._db.execute("""SELECT * FROM %s WHERE msg_id==?"""%self.table, (msg_id,)) | |
|
341 | cursor = self._db.execute("""SELECT * FROM '%s' WHERE msg_id==?"""%self.table, (msg_id,)) | |
|
342 | 342 | line = cursor.fetchone() |
|
343 | 343 | if line is None: |
|
344 | 344 | raise KeyError("No such msg: %r"%msg_id) |
@@ -346,7 +346,7 b' class SQLiteDB(BaseDB):' | |||
|
346 | 346 | |
|
347 | 347 | def update_record(self, msg_id, rec): |
|
348 | 348 | """Update the data in an existing record.""" |
|
349 | query = "UPDATE %s SET "%self.table | |
|
349 | query = "UPDATE '%s' SET "%self.table | |
|
350 | 350 | sets = [] |
|
351 | 351 | keys = sorted(rec.keys()) |
|
352 | 352 | values = [] |
@@ -361,13 +361,13 b' class SQLiteDB(BaseDB):' | |||
|
361 | 361 | |
|
362 | 362 | def drop_record(self, msg_id): |
|
363 | 363 | """Remove a record from the DB.""" |
|
364 | self._db.execute("""DELETE FROM %s WHERE msg_id==?"""%self.table, (msg_id,)) | |
|
364 | self._db.execute("""DELETE FROM '%s' WHERE msg_id==?"""%self.table, (msg_id,)) | |
|
365 | 365 | # self._db.commit() |
|
366 | 366 | |
|
367 | 367 | def drop_matching_records(self, check): |
|
368 | 368 | """Remove a record from the DB.""" |
|
369 | 369 | expr,args = self._render_expression(check) |
|
370 | query = "DELETE FROM %s WHERE %s"%(self.table, expr) | |
|
370 | query = "DELETE FROM '%s' WHERE %s"%(self.table, expr) | |
|
371 | 371 | self._db.execute(query,args) |
|
372 | 372 | # self._db.commit() |
|
373 | 373 | |
@@ -399,7 +399,7 b' class SQLiteDB(BaseDB):' | |||
|
399 | 399 | else: |
|
400 | 400 | req = '*' |
|
401 | 401 | expr,args = self._render_expression(check) |
|
402 | query = """SELECT %s FROM %s WHERE %s"""%(req, self.table, expr) | |
|
402 | query = """SELECT %s FROM '%s' WHERE %s"""%(req, self.table, expr) | |
|
403 | 403 | cursor = self._db.execute(query, args) |
|
404 | 404 | matches = cursor.fetchall() |
|
405 | 405 | records = [] |
@@ -410,7 +410,7 b' class SQLiteDB(BaseDB):' | |||
|
410 | 410 | |
|
411 | 411 | def get_history(self): |
|
412 | 412 | """get all msg_ids, ordered by time submitted.""" |
|
413 | query = """SELECT msg_id FROM %s ORDER by submitted ASC"""%self.table | |
|
413 | query = """SELECT msg_id FROM '%s' ORDER by submitted ASC"""%self.table | |
|
414 | 414 | cursor = self._db.execute(query) |
|
415 | 415 | # will be a list of length 1 tuples |
|
416 | 416 | return [ tup[0] for tup in cursor.fetchall()] |
@@ -24,7 +24,7 b' from zmq.eventloop import ioloop, zmqstream' | |||
|
24 | 24 | |
|
25 | 25 | from IPython.external.ssh import tunnel |
|
26 | 26 | # internal |
|
27 |
from IPython.utils.localinterfaces import |
|
|
27 | from IPython.utils.localinterfaces import localhost | |
|
28 | 28 | from IPython.utils.traitlets import ( |
|
29 | 29 | Instance, Dict, Integer, Type, Float, Integer, Unicode, CBytes, Bool |
|
30 | 30 | ) |
@@ -184,13 +184,13 b' class EngineFactory(RegistrationFactory):' | |||
|
184 | 184 | if self.max_heartbeat_misses > 0: |
|
185 | 185 | # Add a monitor socket which will record the last time a ping was seen |
|
186 | 186 | mon = self.context.socket(zmq.SUB) |
|
187 |
mport = mon.bind_to_random_port('tcp://%s' % |
|
|
187 | mport = mon.bind_to_random_port('tcp://%s' % localhost()) | |
|
188 | 188 | mon.setsockopt(zmq.SUBSCRIBE, b"") |
|
189 | 189 | self._hb_listener = zmqstream.ZMQStream(mon, self.loop) |
|
190 | 190 | self._hb_listener.on_recv(self._report_ping) |
|
191 | 191 | |
|
192 | 192 | |
|
193 |
hb_monitor = "tcp://%s:%i" % ( |
|
|
193 | hb_monitor = "tcp://%s:%i" % (localhost(), mport) | |
|
194 | 194 | |
|
195 | 195 | heart = Heart(hb_ping, hb_pong, hb_monitor , heart_id=identity) |
|
196 | 196 | heart.start() |
@@ -24,7 +24,7 b' import zmq' | |||
|
24 | 24 | from zmq.eventloop.ioloop import IOLoop |
|
25 | 25 | |
|
26 | 26 | from IPython.config.configurable import Configurable |
|
27 |
from IPython.utils.localinterfaces import |
|
|
27 | from IPython.utils.localinterfaces import localhost | |
|
28 | 28 | from IPython.utils.traitlets import Integer, Instance, Unicode |
|
29 | 29 | |
|
30 | 30 | from IPython.parallel.util import select_random_ports |
@@ -40,16 +40,18 b' class RegistrationFactory(SessionFactory):' | |||
|
40 | 40 | |
|
41 | 41 | url = Unicode('', config=True, |
|
42 | 42 | help="""The 0MQ url used for registration. This sets transport, ip, and port |
|
43 |
in one variable. For example: url='tcp:// |
|
|
43 | in one variable. For example: url='tcp://127.0.0.1:12345' or | |
|
44 | 44 | url='epgm://*:90210'""" |
|
45 |
|
|
|
45 | ) # url takes precedence over ip,regport,transport | |
|
46 | 46 | transport = Unicode('tcp', config=True, |
|
47 | 47 | help="""The 0MQ transport for communications. This will likely be |
|
48 | 48 | the default of 'tcp', but other values include 'ipc', 'epgm', 'inproc'.""") |
|
49 |
ip = Unicode( |
|
|
49 | ip = Unicode(config=True, | |
|
50 | 50 | help="""The IP address for registration. This is generally either |
|
51 | 51 | '127.0.0.1' for loopback only or '*' for all interfaces. |
|
52 | [default: '%s']""" % LOCALHOST) | |
|
52 | """) | |
|
53 | def _ip_default(self): | |
|
54 | return localhost() | |
|
53 | 55 | regport = Integer(config=True, |
|
54 | 56 | help="""The port on which the Hub listens for registration.""") |
|
55 | 57 | def _regport_default(self): |
@@ -63,8 +63,7 b' def setup():' | |||
|
63 | 63 | tic = time.time() |
|
64 | 64 | while not os.path.exists(engine_json) or not os.path.exists(client_json): |
|
65 | 65 | if cp.poll() is not None: |
|
66 | print cp.poll() | |
|
67 | raise RuntimeError("The test controller failed to start.") | |
|
66 | raise RuntimeError("The test controller exited with status %s" % cp.poll()) | |
|
68 | 67 | elif time.time()-tic > 15: |
|
69 | 68 | raise RuntimeError("Timeout waiting for the test controller to start.") |
|
70 | 69 | time.sleep(0.1) |
@@ -172,7 +172,6 b' class TestClient(ClusterTestCase):' | |||
|
172 | 172 | # give the monitor time to notice the message |
|
173 | 173 | time.sleep(.25) |
|
174 | 174 | ahr = self.client.get_result(ar.msg_ids[0]) |
|
175 | print ar.get(), ahr.get(), ar._single_result, ahr._single_result | |
|
176 | 175 | self.assertTrue(isinstance(ahr, AsyncHubResult)) |
|
177 | 176 | self.assertEqual(ahr.get().pyout, ar.get().pyout) |
|
178 | 177 | ar2 = self.client.get_result(ar.msg_ids[0]) |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: modified file | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed | |
This diff has been collapsed as it changes many lines, (2353 lines changed) Show them Hide them |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
|
1 | NO CONTENT: file was removed | |
The requested commit or file is too big and content was truncated. Show full diff |
General Comments 0
You need to be logged in to leave comments.
Login now