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/_docxref.py ADDED
@@ -0,0 +1,379 @@
1
+ # pyflyby/_docxref.py.
2
+
3
+ # Module for checking Epydoc cross-references.
4
+
5
+ # Portions of the code below are derived from Epydoc, which is distributed
6
+ # under the MIT license:
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a
9
+ # copy of this software and any associated documentation files (the
10
+ # "Software"), to deal in the Software without restriction, including
11
+ # without limitation the rights to use, copy, modify, merge, publish,
12
+ # distribute, sublicense, and/or sell copies of the Software, and to permit
13
+ # persons to whom the Software is furnished to do so, subject to the
14
+ # following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be included in
17
+ # all copies or substantial portions of the Software.
18
+ #
19
+ # The software is provided "as is", without warranty of any kind, express or
20
+ # implied, including but not limited to the warranties of merchantability,
21
+ # fitness for a particular purpose and noninfringement. In no event shall
22
+ # the authors or copyright holders be liable for any claim, damages or other
23
+ # liability, whether in an action of contract, tort or otherwise, arising
24
+ # from, out of or in connection with the software or the use or other
25
+ # dealings in the software.
26
+
27
+
28
+ import re
29
+ import builtins
30
+ from textwrap import dedent
31
+
32
+ from epydoc.apidoc import (ClassDoc, ModuleDoc, PropertyDoc,
33
+ RoutineDoc, UNKNOWN, VariableDoc)
34
+ from epydoc.docbuilder import build_doc_index
35
+ from epydoc.markup.plaintext import ParsedPlaintextDocstring
36
+
37
+ from pyflyby._file import Filename
38
+ from pyflyby._idents import DottedIdentifier
39
+ from pyflyby._log import logger
40
+ from pyflyby._modules import ModuleHandle
41
+ from pyflyby._util import cached_attribute, memoize, prefixes
42
+
43
+ # If someone references numpy.*, just assume it's OK - it's not worth
44
+ # following into numpy because it's too slow.
45
+ ASSUME_MODULES_OK = set(['numpy'])
46
+
47
+ @memoize
48
+ def map_strings_to_line_numbers(module):
49
+ """
50
+ Walk ``module.ast``, looking at all string literals. Return a map from
51
+ string literals to line numbers (1-index).
52
+
53
+ :rtype:
54
+ ``dict`` from ``str`` to (``int``, ``str``)
55
+ """
56
+ d = {}
57
+ for field in module.block.string_literals():
58
+ # Dedent because epydoc dedents strings and we need to look up by
59
+ # those. But keep track of original version because we need to count
60
+ # exact line numbers.
61
+ s = dedent(field.s).strip()
62
+ start_lineno = field.startpos.lineno
63
+ d[s] = (start_lineno, field.s)
64
+ return d
65
+
66
+
67
+ def get_string_linenos(module, searchstring, within_string):
68
+ """
69
+ Return the line numbers (1-indexed) within ``filename`` that contain
70
+ ``searchstring``. Only consider string literals (i.e. not comments).
71
+ First look for exact matches of ``within_string`` (modulo indenting) and
72
+ then search within that. Only if the ``within_string`` is not found,
73
+ search the entire file.
74
+
75
+ [If there's a comment on the same line as a string that also contains the
76
+ searchstring, we'll get confused.]
77
+ """
78
+ module = ModuleHandle(module)
79
+ regexp = re.compile(searchstring)
80
+ map = map_strings_to_line_numbers(module)
81
+ results = []
82
+ def scan_within_string(results, start_lineno, orig_full_string):
83
+ for i, line in enumerate(orig_full_string.splitlines()):
84
+ if regexp.search(line):
85
+ results.append( start_lineno + i )
86
+ try:
87
+ lineno, orig_full_string = map[within_string.strip()]
88
+ except KeyError:
89
+ pass
90
+ else:
91
+ # We found the larger string exactly within the ast.
92
+ scan_within_string(results, lineno, orig_full_string)
93
+ if results:
94
+ return tuple(results)
95
+ # We could continue down if this ever happened.
96
+ raise Exception(
97
+ "Found superstring in %r but not substring %r within superstring"
98
+ % (module.filename, searchstring))
99
+ # Try a full text search.
100
+ for lineno, orig_full_string in map.values():
101
+ scan_within_string(results, lineno, orig_full_string)
102
+ if results:
103
+ return tuple(sorted(results))
104
+ raise Exception(
105
+ "Could not find %r anywhere in %r" % (searchstring, module.filename))
106
+
107
+
108
+ def describe_xref(identifier, container):
109
+ module = ModuleHandle(str(container.defining_module.canonical_name))
110
+ assert module.filename == Filename(container.defining_module.filename)
111
+ linenos = get_string_linenos(
112
+ module,
113
+ "(L{|<)%s" % (identifier,),
114
+ container.docstring)
115
+ return (module, linenos, str(container.canonical_name), identifier)
116
+
117
+
118
+
119
+ def safe_build_doc_index(modules):
120
+ # build_doc_index isn't re-entrant due to crappy caching! >:(
121
+ from epydoc.docintrospecter import clear_cache
122
+ clear_cache()
123
+ from epydoc.docparser import _moduledoc_cache
124
+ _moduledoc_cache.clear()
125
+ # Build a new DocIndex. It swallows exceptions and returns None on error!
126
+ # >:(
127
+ result = build_doc_index(modules)
128
+ if result is None:
129
+ raise Exception("Failed to build doc index on %r" % (modules,))
130
+ return result
131
+
132
+
133
+ class ExpandedDocIndex(object):
134
+ """
135
+ A wrapper around DocIndex that automatically expands with more modules as
136
+ needed.
137
+ """
138
+ # TODO: this is kludgy and inefficient since it re-reads modules.
139
+ def __init__(self, modules):
140
+ self.modules = set([ModuleHandle(m) for m in modules])
141
+
142
+ def add_module(self, module):
143
+ """
144
+ Adds ``module`` and recreates the DocIndex with the updated set of
145
+ modules.
146
+
147
+ :return:
148
+ Whether anything was added.
149
+ """
150
+ module = ModuleHandle(module)
151
+ for prefix in module.ancestors:
152
+ if prefix in self.modules:
153
+ # The module, or a prefix of it, was already added.
154
+ return False
155
+
156
+ for existing_module in sorted(self.modules):
157
+ if existing_module.startswith(module):
158
+ # This supersedes an existing module.
159
+ assert existing_module != module
160
+ self.modules.remove(existing_module)
161
+
162
+ logger.debug("Expanding docindex to include %r", module)
163
+ self.modules.add(module)
164
+ del self.docindex
165
+ return True
166
+
167
+ def find(self, a, b):
168
+ return self.docindex.find(a, b)
169
+
170
+ def get_vardoc(self, a):
171
+ return self.docindex.get_vardoc(a)
172
+
173
+ @cached_attribute
174
+ def docindex(self):
175
+ return safe_build_doc_index(
176
+ [str(m.name) for m in sorted(self.modules)])
177
+
178
+
179
+ def remove_epydoc_sym_suffix(s):
180
+ """
181
+ Remove trailing "'" that Epydoc annoyingly adds to 'shadowed' names.
182
+
183
+ >>> remove_epydoc_sym_suffix("a.b'.c'.d")
184
+ 'a.b.c.d'
185
+
186
+ """
187
+ return re.sub(r"'([.]|$)", r'\1', s)
188
+
189
+ class XrefScanner(object):
190
+
191
+ def __init__(self, modules):
192
+ self.modules = modules
193
+ self.docindex = safe_build_doc_index(modules)
194
+
195
+ @cached_attribute
196
+ def expanded_docindex(self):
197
+ return ExpandedDocIndex(self.modules)
198
+
199
+ def scan(self):
200
+ self._failed_xrefs = []
201
+ valdocs = sorted(self.docindex.reachable_valdocs(
202
+ imports=False, packages=False, bases=False, submodules=False,
203
+ subclasses=False, private=True
204
+ ))
205
+ for doc in valdocs:
206
+ if isinstance(doc, ClassDoc):
207
+ self.scan_class(doc)
208
+ elif isinstance(doc, ModuleDoc):
209
+ self.scan_module(doc)
210
+ return tuple(sorted(self._failed_xrefs))
211
+
212
+ def scan_module(self, doc):
213
+ self.descr(doc)
214
+ if doc.is_package is True:
215
+ for submodule in doc.submodules:
216
+ self.scan_module(submodule)
217
+ # self.scan_module_list(doc)
218
+ self.scan_details_list(doc, "function")
219
+ self.scan_details_list(doc, "other")
220
+
221
+ def scan_class(self, doc):
222
+ self.descr(doc)
223
+ self.scan_details_list(doc, "method")
224
+ self.scan_details_list(doc, "classvariable")
225
+ self.scan_details_list(doc, "instancevariable")
226
+ self.scan_details_list(doc, "property")
227
+
228
+ def scan_details_list(self, doc, value_type):
229
+ detailed = True
230
+ if isinstance(doc, ClassDoc):
231
+ var_docs = doc.select_variables(value_type=value_type,
232
+ imported=False, inherited=False,
233
+ public=None,
234
+ detailed=detailed)
235
+ else:
236
+ var_docs = doc.select_variables(value_type=value_type,
237
+ imported=False,
238
+ public=None,
239
+ detailed=detailed)
240
+ for var_doc in var_docs:
241
+ self.scan_details(var_doc)
242
+
243
+ def scan_details(self, var_doc):
244
+ self.descr(var_doc)
245
+ if isinstance(var_doc.value, RoutineDoc):
246
+ self.return_type(var_doc)
247
+ self.return_descr(var_doc)
248
+ for (arg_names, arg_descr) in var_doc.value.arg_descrs:
249
+ self.scan_docstring(arg_descr, var_doc.value)
250
+ for arg in var_doc.value.arg_types:
251
+ self.scan_docstring(
252
+ var_doc.value.arg_types[arg], var_doc.value)
253
+ elif isinstance(var_doc.value, PropertyDoc):
254
+ prop_doc = var_doc.value
255
+ self.return_type(prop_doc.fget)
256
+ self.return_type(prop_doc.fset)
257
+ self.return_type(prop_doc.fdel)
258
+ else:
259
+ self.type_descr(var_doc)
260
+
261
+ def _scan_attr(self, attr, api_doc):
262
+ if api_doc in (None, UNKNOWN):
263
+ return ''
264
+ pds = getattr(api_doc, attr, None) # pds = ParsedDocstring.
265
+ if pds not in (None, UNKNOWN):
266
+ self.scan_docstring(pds, api_doc)
267
+ elif isinstance(api_doc, VariableDoc):
268
+ self._scan_attr(attr, api_doc.value)
269
+
270
+ def summary(self, api_doc):
271
+ self._scan_attr('summary', api_doc)
272
+
273
+ def descr(self, api_doc):
274
+ self._scan_attr('descr', api_doc)
275
+
276
+ def type_descr(self, api_doc):
277
+ self._scan_attr('type_descr', api_doc)
278
+
279
+ def return_type(self, api_doc):
280
+ self._scan_attr('return_type', api_doc)
281
+
282
+ def return_descr(self, api_doc):
283
+ self._scan_attr('return_descr', api_doc)
284
+
285
+ def check_xref(self, identifier, container):
286
+ """
287
+ Check that ``identifier`` cross-references a proper symbol.
288
+
289
+ Look in modules that we weren't explicitly asked to look in, if
290
+ needed.
291
+ """
292
+ if identifier in builtins.__dict__:
293
+ return True
294
+ def check_container():
295
+ if self.expanded_docindex.find(identifier, container) is not None:
296
+ return True
297
+ if isinstance(container, RoutineDoc):
298
+ tcontainer = self.expanded_docindex.get_vardoc(
299
+ container.canonical_name)
300
+ doc = self.expanded_docindex.find(identifier, tcontainer)
301
+ while (doc is not None and tcontainer not in (None, UNKNOWN)
302
+ and tcontainer.overrides not in (None, UNKNOWN)):
303
+ tcontainer = tcontainer.overrides
304
+ doc = self.expanded_docindex.find(identifier, tcontainer)
305
+ return doc is not None
306
+ return False
307
+ def check_defining_module(x):
308
+ if x is None:
309
+ return False
310
+ defining_module_name = remove_epydoc_sym_suffix(str(
311
+ x.defining_module.canonical_name))
312
+ if defining_module_name in ASSUME_MODULES_OK:
313
+ return True
314
+ if self.expanded_docindex.add_module(defining_module_name):
315
+ if check_container():
316
+ return True
317
+ return False
318
+ if check_container():
319
+ return True
320
+ if (isinstance(container, RoutineDoc) and
321
+ identifier in container.all_args()):
322
+ return True
323
+ if check_defining_module(container):
324
+ return True
325
+ # If the user has imported foo.bar.baz as baz and now uses
326
+ # ``baz.quux``, we need to add the module foo.bar.baz.
327
+ for prefix in reversed(list(prefixes(
328
+ DottedIdentifier(remove_epydoc_sym_suffix(identifier))))):
329
+ if check_defining_module(
330
+ self.docindex.find(str(prefix), container)):
331
+ return True
332
+ try:
333
+ module = ModuleHandle.containing(identifier)
334
+ except ImportError:
335
+ pass
336
+ else:
337
+ if str(module.name) in ASSUME_MODULES_OK:
338
+ return True
339
+ if self.expanded_docindex.add_module(module):
340
+ if check_container():
341
+ return True
342
+ return False
343
+
344
+ def scan_docstring(self, parsed_docstring, container):
345
+ if parsed_docstring in (None, UNKNOWN): return ''
346
+ if isinstance(parsed_docstring, ParsedPlaintextDocstring):
347
+ return ''
348
+
349
+ def scan_tree(tree):
350
+ if isinstance(tree, str):
351
+ return tree
352
+ variables = [scan_tree(child) for child in tree.children]
353
+ if tree.tag == 'link':
354
+ identifier = variables[1]
355
+ if not self.check_xref(identifier, container):
356
+ self._failed_xrefs.append(
357
+ describe_xref(identifier, container) )
358
+ return '?'
359
+ elif tree.tag == 'indexed':
360
+ return '?'
361
+ elif tree.tag in ('epytext', 'section', 'tag', 'arg',
362
+ 'name', 'target', 'html', 'para'):
363
+ return ''.join(variables)
364
+ return '?'
365
+
366
+ scan_tree(parsed_docstring._tree)
367
+
368
+
369
+ def find_bad_doc_cross_references(names):
370
+ """
371
+ Find docstring cross references that fail to resolve.
372
+
373
+ :type names:
374
+ Sequence of module names or filenames.
375
+ :return:
376
+ Sequence of ``(module, linenos, container_name, identifier)`` tuples.
377
+ """
378
+ xrs = XrefScanner(names)
379
+ return xrs.scan()
pyflyby/_dynimp.py ADDED
@@ -0,0 +1,154 @@
1
+ """
2
+ Virtual module to create dynamic import at runtime.
3
+
4
+ It is sometime desirable to have auto import which are define only during
5
+ a session and never exist on a on-disk file.
6
+
7
+ This is injects a Dict module loader as well as a dictionary registry of in
8
+ memory module.
9
+
10
+ This is mostly use in IPython for lazy variable initialisation without having
11
+ to use proxy objects.
12
+
13
+ To use, put the following in your IPython startup files
14
+ (``~/.ipython/profile_default/startup/autoimp.py`), or in your IPython
15
+ configuration file:
16
+
17
+
18
+ .. code:: python
19
+
20
+ from pyflyby._dynimp import add_import
21
+
22
+ add_import("foo", "foo = 1")
23
+
24
+ add_import(
25
+ "df, data",
26
+ '''
27
+ import pandas as pd
28
+ data = [1,2,3]
29
+ df = pd.DataFrame(data)
30
+ ''',
31
+ )
32
+
33
+ Now at the IPython prompt, if the pyflyby extension is loaded (either because
34
+ you started using the ``py`` cli, or some configuration options like ``ipython
35
+ --TerminalIPythonApp.extra_extensions=pyflyby``. When trying to use an undefined
36
+ variable like ``foo``, ``df`` or ``data``, the corresponding module will be
37
+ executed and the relevant variable imported.
38
+
39
+
40
+ """
41
+ import importlib.abc
42
+ import importlib.util
43
+ import sys
44
+
45
+ from textwrap import dedent
46
+ from typing import FrozenSet
47
+
48
+ from pyflyby._importclns import ImportSet, Import
49
+
50
+ module_dict = {}
51
+
52
+ PYFLYBY_LAZY_LOAD_PREFIX = "from pyflyby_autoimport_"
53
+
54
+ def add_import(names: str, code: str, *, strict: bool = True):
55
+ """
56
+ Add a runtime generated import module
57
+
58
+ Parameters
59
+ ----------
60
+ names: str
61
+ name, or comma separated list variable names that should be created by
62
+ executing and importing `code`.
63
+ code: str
64
+ potentially multiline string that will be turned into a module,
65
+ executed and from which variables listed in names can be imported.
66
+ strict: bool
67
+ Raise in case of problem loading IPython of if pyflyby extension not installed.
68
+ otherwise just ignore error
69
+
70
+
71
+
72
+ Examples
73
+ --------
74
+
75
+ >>> add_import('pd, df', '''
76
+ ... import pandas a pd
77
+ ...
78
+ ... df = pd.DataFrame([[1,2], [3,4]])
79
+ ... ''', strict=False) # don't fail doctest
80
+
81
+ """
82
+ try:
83
+ ip = _raise_if_problem()
84
+ except Exception:
85
+ if strict:
86
+ raise
87
+ else:
88
+ return
89
+ return _add_import(ip, names, code)
90
+
91
+
92
+ def _raise_if_problem():
93
+ try:
94
+ import IPython
95
+ except ModuleNotFoundError as e:
96
+ raise ImportError("Dynamic autoimport requires IPython to be installed") from e
97
+
98
+ ip = IPython.get_ipython()
99
+ if ip is None:
100
+ raise ImportError("Dynamic autoimport only work from within IPython")
101
+
102
+ if not hasattr(ip, "_auto_importer"):
103
+ raise ValueError(
104
+ "IPython needs to be loaded with pyflyby extension for lazy variable to work"
105
+ )
106
+ return ip
107
+
108
+
109
+ def _add_import(ip, names: str, code: str) -> None:
110
+ """
111
+ private version of add_import
112
+ """
113
+ assert ip is not None
114
+ module = PYFLYBY_LAZY_LOAD_PREFIX.split()[1]
115
+ mang = module + names.replace(",", "_").replace(" ", "_")
116
+ a: FrozenSet[Import] = ImportSet(f"from {mang} import {names}")._importset
117
+ b: FrozenSet[Import] = ip._auto_importer.db.known_imports._importset
118
+ s_import: FrozenSet[Import] = a | b
119
+
120
+ ip._auto_importer.db.known_imports = ImportSet._from_imports(list(s_import))
121
+ module_dict[mang] = dedent(code)
122
+
123
+ class DictLoader(importlib.abc.Loader):
124
+ """
125
+ A dict based loader for in-memory module definition.
126
+ """
127
+ def __init__(self, module_name, module_code):
128
+ self.module_name = module_name
129
+ self.module_code = module_code
130
+
131
+ def create_module(self, spec):
132
+ return None # Use default module creation semantics
133
+
134
+ def exec_module(self, module):
135
+ """
136
+ we exec module code directly in memory
137
+ """
138
+ exec(self.module_code, module.__dict__)
139
+
140
+
141
+ class DictFinder(importlib.abc.MetaPathFinder):
142
+ """
143
+ A meta path finder for abode DictLoader
144
+ """
145
+ def find_spec(self, fullname, path, target=None):
146
+ if fullname in module_dict:
147
+ module_code = module_dict[fullname]
148
+ loader = DictLoader(fullname, module_code)
149
+ return importlib.util.spec_from_loader(fullname, loader)
150
+ return None
151
+
152
+
153
+ def inject():
154
+ sys.meta_path.insert(0, DictFinder())