Index: tools/telemetry/catapult_base/refactor/annotated_symbol/import_statement.py |
diff --git a/tools/telemetry/catapult_base/refactor/annotated_symbol/import_statement.py b/tools/telemetry/catapult_base/refactor/annotated_symbol/import_statement.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e598f5aa9a22b2ae3c5ddfe5a37c6adfcd4c46f1 |
--- /dev/null |
+++ b/tools/telemetry/catapult_base/refactor/annotated_symbol/import_statement.py |
@@ -0,0 +1,322 @@ |
+# Copyright 2015 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import itertools |
+import keyword |
+import symbol |
+import token |
+ |
+from catapult_base.refactor.annotated_symbol import base_symbol |
+from catapult_base.refactor import snippet |
+ |
+ |
+__all__ = [ |
+ 'AsName', |
+ 'DottedName', |
+ 'Import', |
+ 'ImportFrom', |
+ 'ImportName', |
+] |
+ |
+ |
+class DottedName(base_symbol.AnnotatedSymbol): |
+ @classmethod |
+ def Annotate(cls, symbol_type, children): |
+ if symbol_type != symbol.dotted_name: |
+ return None |
+ return cls(symbol_type, children) |
+ |
+ @property |
+ def value(self): |
+ return ''.join(token_snippet.value for token_snippet in self._children) |
+ |
+ @value.setter |
+ def value(self, value): |
+ value_parts = value.split('.') |
+ for value_part in value_parts: |
+ if keyword.iskeyword(value_part): |
+ raise ValueError('%s is a reserved keyword.' % value_part) |
+ |
+ # If we have too many children, cut the list down to size. |
+ self._children = self._children[:len(value_parts)*2-1] |
+ |
+ # Update child nodes. |
+ for child, value_part in itertools.izip_longest( |
+ self._children[::2], value_parts): |
+ if child: |
+ # Modify existing children. This helps preserve comments and spaces. |
+ child.value = value_part |
+ else: |
+ # Add children as needed. |
+ self._children.append(snippet.TokenSnippet.Create(token.DOT, '.')) |
+ self._children.append( |
+ snippet.TokenSnippet.Create(token.NAME, value_part)) |
+ |
+ |
+class AsName(base_symbol.AnnotatedSymbol): |
+ @classmethod |
+ def Annotate(cls, symbol_type, children): |
+ if (symbol_type != symbol.dotted_as_name and |
+ symbol_type != symbol.import_as_name): |
+ return None |
+ return cls(symbol_type, children) |
+ |
+ @property |
+ def name(self): |
+ return self.children[0].value |
+ |
+ @name.setter |
+ def name(self, value): |
+ self.children[0].value = value |
+ |
+ @property |
+ def alias(self): |
+ if len(self.children) < 3: |
+ return None |
+ return self.children[2].value |
+ |
+ @alias.setter |
+ def alias(self, value): |
+ if keyword.iskeyword(value): |
+ raise ValueError('%s is a reserved keyword.' % value) |
+ |
+ if value: |
+ if len(self.children) < 3: |
+ # If we currently have no alias, add one. |
+ self.children.append( |
+ snippet.TokenSnippet.Create(token.NAME, 'as', (0, 1))) |
+ self.children.append( |
+ snippet.TokenSnippet.Create(token.NAME, value, (0, 1))) |
+ else: |
+ # We already have an alias. Just update the value. |
+ self.children[2].value = value |
+ else: |
+ # Removing the alias. Strip the "as foo". |
+ self.children = [self.children[0]] |
+ |
+ |
+class Import(base_symbol.AnnotatedSymbol): |
+ """An import statement. |
+ |
+ Example: |
+ import a.b.c as d |
+ from a.b import c as d |
+ |
+ In these examples, |
+ path == 'a.b.c' |
+ alias == 'd' |
+ root == 'a.b' (only for "from" imports) |
+ module == 'c' (only for "from" imports) |
+ name (read-only) == the name used by references to the module, which is the |
+ alias if there is one, the full module path in "full" imports, and the |
+ module name in "from" imports. |
+ """ |
+ @property |
+ def has_from(self): |
+ """Returns True iff the import statment is of the form "from x import y".""" |
+ raise NotImplementedError() |
+ |
+ @property |
+ def values(self): |
+ raise NotImplementedError() |
+ |
+ @property |
+ def paths(self): |
+ raise NotImplementedError() |
+ |
+ @property |
+ def aliases(self): |
+ raise NotImplementedError() |
+ |
+ @property |
+ def path(self): |
+ """The full dotted path of the module.""" |
+ raise NotImplementedError() |
+ |
+ @path.setter |
+ def path(self, value): |
+ raise NotImplementedError() |
+ |
+ @property |
+ def alias(self): |
+ """The alias, if the module is renamed with "as". None otherwise.""" |
+ raise NotImplementedError() |
+ |
+ @alias.setter |
+ def alias(self, value): |
+ raise NotImplementedError() |
+ |
+ @property |
+ def name(self): |
+ """The name used to reference this import's module.""" |
+ raise NotImplementedError() |
+ |
+ |
+class ImportName(Import): |
+ @classmethod |
+ def Annotate(cls, symbol_type, children): |
+ if symbol_type != symbol.import_stmt: |
+ return None |
+ if children[0].type != symbol.import_name: |
+ return None |
+ assert len(children) == 1 |
+ return cls(symbol_type, children[0].children) |
+ |
+ @property |
+ def has_from(self): |
+ return False |
+ |
+ @property |
+ def values(self): |
+ dotted_as_names = self.children[1] |
+ return tuple((dotted_as_name.name, dotted_as_name.alias) |
+ for dotted_as_name in dotted_as_names.children[::2]) |
+ |
+ @property |
+ def paths(self): |
+ return tuple(path for path, _ in self.values) |
+ |
+ @property |
+ def aliases(self): |
+ return tuple(alias for _, alias in self.values) |
+ |
+ @property |
+ def _dotted_as_name(self): |
+ dotted_as_names = self.children[1] |
+ if len(dotted_as_names.children) != 1: |
+ raise NotImplementedError( |
+ 'This method only works if the statement has one import.') |
+ return dotted_as_names.children[0] |
+ |
+ @property |
+ def path(self): |
+ return self._dotted_as_name.name |
+ |
+ @path.setter |
+ def path(self, value): # pylint: disable=arguments-differ |
+ self._dotted_as_name.name = value |
+ |
+ @property |
+ def alias(self): |
+ return self._dotted_as_name.alias |
+ |
+ @alias.setter |
+ def alias(self, value): # pylint: disable=arguments-differ |
+ self._dotted_as_name.alias = value |
+ |
+ @property |
+ def name(self): |
+ if self.alias: |
+ return self.alias |
+ else: |
+ return self.path |
+ |
+ |
+class ImportFrom(Import): |
+ @classmethod |
+ def Annotate(cls, symbol_type, children): |
+ if symbol_type != symbol.import_stmt: |
+ return None |
+ if children[0].type != symbol.import_from: |
+ return None |
+ assert len(children) == 1 |
+ return cls(symbol_type, children[0].children) |
+ |
+ @property |
+ def has_from(self): |
+ return True |
+ |
+ @property |
+ def values(self): |
+ try: |
+ import_as_names = self.FindChild(symbol.import_as_names) |
+ except ValueError: |
+ return (('*', None),) |
+ |
+ return tuple((import_as_name.name, import_as_name.alias) |
+ for import_as_name in import_as_names.children[::2]) |
+ |
+ @property |
+ def paths(self): |
+ module = self.module |
+ return tuple('.'.join((module, name)) for name, _ in self.values) |
+ |
+ @property |
+ def aliases(self): |
+ return tuple(alias for _, alias in self.values) |
+ |
+ @property |
+ def root(self): |
+ return self.FindChild(symbol.dotted_name).value |
+ |
+ @root.setter |
+ def root(self, value): |
+ self.FindChild(symbol.dotted_name).value = value |
+ |
+ @property |
+ def _import_as_name(self): |
+ try: |
+ import_as_names = self.FindChild(symbol.import_as_names) |
+ except ValueError: |
+ return None |
+ |
+ if len(import_as_names.children) != 1: |
+ raise NotImplementedError( |
+ 'This method only works if the statement has one import.') |
+ |
+ return import_as_names.children[0] |
+ |
+ @property |
+ def module(self): |
+ import_as_name = self._import_as_name |
+ if import_as_name: |
+ return import_as_name.name |
+ else: |
+ return '*' |
+ |
+ @module.setter |
+ def module(self, value): |
+ if keyword.iskeyword(value): |
+ raise ValueError('%s is a reserved keyword.' % value) |
+ |
+ import_as_name = self._import_as_name |
+ if value == '*': |
+ # TODO: Implement this. |
+ raise NotImplementedError() |
+ else: |
+ if import_as_name: |
+ import_as_name.name = value |
+ else: |
+ # TODO: Implement this. |
+ raise NotImplementedError() |
+ |
+ @property |
+ def path(self): |
+ return '.'.join((self.root, self.module)) |
+ |
+ @path.setter |
+ def path(self, value): # pylint: disable=arguments-differ |
+ self.root, _, self.module = value.rpartition('.') |
+ |
+ @property |
+ def alias(self): |
+ import_as_name = self._import_as_name |
+ if import_as_name: |
+ return import_as_name.alias |
+ else: |
+ return None |
+ |
+ @alias.setter |
+ def alias(self, value): # pylint: disable=arguments-differ |
+ import_as_name = self._import_as_name |
+ if not import_as_name: |
+ raise NotImplementedError('Cannot change alias for "import *".') |
+ import_as_name.alias = value |
+ |
+ @property |
+ def name(self): |
+ if self.alias: |
+ return self.alias |
+ else: |
+ return self.module |