pyflyby 1.10.4__cp311-cp311-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. pyflyby/__init__.py +61 -0
  2. pyflyby/__main__.py +9 -0
  3. pyflyby/_autoimp.py +2228 -0
  4. pyflyby/_cmdline.py +591 -0
  5. pyflyby/_comms.py +221 -0
  6. pyflyby/_dbg.py +1383 -0
  7. pyflyby/_dynimp.py +154 -0
  8. pyflyby/_fast_iter_modules.cpython-311-darwin.so +0 -0
  9. pyflyby/_file.py +771 -0
  10. pyflyby/_flags.py +230 -0
  11. pyflyby/_format.py +186 -0
  12. pyflyby/_idents.py +227 -0
  13. pyflyby/_import_sorting.py +165 -0
  14. pyflyby/_importclns.py +658 -0
  15. pyflyby/_importdb.py +535 -0
  16. pyflyby/_imports2s.py +643 -0
  17. pyflyby/_importstmt.py +723 -0
  18. pyflyby/_interactive.py +2113 -0
  19. pyflyby/_livepatch.py +793 -0
  20. pyflyby/_log.py +107 -0
  21. pyflyby/_modules.py +646 -0
  22. pyflyby/_parse.py +1396 -0
  23. pyflyby/_py.py +2165 -0
  24. pyflyby/_saveframe.py +1145 -0
  25. pyflyby/_saveframe_reader.py +471 -0
  26. pyflyby/_util.py +458 -0
  27. pyflyby/_version.py +8 -0
  28. pyflyby/autoimport.py +20 -0
  29. pyflyby/etc/pyflyby/canonical.py +10 -0
  30. pyflyby/etc/pyflyby/common.py +27 -0
  31. pyflyby/etc/pyflyby/forget.py +10 -0
  32. pyflyby/etc/pyflyby/mandatory.py +10 -0
  33. pyflyby/etc/pyflyby/numpy.py +156 -0
  34. pyflyby/etc/pyflyby/std.py +335 -0
  35. pyflyby/importdb.py +19 -0
  36. pyflyby/libexec/pyflyby/colordiff +34 -0
  37. pyflyby/libexec/pyflyby/diff-colorize +148 -0
  38. pyflyby/share/emacs/site-lisp/pyflyby.el +112 -0
  39. pyflyby-1.10.4.data/scripts/collect-exports +76 -0
  40. pyflyby-1.10.4.data/scripts/collect-imports +58 -0
  41. pyflyby-1.10.4.data/scripts/find-import +38 -0
  42. pyflyby-1.10.4.data/scripts/prune-broken-imports +34 -0
  43. pyflyby-1.10.4.data/scripts/pyflyby-diff +34 -0
  44. pyflyby-1.10.4.data/scripts/reformat-imports +27 -0
  45. pyflyby-1.10.4.data/scripts/replace-star-imports +37 -0
  46. pyflyby-1.10.4.data/scripts/saveframe +299 -0
  47. pyflyby-1.10.4.data/scripts/tidy-imports +170 -0
  48. pyflyby-1.10.4.data/scripts/transform-imports +47 -0
  49. pyflyby-1.10.4.dist-info/METADATA +605 -0
  50. pyflyby-1.10.4.dist-info/RECORD +53 -0
  51. pyflyby-1.10.4.dist-info/WHEEL +6 -0
  52. pyflyby-1.10.4.dist-info/entry_points.txt +4 -0
  53. pyflyby-1.10.4.dist-info/licenses/LICENSE.txt +19 -0
@@ -0,0 +1,2113 @@
1
+ # pyflyby/_interactive.py.
2
+ # Copyright (C) 2011, 2012, 2013, 2014, 2015, 2018 Karl Chen.
3
+ # License: MIT http://opensource.org/licenses/MIT
4
+
5
+ import ast
6
+ import builtins
7
+ from contextlib import contextmanager
8
+ import errno
9
+ import inspect
10
+ import operator
11
+ import os
12
+ import re
13
+ import subprocess
14
+ import sys
15
+
16
+ from typing import Any, Dict, List, Literal, Union
17
+
18
+
19
+ from pyflyby._autoimp import (ScopeStack, auto_import,
20
+ auto_import_symbol,
21
+ clear_failed_imports_cache)
22
+ from pyflyby._comms import (MISSING_IMPORTS, initialize_comms,
23
+ remove_comms, send_comm_message)
24
+ from pyflyby._dynimp import (PYFLYBY_LAZY_LOAD_PREFIX,
25
+ inject as inject_dynamic_import)
26
+ from pyflyby._file import Filename, atomic_write_file, read_file
27
+ from pyflyby._idents import is_identifier
28
+ from pyflyby._importdb import ImportDB
29
+ from pyflyby._log import logger
30
+ from pyflyby._modules import ModuleHandle
31
+ from pyflyby._parse import PythonBlock
32
+ from pyflyby._util import (AdviceCtx, Aspect, CwdCtx,
33
+ FunctionWithGlobals, advise, indent)
34
+
35
+ if False:
36
+ __original__ = None # for pyflakes
37
+
38
+
39
+ get_method_self = operator.attrgetter('__self__')
40
+
41
+ # TODO: also support arbitrary code (in the form of a lambda and/or
42
+ # assignment) as new way to do "lazy" creations, e.g. foo = a.b.c(d.e+f.g())
43
+
44
+
45
+ class NoIPythonPackageError(Exception):
46
+ """
47
+ Exception raised when the IPython package is not installed in the system.
48
+ """
49
+
50
+
51
+ class NoActiveIPythonAppError(Exception):
52
+ """
53
+ Exception raised when there is no current IPython application instance.
54
+ """
55
+
56
+
57
+ def _get_or_create_ipython_terminal_app():
58
+ """
59
+ Create/get the singleton IPython terminal application.
60
+
61
+ :rtype:
62
+ ``TerminalIPythonApp``
63
+ :raise NoIPythonPackageError:
64
+ IPython is not installed in the system.
65
+ """
66
+ try:
67
+ import IPython
68
+ except ImportError as e:
69
+ raise NoIPythonPackageError(e)
70
+ # The following has been tested on IPython 1.0, 1.2, 2.0, 2.1, 2.2, 2.3.
71
+ try:
72
+ TerminalIPythonApp = IPython.terminal.ipapp.TerminalIPythonApp
73
+ except AttributeError:
74
+ pass
75
+ else:
76
+ return TerminalIPythonApp.instance()
77
+ # The following has been tested on IPython 0.11, 0.12, 0.13.
78
+ try:
79
+ TerminalIPythonApp = IPython.frontend.terminal.ipapp.TerminalIPythonApp
80
+ except AttributeError:
81
+ pass
82
+ else:
83
+ return TerminalIPythonApp.instance()
84
+ raise RuntimeError(
85
+ "Couldn't get TerminalIPythonApp class. "
86
+ "Is your IPython version too old (or too new)? "
87
+ "IPython.__version__=%r" % (IPython.__version__))
88
+
89
+
90
+ def _app_is_initialized(app):
91
+ """
92
+ Return whether ``app.initialize()`` has been called.
93
+
94
+ :type app:
95
+ `IPython.Application`
96
+ :rtype:
97
+ ``bool``
98
+ """
99
+ # There's no official way to tell whether app.initialize() has been called
100
+ # before. We guess whether the app has been initialized by checking
101
+ # whether all traits have values.
102
+ #
103
+ # There's a method app.initialized(), but it doesn't do what we want. It
104
+ # does not return whether app.initialize() has been called - rather,
105
+ # type(app).initialized() returns whether an instance of the class has
106
+ # ever been constructed, i.e. app.initialized() always returns True.
107
+ cache_name = "__is_initialized_54283907"
108
+ if cache_name in app.__dict__:
109
+ return True
110
+ if all(n in app._trait_values for n in app.trait_names()):
111
+ app.__dict__[cache_name] = True
112
+ return True
113
+ else:
114
+ return False
115
+
116
+
117
+
118
+
119
+ class _DummyIPythonEmbeddedApp(object):
120
+ """
121
+ Small wrapper around an `InteractiveShellEmbed`.
122
+ """
123
+
124
+ def __init__(self, shell):
125
+ self.shell = shell
126
+
127
+
128
+
129
+ def _get_or_create_ipython_kernel_app():
130
+ """
131
+ Create/get the singleton IPython kernel application.
132
+
133
+ :rtype:
134
+ ``callable``
135
+ :return:
136
+ The function that can be called to start the kernel application.
137
+ """
138
+ import IPython
139
+ # The following has been tested on IPython 4.0
140
+ try:
141
+ from ipykernel.kernelapp import IPKernelApp
142
+ except ImportError:
143
+ pass
144
+ else:
145
+ return IPKernelApp.instance()
146
+ # The following has been tested on IPython 1.0, 1.2, 2.0, 2.1, 2.2, 2.3,
147
+ # 2.4, 3.0, 3.1, 3.2
148
+ try:
149
+ from IPython.kernel.zmq.kernelapp import IPKernelApp
150
+ except ImportError:
151
+ pass
152
+ else:
153
+ return IPKernelApp.instance()
154
+ # The following has been tested on IPython 0.12, 0.13
155
+ try:
156
+ from IPython.zmq.ipkernel import IPKernelApp
157
+ except ImportError:
158
+ pass
159
+ else:
160
+ return IPKernelApp.instance()
161
+ raise RuntimeError(
162
+ "Couldn't get IPKernelApp class. "
163
+ "Is your IPython version too old (or too new)? "
164
+ "IPython.__version__=%r" % (IPython.__version__))
165
+
166
+
167
+ def get_ipython_terminal_app_with_autoimporter():
168
+ """
169
+ Return an initialized ``TerminalIPythonApp``.
170
+
171
+ If a ``TerminalIPythonApp`` has already been created, then use it (whether
172
+ we are inside that app or not). If there isn't already one, then create
173
+ one. Enable the auto importer, if it hasn't already been enabled. If the
174
+ app hasn't been initialized yet, then initialize() it (but don't start()
175
+ it).
176
+
177
+ :rtype:
178
+ ``TerminalIPythonApp``
179
+ :raise NoIPythonPackageError:
180
+ IPython is not installed in the system.
181
+ """
182
+ app = _get_or_create_ipython_terminal_app()
183
+ AutoImporter(app).enable()
184
+ if not _app_is_initialized(app):
185
+ old_display_banner = app.display_banner
186
+ try:
187
+ app.display_banner = False
188
+ app.initialize([])
189
+ finally:
190
+ app.display_banner = old_display_banner
191
+ return app
192
+
193
+
194
+ def start_ipython_with_autoimporter(argv=None, app=None, _user_ns=None):
195
+ """
196
+ Start IPython (terminal) with autoimporter enabled.
197
+ """
198
+ if app is None:
199
+ subcmd = argv and argv[0]
200
+ if subcmd == 'console':
201
+ # The following has been tested on IPython 5.8 / Jupyter console 5.2.
202
+ # Note: jupyter_console.app.JupyterApp also appears to work in some
203
+ # contexts, but that actually execs the script jupyter-console which
204
+ # uses ZMQTerminalIPythonApp. The exec makes the target use whatever
205
+ # shebang line is in that script, which may be a different python
206
+ # major version than what we're currently running. We want to avoid
207
+ # the exec in general (as a library function) and avoid changing
208
+ # python versions.
209
+ try:
210
+ from ipkernel.app import IPKernelApp
211
+ except (ImportError, AttributeError):
212
+ pass
213
+ else:
214
+ app = IPKernelApp.instance()
215
+ argv = argv[1:]
216
+ elif subcmd == 'notebook':
217
+ try:
218
+ from notebook.notebookapp import NotebookApp
219
+ except (ImportError, AttributeError):
220
+ pass
221
+ else:
222
+ app = NotebookApp.instance()
223
+ argv = argv[1:]
224
+ if app is None:
225
+ app = _get_or_create_ipython_terminal_app()
226
+ if _user_ns is not None:
227
+ # Tested with IPython 1.2, 2.0, 2.1, 2.2, 2.3. 2.4, 3.0, 3.1, 3.2
228
+ # TODO: support older versions of IPython.
229
+ # FIXME TODO: fix attaching debugger to IPython started this way. It
230
+ # has to do with assigning user_ns. Apparently if user_ns["__name__"]
231
+ # is "__main__" (which IPython defaults to, and we want to use
232
+ # anyway), then user_module must be a true ModuleType in order for
233
+ # attaching to work correctly. If you specify user_ns but not
234
+ # user_module, then user_module is a DummyModule rather than a true
235
+ # ModuleType (since ModuleType.__dict__ is read-only). Thus, if we
236
+ # specify user_ns, we should specify user_module also. However, while
237
+ # user_module is a constructor parameter to InteractiveShell,
238
+ # IPythonTerminalApp doesn't pass that parameter to it. We can't
239
+ # assign after initialize() because user_module and user_ns are
240
+ # already used during initialization. One workaround idea is to let
241
+ # IPython initialize without specifying either user_ns or user_module,
242
+ # and then patch in members. However, that has the downside of
243
+ # breaking func_globals of lambdas, e.g. if a script does 'def f():
244
+ # global x; x=4', then we run it with 'py -i', our globals dict won't
245
+ # be the same dict. We should create a true ModuleType anyway even if
246
+ # not using IPython. We might need to resort to advising
247
+ # init_create_namespaces etc. depending on IPython version.
248
+ if getattr(app, 'shell', None) is not None:
249
+ app.shell.user_ns.update(_user_ns)
250
+ else:
251
+ app.user_ns = _user_ns
252
+ return _initialize_and_start_app_with_autoimporter(app, argv)
253
+
254
+
255
+ def start_ipython_kernel_with_autoimporter(argv=None):
256
+ """
257
+ Start IPython kernel with autoimporter enabled.
258
+ """
259
+ app = _get_or_create_ipython_kernel_app()
260
+ return _initialize_and_start_app_with_autoimporter(app, argv)
261
+
262
+
263
+ def _initialize_and_start_app_with_autoimporter(app, argv):
264
+ """
265
+ Initialize and start an IPython app, with autoimporting enabled.
266
+
267
+ :type app:
268
+ `BaseIPythonApplication`
269
+ """
270
+ # Enable the auto importer.
271
+ AutoImporter(app).enable()
272
+ # Save the value of the "_" name in the user namespace, to avoid
273
+ # initialize() clobbering it.
274
+ user_ns = getattr(app, "user_ns", None)
275
+ saved_user_ns = {}
276
+ if user_ns is not None:
277
+ for k in ["_"]:
278
+ try:
279
+ saved_user_ns[k] = user_ns[k]
280
+ except KeyError:
281
+ pass
282
+ # Initialize the app.
283
+ if not _app_is_initialized(app):
284
+ app.initialize(argv)
285
+ if user_ns is not None:
286
+ user_ns.update(saved_user_ns)
287
+ # Start the app mainloop.
288
+ return app.start()
289
+
290
+
291
+ def run_ipython_line_magic(arg):
292
+ """
293
+ Run IPython magic command.
294
+ If necessary, start an IPython terminal app to do so.
295
+ """
296
+ import IPython
297
+ if not arg.startswith("%"):
298
+ arg = "%" + arg
299
+ app = _get_or_create_ipython_terminal_app()
300
+ AutoImporter(app).enable()
301
+ # TODO: only initialize if not already initialized.
302
+ if not _app_is_initialized(app):
303
+ app.initialize([])
304
+ ip = app.shell
305
+ if hasattr(ip, "magic"):
306
+ # IPython 0.11+.
307
+ # The following has been tested on IPython 0.11, 0.12, 0.13, 1.0, 1.2,
308
+ # 2.0, 2.1, 2.2, 2.3.
309
+ # TODO: may want to wrap in one or two layers of dummy functions to make
310
+ # sure run_line_magic() doesn't inspect our locals.
311
+ return ip.magic(arg)
312
+ elif hasattr(ip, "runlines"):
313
+ # IPython 0.10
314
+ return ip.runlines(arg)
315
+ else:
316
+ raise RuntimeError(
317
+ "Couldn't run IPython magic. "
318
+ "Is your IPython version too old (or too new)? "
319
+ "IPython.__version__=%r" % (IPython.__version__))
320
+
321
+
322
+ def _python_can_import_pyflyby(expected_path, sys_path_entry=None):
323
+ """
324
+ Try to figure out whether python (when started from scratch) can get the
325
+ same pyflyby package as the current process.
326
+ """
327
+ with CwdCtx("/"):
328
+ cmd = 'import pyflyby; print(pyflyby.__path__[0])'
329
+ if sys_path_entry is not None:
330
+ impcmd = "import sys; sys.path.insert(0, %r)\n" % (sys_path_entry,)
331
+ cmd = impcmd + cmd
332
+ proc = subprocess.Popen(
333
+ [sys.executable, '-c', cmd],
334
+ stdin=open("/dev/null"),
335
+ stdout=subprocess.PIPE,
336
+ stderr=open("/dev/null",'w'))
337
+ result = proc.communicate()[0].strip()
338
+ if not result:
339
+ return False
340
+ try:
341
+ return os.path.samefile(result, expected_path)
342
+ except OSError:
343
+ return False
344
+
345
+
346
+ def install_in_ipython_config_file():
347
+ """
348
+ Install the call to 'pyflyby.enable_auto_importer()' to the default
349
+ IPython startup file.
350
+
351
+ This makes all "ipython" sessions behave like "autoipython", i.e. start
352
+ with the autoimporter already enabled.
353
+ """
354
+ import IPython
355
+ # The following has been tested on IPython 4.0, 5.0
356
+ try:
357
+ IPython.paths
358
+ except AttributeError:
359
+ pass
360
+ else:
361
+ _install_in_ipython_config_file_40()
362
+ return
363
+
364
+ raise RuntimeError(
365
+ "Couldn't install pyflyby autoimporter in IPython. "
366
+ "Is your IPython version too old (or too new)? "
367
+ "IPython.__version__=%r" % (IPython.__version__))
368
+
369
+
370
+ def _generate_enabler_code():
371
+ """
372
+ Generate code for enabling the auto importer.
373
+
374
+ :rtype:
375
+ ``str``
376
+ """
377
+ funcdef = (
378
+ "import pyflyby\n"
379
+ "pyflyby.enable_auto_importer()\n"
380
+ )
381
+ # Check whether we need to include the path in sys.path, and if so, add
382
+ # that to the contents.
383
+ import pyflyby
384
+ pyflyby_path = pyflyby.__path__[0]
385
+ if not _python_can_import_pyflyby(pyflyby_path):
386
+ path_entry = os.path.dirname(os.path.realpath(pyflyby_path))
387
+ assert _python_can_import_pyflyby(pyflyby_path, path_entry)
388
+ funcdef = (
389
+ "import sys\n"
390
+ "saved_sys_path = sys.path[:]\n"
391
+ "try:\n"
392
+ " sys.path.insert(0, %r)\n" % (path_entry,) +
393
+ indent(funcdef, " ") +
394
+ "finally:\n"
395
+ " sys.path = saved_sys_path\n"
396
+ )
397
+ # Wrap the code in a temporary function, call it, then delete the
398
+ # function. This avoids polluting the user's global namespace. Although
399
+ # the global name "pyflyby" will almost always end up meaning the module
400
+ # "pyflyby" anyway, if the user types it, there's still value in not
401
+ # polluting the namespace in case something enumerates over globals().
402
+ # For the function name we use a name that's unlikely to be used by the
403
+ # user.
404
+ contents = (
405
+ "def __pyflyby_enable_auto_importer_60321389():\n" +
406
+ indent(funcdef, " ") +
407
+ "__pyflyby_enable_auto_importer_60321389()\n"
408
+ "del __pyflyby_enable_auto_importer_60321389\n"
409
+ )
410
+ return contents
411
+
412
+
413
+ def _install_in_ipython_config_file_40():
414
+ """
415
+ Implementation of `install_in_ipython_config_file` for IPython 4.0+.
416
+ """
417
+ import IPython
418
+ ipython_dir = Filename(IPython.paths.get_ipython_dir())
419
+ if not ipython_dir.isdir:
420
+ raise RuntimeError(
421
+ "Couldn't find IPython config dir. Tried %s" % (ipython_dir,))
422
+
423
+ # Add to extensions list in ~/.ipython/profile_default/ipython_config.py
424
+ config_fn = ipython_dir / "profile_default" / "ipython_config.py"
425
+ if not config_fn.exists:
426
+ subprocess.call(['ipython', 'profile', 'create'])
427
+ if not config_fn.exists:
428
+ raise RuntimeError(
429
+ "Couldn't find IPython config file. Tried %s" % (config_fn,))
430
+ old_config_blob = read_file(config_fn)
431
+ # This is the line we'll add.
432
+ line_to_add = 'c.InteractiveShellApp.extensions.append("pyflyby")'
433
+ non_comment_lines = [re.sub("#.*", "", line) for line in old_config_blob.lines]
434
+ if any(line_to_add in line for line in non_comment_lines):
435
+ logger.info("[NOTHING TO DO] File %s already loads pyflyby", config_fn)
436
+ elif any("pyflyby" in line for line in non_comment_lines):
437
+ logger.info("[NOTHING TO DO] File %s already references pyflyby in some nonstandard way, assuming you configured it manually", config_fn)
438
+ else:
439
+ # Add pyflyby to config file.
440
+ lines_to_add = [line_to_add]
441
+ # Check whether we need to include the path in sys.path, and if so, add
442
+ # that to the contents. This is only needed if pyflyby is running out
443
+ # of a home directory rather than site-packages/virtualenv/etc.
444
+ # TODO: we should use tidy-imports to insert the 'import sys', if
445
+ # needed, at the top, rather than always appending it at the bottom.
446
+ import pyflyby
447
+ pyflyby_path = pyflyby.__path__[0]
448
+ if not _python_can_import_pyflyby(pyflyby_path):
449
+ path_entry = os.path.dirname(os.path.realpath(pyflyby_path))
450
+ assert _python_can_import_pyflyby(pyflyby_path, path_entry)
451
+ lines_to_add = [
452
+ "import sys",
453
+ "sys.path.append(%r)" % (path_entry,)
454
+ ] + lines_to_add
455
+ lines_to_add.insert(0, "# Pyflyby")
456
+ blob_to_add = "\n\n" + "\n".join(lines_to_add) + "\n"
457
+ new_config_blob = old_config_blob.joined.rstrip() + blob_to_add
458
+ atomic_write_file(config_fn, new_config_blob)
459
+ logger.info("[DONE] Appended to %s: %s", config_fn, line_to_add)
460
+
461
+ # Delete file installed with older approach.
462
+ startup_dir = ipython_dir / "profile_default" / "startup"
463
+ old_fn = startup_dir / "50-pyflyby.py"
464
+ if old_fn.exists:
465
+ trash_dir = old_fn.dir / ".TRASH"
466
+ trash_fn = trash_dir / old_fn.base
467
+ try:
468
+ os.mkdir(str(trash_dir))
469
+ except EnvironmentError as e:
470
+ if e.errno == errno.EEXIST:
471
+ pass
472
+ else:
473
+ raise RuntimeError("Couldn't mkdir %s: %s: %s"
474
+ % (trash_dir, type(e).__name__, e))
475
+ try:
476
+ os.rename(str(old_fn), str(trash_fn))
477
+ except EnvironmentError as e:
478
+ raise RuntimeError("Couldn't rename %s to %s: %s: %s"
479
+ % (old_fn, trash_fn, type(e).__name__, e))
480
+ logger.info("[DONE] Removed old file %s (moved to %s)", old_fn, trash_fn)
481
+
482
+
483
+ def _ipython_in_multiline(ip):
484
+ """
485
+ Return ``False`` if the user has entered only one line of input so far,
486
+ including the current line, or ``True`` if it is the second or later line.
487
+
488
+ :type ip:
489
+ ``InteractiveShell``
490
+ :rtype:
491
+ ``bool``
492
+ """
493
+ if hasattr(ip, "input_splitter"):
494
+ # IPython 0.11+. Tested with IPython 0.11, 0.12, 0.13, 1.0, 1.2, 2.0,
495
+ # 2.1, 2.2, 2.3, 2.4, 3.0, 3.1, 3.2, 4.0.
496
+ return bool(ip.input_splitter.source)
497
+ elif hasattr(ip, "buffer"):
498
+ # IPython 0.10
499
+ return bool(ip.buffer)
500
+ else:
501
+ # IPython version too old or too new?
502
+ return False
503
+
504
+
505
+ def _get_ipython_app():
506
+ """
507
+ Get an IPython application instance, if we are inside an IPython session.
508
+
509
+ If there isn't already an IPython application, raise an exception; don't
510
+ create one.
511
+
512
+ If there is a subapp, return it.
513
+
514
+ :rtype:
515
+ `BaseIPythonApplication` or an object that mimics some of its behavior
516
+ """
517
+ try:
518
+ IPython = sys.modules['IPython']
519
+ except KeyError:
520
+ # The 'IPython' module isn't already loaded, so we're not in an
521
+ # IPython session. Don't import it.
522
+ raise NoActiveIPythonAppError(
523
+ "No active IPython application (IPython not even imported yet)")
524
+ # The following has been tested on IPython 0.11, 0.12, 0.13, 1.0, 1.2,
525
+ # 2.0, 2.1, 2.2, 2.3.
526
+ try:
527
+ App = IPython.core.application.BaseIPythonApplication
528
+ except AttributeError:
529
+ pass
530
+ else:
531
+ app = App._instance
532
+ if app is not None:
533
+ if app.subapp is not None:
534
+ return app.subapp
535
+ else:
536
+ return app
537
+ # If we're inside an embedded shell, then there will be an active
538
+ # InteractiveShellEmbed but no application. In that case, create a
539
+ # fake application.
540
+ # (An alternative implementation would be to use
541
+ # IPython.core.interactiveshell.InteractiveShell._instance. However,
542
+ # that doesn't work with older versions of IPython, where the embedded
543
+ # shell is not a singleton.)
544
+ if hasattr(builtins, "get_ipython"):
545
+ shell = builtins.get_ipython()
546
+ else:
547
+ shell = None
548
+ if shell is not None:
549
+ return _DummyIPythonEmbeddedApp(shell)
550
+ # No active IPython app/shell.
551
+ raise NoActiveIPythonAppError("No active IPython application")
552
+ # The following has been tested on IPython 0.10.
553
+ raise NoActiveIPythonAppError(
554
+ "Could not figure out how to get active IPython application for IPython version %s"
555
+ % (IPython.__version__,))
556
+
557
+
558
+ def _ipython_namespaces(ip):
559
+ """
560
+ Return the (global) namespaces used for IPython.
561
+
562
+ The ordering follows IPython convention of most-local to most-global.
563
+
564
+ :type ip:
565
+ ``InteractiveShell``
566
+ :rtype:
567
+ ``list``
568
+ :return:
569
+ List of (name, namespace_dict) tuples.
570
+ """
571
+ # This list is copied from IPython 2.2's InteractiveShell._ofind().
572
+ # Earlier versions of IPython (back to 1.x) also include
573
+ # ip.alias_manager.alias_table at the end. This doesn't work in IPython
574
+ # 2.2 and isn't necessary anyway in earlier versions of IPython.
575
+ return [ ('Interactive' , ip.user_ns),
576
+ ('Interactive (global)', ip.user_global_ns),
577
+ ('Python builtin' , builtins.__dict__),
578
+ ]
579
+
580
+
581
+ # TODO class NamespaceList(tuple):
582
+
583
+
584
+ _IS_PDB_IGNORE_PKGS = frozenset([
585
+ 'IPython',
586
+ 'cmd',
587
+ 'contextlib',
588
+ 'prompt_toolkit',
589
+ 'pyflyby',
590
+ 'asyncio',
591
+ ])
592
+
593
+ _IS_PDB_IGNORE_PKGS_OTHER_THREADS = frozenset([
594
+ 'IPython',
595
+ 'cmd',
596
+ 'contextlib',
597
+ 'prompt_toolkit',
598
+ 'pyflyby',
599
+ 'threading',
600
+ ])
601
+
602
+ def _get_pdb_if_is_in_pdb():
603
+ """
604
+ Return the current Pdb instance, if we're currently called from Pdb.
605
+
606
+ :rtype:
607
+ ``pdb.Pdb`` or ``NoneType``
608
+ """
609
+ # This is kludgy. Todo: Is there a better way to do this?
610
+ pframe, pkgname = _skip_frames(sys._getframe(1), _IS_PDB_IGNORE_PKGS)
611
+ if pkgname == "threading":
612
+ # _skip_frames skipped all the way back to threading.__bootstrap.
613
+ # prompt_toolkit calls completion in a separate thread.
614
+ # Search all other threads for pdb.
615
+ # TODO: make this less kludgy.
616
+ import threading
617
+ current_tid = threading.current_thread().ident
618
+ pframes = [_skip_frames(frame, _IS_PDB_IGNORE_PKGS_OTHER_THREADS)
619
+ for tid, frame in sys._current_frames().items()
620
+ if tid != current_tid]
621
+ else:
622
+ pframes = [(pframe, pkgname)]
623
+ logger.debug("_get_pdb_if_is_in_pdb(): pframes = %r", pframes)
624
+ del pframe, pkgname
625
+ pdb_frames = [pframe for pframe,pkgname in pframes
626
+ if pkgname == "pdb"]
627
+ if not pdb_frames:
628
+ return None
629
+ # Found a pdb frame.
630
+ pdb_frame = pdb_frames[0]
631
+ import pdb
632
+
633
+ pdb_instance = pdb_frame.f_locals.get("self", None)
634
+ if (type(pdb_instance).__name__ == "Pdb" or
635
+ isinstance(pdb_instance, pdb.Pdb)):
636
+ return pdb_instance
637
+ else:
638
+ return None
639
+
640
+
641
+ def _skip_frames(frame, ignore_pkgs):
642
+ # import traceback;print("".join(traceback.format_stack(frame)))
643
+ while True:
644
+ if frame is None:
645
+ return None, None
646
+ modname = frame.f_globals.get("__name__", None) or ""
647
+ pkgname = modname.split(".",1)[0]
648
+ # logger.debug("_skip_frames: frame: %r %r", frame, modname)
649
+ if pkgname in ignore_pkgs:
650
+ frame = frame.f_back
651
+ continue
652
+ break
653
+ # logger.debug("_skip_frames: => %r %r", frame, pkgname)
654
+ return frame, pkgname
655
+
656
+
657
+ def get_global_namespaces(ip):
658
+ """
659
+ Get the global interactive namespaces.
660
+
661
+ :type ip:
662
+ ``InteractiveShell``
663
+ :param ip:
664
+ IPython shell or ``None`` to assume not in IPython.
665
+ :rtype:
666
+ ``list`` of ``dict``
667
+ """
668
+ # logger.debug("get_global_namespaces()")
669
+ pdb_instance = _get_pdb_if_is_in_pdb()
670
+ # logger.debug("get_global_namespaces(): pdb_instance=%r", pdb_instance)
671
+ if pdb_instance:
672
+ frame = pdb_instance.curframe
673
+ return [frame.f_globals, pdb_instance.curframe_locals]
674
+ elif ip:
675
+ return [ns for nsname, ns in _ipython_namespaces(ip)][::-1]
676
+ else:
677
+ import __main__
678
+ return [builtins.__dict__, __main__.__dict__]
679
+
680
+
681
+ class NamespaceWithPotentialImports(dict):
682
+ def __init__(self, values, ip):
683
+ dict.__init__(values)
684
+ self._ip = ip
685
+
686
+ @property
687
+ def _potential_imports_list(self):
688
+ """Collect symbols that could be imported into the namespace.
689
+
690
+ This needs to be executed each time because the context can change,
691
+ e.g. when in pdb the frames and their namespaces will change."""
692
+
693
+ db = None
694
+ db = ImportDB.interpret_arg(db, target_filename=".")
695
+ known = db.known_imports
696
+ # Check global names, including global-level known modules and
697
+ # importable modules.
698
+ results = set()
699
+ namespaces = ScopeStack(get_global_namespaces(self._ip))
700
+ for ns in namespaces:
701
+ for name in ns:
702
+ if '.' not in name:
703
+ results.add(name)
704
+ results.update(known.member_names.get("", []))
705
+ results.update([str(m) for m in ModuleHandle.list()])
706
+ assert all('.' not in r for r in results)
707
+ return sorted([r for r in results])
708
+
709
+ def keys(self):
710
+ return list(self) + self._potential_imports_list
711
+
712
+
713
+ def _auto_import_hook(name: str):
714
+ logger.debug("_auto_import_hook(%r)", name)
715
+ ip = _get_ipython_app().shell
716
+ try:
717
+ namespaces = ScopeStack(get_global_namespaces(ip))
718
+ db = ImportDB.interpret_arg(None, target_filename='.')
719
+ did_auto_import = auto_import_symbol(name, namespaces, db)
720
+ except Exception as e:
721
+ logger.debug("_auto_import_hook preparation error: %r", e)
722
+ raise e
723
+ if not did_auto_import:
724
+ raise ImportError(f"{name} not auto-imported")
725
+ try:
726
+ # relies on `auto_import_symbol` auto-importing into [-1] namespace
727
+ return namespaces[-1][name]
728
+ except Exception as e:
729
+ logger.debug("_auto_import_hook internal error: %r", e)
730
+ raise e
731
+
732
+
733
+ def _auto_import_in_pdb_frame(pdb_instance, arg):
734
+ frame = pdb_instance.curframe
735
+ namespaces = [frame.f_globals, pdb_instance.curframe_locals]
736
+ filename = frame.f_code.co_filename
737
+ if not filename or filename.startswith("<"):
738
+ filename = "."
739
+ db = ImportDB.get_default(filename)
740
+ auto_import(arg, namespaces=namespaces, db=db)
741
+
742
+
743
+ def _enable_pdb_hooks(pdb_instance):
744
+ # Enable hooks in pdb.Pdb.
745
+ # Should be called after pdb.Pdb.__init__().
746
+ logger.debug("_enable_pdb_hooks(%r)", pdb_instance)
747
+ # Patch Pdb._getval() to use auto_eval.
748
+ # This supports 'ipdb> p foo'.
749
+ @advise(pdb_instance._getval)
750
+ def _getval_with_autoimport(arg):
751
+ logger.debug("Pdb._getval(%r)", arg)
752
+ _auto_import_in_pdb_frame(pdb_instance, arg)
753
+ return __original__(arg)
754
+ # Patch Pdb.default() to use auto_import.
755
+ # This supports 'ipdb> foo()'.
756
+ @advise(pdb_instance.default)
757
+ def default_with_autoimport(arg):
758
+ logger.debug("Pdb.default(%r)", arg)
759
+ if arg.startswith("!"):
760
+ arg = arg[1:]
761
+ _auto_import_in_pdb_frame(pdb_instance, arg)
762
+ return __original__(arg)
763
+
764
+
765
+ def _enable_terminal_pdb_hooks(pdb_instance, auto_importer=None):
766
+ # Should be called after TerminalPdb.__init__().
767
+ # Tested with IPython 5.8 with prompt_toolkit.
768
+ logger.debug("_enable_terminal_pdb_hooks(%r)", pdb_instance)
769
+ ptcomp = getattr(pdb_instance, "_ptcomp", None)
770
+ completer = getattr(ptcomp, "ipy_completer", None)
771
+ logger.debug("_enable_terminal_pdb_hooks(): completer=%r", completer)
772
+ if completer is not None and auto_importer is not None:
773
+ auto_importer._enable_completer_hooks(completer)
774
+
775
+
776
+ def _get_IPdb_class():
777
+ """
778
+ Get the IPython (core) Pdb class.
779
+ """
780
+ try:
781
+ import IPython
782
+ except ImportError:
783
+ raise NoIPythonPackageError()
784
+ try:
785
+ # IPython 0.11+. Tested with IPython 0.11, 0.12, 0.13, 1.0, 1.1, 1.2,
786
+ # 2.0, 2.1, 2.2, 2.3, 2.4, 3.0, 3.1, 3.2, 4.0
787
+ from IPython.core import debugger
788
+ return debugger.Pdb
789
+ except ImportError:
790
+ pass
791
+ try:
792
+ # IPython 0.10
793
+ from IPython import Debugger
794
+ return Debugger.Pdb
795
+ except ImportError:
796
+ pass
797
+ # IPython exists but couldn't figure out how to get Pdb.
798
+ raise RuntimeError(
799
+ "Couldn't get IPython Pdb. "
800
+ "Is your IPython version too old (or too new)? "
801
+ "IPython.__version__=%r" % (IPython.__version__))
802
+
803
+
804
+ def _get_TerminalPdb_class():
805
+ """
806
+ Get the IPython TerminalPdb class.
807
+ """
808
+ # The TerminalPdb subclasses the (core) Pdb class. If the TerminalPdb
809
+ # class is being used, then in that case we only need to advise
810
+ # TerminalPdb stuff, not (core) Pdb stuff. However, in some cases the
811
+ # TerminalPdb class is not used even if it exists, so we advise the (core)
812
+ # Pdb class separately.
813
+ try:
814
+ import IPython
815
+ del IPython
816
+ except ImportError:
817
+ raise NoIPythonPackageError()
818
+ try:
819
+ from IPython.terminal.debugger import TerminalPdb
820
+ return TerminalPdb
821
+ except ImportError:
822
+ pass
823
+ raise RuntimeError("Couldn't get TerminalPdb")
824
+
825
+
826
+ def new_IPdb_instance():
827
+ """
828
+ Create a new Pdb instance.
829
+
830
+ If IPython is available, then use IPython's Pdb. Initialize a new IPython
831
+ terminal application if necessary.
832
+
833
+ If the IPython package is not installed in the system, then use regular Pdb.
834
+
835
+ Enable the auto importer.
836
+
837
+ :rtype:
838
+ `Pdb`
839
+ """
840
+ logger.debug("new_IPdb_instance()")
841
+ try:
842
+ app = get_ipython_terminal_app_with_autoimporter()
843
+ except Exception as e:
844
+ if isinstance(e, NoIPythonPackageError) or e.__class__.__name__ == "MultipleInstanceError":
845
+ logger.debug("%s: %s", type(e).__name__, e)
846
+ from pdb import Pdb
847
+ pdb_instance = Pdb()
848
+ _enable_pdb_hooks(pdb_instance)
849
+ _enable_terminal_pdb_hooks(pdb_instance)
850
+ return pdb_instance
851
+ else:
852
+ raise
853
+ pdb_class = _get_IPdb_class()
854
+ logger.debug("new_IPdb_instance(): pdb_class=%s", pdb_class)
855
+ color_scheme = _get_ipython_color_scheme(app)
856
+ try:
857
+ pdb_instance = pdb_class(completekey='tab', color_scheme=color_scheme)
858
+ except TypeError:
859
+ pdb_instance = pdb_class(completekey='tab')
860
+ _enable_pdb_hooks(pdb_instance)
861
+ _enable_terminal_pdb_hooks(pdb_instance)
862
+ return pdb_instance
863
+
864
+
865
+ def _get_ipython_color_scheme(app):
866
+ """
867
+ Get the configured IPython color scheme.
868
+
869
+ :type app:
870
+ `TerminalIPythonApp`
871
+ :param app:
872
+ An initialized IPython terminal application.
873
+ :rtype:
874
+ ``str``
875
+ """
876
+ try:
877
+ # Tested with IPython 0.11, 0.12, 0.13, 1.0, 1.1, 1.2, 2.0, 2.1, 2.2,
878
+ # 2.3, 2.4, 3.0, 3.1, 3.2, 4.0.
879
+ return app.shell.colors
880
+ except AttributeError:
881
+ pass
882
+ try:
883
+ # Tested with IPython 0.10.
884
+ import IPython
885
+ ipapi = IPython.ipapi.get()
886
+ return ipapi.options.colors
887
+ except AttributeError:
888
+ pass
889
+ import IPython
890
+ raise RuntimeError(
891
+ "Couldn't get IPython colors. "
892
+ "Is your IPython version too old (or too new)? "
893
+ "IPython.__version__=%r" % (IPython.__version__))
894
+
895
+
896
+ def print_verbose_tb(*exc_info):
897
+ """
898
+ Print a traceback, using IPython's ultraTB if possible.
899
+
900
+ :param exc_info:
901
+ 3 arguments as returned by sys.exc_info().
902
+ """
903
+ if not exc_info:
904
+ exc_info = sys.exc_info()
905
+ elif len(exc_info) == 1 and isinstance(exc_info[0], tuple):
906
+ exc_info, = exc_info
907
+ if len(exc_info) != 3:
908
+ raise TypeError(
909
+ "Expected 3 items for exc_info; got %d" % len(exc_info))
910
+ try:
911
+ # Tested with IPython 0.11, 0.12, 0.13, 1.0, 1.1, 1.2, 2.0, 2.1, 2.2,
912
+ # 2.3, 2.4, 3.0, 3.1, 3.2, 4.0.
913
+ from IPython.core.ultratb import VerboseTB
914
+ except ImportError:
915
+ try:
916
+ # Tested with IPython 0.10.
917
+ from IPython.ultraTB import VerboseTB
918
+ except ImportError:
919
+ VerboseTB = None
920
+ exc_type, exc_value, exc_tb = exc_info
921
+ # TODO: maybe use ip.showtraceback() instead?
922
+ if VerboseTB is not None:
923
+ VerboseTB(include_vars=False)(exc_type, exc_value, exc_tb)
924
+ else:
925
+ import traceback
926
+ def red(x):
927
+ return "\033[0m\033[31;1m%s\033[0m" % (x,)
928
+ exc_name = exc_type
929
+ try:
930
+ exc_name = exc_name.__name__
931
+ except AttributeError:
932
+ pass
933
+ exc_name = str(exc_name)
934
+ print(red("---------------------------------------------------------------------------"))
935
+ print(red(exc_name.ljust(42)) + "Traceback (most recent call last)")
936
+ traceback.print_tb(exc_tb)
937
+ print()
938
+ print("%s: %s" % (red(exc_name), exc_value),
939
+ file=sys.stderr)
940
+ print()
941
+
942
+
943
+ @contextmanager
944
+ def UpdateIPythonStdioCtx():
945
+ """
946
+ Context manager that updates IPython's cached stdin/stdout/stderr handles
947
+ to match the current values of sys.stdin/sys.stdout/sys.stderr.
948
+ """
949
+ if "IPython" not in sys.modules:
950
+ yield
951
+ return
952
+
953
+ import IPython
954
+
955
+ if IPython.version_info[:1] >= (8,):
956
+ yield
957
+ return
958
+
959
+ if "IPython.utils.io" in sys.modules:
960
+ # Tested with IPython 0.11, 0.12, 0.13, 1.0, 1.1, 1.2, 2.0, 2.1, 2.2,
961
+ # 2.3, 2.4, 3.0, 3.1, 3.2, 4.0.
962
+ module = sys.modules["IPython.utils.io"]
963
+ container = module
964
+ IOStream = module.IOStream
965
+ else:
966
+ # IPython version too old or too new?
967
+ # For now just silently do nothing.
968
+ yield
969
+ return
970
+ old_stdin = container.stdin
971
+ old_stdout = container.stdout
972
+ old_stderr = container.stderr
973
+ try:
974
+ container.stdin = IOStream(sys.stdin)
975
+ container.stdout = IOStream(sys.stdout)
976
+ container.stderr = IOStream(sys.stderr)
977
+ yield
978
+ finally:
979
+ container.stdin = old_stdin
980
+ container.stdout = old_stdout
981
+ container.stderr = old_stderr
982
+
983
+
984
+
985
+ class _EnableState:
986
+ DISABLING = "DISABLING"
987
+ DISABLED = "DISABLED"
988
+ ENABLING = "ENABLING"
989
+ ENABLED = "ENABLED"
990
+
991
+
992
+ class AutoImporter:
993
+ """
994
+ Auto importer enable state.
995
+
996
+ The state is attached to an IPython "application".
997
+ """
998
+
999
+ db: ImportDB
1000
+ app: Any
1001
+ _state: _EnableState
1002
+ _disablers: List[Any]
1003
+
1004
+ _errored: bool
1005
+ _ip: Any
1006
+ _ast_transformer: Any
1007
+ _autoimported_this_cell: Dict[Any, Any]
1008
+
1009
+ def __new__(cls, arg=Ellipsis):
1010
+ """
1011
+ Get the AutoImporter for the given app, or create and assign one.
1012
+
1013
+ :type arg:
1014
+ `AutoImporter`, `BaseIPythonApplication`, `InteractiveShell`
1015
+ """
1016
+ if isinstance(arg, AutoImporter):
1017
+ return arg
1018
+ # Check the type of the arg. Avoid isinstance because it's so hard
1019
+ # to know where to import something from.
1020
+ # Todo: make this more robust.
1021
+ if arg is Ellipsis:
1022
+ app = _get_ipython_app()
1023
+ return cls._from_app(app)
1024
+ clsname = type(arg).__name__
1025
+ if "App" in clsname:
1026
+ return cls._from_app(arg)
1027
+ elif "Shell" in clsname:
1028
+ # If given an ``InteractiveShell`` argument, then get its parent app.
1029
+ # Tested with IPython 1.0, 1.2, 2.0, 2.1, 2.2, 2.3, 2.4, 3.0, 3.1,
1030
+ # 3.2, 4.0.
1031
+ if hasattr(arg, 'parent') and getattr(arg.parent, 'shell', None) is arg:
1032
+ app = arg.parent
1033
+ return cls._from_app(app)
1034
+ # Tested with IPython 0.10, 0.11, 0.12, 0.13.
1035
+ app = _get_ipython_app()
1036
+ if app.shell is arg:
1037
+ return cls._from_app(app)
1038
+ raise ValueError(
1039
+ "Got a shell instance %r but couldn't match it to an app"
1040
+ % (arg,))
1041
+ else:
1042
+ raise TypeError("AutoImporter(): unexpected %s" % (clsname,))
1043
+
1044
+ @classmethod
1045
+ def _from_app(cls, app) -> 'AutoImporter':
1046
+ subapp = getattr(app, "subapp", None)
1047
+ if subapp is not None:
1048
+ app = subapp
1049
+ try:
1050
+ self = app.auto_importer
1051
+ except AttributeError:
1052
+ pass
1053
+ else:
1054
+ assert isinstance(self, cls)
1055
+ return self
1056
+ # Create a new instance and assign to the app.
1057
+ self = cls._construct(app)
1058
+ app.auto_importer = self
1059
+ self.db = ImportDB("")
1060
+ return self
1061
+
1062
+ @classmethod
1063
+ def _construct(cls, app):
1064
+ """
1065
+ Create a new AutoImporter for ``app``.
1066
+
1067
+ :type app:
1068
+ `IPython.core.application.BaseIPythonApplication`
1069
+ """
1070
+ self = object.__new__(cls)
1071
+ self.app = app
1072
+ logger.debug("Constructing %r for app=%r, subapp=%r", self, app,
1073
+ getattr(app, "subapp", None))
1074
+ # Functions to call to disable the auto importer.
1075
+ self._disablers = []
1076
+ # Current enabling state.
1077
+ self._state = _EnableState.DISABLED
1078
+ # Whether there has been an error implying a bug in pyflyby code or a
1079
+ # problem with the import database.
1080
+ self._errored = False
1081
+ # A reference to the IPython shell object.
1082
+ self._ip = None
1083
+ # The AST transformer, if any (IPython 1.0+).
1084
+ self._ast_transformer = None
1085
+ # Dictionary of things we've attempted to autoimport for this cell.
1086
+ self._autoimported_this_cell = {}
1087
+ return self
1088
+
1089
+ def enable(self, even_if_previously_errored=False):
1090
+ """
1091
+ Turn on the auto-importer in the current IPython session.
1092
+ """
1093
+ # Check that we are not enabled/enabling yet.
1094
+ if self._state is _EnableState.DISABLED:
1095
+ pass
1096
+ elif self._state is _EnableState.ENABLED:
1097
+ logger.debug("Already enabled")
1098
+ return
1099
+ elif self._state is _EnableState.ENABLING:
1100
+ logger.debug("Already enabling")
1101
+ return
1102
+ elif self._state is _EnableState.DISABLING:
1103
+ logger.debug("Still disabling (run disable() to completion first)")
1104
+ return
1105
+ else:
1106
+ raise AssertionError
1107
+ self.reset_state_new_cell()
1108
+ # Check if previously errored.
1109
+ if self._errored:
1110
+ if even_if_previously_errored:
1111
+ self._errored = False
1112
+ else:
1113
+ # Be conservative: Once we've had problems, don't try again
1114
+ # this session. Exceptions in the interactive loop can be
1115
+ # annoying to deal with.
1116
+ logger.warning(
1117
+ "Not reattempting to enable auto importer after earlier "
1118
+ "error")
1119
+ return
1120
+ import IPython
1121
+ logger.debug("Enabling auto importer for IPython version %s, pid=%r",
1122
+ IPython.__version__, os.getpid())
1123
+ logger.debug("enable(): state %s=>ENABLING", self._state)
1124
+ self._errored = False
1125
+ self._state = _EnableState.ENABLING
1126
+ self._safe_call(self._enable_internal)
1127
+
1128
+ def _continue_enable(self):
1129
+ if self._state != _EnableState.ENABLING:
1130
+ logger.debug("_enable_internal(): state = %s", self._state)
1131
+ return
1132
+ logger.debug("Continuing enabling auto importer")
1133
+ self._safe_call(self._enable_internal)
1134
+
1135
+ def _enable_internal(self):
1136
+ # Main enabling entry point. This function can get called multiple
1137
+ # times, depending on what's been initialized so far.
1138
+ app = self.app
1139
+ assert app is not None
1140
+ if getattr(app, "subapp", None) is not None:
1141
+ app = app.subapp
1142
+ self.app = app
1143
+ logger.debug("app = %r", app)
1144
+ ok = True
1145
+ ok &= self._enable_initializer_hooks(app)
1146
+ ok &= self._enable_kernel_manager_hook(app)
1147
+ ok &= self._enable_shell_hooks(app)
1148
+ if ok:
1149
+ logger.debug("_enable_internal(): success! state: %s=>ENABLED",
1150
+ self._state)
1151
+ self._state = _EnableState.ENABLED
1152
+ elif self._pending_initializers:
1153
+ logger.debug("_enable_internal(): did what we can for now; "
1154
+ "will enable more after further IPython initialization. "
1155
+ "state=%s", self._state)
1156
+ else:
1157
+ logger.debug("_enable_internal(): did what we can, but not "
1158
+ "fully successful. state: %s=>ENABLED",
1159
+ self._state)
1160
+ self._state = _EnableState.ENABLED
1161
+
1162
+ def _enable_initializer_hooks(self, app):
1163
+ # Hook initializers. There are various things we want to hook, and
1164
+ # the hooking needs to be done at different times, depending on the
1165
+ # IPython version and the "app". For example, for most versions of
1166
+ # IPython, terminal app, many things need to be done after
1167
+ # initialize()/init_shell(); on the other hand, in some cases
1168
+ # (e.g. IPython console), we need to do stuff *inside* the
1169
+ # initialization function.
1170
+ # Thus, we take a brute force approach: add hooks to a bunch of
1171
+ # places, if they seem to not have run yet, and each time add any
1172
+ # hooks that are ready to be added.
1173
+ ok = True
1174
+ pending = False
1175
+ ip = getattr(app, "shell", None)
1176
+ if ip is None:
1177
+ if hasattr(app, "init_shell"):
1178
+ @self._advise(app.init_shell)
1179
+ def init_shell_enable_auto_importer():
1180
+ __original__()
1181
+ logger.debug("init_shell() completed")
1182
+ ip = app.shell
1183
+ if ip is None:
1184
+ logger.debug("Aborting enabling AutoImporter: "
1185
+ "even after init_shell(), "
1186
+ "still no shell in app=%r", app)
1187
+ return
1188
+ self._continue_enable()
1189
+ elif not hasattr(app, "shell") and hasattr(app, "kernel_manager"):
1190
+ logger.debug("No shell applicable; ok because using kernel manager")
1191
+ pass
1192
+ else:
1193
+ logger.debug("App shell missing and no init_shell() to advise")
1194
+ ok = False
1195
+ if hasattr(app, "initialize_subcommand"):
1196
+ # Hook the subapp, if any. This requires some cleverness:
1197
+ # 'ipython console' requires us to do some stuff *before*
1198
+ # initialize() is called on the new app, while 'ipython
1199
+ # notebook' requires us to do stuff *after* initialize() is
1200
+ # called.
1201
+ @self._advise(app.initialize_subcommand)
1202
+ def init_subcmd_enable_auto_importer(*args, **kwargs):
1203
+ logger.debug("initialize_subcommand()")
1204
+ from IPython.core.application import Application
1205
+ @advise((Application, "instance"))
1206
+ def app_instance_enable_auto_importer(cls, *args, **kwargs):
1207
+ logger.debug("%s.instance()", cls.__name__)
1208
+ app = __original__(cls, *args, **kwargs)
1209
+ if app != self.app:
1210
+ self.app = app
1211
+ self._continue_enable()
1212
+ return app
1213
+ try:
1214
+ __original__(*args, **kwargs)
1215
+ finally:
1216
+ app_instance_enable_auto_importer.unadvise()
1217
+ self._continue_enable()
1218
+ pending = True
1219
+ if (hasattr(ip, "post_config_initialization") and
1220
+ not hasattr(ip, "rl_next_input")):
1221
+ # IPython 0.10 might not be ready to hook yet because we're called
1222
+ # from the config phase, and certain stuff (like Completer) is set
1223
+ # up in post-config. Re-run after post_config_initialization.
1224
+ # Kludge: post_config_initialization() sets ip.rl_next_input=None,
1225
+ # so detect whether it's been run by checking for that attribute.
1226
+ @self._advise(ip.post_config_initialization)
1227
+ def post_config_enable_auto_importer():
1228
+ __original__()
1229
+ logger.debug("post_config_initialization() completed")
1230
+ if not hasattr(ip, "rl_next_input"):
1231
+ # Post-config initialization failed?
1232
+ return
1233
+ self._continue_enable()
1234
+ pending = True
1235
+ self._pending_initializers = pending
1236
+ return ok
1237
+
1238
+ def _enable_kernel_manager_hook(self, app):
1239
+ # For IPython notebook, by the time we get here, there's generally a
1240
+ # kernel_manager already assigned, but kernel_manager.start_kernel()
1241
+ # hasn't been called yet. Hook app.kernel_manager.start_kernel().
1242
+ kernel_manager = getattr(app, "kernel_manager", None)
1243
+ ok = True
1244
+ if kernel_manager is not None:
1245
+ ok &= self._enable_start_kernel_hook(kernel_manager)
1246
+ # For IPython console, a single function constructs the kernel_manager
1247
+ # and then immediately calls kernel_manager.start_kernel(). The
1248
+ # easiest way to intercept start_kernel() is by installing a hook
1249
+ # after the kernel_manager is constructed.
1250
+ if getattr(app, "kernel_manager_class", None) is not None:
1251
+ @self._advise((app, "kernel_manager_class"))
1252
+ def kernel_manager_class_with_autoimport(*args, **kwargs):
1253
+ logger.debug("kernel_manager_class_with_autoimport()")
1254
+ kernel_manager = __original__(*args, **kwargs)
1255
+ self._enable_start_kernel_hook(kernel_manager)
1256
+ return kernel_manager
1257
+ # It's OK if no kernel_manager nor kernel_manager_class; this is the
1258
+ # typical case, when using regular IPython terminal console (not
1259
+ # IPython notebook/console).
1260
+ return True
1261
+
1262
+ def _enable_start_kernel_hook(self, kernel_manager):
1263
+ # Various IPython versions have different 'main' commands called from
1264
+ # here, e.g.
1265
+ # IPython 2: IPython.kernel.zmq.kernelapp.main
1266
+ # IPython 3: IPython.kernel.__main__
1267
+ # IPython 4: ipykernel.__main__
1268
+ # These essentially all do 'kernelapp.launch_new_instance()' (imported
1269
+ # from different places). We hook the guts of that to enable the
1270
+ # autoimporter.
1271
+ new_cmd = [
1272
+ '-c',
1273
+ 'from pyflyby._interactive import start_ipython_kernel_with_autoimporter; '
1274
+ 'start_ipython_kernel_with_autoimporter()'
1275
+ ]
1276
+ try:
1277
+ # Tested with Jupyter/IPython 4.0
1278
+ from jupyter_client.manager import KernelManager as JupyterKernelManager
1279
+ except ImportError:
1280
+ pass
1281
+ else:
1282
+ @self._advise(kernel_manager.start_kernel)
1283
+ def start_kernel_with_autoimport_jupyter(*args, **kwargs):
1284
+ logger.debug("start_kernel()")
1285
+ # Advise format_kernel_cmd(), which is the function that
1286
+ # computes the command line for a subprocess to run a new
1287
+ # kernel. Note that we advise the method on the class, rather
1288
+ # than this instance of kernel_manager, because start_kernel()
1289
+ # actually creates a *new* KernelInstance for this.
1290
+ @advise(JupyterKernelManager.format_kernel_cmd)
1291
+ def format_kernel_cmd_with_autoimport(*args, **kwargs):
1292
+ result = __original__(*args, **kwargs)
1293
+ logger.debug("intercepting format_kernel_cmd(): orig = %r", result)
1294
+ if (len(result) >= 3 and
1295
+ result[1] == '-m' and
1296
+ result[2] in ['ipykernel', 'ipykernel_launcher']):
1297
+ result[1:3] = new_cmd
1298
+ logger.debug("intercepting format_kernel_cmd(): new = %r", result)
1299
+ return result
1300
+ else:
1301
+ logger.debug("intercepting format_kernel_cmd(): unexpected output; not modifying it")
1302
+ return result
1303
+ try:
1304
+ return __original__(*args, **kwargs)
1305
+ finally:
1306
+ format_kernel_cmd_with_autoimport.unadvise()
1307
+ return True
1308
+ logger.debug("Couldn't enable start_kernel hook")
1309
+ return False
1310
+
1311
+ def _enable_shell_hooks(self, app):
1312
+ """
1313
+ Enable hooks to run auto_import before code execution.
1314
+ """
1315
+ # Check again in case this was registered delayed
1316
+ if self._state != _EnableState.ENABLING:
1317
+ return False
1318
+ try:
1319
+ ip = app.shell
1320
+ except AttributeError:
1321
+ logger.debug("_enable_shell_hooks(): no shell at all")
1322
+ return True
1323
+ if ip is None:
1324
+ logger.debug("_enable_shell_hooks(): no shell yet")
1325
+ return False
1326
+ logger.debug("Enabling IPython shell hooks, shell=%r", ip)
1327
+ self._ip = ip
1328
+ # Notes on why we hook what we hook:
1329
+ #
1330
+ # There are many different places within IPython we can consider
1331
+ # hooking/advising, depending on the version:
1332
+ # * ip.input_transformer_manager.logical_line_transforms
1333
+ # * ip.compile.ast_parse (IPython 0.12+)
1334
+ # * ip.run_ast_nodes (IPython 0.11+)
1335
+ # * ip.runsource (IPython 0.10)
1336
+ # * ip.prefilter_manager.checks
1337
+ # * ip.prefilter_manager.handlers["auto"]
1338
+ # * ip.ast_transformers
1339
+ # * ip.hooks['pre_run_code_hook']
1340
+ # * ip._ofind
1341
+ #
1342
+ # We choose to hook in two places: (1) _ofind and (2)
1343
+ # ast_transformers. The motivation follows. We want to handle
1344
+ # auto-imports for all of these input cases:
1345
+ # (1) "foo.bar"
1346
+ # (2) "arbitrarily_complicated_stuff((lambda: foo.bar)())"
1347
+ # (3) "foo.bar?", "foo.bar??" (pinfo/pinfo2)
1348
+ # (4) "foo.bar 1, 2" => "foo.bar(1, 2)" (autocall)
1349
+ #
1350
+ # Case 1 is the easiest and can be handled by nearly any method. Case
1351
+ # 2 must be done either as a prefilter or as an AST transformer.
1352
+ # Cases 3 and 4 must be done either as an input line transformer or by
1353
+ # monkey-patching _ofind, because by the time the
1354
+ # prefilter/ast_transformer is called, it's too late.
1355
+ #
1356
+ # To handle case 2, we use an AST transformer (for IPython > 1.0), or
1357
+ # monkey-patch one of the compilation steps (ip.compile for IPython
1358
+ # 0.10 and ip.run_ast_nodes for IPython 0.11-0.13).
1359
+ # prefilter_manager.checks() is the "supported" way to add a
1360
+ # pre-execution hook, but it only works for single lines, not for
1361
+ # multi-line cells. (There is no explanation in the IPython source
1362
+ # for why prefilter hooks are seemingly intentionally skipped for
1363
+ # multi-line cells).
1364
+ #
1365
+ # To handle cases 3/4 (pinfo/autocall), we choose to advise _ofind.
1366
+ # This is a private function that is called by both pinfo and autocall
1367
+ # code paths. (Alternatively, we could have added something to the
1368
+ # logical_line_transforms. The downside of that is that we would need
1369
+ # to re-implement all the parsing perfectly matching IPython.
1370
+ # Although monkey-patching is in general bad, it seems the lesser of
1371
+ # the two evils in this case.)
1372
+ #
1373
+ # Since we have two invocations of auto_import(), case 1 is
1374
+ # handled twice. That's fine, because it runs quickly.
1375
+ ok = True
1376
+ ok &= self._enable_reset_hook(ip)
1377
+ ok &= self._enable_ofind_hook(ip)
1378
+ ok &= self._enable_ast_hook(ip)
1379
+ ok &= self._enable_time_hook(ip)
1380
+ ok &= self._enable_timeit_hook(ip)
1381
+ ok &= self._enable_prun_hook(ip)
1382
+ ok &= self._enable_completion_hook(ip)
1383
+ ok &= self._enable_run_hook(ip)
1384
+ ok &= self._enable_debugger_hook(ip)
1385
+ ok &= self._enable_ipython_shell_bugfixes(ip)
1386
+ return ok
1387
+
1388
+ def _enable_reset_hook(self, ip):
1389
+ # Register a hook that resets autoimporter state per input cell.
1390
+ # The only per-input-cell state we currently have is the recording of
1391
+ # which autoimports we've attempted but failed. We keep track of this
1392
+ # to avoid multiple error messages for a single import, in case of
1393
+ # overlapping hooks.
1394
+ # Note: Some of the below approaches (both registering an
1395
+ # input_transformer_manager hook or advising reset()) cause the reset
1396
+ # function to get called twice per cell. This seems like an
1397
+ # unintentional repeated call in IPython itself. This is harmless for
1398
+ # us, since doing an extra reset shouldn't hurt.
1399
+ if hasattr(ip, "input_transformers_post"):
1400
+ # In IPython 7.0+, the input transformer API changed.
1401
+ def reset_auto_importer_state(line):
1402
+ # There is a bug in IPython that causes the transformer to be
1403
+ # called multiple times
1404
+ # (https://github.com/ipython/ipython/issues/11714). Until it
1405
+ # is fixed, workaround it by skipping one of the calls.
1406
+ stack = inspect.stack()
1407
+ if any([
1408
+ stack[3].function == 'run_cell_async',
1409
+ # These are the other places it is called.
1410
+ # stack[3].function == 'should_run_async',
1411
+ # stack[1].function == 'check_complete'
1412
+ ]):
1413
+ return line
1414
+ logger.debug("reset_auto_importer_state(%r)", line)
1415
+ self.reset_state_new_cell()
1416
+ return line
1417
+ # on IPython 7.17 (July 2020) or above, the check_complete
1418
+ # path of the code will not call transformer that have this magic attribute
1419
+ # when trying to check whether the code is complete.
1420
+ reset_auto_importer_state.has_side_effect = True
1421
+ ip.input_transformers_cleanup.append(reset_auto_importer_state)
1422
+ return True
1423
+ elif hasattr(ip, "input_transformer_manager"):
1424
+ # Tested with IPython 1.0, 1.2, 2.0, 2.1, 2.2, 2.3, 2.4, 3.0, 3.1,
1425
+ # 3.2, 4.0.
1426
+ class ResetAutoImporterState(object):
1427
+ def push(self_, line):
1428
+ return line
1429
+ def reset(self_):
1430
+ logger.debug("ResetAutoImporterState.reset()")
1431
+ self.reset_state_new_cell()
1432
+ t = ResetAutoImporterState()
1433
+ transforms = ip.input_transformer_manager.python_line_transforms
1434
+ transforms.append(t)
1435
+ def unregister_input_transformer():
1436
+ try:
1437
+ transforms.remove(t)
1438
+ except ValueError:
1439
+ logger.info(
1440
+ "Couldn't remove python_line_transformer hook")
1441
+ self._disablers.append(unregister_input_transformer)
1442
+ return True
1443
+ elif hasattr(ip, "input_splitter"):
1444
+ # Tested with IPython 0.13. Also works with later versions, but
1445
+ # for those versions, we can use a real hook instead of advising.
1446
+ @self._advise(ip.input_splitter.reset)
1447
+ def reset_input_splitter_and_autoimporter_state():
1448
+ logger.debug("reset_input_splitter_and_autoimporter_state()")
1449
+ self.reset_state_new_cell()
1450
+ return __original__()
1451
+ return True
1452
+ else:
1453
+ logger.debug("Couldn't enable reset hook")
1454
+ return False
1455
+
1456
+ def _enable_ofind_hook(self, ip):
1457
+ """
1458
+ Enable a hook of _ofind(), which is used for pinfo, autocall, etc.
1459
+ """
1460
+ # Advise _ofind.
1461
+ if hasattr(ip, "_ofind"):
1462
+ # Tested with IPython 0.10, 0.11, 0.12, 0.13, 1.0, 1.2, 2.0, 2.3,
1463
+ # 2.4, 3.0, 3.1, 3.2, 4.0.
1464
+ @self._advise(ip._ofind)
1465
+ def ofind_with_autoimport(oname, namespaces=None):
1466
+ logger.debug("_ofind(oname=%r, namespaces=%r)", oname, namespaces)
1467
+ is_multiline = False
1468
+ if hasattr(ip, "buffer"):
1469
+ # In IPython 0.10, _ofind() gets called for each line of a
1470
+ # multiline input. Skip them.
1471
+ is_multiline = len(ip.buffer) > 0
1472
+ if namespaces is None:
1473
+ namespaces = _ipython_namespaces(ip)
1474
+ is_network_request = False
1475
+ frame = inspect.currentframe()
1476
+ # jupyter_lab_completer seem to send inspect request when
1477
+ # cycling through completions which trigger import.
1478
+ # We cannot differentiate those from actual inspect when
1479
+ # clicking on an object.
1480
+ # So for now when we see the inspect request comes from
1481
+ # ipykernel, we just don't autoimport
1482
+ while frame is not None:
1483
+ if "ipykernel/ipkernel.py" in inspect.getframeinfo(frame).filename:
1484
+ is_network_request = True
1485
+ break
1486
+ frame = frame.f_back
1487
+ if (
1488
+ not is_multiline
1489
+ and is_identifier(oname, dotted=True)
1490
+ and not is_network_request
1491
+ ):
1492
+ self.auto_import(
1493
+ str(oname), [ns for nsname, ns in namespaces][::-1]
1494
+ )
1495
+ result = __original__(oname, namespaces=namespaces)
1496
+ return result
1497
+ return True
1498
+ else:
1499
+ logger.debug("Couldn't enable ofind hook")
1500
+ return False
1501
+
1502
+ def _enable_ast_hook(self, ip):
1503
+ """
1504
+ Enable a hook somewhere in the source => parsed AST => compiled code
1505
+ pipeline.
1506
+ """
1507
+ # Register an AST transformer.
1508
+ if hasattr(ip, 'ast_transformers'):
1509
+ logger.debug("Registering an ast_transformer")
1510
+ # First choice: register a formal ast_transformer.
1511
+ # Tested with IPython 1.0, 1.2, 2.0, 2.3, 2.4, 3.0, 3.1, 3.2, 4.0.
1512
+ class _AutoImporter_ast_transformer(object):
1513
+ """
1514
+ A NodeVisitor-like wrapper around ``auto_import_for_ast`` for
1515
+ the API that IPython 1.x's ``ast_transformers`` needs.
1516
+ """
1517
+ def visit(self_, node):
1518
+ # We don't actually transform the node; we just use
1519
+ # the ast_transformers mechanism instead of the
1520
+ # prefilter mechanism as an optimization to avoid
1521
+ # re-parsing the text into an AST.
1522
+ #
1523
+ # We use raise_on_error=False to avoid propagating any
1524
+ # exceptions here. That would cause IPython to try to
1525
+ # remove the ast_transformer. On error, we've already
1526
+ # done that ourselves.
1527
+ logger.debug("_AutoImporter_ast_transformer.visit()")
1528
+ self.auto_import(node, raise_on_error=False)
1529
+ return node
1530
+ self._ast_transformer = t = _AutoImporter_ast_transformer()
1531
+ ip.ast_transformers.append(t)
1532
+ def unregister_ast_transformer():
1533
+ try:
1534
+ ip.ast_transformers.remove(t)
1535
+ except ValueError:
1536
+ logger.info(
1537
+ "Couldn't remove ast_transformer hook - already gone?")
1538
+ self._ast_transformer = None
1539
+ self._disablers.append(unregister_ast_transformer)
1540
+ return True
1541
+ elif hasattr(ip, "run_ast_nodes"):
1542
+ # Second choice: advise the run_ast_nodes() function. Tested with
1543
+ # IPython 0.11, 0.12, 0.13. This is the most robust way available
1544
+ # for those versions.
1545
+ # (ip.compile.ast_parse also works in IPython 0.12-0.13; no major
1546
+ # flaw, but might as well use the same mechanism that works in
1547
+ # 0.11.)
1548
+ @self._advise(ip.run_ast_nodes)
1549
+ def run_ast_nodes_with_autoimport(nodelist, *args, **kwargs):
1550
+ logger.debug("run_ast_nodes")
1551
+ ast_node = ast.Module(nodelist)
1552
+ self.auto_import(ast_node)
1553
+ return __original__(nodelist, *args, **kwargs)
1554
+ return True
1555
+ elif hasattr(ip, 'compile'):
1556
+ # Third choice: Advise ip.compile.
1557
+ # Tested with IPython 0.10.
1558
+ # We don't hook prefilter because that gets called once per line,
1559
+ # not per multiline code.
1560
+ # We don't hook runsource because that gets called incrementally
1561
+ # with partial multiline source until the source is complete.
1562
+ @self._advise((ip, "compile"))
1563
+ def compile_with_autoimport(source, filename="<input>",
1564
+ symbol="single"):
1565
+ result = __original__(source, filename, symbol)
1566
+ if result is None:
1567
+ # The original ip.compile is an instance of
1568
+ # codeop.CommandCompiler. CommandCompiler.__call__
1569
+ # returns None if the source is a possibly incomplete
1570
+ # multiline block of code. In that case we don't
1571
+ # autoimport yet.
1572
+ pass
1573
+ else:
1574
+ # Got full code that our caller, runsource, will execute.
1575
+ self.auto_import(source)
1576
+ return result
1577
+ return True
1578
+ else:
1579
+ logger.debug("Couldn't enable parse hook")
1580
+ return False
1581
+
1582
+ def _enable_time_hook(self, ip):
1583
+ """
1584
+ Enable a hook so that %time will autoimport.
1585
+ """
1586
+ # For IPython 1.0+, the ast_transformer takes care of it.
1587
+ if self._ast_transformer:
1588
+ return True
1589
+ # Otherwise, we advise %time to temporarily override the compile()
1590
+ # builtin within it.
1591
+ if hasattr(ip, 'magics_manager'):
1592
+ # Tested with IPython 0.13. (IPython 1.0+ also has
1593
+ # magics_manager, but for those versions, ast_transformer takes
1594
+ # care of %time.)
1595
+ line_magics = ip.magics_manager.magics['line']
1596
+ @self._advise((line_magics, 'time'))
1597
+ def time_with_autoimport(*args, **kwargs):
1598
+ logger.debug("time_with_autoimport()")
1599
+ wrapped = FunctionWithGlobals(
1600
+ __original__, compile=self.compile_with_autoimport)
1601
+ return wrapped(*args, **kwargs)
1602
+ return True
1603
+ else:
1604
+ logger.debug("Couldn't enable time hook")
1605
+ return False
1606
+
1607
+ def _enable_timeit_hook(self, ip):
1608
+ """
1609
+ Enable a hook so that %timeit will autoimport.
1610
+ """
1611
+ # For IPython 1.0+, the ast_transformer takes care of it.
1612
+ if self._ast_transformer:
1613
+ return True
1614
+ # Otherwise, we advise %timeit to temporarily override the compile()
1615
+ # builtin within it.
1616
+ if hasattr(ip, 'magics_manager'):
1617
+ # Tested with IPython 0.13. (IPython 1.0+ also has
1618
+ # magics_manager, but for those versions, ast_transformer takes
1619
+ # care of %timeit.)
1620
+ line_magics = ip.magics_manager.magics['line']
1621
+ @self._advise((line_magics, 'timeit'))
1622
+ def timeit_with_autoimport(*args, **kwargs):
1623
+ logger.debug("timeit_with_autoimport()")
1624
+ wrapped = FunctionWithGlobals(
1625
+ __original__, compile=self.compile_with_autoimport)
1626
+ return wrapped(*args, **kwargs)
1627
+ return True
1628
+ else:
1629
+ logger.debug("Couldn't enable timeit hook")
1630
+ return False
1631
+
1632
+ def _enable_prun_hook(self, ip):
1633
+ """
1634
+ Enable a hook so that %prun will autoimport.
1635
+ """
1636
+ if hasattr(ip, 'magics_manager'):
1637
+ # Tested with IPython 1.0, 1.1, 1.2, 2.0, 2.1, 2.2, 2.3, 2.4, 3.0,
1638
+ # 3.1, 3.2, 4.0.
1639
+ line_magics = ip.magics_manager.magics['line']
1640
+ execmgr = get_method_self(line_magics['prun'])#.im_self
1641
+ if hasattr(execmgr, "_run_with_profiler"):
1642
+ @self._advise(execmgr._run_with_profiler)
1643
+ def run_with_profiler_with_autoimport(code, opts, namespace):
1644
+ logger.debug("run_with_profiler_with_autoimport()")
1645
+ self.auto_import(code, [namespace])
1646
+ return __original__(code, opts, namespace)
1647
+ return True
1648
+ else:
1649
+ # Tested with IPython 0.13.
1650
+ class ProfileFactory_with_autoimport(object):
1651
+ def Profile(self_, *args):
1652
+ import profile
1653
+ p = profile.Profile()
1654
+ @advise(p.runctx)
1655
+ def runctx_with_autoimport(cmd, globals, locals):
1656
+ self.auto_import(cmd, [globals, locals])
1657
+ return __original__(cmd, globals, locals)
1658
+ return p
1659
+ @self._advise((line_magics, 'prun'))
1660
+ def prun_with_autoimport(*args, **kwargs):
1661
+ logger.debug("prun_with_autoimport()")
1662
+ wrapped = FunctionWithGlobals(
1663
+ __original__, profile=ProfileFactory_with_autoimport())
1664
+ return wrapped(*args, **kwargs)
1665
+ return True
1666
+ else:
1667
+ logger.debug("Couldn't enable prun hook")
1668
+ return False
1669
+
1670
+
1671
+ def _enable_completer_hooks(self, completer):
1672
+ # Hook a completer instance.
1673
+ #
1674
+ # This is called:
1675
+ # - initially when enabling pyflyby
1676
+ # - each time we enter the debugger, since each Pdb instance has its
1677
+ # own completer
1678
+ #
1679
+ # There are a few different places within IPython we can consider
1680
+ # hooking/advising:
1681
+ # * ip.completer.custom_completers / ip.set_hook("complete_command")
1682
+ # * ip.completer.python_matches
1683
+ # * ip.completer.global_matches
1684
+ # * ip.completer.attr_matches
1685
+ # * ip.completer.python_func_kw_matches
1686
+ #
1687
+ # The "custom_completers" list, which set_hook("complete_command")
1688
+ # manages, is not useful because that only works for specific commands.
1689
+ # (A "command" refers to the first word on a line, such as "cd".)
1690
+ #
1691
+ # We avoid advising attr_matcher() and minimise inference with
1692
+ # global_matcher(), because these are not public API hooks,
1693
+ # and contain a lot of logic which would need to be reproduced
1694
+ # here for high quality completions in edge cases.
1695
+ #
1696
+ # Instead, we hook into three public APIs:
1697
+ # * generics.complete_object - for attribute completion
1698
+ # * global_namespace - for completion of modules before they get imported
1699
+ # (in the `global_matches` context only)
1700
+ # * auto_import_method - for auto-import
1701
+ logger.debug("_enable_completer_hooks(%r)", completer)
1702
+
1703
+ if hasattr(completer, "policy_overrides"):
1704
+ # `policy_overrides` and `auto_import_method` were added in IPython 9.3
1705
+ old_policy = completer.policy_overrides.copy()
1706
+ old_auto_import_method = completer.auto_import_method
1707
+
1708
+ completer.policy_overrides.update({"allow_auto_import": True})
1709
+ completer.auto_import_method = "pyflyby._interactive._auto_import_hook"
1710
+
1711
+ def disable_custom_completer_policies():
1712
+ completer.policy_overrides = old_policy
1713
+ completer.auto_import_method = old_auto_import_method
1714
+
1715
+ self._disablers.append(disable_custom_completer_policies)
1716
+
1717
+ if getattr(completer, 'use_jedi', False) and hasattr(completer, 'python_matcher'):
1718
+ # IPython 6.0+ uses jedi completion by default, which bypasses
1719
+ # the global and attr matchers. For now we manually reenable
1720
+ # them. A TODO would be to hook the Jedi completer itself.
1721
+ if completer.python_matcher not in completer.matchers:
1722
+ @self._advise(type(completer).matchers)
1723
+ def matchers_with_python_matcher(completer):
1724
+ return __original__.fget(completer) + [completer.python_matcher]
1725
+
1726
+ @self._advise(completer.global_matches)
1727
+ def global_matches_with_autoimport(name, *args, **kwargs):
1728
+ old_global_namespace = completer.global_namespace
1729
+ completer.global_namespace = NamespaceWithPotentialImports(
1730
+ old_global_namespace,
1731
+ ip=self._ip
1732
+ )
1733
+ try:
1734
+ return self._safe_call(__original__, name, *args, **kwargs)
1735
+ finally:
1736
+ completer.global_namespace = old_global_namespace
1737
+
1738
+ from IPython.utils import generics
1739
+ object_hook_enabled = True
1740
+
1741
+ @generics.complete_object.register(object)
1742
+ def complete_object_hook(obj, words):
1743
+ if not object_hook_enabled:
1744
+ return words
1745
+ logger.debug("complete_object_hook(%r)", obj)
1746
+ # Get the database of known imports.
1747
+ db = ImportDB.interpret_arg(None, target_filename=".")
1748
+ known = db.known_imports
1749
+ results = set(words)
1750
+ pname = obj.__name__
1751
+ # Is it a package/module?
1752
+ if sys.modules.get(pname, Ellipsis) is obj:
1753
+ # Add known_imports entries from the database.
1754
+ results.update(known.member_names.get(pname, []))
1755
+ # Get the module handle. Note that we use ModuleHandle() on the
1756
+ # *name* of the module (``pname``) instead of the module instance
1757
+ # (``obj``). Using the module instance normally works, but
1758
+ # breaks if the module hackily replaced itself with a pseudo
1759
+ # module (e.g. https://github.com/josiahcarlson/mprop).
1760
+ pmodule = ModuleHandle(pname)
1761
+ # Add importable submodules.
1762
+ results.update([m.name.parts[-1] for m in pmodule.submodules])
1763
+ results = sorted([r for r in results])
1764
+ return results
1765
+
1766
+ def disable_custom_completer_object_hook():
1767
+ nonlocal object_hook_enabled
1768
+ object_hook_enabled = False
1769
+
1770
+ self._disablers.append(disable_custom_completer_object_hook)
1771
+
1772
+ return True
1773
+
1774
+ def _enable_completion_hook(self, ip):
1775
+ """
1776
+ Enable a tab-completion hook.
1777
+ """
1778
+ return self._enable_completer_hooks(getattr(ip, "Completer", None))
1779
+
1780
+ def _enable_run_hook(self, ip):
1781
+ """
1782
+ Enable a hook so that %run will autoimport.
1783
+ """
1784
+ if hasattr(ip, "safe_execfile"):
1785
+ # Tested with IPython 0.10, 0.11, 0.12, 0.13, 1.0, 1.2, 2.0, 2.3,
1786
+ # 2.4, 3.0, 3.1, 3.2, 4.0.
1787
+ @self._advise(ip.safe_execfile)
1788
+ def safe_execfile_with_autoimport(filename,
1789
+ globals=None, locals=None,
1790
+ **kwargs):
1791
+ logger.debug("safe_execfile %r", filename)
1792
+ if globals is None:
1793
+ globals = {}
1794
+ if locals is None:
1795
+ locals = globals
1796
+ namespaces = [globals, locals]
1797
+ try:
1798
+ block = PythonBlock(Filename(filename))
1799
+ ast_node = block.ast_node
1800
+ self.auto_import(ast_node, namespaces)
1801
+ except Exception as e:
1802
+ logger.error("%s: %s", type(e).__name__, e)
1803
+ return __original__(filename, *namespaces, **kwargs)
1804
+ return True
1805
+ else:
1806
+ logger.debug("Couldn't enable execfile hook")
1807
+ return False
1808
+
1809
+ def _enable_debugger_hook(self, ip):
1810
+ try:
1811
+ Pdb = _get_IPdb_class()
1812
+ except Exception as e:
1813
+ logger.debug("Couldn't locate Pdb class: %s: %s",
1814
+ type(e).__name__, e)
1815
+ return False
1816
+ try:
1817
+ TerminalPdb = _get_TerminalPdb_class()
1818
+ except Exception as e:
1819
+ logger.debug("Couldn't locate TerminalPdb class: %s: %s",
1820
+ type(e).__name__, e)
1821
+ TerminalPdb = None
1822
+ @contextmanager
1823
+ def HookPdbCtx():
1824
+ def Pdb_with_autoimport(self_pdb, *args):
1825
+ __original__(self_pdb, *args)
1826
+ _enable_pdb_hooks(self_pdb)
1827
+ def TerminalPdb_with_autoimport(self_pdb, *args):
1828
+ __original__(self_pdb, *args)
1829
+ _enable_terminal_pdb_hooks(self_pdb, self)
1830
+ with AdviceCtx(Pdb.__init__, Pdb_with_autoimport):
1831
+ if TerminalPdb is None:
1832
+ yield
1833
+ else:
1834
+ with AdviceCtx(TerminalPdb.__init__, TerminalPdb_with_autoimport):
1835
+ yield
1836
+ iptb = getattr(ip, "InteractiveTB", None)
1837
+ ok = True
1838
+ if hasattr(iptb, "debugger"):
1839
+ # Hook ip.InteractiveTB.debugger(). This implements auto
1840
+ # importing for "%debug" (postmortem mode).
1841
+ # Tested with IPython 0.10, 0.11, 0.12, 0.13, 1.0, 1.1, 1.2, 2.0,
1842
+ # 2.1, 2.2, 2.3, 2.4, 3.0, 3.1, 3.2, 4.0.
1843
+ @self._advise(iptb.debugger)
1844
+ def debugger_with_autoimport(*args, **kwargs):
1845
+ with HookPdbCtx():
1846
+ return __original__(*args, **kwargs)
1847
+ else:
1848
+ ok = False
1849
+ if hasattr(ip, 'magics_manager'):
1850
+ # Hook ExecutionMagics._run_with_debugger(). This implements auto
1851
+ # importing for "%debug <statement>".
1852
+ # Tested with IPython 1.0, 1.1, 1.2, 2.0, 2.1, 2.2, 2.3, 2.4, 3.0,
1853
+ # 3.1, 3.2, 4.0, 5.8.
1854
+ line_magics = ip.magics_manager.magics['line']
1855
+ execmgr = get_method_self(line_magics['debug'])
1856
+ if hasattr(execmgr, "_run_with_debugger"):
1857
+ @self._advise(execmgr._run_with_debugger)
1858
+ def run_with_debugger_with_autoimport(code, code_ns,
1859
+ filename=None,
1860
+ *args, **kwargs):
1861
+ db = ImportDB.get_default(filename or ".")
1862
+ auto_import(code, namespaces=[code_ns], db=db)
1863
+ with HookPdbCtx():
1864
+ return __original__(code, code_ns, filename,
1865
+ *args, **kwargs
1866
+ )
1867
+ else:
1868
+ # IPython 0.13 and earlier don't have "%debug <statement>".
1869
+ pass
1870
+ else:
1871
+ ok = False
1872
+ return ok
1873
+
1874
+
1875
+ def _enable_ipython_shell_bugfixes(self, ip):
1876
+ """
1877
+ Enable some advice that's actually just fixing bugs in IPython.
1878
+ """
1879
+ # IPython 2.x on Python 2.x has a bug where 'run -n' doesn't work
1880
+ # because it uses Unicode for the module name. This is a bug in
1881
+ # IPython itself ("run -n" is plain broken for ipython-2.x on
1882
+ # python-2.x); we patch it here.
1883
+ return True
1884
+
1885
+ def disable(self):
1886
+ """
1887
+ Turn off auto-importer in the current IPython session.
1888
+ """
1889
+ if self._state is _EnableState.DISABLED:
1890
+ logger.debug("disable(): already disabled")
1891
+ return
1892
+ logger.debug("disable(): state: %s=>DISABLING", self._state)
1893
+ self._state = _EnableState.DISABLING
1894
+ while self._disablers:
1895
+ f = self._disablers.pop(-1)
1896
+ try:
1897
+ f()
1898
+ except Exception as e:
1899
+ self._errored = True
1900
+ logger.error("Error while disabling: %s: %s", type(e).__name__, e)
1901
+ if logger.debug_enabled:
1902
+ raise
1903
+ else:
1904
+ logger.info(
1905
+ "Set the env var PYFLYBY_LOG_LEVEL=DEBUG to debug.")
1906
+ logger.debug("disable(): state: %s=>DISABLED", self._state)
1907
+ self._state = _EnableState.DISABLED
1908
+
1909
+ def _safe_call(self, function, *args, **kwargs):
1910
+ on_error = kwargs.pop("on_error", None)
1911
+ raise_on_error: Union[bool, Literal["if_debug"]] = kwargs.pop(
1912
+ "raise_on_error", "if_debug"
1913
+ )
1914
+ if self._errored:
1915
+ # If we previously errored, then we should already have
1916
+ # unregistered the hook that led to here. However, in some corner
1917
+ # cases we can get called one more time. If so, go straight to
1918
+ # the on_error case.
1919
+ pass
1920
+ else:
1921
+ try:
1922
+ return function(*args, **kwargs)
1923
+ except Exception as e:
1924
+ # Something went wrong. Remember that we've had a problem.
1925
+ self._errored = True
1926
+ logger.error("%s: %s", type(e).__name__, e)
1927
+ if not logger.debug_enabled:
1928
+ logger.info(
1929
+ "Set the env var PYFLYBY_LOG_LEVEL=DEBUG to debug.")
1930
+ logger.warning("Disabling pyflyby auto importer.")
1931
+ # Disable everything. If something's broken, chances are
1932
+ # other stuff is broken too.
1933
+ try:
1934
+ self.disable()
1935
+ except Exception as e2:
1936
+ logger.error("Error trying to disable: %s: %s",
1937
+ type(e2).__name__, e2)
1938
+ # Raise or print traceback in debug mode.
1939
+ if raise_on_error is True:
1940
+ raise
1941
+ elif raise_on_error == 'if_debug':
1942
+ if logger.debug_enabled:
1943
+ if type(e) == SyntaxError:
1944
+ # The traceback for SyntaxError tends to get
1945
+ # swallowed, so print it out now.
1946
+ import traceback
1947
+ traceback.print_exc()
1948
+ raise
1949
+ elif raise_on_error is False:
1950
+ if logger.debug_enabled:
1951
+ import traceback
1952
+ traceback.print_exc()
1953
+ else:
1954
+ logger.error("internal error: invalid raise_on_error=%r",
1955
+ raise_on_error)
1956
+ # Return what user wanted to in case of error.
1957
+ if on_error:
1958
+ return on_error(*args, **kwargs)
1959
+ else:
1960
+ return None # just to be explicit
1961
+
1962
+ def reset_state_new_cell(self):
1963
+ # Reset the state for a new cell.
1964
+ if logger.debug_enabled:
1965
+ autoimported = self._autoimported_this_cell
1966
+ logger.debug("reset_state_new_cell(): previously autoimported: "
1967
+ "succeeded=%s, failed=%s",
1968
+ sorted([k for k,v in autoimported.items() if v]),
1969
+ sorted([k for k,v in autoimported.items() if not v]))
1970
+ self._autoimported_this_cell = {}
1971
+
1972
+ def auto_import(
1973
+ self,
1974
+ arg,
1975
+ namespaces=None,
1976
+ raise_on_error: Union[bool, Literal["if_debug"]] = "if_debug",
1977
+ on_error=None,
1978
+ ):
1979
+ if namespaces is None:
1980
+ namespaces = get_global_namespaces(self._ip)
1981
+
1982
+ def post_import_hook(imp):
1983
+ if not str(imp).startswith(PYFLYBY_LAZY_LOAD_PREFIX):
1984
+ send_comm_message(MISSING_IMPORTS, {"missing_imports": str(imp)})
1985
+
1986
+ return self._safe_call(
1987
+ auto_import, arg=arg, namespaces=namespaces,
1988
+ extra_db=self.db,
1989
+ autoimported=self._autoimported_this_cell,
1990
+ raise_on_error=raise_on_error, on_error=on_error,
1991
+ post_import_hook=post_import_hook)
1992
+
1993
+ def compile_with_autoimport(self, src, filename, mode, flags=0):
1994
+ logger.debug("compile_with_autoimport(%r)", src)
1995
+ ast_node = compile(src, filename, mode, flags|ast.PyCF_ONLY_AST,
1996
+ dont_inherit=True)
1997
+ self.auto_import(ast_node)
1998
+ if flags & ast.PyCF_ONLY_AST:
1999
+ return ast_node
2000
+ else:
2001
+ return compile(ast_node, filename, mode, flags, dont_inherit=True)
2002
+
2003
+ def _advise(self, joinpoint):
2004
+ def advisor(f):
2005
+ aspect = Aspect(joinpoint)
2006
+ if aspect.advise(f, once=True):
2007
+ self._disablers.append(aspect.unadvise)
2008
+ return advisor
2009
+
2010
+
2011
+
2012
+ def enable_auto_importer(if_no_ipython='raise'):
2013
+ """
2014
+ Turn on the auto-importer in the current IPython application.
2015
+
2016
+ :param if_no_ipython:
2017
+ If we are not inside IPython and if_no_ipython=='ignore', then silently
2018
+ do nothing.
2019
+ If we are not inside IPython and if_no_ipython=='raise', then raise
2020
+ NoActiveIPythonAppError.
2021
+ """
2022
+ try:
2023
+ app = _get_ipython_app()
2024
+ except NoActiveIPythonAppError:
2025
+ if if_no_ipython=='ignore':
2026
+ return
2027
+ else:
2028
+ raise
2029
+ auto_importer = AutoImporter(app)
2030
+ auto_importer.enable()
2031
+
2032
+
2033
+ def disable_auto_importer():
2034
+ """
2035
+ Turn off the auto-importer in the current IPython application.
2036
+ """
2037
+ try:
2038
+ app = _get_ipython_app()
2039
+ except NoActiveIPythonAppError:
2040
+ return
2041
+ auto_importer = AutoImporter(app)
2042
+ auto_importer.disable()
2043
+
2044
+
2045
+ def load_ipython_extension(arg=Ellipsis):
2046
+ """
2047
+ Turn on pyflyby features, including the auto-importer, for the given
2048
+ IPython shell.
2049
+
2050
+ Clear the ImportDB cache of known-imports.
2051
+
2052
+ This function is used by IPython's extension mechanism.
2053
+
2054
+ To load pyflyby in an existing IPython session, run::
2055
+
2056
+ In [1]: %load_ext pyflyby
2057
+
2058
+ To refresh the imports database (if you modified ~/.pyflyby), run::
2059
+
2060
+ In [1]: %reload_ext pyflyby
2061
+
2062
+ To load pyflyby automatically on IPython startup, append to
2063
+ ~/.ipython/profile_default/ipython_config.py::
2064
+ c.InteractiveShellApp.extensions.append("pyflyby")
2065
+
2066
+ :type arg:
2067
+ ``InteractiveShell``
2068
+ :see:
2069
+ http://ipython.org/ipython-doc/dev/config/extensions/index.html
2070
+ """
2071
+ logger.debug("load_ipython_extension() called for %s",
2072
+ os.path.dirname(__file__))
2073
+ # Turn on the auto-importer.
2074
+ auto_importer = AutoImporter(arg)
2075
+ if arg is not Ellipsis:
2076
+ arg._auto_importer = auto_importer
2077
+ auto_importer.enable(even_if_previously_errored=True)
2078
+ # Clear ImportDB cache.
2079
+ ImportDB.clear_default_cache()
2080
+ # Clear the set of errored imports.
2081
+ clear_failed_imports_cache()
2082
+ # Enable debugging tools. These aren't IPython-specific, and are better
2083
+ # put in usercustomize.py. But this is a convenient way for them to be
2084
+ # loaded. They're fine to run again even if they've already been run via
2085
+ # usercustomize.py.
2086
+ from ._dbg import (enable_faulthandler,
2087
+ enable_signal_handler_debugger,
2088
+ enable_sigterm_handler,
2089
+ add_debug_functions_to_builtins)
2090
+ enable_faulthandler()
2091
+ enable_signal_handler_debugger()
2092
+ enable_sigterm_handler(on_existing_handler='keep_existing')
2093
+ add_debug_functions_to_builtins(add_deprecated=False)
2094
+ inject_dynamic_import()
2095
+ initialize_comms()
2096
+
2097
+
2098
+ def unload_ipython_extension(arg=Ellipsis):
2099
+ """
2100
+ Turn off pyflyby features, including the auto-importer.
2101
+
2102
+ This function is used by IPython's extension mechanism.
2103
+
2104
+ To unload interactively, run::
2105
+
2106
+ In [1]: %unload_ext pyflyby
2107
+ """
2108
+ logger.debug("unload_ipython_extension() called for %s",
2109
+ os.path.dirname(__file__))
2110
+ auto_importer = AutoImporter(arg)
2111
+ auto_importer.disable()
2112
+ remove_comms()
2113
+ # TODO: disable signal handlers etc.