| 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')): |
| 87 # All recipe modules are in a magic RECIPE_MODULES package. Remove it |
| 88 # before rendering MODULE[_] form. |
| 89 MODULE_PREFIX_RE = r'^RECIPE_MODULES\.' |
| 90 |
| 91 def __repr__(self): |
| 92 name = re.sub(self.MODULE_PREFIX_RE, '', self.module.__name__) |
| 93 return 'RECIPE_MODULE[%s]' % name |
| 94 |
| 95 |
| 63 class Path(RecipeConfigType): | 96 class Path(RecipeConfigType): |
| 64 """Represents a path which is relative to a semantically-named base. | 97 """Represents a path which is relative to a semantically-named base. |
| 65 | 98 |
| 66 Because there's a lot of platform (separator style) and runtime-specific | 99 Because there's a lot of platform (separator style) and runtime-specific |
| 67 context (working directory) which goes into assembling a final OS-specific | 100 context (working directory) which goes into assembling a final OS-specific |
| 68 absolute path, we only store three context-free attributes in this Path | 101 absolute path, we only store three context-free attributes in this Path |
| 69 object. | 102 object. |
| 70 """ | 103 """ |
| 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 | 104 |
| 76 def __init__(self, base, *pieces, **kwargs): | 105 def __init__(self, base, *pieces, **kwargs): |
| 77 """Creates a Path | 106 """Creates a Path |
| 78 | 107 |
| 79 Args: | 108 Args: |
| 80 base (str) - The 'name' of a base path, to be filled in at recipe runtime | 109 base (str) - The 'name' of a base path, to be filled in at recipe runtime |
| 81 by the 'path' recipe module. | 110 by the 'path' recipe module. |
| 82 pieces (tuple(str)) - The components of the path relative to base. These | 111 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). | 112 pieces must be non-relative (i.e. no '..' or '.', etc. as a piece). |
| 84 | 113 |
| 85 Kwargs: | 114 Kwargs: |
| 86 platform_ext (dict(str, str)) - A mapping from platform name (as defined | 115 platform_ext (dict(str, str)) - A mapping from platform name (as defined |
| 87 by the 'platform' module), to a suffix for the path. | 116 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 """ | 117 """ |
| 91 super(Path, self).__init__() | 118 super(Path, self).__init__() |
| 92 assert all(isinstance(x, basestring) for x in pieces), pieces | 119 assert all(isinstance(x, basestring) for x in pieces), pieces |
| 93 assert not any(x in ('..', '.', '/', '\\') for x in pieces) | 120 assert not any(x in ('..', '.', '/', '\\') for x in pieces) |
| 94 self.pieces = pieces | 121 self.pieces = pieces |
| 95 | 122 |
| 96 if kwargs.get('_bypass'): | 123 if isinstance(base, BasePath): |
| 97 self.base = base | 124 self.base = base |
| 125 elif isinstance(base, basestring): |
| 126 self.base = NamedBasePath.parse(base) |
| 98 else: | 127 else: |
| 99 base_match = self.BASE_RE.match(base) | 128 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 | 129 |
| 103 self.platform_ext = kwargs.get('platform_ext', {}) | 130 self.platform_ext = kwargs.get('platform_ext', {}) |
| 104 | 131 |
| 105 def __eq__(self, other): | 132 def __eq__(self, other): |
| 106 return (self.base == other.base and | 133 return (self.base == other.base and |
| 107 self.pieces == other.pieces and | 134 self.pieces == other.pieces and |
| 108 self.platform_ext == other.platform_ext) | 135 self.platform_ext == other.platform_ext) |
| 109 | 136 |
| 110 def __ne__(self, other): | 137 def __ne__(self, other): |
| 111 return not self.base == other | 138 return not self.base == other |
| 112 | 139 |
| 113 def join(self, *pieces, **kwargs): | 140 def join(self, *pieces, **kwargs): |
| 114 kwargs.setdefault('platform_ext', self.platform_ext) | 141 kwargs.setdefault('platform_ext', self.platform_ext) |
| 115 kwargs['_bypass'] = True | |
| 116 return Path(self.base, *filter(bool, self.pieces + pieces), **kwargs) | 142 return Path(self.base, *filter(bool, self.pieces + pieces), **kwargs) |
| 117 | 143 |
| 118 def is_parent_of(self, child): | 144 def is_parent_of(self, child): |
| 119 """True if |child| is in a subdirectory of this path.""" | 145 """True if |child| is in a subdirectory of this path.""" |
| 120 # Assumes base paths are not nested. | 146 # Assumes base paths are not nested. |
| 121 # TODO(vadimsh): We should not rely on this assumption. | 147 # TODO(vadimsh): We should not rely on this assumption. |
| 122 if self.base != child.base: | 148 if self.base != child.base: |
| 123 return False | 149 return False |
| 124 # A path is not a parent to itself. | 150 # A path is not a parent to itself. |
| 125 if len(self.pieces) >= len(child.pieces): | 151 if len(self.pieces) >= len(child.pieces): |
| 126 return False | 152 return False |
| 127 return child.pieces[:len(self.pieces)] == self.pieces | 153 return child.pieces[:len(self.pieces)] == self.pieces |
| 128 | 154 |
| 129 def default_tostring_fn(self): | 155 def default_tostring_fn(self): |
| 130 suffix = '' | 156 suffix = '' |
| 131 if self.platform_ext: | 157 if self.platform_ext: |
| 132 suffix = ', platform_ext=%r' % (self.platform_ext,) | 158 suffix = ', platform_ext=%r' % (self.platform_ext,) |
| 133 pieces = '' | 159 pieces = '' |
| 134 if self.pieces: | 160 if self.pieces: |
| 135 pieces = ', ' + (', '.join(map(repr, self.pieces))) | 161 pieces = ', ' + (', '.join(map(repr, self.pieces))) |
| 136 return 'Path(\'[%s]\'%s%s)' % (self.base.upper(), pieces, suffix) | 162 return 'Path(\'%s\'%s%s)' % (self.base, pieces, suffix) |
| OLD | NEW |