pyflyby 1.10.1__cp311-cp311-manylinux_2_24_x86_64.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.

Files changed (55) hide show
  1. pyflyby/__init__.py +61 -0
  2. pyflyby/__main__.py +9 -0
  3. pyflyby/_autoimp.py +2229 -0
  4. pyflyby/_cmdline.py +548 -0
  5. pyflyby/_comms.py +221 -0
  6. pyflyby/_dbg.py +1367 -0
  7. pyflyby/_docxref.py +379 -0
  8. pyflyby/_dynimp.py +154 -0
  9. pyflyby/_fast_iter_modules.cpython-311-x86_64-linux-gnu.so +0 -0
  10. pyflyby/_file.py +771 -0
  11. pyflyby/_flags.py +230 -0
  12. pyflyby/_format.py +186 -0
  13. pyflyby/_idents.py +227 -0
  14. pyflyby/_import_sorting.py +165 -0
  15. pyflyby/_importclns.py +658 -0
  16. pyflyby/_importdb.py +680 -0
  17. pyflyby/_imports2s.py +643 -0
  18. pyflyby/_importstmt.py +723 -0
  19. pyflyby/_interactive.py +2113 -0
  20. pyflyby/_livepatch.py +793 -0
  21. pyflyby/_log.py +104 -0
  22. pyflyby/_modules.py +641 -0
  23. pyflyby/_parse.py +1381 -0
  24. pyflyby/_py.py +2166 -0
  25. pyflyby/_saveframe.py +1145 -0
  26. pyflyby/_saveframe_reader.py +471 -0
  27. pyflyby/_util.py +458 -0
  28. pyflyby/_version.py +7 -0
  29. pyflyby/autoimport.py +20 -0
  30. pyflyby/etc/pyflyby/canonical.py +10 -0
  31. pyflyby/etc/pyflyby/common.py +27 -0
  32. pyflyby/etc/pyflyby/forget.py +10 -0
  33. pyflyby/etc/pyflyby/mandatory.py +10 -0
  34. pyflyby/etc/pyflyby/numpy.py +156 -0
  35. pyflyby/etc/pyflyby/std.py +335 -0
  36. pyflyby/importdb.py +19 -0
  37. pyflyby/libexec/pyflyby/colordiff +34 -0
  38. pyflyby/libexec/pyflyby/diff-colorize +148 -0
  39. pyflyby/share/emacs/site-lisp/pyflyby.el +108 -0
  40. pyflyby-1.10.1.data/scripts/collect-exports +76 -0
  41. pyflyby-1.10.1.data/scripts/collect-imports +58 -0
  42. pyflyby-1.10.1.data/scripts/find-import +38 -0
  43. pyflyby-1.10.1.data/scripts/list-bad-xrefs +34 -0
  44. pyflyby-1.10.1.data/scripts/prune-broken-imports +34 -0
  45. pyflyby-1.10.1.data/scripts/pyflyby-diff +34 -0
  46. pyflyby-1.10.1.data/scripts/reformat-imports +27 -0
  47. pyflyby-1.10.1.data/scripts/replace-star-imports +37 -0
  48. pyflyby-1.10.1.data/scripts/saveframe +299 -0
  49. pyflyby-1.10.1.data/scripts/tidy-imports +163 -0
  50. pyflyby-1.10.1.data/scripts/transform-imports +47 -0
  51. pyflyby-1.10.1.dist-info/METADATA +591 -0
  52. pyflyby-1.10.1.dist-info/RECORD +55 -0
  53. pyflyby-1.10.1.dist-info/WHEEL +5 -0
  54. pyflyby-1.10.1.dist-info/entry_points.txt +4 -0
  55. pyflyby-1.10.1.dist-info/licenses/LICENSE.txt +23 -0
pyflyby/_autoimp.py ADDED
@@ -0,0 +1,2229 @@
1
+ # pyflyby/_autoimp.py.
2
+ # Copyright (C) 2011, 2012, 2013, 2014, 2015, 2018, 2019, 2024 Karl Chen.
3
+ # License: MIT http://opensource.org/licenses/MIT
4
+
5
+
6
+
7
+ from __future__ import annotations, print_function
8
+
9
+ import ast
10
+ import builtins
11
+ from collections.abc import Sequence
12
+ import contextlib
13
+ import copy
14
+
15
+ from pyflyby._file import FileText, Filename
16
+ from pyflyby._flags import CompilerFlags
17
+ from pyflyby._idents import (BadDottedIdentifierError,
18
+ DottedIdentifier, brace_identifiers)
19
+ from pyflyby._importdb import ImportDB
20
+ from pyflyby._importstmt import Import
21
+ from pyflyby._log import logger
22
+ from pyflyby._modules import ModuleHandle
23
+ from pyflyby._parse import (MatchAs, PythonBlock, _is_ast_str,
24
+ infer_compile_mode)
25
+
26
+ from six import reraise
27
+ import sys
28
+ import types
29
+ from typing import (Any, Dict, List, Optional, Set, Tuple,
30
+ Union)
31
+
32
+
33
+ if sys.version_info >= (3, 13):
34
+ def f():
35
+ return sys._getframe().f_locals
36
+
37
+ FrameLocalsProxyType = type(f())
38
+ else:
39
+ FrameLocalsProxyType = dict
40
+
41
+ if sys.version_info >= (3, 12):
42
+ ATTRIBUTE_NAME = "value"
43
+ else:
44
+ ATTRIBUTE_NAME = "s"
45
+
46
+ if sys.version_info > (3, 11):
47
+ LOAD_SHIFT = 1
48
+ else:
49
+ LOAD_SHIFT = 0
50
+
51
+
52
+ if sys.version_info >= (3, 10):
53
+ from types import NoneType, EllipsisType
54
+ else:
55
+ NoneType = type(None)
56
+ EllipsisType = type(Ellipsis)
57
+
58
+
59
+ class _ClassScope(dict):
60
+ pass
61
+
62
+
63
+ def __repr__(self):
64
+ return "_ClassScope(" + repr(super()) + ")"
65
+
66
+
67
+ _builtins2 = {"__file__": None}
68
+
69
+
70
+ class ScopeStack(Sequence):
71
+ """
72
+ A stack of namespace scopes, as a tuple of ``dict`` s.
73
+
74
+ Each entry is a ``dict``.
75
+
76
+ Ordered from most-global to most-local.
77
+ Builtins are always included.
78
+ Duplicates are removed.
79
+ """
80
+
81
+ _cached_has_star_import = False
82
+
83
+ def __init__(self, arg, _class_delayed=None):
84
+ """
85
+ Interpret argument as a ``ScopeStack``.
86
+
87
+ :type arg:
88
+ ``ScopeStack``, ``dict``, ``list`` of ``dict``
89
+ :param arg:
90
+ Input namespaces
91
+ :rtype:
92
+ ``ScopeStack``
93
+ """
94
+ if isinstance(arg, ScopeStack):
95
+ scopes = list(arg._tup)
96
+ elif isinstance(arg, dict):
97
+ scopes = [arg]
98
+ elif isinstance(arg, (tuple, list)):
99
+ scopes = list(arg)
100
+ else:
101
+ raise TypeError(
102
+ "ScopeStack: expected a sequence of dicts; got a %s"
103
+ % (type(arg).__name__,))
104
+ if not len(scopes):
105
+ raise TypeError("ScopeStack: no scopes given")
106
+ if not all(isinstance(scope, (dict, FrameLocalsProxyType)) for scope in scopes):
107
+ raise TypeError("ScopeStack: Expected list of dict or FrameLocalsProxy objects; got a sequence of %r"
108
+ % ([type(x).__name__ for x in scopes]))
109
+ scopes = [builtins.__dict__, _builtins2] + scopes
110
+ result = []
111
+ seen = set()
112
+ # Keep only unique items, checking uniqueness by object identity.
113
+ for scope in scopes:
114
+ if id(scope) in seen:
115
+ continue
116
+ seen.add(id(scope))
117
+ result.append(scope)
118
+ tup = tuple(result)
119
+ self._tup = tup
120
+
121
+ # class name definitions scope may need to be delayed.
122
+ # so we store them separately, and if they are present in methods def, we can readd them
123
+ if _class_delayed is None:
124
+ _class_delayed = {}
125
+ self._class_delayed = _class_delayed
126
+
127
+ def __contains__(self, item):
128
+ if isinstance(item, DottedIdentifier):
129
+ item = item.name
130
+ if isinstance(item, str):
131
+ for sub in self:
132
+ if item in sub.keys():
133
+ return True
134
+
135
+ return False
136
+
137
+ def __getitem__(self, item):
138
+ if isinstance(item, slice):
139
+ return self.__class__(self._tup[item])
140
+ return self._tup[item]
141
+
142
+ def __len__(self):
143
+ return len(self._tup)
144
+
145
+ def _with_new_scope(
146
+ self,
147
+ *,
148
+ include_class_scopes: bool,
149
+ new_class_scope: bool,
150
+ unhide_classdef: bool,
151
+ ) -> ScopeStack:
152
+ """
153
+ Return a new ``ScopeStack`` with an additional empty scope.
154
+
155
+ :param include_class_scopes:
156
+ Whether to include previous scopes that are meant for ClassDefs.
157
+ :param new_class_scope:
158
+ Whether the new scope is for a ClassDef.
159
+ :param unhide_classdef:
160
+ Unhide class definitiion scope (when we enter a method)
161
+ :rtype:
162
+ ``ScopeStack``
163
+ """
164
+ if include_class_scopes:
165
+ scopes = tuple(self)
166
+ else:
167
+ scopes = tuple(s for s in self if not isinstance(s, _ClassScope))
168
+ new_scope: Union[_ClassScope, Dict[str, Any]]
169
+ if new_class_scope:
170
+ new_scope = _ClassScope()
171
+ else:
172
+ new_scope = {}
173
+ cls = type(self)
174
+ if unhide_classdef and self._class_delayed:
175
+ scopes = tuple([self._class_delayed]) + scopes
176
+ result = cls(scopes + (new_scope,), _class_delayed=self._class_delayed)
177
+ return result
178
+
179
+ def clone_top(self):
180
+ """
181
+ Return a new ``ScopeStack`` referencing the same namespaces as ``self``,
182
+ but cloning the topmost namespace (and aliasing the others).
183
+ """
184
+ scopes = list(self)
185
+ scopes[-1] = copy.copy(scopes[-1])
186
+ cls = type(self)
187
+ return cls(scopes)
188
+
189
+ def merged_to_two(self):
190
+ """
191
+ Return a 2-tuple of dicts.
192
+
193
+ These can be used for functions that take a ``globals`` and ``locals``
194
+ argument, such as ``eval``.
195
+
196
+ If there is only one entry, then return it twice.
197
+
198
+ If there are more than two entries, then create a new dict that merges
199
+ the more-global ones. The most-local stack will alias the dict from
200
+ the existing ScopeStack.
201
+
202
+ :rtype:
203
+ ``tuple`` of (``dict``, ``dict``)
204
+ """
205
+ assert len(self) >= 1
206
+ if len(self) == 1:
207
+ return (self[0], self[0])
208
+ if len(self) == 2:
209
+ return tuple(self)
210
+ d = {}
211
+ for scope in self[:-1]:
212
+ d.update(scope)
213
+ # Return as a 2-tuple. We don't cast the result to ScopeStack because
214
+ # it may add __builtins__ again, creating something of length 3.
215
+ return (d, self[-1])
216
+
217
+
218
+ def has_star_import(self) -> bool:
219
+ """
220
+ Return whether there are any star-imports in this ScopeStack.
221
+ Only relevant in AST-based static analysis mode.
222
+ """
223
+ if self._cached_has_star_import:
224
+ return True
225
+ if any('*' in scope for scope in self):
226
+ # There was a star import. Cache that fact before returning. We
227
+ # can cache a positive result because a star import can't be undone.
228
+ self._cached_has_star_import = True
229
+ return True
230
+ else:
231
+ # There was no star import yet. We can't cache that fact because
232
+ # there might be a star import later.
233
+ return False
234
+
235
+ def __repr__(self):
236
+ scopes_reprs = [
237
+ "{:2}".format(i) + " : " + repr(namespace)
238
+ for i, namespace in enumerate(self)
239
+ ][1:]
240
+
241
+ return (
242
+ "<{class_name} object at 0x{hex_id} with namespaces: [\n".format(
243
+ class_name=self.__class__.__name__, hex_id=id(self)
244
+ )
245
+ + " 0 : {builtins namespace elided.}\n"
246
+ + "\n".join(scopes_reprs)
247
+ + "\n]>"
248
+ )
249
+
250
+
251
+ def symbol_needs_import(fullname, namespaces):
252
+ """
253
+ Return whether ``fullname`` is a symbol that needs to be imported, given
254
+ the current namespace scopes.
255
+
256
+ A symbol needs importing if it is not previously imported or otherwise
257
+ assigned. ``namespaces`` normally includes builtins and globals as well as
258
+ symbols imported/assigned locally within the scope.
259
+
260
+ If the user requested "foo.bar.baz", and we see that "foo.bar" exists
261
+ and is not a module, we assume nothing under foo.bar needs import.
262
+ This is intentional because (1) the import would not match what is
263
+ already in the namespace, and (2) we don't want to do call
264
+ getattr(foo.bar, "baz"), since that could invoke code that is slow or
265
+ has side effects.
266
+
267
+ :type fullname:
268
+ ``DottedIdentifier``
269
+ :param fullname:
270
+ Fully-qualified symbol name, e.g. "os.path.join".
271
+ :type namespaces:
272
+ ``list`` of ``dict``
273
+ :param namespaces:
274
+ Stack of namespaces to search for existing items.
275
+ :rtype:
276
+ ``bool``
277
+ :return:
278
+ ``True`` if ``fullname`` needs import, else ``False``
279
+ """
280
+ namespaces = ScopeStack(namespaces)
281
+ fullname = DottedIdentifier(fullname)
282
+ partial_names = fullname.prefixes[::-1]
283
+ # Iterate over local scopes.
284
+ for ns_idx, ns in reversed(list(enumerate(namespaces))):
285
+ # Iterate over partial names: "foo.bar.baz.quux", "foo.bar.baz", ...
286
+ for partial_name in partial_names:
287
+ # Check if this partial name was imported/assigned in this
288
+ # scope. In the common case, there will only be one namespace
289
+ # in the namespace stack, i.e. the user globals.
290
+ try:
291
+ var = ns[str(partial_name)]
292
+ except KeyError:
293
+ continue
294
+ # If we're doing static analysis where we also care about which
295
+ # imports are unused, then mark the used ones now.
296
+ if isinstance(var, _UseChecker):
297
+ var.used = True
298
+ # Suppose the user accessed fullname="foo.bar.baz.quux" and
299
+ # suppose we see "foo.bar" was imported (or otherwise assigned) in
300
+ # the scope vars (most commonly this means it was imported
301
+ # globally). Let's check if foo.bar already has a "baz".
302
+ prefix_len = len(partial_name.parts)
303
+ suffix_parts = fullname.parts[prefix_len:]
304
+ pname = str(partial_name)
305
+ for part in suffix_parts:
306
+ # Check if the var so far is a module -- in fact that it's
307
+ # *the* module of a given name. That is, for var ==
308
+ # foo.bar.baz, check if var is sys.modules['foo.bar.baz']. We
309
+ # used to just check if isinstance(foo.bar.baz, ModuleType).
310
+ # However, that naive check is wrong for these situations:
311
+ # - A module that contains an import of anything other than a
312
+ # submodule with its exact name. For example, suppose
313
+ # foo.bar contains 'import sqlalchemy'.
314
+ # foo.bar.sqlalchemy is of ModuleType, but that doesn't
315
+ # mean that we could import foo.bar.sqlalchemy.orm.
316
+ # Similar case if foo.bar contains 'from . import baz as
317
+ # baz2'. Mistaking these doesn't break much, but might as
318
+ # well avoid an unnecessary import attempt.
319
+ # - A "proxy module". Suppose foo.bar replaces itself with
320
+ # an object with a __getattr__, using
321
+ # 'sys.modules[__name__] = ...' Submodules are still
322
+ # importable, but sys.modules['foo.bar'] would not be of
323
+ # type ModuleType.
324
+ if var is not sys.modules.get(pname, object()):
325
+ # The variable is not a module. (If this came from a
326
+ # local assignment then ``var`` will just be "None"
327
+ # here to indicate we know it was assigned but don't
328
+ # know about its type.) Thus nothing under it needs
329
+ # import.
330
+ logger.debug("symbol_needs_import(%r): %s is in namespace %d (under %r) and not a global module, so it doesn't need import", fullname, pname, ns_idx, partial_name)
331
+ return False
332
+ try:
333
+ var = getattr(var, part)
334
+ except AttributeError:
335
+ # We saw that "foo.bar" is imported, and is a module, but
336
+ # it does not have a "baz" attribute. Thus, as far as we
337
+ # know so far, foo.bar.baz requires import. But continue
338
+ # on to the next scope.
339
+ logger.debug("symbol_needs_import(%r): %s is a module in namespace %d (under %r), but has no %r attribute", fullname, pname, ns_idx, partial_name, part)
340
+ break # continue outer loop
341
+ pname = "%s.%s" % (pname, part)
342
+ else:
343
+ # We saw that "foo.bar" is imported, and checked that
344
+ # foo.bar has an attribute "baz", which has an
345
+ # attribute "quux" - so foo.bar.baz.quux does not need
346
+ # to be imported.
347
+ assert pname == str(fullname)
348
+ logger.debug("symbol_needs_import(%r): found it in namespace %d (under %r), so it doesn't need import", fullname, ns_idx, partial_name)
349
+ return False
350
+ # We didn't find any scope that defined the name. Therefore it needs
351
+ # import.
352
+ logger.debug(
353
+ "symbol_needs_import(%r): no match found in namespaces %s; it needs import",
354
+ fullname,
355
+ namespaces,
356
+ )
357
+ return True
358
+
359
+
360
+ class _UseChecker:
361
+ """
362
+ An object that can check whether it was used.
363
+ """
364
+
365
+ used: bool = False
366
+ name: str
367
+ source: str
368
+ lineno: int
369
+
370
+ def __init__(self, name: str, source: str, lineno: int):
371
+ self.name = name
372
+ self.source = source # generally an Import
373
+ self.lineno = lineno
374
+ logger.debug("Create _UseChecker : %r", self)
375
+
376
+ def __repr__(self):
377
+ return f"<{type(self).__name__}: name:{self.name!r} source:{self.source!r} lineno:{self.lineno} used:{self.used}>"
378
+
379
+
380
+ class _MissingImportFinder:
381
+ """
382
+ A helper class to be used only by `_find_missing_imports_in_ast`.
383
+
384
+ This class visits every AST node and collects symbols that require
385
+ importing. A symbol requires importing if it is not already imported or
386
+ otherwise defined/assigned in this scope.
387
+
388
+ For attributes like "foo.bar.baz", we need to be more sophisticated:
389
+
390
+ Suppose the user imports "foo.bar" and then accesses "foo.bar.baz.quux".
391
+ Baz may be already available just by importing foo.bar, or it may require
392
+ further import. We decide as follows. If foo.bar is not a module, then
393
+ we assume whatever's under it can't be imported. If foo.bar is a module
394
+ but does not have a 'baz' attribute, then it does require import.
395
+
396
+ """
397
+
398
+ scopestack: ScopeStack
399
+ _lineno: Optional[int]
400
+ missing_imports: List[Tuple[Optional[int], DottedIdentifier]]
401
+ parse_docstrings: bool
402
+ unused_imports: Optional[List[Tuple[int, str]]]
403
+ _deferred_load_checks: list[tuple[str, ScopeStack, Optional[int]]]
404
+
405
+ def __init__(self, scopestack, *, find_unused_imports:bool, parse_docstrings:bool):
406
+ """
407
+ Construct the AST visitor.
408
+
409
+ :type scopestack:
410
+ `ScopeStack`
411
+ :param scopestack:
412
+ Initial scope stack.
413
+ """
414
+ # Create a stack of namespaces. The caller should pass in a list that
415
+ # includes the globals dictionary. ScopeStack() will make sure this
416
+ # includes builtins.
417
+ _scopestack = ScopeStack(scopestack)
418
+
419
+ # Add an empty namespace to the stack. This facilitates adding stuff
420
+ # to scopestack[-1] without ever modifying user globals.
421
+ self.scopestack = _scopestack._with_new_scope(
422
+ include_class_scopes=False, new_class_scope=False, unhide_classdef=False
423
+ )
424
+
425
+ # Create data structure to hold the result.
426
+ # missing_imports is a list of (lineno, DottedIdentifier) tuples.
427
+ self.missing_imports = []
428
+
429
+ # unused_imports is a list of (lineno, Import) tuples, if enabled.
430
+ self.unused_imports = [] if find_unused_imports else None
431
+
432
+ self.parse_docstrings = parse_docstrings
433
+
434
+ # Function bodies that we need to check after defining names in this
435
+ # function scope.
436
+ self._deferred_load_checks = []
437
+
438
+ # Whether we're currently in a FunctionDef.
439
+ self._in_FunctionDef = False
440
+ # Current lineno.
441
+ self._lineno = None
442
+ self._in_class_def = 0
443
+
444
+ def find_missing_imports(self, node):
445
+ self._scan_node(node)
446
+ return sorted(set(imp for lineno,imp in self.missing_imports))
447
+
448
+ def _scan_node(self, node):
449
+ oldscopestack = self.scopestack
450
+ myglobals = self.scopestack[-1]
451
+ try:
452
+ self.visit(node)
453
+ self._finish_deferred_load_checks()
454
+ assert self.scopestack is oldscopestack
455
+ assert self.scopestack[-1] is myglobals
456
+ finally:
457
+ self.scopestack = oldscopestack
458
+
459
+ def scan_for_import_issues(self, codeblock: PythonBlock):
460
+ assert isinstance(codeblock, PythonBlock)
461
+ # See global `scan_for_import_issues`
462
+ if not isinstance(codeblock, PythonBlock):
463
+ codeblock = PythonBlock(codeblock)
464
+ node = codeblock.ast_node
465
+ self._scan_node(node)
466
+ # Get missing imports now, before handling docstrings. We don't want
467
+ # references in doctests to be noted as missing-imports. For now we
468
+ # just let the code accumulate into self.missing_imports and ignore
469
+ # the result.
470
+ logger.debug("unused: %r", self.unused_imports)
471
+ missing_imports = sorted(self.missing_imports)
472
+ if self.parse_docstrings and self.unused_imports is not None:
473
+ doctest_blocks = codeblock.get_doctests()
474
+ # Parse each doctest. Don't report missing imports in doctests,
475
+ # but do treat existing imports as 'used' if they are used in
476
+ # doctests. The linenos are currently wrong, but we don't use
477
+ # them so it's not important to fix.
478
+ for block in doctest_blocks:
479
+ # There are doctests. Parse them.
480
+ # Doctest blocks inherit the global scope after parsing all
481
+ # non-doctest code, and each doctest block individually creates a new
482
+ # scope (not shared between doctest blocks).
483
+ # TODO: Theoretically we should clone the entire scopestack,
484
+ # not just add a new scope, in case the doctest uses 'global'.
485
+ # Currently we don't support the 'global' keyword anyway so
486
+ # this doesn't matter yet, and it's uncommon to use 'global'
487
+ # in a doctest, so this is low priority to fix.
488
+ with self._NewScopeCtx(check_unused_imports=False):
489
+ self._scan_node(block.ast_node)
490
+ # Find literal brace identifiers like "... `Foo` ...".
491
+ # TODO: Do this inline: (1) faster; (2) can use proper scope of vars
492
+ # Once we do that, use _check_load() with new args
493
+ # check_missing_imports=False, check_unused_imports=True
494
+ literal_brace_identifiers = set(
495
+ iden
496
+ for f in codeblock.string_literals()
497
+ for iden in brace_identifiers(getattr(f, ATTRIBUTE_NAME))
498
+ )
499
+ if literal_brace_identifiers:
500
+ for ident in literal_brace_identifiers:
501
+ try:
502
+ ident = DottedIdentifier(ident)
503
+ except BadDottedIdentifierError:
504
+ continue
505
+ symbol_needs_import(ident, self.scopestack)
506
+ self._scan_unused_imports()
507
+ logger.debug("missing: %s, unused: %s", missing_imports, self.unused_imports)
508
+ return missing_imports, self.unused_imports
509
+
510
+ def visit(self, node):
511
+ """
512
+ Visit a node.
513
+
514
+ :type node:
515
+ ``ast.AST`` or ``list`` of ``ast.AST``
516
+ """
517
+ # Modification of ast.NodeVisitor.visit(). Support list inputs.
518
+ logger.debug("_MissingImportFinder.visit(%r)", node)
519
+ lineno = getattr(node, 'lineno', None)
520
+ if lineno:
521
+ self._lineno = lineno
522
+ if isinstance(node, list):
523
+ for item in node:
524
+ self.visit(item)
525
+ elif isinstance(node, ast.AST):
526
+ method = 'visit_' + node.__class__.__name__
527
+ if not hasattr(self, method):
528
+ logger.debug(
529
+ "_MissingImportFinder has no method %r, using generic_visit", method
530
+ )
531
+ if hasattr(self, method):
532
+ visitor = getattr(self, method)
533
+ else:
534
+ logger.debug("No method `%s`, using `generic_visit`", method)
535
+ visitor = self.generic_visit
536
+ return visitor(node)
537
+ else:
538
+ raise TypeError("unexpected %s" % (type(node).__name__,))
539
+
540
+ def generic_visit(self, node):
541
+ """
542
+ Generic visitor that visits all of the node's field values, in the
543
+ order declared by ``node._fields``.
544
+
545
+ Called if no explicit visitor function exists for a node.
546
+ """
547
+ # Modification of ast.NodeVisitor.generic_visit: recurse to visit()
548
+ # even for lists, and be more explicit about type checking.
549
+ for field, value in ast.iter_fields(node):
550
+ if isinstance(value, ast.AST):
551
+ self.visit(value)
552
+ elif isinstance(value, list):
553
+ if all(isinstance(v, str) for v in value):
554
+ pass
555
+ elif all(isinstance(v, ast.AST) for v in value):
556
+ self.visit(value)
557
+ else:
558
+ raise TypeError(
559
+ "unexpected %s" % (", ".join(type(v).__name__ for v in value))
560
+ )
561
+ elif isinstance(
562
+ value, (int, float, complex, str, NoneType, bytes, EllipsisType)
563
+ ):
564
+ pass
565
+ else:
566
+ raise TypeError(
567
+ "unexpected %s for %s.%s"
568
+ % (type(value).__name__, type(node).__name__, field))
569
+
570
+
571
+ @contextlib.contextmanager
572
+ def _NewScopeCtx(
573
+ self,
574
+ include_class_scopes=False,
575
+ new_class_scope=False,
576
+ unhide_classdef=False,
577
+ check_unused_imports=True,
578
+ ):
579
+ """
580
+ Context manager that temporarily pushes a new empty namespace onto the
581
+ stack of namespaces.
582
+ """
583
+ prev_scopestack = self.scopestack
584
+ new_scopestack = prev_scopestack._with_new_scope(
585
+ include_class_scopes=include_class_scopes,
586
+ new_class_scope=new_class_scope,
587
+ unhide_classdef=unhide_classdef,
588
+ )
589
+ self.scopestack = new_scopestack
590
+ try:
591
+ yield
592
+ finally:
593
+ logger.debug("throwing last scope from scopestack: %r", new_scopestack[-1])
594
+ for name, use_checker in new_scopestack[-1].items():
595
+ if use_checker and use_checker.used == False and check_unused_imports:
596
+ logger.debug(
597
+ "unused checker %r scopestack_depth %r",
598
+ use_checker,
599
+ len(self.scopestack),
600
+ )
601
+ self.unused_imports.append((use_checker.lineno, use_checker.source))
602
+ assert self.scopestack is new_scopestack
603
+ self.scopestack = prev_scopestack
604
+
605
+ @contextlib.contextmanager
606
+ def _UpScopeCtx(self):
607
+ """
608
+ Context manager that temporarily moves up one in the scope stack
609
+ """
610
+ if len(self.scopestack) < 2:
611
+ raise ValueError("There must be at least two scopes on the stack to move up a scope.")
612
+ prev_scopestack = self.scopestack
613
+ new_scopestack = prev_scopestack[:-1]
614
+ try:
615
+ self.scopestack = new_scopestack
616
+ yield
617
+ finally:
618
+ assert self.scopestack is new_scopestack
619
+ self.scopestack = prev_scopestack
620
+
621
+ def visit_Assign(self, node):
622
+ # Visit an assignment statement (lhs = rhs). This implementation of
623
+ # visit_Assign is just like the generic one, but we make sure we visit
624
+ # node.value (RHS of assignment operator), then node.targets (LHS of
625
+ # assignment operator). The default would have been to visit LHS,
626
+ # then RHS. The reason we need to visit RHS first is the following.
627
+ # If the code is 'foo = foo + 1', we want to first process the Load
628
+ # for foo (RHS) before we process the Store for foo (LHS). If we
629
+ # visited LHS then RHS, we would have a bug in the following sample
630
+ # code:
631
+ # from bar import foo # L1
632
+ # foo = foo + 1 # L2
633
+ # The good RHS-then-LHS visit-order would see the Load('foo') on L2,
634
+ # understand that it got used before the Store('foo') overwrote it.
635
+ # The bad LHS-then-RHS visit-order would visit Store('foo') on L2, and
636
+ # think that foo was never referenced before it was overwritten, and
637
+ # therefore think that the 'import foo' on L1 could be removed.
638
+ self.visit(node.value)
639
+ self.visit(node.targets)
640
+ self._visit__all__(node)
641
+
642
+ def _visit__all__(self, node):
643
+ if self._in_FunctionDef:
644
+ return
645
+ if (len(node.targets) == 1 and isinstance(node.targets[0], ast.Name)
646
+ and node.targets[0].id == '__all__'):
647
+ if not isinstance(node.value, (ast.List, ast.Tuple)):
648
+ logger.warning("Don't know how to handle __all__ as (%s)" % node.value)
649
+ return
650
+ if not all(_is_ast_str(e) for e in node.value.elts):
651
+ logger.warning("Don't know how to handle __all__ with list elements other than str")
652
+ return
653
+ for e in node.value.elts:
654
+ if sys.version_info > (3,10):
655
+ self._visit_Load_defered_global(e.value)
656
+ else:
657
+ self._visit_Load_defered_global(e.s)
658
+
659
+ def visit_ClassDef(self, node):
660
+ logger.debug("visit_ClassDef(%r)", node)
661
+ if sys.version_info > (3,12):
662
+ # we don't visit type_params, so autoimport won't work yet for type annotations
663
+ assert node._fields == ('name', 'bases', 'keywords', 'body', 'decorator_list', 'type_params'), node._fields
664
+ else:
665
+ assert node._fields == ('name', 'bases', 'keywords', 'body', 'decorator_list'), node._fields
666
+ self.visit(node.bases)
667
+ self.visit(node.decorator_list)
668
+ # The class's name is only visible to others (not to the body to the
669
+ # class), but is accessible in the methods themselves. See https://github.com/deshaw/pyflyby/issues/147
670
+ self.visit(node.keywords)
671
+
672
+ # we only care about the first defined class,
673
+ # we don't detect issues with nested classes.
674
+ if self._in_class_def == 0:
675
+ self.scopestack._class_delayed[node.name] = None
676
+ with self._NewScopeCtx(new_class_scope=True):
677
+ self._in_class_def += 1
678
+ self._visit_Store(node.name)
679
+ self.visit(node.body)
680
+ self._in_class_def -= 1
681
+ assert self._in_class_def >= 0
682
+ self._remove_from_missing_imports(node.name)
683
+ self._visit_Store(node.name)
684
+
685
+ def visit_AsyncFunctionDef(self, node):
686
+ return self.visit_FunctionDef(node)
687
+
688
+ def visit_FunctionDef(self, node):
689
+ # Visit a function definition.
690
+ # - Visit args and decorator list normally.
691
+ # - Visit function body in a special mode where we defer checking
692
+ # loads until later, and don't load names from the parent ClassDef
693
+ # scope.
694
+ # - Store the name in the current scope (but not visibly to
695
+ # args/decorator_list).
696
+ if sys.version_info > (3, 12):
697
+ # we don't visit type_params, so autoimport won't work yet for type annotations
698
+ assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment', 'type_params'), node._fields
699
+ else:
700
+ assert node._fields == ('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment'), node._fields
701
+ with self._NewScopeCtx(include_class_scopes=True):
702
+ # we want `__class__` to only be defined in
703
+ # methods and not class body
704
+ if self._in_class_def:
705
+ self.scopestack[-1]["__class__"] = None # we just need to to be defined
706
+ self.visit(node.decorator_list)
707
+ self.visit(node.args)
708
+ if node.returns:
709
+ self.visit(node.returns)
710
+ self._visit_typecomment(node.type_comment)
711
+ old_in_FunctionDef = self._in_FunctionDef
712
+ self._in_FunctionDef = True
713
+ with self._NewScopeCtx(unhide_classdef=True):
714
+ if not self._in_class_def:
715
+ self._visit_Store(node.name)
716
+ self.visit(node.body)
717
+ self._in_FunctionDef = old_in_FunctionDef
718
+ self._visit_Store(node.name)
719
+
720
+ def visit_Lambda(self, node):
721
+ # Like FunctionDef, but without the decorator_list or name.
722
+ assert node._fields == ('args', 'body'), node._fields
723
+ with self._NewScopeCtx(include_class_scopes=True):
724
+ self.visit(node.args)
725
+ old_in_FunctionDef = self._in_FunctionDef
726
+ self._in_FunctionDef = True
727
+ with self._NewScopeCtx():
728
+ self.visit(node.body)
729
+ self._in_FunctionDef = old_in_FunctionDef
730
+
731
+ def _visit_typecomment(self, typecomment: str) -> None:
732
+ """
733
+ Warning, when a type comment the node is a string, not an ast node.
734
+ We also get two types of type comments:
735
+
736
+
737
+ The signature one just after a function definition
738
+
739
+ def foo(a):
740
+ # type: int -> None
741
+ pass
742
+
743
+ And the variable annotation ones:
744
+
745
+ def foo(a #type: int
746
+ ):
747
+ pass
748
+
749
+ ast parse "func_type" mode only support the first one.
750
+
751
+ """
752
+ if typecomment is None:
753
+ return
754
+ node: Union[ast.Module, ast.FunctionType]
755
+ if '->' in typecomment:
756
+ node = ast.parse(typecomment, mode='func_type')
757
+ else:
758
+ node = ast.parse(typecomment)
759
+
760
+ self.visit(node)
761
+
762
+ def visit_arguments(self, node) -> None:
763
+ assert node._fields == ('posonlyargs', 'args', 'vararg', 'kwonlyargs', 'kw_defaults', 'kwarg', 'defaults'), node._fields
764
+ # Argument/parameter list. Note that the defaults should be
765
+ # considered "Load"s from the upper scope, and the argument names are
766
+ # "Store"s in the function scope.
767
+
768
+ # E.g. consider:
769
+ # def f(x=y, y=x): pass
770
+ # Both x and y should be considered undefined (unless they were indeed
771
+ # defined before the def).
772
+ # We assume visit_arguments is always called from a _NewScopeCtx
773
+ # context
774
+ with self._UpScopeCtx():
775
+ self.visit(node.defaults)
776
+ for i in node.kw_defaults:
777
+ if i:
778
+ self.visit(i)
779
+ # Store arg names.
780
+ self.visit(node.args)
781
+ self.visit(node.kwonlyargs)
782
+ self.visit(node.posonlyargs)
783
+ # may be None.
784
+ if node.vararg:
785
+ self.visit(node.vararg)
786
+ else:
787
+ self._visit_Store(node.vararg)
788
+ if node.kwarg:
789
+ self.visit(node.kwarg)
790
+ else:
791
+ self._visit_Store(node.kwarg)
792
+
793
+ def visit_ExceptHandler(self, node) -> None:
794
+ assert node._fields == ('type', 'name', 'body')
795
+ if node.type:
796
+ self.visit(node.type)
797
+ if node.name:
798
+ self._visit_Store(node.name)
799
+ self.visit(node.body)
800
+
801
+ def visit_Dict(self, node):
802
+ assert node._fields == ('keys', 'values')
803
+ # In Python 3, keys can be None, indicating a ** expression
804
+ for key in node.keys:
805
+ if key:
806
+ self.visit(key)
807
+ self.visit(node.values)
808
+
809
+ def visit_comprehension(self, node):
810
+ # Visit a "comprehension" node, which is a component of list
811
+ # comprehensions and generator expressions.
812
+ self.visit(node.iter)
813
+ def visit_target(target):
814
+ if isinstance(target, ast.Name):
815
+ self._visit_Store(target.id)
816
+ elif isinstance(target, (ast.Tuple, ast.List)):
817
+ for elt in target.elts:
818
+ visit_target(elt)
819
+ else:
820
+ # Unusual stuff like:
821
+ # [f(x) for x[0] in mylist]
822
+ # [f(x) for x.foo in mylist]
823
+ # [f(x) for x.foo[0].foo in mylist]
824
+ self.visit(target)
825
+ visit_target(node.target)
826
+ self.visit(node.ifs)
827
+
828
+ def visit_ListComp(self, node):
829
+ # Visit a list comprehension node.
830
+ # This is basically the same as the generic visit, except that we
831
+ # visit the comprehension node(s) before the elt node.
832
+ # (generic_visit() would visit the elt first, because that comes first
833
+ # in ListComp._fields).
834
+ # For Python2, we intentionally don't enter a new scope here, because
835
+ # a list comprehensive _does_ leak variables out of its scope (unlike
836
+ # generator expressions).
837
+ # For Python3, we do need to enter a new scope here.
838
+ with self._NewScopeCtx(include_class_scopes=True):
839
+ self.visit(node.generators)
840
+ self.visit(node.elt)
841
+
842
+ def visit_DictComp(self, node):
843
+ # Visit a dict comprehension node.
844
+ # This is similar to the generic visit, except:
845
+ # - We visit the comprehension node(s) before the elt node.
846
+ # - We create a new scope for the variables.
847
+ # We do enter a new scope. A dict comprehension
848
+ # does _not_ leak variables out of its scope (unlike py2 list
849
+ # comprehensions).
850
+ with self._NewScopeCtx(include_class_scopes=True):
851
+ self.visit(node.generators)
852
+ self.visit(node.key)
853
+ self.visit(node.value)
854
+
855
+ def visit_SetComp(self, node):
856
+ # Visit a set comprehension node.
857
+ # We do enter a new scope. A set comprehension
858
+ # does _not_ leak variables out of its scope (unlike py2 list
859
+ # comprehensions).
860
+ with self._NewScopeCtx(include_class_scopes=True):
861
+ self.visit(node.generators)
862
+ self.visit(node.elt)
863
+
864
+ def visit_GeneratorExp(self, node):
865
+ # Visit a generator expression node.
866
+ # We do enter a new scope. A generator
867
+ # expression does _not_ leak variables out of its scope (unlike py2
868
+ # list comprehensions).
869
+ with self._NewScopeCtx(include_class_scopes=True):
870
+ self.visit(node.generators)
871
+ self.visit(node.elt)
872
+
873
+ def visit_ImportFrom(self, node):
874
+ modulename = "." * node.level + (node.module or "")
875
+ logger.debug("visit_ImportFrom(%r, ...)", modulename)
876
+ for alias_node in node.names:
877
+ self.visit_alias(alias_node, modulename)
878
+
879
+ def visit_alias(self, node, modulename=None):
880
+ # Visit an import alias node.
881
+ # TODO: Currently we treat 'import foo' the same as if the user did
882
+ # 'foo = 123', i.e. we treat it as a black box (non-module). This is
883
+ # to avoid actually importing it yet. But this means we won't know
884
+ # whether foo.bar is available so we won't auto-import it. Maybe we
885
+ # should give up on not importing it and just import it in a scratch
886
+ # namespace, so we can check.
887
+ self._visit_StoreImport(node, modulename)
888
+ self.generic_visit(node)
889
+
890
+ if sys.version_info >= (3,10):
891
+ def visit_match_case(self, node:ast.match_case):
892
+ logger.debug("visit_match_case(%r)", node)
893
+ return self.generic_visit(node)
894
+
895
+ def visit_Match(self, node:ast.Match):
896
+ logger.debug("visit_Match(%r)", node)
897
+ return self.generic_visit(node)
898
+
899
+ def visit_MatchMapping(self, node:ast.MatchMapping):
900
+ logger.debug("visit_MatchMapping(%r)", node)
901
+ return self.generic_visit(node)
902
+
903
+ def visit_MatchAs(self, node:MatchAs):
904
+ logger.debug("visit_MatchAs(%r)", node)
905
+ if node.name is None:
906
+ return
907
+ isinstance(node.name, str), node.name
908
+ return self._visit_Store(node.name)
909
+
910
+ def visit_Call(self, node:ast.Call):
911
+ logger.debug("visit_Call(%r)", node)
912
+ return self.generic_visit(node)
913
+
914
+ def visit_Pass(self, node:ast.Pass):
915
+ logger.debug("visit_Pass(%r)", node)
916
+ return self.generic_visit(node)
917
+
918
+ def visit_Constant(self, node:ast.Constant):
919
+ logger.debug("visit_Constant(%r)", node)
920
+ return self.generic_visit(node)
921
+
922
+ def visit_Module(self, node:ast.Module):
923
+ logger.debug("visit_Module(%r)", node)
924
+ return self.generic_visit(node)
925
+
926
+ def visit_Expr(self, node:ast.Expr):
927
+ logger.debug("visit_Expr(%r)", node)
928
+ return self.generic_visit(node)
929
+
930
+
931
+ def visit_Name(self, node):
932
+ logger.debug("visit_Name(%r)", node.id)
933
+ self._visit_fullname(node.id, node.ctx)
934
+
935
+ def visit_arg(self, node):
936
+ assert node._fields == ('arg', 'annotation', 'type_comment'), node._fields
937
+ if node.annotation:
938
+ self.visit(node.annotation)
939
+ # Treat it like a Name node would from Python 2
940
+ self._visit_fullname(node.arg, ast.Param())
941
+ self._visit_typecomment(node.type_comment)
942
+
943
+ def visit_Attribute(self, node):
944
+ name_revparts = []
945
+ n = node
946
+ while isinstance(n, ast.Attribute):
947
+ name_revparts.append(n.attr)
948
+ n = n.value
949
+ if not isinstance(n, ast.Name):
950
+ # Attribute of a non-symbol, e.g. (a+b).c
951
+ # We do nothing about "c", but we do recurse on (a+b) since those
952
+ # may have symbols we care about.
953
+ self.generic_visit(node)
954
+ return
955
+ name_revparts.append(n.id)
956
+ name_parts = name_revparts[::-1]
957
+ fullname = ".".join(name_parts)
958
+ logger.debug("visit_Attribute(%r): fullname=%r, ctx=%r", node.attr, fullname, node.ctx)
959
+ self._visit_fullname(fullname, node.ctx)
960
+
961
+ def _visit_fullname(self, fullname, ctx):
962
+ if isinstance(ctx, (ast.Store, ast.Param)):
963
+ self._visit_Store(fullname)
964
+ elif isinstance(ctx, ast.Load):
965
+ self._visit_Load(fullname)
966
+
967
+ def _visit_StoreImport(self, node, modulename):
968
+ name = node.asname or node.name
969
+ logger.debug("_visit_StoreImport(asname=%r, name=%r)", node.asname, node.name)
970
+ is_star = node.name == "*"
971
+ if is_star:
972
+ logger.debug("Got star import: line %s: 'from %s import *'",
973
+ self._lineno, modulename)
974
+ if not node.asname and not is_star:
975
+ # Handle leading prefixes so we don't think they're unused
976
+ for prefix in DottedIdentifier(node.name).prefixes[:-1]:
977
+ self._visit_Store(str(prefix), None)
978
+ if self.unused_imports is None or is_star or modulename == "__future__":
979
+ value = None
980
+ else:
981
+ imp = Import.from_split((modulename, node.name, name))
982
+ logger.debug("_visit_StoreImport(): imp = %r", imp)
983
+ # Keep track of whether we've used this import.
984
+ value = _UseChecker(name, imp, self._lineno)
985
+ self._visit_Store(name, value)
986
+
987
+ def _visit_Store(self, fullname: str, value: Optional[_UseChecker] = None):
988
+ """
989
+ Visit a Store action, check for unused import
990
+ and add current value to the last scope.
991
+ """
992
+ assert isinstance(value, (_UseChecker, type(None)))
993
+ logger.debug("_visit_Store(%r)", fullname)
994
+ if fullname is None:
995
+ return
996
+ scope = self.scopestack[-1]
997
+ if isinstance(fullname, ast.arg):
998
+ fullname = fullname.arg
999
+ if self.unused_imports is not None:
1000
+ if fullname != '*':
1001
+ # If we're storing "foo.bar.baz = 123", then "foo" and
1002
+ # "foo.bar" have now been used and the import should not be
1003
+ # removed.
1004
+ for ancestor in DottedIdentifier(fullname).prefixes[:-1]:
1005
+ if symbol_needs_import(ancestor, self.scopestack):
1006
+ m = (self._lineno, DottedIdentifier(fullname, scope_info=self._get_scope_info()))
1007
+ if m not in self.missing_imports:
1008
+ self.missing_imports.append(m)
1009
+ # If we're redefining something, and it has not been used, then
1010
+ # record it as unused.
1011
+ oldvalue = scope.get(fullname)
1012
+ if isinstance(oldvalue, _UseChecker) and not oldvalue.used:
1013
+ logger.debug("Adding to unused %s", oldvalue)
1014
+ self.unused_imports.append((oldvalue.lineno, oldvalue.source))
1015
+ scope[fullname] = value
1016
+
1017
+ def _remove_from_missing_imports(self, fullname):
1018
+ for missing_import in self.missing_imports:
1019
+ # If it was defined inside a class method, then it wouldn't have been added to
1020
+ # the missing imports anyways (except in that case of annotations)
1021
+ # See the following tests:
1022
+ # - tests.test_autoimp.test_method_reference_current_class
1023
+ # - tests.test_autoimp.test_find_missing_imports_class_name_1
1024
+ # - tests.test_autoimp.test_scan_for_import_issues_class_defined_after_use
1025
+ scopestack = missing_import[1].scope_info['scopestack']
1026
+ in_class_scope = isinstance(scopestack[-1], _ClassScope)
1027
+ inside_class = missing_import[1].scope_info.get('_in_class_def')
1028
+ if missing_import[1].startswith(fullname):
1029
+ if in_class_scope or not inside_class:
1030
+ self.missing_imports.remove(missing_import)
1031
+
1032
+ def _get_scope_info(self):
1033
+ return {
1034
+ "scopestack": self.scopestack,
1035
+ "_in_class_def": self._in_class_def,
1036
+ }
1037
+
1038
+ def visit_Delete(self, node):
1039
+ scope = self.scopestack[-1]
1040
+ for target in node.targets:
1041
+ if isinstance(target, ast.Name):
1042
+ # 'del foo'
1043
+ if target.id not in scope:
1044
+ # 'del x' without 'x' in current scope. Should we warn?
1045
+ continue
1046
+ del scope[target.id]
1047
+ elif isinstance(target, ast.Attribute):
1048
+ # 'del foo.bar.baz', 'del foo().bar', etc
1049
+ # We ignore the 'del ...bar' part and just visit the
1050
+ # left-hand-side of the delattr. We need to do this explicitly
1051
+ # instead of relying on a generic_visit on ``node`` itself.
1052
+ # Reason: We want visit_Attribute to process a getattr for
1053
+ # 'foo.bar'.
1054
+ self.visit(target.value)
1055
+ else:
1056
+ # 'del foo.bar[123]' (ast.Subscript), etc.
1057
+ # We can generically-visit the entire target node here.
1058
+ self.visit(target)
1059
+ # Don't call generic_visit(node) here. Reason: We already visit the
1060
+ # parts above, if relevant.
1061
+
1062
+ def _visit_Load_defered_global(self, fullname:str):
1063
+ """
1064
+ Some things will be resolved in global scope later.
1065
+ """
1066
+ assert isinstance(fullname, str), fullname
1067
+ logger.debug("_visit_Load_defered_global(%r)", fullname)
1068
+ if symbol_needs_import(fullname, self.scopestack):
1069
+ data = (fullname, self.scopestack, self._lineno)
1070
+ self._deferred_load_checks.append(data)
1071
+
1072
+
1073
+ def _visit_Load_defered(self, fullname):
1074
+ logger.debug("_visit_Load_defered(%r)", fullname)
1075
+ if symbol_needs_import(fullname, self.scopestack):
1076
+ data = (fullname, self.scopestack.clone_top(), self._lineno)
1077
+ self._deferred_load_checks.append(data)
1078
+
1079
+ def _visit_Load_immediate(self, fullname):
1080
+ logger.debug("_visit_Load_immediate(%r)", fullname)
1081
+ self._check_load(fullname, self.scopestack, self._lineno)
1082
+
1083
+
1084
+
1085
+ def _visit_Load(self, fullname):
1086
+ logger.debug("_visit_Load(%r)", fullname)
1087
+ if self._in_FunctionDef:
1088
+ self._visit_Load_defered(fullname)
1089
+ # We're in a FunctionDef. We need to defer checking whether this
1090
+ # references undefined names. The reason is that globals (or
1091
+ # stores in a parent function scope) may be stored later.
1092
+ # For example, bar() is defined later after the body of foo(), but
1093
+ # still available to foo() when it is called:
1094
+ # def foo():
1095
+ # return bar()
1096
+ # def bar():
1097
+ # return 42
1098
+ # foo()
1099
+ # To support this, we clone the top of the scope stack and alias
1100
+ # the other scopes in the stack. Later stores in the same scope
1101
+ # shouldn't count, e.g. x should be considered undefined in the
1102
+ # following example:
1103
+ # def foo():
1104
+ # print x
1105
+ # x = 1
1106
+ # On the other hand, we intentionally alias the other scopes
1107
+ # rather than cloning them, because the point is to allow them to
1108
+ # be modified until we do the check at the end.
1109
+ self._visit_Load_defered(fullname)
1110
+
1111
+ else:
1112
+ # We're not in a FunctionDef. Deferring would give us the same
1113
+ # result; we do the check now to avoid the overhead of cloning the
1114
+ # stack.
1115
+ self._visit_Load_immediate(fullname)
1116
+
1117
+ def _check_load(self, fullname, scopestack, lineno):
1118
+ """
1119
+ Check if the symbol needs import. (As a side effect, if the object
1120
+ is a _UseChecker, this will mark it as used.
1121
+
1122
+ TODO: It would be
1123
+ better to refactor symbol_needs_import so that it just returns the
1124
+ object it found, and we mark it as used here.)
1125
+ """
1126
+ fullname = DottedIdentifier(fullname, scope_info=self._get_scope_info())
1127
+ if symbol_needs_import(fullname, scopestack) and not scopestack.has_star_import():
1128
+ if (lineno, fullname) not in self.missing_imports:
1129
+ self.missing_imports.append((lineno, fullname))
1130
+
1131
+ def _finish_deferred_load_checks(self):
1132
+ for fullname, scopestack, lineno in self._deferred_load_checks:
1133
+ self._check_load(fullname, scopestack, lineno)
1134
+ self._deferred_load_checks = []
1135
+
1136
+ def _scan_unused_imports(self):
1137
+ # If requested, then check which of our imports were unused.
1138
+ # For now we only scan the top level. If we wanted to support
1139
+ # non-global unused-import checking, then we should check this
1140
+ # whenever popping a scopestack.
1141
+ unused_imports = self.unused_imports
1142
+ if unused_imports is None:
1143
+ return
1144
+ scope = self.scopestack[-1]
1145
+ for name, value in scope.items():
1146
+ if not isinstance(value, _UseChecker):
1147
+ continue
1148
+ if value.used:
1149
+ continue
1150
+ logger.debug("Also Adding to usunsed import: %s ", value)
1151
+ unused_imports.append(( value.lineno, value.source ))
1152
+ unused_imports.sort()
1153
+
1154
+
1155
+ def scan_for_import_issues(
1156
+ codeblock: PythonBlock,
1157
+ find_unused_imports: bool = True,
1158
+ parse_docstrings: bool = False,
1159
+ ):
1160
+ """
1161
+ Find missing and unused imports, by lineno.
1162
+
1163
+ >>> arg = "import numpy, aa.bb as cc\\nnumpy.arange(x)\\narange(x)"
1164
+ >>> missing, unused = scan_for_import_issues(arg)
1165
+ >>> missing
1166
+ [(2, DottedIdentifier('x')), (3, DottedIdentifier('arange')), (3, DottedIdentifier('x'))]
1167
+ >>> unused
1168
+ [(1, Import('from aa import bb as cc'))]
1169
+
1170
+ :type codeblock:
1171
+ ``PythonBlock``
1172
+ :type namespaces:
1173
+ ``dict`` or ``list`` of ``dict``
1174
+ :param parse_docstrings:
1175
+ Whether to parse docstrings.
1176
+ Compare the following examples. When parse_docstrings=True, 'bar' is
1177
+ not considered unused because there is a string that references it in
1178
+ braces::
1179
+
1180
+ >>> scan_for_import_issues("import foo as bar, baz\\n'{bar}'\\n")
1181
+ ([], [(1, Import('import baz')), (1, Import('import foo as bar'))])
1182
+ >>> scan_for_import_issues("import foo as bar, baz\\n'{bar}'\\n", parse_docstrings=True)
1183
+ ([], [(1, Import('import baz'))])
1184
+
1185
+ """
1186
+ logger.debug("global scan_for_import_issues()")
1187
+ if not isinstance(codeblock, PythonBlock):
1188
+ codeblock = PythonBlock(codeblock)
1189
+ namespaces = ScopeStack([{}])
1190
+ finder = _MissingImportFinder(namespaces,
1191
+ find_unused_imports=find_unused_imports,
1192
+ parse_docstrings=parse_docstrings)
1193
+ return finder.scan_for_import_issues(codeblock)
1194
+
1195
+
1196
+ def _find_missing_imports_in_ast(node, namespaces):
1197
+ """
1198
+ Find missing imports in an AST node.
1199
+ Helper function to `find_missing_imports`.
1200
+
1201
+ >>> node = ast.parse("import numpy; numpy.arange(x) + arange(x)")
1202
+ >>> _find_missing_imports_in_ast(node, [{}])
1203
+ [DottedIdentifier('arange'), DottedIdentifier('x')]
1204
+
1205
+ :type node:
1206
+ ``ast.AST``
1207
+ :type namespaces:
1208
+ ``dict`` or ``list`` of ``dict``
1209
+ :rtype:
1210
+ ``list`` of ``DottedIdentifier``
1211
+ """
1212
+ if not isinstance(node, ast.AST):
1213
+ raise TypeError
1214
+ # Traverse the abstract syntax tree.
1215
+ if logger.debug_enabled:
1216
+ logger.debug("ast=%s", ast.dump(node))
1217
+ return _MissingImportFinder(
1218
+ namespaces,
1219
+ find_unused_imports=False,
1220
+ parse_docstrings=False).find_missing_imports(node)
1221
+
1222
+ # TODO: maybe we should replace _find_missing_imports_in_ast with
1223
+ # _find_missing_imports_in_code(compile(node)). The method of parsing opcodes
1224
+ # is simpler, because Python takes care of the scoping issue for us and we
1225
+ # don't have to worry about locals. It does, however, depend on CPython
1226
+ # implementation details, whereas the AST is well-defined by the language.
1227
+
1228
+
1229
+ def _find_missing_imports_in_code(co, namespaces):
1230
+ """
1231
+ Find missing imports in a code object.
1232
+ Helper function to `find_missing_imports`.
1233
+
1234
+ >>> f = lambda: foo.bar(x) + baz(y)
1235
+ >>> [str(m) for m in _find_missing_imports_in_code(f.__code__, [{}])]
1236
+ ['baz', 'foo.bar', 'x', 'y']
1237
+
1238
+ >>> f = lambda x: (lambda: x+y)
1239
+ >>> _find_missing_imports_in_code(f.__code__, [{}])
1240
+ [DottedIdentifier('y')]
1241
+
1242
+ :type co:
1243
+ ``types.CodeType``
1244
+ :type namespaces:
1245
+ ``dict`` or ``list`` of ``dict``
1246
+ :rtype:
1247
+ ``list`` of ``str``
1248
+ """
1249
+ loads_without_stores = set()
1250
+ _find_loads_without_stores_in_code(co, loads_without_stores)
1251
+ missing_imports = [
1252
+ DottedIdentifier(fullname) for fullname in sorted(loads_without_stores)
1253
+ if symbol_needs_import(fullname, namespaces)
1254
+ ]
1255
+ return missing_imports
1256
+
1257
+
1258
+ def _find_loads_without_stores_in_code(co, loads_without_stores):
1259
+ """
1260
+ Find global LOADs without corresponding STOREs, by disassembling code.
1261
+ Recursive helper for `_find_missing_imports_in_code`.
1262
+
1263
+ :type co:
1264
+ ``types.CodeType``
1265
+ :param co:
1266
+ Code object, e.g. ``function.__code__``
1267
+ :type loads_without_stores:
1268
+ ``set``
1269
+ :param loads_without_stores:
1270
+ Mutable set to which we add loads without stores.
1271
+ :return:
1272
+ ``None``
1273
+ """
1274
+ if not isinstance(co, types.CodeType):
1275
+ raise TypeError(
1276
+ "_find_loads_without_stores_in_code(): expected a CodeType; got a %s"
1277
+ % (type(co).__name__,))
1278
+ # Initialize local constants for fast access.
1279
+ from opcode import EXTENDED_ARG, opmap
1280
+
1281
+ LOAD_ATTR = opmap['LOAD_ATTR']
1282
+ # LOAD_METHOD is _supposed_ to be removed in 3.12 but still present in opmap
1283
+ # if sys.version_info < (3, 12):
1284
+ LOAD_METHOD = opmap['LOAD_METHOD']
1285
+ # endif
1286
+ LOAD_GLOBAL = opmap['LOAD_GLOBAL']
1287
+ LOAD_NAME = opmap['LOAD_NAME']
1288
+ STORE_ATTR = opmap['STORE_ATTR']
1289
+ STORE_GLOBAL = opmap['STORE_GLOBAL']
1290
+ STORE_NAME = opmap['STORE_NAME']
1291
+
1292
+ if sys.version_info > (3, 11):
1293
+ CACHE = opmap["CACHE"]
1294
+ else:
1295
+ CACHE = object()
1296
+ # Keep track of the partial name so far that started with a LOAD_GLOBAL.
1297
+ # If ``pending`` is not None, then it is a list representing the name
1298
+ # components we've seen so far.
1299
+ pending = None
1300
+ # Disassemble the code. Look for LOADs and STOREs. This code is based on
1301
+ # ``dis.disassemble``.
1302
+ #
1303
+ # Scenarios:
1304
+ #
1305
+ # * Function-level load a toplevel global
1306
+ # def f():
1307
+ # aa
1308
+ # => LOAD_GLOBAL; other (not LOAD_ATTR or STORE_ATTR)
1309
+ # * Function-level load an attribute of global
1310
+ # def f():
1311
+ # aa.bb.cc
1312
+ # => LOAD_GLOBAL; LOAD_ATTR; LOAD_ATTR; other
1313
+ # * Function-level store a toplevel global
1314
+ # def f():
1315
+ # global aa
1316
+ # aa = 42
1317
+ # => STORE_GLOBAL
1318
+ # * Function-level store an attribute of global
1319
+ # def f():
1320
+ # aa.bb.cc = 42
1321
+ # => LOAD_GLOBAL, LOAD_ATTR, STORE_ATTR
1322
+ # * Function-level load a local
1323
+ # def f():
1324
+ # aa = 42
1325
+ # return aa
1326
+ # => LOAD_FAST or LOAD_NAME
1327
+ # * Function-level store a local
1328
+ # def f():
1329
+ # aa = 42
1330
+ # => STORE_FAST or STORE_NAME
1331
+ # * Function-level load an attribute of a local
1332
+ # def f():
1333
+ # aa = 42
1334
+ # return aa.bb.cc
1335
+ # => LOAD_FAST; LOAD_ATTR; LOAD_ATTR
1336
+ # * Function-level store an attribute of a local
1337
+ # def f():
1338
+ # aa == 42
1339
+ # aa.bb.cc = 99
1340
+ # => LOAD_FAST; LOAD_ATTR; STORE_ATTR
1341
+ # * Function-level load an attribute of an expression other than a name
1342
+ # def f():
1343
+ # foo().bb.cc
1344
+ # => [CALL_FUNCTION, etc]; LOAD_ATTR; LOAD_ATTR
1345
+ # * Function-level store an attribute of an expression other than a name
1346
+ # def f():
1347
+ # foo().bb.cc = 42
1348
+ # => [CALL_FUNCTION, etc]; LOAD_ATTR; STORE_ATTR
1349
+ # * Function-level import
1350
+ # def f():
1351
+ # import aa.bb.cc
1352
+ # => IMPORT_NAME "aa.bb.cc", STORE_FAST "aa"
1353
+ # * Module-level load of a top-level global
1354
+ # aa
1355
+ # => LOAD_NAME
1356
+ # * Module-level store of a top-level global
1357
+ # aa = 42
1358
+ # => STORE_NAME
1359
+ # * Module-level load of an attribute of a global
1360
+ # aa.bb.cc
1361
+ # => LOAD_NAME, LOAD_ATTR, LOAD_ATTR
1362
+ # * Module-level store of an attribute of a global
1363
+ # aa.bb.cc = 42
1364
+ # => LOAD_NAME, LOAD_ATTR, STORE_ATTR
1365
+ # * Module-level import
1366
+ # import aa.bb.cc
1367
+ # IMPORT_NAME "aa.bb.cc", STORE_NAME "aa"
1368
+ # * Closure
1369
+ # def f():
1370
+ # aa = 42
1371
+ # return lambda: aa
1372
+ # f: STORE_DEREF, LOAD_CLOSURE, MAKE_CLOSURE
1373
+ # g = f(): LOAD_DEREF
1374
+ bytecode = co.co_code
1375
+ n = len(bytecode)
1376
+ i = 0
1377
+ extended_arg = 0
1378
+ stores = set()
1379
+ loads_after_label = set()
1380
+ loads_before_label_without_stores = set()
1381
+ # Find the earliest target of a backward jump.
1382
+ earliest_backjump_label = _find_earliest_backjump_label(bytecode)
1383
+ # Loop through bytecode.
1384
+ while i < n:
1385
+ op = bytecode[i]
1386
+ i += 1
1387
+ if op == CACHE:
1388
+ continue
1389
+ if take_arg(op):
1390
+ oparg = bytecode[i] | extended_arg
1391
+ extended_arg = 0
1392
+ if op == EXTENDED_ARG:
1393
+ extended_arg = (oparg << 8)
1394
+ continue
1395
+ i += 1
1396
+
1397
+ if pending is not None:
1398
+ if op == STORE_ATTR:
1399
+ # {LOAD_GLOBAL|LOAD_NAME} {LOAD_ATTR}* {STORE_ATTR}
1400
+ pending.append(co.co_names[oparg])
1401
+ fullname = ".".join(pending)
1402
+ pending = None
1403
+ stores.add(fullname)
1404
+ continue
1405
+ if op in [LOAD_ATTR, LOAD_METHOD]:
1406
+ if sys.version_info >= (3,12):
1407
+ # from the docs:
1408
+ #
1409
+ # If the low bit of namei is not set, this replaces
1410
+ # STACK[-1] with getattr(STACK[-1], co_names[namei>>1]).
1411
+ #
1412
+ # If the low bit of namei is set, this will attempt to load
1413
+ # a method named co_names[namei>>1] from the STACK[-1]
1414
+ # object. STACK[-1] is popped. This bytecode distinguishes
1415
+ # two cases: if STACK[-1] has a method with the correct
1416
+ # name, the bytecode pushes the unbound method and
1417
+ # STACK[-1]. STACK[-1] will be used as the first argument
1418
+ # (self) by CALL when calling the unbound method. Otherwise,
1419
+ # NULL and the object returned by the attribute lookup are
1420
+ # pushed.
1421
+ #
1422
+ # Changed in version 3.12: If the low bit of namei is set,
1423
+ # then a NULL or self is pushed to the stack before the
1424
+ # attribute or unbound method respectively.
1425
+ #
1426
+ # Implication for Pyflyby
1427
+ #
1428
+ # In our case I think it means we are always looking at
1429
+ # oparg>>1 as the name of the names we need to load,
1430
+ # Though we don't keep track of the stack, and so we may get
1431
+ # wrong results ?
1432
+ #
1433
+ # In any case this seem to match what load_method was doing
1434
+ # before.
1435
+ pending.append(co.co_names[oparg>>1])
1436
+ else:
1437
+ # {LOAD_GLOBAL|LOAD_NAME} {LOAD_ATTR}* so far;
1438
+ # possibly more LOAD_ATTR/STORE_ATTR will follow
1439
+ pending.append(co.co_names[oparg])
1440
+ continue
1441
+ # {LOAD_GLOBAL|LOAD_NAME} {LOAD_ATTR}* (and no more
1442
+ # LOAD_ATTR/STORE_ATTR)
1443
+ fullname = ".".join(pending)
1444
+ pending = None
1445
+ if i >= earliest_backjump_label:
1446
+ loads_after_label.add(fullname)
1447
+ elif fullname not in stores:
1448
+ loads_before_label_without_stores.add(fullname)
1449
+ # Fall through.
1450
+
1451
+ if op is LOAD_GLOBAL:
1452
+ # Starting with 3.11, the low bit is used to tell whether to
1453
+ # push an extra null on the stack, so we need to >> 1
1454
+ # >> 0 does nothing
1455
+ pending = [co.co_names[oparg >> LOAD_SHIFT]]
1456
+ continue
1457
+ if op is LOAD_NAME:
1458
+ pending = [co.co_names[oparg]]
1459
+ continue
1460
+
1461
+ if op in [STORE_GLOBAL, STORE_NAME]:
1462
+ stores.add(co.co_names[oparg])
1463
+ continue
1464
+
1465
+ # We don't need to worry about: LOAD_FAST, STORE_FAST, LOAD_CLOSURE,
1466
+ # LOAD_DEREF, STORE_DEREF. LOAD_FAST and STORE_FAST refer to local
1467
+ # variables; LOAD_CLOSURE, LOAD_DEREF, and STORE_DEREF relate to
1468
+ # closure variables. In both cases we know these are not missing
1469
+ # imports. It's convenient that these are separate opcodes, because
1470
+ # then we don't need to deal with them manually.
1471
+
1472
+ # Record which variables we saw that were loaded in this module without a
1473
+ # corresponding store. We handle two cases.
1474
+ #
1475
+ # 1. Load-before-store; no loops (i.e. no backward jumps).
1476
+ # Example A::
1477
+ # foo.bar()
1478
+ # import foo
1479
+ # In the above example A, "foo" was used before it was imported. We
1480
+ # consider it a candidate for auto-import.
1481
+ # Example B:
1482
+ # if condition1(): # L1
1483
+ # import foo1 # L2
1484
+ # foo1.bar() + foo2.bar() # L3
1485
+ # import foo2 # L4
1486
+ # In the above example B, "foo2" was used before it was imported; the
1487
+ # fact that there is a jump target at L3 is irrelevant because it is
1488
+ # the target of a forward jump; there is no way that foo2 can be
1489
+ # imported (L4) before foo2 is used (L3).
1490
+ # On the other hand, we don't know whether condition1 will be true,
1491
+ # so we assume L2 will be executed and therefore don't consider the
1492
+ # use of "foo1" at L3 to be problematic.
1493
+ #
1494
+ # 2. Load-before-store; with loops (backward jumps). Example:
1495
+ # for i in range(10):
1496
+ # if i > 0:
1497
+ # print x
1498
+ # else:
1499
+ # x = "hello"
1500
+ # In the above example, "x" is actually always stored before load,
1501
+ # even though in a linear reading of the bytecode we would see the
1502
+ # store before any loads.
1503
+ #
1504
+ # It would be impossible to perfectly follow conditional code, because
1505
+ # code could be arbitrarily complicated and would require a flow control
1506
+ # analysis that solves the halting problem. We do the best we can and
1507
+ # handle case 1 as a common case.
1508
+ #
1509
+ # Case 1: If we haven't seen a label, then we know that any load
1510
+ # before a preceding store is definitely too early.
1511
+ # Case 2: If we have seen a label, then we consider any preceding
1512
+ # or subsequent store to potentially match the load.
1513
+ loads_without_stores.update( loads_before_label_without_stores ) # case 1
1514
+ loads_without_stores.update( loads_after_label - stores ) # case 2
1515
+
1516
+ # The ``pending`` variable should have been reset at this point, because a
1517
+ # function should always end with a RETURN_VALUE opcode and therefore not
1518
+ # end in a LOAD_ATTR.
1519
+ assert pending is None
1520
+
1521
+ # Recurse on inner function definitions, lambdas, generators, etc.
1522
+ for arg in co.co_consts:
1523
+ if isinstance(arg, types.CodeType):
1524
+ _find_loads_without_stores_in_code(arg, loads_without_stores)
1525
+
1526
+ if sys.version_info >= (3,12):
1527
+ from dis import hasarg
1528
+ def take_arg(op):
1529
+ return op in hasarg
1530
+ else:
1531
+ def take_arg(op):
1532
+ from opcode import HAVE_ARGUMENT
1533
+ return op >= HAVE_ARGUMENT
1534
+
1535
+ def _find_earliest_backjump_label(bytecode):
1536
+ """
1537
+ Find the earliest target of a backward jump.
1538
+
1539
+ These normally represent loops.
1540
+
1541
+ For example, given the source code::
1542
+
1543
+ >>> def f():
1544
+ ... if foo1():
1545
+ ... foo2()
1546
+ ... else:
1547
+ ... foo3()
1548
+ ... foo4()
1549
+ ... while foo5(): # L7
1550
+ ... foo6()
1551
+
1552
+ The earliest target of a backward jump would be the 'while' loop at L7, at
1553
+ bytecode offset 38::
1554
+
1555
+ >>> _find_earliest_backjump_label(f.__code__.co_code) # doctest: +SKIP
1556
+ 38
1557
+
1558
+ Note that in this example there are earlier targets of jumps at bytecode
1559
+ offsets 20 and 28, but those are targets of _forward_ jumps, and the
1560
+ clients of this function care about the earliest _backward_ jump.
1561
+
1562
+ If there are no backward jumps, return an offset that points after the end
1563
+ of the bytecode.
1564
+
1565
+ :type bytecode:
1566
+ ``bytes``
1567
+ :param bytecode:
1568
+ Compiled bytecode, e.g. ``function.__code__.co_code``.
1569
+ :rtype:
1570
+ ``int``
1571
+ :return:
1572
+ The earliest target of a backward jump, as an offset into the bytecode.
1573
+ """
1574
+ # Code based on dis.findlabels().
1575
+ from opcode import hasjrel, hasjabs
1576
+ if not isinstance(bytecode, bytes):
1577
+ raise TypeError
1578
+ n = len(bytecode)
1579
+ earliest_backjump_label = n
1580
+ i = 0
1581
+ while i < n:
1582
+ op = bytecode[i]
1583
+ i += 1
1584
+ if not take_arg(op):
1585
+ continue
1586
+ if i+1 >= len(bytecode):
1587
+ break
1588
+ oparg = bytecode[i] + bytecode[i+1]*256
1589
+ i += 2
1590
+ label = None
1591
+ if op in hasjrel:
1592
+ label = i+oparg
1593
+ elif op in hasjabs:
1594
+ label = oparg
1595
+ else:
1596
+ # No label
1597
+ continue
1598
+ if label >= i:
1599
+ # Label is a forward jump
1600
+ continue
1601
+ # Found a backjump label. Keep track of the earliest one.
1602
+ earliest_backjump_label = min(earliest_backjump_label, label)
1603
+ return earliest_backjump_label
1604
+
1605
+
1606
+ def find_missing_imports(arg, namespaces):
1607
+ """
1608
+ Find symbols in the given code that require import.
1609
+
1610
+ We consider a symbol to require import if we see an access ("Load" in AST
1611
+ terminology) without an import or assignment ("Store" in AST terminology)
1612
+ in the same lexical scope.
1613
+
1614
+ For example, if we use an empty list of namespaces, then "os.path.join" is
1615
+ a symbol that requires import::
1616
+
1617
+ >>> [str(m) for m in find_missing_imports("os.path.join", namespaces=[{}])]
1618
+ ['os.path.join']
1619
+
1620
+ But if the global namespace already has the "os" module imported, then we
1621
+ know that ``os`` has a "path" attribute, which has a "join" attribute, so
1622
+ nothing needs import::
1623
+
1624
+ >>> import os
1625
+ >>> find_missing_imports("os.path.join", namespaces=[{"os":os}])
1626
+ []
1627
+
1628
+ Builtins are always included::
1629
+
1630
+ >>> [str(m) for m in find_missing_imports("os, sys, eval", [{"os": os}])]
1631
+ ['sys']
1632
+
1633
+ All symbols that are not defined are included::
1634
+
1635
+ >>> [str(m) for m in find_missing_imports("numpy.arange(x) + arange(y)", [{"y": 3}])]
1636
+ ['arange', 'numpy.arange', 'x']
1637
+
1638
+ If something is imported/assigned/etc within the scope, then we assume it
1639
+ doesn't require importing::
1640
+
1641
+ >>> [str(m) for m in find_missing_imports("import numpy; numpy.arange(x) + arange(x)", [{}])]
1642
+ ['arange', 'x']
1643
+
1644
+ >>> [str(m) for m in find_missing_imports("from numpy import pi; numpy.pi + pi + x", [{}])]
1645
+ ['numpy.pi', 'x']
1646
+
1647
+ >>> [str(m) for m in find_missing_imports("for x in range(3): print(numpy.arange(x))", [{}])]
1648
+ ['numpy.arange']
1649
+
1650
+ >>> [str(m) for m in find_missing_imports("foo1 = func(); foo1.bar + foo2.bar", [{}])]
1651
+ ['foo2.bar', 'func']
1652
+
1653
+ >>> [str(m) for m in find_missing_imports("a.b.y = 1; a.b.x, a.b.y, a.b.z", [{}])]
1654
+ ['a.b.x', 'a.b.z']
1655
+
1656
+ find_missing_imports() parses the AST, so it understands scoping. In the
1657
+ following example, ``x`` is never undefined::
1658
+
1659
+ >>> find_missing_imports("(lambda x: x*x)(7)", [{}])
1660
+ []
1661
+
1662
+ but this example, ``x`` is undefined at global scope::
1663
+
1664
+ >>> [str(m) for m in find_missing_imports("(lambda x: x*x)(7) + x", [{}])]
1665
+ ['x']
1666
+
1667
+ >>> # Python 3
1668
+ >>> [str(m) for m in find_missing_imports("[x+y+z for x,y in [(1,2)]], y", [{}])]
1669
+ ['y', 'z']
1670
+
1671
+ >>> [str(m) for m in find_missing_imports("(x+y+z for x,y in [(1,2)]), y", [{}])]
1672
+ ['y', 'z']
1673
+
1674
+ Only fully-qualified names starting at top-level are included::
1675
+
1676
+ >>> [str(m) for m in find_missing_imports("( ( a . b ) . x ) . y + ( c + d ) . x . y", [{}])]
1677
+ ['a.b.x.y', 'c', 'd']
1678
+
1679
+ :type arg:
1680
+ ``str``, ``ast.AST``, `PythonBlock`, ``callable``, or ``types.CodeType``
1681
+ :param arg:
1682
+ Python code, either as source text, a parsed AST, or compiled code; can
1683
+ be as simple as a single qualified name, or as complex as an entire
1684
+ module text.
1685
+ :type namespaces:
1686
+ ``dict`` or ``list`` of ``dict``
1687
+ :param namespaces:
1688
+ Stack of namespaces of symbols that exist per scope.
1689
+ :rtype:
1690
+ ``list`` of ``DottedIdentifier``
1691
+ """
1692
+ namespaces = ScopeStack(namespaces)
1693
+ if isinstance(arg, (DottedIdentifier, str)):
1694
+ try:
1695
+ arg = DottedIdentifier(arg)
1696
+ except BadDottedIdentifierError:
1697
+ pass
1698
+ else:
1699
+ # The string is a single identifier. Check directly whether it
1700
+ # needs import. This is an optimization to not bother parsing an
1701
+ # AST.
1702
+ if symbol_needs_import(arg, namespaces):
1703
+ return [arg]
1704
+ else:
1705
+ return []
1706
+ # Parse the string into an AST.
1707
+ node = ast.parse(arg, type_comments=True) # may raise SyntaxError
1708
+ # Get missing imports from AST.
1709
+ return _find_missing_imports_in_ast(node, namespaces)
1710
+ elif isinstance(arg, PythonBlock):
1711
+ return _find_missing_imports_in_ast(arg.ast_node, namespaces)
1712
+ elif isinstance(arg, ast.AST):
1713
+ return _find_missing_imports_in_ast(arg, namespaces)
1714
+ elif isinstance(arg, types.CodeType):
1715
+ return _find_missing_imports_in_code(arg, namespaces)
1716
+ elif callable(arg):
1717
+ # Find the code object.
1718
+ try:
1719
+ co = arg.__code__
1720
+ except AttributeError:
1721
+ # User-defined callable
1722
+ try:
1723
+ co = arg.__call__.__code__
1724
+ except AttributeError:
1725
+ # Built-in function; no auto importing needed.
1726
+ return []
1727
+ # Get missing imports from code object.
1728
+ return _find_missing_imports_in_code(co, namespaces)
1729
+ else:
1730
+ raise TypeError(
1731
+ "find_missing_imports(): expected a string, AST node, or code object; got a %s"
1732
+ % (type(arg).__name__,))
1733
+
1734
+
1735
+ def get_known_import(fullname, db=None):
1736
+ """
1737
+ Get the deepest known import.
1738
+
1739
+ For example, suppose:
1740
+
1741
+ - The user accessed "foo.bar.baz",
1742
+ - We know imports for "foo", "foo.bar", and "foo.bar.quux".
1743
+
1744
+ Then we return "import foo.bar".
1745
+
1746
+ :type fullname:
1747
+ `DottedIdentifier`
1748
+ :param fullname:
1749
+ Fully-qualified name, such as "scipy.interpolate"
1750
+ """
1751
+ # Get the import database.
1752
+ db = ImportDB.interpret_arg(db, target_filename=".")
1753
+ fullname = DottedIdentifier(fullname)
1754
+ # Look for the "deepest" import we know about. Suppose the user
1755
+ # accessed "foo.bar.baz". If we have an auto-import for "foo.bar",
1756
+ # then import that. (Presumably, the auto-import for "foo", if it
1757
+ # exists, refers to the same foo.)
1758
+ for partial_name in fullname.prefixes[::-1]:
1759
+ try:
1760
+ result = db.by_fullname_or_import_as[str(partial_name)]
1761
+ logger.debug("get_known_import(%r): found %r", fullname, result)
1762
+ return result
1763
+ except KeyError:
1764
+ logger.debug("get_known_import(%r): no known import for %r", fullname, partial_name)
1765
+ pass
1766
+ logger.debug("get_known_import(%r): found nothing", fullname)
1767
+ return None
1768
+
1769
+
1770
+ _IMPORT_FAILED:Set[Any] = set()
1771
+ """
1772
+ Set of imports we've already attempted and failed.
1773
+ """
1774
+
1775
+
1776
+ def clear_failed_imports_cache():
1777
+ """
1778
+ Clear the cache of previously failed imports.
1779
+ """
1780
+ if _IMPORT_FAILED:
1781
+ logger.debug("Clearing all %d entries from cache of failed imports",
1782
+ len(_IMPORT_FAILED))
1783
+ _IMPORT_FAILED.clear()
1784
+
1785
+
1786
+ def _try_import(imp, namespace):
1787
+ """
1788
+ Try to execute an import. Import the result into the namespace
1789
+ ``namespace``.
1790
+
1791
+ Print to stdout what we're about to do.
1792
+
1793
+ Only import into ``namespace`` if we won't clobber an existing definition.
1794
+
1795
+ :type imp:
1796
+ ``Import`` or ``str``
1797
+ :param imp:
1798
+ The import to execute, e.g. "from numpy import arange"
1799
+ :type namespace:
1800
+ ``dict``
1801
+ :param namespace:
1802
+ Namespace to import into.
1803
+ :return:
1804
+ ``True`` on success, ``False`` on failure
1805
+ """
1806
+ # TODO: generalize "imp" to any python statement whose toplevel is a
1807
+ # single Store (most importantly import and assignment, but could also
1808
+ # include def & cdef). For things other than imports, we would want to
1809
+ # first run handle_auto_imports() on the code.
1810
+ imp = Import(imp)
1811
+ if imp in _IMPORT_FAILED:
1812
+ logger.debug("Not attempting previously failed %r", imp)
1813
+ return False
1814
+ impas = imp.import_as
1815
+ name0 = impas.split(".", 1)[0]
1816
+ stmt = str(imp)
1817
+ logger.info(stmt)
1818
+ # Do the import in a temporary namespace, then copy it to ``namespace``
1819
+ # manually. We do this instead of just importing directly into
1820
+ # ``namespace`` for the following reason: Suppose the user wants "foo.bar",
1821
+ # but "foo" already exists in the global namespace. In order to import
1822
+ # "foo.bar" we need to import its parent module "foo". We only want to do
1823
+ # the "foo.bar" import if what we import as "foo" is the same as the
1824
+ # preexisting "foo". OTOH, we _don't_ want to do the "foo.bar" import if
1825
+ # the user had for some reason done "import fool as foo". So we (1)
1826
+ # import into a scratch namespace, (2) check that the top-level matches,
1827
+ # then (3) copy into the user's namespace if it didn't already exist.
1828
+ scratch_namespace = {}
1829
+ try:
1830
+ exec(stmt, scratch_namespace)
1831
+ imported = scratch_namespace[name0]
1832
+ except Exception as e:
1833
+ logger.warning("Error attempting to %r: %s: %s", stmt, type(e).__name__, e,
1834
+ exc_info=True)
1835
+ _IMPORT_FAILED.add(imp)
1836
+ return False
1837
+ try:
1838
+ preexisting = namespace[name0]
1839
+ except KeyError:
1840
+ # The top-level symbol didn't previously exist in the user's global
1841
+ # namespace. Add it.
1842
+ namespace[name0] = imported
1843
+ else:
1844
+ # The top-level symbol already existed in the user's global namespace.
1845
+ # Check that it matched.
1846
+ if preexisting is not imported:
1847
+ logger.info(" => Failed: pre-existing %r (%r) differs from imported %r",
1848
+ name0, preexisting, name0)
1849
+ return False
1850
+ return True
1851
+
1852
+
1853
+ def auto_import_symbol(fullname, namespaces, db=None, autoimported=None, post_import_hook=None):
1854
+ """
1855
+ Try to auto-import a single name.
1856
+
1857
+ :type fullname:
1858
+ ``str``
1859
+ :param fullname:
1860
+ Fully-qualified module name, e.g. "sqlalchemy.orm".
1861
+ :type namespaces:
1862
+ ``list`` of ``dict``, e.g. [globals()].
1863
+ :param namespaces:
1864
+ Namespaces to check. Namespace[-1] is the namespace to import into.
1865
+ :type db:
1866
+ `ImportDB`
1867
+ :param db:
1868
+ Import database to use.
1869
+ :param autoimported:
1870
+ If not ``None``, then a dictionary of identifiers already attempted.
1871
+ ``auto_import`` will not attempt to auto-import symbols already in this
1872
+ dictionary, and will add attempted symbols to this dictionary, with
1873
+ value ``True`` if the autoimport succeeded, or ``False`` if the autoimport
1874
+ did not succeed.
1875
+ :rtype:
1876
+ ``bool``
1877
+ :param post_import_hook:
1878
+ A callable that is invoked if an import was successfully made.
1879
+ It is invoked with the `Import` object representing the successful import
1880
+ :type post_import_hook:
1881
+ ``callable``
1882
+ :return:
1883
+ ``True`` if the symbol was already in the namespace, or the auto-import
1884
+ succeeded; ``False`` if the auto-import failed.
1885
+ """
1886
+ namespaces = ScopeStack(namespaces)
1887
+ if not symbol_needs_import(fullname, namespaces):
1888
+ return True
1889
+ if autoimported is None:
1890
+ autoimported = {}
1891
+ if DottedIdentifier(fullname) in autoimported:
1892
+ logger.debug("auto_import_symbol(%r): already attempted", fullname)
1893
+ return False
1894
+ # See whether there's a known import for this name. This is mainly
1895
+ # important for things like "from numpy import arange". Imports such as
1896
+ # "import sqlalchemy.orm" will also be handled by this, although it's less
1897
+ # important, since we're going to attempt that import anyway if it looks
1898
+ # like a "sqlalchemy" package is importable.
1899
+ imports = get_known_import(fullname, db=db)
1900
+ # successful_import will store last successfully executed import statement
1901
+ # to be passed to post_import_hook
1902
+ successful_import = None
1903
+ logger.debug("auto_import_symbol(%r): get_known_import() => %r",
1904
+ fullname, imports)
1905
+ if imports is None:
1906
+ # No known imports.
1907
+ pass
1908
+ else:
1909
+ assert len(imports) >= 1
1910
+ if len(imports) > 1:
1911
+ # Doh, multiple imports.
1912
+ logger.info("Multiple candidate imports for %s. Please pick one:", fullname)
1913
+ for imp in imports:
1914
+ logger.info(" %s", imp)
1915
+ autoimported[DottedIdentifier(fullname)] = False
1916
+ return False
1917
+ imp, = imports
1918
+ if symbol_needs_import(imp.import_as, namespaces=namespaces):
1919
+ # We're ready for some real action. The input code references a
1920
+ # name/attribute that (a) is not locally assigned, (b) is not a
1921
+ # global, (c) is not yet imported, (d) is a known auto-import, (e)
1922
+ # has only one definition
1923
+ # TODO: label which known_imports file the autoimport came from
1924
+ if not _try_import(imp, namespaces[-1]):
1925
+ # Failed; don't do anything else.
1926
+ autoimported[DottedIdentifier(fullname)] = False
1927
+ return False
1928
+ # Succeeded.
1929
+ successful_import = imp
1930
+ autoimported[DottedIdentifier(imp.import_as)] = True
1931
+ if imp.import_as == fullname:
1932
+ if post_import_hook:
1933
+ post_import_hook(imp)
1934
+ # We got what we wanted, so nothing more to do.
1935
+ return True
1936
+ if imp.import_as != imp.fullname:
1937
+ if post_import_hook:
1938
+ post_import_hook(imp)
1939
+ # This is not just an 'import foo.bar'; rather, it's a 'import
1940
+ # foo.bar as baz' or 'from foo import bar'. So don't go any
1941
+ # further.
1942
+ return True
1943
+ # Fall through.
1944
+ # We haven't yet imported what we want. Either there was no entry in the
1945
+ # known imports database, or it wasn't "complete" (e.g. the user wanted
1946
+ # "foo.bar.baz", and the known imports database only knew about "import
1947
+ # foo.bar"). For each component that may need importing, check if the
1948
+ # loader thinks it should be importable, and if so import it.
1949
+ for pmodule in ModuleHandle(fullname).ancestors:
1950
+ if not symbol_needs_import(pmodule.name, namespaces):
1951
+ continue
1952
+ pmodule_name = DottedIdentifier(pmodule.name)
1953
+ if pmodule_name in autoimported:
1954
+ if not autoimported[pmodule_name]:
1955
+ logger.debug("auto_import_symbol(%r): stopping because "
1956
+ "already previously failed to autoimport %s",
1957
+ fullname, pmodule_name)
1958
+ return False
1959
+ if not pmodule.exists:
1960
+ logger.debug("auto_import_symbol(%r): %r doesn't exist according to pkgutil",
1961
+ fullname, pmodule)
1962
+ autoimported[pmodule_name] = False
1963
+ return False
1964
+ imp_stmt = "import %s" % pmodule_name
1965
+ result = _try_import(imp_stmt, namespaces[-1])
1966
+ autoimported[pmodule_name] = result
1967
+ if not result:
1968
+ return False
1969
+ else:
1970
+ successful_import = Import(imp_stmt)
1971
+ if post_import_hook and successful_import:
1972
+ post_import_hook(successful_import)
1973
+ return True
1974
+
1975
+
1976
+ def auto_import(arg, namespaces, db=None, autoimported=None, post_import_hook=None, *, extra_db=None):
1977
+ """
1978
+ Parse ``arg`` for symbols that need to be imported and automatically import
1979
+ them.
1980
+
1981
+ :type arg:
1982
+ ``str``, ``ast.AST``, `PythonBlock`, ``callable``, or ``types.CodeType``
1983
+ :param arg:
1984
+ Python code, either as source text, a parsed AST, or compiled code; can
1985
+ be as simple as a single qualified name, or as complex as an entire
1986
+ module text.
1987
+ :type namespaces:
1988
+ ``dict`` or ``list`` of ``dict``
1989
+ :param namespaces:
1990
+ Namespaces to check. Namespace[-1] is the namespace to import into.
1991
+ :type db:
1992
+ `ImportDB`
1993
+ :param db:
1994
+ Import database to use.
1995
+ :type autoimported:
1996
+ ``dict``
1997
+ :param autoimported:
1998
+ If not ``None``, then a dictionary of identifiers already attempted.
1999
+ ``auto_import`` will not attempt to auto-import symbols already in this
2000
+ dictionary, and will add attempted symbols to this dictionary, with
2001
+ value ``True`` if the autoimport succeeded, or ``False`` if the autoimport
2002
+ did not succeed.
2003
+ :rtype:
2004
+ ``bool``
2005
+ :param post_import_hook:
2006
+ A callable invoked on each successful import. This is passed to
2007
+ `auto_import_symbol`
2008
+ :type post_import_hook:
2009
+ ``callable``
2010
+ :return:
2011
+ ``True`` if all symbols are already in the namespace or successfully
2012
+ auto-imported; ``False`` if any auto-imports failed.
2013
+ """
2014
+ namespaces = ScopeStack(namespaces)
2015
+ if isinstance(arg, PythonBlock):
2016
+ filename = arg.filename
2017
+ else:
2018
+ filename = "."
2019
+ try:
2020
+ fullnames = find_missing_imports(arg, namespaces)
2021
+ except SyntaxError:
2022
+ logger.debug("syntax error parsing %r", arg)
2023
+ return False
2024
+ logger.debug("Missing imports: %r", fullnames)
2025
+ if not fullnames:
2026
+ return True
2027
+ if autoimported is None:
2028
+ autoimported = {}
2029
+ db = ImportDB.interpret_arg(db, target_filename=filename)
2030
+ if extra_db:
2031
+ db = db|extra_db
2032
+ ok = True
2033
+ for fullname in fullnames:
2034
+ ok &= auto_import_symbol(fullname, namespaces, db, autoimported, post_import_hook=post_import_hook)
2035
+ return ok
2036
+
2037
+
2038
+ def auto_eval(arg, filename=None, mode=None,
2039
+ flags=None, auto_flags=True, globals=None, locals=None,
2040
+ db=None):
2041
+ """
2042
+ Evaluate/execute the given code, automatically importing as needed.
2043
+
2044
+ ``auto_eval`` will default the compilation ``mode`` to "eval" if possible::
2045
+
2046
+ >>> auto_eval("b64decode('aGVsbG8=')") + b"!"
2047
+ [PYFLYBY] from base64 import b64decode
2048
+ b'hello!'
2049
+
2050
+ ``auto_eval`` will default the compilation ``mode`` to "exec" if the input
2051
+ is not a single expression::
2052
+
2053
+ >>> auto_eval("if True: print(b64decode('aGVsbG8=').decode('utf-8'))")
2054
+ [PYFLYBY] from base64 import b64decode
2055
+ hello
2056
+
2057
+ This is roughly equivalent to "auto_import(arg); eval(arg)", but handles
2058
+ details better and more efficiently.
2059
+
2060
+ :type arg:
2061
+ ``str``, ``ast.AST``, ``code``, `Filename`, `FileText`, `PythonBlock`
2062
+ :param arg:
2063
+ Code to evaluate.
2064
+ :type filename:
2065
+ ``str``
2066
+ :param filename:
2067
+ Filename for compilation error messages. If ``None``, defaults to
2068
+ ``arg.filename`` if relevant, else ``"<stdin>"``.
2069
+ :type mode:
2070
+ ``str``
2071
+ :param mode:
2072
+ Compilation mode: ``None``, "exec", "single", or "eval". "exec",
2073
+ "single", and "eval" work as the built-in ``compile`` function do.
2074
+ If ``None``, then default to "eval" if the input is a string with a
2075
+ single expression, else "exec".
2076
+ :type flags:
2077
+ ``CompilerFlags`` or convertible (``int``, ``list`` of ``str``, etc.)
2078
+ :param flags:
2079
+ Compilation feature flags, e.g. ["division", "with_statement"]. If
2080
+ ``None``, defaults to no flags. Does not inherit flags from parent
2081
+ scope.
2082
+ :type auto_flags:
2083
+ ``bool``
2084
+ :param auto_flags:
2085
+ Whether to try other flags if ``flags`` causes SyntaxError.
2086
+ :type globals:
2087
+ ``dict``
2088
+ :param globals:
2089
+ Globals for evaluation. If ``None``, use an empty dictionary.
2090
+ :type locals:
2091
+ ``dict``
2092
+ :param locals:
2093
+ Locals for evaluation. If ``None``, use ``globals``.
2094
+ :type db:
2095
+ `ImportDB`
2096
+ :param db:
2097
+ Import database to use.
2098
+ :return:
2099
+ Result of evaluation (for mode="eval")
2100
+ """
2101
+ if isinstance(flags, int):
2102
+ assert isinstance(flags, CompilerFlags)
2103
+ if isinstance(arg, (str, Filename, FileText, PythonBlock)):
2104
+ block = PythonBlock(arg, filename=filename, flags=flags,
2105
+ auto_flags=auto_flags)
2106
+ flags = block.flags
2107
+ filename = block.filename
2108
+ arg = block.parse(mode=mode)
2109
+ elif isinstance(arg, (ast.AST, types.CodeType)):
2110
+ pass
2111
+ else:
2112
+ raise TypeError(
2113
+ "auto_eval(): expected some form of code; got a %s"
2114
+ % (type(arg).__name__,))
2115
+ # Canonicalize other args.
2116
+ if filename:
2117
+ filename = Filename(filename)
2118
+ else:
2119
+ filename = None
2120
+ if globals is None:
2121
+ globals = {}
2122
+ if locals is None:
2123
+ locals = globals
2124
+ db = ImportDB.interpret_arg(db, target_filename=filename)
2125
+ namespaces = [globals, locals]
2126
+ # Import as needed.
2127
+ auto_import(arg, namespaces, db)
2128
+ # Compile from AST to code object.
2129
+ if isinstance(arg, types.CodeType):
2130
+ code = arg
2131
+ else:
2132
+ # Infer mode from ast object.
2133
+ mode = infer_compile_mode(arg)
2134
+ # Compile ast node => code object. This step is necessary because
2135
+ # eval() doesn't work on AST objects. We don't need to pass ``flags``
2136
+ # to compile() because flags are irrelevant when we already have an
2137
+ # AST node.
2138
+ code = compile(arg, str(filename or "<unknown>"), mode)
2139
+ # Evaluate/execute.
2140
+ return eval(code, globals, locals)
2141
+
2142
+
2143
+ class LoadSymbolError(Exception):
2144
+
2145
+ def __str__(self):
2146
+ r = ": ".join(map(str, self.args))
2147
+ e = getattr(self, "__cause__", None)
2148
+ if e:
2149
+ r += ": %s: %s" % (type(e).__name__, e)
2150
+ return r
2151
+
2152
+
2153
+ def load_symbol(fullname, namespaces, autoimport=False, db=None,
2154
+ autoimported=None):
2155
+ """
2156
+ Load the symbol ``fullname``.
2157
+
2158
+ >>> import os
2159
+ >>> load_symbol("os.path.join.__name__", {"os": os})
2160
+ 'join'
2161
+
2162
+ >>> load_symbol("os.path.join.asdf", {"os": os})
2163
+ Traceback (most recent call last):
2164
+ ...
2165
+ pyflyby._autoimp.LoadSymbolError: os.path.join.asdf: AttributeError: 'function' object has no attribute 'asdf'
2166
+
2167
+ >>> load_symbol("os.path.join", {})
2168
+ Traceback (most recent call last):
2169
+ ...
2170
+ pyflyby._autoimp.LoadSymbolError: os.path.join: NameError: os
2171
+
2172
+ :type fullname:
2173
+ ``str``
2174
+ :param fullname:
2175
+ Fully-qualified symbol name, e.g. "os.path.join".
2176
+ :type namespaces:
2177
+ ``dict`` or ``list`` of ``dict``
2178
+ :param namespaces:
2179
+ Namespaces to check.
2180
+ :param autoimport:
2181
+ If ``False`` (default), the symbol must already be imported.
2182
+ If ``True``, then auto-import the symbol first.
2183
+ :type db:
2184
+ `ImportDB`
2185
+ :param db:
2186
+ Import database to use when ``autoimport=True``.
2187
+ :param autoimported:
2188
+ If not ``None``, then a dictionary of identifiers already attempted.
2189
+ ``auto_import`` will not attempt to auto-import symbols already in this
2190
+ dictionary, and will add attempted symbols to this dictionary, with
2191
+ value ``True`` if the autoimport succeeded, or ``False`` if the autoimport
2192
+ did not succeed.
2193
+ :return:
2194
+ Object.
2195
+ :raise LoadSymbolError:
2196
+ Object was not found or there was another exception.
2197
+ """
2198
+ namespaces = ScopeStack(namespaces)
2199
+ if autoimport:
2200
+ # Auto-import the symbol first.
2201
+ # We do the lookup as a separate step after auto-import. (An
2202
+ # alternative design could be to have auto_import_symbol() return the
2203
+ # symbol if possible. We don't do that because most users of
2204
+ # auto_import_symbol() don't need to follow down arbitrary (possibly
2205
+ # non-module) attributes.)
2206
+ auto_import_symbol(fullname, namespaces, db, autoimported=autoimported)
2207
+ name_parts = fullname.split(".")
2208
+ name0 = name_parts[0]
2209
+ for namespace in namespaces:
2210
+ try:
2211
+ obj = namespace[name0]
2212
+ except KeyError:
2213
+ pass
2214
+ else:
2215
+ for n in name_parts[1:]:
2216
+ try:
2217
+ # Do the getattr. This may raise AttributeError or
2218
+ # other exception.
2219
+ obj = getattr(obj, n)
2220
+ except Exception as e:
2221
+ e2 = LoadSymbolError(fullname)
2222
+ e2.__cause__ = e
2223
+ reraise(LoadSymbolError, e2, sys.exc_info()[2])
2224
+ return obj
2225
+ else:
2226
+ # Not found in any namespace.
2227
+ e2 = LoadSymbolError(fullname)
2228
+ e2.__cause__ = NameError(name0)
2229
+ raise e2