Show More
@@ -0,0 +1,63 b'' | |||
|
1 | """Tests for kernel utility functions | |
|
2 | ||
|
3 | Authors | |
|
4 | ------- | |
|
5 | * MinRK | |
|
6 | """ | |
|
7 | #----------------------------------------------------------------------------- | |
|
8 | # Copyright (c) 2011, 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 | # Stdlib imports | |
|
20 | from unittest import TestCase | |
|
21 | ||
|
22 | # Third-party imports | |
|
23 | import nose.tools as nt | |
|
24 | ||
|
25 | # Our own imports | |
|
26 | from IPython.testing import decorators as dec | |
|
27 | from IPython.lib import kernel | |
|
28 | ||
|
29 | #----------------------------------------------------------------------------- | |
|
30 | # Classes and functions | |
|
31 | #----------------------------------------------------------------------------- | |
|
32 | ||
|
33 | @dec.parametric | |
|
34 | def test_swallow_argv(): | |
|
35 | tests = [ | |
|
36 | # expected , argv , aliases, flags | |
|
37 | (['-a', '5'], ['-a', '5'], None, None), | |
|
38 | (['5'], ['-a', '5'], None, ['a']), | |
|
39 | ([], ['-a', '5'], ['a'], None), | |
|
40 | ([], ['-a', '5'], ['a'], ['a']), | |
|
41 | ([], ['--foo'], None, ['foo']), | |
|
42 | (['--foo'], ['--foo'], ['foobar'], []), | |
|
43 | ([], ['--foo', '5'], ['foo'], []), | |
|
44 | ([], ['--foo=5'], ['foo'], []), | |
|
45 | (['--foo=5'], ['--foo=5'], [], ['foo']), | |
|
46 | (['5'], ['--foo', '5'], [], ['foo']), | |
|
47 | (['bar'], ['--foo', '5', 'bar'], ['foo'], ['foo']), | |
|
48 | (['bar'], ['--foo=5', 'bar'], ['foo'], ['foo']), | |
|
49 | (['5','bar'], ['--foo', '5', 'bar'], None, ['foo']), | |
|
50 | (['bar'], ['--foo', '5', 'bar'], ['foo'], None), | |
|
51 | (['bar'], ['--foo=5', 'bar'], ['foo'], None), | |
|
52 | ] | |
|
53 | for expected, argv, aliases, flags in tests: | |
|
54 | stripped = kernel.swallow_argv(argv, aliases=aliases, flags=flags) | |
|
55 | message = '\n'.join(['', | |
|
56 | "argv: %r" % argv, | |
|
57 | "aliases: %r" % aliases, | |
|
58 | "flags : %r" % flags, | |
|
59 | "expected : %r" % expected, | |
|
60 | "returned : %r" % stripped, | |
|
61 | ]) | |
|
62 | yield nt.assert_equal(expected, stripped, message) | |
|
63 |
@@ -33,7 +33,7 b' import uuid' | |||
|
33 | 33 | from IPython.config.application import boolean_flag |
|
34 | 34 | from IPython.config.configurable import Configurable |
|
35 | 35 | from IPython.core.profiledir import ProfileDir |
|
36 | from IPython.lib.kernel import tunnel_to_kernel, find_connection_file | |
|
36 | from IPython.lib.kernel import tunnel_to_kernel, find_connection_file, swallow_argv | |
|
37 | 37 | from IPython.zmq.blockingkernelmanager import BlockingKernelManager |
|
38 | 38 | from IPython.utils.path import filefind |
|
39 | 39 | from IPython.utils.py3compat import str_to_bytes |
@@ -136,6 +136,9 b' class IPythonConsoleApp(Configurable):' | |||
|
136 | 136 | kernel_manager_class = BlockingKernelManager |
|
137 | 137 | |
|
138 | 138 | kernel_argv = List(Unicode) |
|
139 | # frontend flags&aliases to be stripped when building kernel_argv | |
|
140 | frontend_flags = Any(app_flags) | |
|
141 | frontend_aliases = Any(app_aliases) | |
|
139 | 142 | |
|
140 | 143 | pure = CBool(False, config=True, |
|
141 | 144 | help="Use a pure Python kernel instead of an IPython kernel.") |
@@ -182,45 +185,13 b' class IPythonConsoleApp(Configurable):' | |||
|
182 | 185 | ) |
|
183 | 186 | |
|
184 | 187 | |
|
185 |
def |
|
|
186 | #super(PythonBaseConsoleApp, self).parse_command_line(argv) | |
|
187 | # make this stuff after this a function, in case the super stuff goes | |
|
188 | # away. Also, Min notes that this functionality should be moved to a | |
|
189 | # generic library of kernel stuff | |
|
190 | self.swallow_args(app_aliases,app_flags,argv=argv) | |
|
191 | ||
|
192 | def swallow_args(self, aliases,flags, argv=None): | |
|
188 | def build_kernel_argv(self, argv=None): | |
|
189 | """build argv to be passed to kernel subprocess""" | |
|
193 | 190 | if argv is None: |
|
194 | 191 | argv = sys.argv[1:] |
|
195 | self.kernel_argv = list(argv) # copy | |
|
192 | self.kernel_argv = swallow_argv(argv, self.frontend_aliases, self.frontend_flags) | |
|
196 | 193 | # kernel should inherit default config file from frontend |
|
197 | 194 | self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name) |
|
198 | # Scrub frontend-specific flags | |
|
199 | swallow_next = False | |
|
200 | was_flag = False | |
|
201 | for a in argv: | |
|
202 | if swallow_next: | |
|
203 | swallow_next = False | |
|
204 | # last arg was an alias, remove the next one | |
|
205 | # *unless* the last alias has a no-arg flag version, in which | |
|
206 | # case, don't swallow the next arg if it's also a flag: | |
|
207 | if not (was_flag and a.startswith('-')): | |
|
208 | self.kernel_argv.remove(a) | |
|
209 | continue | |
|
210 | if a.startswith('-'): | |
|
211 | split = a.lstrip('-').split('=') | |
|
212 | alias = split[0] | |
|
213 | if alias in aliases: | |
|
214 | self.kernel_argv.remove(a) | |
|
215 | if len(split) == 1: | |
|
216 | # alias passed with arg via space | |
|
217 | swallow_next = True | |
|
218 | # could have been a flag that matches an alias, e.g. `existing` | |
|
219 | # in which case, we might not swallow the next arg | |
|
220 | was_flag = alias in flags | |
|
221 | elif alias in flags: | |
|
222 | # strip flag, but don't swallow next, as flags don't take args | |
|
223 | self.kernel_argv.remove(a) | |
|
224 | 195 | |
|
225 | 196 | def init_connection_file(self): |
|
226 | 197 | """find the connection file, and load the info if found. |
@@ -55,6 +55,7 b' from .notebookmanager import NotebookManager' | |||
|
55 | 55 | from IPython.config.application import catch_config_error |
|
56 | 56 | from IPython.core.application import BaseIPythonApplication |
|
57 | 57 | from IPython.core.profiledir import ProfileDir |
|
58 | from IPython.lib.kernel import swallow_argv | |
|
58 | 59 | from IPython.zmq.session import Session, default_secure |
|
59 | 60 | from IPython.zmq.zmqshell import ZMQInteractiveShell |
|
60 | 61 | from IPython.zmq.ipkernel import ( |
@@ -283,27 +284,10 b' class NotebookApp(BaseIPythonApplication):' | |||
|
283 | 284 | if argv is None: |
|
284 | 285 | argv = sys.argv[1:] |
|
285 | 286 | |
|
286 | self.kernel_argv = list(argv) # copy | |
|
287 | # Scrub frontend-specific flags | |
|
288 | self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags) | |
|
287 | 289 | # Kernel should inherit default config file from frontend |
|
288 | 290 | self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name) |
|
289 | # Scrub frontend-specific flags | |
|
290 | for a in argv: | |
|
291 | if a.startswith('-') and a.lstrip('-') in notebook_flags: | |
|
292 | self.kernel_argv.remove(a) | |
|
293 | swallow_next = False | |
|
294 | for a in argv: | |
|
295 | if swallow_next: | |
|
296 | self.kernel_argv.remove(a) | |
|
297 | swallow_next = False | |
|
298 | continue | |
|
299 | if a.startswith('-'): | |
|
300 | split = a.lstrip('-').split('=') | |
|
301 | alias = split[0] | |
|
302 | if alias in notebook_aliases: | |
|
303 | self.kernel_argv.remove(a) | |
|
304 | if len(split) == 1: | |
|
305 | # alias passed with arg via space | |
|
306 | swallow_next = True | |
|
307 | 291 | |
|
308 | 292 | def init_configurables(self): |
|
309 | 293 | # Don't let Qt or ZMQ swallow KeyboardInterupts. |
@@ -144,6 +144,8 b' class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):' | |||
|
144 | 144 | classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session] |
|
145 | 145 | flags = Dict(flags) |
|
146 | 146 | aliases = Dict(aliases) |
|
147 | frontend_flags = Any(qt_flags) | |
|
148 | frontend_aliases = Any(qt_aliases) | |
|
147 | 149 | kernel_manager_class = QtKernelManager |
|
148 | 150 | |
|
149 | 151 | stylesheet = Unicode('', config=True, |
@@ -169,7 +171,7 b' class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):' | |||
|
169 | 171 | |
|
170 | 172 | def parse_command_line(self, argv=None): |
|
171 | 173 | super(IPythonQtConsoleApp, self).parse_command_line(argv) |
|
172 | self.swallow_args(qt_aliases,qt_flags,argv=argv) | |
|
174 | self.build_kernel_argv(argv) | |
|
173 | 175 | |
|
174 | 176 | |
|
175 | 177 | def new_frontend_master(self): |
@@ -101,11 +101,14 b' class ZMQTerminalIPythonApp(TerminalIPythonApp, IPythonConsoleApp):' | |||
|
101 | 101 | classes = List([IPKernelApp, ZMQTerminalInteractiveShell, Session]) |
|
102 | 102 | flags = Dict(flags) |
|
103 | 103 | aliases = Dict(aliases) |
|
104 | frontend_aliases = Any(frontend_aliases) | |
|
105 | frontend_flags = Any(frontend_flags) | |
|
106 | ||
|
104 | 107 | subcommands = Dict() |
|
105 | 108 | |
|
106 | 109 | def parse_command_line(self, argv=None): |
|
107 | 110 | super(ZMQTerminalIPythonApp, self).parse_command_line(argv) |
|
108 | self.swallow_args(frontend_aliases,frontend_flags,argv=argv) | |
|
111 | self.build_kernel_argv(argv) | |
|
109 | 112 | |
|
110 | 113 | def init_shell(self): |
|
111 | 114 | IPythonConsoleApp.initialize(self) |
@@ -253,3 +253,63 b' def tunnel_to_kernel(connection_info, sshserver, sshkey=None):' | |||
|
253 | 253 | return tuple(lports) |
|
254 | 254 | |
|
255 | 255 | |
|
256 | def swallow_argv(argv, aliases=None, flags=None): | |
|
257 | """strip frontend-specific aliases and flags from an argument list | |
|
258 | ||
|
259 | For use primarily in frontend apps that want to pass a subset of command-line | |
|
260 | arguments through to a subprocess, where frontend-specific flags and aliases | |
|
261 | should be removed from the list. | |
|
262 | ||
|
263 | Parameters | |
|
264 | ---------- | |
|
265 | ||
|
266 | argv : list(str) | |
|
267 | The starting argv, to be filtered | |
|
268 | aliases : container of aliases (dict, list, set, etc.) | |
|
269 | The frontend-specific aliases to be removed | |
|
270 | flags : container of flags (dict, list, set, etc.) | |
|
271 | The frontend-specific flags to be removed | |
|
272 | ||
|
273 | Returns | |
|
274 | ------- | |
|
275 | ||
|
276 | argv : list(str) | |
|
277 | The argv list, excluding flags and aliases that have been stripped | |
|
278 | """ | |
|
279 | ||
|
280 | if aliases is None: | |
|
281 | aliases = set() | |
|
282 | if flags is None: | |
|
283 | flags = set() | |
|
284 | ||
|
285 | stripped = list(argv) # copy | |
|
286 | ||
|
287 | swallow_next = False | |
|
288 | was_flag = False | |
|
289 | for a in argv: | |
|
290 | if swallow_next: | |
|
291 | swallow_next = False | |
|
292 | # last arg was an alias, remove the next one | |
|
293 | # *unless* the last alias has a no-arg flag version, in which | |
|
294 | # case, don't swallow the next arg if it's also a flag: | |
|
295 | if not (was_flag and a.startswith('-')): | |
|
296 | stripped.remove(a) | |
|
297 | continue | |
|
298 | if a.startswith('-'): | |
|
299 | split = a.lstrip('-').split('=') | |
|
300 | alias = split[0] | |
|
301 | if alias in aliases: | |
|
302 | stripped.remove(a) | |
|
303 | if len(split) == 1: | |
|
304 | # alias passed with arg via space | |
|
305 | swallow_next = True | |
|
306 | # could have been a flag that matches an alias, e.g. `existing` | |
|
307 | # in which case, we might not swallow the next arg | |
|
308 | was_flag = alias in flags | |
|
309 | elif alias in flags and len(split) == 1: | |
|
310 | # strip flag, but don't swallow next, as flags don't take args | |
|
311 | stripped.remove(a) | |
|
312 | ||
|
313 | # return shortened list | |
|
314 | return stripped | |
|
315 |
General Comments 0
You need to be logged in to leave comments.
Login now