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 | from IPython.config.application import boolean_flag |
|
33 | from IPython.config.application import boolean_flag | |
34 | from IPython.config.configurable import Configurable |
|
34 | from IPython.config.configurable import Configurable | |
35 | from IPython.core.profiledir import ProfileDir |
|
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 | from IPython.zmq.blockingkernelmanager import BlockingKernelManager |
|
37 | from IPython.zmq.blockingkernelmanager import BlockingKernelManager | |
38 | from IPython.utils.path import filefind |
|
38 | from IPython.utils.path import filefind | |
39 | from IPython.utils.py3compat import str_to_bytes |
|
39 | from IPython.utils.py3compat import str_to_bytes | |
@@ -136,6 +136,9 b' class IPythonConsoleApp(Configurable):' | |||||
136 | kernel_manager_class = BlockingKernelManager |
|
136 | kernel_manager_class = BlockingKernelManager | |
137 |
|
137 | |||
138 | kernel_argv = List(Unicode) |
|
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 | pure = CBool(False, config=True, |
|
143 | pure = CBool(False, config=True, | |
141 | help="Use a pure Python kernel instead of an IPython kernel.") |
|
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 |
|
188 | def build_kernel_argv(self, argv=None): | |
186 | #super(PythonBaseConsoleApp, self).parse_command_line(argv) |
|
189 | """build argv to be passed to kernel subprocess""" | |
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): |
|
|||
193 | if argv is None: |
|
190 | if argv is None: | |
194 | argv = sys.argv[1:] |
|
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 | # kernel should inherit default config file from frontend |
|
193 | # kernel should inherit default config file from frontend | |
197 | self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name) |
|
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 | def init_connection_file(self): |
|
196 | def init_connection_file(self): | |
226 | """find the connection file, and load the info if found. |
|
197 | """find the connection file, and load the info if found. |
@@ -55,6 +55,7 b' from .notebookmanager import NotebookManager' | |||||
55 | from IPython.config.application import catch_config_error |
|
55 | from IPython.config.application import catch_config_error | |
56 | from IPython.core.application import BaseIPythonApplication |
|
56 | from IPython.core.application import BaseIPythonApplication | |
57 | from IPython.core.profiledir import ProfileDir |
|
57 | from IPython.core.profiledir import ProfileDir | |
|
58 | from IPython.lib.kernel import swallow_argv | |||
58 | from IPython.zmq.session import Session, default_secure |
|
59 | from IPython.zmq.session import Session, default_secure | |
59 | from IPython.zmq.zmqshell import ZMQInteractiveShell |
|
60 | from IPython.zmq.zmqshell import ZMQInteractiveShell | |
60 | from IPython.zmq.ipkernel import ( |
|
61 | from IPython.zmq.ipkernel import ( | |
@@ -283,27 +284,10 b' class NotebookApp(BaseIPythonApplication):' | |||||
283 | if argv is None: |
|
284 | if argv is None: | |
284 | argv = sys.argv[1:] |
|
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 | # Kernel should inherit default config file from frontend |
|
289 | # Kernel should inherit default config file from frontend | |
288 | self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name) |
|
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 | def init_configurables(self): |
|
292 | def init_configurables(self): | |
309 | # Don't let Qt or ZMQ swallow KeyboardInterupts. |
|
293 | # Don't let Qt or ZMQ swallow KeyboardInterupts. |
@@ -144,6 +144,8 b' class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):' | |||||
144 | classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session] |
|
144 | classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session] | |
145 | flags = Dict(flags) |
|
145 | flags = Dict(flags) | |
146 | aliases = Dict(aliases) |
|
146 | aliases = Dict(aliases) | |
|
147 | frontend_flags = Any(qt_flags) | |||
|
148 | frontend_aliases = Any(qt_aliases) | |||
147 | kernel_manager_class = QtKernelManager |
|
149 | kernel_manager_class = QtKernelManager | |
148 |
|
150 | |||
149 | stylesheet = Unicode('', config=True, |
|
151 | stylesheet = Unicode('', config=True, | |
@@ -169,7 +171,7 b' class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):' | |||||
169 |
|
171 | |||
170 | def parse_command_line(self, argv=None): |
|
172 | def parse_command_line(self, argv=None): | |
171 | super(IPythonQtConsoleApp, self).parse_command_line(argv) |
|
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 | def new_frontend_master(self): |
|
177 | def new_frontend_master(self): |
@@ -101,11 +101,14 b' class ZMQTerminalIPythonApp(TerminalIPythonApp, IPythonConsoleApp):' | |||||
101 | classes = List([IPKernelApp, ZMQTerminalInteractiveShell, Session]) |
|
101 | classes = List([IPKernelApp, ZMQTerminalInteractiveShell, Session]) | |
102 | flags = Dict(flags) |
|
102 | flags = Dict(flags) | |
103 | aliases = Dict(aliases) |
|
103 | aliases = Dict(aliases) | |
|
104 | frontend_aliases = Any(frontend_aliases) | |||
|
105 | frontend_flags = Any(frontend_flags) | |||
|
106 | ||||
104 | subcommands = Dict() |
|
107 | subcommands = Dict() | |
105 |
|
108 | |||
106 | def parse_command_line(self, argv=None): |
|
109 | def parse_command_line(self, argv=None): | |
107 | super(ZMQTerminalIPythonApp, self).parse_command_line(argv) |
|
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 | def init_shell(self): |
|
113 | def init_shell(self): | |
111 | IPythonConsoleApp.initialize(self) |
|
114 | IPythonConsoleApp.initialize(self) |
@@ -253,3 +253,63 b' def tunnel_to_kernel(connection_info, sshserver, sshkey=None):' | |||||
253 | return tuple(lports) |
|
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