1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
10 from catapult_base
.refactor
.annotated_symbol
import base_symbol
11 from catapult_base
.refactor
import snippet
23 class DottedName(base_symbol
.AnnotatedSymbol
):
25 def Annotate(cls
, symbol_type
, children
):
26 if symbol_type
!= symbol
.dotted_name
:
28 return cls(symbol_type
, children
)
32 return ''.join(token_snippet
.value
for token_snippet
in self
._children
)
35 def value(self
, value
):
36 value_parts
= value
.split('.')
37 for value_part
in value_parts
:
38 if keyword
.iskeyword(value_part
):
39 raise ValueError('%s is a reserved keyword.' % value_part
)
41 # If we have too many children, cut the list down to size.
42 self
._children
= self
._children
[:len(value_parts
)*2-1]
45 for child
, value_part
in itertools
.izip_longest(
46 self
._children
[::2], value_parts
):
48 # Modify existing children. This helps preserve comments and spaces.
49 child
.value
= value_part
51 # Add children as needed.
52 self
._children
.append(snippet
.TokenSnippet
.Create(token
.DOT
, '.'))
53 self
._children
.append(
54 snippet
.TokenSnippet
.Create(token
.NAME
, value_part
))
57 class AsName(base_symbol
.AnnotatedSymbol
):
59 def Annotate(cls
, symbol_type
, children
):
60 if (symbol_type
!= symbol
.dotted_as_name
and
61 symbol_type
!= symbol
.import_as_name
):
63 return cls(symbol_type
, children
)
67 return self
.children
[0].value
70 def name(self
, value
):
71 self
.children
[0].value
= value
75 if len(self
.children
) < 3:
77 return self
.children
[2].value
80 def alias(self
, value
):
81 if keyword
.iskeyword(value
):
82 raise ValueError('%s is a reserved keyword.' % value
)
85 if len(self
.children
) < 3:
86 # If we currently have no alias, add one.
88 snippet
.TokenSnippet
.Create(token
.NAME
, 'as', (0, 1)))
90 snippet
.TokenSnippet
.Create(token
.NAME
, value
, (0, 1)))
92 # We already have an alias. Just update the value.
93 self
.children
[2].value
= value
95 # Removing the alias. Strip the "as foo".
96 self
.children
= [self
.children
[0]]
99 class Import(base_symbol
.AnnotatedSymbol
):
100 """An import statement.
104 from a.b import c as d
109 root == 'a.b' (only for "from" imports)
110 module == 'c' (only for "from" imports)
111 name (read-only) == the name used by references to the module, which is the
112 alias if there is one, the full module path in "full" imports, and the
113 module name in "from" imports.
117 """Returns True iff the import statment is of the form "from x import y"."""
118 raise NotImplementedError()
122 raise NotImplementedError()
126 raise NotImplementedError()
130 raise NotImplementedError()
134 """The full dotted path of the module."""
135 raise NotImplementedError()
138 def path(self
, value
):
139 raise NotImplementedError()
143 """The alias, if the module is renamed with "as". None otherwise."""
144 raise NotImplementedError()
147 def alias(self
, value
):
148 raise NotImplementedError()
152 """The name used to reference this import's module."""
153 raise NotImplementedError()
156 class ImportName(Import
):
158 def Annotate(cls
, symbol_type
, children
):
159 if symbol_type
!= symbol
.import_stmt
:
161 if children
[0].type != symbol
.import_name
:
163 assert len(children
) == 1
164 return cls(symbol_type
, children
[0].children
)
172 dotted_as_names
= self
.children
[1]
173 return tuple((dotted_as_name
.name
, dotted_as_name
.alias
)
174 for dotted_as_name
in dotted_as_names
.children
[::2])
178 return tuple(path
for path
, _
in self
.values
)
182 return tuple(alias
for _
, alias
in self
.values
)
185 def _dotted_as_name(self
):
186 dotted_as_names
= self
.children
[1]
187 if len(dotted_as_names
.children
) != 1:
188 raise NotImplementedError(
189 'This method only works if the statement has one import.')
190 return dotted_as_names
.children
[0]
194 return self
._dotted
_as
_name
.name
197 def path(self
, value
): # pylint: disable=arguments-differ
198 self
._dotted
_as
_name
.name
= value
202 return self
._dotted
_as
_name
.alias
205 def alias(self
, value
): # pylint: disable=arguments-differ
206 self
._dotted
_as
_name
.alias
= value
216 class ImportFrom(Import
):
218 def Annotate(cls
, symbol_type
, children
):
219 if symbol_type
!= symbol
.import_stmt
:
221 if children
[0].type != symbol
.import_from
:
223 assert len(children
) == 1
224 return cls(symbol_type
, children
[0].children
)
233 import_as_names
= self
.FindChild(symbol
.import_as_names
)
235 return (('*', None),)
237 return tuple((import_as_name
.name
, import_as_name
.alias
)
238 for import_as_name
in import_as_names
.children
[::2])
243 return tuple('.'.join((module
, name
)) for name
, _
in self
.values
)
247 return tuple(alias
for _
, alias
in self
.values
)
251 return self
.FindChild(symbol
.dotted_name
).value
254 def root(self
, value
):
255 self
.FindChild(symbol
.dotted_name
).value
= value
258 def _import_as_name(self
):
260 import_as_names
= self
.FindChild(symbol
.import_as_names
)
264 if len(import_as_names
.children
) != 1:
265 raise NotImplementedError(
266 'This method only works if the statement has one import.')
268 return import_as_names
.children
[0]
272 import_as_name
= self
._import
_as
_name
274 return import_as_name
.name
279 def module(self
, value
):
280 if keyword
.iskeyword(value
):
281 raise ValueError('%s is a reserved keyword.' % value
)
283 import_as_name
= self
._import
_as
_name
285 # TODO: Implement this.
286 raise NotImplementedError()
289 import_as_name
.name
= value
291 # TODO: Implement this.
292 raise NotImplementedError()
296 return '.'.join((self
.root
, self
.module
))
299 def path(self
, value
): # pylint: disable=arguments-differ
300 self
.root
, _
, self
.module
= value
.rpartition('.')
304 import_as_name
= self
._import
_as
_name
306 return import_as_name
.alias
311 def alias(self
, value
): # pylint: disable=arguments-differ
312 import_as_name
= self
._import
_as
_name
313 if not import_as_name
:
314 raise NotImplementedError('Cannot change alias for "import *".')
315 import_as_name
.alias
= value