pyflyby 1.9.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyflyby might be problematic. Click here for more details.
- pyflyby/__init__.py +56 -0
- pyflyby/__main__.py +9 -0
- pyflyby/_autoimp.py +2114 -0
- pyflyby/_cmdline.py +531 -0
- pyflyby/_comms.py +221 -0
- pyflyby/_dbg.py +1339 -0
- pyflyby/_docxref.py +379 -0
- pyflyby/_file.py +738 -0
- pyflyby/_flags.py +230 -0
- pyflyby/_format.py +182 -0
- pyflyby/_idents.py +233 -0
- pyflyby/_import_sorting.py +165 -0
- pyflyby/_importclns.py +642 -0
- pyflyby/_importdb.py +588 -0
- pyflyby/_imports2s.py +639 -0
- pyflyby/_importstmt.py +662 -0
- pyflyby/_interactive.py +2605 -0
- pyflyby/_livepatch.py +793 -0
- pyflyby/_log.py +199 -0
- pyflyby/_modules.py +515 -0
- pyflyby/_parse.py +1441 -0
- pyflyby/_py.py +2078 -0
- pyflyby/_util.py +459 -0
- pyflyby/_version.py +7 -0
- pyflyby/autoimport.py +20 -0
- pyflyby/importdb.py +19 -0
- pyflyby-1.9.4.data/data/etc/pyflyby/canonical.py +10 -0
- pyflyby-1.9.4.data/data/etc/pyflyby/common.py +27 -0
- pyflyby-1.9.4.data/data/etc/pyflyby/forget.py +10 -0
- pyflyby-1.9.4.data/data/etc/pyflyby/mandatory.py +10 -0
- pyflyby-1.9.4.data/data/etc/pyflyby/numpy.py +156 -0
- pyflyby-1.9.4.data/data/etc/pyflyby/std.py +335 -0
- pyflyby-1.9.4.data/data/libexec/pyflyby/colordiff +34 -0
- pyflyby-1.9.4.data/data/libexec/pyflyby/diff-colorize +148 -0
- pyflyby-1.9.4.data/data/share/doc/pyflyby/LICENSE.txt +23 -0
- pyflyby-1.9.4.data/data/share/doc/pyflyby/TODO.txt +115 -0
- pyflyby-1.9.4.data/data/share/doc/pyflyby/testing.txt +13 -0
- pyflyby-1.9.4.data/data/share/emacs/site-lisp/pyflyby.el +108 -0
- pyflyby-1.9.4.data/scripts/collect-exports +76 -0
- pyflyby-1.9.4.data/scripts/collect-imports +58 -0
- pyflyby-1.9.4.data/scripts/find-import +38 -0
- pyflyby-1.9.4.data/scripts/list-bad-xrefs +34 -0
- pyflyby-1.9.4.data/scripts/prune-broken-imports +34 -0
- pyflyby-1.9.4.data/scripts/pyflyby-diff +34 -0
- pyflyby-1.9.4.data/scripts/reformat-imports +27 -0
- pyflyby-1.9.4.data/scripts/replace-star-imports +37 -0
- pyflyby-1.9.4.data/scripts/tidy-imports +191 -0
- pyflyby-1.9.4.data/scripts/transform-imports +47 -0
- pyflyby-1.9.4.dist-info/LICENSE.txt +23 -0
- pyflyby-1.9.4.dist-info/METADATA +507 -0
- pyflyby-1.9.4.dist-info/RECORD +54 -0
- pyflyby-1.9.4.dist-info/WHEEL +5 -0
- pyflyby-1.9.4.dist-info/entry_points.txt +3 -0
- pyflyby-1.9.4.dist-info/top_level.txt +1 -0
pyflyby/_flags.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# pyflyby/_flags.py.
|
|
2
|
+
# Copyright (C) 2011, 2012, 2013, 2014 Karl Chen.
|
|
3
|
+
# License: MIT http://opensource.org/licenses/MIT
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import __future__
|
|
8
|
+
import ast
|
|
9
|
+
import operator
|
|
10
|
+
from functools import reduce
|
|
11
|
+
import warnings
|
|
12
|
+
|
|
13
|
+
from pyflyby._util import cached_attribute
|
|
14
|
+
from typing import Tuple
|
|
15
|
+
|
|
16
|
+
# Initialize mappings from compiler_flag to feature name and vice versa.
|
|
17
|
+
_FLAG2NAME = {}
|
|
18
|
+
_NAME2FLAG = {}
|
|
19
|
+
for name in __future__.all_feature_names:
|
|
20
|
+
flag = getattr(__future__, name).compiler_flag
|
|
21
|
+
_FLAG2NAME[flag] = name
|
|
22
|
+
_NAME2FLAG[name] = flag
|
|
23
|
+
for name in dir(ast):
|
|
24
|
+
if name.startswith('PyCF'):
|
|
25
|
+
flag_name = name[len('PyCF_'):].lower()
|
|
26
|
+
flag = getattr(ast, name)
|
|
27
|
+
_FLAG2NAME[flag] = flag_name
|
|
28
|
+
_NAME2FLAG[flag_name] = flag
|
|
29
|
+
_FLAGNAME_ITEMS = sorted(_FLAG2NAME.items())
|
|
30
|
+
_ALL_FLAGS = reduce(operator.or_, _FLAG2NAME.keys())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class CompilerFlags(int):
|
|
35
|
+
"""
|
|
36
|
+
Representation of Python "compiler flags", i.e. features from __future__.
|
|
37
|
+
|
|
38
|
+
>>> print(CompilerFlags(0x18000).__interactive_display__()) # doctest: +SKIP
|
|
39
|
+
CompilerFlags(0x18000) # from __future__ import with_statement, print_function
|
|
40
|
+
|
|
41
|
+
>>> print(CompilerFlags(0x10000, 0x8000).__interactive_display__()) # doctest: +SKIP
|
|
42
|
+
CompilerFlags(0x18000) # from __future__ import with_statement, print_function
|
|
43
|
+
|
|
44
|
+
>>> print(CompilerFlags('with_statement', 'print_function').__interactive_display__()) # doctest: +SKIP
|
|
45
|
+
CompilerFlags(0x18000) # from __future__ import with_statement, print_function
|
|
46
|
+
|
|
47
|
+
>>> compile("print('x', file=None)", "?", "exec", flags=CompilerFlags("print_function"), dont_inherit=1) #doctest:+ELLIPSIS
|
|
48
|
+
<code object ...>
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
# technically both those are compiler flags, but we can't use Self. May need typing_extensions ?
|
|
53
|
+
_ZERO:int
|
|
54
|
+
_UNKNOWN: int
|
|
55
|
+
|
|
56
|
+
def __new__(cls, *args):
|
|
57
|
+
"""
|
|
58
|
+
Construct a new ``CompilerFlags`` instance.
|
|
59
|
+
|
|
60
|
+
:param args:
|
|
61
|
+
Any number (zero or more) ``CompilerFlags`` s, ``int`` s, or ``str`` s,
|
|
62
|
+
which are bitwise-ORed together.
|
|
63
|
+
:rtype:
|
|
64
|
+
`CompilerFlags`
|
|
65
|
+
"""
|
|
66
|
+
if len(args) == 0:
|
|
67
|
+
return cls._ZERO
|
|
68
|
+
elif len(args) == 1:
|
|
69
|
+
arg, = args
|
|
70
|
+
if isinstance(arg, cls):
|
|
71
|
+
return arg
|
|
72
|
+
elif arg is None:
|
|
73
|
+
return cls._ZERO
|
|
74
|
+
elif isinstance(arg, int):
|
|
75
|
+
warnings.warn('creating CompilerFlags from integers is deprecated, '
|
|
76
|
+
' flags values change between Python versions. If you are sure use .from_int',
|
|
77
|
+
DeprecationWarning, stacklevel=2)
|
|
78
|
+
return cls.from_int(arg)
|
|
79
|
+
elif isinstance(arg, str):
|
|
80
|
+
return cls.from_str(arg)
|
|
81
|
+
elif isinstance(arg, ast.AST):
|
|
82
|
+
return cls.from_ast(arg)
|
|
83
|
+
elif isinstance(arg, (tuple, list)):
|
|
84
|
+
return cls(*arg)
|
|
85
|
+
else:
|
|
86
|
+
raise TypeError("CompilerFlags: unknown type %s"
|
|
87
|
+
% (type(arg).__name__,))
|
|
88
|
+
else:
|
|
89
|
+
flags = []
|
|
90
|
+
for x in args:
|
|
91
|
+
if isinstance(x, cls):
|
|
92
|
+
flags.append(int(x))
|
|
93
|
+
elif isinstance(x, int):
|
|
94
|
+
warnings.warn(
|
|
95
|
+
"creating CompilerFlags from integers is deprecated, "
|
|
96
|
+
" flags values change between Python versions. If you are sure use .from_int",
|
|
97
|
+
DeprecationWarning,
|
|
98
|
+
stacklevel=2,
|
|
99
|
+
)
|
|
100
|
+
flags.append(x)
|
|
101
|
+
elif isinstance(x, str):
|
|
102
|
+
flags.append(int(cls(x)))
|
|
103
|
+
else:
|
|
104
|
+
raise ValueError
|
|
105
|
+
|
|
106
|
+
#assert flags == [0x10000, 0x8000], flags
|
|
107
|
+
|
|
108
|
+
return cls.from_int(reduce(operator.or_, flags))
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
def from_int(cls, arg):
|
|
112
|
+
if arg == -1:
|
|
113
|
+
return cls._UNKNOWN # Instance optimization
|
|
114
|
+
if arg == 0:
|
|
115
|
+
return cls._ZERO # Instance optimization
|
|
116
|
+
self = int.__new__(cls, arg)
|
|
117
|
+
bad_flags = int(self) & ~_ALL_FLAGS
|
|
118
|
+
if bad_flags:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
"CompilerFlags: unknown flag value(s) %s %s" % (bin(bad_flags), hex(bad_flags)))
|
|
121
|
+
return self
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def from_str(cls, arg:str):
|
|
125
|
+
try:
|
|
126
|
+
flag = _NAME2FLAG[arg]
|
|
127
|
+
except KeyError:
|
|
128
|
+
raise ValueError(
|
|
129
|
+
"CompilerFlags: unknown flag %r" % (arg,))
|
|
130
|
+
return cls.from_int(flag)
|
|
131
|
+
|
|
132
|
+
@classmethod
|
|
133
|
+
def from_ast(cls, nodes):
|
|
134
|
+
"""
|
|
135
|
+
Parse the compiler flags from AST node(s).
|
|
136
|
+
|
|
137
|
+
:type nodes:
|
|
138
|
+
``ast.AST`` or sequence thereof
|
|
139
|
+
:rtype:
|
|
140
|
+
``CompilerFlags``
|
|
141
|
+
"""
|
|
142
|
+
if isinstance(nodes, ast.Module):
|
|
143
|
+
nodes = nodes.body
|
|
144
|
+
elif isinstance(nodes, ast.AST):
|
|
145
|
+
nodes = [nodes]
|
|
146
|
+
flags = []
|
|
147
|
+
for node in nodes:
|
|
148
|
+
if not isinstance(node, ast.ImportFrom):
|
|
149
|
+
# Got a non-import; stop looking further.
|
|
150
|
+
break
|
|
151
|
+
if not node.module == "__future__":
|
|
152
|
+
# Got a non-__future__-import; stop looking further.
|
|
153
|
+
break
|
|
154
|
+
# Get the feature names.
|
|
155
|
+
names = [n.name for n in node.names]
|
|
156
|
+
flags.extend(names)
|
|
157
|
+
return cls(flags)
|
|
158
|
+
|
|
159
|
+
@cached_attribute
|
|
160
|
+
def names(self) -> Tuple[str, ...]:
|
|
161
|
+
return tuple(
|
|
162
|
+
n
|
|
163
|
+
for f, n in _FLAGNAME_ITEMS
|
|
164
|
+
if f & self)
|
|
165
|
+
|
|
166
|
+
def __or__(self, o):
|
|
167
|
+
if o == 0:
|
|
168
|
+
return self
|
|
169
|
+
if not isinstance(o, CompilerFlags):
|
|
170
|
+
o = CompilerFlags(o)
|
|
171
|
+
if self == 0:
|
|
172
|
+
return o
|
|
173
|
+
return CompilerFlags.from_int(int(self) | int(o))
|
|
174
|
+
|
|
175
|
+
def __ror__(self, o):
|
|
176
|
+
return self | o
|
|
177
|
+
|
|
178
|
+
def __and__(self, o):
|
|
179
|
+
if not isinstance(o, int):
|
|
180
|
+
o = CompilerFlags(o)
|
|
181
|
+
return CompilerFlags.from_int(int(self) & int(o))
|
|
182
|
+
|
|
183
|
+
def __rand__(self, o):
|
|
184
|
+
return self & o
|
|
185
|
+
|
|
186
|
+
def __xor__(self, o):
|
|
187
|
+
if not isinstance(o, CompilerFlags):
|
|
188
|
+
o = CompilerFlags.from_int(o)
|
|
189
|
+
return CompilerFlags.from_int(int(self) ^ int(o))
|
|
190
|
+
|
|
191
|
+
def __rxor__(self, o):
|
|
192
|
+
return self ^ o
|
|
193
|
+
|
|
194
|
+
def __repr__(self):
|
|
195
|
+
return "CompilerFlags(%s)" % (hex(self),)
|
|
196
|
+
|
|
197
|
+
def __str__(self):
|
|
198
|
+
return hex(self)
|
|
199
|
+
|
|
200
|
+
def __interactive_display__(self):
|
|
201
|
+
s = repr(self)
|
|
202
|
+
if self != 0:
|
|
203
|
+
s += " # from __future__ import " + ", ".join(self.names)
|
|
204
|
+
return s
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
CompilerFlags._ZERO = int.__new__(CompilerFlags, 0)
|
|
208
|
+
CompilerFlags._UNKNOWN = int.__new__(CompilerFlags, -1)
|
|
209
|
+
|
|
210
|
+
# flags that _may_ exists on future versions.
|
|
211
|
+
_future_flags = {
|
|
212
|
+
"nested_scopes",
|
|
213
|
+
"generators",
|
|
214
|
+
"division",
|
|
215
|
+
"absolute_import",
|
|
216
|
+
"with_statement",
|
|
217
|
+
"print_function",
|
|
218
|
+
"unicode_literals",
|
|
219
|
+
"barry_as_FLUFL",
|
|
220
|
+
"generator_stop",
|
|
221
|
+
"annotations",
|
|
222
|
+
"allow_top_level_await",
|
|
223
|
+
"only_ast",
|
|
224
|
+
"type_comments",
|
|
225
|
+
}
|
|
226
|
+
for k in _future_flags:
|
|
227
|
+
setattr(CompilerFlags, k, CompilerFlags._UNKNOWN)
|
|
228
|
+
|
|
229
|
+
for k, v in _NAME2FLAG.items():
|
|
230
|
+
setattr(CompilerFlags, k, CompilerFlags.from_int(v))
|
pyflyby/_format.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# pyflyby/_format.py.
|
|
2
|
+
# Copyright (C) 2011, 2012, 2013, 2014 Karl Chen.
|
|
3
|
+
# License: MIT http://opensource.org/licenses/MIT
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FormatParams(object):
|
|
8
|
+
max_line_length = None
|
|
9
|
+
_max_line_lenght_default = 79
|
|
10
|
+
wrap_paren = True
|
|
11
|
+
indent = 4
|
|
12
|
+
hanging_indent = 'never'
|
|
13
|
+
use_black = False
|
|
14
|
+
|
|
15
|
+
def __new__(cls, *args, **kwargs):
|
|
16
|
+
if not kwargs and len(args) == 1 and isinstance(args[0], cls):
|
|
17
|
+
return args[0]
|
|
18
|
+
self = object.__new__(cls)
|
|
19
|
+
# TODO: be more careful here
|
|
20
|
+
dicts = []
|
|
21
|
+
for arg in args:
|
|
22
|
+
if arg is None:
|
|
23
|
+
pass
|
|
24
|
+
elif isinstance(arg, cls) or hasattr(self, "__dict__"):
|
|
25
|
+
dicts.append(arg.__dict__)
|
|
26
|
+
else:
|
|
27
|
+
raise TypeError(
|
|
28
|
+
"expected None, or instance of %s cls, got %s" % (cls, arg)
|
|
29
|
+
)
|
|
30
|
+
if kwargs:
|
|
31
|
+
dicts.append(kwargs)
|
|
32
|
+
for kwargs in dicts:
|
|
33
|
+
for key, value in kwargs.items():
|
|
34
|
+
if hasattr(self, key):
|
|
35
|
+
setattr(self, key, value)
|
|
36
|
+
else:
|
|
37
|
+
raise ValueError("bad kwarg %r" % (key,))
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
def __repr__(self):
|
|
41
|
+
return f'<{self.__class__.__name__} {self.__dict__}>'
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def fill(tokens, sep=(", ", ""), prefix="", suffix="", newline="\n",
|
|
45
|
+
max_line_length=80):
|
|
46
|
+
r"""
|
|
47
|
+
Given a sequences of strings, fill them into a single string with up to
|
|
48
|
+
``max_line_length`` characters each.
|
|
49
|
+
|
|
50
|
+
>>> fill(["'hello world'", "'hello two'"],
|
|
51
|
+
... prefix=("print ", " "), suffix=(" \\", ""),
|
|
52
|
+
... max_line_length=25)
|
|
53
|
+
"print 'hello world', \\\n 'hello two'\n"
|
|
54
|
+
|
|
55
|
+
:param tokens:
|
|
56
|
+
Sequence of strings to fill. There must be at least one token.
|
|
57
|
+
:param sep:
|
|
58
|
+
Separator string to append to each token. If a 2-element tuple, then
|
|
59
|
+
indicates the separator between tokens and the separator after the last
|
|
60
|
+
token. Trailing whitespace is removed from each line before appending
|
|
61
|
+
the suffix, but not from between tokens on the same line.
|
|
62
|
+
:param prefix:
|
|
63
|
+
String to prepend at the beginning of each line. If a 2-element tuple,
|
|
64
|
+
then indicates the prefix for the first line and prefix for subsequent
|
|
65
|
+
lines.
|
|
66
|
+
:param suffix:
|
|
67
|
+
String to append to the end of each line. If a 2-element tuple, then
|
|
68
|
+
indicates the suffix for all lines except the last, and the suffix for
|
|
69
|
+
the last line.
|
|
70
|
+
:return:
|
|
71
|
+
Filled string.
|
|
72
|
+
"""
|
|
73
|
+
N = max_line_length
|
|
74
|
+
assert len(tokens) > 0
|
|
75
|
+
if isinstance(prefix, tuple):
|
|
76
|
+
first_prefix, cont_prefix = prefix
|
|
77
|
+
else:
|
|
78
|
+
first_prefix = cont_prefix = prefix
|
|
79
|
+
if isinstance(suffix, tuple):
|
|
80
|
+
nonterm_suffix, term_suffix = suffix
|
|
81
|
+
else:
|
|
82
|
+
nonterm_suffix = term_suffix = suffix
|
|
83
|
+
if isinstance(sep, tuple):
|
|
84
|
+
nonterm_sep, term_sep = sep
|
|
85
|
+
else:
|
|
86
|
+
nonterm_sep = term_sep = sep
|
|
87
|
+
lines = [first_prefix + tokens[0]]
|
|
88
|
+
for token, is_last in zip(tokens[1:], [False]*(len(tokens)-2) + [True]):
|
|
89
|
+
suffix = term_suffix if is_last else nonterm_suffix
|
|
90
|
+
sep = (term_sep if is_last else nonterm_sep).rstrip()
|
|
91
|
+
# Does the next token fit?
|
|
92
|
+
if len(lines[-1] + nonterm_sep + token + sep + suffix) <= N:
|
|
93
|
+
# Yes; add it.
|
|
94
|
+
lines[-1] += nonterm_sep + token
|
|
95
|
+
else:
|
|
96
|
+
# No; break into new line.
|
|
97
|
+
lines[-1] += nonterm_sep.rstrip() + nonterm_suffix + newline
|
|
98
|
+
lines.append(cont_prefix + token)
|
|
99
|
+
lines[-1] += term_sep.rstrip() + term_suffix + newline
|
|
100
|
+
return ''.join(lines)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def pyfill(prefix, tokens, params=FormatParams()):
|
|
104
|
+
"""
|
|
105
|
+
Fill a Python statement.
|
|
106
|
+
|
|
107
|
+
>>> print(pyfill('print ', ["foo.bar", "baz", "quux", "quuuuux"]), end='')
|
|
108
|
+
print foo.bar, baz, quux, quuuuux
|
|
109
|
+
>>> print(pyfill('print ', ["foo.bar", "baz", "quux", "quuuuux"],
|
|
110
|
+
... FormatParams(max_line_length=15, hanging_indent='auto')), end='')
|
|
111
|
+
print (foo.bar,
|
|
112
|
+
baz,
|
|
113
|
+
quux,
|
|
114
|
+
quuuuux)
|
|
115
|
+
>>> print(pyfill('print ', ["foo.bar", "baz", "quux", "quuuuux"],
|
|
116
|
+
... FormatParams(max_line_length=14, hanging_indent='auto')), end='')
|
|
117
|
+
print (
|
|
118
|
+
foo.bar,
|
|
119
|
+
baz, quux,
|
|
120
|
+
quuuuux)
|
|
121
|
+
|
|
122
|
+
:param prefix:
|
|
123
|
+
Prefix for first line.
|
|
124
|
+
:param tokens:
|
|
125
|
+
Sequence of string tokens
|
|
126
|
+
:type params:
|
|
127
|
+
`FormatParams`
|
|
128
|
+
:rtype:
|
|
129
|
+
``str``
|
|
130
|
+
"""
|
|
131
|
+
N = params.max_line_length or params._max_line_lenght_default
|
|
132
|
+
if params.wrap_paren:
|
|
133
|
+
# Check how we will break up the tokens.
|
|
134
|
+
len_full = sum(len(tok) for tok in tokens) + 2 * (len(tokens)-1)
|
|
135
|
+
if len(prefix) + len_full <= N:
|
|
136
|
+
# The entire thing fits on one line; no parens needed. We check
|
|
137
|
+
# this first because breaking into lines adds paren overhead.
|
|
138
|
+
#
|
|
139
|
+
# Output looks like:
|
|
140
|
+
# from foo import abc, defgh, ijkl, mnopq, rst
|
|
141
|
+
return prefix + ", ".join(tokens) + "\n"
|
|
142
|
+
if params.hanging_indent == "never":
|
|
143
|
+
hanging_indent = False
|
|
144
|
+
elif params.hanging_indent == "always":
|
|
145
|
+
hanging_indent = True
|
|
146
|
+
elif params.hanging_indent == "auto":
|
|
147
|
+
# Decide automatically whether to do hanging-indent mode. If any
|
|
148
|
+
# line would exceed the max_line_length, then do hanging indent;
|
|
149
|
+
# else don't.
|
|
150
|
+
#
|
|
151
|
+
# In order to use non-hanging-indent mode, the first line would
|
|
152
|
+
# have an overhead of 2 because of "(" and ",". We check the
|
|
153
|
+
# longest token since even if the first token fits, we still want
|
|
154
|
+
# to avoid later tokens running over N.
|
|
155
|
+
maxtoklen = max(len(token) for token in tokens)
|
|
156
|
+
hanging_indent = (len(prefix) + maxtoklen + 2 > N)
|
|
157
|
+
else:
|
|
158
|
+
raise ValueError("bad params.hanging_indent=%r"
|
|
159
|
+
% (params.hanging_indent,))
|
|
160
|
+
if hanging_indent:
|
|
161
|
+
# Hanging indent mode. We need a single opening paren and
|
|
162
|
+
# continue all imports on separate lines.
|
|
163
|
+
#
|
|
164
|
+
# Output looks like:
|
|
165
|
+
# from foo import (
|
|
166
|
+
# abc, defgh, ijkl,
|
|
167
|
+
# mnopq, rst)
|
|
168
|
+
return (prefix + "(\n"
|
|
169
|
+
+ fill(tokens, max_line_length=N,
|
|
170
|
+
prefix=(" " * params.indent), suffix=("", ")")))
|
|
171
|
+
else:
|
|
172
|
+
# Non-hanging-indent mode.
|
|
173
|
+
#
|
|
174
|
+
# Output looks like:
|
|
175
|
+
# from foo import (abc, defgh,
|
|
176
|
+
# ijkl, mnopq,
|
|
177
|
+
# rst)
|
|
178
|
+
pprefix = prefix + "("
|
|
179
|
+
return fill(tokens, max_line_length=N,
|
|
180
|
+
prefix=(pprefix, " " * len(pprefix)), suffix=("", ")"))
|
|
181
|
+
else:
|
|
182
|
+
raise NotImplementedError
|
pyflyby/_idents.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# pyflyby/_idents.py.
|
|
2
|
+
# Copyright (C) 2011, 2012, 2013, 2014, 2018 Karl Chen.
|
|
3
|
+
# License: MIT http://opensource.org/licenses/MIT
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from functools import total_ordering
|
|
8
|
+
from keyword import kwlist
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
from pyflyby._util import cached_attribute, cmp
|
|
12
|
+
|
|
13
|
+
from typing import Optional, Tuple, Dict
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Don't consider "print" a keyword, in order to be compatible with user code
|
|
17
|
+
# that uses "from __future__ import print_function".
|
|
18
|
+
_my_kwlist = list(kwlist)
|
|
19
|
+
_my_iskeyword = frozenset(_my_kwlist).__contains__
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# TODO: use DottedIdentifier.prefixes
|
|
23
|
+
def dotted_prefixes(dotted_name, reverse=False):
|
|
24
|
+
"""
|
|
25
|
+
Return the prefixes of a dotted name.
|
|
26
|
+
|
|
27
|
+
>>> dotted_prefixes("aa.bb.cc")
|
|
28
|
+
['aa', 'aa.bb', 'aa.bb.cc']
|
|
29
|
+
|
|
30
|
+
>>> dotted_prefixes("aa.bb.cc", reverse=True)
|
|
31
|
+
['aa.bb.cc', 'aa.bb', 'aa']
|
|
32
|
+
|
|
33
|
+
:type dotted_name:
|
|
34
|
+
``str``
|
|
35
|
+
:param reverse:
|
|
36
|
+
If False (default), return shortest to longest. If True, return longest
|
|
37
|
+
to shortest.
|
|
38
|
+
:rtype:
|
|
39
|
+
``list`` of ``str``
|
|
40
|
+
"""
|
|
41
|
+
name_parts = dotted_name.split(".")
|
|
42
|
+
if reverse:
|
|
43
|
+
idxes = range(len(name_parts), 0, -1)
|
|
44
|
+
else:
|
|
45
|
+
idxes = range(1, len(name_parts)+1)
|
|
46
|
+
result = ['.'.join(name_parts[:i]) or '.' for i in idxes]
|
|
47
|
+
return result
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def is_identifier(s: str, dotted: bool = False, prefix: bool = False):
|
|
51
|
+
"""
|
|
52
|
+
Return whether ``s`` is a valid Python identifier name.
|
|
53
|
+
|
|
54
|
+
>>> is_identifier("foo")
|
|
55
|
+
True
|
|
56
|
+
|
|
57
|
+
>>> is_identifier("foo+bar")
|
|
58
|
+
False
|
|
59
|
+
|
|
60
|
+
>>> is_identifier("from")
|
|
61
|
+
False
|
|
62
|
+
|
|
63
|
+
By default, we check whether ``s`` is a single valid identifier, meaning
|
|
64
|
+
dots are not allowed. If ``dotted=True``, then we check each dotted
|
|
65
|
+
component::
|
|
66
|
+
|
|
67
|
+
>>> is_identifier("foo.bar")
|
|
68
|
+
False
|
|
69
|
+
|
|
70
|
+
>>> is_identifier("foo.bar", dotted=True)
|
|
71
|
+
True
|
|
72
|
+
|
|
73
|
+
>>> is_identifier("foo..bar", dotted=True)
|
|
74
|
+
False
|
|
75
|
+
|
|
76
|
+
>>> is_identifier("foo.from", dotted=True)
|
|
77
|
+
False
|
|
78
|
+
|
|
79
|
+
By default, the string must comprise a valid identifier. If
|
|
80
|
+
``prefix=True``, then allow strings that are prefixes of valid identifiers.
|
|
81
|
+
Prefix=False excludes the empty string, strings with a trailing dot, and
|
|
82
|
+
strings with a trailing keyword component, but prefix=True does not
|
|
83
|
+
exclude these.
|
|
84
|
+
|
|
85
|
+
>>> is_identifier("foo.bar.", dotted=True)
|
|
86
|
+
False
|
|
87
|
+
|
|
88
|
+
>>> is_identifier("foo.bar.", dotted=True, prefix=True)
|
|
89
|
+
True
|
|
90
|
+
|
|
91
|
+
>>> is_identifier("foo.or", dotted=True)
|
|
92
|
+
False
|
|
93
|
+
|
|
94
|
+
>>> is_identifier("foo.or", dotted=True, prefix=True)
|
|
95
|
+
True
|
|
96
|
+
|
|
97
|
+
:type s:
|
|
98
|
+
``str``
|
|
99
|
+
:param dotted:
|
|
100
|
+
If ``False`` (default), then the input must be a single name such as
|
|
101
|
+
"foo". If ``True``, then the input can be a single name or a dotted name
|
|
102
|
+
such as "foo.bar.baz".
|
|
103
|
+
:param prefix:
|
|
104
|
+
If ``False`` (Default), then the input must be a valid identifier. If
|
|
105
|
+
``True``, then the input can be a valid identifier or the prefix of a
|
|
106
|
+
valid identifier.
|
|
107
|
+
:rtype:
|
|
108
|
+
``bool``
|
|
109
|
+
"""
|
|
110
|
+
if not isinstance(s, str):
|
|
111
|
+
raise TypeError("is_identifier(): expected a string; got a %s"
|
|
112
|
+
% (type(s).__name__,))
|
|
113
|
+
if prefix:
|
|
114
|
+
return is_identifier(s + '_', dotted=dotted, prefix=False)
|
|
115
|
+
if dotted:
|
|
116
|
+
return all(is_identifier(w, dotted=False) for w in s.split('.'))
|
|
117
|
+
return s.isidentifier() and not _my_iskeyword(s)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def brace_identifiers(text):
|
|
121
|
+
"""
|
|
122
|
+
Parse a string and yield all tokens of the form "{some_token}".
|
|
123
|
+
|
|
124
|
+
>>> list(brace_identifiers("{salutation}, {your_name}."))
|
|
125
|
+
['salutation', 'your_name']
|
|
126
|
+
"""
|
|
127
|
+
if isinstance(text, bytes):
|
|
128
|
+
text = text.decode('utf-8', errors='replace')
|
|
129
|
+
for match in re.finditer("{([a-zA-Z_][a-zA-Z0-9_]*)}", text):
|
|
130
|
+
yield match.group(1)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class BadDottedIdentifierError(ValueError):
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# TODO: Use in various places, esp where e.g. dotted_prefixes is used.
|
|
138
|
+
@total_ordering
|
|
139
|
+
class DottedIdentifier:
|
|
140
|
+
name: str
|
|
141
|
+
parts: Tuple[str, ...]
|
|
142
|
+
scope_info: Optional[Dict]
|
|
143
|
+
|
|
144
|
+
def __new__(cls, arg, scope_info=None):
|
|
145
|
+
if isinstance(arg, cls):
|
|
146
|
+
return arg
|
|
147
|
+
if isinstance(arg, str):
|
|
148
|
+
return cls._from_name(arg, scope_info)
|
|
149
|
+
if isinstance(arg, (tuple, list)):
|
|
150
|
+
return cls._from_name(".".join(arg), scope_info)
|
|
151
|
+
raise TypeError("DottedIdentifier: unexpected %s"
|
|
152
|
+
% (type(arg).__name__,))
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def _from_name(cls, name, scope_info=None):
|
|
156
|
+
self = object.__new__(cls)
|
|
157
|
+
self.name = str(name)
|
|
158
|
+
# TODO: change magic methods to compare with scopestack included
|
|
159
|
+
self.scope_info = scope_info
|
|
160
|
+
if not is_identifier(self.name, dotted=True):
|
|
161
|
+
if len(self.name) > 20:
|
|
162
|
+
raise BadDottedIdentifierError("Invalid python symbol name")
|
|
163
|
+
else:
|
|
164
|
+
raise BadDottedIdentifierError("Invalid python symbol name %r"
|
|
165
|
+
% (name,))
|
|
166
|
+
self.parts = tuple(self.name.split('.'))
|
|
167
|
+
return self
|
|
168
|
+
|
|
169
|
+
@cached_attribute
|
|
170
|
+
def parent(self):
|
|
171
|
+
if len(self.parts) > 1:
|
|
172
|
+
return DottedIdentifier('.'.join(self.parts[:-1]))
|
|
173
|
+
else:
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
@cached_attribute
|
|
177
|
+
def prefixes(self):
|
|
178
|
+
parts = self.parts
|
|
179
|
+
idxes = range(1, len(parts)+1)
|
|
180
|
+
result = ['.'.join(parts[:i]) for i in idxes]
|
|
181
|
+
return tuple(DottedIdentifier(x) for x in result)
|
|
182
|
+
|
|
183
|
+
def startswith(self, o):
|
|
184
|
+
o = type(self)(o)
|
|
185
|
+
return self.parts[:len(o.parts)] == o.parts
|
|
186
|
+
|
|
187
|
+
def __getitem__(self, x):
|
|
188
|
+
return type(self)(self.parts[x])
|
|
189
|
+
|
|
190
|
+
def __len__(self):
|
|
191
|
+
return len(self.parts)
|
|
192
|
+
|
|
193
|
+
def __iter__(self):
|
|
194
|
+
return (type(self)(x) for x in self.parts)
|
|
195
|
+
|
|
196
|
+
def __add__(self, suffix):
|
|
197
|
+
return type(self)("%s.%s" % (self, suffix))
|
|
198
|
+
|
|
199
|
+
def __str__(self):
|
|
200
|
+
return self.name
|
|
201
|
+
|
|
202
|
+
def __repr__(self):
|
|
203
|
+
return "%s(%r)" % (type(self).__name__, self.name)
|
|
204
|
+
|
|
205
|
+
def __hash__(self):
|
|
206
|
+
return hash(self.name)
|
|
207
|
+
|
|
208
|
+
def __eq__(self, other):
|
|
209
|
+
if self is other:
|
|
210
|
+
return True
|
|
211
|
+
if not isinstance(other, DottedIdentifier):
|
|
212
|
+
return NotImplemented
|
|
213
|
+
return self.name == other.name
|
|
214
|
+
|
|
215
|
+
def __ne__(self, other):
|
|
216
|
+
if self is other:
|
|
217
|
+
return False
|
|
218
|
+
if not isinstance(other, DottedIdentifier):
|
|
219
|
+
return NotImplemented
|
|
220
|
+
return self.name != other.name
|
|
221
|
+
|
|
222
|
+
# The rest are defined by total_ordering
|
|
223
|
+
def __lt__(self, other):
|
|
224
|
+
if not isinstance(other, DottedIdentifier):
|
|
225
|
+
return NotImplemented
|
|
226
|
+
return self.name < other.name
|
|
227
|
+
|
|
228
|
+
def __cmp__(self, other):
|
|
229
|
+
if self is other:
|
|
230
|
+
return 0
|
|
231
|
+
if not isinstance(other, DottedIdentifier):
|
|
232
|
+
return NotImplemented
|
|
233
|
+
return cmp(self.name, other.name)
|