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