| Index: third_party/recipe_engine/util.py
|
| diff --git a/third_party/recipe_engine/util.py b/third_party/recipe_engine/util.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3a72385a8eeeeb17766a643e3cbe9473bb6ed3e3
|
| --- /dev/null
|
| +++ b/third_party/recipe_engine/util.py
|
| @@ -0,0 +1,148 @@
|
| +# Copyright 2013-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 functools
|
| +import os
|
| +
|
| +from cStringIO import StringIO
|
| +
|
| +class RecipeAbort(Exception):
|
| + pass
|
| +
|
| +
|
| +class ModuleInjectionError(AttributeError):
|
| + pass
|
| +
|
| +
|
| +class ModuleInjectionSite(object):
|
| + def __init__(self, owner_module=None):
|
| + self.owner_module = owner_module
|
| +
|
| + def __getattr__(self, key):
|
| + if self.owner_module is None:
|
| + raise ModuleInjectionError(
|
| + "RecipeApi has no dependency %r. (Add it to DEPS?)" % (key,))
|
| + else:
|
| + raise ModuleInjectionError(
|
| + "Recipe Module %r has no dependency %r. (Add it to __init__.py:DEPS?)"
|
| + % (self.owner_module.name, key))
|
| +
|
| +
|
| +class Placeholder(object):
|
| + """Base class for json placeholders. Do not use directly."""
|
| + def __init__(self):
|
| + self.name_pieces = None
|
| +
|
| + @property
|
| + def backing_file(self): # pragma: no cover
|
| + """Return path to a temp file that holds or receives the data.
|
| +
|
| + Valid only after 'render' has been called.
|
| + """
|
| + raise NotImplementedError
|
| +
|
| + def render(self, test): # pragma: no cover
|
| + """Return [cmd items]*"""
|
| + raise NotImplementedError
|
| +
|
| + def result(self, presentation, test):
|
| + """Called after step completion.
|
| +
|
| + Args:
|
| + presentation (StepPresentation) - for the current step.
|
| + test (PlaceholderTestData) - test data for this placeholder.
|
| +
|
| + Returns value to add to step result.
|
| +
|
| + May optionally modify presentation as a side-effect.
|
| + """
|
| + pass
|
| +
|
| + @property
|
| + def name(self):
|
| + assert self.name_pieces
|
| + return "%s.%s" % self.name_pieces
|
| +
|
| +
|
| +def static_wraps(func):
|
| + wrapped_fn = func
|
| + if isinstance(func, staticmethod):
|
| + # __get__(obj) is the way to get the function contained in the staticmethod.
|
| + # python 2.7+ has a __func__ member, but previous to this the attribute
|
| + # doesn't exist. It doesn't matter what the obj is, as long as it's not
|
| + # None.
|
| + wrapped_fn = func.__get__(object)
|
| + return functools.wraps(wrapped_fn)
|
| +
|
| +
|
| +def static_call(obj, func, *args, **kwargs):
|
| + if isinstance(func, staticmethod):
|
| + return func.__get__(obj)(*args, **kwargs)
|
| + else:
|
| + return func(obj, *args, **kwargs)
|
| +
|
| +
|
| +def static_name(obj, func):
|
| + if isinstance(func, staticmethod):
|
| + return func.__get__(obj).__name__
|
| + else:
|
| + return func.__name__
|
| +
|
| +
|
| +def returns_placeholder(func):
|
| + @static_wraps(func)
|
| + def inner(self, *args, **kwargs):
|
| + ret = static_call(self, func, *args, **kwargs)
|
| + assert isinstance(ret, Placeholder)
|
| + ret.name_pieces = (self.name, static_name(self, func))
|
| + return ret
|
| + # prevent this placeholder-returning function from becoming a composite_step.
|
| + inner._non_step = True # pylint: disable=protected-access
|
| + return inner
|
| +
|
| +
|
| +def cached_unary(f):
|
| + """Decorator that caches/memoizes an unary function result.
|
| +
|
| + If the function throws an exception, the cache table will not be updated.
|
| + """
|
| + cache = {}
|
| + empty = object()
|
| +
|
| + @functools.wraps(f)
|
| + def cached_f(inp):
|
| + cache_entry = cache.get(inp, empty)
|
| + if cache_entry is empty:
|
| + cache_entry = f(inp)
|
| + cache[inp] = cache_entry
|
| + return cache_entry
|
| + return cached_f
|
| +
|
| +
|
| +def scan_directory(path, predicate):
|
| + """Recursively scans a directory and yields paths that match predicate."""
|
| + for root, _dirs, files in os.walk(path):
|
| + for file_name in (f for f in files if predicate(f)):
|
| + file_path = os.path.join(root, file_name)
|
| + yield file_path
|
| +
|
| +
|
| +class StringListIO(object):
|
| + def __init__(self):
|
| + self.lines = [StringIO()]
|
| +
|
| + def write(self, s):
|
| + while s:
|
| + i = s.find('\n')
|
| + if i == -1:
|
| + self.lines[-1].write(s)
|
| + break
|
| + self.lines[-1].write(s[:i])
|
| + self.lines[-1] = self.lines[-1].getvalue()
|
| + self.lines.append(StringIO())
|
| + s = s[i+1:]
|
| +
|
| + def close(self):
|
| + if not isinstance(self.lines[-1], basestring):
|
| + self.lines[-1] = self.lines[-1].getvalue()
|
|
|