Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import abc | |
| 5 import re | 6 import re |
| 6 | 7 |
| 8 from collections import namedtuple | |
| 9 | |
| 7 from infra.libs import infra_types | 10 from infra.libs import infra_types |
| 8 | 11 |
| 9 def ResetTostringFns(): | 12 def ResetTostringFns(): |
| 10 RecipeConfigType._TOSTRING_MAP.clear() # pylint: disable=W0212 | 13 RecipeConfigType._TOSTRING_MAP.clear() # pylint: disable=W0212 |
| 11 | 14 |
| 12 | 15 |
| 13 def json_fixup(obj): | 16 def json_fixup(obj): |
| 14 if isinstance(obj, RecipeConfigType): | 17 if isinstance(obj, RecipeConfigType): |
| 15 return str(obj) | 18 return str(obj) |
| 16 thawed = infra_types.thaw(obj) | 19 thawed = infra_types.thaw(obj) |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 53 'tostring_fn already installed for %s' % cls) | 56 'tostring_fn already installed for %s' % cls) |
| 54 cls._TOSTRING_MAP[cls.__name__] = new_tostring_fn | 57 cls._TOSTRING_MAP[cls.__name__] = new_tostring_fn |
| 55 | 58 |
| 56 def default_tostring_fn(self): | 59 def default_tostring_fn(self): |
| 57 raise NotImplementedError | 60 raise NotImplementedError |
| 58 | 61 |
| 59 def __str__(self): | 62 def __str__(self): |
| 60 return self.tostring_fn(self) # pylint: disable=not-callable | 63 return self.tostring_fn(self) # pylint: disable=not-callable |
| 61 | 64 |
| 62 | 65 |
| 66 class BasePath(object): | |
| 67 __metaclass__ = abc.ABCMeta | |
| 68 | |
| 69 | |
| 70 class NamedBasePath(BasePath, namedtuple('NamedBasePath', 'name')): | |
| 71 # Restrict basenames to '[ALL_CAPS]'. This will help catch | |
| 72 # errors if someone attempts to provide an actual string path '/some/example' | |
| 73 # as the 'base'. | |
| 74 BASE_RE = re.compile(r'\[([A-Z][A-Z_]*)\]') | |
| 75 | |
| 76 @staticmethod | |
| 77 def parse(base): | |
| 78 base_match = NamedBasePath.BASE_RE.match(base) | |
| 79 assert base_match, 'Base should be [ALL_CAPS], got %r' % base | |
| 80 return NamedBasePath(base_match.group(1).lower()) | |
| 81 | |
| 82 def __repr__(self): | |
| 83 return '[%s]' % self.name.upper() | |
| 84 | |
| 85 | |
| 86 class ModuleBasePath(BasePath, namedtuple('ModuleBasePath', ('module',))): | |
|
iannucci
2015/03/07 02:55:02
I think this r an oops. Let's stick with line 70 s
luqui
2015/03/09 18:49:28
Done.
| |
| 87 def __repr__(self): | |
| 88 name = re.sub(r'^RECIPE_MODULES\.', '', self.module.__name__) | |
|
iannucci
2015/03/07 02:55:03
let's pull the regex out too, and add a comment as
luqui
2015/03/09 18:49:28
Done.
| |
| 89 return 'MODULE[%s]' % name | |
|
iannucci
2015/03/07 02:55:02
let's do "RECIPE_MODULE" instead, since MODULE is
luqui
2015/03/09 18:49:28
Done.
| |
| 90 | |
| 91 | |
| 92 class ConcreteBasePath(BasePath, namedtuple('ConcreteBasePath', 'path')): | |
| 93 def __repr__(self): | |
| 94 return self.path | |
| 95 | |
| 96 | |
| 63 class Path(RecipeConfigType): | 97 class Path(RecipeConfigType): |
| 64 """Represents a path which is relative to a semantically-named base. | 98 """Represents a path which is relative to a semantically-named base. |
| 65 | 99 |
| 66 Because there's a lot of platform (separator style) and runtime-specific | 100 Because there's a lot of platform (separator style) and runtime-specific |
| 67 context (working directory) which goes into assembling a final OS-specific | 101 context (working directory) which goes into assembling a final OS-specific |
| 68 absolute path, we only store three context-free attributes in this Path | 102 absolute path, we only store three context-free attributes in this Path |
| 69 object. | 103 object. |
| 70 """ | 104 """ |
| 71 # Restrict basenames to '[ALL_CAPS]'. This will help catch | |
| 72 # errors if someone attempts to provide an actual string path '/some/example' | |
| 73 # as the 'base'. | |
| 74 BASE_RE = re.compile(r'\[([A-Z][A-Z_]*)\]') | |
| 75 | 105 |
| 76 def __init__(self, base, *pieces, **kwargs): | 106 def __init__(self, base, *pieces, **kwargs): |
| 77 """Creates a Path | 107 """Creates a Path |
| 78 | 108 |
| 79 Args: | 109 Args: |
| 80 base (str) - The 'name' of a base path, to be filled in at recipe runtime | 110 base (str) - The 'name' of a base path, to be filled in at recipe runtime |
| 81 by the 'path' recipe module. | 111 by the 'path' recipe module. |
| 82 pieces (tuple(str)) - The components of the path relative to base. These | 112 pieces (tuple(str)) - The components of the path relative to base. These |
| 83 pieces must be non-relative (i.e. no '..' or '.', etc. as a piece). | 113 pieces must be non-relative (i.e. no '..' or '.', etc. as a piece). |
| 84 | 114 |
| 85 Kwargs: | 115 Kwargs: |
| 86 platform_ext (dict(str, str)) - A mapping from platform name (as defined | 116 platform_ext (dict(str, str)) - A mapping from platform name (as defined |
| 87 by the 'platform' module), to a suffix for the path. | 117 by the 'platform' module), to a suffix for the path. |
| 88 _bypass (bool) - Bypass the type checking and use |base| directly. Don't | |
| 89 use this outside of the 'path' module or this class. | |
| 90 """ | 118 """ |
| 91 super(Path, self).__init__() | 119 super(Path, self).__init__() |
| 92 assert all(isinstance(x, basestring) for x in pieces), pieces | 120 assert all(isinstance(x, basestring) for x in pieces), pieces |
| 93 assert not any(x in ('..', '.', '/', '\\') for x in pieces) | 121 assert not any(x in ('..', '.', '/', '\\') for x in pieces) |
| 94 self.pieces = pieces | 122 self.pieces = pieces |
| 95 | 123 |
| 96 if kwargs.get('_bypass'): | 124 if isinstance(base, BasePath): |
| 97 self.base = base | 125 self.base = base |
| 126 elif isinstance(base, basestring): | |
| 127 self.base = NamedBasePath.parse(base) | |
| 98 else: | 128 else: |
| 99 base_match = self.BASE_RE.match(base) | 129 raise ValueError('%s is not a valid base path' % base) |
| 100 assert base_match, 'Base should be [ALL_CAPS], got %r' % base | |
| 101 self.base = base_match.group(1).lower() | |
| 102 | 130 |
| 103 self.platform_ext = kwargs.get('platform_ext', {}) | 131 self.platform_ext = kwargs.get('platform_ext', {}) |
| 104 | 132 |
| 105 def __eq__(self, other): | 133 def __eq__(self, other): |
| 106 return (self.base == other.base and | 134 return (self.base == other.base and |
| 107 self.pieces == other.pieces and | 135 self.pieces == other.pieces and |
| 108 self.platform_ext == other.platform_ext) | 136 self.platform_ext == other.platform_ext) |
| 109 | 137 |
| 110 def __ne__(self, other): | 138 def __ne__(self, other): |
| 111 return not self.base == other | 139 return not self.base == other |
| 112 | 140 |
| 113 def join(self, *pieces, **kwargs): | 141 def join(self, *pieces, **kwargs): |
| 114 kwargs.setdefault('platform_ext', self.platform_ext) | 142 kwargs.setdefault('platform_ext', self.platform_ext) |
| 115 kwargs['_bypass'] = True | |
| 116 return Path(self.base, *filter(bool, self.pieces + pieces), **kwargs) | 143 return Path(self.base, *filter(bool, self.pieces + pieces), **kwargs) |
| 117 | 144 |
| 118 def is_parent_of(self, child): | 145 def is_parent_of(self, child): |
| 119 """True if |child| is in a subdirectory of this path.""" | 146 """True if |child| is in a subdirectory of this path.""" |
| 120 # Assumes base paths are not nested. | 147 # Assumes base paths are not nested. |
| 121 # TODO(vadimsh): We should not rely on this assumption. | 148 # TODO(vadimsh): We should not rely on this assumption. |
| 122 if self.base != child.base: | 149 if self.base != child.base: |
| 123 return False | 150 return False |
| 124 # A path is not a parent to itself. | 151 # A path is not a parent to itself. |
| 125 if len(self.pieces) >= len(child.pieces): | 152 if len(self.pieces) >= len(child.pieces): |
| 126 return False | 153 return False |
| 127 return child.pieces[:len(self.pieces)] == self.pieces | 154 return child.pieces[:len(self.pieces)] == self.pieces |
| 128 | 155 |
| 129 def default_tostring_fn(self): | 156 def default_tostring_fn(self): |
| 130 suffix = '' | 157 suffix = '' |
| 131 if self.platform_ext: | 158 if self.platform_ext: |
| 132 suffix = ', platform_ext=%r' % (self.platform_ext,) | 159 suffix = ', platform_ext=%r' % (self.platform_ext,) |
| 133 pieces = '' | 160 pieces = '' |
| 134 if self.pieces: | 161 if self.pieces: |
| 135 pieces = ', ' + (', '.join(map(repr, self.pieces))) | 162 pieces = ', ' + (', '.join(map(repr, self.pieces))) |
| 136 return 'Path(\'[%s]\'%s%s)' % (self.base.upper(), pieces, suffix) | 163 return 'Path(\'%s\'%s%s)' % (self.base, pieces, suffix) |
| OLD | NEW |