##// END OF EJS Templates
Backport PR #14169: Set instance on singleton configurable when created via constructor.
Matthias Bussonnier -
Show More
@@ -1,420 +1,426 b''
1 1 # encoding: utf-8
2 2 """
3 3 An embedded IPython shell.
4 4 """
5 5 # Copyright (c) IPython Development Team.
6 6 # Distributed under the terms of the Modified BSD License.
7 7
8 8
9 9 import sys
10 10 import warnings
11 11
12 12 from IPython.core import ultratb, compilerop
13 13 from IPython.core import magic_arguments
14 14 from IPython.core.magic import Magics, magics_class, line_magic
15 15 from IPython.core.interactiveshell import DummyMod, InteractiveShell
16 16 from IPython.terminal.interactiveshell import TerminalInteractiveShell
17 17 from IPython.terminal.ipapp import load_default_config
18 18
19 19 from traitlets import Bool, CBool, Unicode
20 20 from IPython.utils.io import ask_yes_no
21 21
22 22 from typing import Set
23 23
24 24 class KillEmbedded(Exception):pass
25 25
26 26 # kept for backward compatibility as IPython 6 was released with
27 27 # the typo. See https://github.com/ipython/ipython/pull/10706
28 28 KillEmbeded = KillEmbedded
29 29
30 30 # This is an additional magic that is exposed in embedded shells.
31 31 @magics_class
32 32 class EmbeddedMagics(Magics):
33 33
34 34 @line_magic
35 35 @magic_arguments.magic_arguments()
36 36 @magic_arguments.argument('-i', '--instance', action='store_true',
37 37 help='Kill instance instead of call location')
38 38 @magic_arguments.argument('-x', '--exit', action='store_true',
39 39 help='Also exit the current session')
40 40 @magic_arguments.argument('-y', '--yes', action='store_true',
41 41 help='Do not ask confirmation')
42 42 def kill_embedded(self, parameter_s=''):
43 43 """%kill_embedded : deactivate for good the current embedded IPython
44 44
45 45 This function (after asking for confirmation) sets an internal flag so
46 46 that an embedded IPython will never activate again for the given call
47 47 location. This is useful to permanently disable a shell that is being
48 48 called inside a loop: once you've figured out what you needed from it,
49 49 you may then kill it and the program will then continue to run without
50 50 the interactive shell interfering again.
51 51
52 52 Kill Instance Option:
53 53
54 54 If for some reasons you need to kill the location where the instance
55 55 is created and not called, for example if you create a single
56 56 instance in one place and debug in many locations, you can use the
57 57 ``--instance`` option to kill this specific instance. Like for the
58 58 ``call location`` killing an "instance" should work even if it is
59 59 recreated within a loop.
60 60
61 61 .. note::
62 62
63 63 This was the default behavior before IPython 5.2
64 64
65 65 """
66 66
67 67 args = magic_arguments.parse_argstring(self.kill_embedded, parameter_s)
68 68 print(args)
69 69 if args.instance:
70 70 # let no ask
71 71 if not args.yes:
72 72 kill = ask_yes_no(
73 73 "Are you sure you want to kill this embedded instance? [y/N] ", 'n')
74 74 else:
75 75 kill = True
76 76 if kill:
77 77 self.shell._disable_init_location()
78 78 print("This embedded IPython instance will not reactivate anymore "
79 79 "once you exit.")
80 80 else:
81 81 if not args.yes:
82 82 kill = ask_yes_no(
83 83 "Are you sure you want to kill this embedded call_location? [y/N] ", 'n')
84 84 else:
85 85 kill = True
86 86 if kill:
87 87 self.shell.embedded_active = False
88 88 print("This embedded IPython call location will not reactivate anymore "
89 89 "once you exit.")
90 90
91 91 if args.exit:
92 92 # Ask-exit does not really ask, it just set internals flags to exit
93 93 # on next loop.
94 94 self.shell.ask_exit()
95 95
96 96
97 97 @line_magic
98 98 def exit_raise(self, parameter_s=''):
99 99 """%exit_raise Make the current embedded kernel exit and raise and exception.
100 100
101 101 This function sets an internal flag so that an embedded IPython will
102 102 raise a `IPython.terminal.embed.KillEmbedded` Exception on exit, and then exit the current I. This is
103 103 useful to permanently exit a loop that create IPython embed instance.
104 104 """
105 105
106 106 self.shell.should_raise = True
107 107 self.shell.ask_exit()
108 108
109 109
110 110 class _Sentinel:
111 111 def __init__(self, repr):
112 112 assert isinstance(repr, str)
113 113 self.repr = repr
114 114
115 115 def __repr__(self):
116 116 return repr
117 117
118 118
119 119 class InteractiveShellEmbed(TerminalInteractiveShell):
120 120
121 121 dummy_mode = Bool(False)
122 122 exit_msg = Unicode('')
123 123 embedded = CBool(True)
124 124 should_raise = CBool(False)
125 125 # Like the base class display_banner is not configurable, but here it
126 126 # is True by default.
127 127 display_banner = CBool(True)
128 128 exit_msg = Unicode()
129 129
130 130 # When embedding, by default we don't change the terminal title
131 131 term_title = Bool(False,
132 132 help="Automatically set the terminal title"
133 133 ).tag(config=True)
134 134
135 135 _inactive_locations: Set[str] = set()
136 136
137 137 def _disable_init_location(self):
138 138 """Disable the current Instance creation location"""
139 139 InteractiveShellEmbed._inactive_locations.add(self._init_location_id)
140 140
141 141 @property
142 142 def embedded_active(self):
143 143 return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\
144 144 and (self._init_location_id not in InteractiveShellEmbed._inactive_locations)
145 145
146 146 @embedded_active.setter
147 147 def embedded_active(self, value):
148 148 if value:
149 149 InteractiveShellEmbed._inactive_locations.discard(
150 150 self._call_location_id)
151 151 InteractiveShellEmbed._inactive_locations.discard(
152 152 self._init_location_id)
153 153 else:
154 154 InteractiveShellEmbed._inactive_locations.add(
155 155 self._call_location_id)
156 156
157 157 def __init__(self, **kw):
158 158 assert (
159 159 "user_global_ns" not in kw
160 160 ), "Key word argument `user_global_ns` has been replaced by `user_module` since IPython 4.0."
161 # temporary fix for https://github.com/ipython/ipython/issues/14164
162 cls = type(self)
163 if cls._instance is None:
164 for subclass in cls._walk_mro():
165 subclass._instance = self
166 cls._instance = self
161 167
162 168 clid = kw.pop('_init_location_id', None)
163 169 if not clid:
164 170 frame = sys._getframe(1)
165 171 clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
166 172 self._init_location_id = clid
167 173
168 174 super(InteractiveShellEmbed,self).__init__(**kw)
169 175
170 176 # don't use the ipython crash handler so that user exceptions aren't
171 177 # trapped
172 178 sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors,
173 179 mode=self.xmode,
174 180 call_pdb=self.pdb)
175 181
176 182 def init_sys_modules(self):
177 183 """
178 184 Explicitly overwrite :mod:`IPython.core.interactiveshell` to do nothing.
179 185 """
180 186 pass
181 187
182 188 def init_magics(self):
183 189 super(InteractiveShellEmbed, self).init_magics()
184 190 self.register_magics(EmbeddedMagics)
185 191
186 192 def __call__(
187 193 self,
188 194 header="",
189 195 local_ns=None,
190 196 module=None,
191 197 dummy=None,
192 198 stack_depth=1,
193 199 compile_flags=None,
194 200 **kw
195 201 ):
196 202 """Activate the interactive interpreter.
197 203
198 204 __call__(self,header='',local_ns=None,module=None,dummy=None) -> Start
199 205 the interpreter shell with the given local and global namespaces, and
200 206 optionally print a header string at startup.
201 207
202 208 The shell can be globally activated/deactivated using the
203 209 dummy_mode attribute. This allows you to turn off a shell used
204 210 for debugging globally.
205 211
206 212 However, *each* time you call the shell you can override the current
207 213 state of dummy_mode with the optional keyword parameter 'dummy'. For
208 214 example, if you set dummy mode on with IPShell.dummy_mode = True, you
209 215 can still have a specific call work by making it as IPShell(dummy=False).
210 216 """
211 217
212 218 # we are called, set the underlying interactiveshell not to exit.
213 219 self.keep_running = True
214 220
215 221 # If the user has turned it off, go away
216 222 clid = kw.pop('_call_location_id', None)
217 223 if not clid:
218 224 frame = sys._getframe(1)
219 225 clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
220 226 self._call_location_id = clid
221 227
222 228 if not self.embedded_active:
223 229 return
224 230
225 231 # Normal exits from interactive mode set this flag, so the shell can't
226 232 # re-enter (it checks this variable at the start of interactive mode).
227 233 self.exit_now = False
228 234
229 235 # Allow the dummy parameter to override the global __dummy_mode
230 236 if dummy or (dummy != 0 and self.dummy_mode):
231 237 return
232 238
233 239 # self.banner is auto computed
234 240 if header:
235 241 self.old_banner2 = self.banner2
236 242 self.banner2 = self.banner2 + '\n' + header + '\n'
237 243 else:
238 244 self.old_banner2 = ''
239 245
240 246 if self.display_banner:
241 247 self.show_banner()
242 248
243 249 # Call the embedding code with a stack depth of 1 so it can skip over
244 250 # our call and get the original caller's namespaces.
245 251 self.mainloop(
246 252 local_ns, module, stack_depth=stack_depth, compile_flags=compile_flags
247 253 )
248 254
249 255 self.banner2 = self.old_banner2
250 256
251 257 if self.exit_msg is not None:
252 258 print(self.exit_msg)
253 259
254 260 if self.should_raise:
255 261 raise KillEmbedded('Embedded IPython raising error, as user requested.')
256 262
257 263 def mainloop(
258 264 self,
259 265 local_ns=None,
260 266 module=None,
261 267 stack_depth=0,
262 268 compile_flags=None,
263 269 ):
264 270 """Embeds IPython into a running python program.
265 271
266 272 Parameters
267 273 ----------
268 274 local_ns, module
269 275 Working local namespace (a dict) and module (a module or similar
270 276 object). If given as None, they are automatically taken from the scope
271 277 where the shell was called, so that program variables become visible.
272 278 stack_depth : int
273 279 How many levels in the stack to go to looking for namespaces (when
274 280 local_ns or module is None). This allows an intermediate caller to
275 281 make sure that this function gets the namespace from the intended
276 282 level in the stack. By default (0) it will get its locals and globals
277 283 from the immediate caller.
278 284 compile_flags
279 285 A bit field identifying the __future__ features
280 286 that are enabled, as passed to the builtin :func:`compile` function.
281 287 If given as None, they are automatically taken from the scope where
282 288 the shell was called.
283 289
284 290 """
285 291
286 292 # Get locals and globals from caller
287 293 if ((local_ns is None or module is None or compile_flags is None)
288 294 and self.default_user_namespaces):
289 295 call_frame = sys._getframe(stack_depth).f_back
290 296
291 297 if local_ns is None:
292 298 local_ns = call_frame.f_locals
293 299 if module is None:
294 300 global_ns = call_frame.f_globals
295 301 try:
296 302 module = sys.modules[global_ns['__name__']]
297 303 except KeyError:
298 304 warnings.warn("Failed to get module %s" % \
299 305 global_ns.get('__name__', 'unknown module')
300 306 )
301 307 module = DummyMod()
302 308 module.__dict__ = global_ns
303 309 if compile_flags is None:
304 310 compile_flags = (call_frame.f_code.co_flags &
305 311 compilerop.PyCF_MASK)
306 312
307 313 # Save original namespace and module so we can restore them after
308 314 # embedding; otherwise the shell doesn't shut down correctly.
309 315 orig_user_module = self.user_module
310 316 orig_user_ns = self.user_ns
311 317 orig_compile_flags = self.compile.flags
312 318
313 319 # Update namespaces and fire up interpreter
314 320
315 321 # The global one is easy, we can just throw it in
316 322 if module is not None:
317 323 self.user_module = module
318 324
319 325 # But the user/local one is tricky: ipython needs it to store internal
320 326 # data, but we also need the locals. We'll throw our hidden variables
321 327 # like _ih and get_ipython() into the local namespace, but delete them
322 328 # later.
323 329 if local_ns is not None:
324 330 reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()}
325 331 self.user_ns = reentrant_local_ns
326 332 self.init_user_ns()
327 333
328 334 # Compiler flags
329 335 if compile_flags is not None:
330 336 self.compile.flags = compile_flags
331 337
332 338 # make sure the tab-completer has the correct frame information, so it
333 339 # actually completes using the frame's locals/globals
334 340 self.set_completer_frame()
335 341
336 342 with self.builtin_trap, self.display_trap:
337 343 self.interact()
338 344
339 345 # now, purge out the local namespace of IPython's hidden variables.
340 346 if local_ns is not None:
341 347 local_ns.update({k: v for (k, v) in self.user_ns.items() if k not in self.user_ns_hidden.keys()})
342 348
343 349
344 350 # Restore original namespace so shell can shut down when we exit.
345 351 self.user_module = orig_user_module
346 352 self.user_ns = orig_user_ns
347 353 self.compile.flags = orig_compile_flags
348 354
349 355
350 356 def embed(*, header="", compile_flags=None, **kwargs):
351 357 """Call this to embed IPython at the current point in your program.
352 358
353 359 The first invocation of this will create a :class:`terminal.embed.InteractiveShellEmbed`
354 360 instance and then call it. Consecutive calls just call the already
355 361 created instance.
356 362
357 363 If you don't want the kernel to initialize the namespace
358 364 from the scope of the surrounding function,
359 365 and/or you want to load full IPython configuration,
360 366 you probably want `IPython.start_ipython()` instead.
361 367
362 368 Here is a simple example::
363 369
364 370 from IPython import embed
365 371 a = 10
366 372 b = 20
367 373 embed(header='First time')
368 374 c = 30
369 375 d = 40
370 376 embed()
371 377
372 378 Parameters
373 379 ----------
374 380
375 381 header : str
376 382 Optional header string to print at startup.
377 383 compile_flags
378 384 Passed to the `compile_flags` parameter of :py:meth:`terminal.embed.InteractiveShellEmbed.mainloop()`,
379 385 which is called when the :class:`terminal.embed.InteractiveShellEmbed` instance is called.
380 386 **kwargs : various, optional
381 387 Any other kwargs will be passed to the :class:`terminal.embed.InteractiveShellEmbed` constructor.
382 388 Full customization can be done by passing a traitlets :class:`Config` in as the
383 389 `config` argument (see :ref:`configure_start_ipython` and :ref:`terminal_options`).
384 390 """
385 391 config = kwargs.get('config')
386 392 if config is None:
387 393 config = load_default_config()
388 394 config.InteractiveShellEmbed = config.TerminalInteractiveShell
389 395 kwargs['config'] = config
390 396 using = kwargs.get('using', 'sync')
391 397 if using :
392 398 kwargs['config'].update({'TerminalInteractiveShell':{'loop_runner':using, 'colors':'NoColor', 'autoawait': using!='sync'}})
393 399 #save ps1/ps2 if defined
394 400 ps1 = None
395 401 ps2 = None
396 402 try:
397 403 ps1 = sys.ps1
398 404 ps2 = sys.ps2
399 405 except AttributeError:
400 406 pass
401 407 #save previous instance
402 408 saved_shell_instance = InteractiveShell._instance
403 409 if saved_shell_instance is not None:
404 410 cls = type(saved_shell_instance)
405 411 cls.clear_instance()
406 412 frame = sys._getframe(1)
407 413 shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % (
408 414 frame.f_code.co_filename, frame.f_lineno), **kwargs)
409 415 shell(header=header, stack_depth=2, compile_flags=compile_flags,
410 416 _call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno))
411 417 InteractiveShellEmbed.clear_instance()
412 418 #restore previous instance
413 419 if saved_shell_instance is not None:
414 420 cls = type(saved_shell_instance)
415 421 cls.clear_instance()
416 422 for subclass in cls._walk_mro():
417 423 subclass._instance = saved_shell_instance
418 424 if ps1 is not None:
419 425 sys.ps1 = ps1
420 426 sys.ps2 = ps2
General Comments 0
You need to be logged in to leave comments. Login now