pyflyby 1.10.4__cp311-cp311-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. pyflyby/__init__.py +61 -0
  2. pyflyby/__main__.py +9 -0
  3. pyflyby/_autoimp.py +2228 -0
  4. pyflyby/_cmdline.py +591 -0
  5. pyflyby/_comms.py +221 -0
  6. pyflyby/_dbg.py +1383 -0
  7. pyflyby/_dynimp.py +154 -0
  8. pyflyby/_fast_iter_modules.cpython-311-darwin.so +0 -0
  9. pyflyby/_file.py +771 -0
  10. pyflyby/_flags.py +230 -0
  11. pyflyby/_format.py +186 -0
  12. pyflyby/_idents.py +227 -0
  13. pyflyby/_import_sorting.py +165 -0
  14. pyflyby/_importclns.py +658 -0
  15. pyflyby/_importdb.py +535 -0
  16. pyflyby/_imports2s.py +643 -0
  17. pyflyby/_importstmt.py +723 -0
  18. pyflyby/_interactive.py +2113 -0
  19. pyflyby/_livepatch.py +793 -0
  20. pyflyby/_log.py +107 -0
  21. pyflyby/_modules.py +646 -0
  22. pyflyby/_parse.py +1396 -0
  23. pyflyby/_py.py +2165 -0
  24. pyflyby/_saveframe.py +1145 -0
  25. pyflyby/_saveframe_reader.py +471 -0
  26. pyflyby/_util.py +458 -0
  27. pyflyby/_version.py +8 -0
  28. pyflyby/autoimport.py +20 -0
  29. pyflyby/etc/pyflyby/canonical.py +10 -0
  30. pyflyby/etc/pyflyby/common.py +27 -0
  31. pyflyby/etc/pyflyby/forget.py +10 -0
  32. pyflyby/etc/pyflyby/mandatory.py +10 -0
  33. pyflyby/etc/pyflyby/numpy.py +156 -0
  34. pyflyby/etc/pyflyby/std.py +335 -0
  35. pyflyby/importdb.py +19 -0
  36. pyflyby/libexec/pyflyby/colordiff +34 -0
  37. pyflyby/libexec/pyflyby/diff-colorize +148 -0
  38. pyflyby/share/emacs/site-lisp/pyflyby.el +112 -0
  39. pyflyby-1.10.4.data/scripts/collect-exports +76 -0
  40. pyflyby-1.10.4.data/scripts/collect-imports +58 -0
  41. pyflyby-1.10.4.data/scripts/find-import +38 -0
  42. pyflyby-1.10.4.data/scripts/prune-broken-imports +34 -0
  43. pyflyby-1.10.4.data/scripts/pyflyby-diff +34 -0
  44. pyflyby-1.10.4.data/scripts/reformat-imports +27 -0
  45. pyflyby-1.10.4.data/scripts/replace-star-imports +37 -0
  46. pyflyby-1.10.4.data/scripts/saveframe +299 -0
  47. pyflyby-1.10.4.data/scripts/tidy-imports +170 -0
  48. pyflyby-1.10.4.data/scripts/transform-imports +47 -0
  49. pyflyby-1.10.4.dist-info/METADATA +605 -0
  50. pyflyby-1.10.4.dist-info/RECORD +53 -0
  51. pyflyby-1.10.4.dist-info/WHEEL +6 -0
  52. pyflyby-1.10.4.dist-info/entry_points.txt +4 -0
  53. pyflyby-1.10.4.dist-info/licenses/LICENSE.txt +19 -0
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,186 @@
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_length_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
+ if params.max_line_length is None:
132
+ max_line_length = params.max_line_length_default
133
+ else:
134
+ max_line_length = params.max_line_length
135
+
136
+ if params.wrap_paren:
137
+ # Check how we will break up the tokens.
138
+ len_full = sum(len(tok) for tok in tokens) + 2 * (len(tokens)-1)
139
+ if len(prefix) + len_full <= max_line_length:
140
+ # The entire thing fits on one line; no parens needed. We check
141
+ # this first because breaking into lines adds paren overhead.
142
+ #
143
+ # Output looks like:
144
+ # from foo import abc, defgh, ijkl, mnopq, rst
145
+ return prefix + ", ".join(tokens) + "\n"
146
+ if params.hanging_indent == "never":
147
+ hanging_indent = False
148
+ elif params.hanging_indent == "always":
149
+ hanging_indent = True
150
+ elif params.hanging_indent == "auto":
151
+ # Decide automatically whether to do hanging-indent mode. If any
152
+ # line would exceed the max_line_length, then do hanging indent;
153
+ # else don't.
154
+ #
155
+ # In order to use non-hanging-indent mode, the first line would
156
+ # have an overhead of 2 because of "(" and ",". We check the
157
+ # longest token since even if the first token fits, we still want
158
+ # to avoid later tokens running over N.
159
+ maxtoklen = max(len(token) for token in tokens)
160
+ hanging_indent = (len(prefix) + maxtoklen + 2 > max_line_length)
161
+ else:
162
+ raise ValueError("bad params.hanging_indent=%r"
163
+ % (params.hanging_indent,))
164
+ if hanging_indent:
165
+ # Hanging indent mode. We need a single opening paren and
166
+ # continue all imports on separate lines.
167
+ #
168
+ # Output looks like:
169
+ # from foo import (
170
+ # abc, defgh, ijkl,
171
+ # mnopq, rst)
172
+ return (prefix + "(\n"
173
+ + fill(tokens, max_line_length=max_line_length,
174
+ prefix=(" " * params.indent), suffix=("", ")")))
175
+ else:
176
+ # Non-hanging-indent mode.
177
+ #
178
+ # Output looks like:
179
+ # from foo import (abc, defgh,
180
+ # ijkl, mnopq,
181
+ # rst)
182
+ pprefix = prefix + "("
183
+ return fill(tokens, max_line_length=max_line_length,
184
+ prefix=(pprefix, " " * len(pprefix)), suffix=("", ")"))
185
+ else:
186
+ raise NotImplementedError
pyflyby/_idents.py ADDED
@@ -0,0 +1,227 @@
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 iskeyword
9
+ import re
10
+
11
+ from pyflyby._util import cached_attribute, cmp
12
+
13
+ from typing import Optional, Tuple, Dict
14
+
15
+
16
+ # TODO: use DottedIdentifier.prefixes
17
+ def dotted_prefixes(dotted_name, reverse=False):
18
+ """
19
+ Return the prefixes of a dotted name.
20
+
21
+ >>> dotted_prefixes("aa.bb.cc")
22
+ ['aa', 'aa.bb', 'aa.bb.cc']
23
+
24
+ >>> dotted_prefixes("aa.bb.cc", reverse=True)
25
+ ['aa.bb.cc', 'aa.bb', 'aa']
26
+
27
+ :type dotted_name:
28
+ ``str``
29
+ :param reverse:
30
+ If False (default), return shortest to longest. If True, return longest
31
+ to shortest.
32
+ :rtype:
33
+ ``list`` of ``str``
34
+ """
35
+ name_parts = dotted_name.split(".")
36
+ if reverse:
37
+ idxes = range(len(name_parts), 0, -1)
38
+ else:
39
+ idxes = range(1, len(name_parts)+1)
40
+ result = ['.'.join(name_parts[:i]) or '.' for i in idxes]
41
+ return result
42
+
43
+
44
+ def is_identifier(s: str, dotted: bool = False, prefix: bool = False):
45
+ """
46
+ Return whether ``s`` is a valid Python identifier name.
47
+
48
+ >>> is_identifier("foo")
49
+ True
50
+
51
+ >>> is_identifier("foo+bar")
52
+ False
53
+
54
+ >>> is_identifier("from")
55
+ False
56
+
57
+ By default, we check whether ``s`` is a single valid identifier, meaning
58
+ dots are not allowed. If ``dotted=True``, then we check each dotted
59
+ component::
60
+
61
+ >>> is_identifier("foo.bar")
62
+ False
63
+
64
+ >>> is_identifier("foo.bar", dotted=True)
65
+ True
66
+
67
+ >>> is_identifier("foo..bar", dotted=True)
68
+ False
69
+
70
+ >>> is_identifier("foo.from", dotted=True)
71
+ False
72
+
73
+ By default, the string must comprise a valid identifier. If
74
+ ``prefix=True``, then allow strings that are prefixes of valid identifiers.
75
+ Prefix=False excludes the empty string, strings with a trailing dot, and
76
+ strings with a trailing keyword component, but prefix=True does not
77
+ exclude these.
78
+
79
+ >>> is_identifier("foo.bar.", dotted=True)
80
+ False
81
+
82
+ >>> is_identifier("foo.bar.", dotted=True, prefix=True)
83
+ True
84
+
85
+ >>> is_identifier("foo.or", dotted=True)
86
+ False
87
+
88
+ >>> is_identifier("foo.or", dotted=True, prefix=True)
89
+ True
90
+
91
+ :type s:
92
+ ``str``
93
+ :param dotted:
94
+ If ``False`` (default), then the input must be a single name such as
95
+ "foo". If ``True``, then the input can be a single name or a dotted name
96
+ such as "foo.bar.baz".
97
+ :param prefix:
98
+ If ``False`` (Default), then the input must be a valid identifier. If
99
+ ``True``, then the input can be a valid identifier or the prefix of a
100
+ valid identifier.
101
+ :rtype:
102
+ ``bool``
103
+ """
104
+ if not isinstance(s, str):
105
+ raise TypeError("is_identifier(): expected a string; got a %s"
106
+ % (type(s).__name__,))
107
+ if prefix:
108
+ return is_identifier(s + '_', dotted=dotted, prefix=False)
109
+ if dotted:
110
+ return all(is_identifier(w, dotted=False) for w in s.split('.'))
111
+ return s.isidentifier() and not iskeyword(s)
112
+
113
+
114
+ def brace_identifiers(text):
115
+ """
116
+ Parse a string and yield all tokens of the form "{some_token}".
117
+
118
+ >>> list(brace_identifiers("{salutation}, {your_name}."))
119
+ ['salutation', 'your_name']
120
+ """
121
+ if isinstance(text, bytes):
122
+ text = text.decode('utf-8', errors='replace')
123
+ for match in re.finditer("{([a-zA-Z_][a-zA-Z0-9_]*)}", text):
124
+ yield match.group(1)
125
+
126
+
127
+ class BadDottedIdentifierError(ValueError):
128
+ pass
129
+
130
+
131
+ # TODO: Use in various places, esp where e.g. dotted_prefixes is used.
132
+ @total_ordering
133
+ class DottedIdentifier:
134
+ name: str
135
+ parts: Tuple[str, ...]
136
+ scope_info: Optional[Dict]
137
+
138
+ def __new__(cls, arg, scope_info=None):
139
+ if isinstance(arg, cls):
140
+ return arg
141
+ if isinstance(arg, str):
142
+ return cls._from_name(arg, scope_info)
143
+ if isinstance(arg, (tuple, list)):
144
+ return cls._from_name(".".join(arg), scope_info)
145
+ raise TypeError("DottedIdentifier: unexpected %s"
146
+ % (type(arg).__name__,))
147
+
148
+ @classmethod
149
+ def _from_name(cls, name, scope_info=None):
150
+ self = object.__new__(cls)
151
+ self.name = str(name)
152
+ # TODO: change magic methods to compare with scopestack included
153
+ self.scope_info = scope_info
154
+ if not is_identifier(self.name, dotted=True):
155
+ if len(self.name) > 20:
156
+ raise BadDottedIdentifierError("Invalid python symbol name")
157
+ else:
158
+ raise BadDottedIdentifierError("Invalid python symbol name %r"
159
+ % (name,))
160
+ self.parts = tuple(self.name.split('.'))
161
+ return self
162
+
163
+ @cached_attribute
164
+ def parent(self):
165
+ if len(self.parts) > 1:
166
+ return DottedIdentifier('.'.join(self.parts[:-1]))
167
+ else:
168
+ return None
169
+
170
+ @cached_attribute
171
+ def prefixes(self):
172
+ parts = self.parts
173
+ idxes = range(1, len(parts)+1)
174
+ result = ['.'.join(parts[:i]) for i in idxes]
175
+ return tuple(DottedIdentifier(x) for x in result)
176
+
177
+ def startswith(self, o):
178
+ o = type(self)(o)
179
+ return self.parts[:len(o.parts)] == o.parts
180
+
181
+ def __getitem__(self, x):
182
+ return type(self)(self.parts[x])
183
+
184
+ def __len__(self):
185
+ return len(self.parts)
186
+
187
+ def __iter__(self):
188
+ return (type(self)(x) for x in self.parts)
189
+
190
+ def __add__(self, suffix):
191
+ return type(self)("%s.%s" % (self, suffix))
192
+
193
+ def __str__(self):
194
+ return self.name
195
+
196
+ def __repr__(self):
197
+ return "%s(%r)" % (type(self).__name__, self.name)
198
+
199
+ def __hash__(self):
200
+ return hash(self.name)
201
+
202
+ def __eq__(self, other):
203
+ if self is other:
204
+ return True
205
+ if not isinstance(other, DottedIdentifier):
206
+ return NotImplemented
207
+ return self.name == other.name
208
+
209
+ def __ne__(self, other):
210
+ if self is other:
211
+ return False
212
+ if not isinstance(other, DottedIdentifier):
213
+ return NotImplemented
214
+ return self.name != other.name
215
+
216
+ # The rest are defined by total_ordering
217
+ def __lt__(self, other):
218
+ if not isinstance(other, DottedIdentifier):
219
+ return NotImplemented
220
+ return self.name < other.name
221
+
222
+ def __cmp__(self, other):
223
+ if self is other:
224
+ return 0
225
+ if not isinstance(other, DottedIdentifier):
226
+ return NotImplemented
227
+ return cmp(self.name, other.name)