| Index: recipe_engine/loader.py
|
| diff --git a/recipe_engine/loader.py b/recipe_engine/loader.py
|
| index f7df5739924f230e6fb963d0fc7db7f662ac9d31..65ee1518316bf24867d8ac01d9fb5181ae09404d 100644
|
| --- a/recipe_engine/loader.py
|
| +++ b/recipe_engine/loader.py
|
| @@ -3,6 +3,7 @@
|
| # that can be found in the LICENSE file.
|
|
|
| import collections
|
| +import functools
|
| import imp
|
| import inspect
|
| import os
|
| @@ -18,7 +19,10 @@ from .recipe_api import RecipeApiPlain, RecipeScriptApi
|
| from .recipe_api import _UnresolvedRequirement
|
| from .recipe_api import BoundProperty
|
| from .recipe_api import UndefinedPropertyException, PROPERTY_SENTINEL
|
| +from .recipe_api import StepFailure
|
| from .recipe_test_api import RecipeTestApi, DisabledTestData
|
| +from .types import StepDataAttributeError
|
| +import subprocess42
|
|
|
|
|
| class LoaderError(Exception):
|
| @@ -31,6 +35,14 @@ class NoSuchRecipe(LoaderError):
|
| super(NoSuchRecipe, self).__init__('No such recipe: %s' % recipe)
|
|
|
|
|
| +class RecipeException(Exception):
|
| + """Raised when an unhandeled exception is raised in a recipe."""
|
| + def __init__(self, inner_exception, traceback):
|
| + self.inner_exception = inner_exception
|
| + self.traceback = traceback
|
| + super(RecipeException, self).__init__(inner_exception.message)
|
| +
|
| +
|
| class RecipeScript(object):
|
| """Holds dict of an evaluated recipe script."""
|
|
|
| @@ -76,9 +88,29 @@ class RecipeScript(object):
|
| """
|
| Run this recipe, with the given api and property arguments.
|
| Check the return value, if we have a RETURN_SCHEMA.
|
| +
|
| + Raises:
|
| + RecipeException if an unhandled exception happened in the recipe.
|
| + TypeError if recipe did not return an expected result.
|
| """
|
| +
|
| + orig_run_steps = self.run_steps
|
| +
|
| + def run_steps(*args, **kwargs):
|
| + try:
|
| + return orig_run_steps(*args, **kwargs)
|
| + except (
|
| + StepFailure,
|
| + StepDataAttributeError,
|
| + subprocess42.TimeoutExpired):
|
| + raise
|
| + except Exception as ex:
|
| + raise RecipeException(ex, sys.exc_info()[2])
|
| +
|
| recipe_result = invoke_with_properties(
|
| - self.run_steps, properties, self.PROPERTIES, api=api)
|
| + run_steps, properties, self.PROPERTIES,
|
| + inspectable_obj=orig_run_steps,
|
| + api=api)
|
|
|
| if self.RETURN_SCHEMA:
|
| if not recipe_result:
|
| @@ -546,7 +578,7 @@ def _invoke_with_properties(callable_obj, all_props, prop_defs, arg_names,
|
|
|
|
|
| def invoke_with_properties(callable_obj, all_props, prop_defs,
|
| - **additional_args):
|
| + inspectable_obj=None, **additional_args):
|
| """
|
| Invokes callable with filtered, type-checked properties.
|
|
|
| @@ -558,6 +590,8 @@ def invoke_with_properties(callable_obj, all_props, prop_defs,
|
| defined in the system.
|
| prop_defs: A dictionary of property name to property definitions
|
| (BoundProperty) for this callable.
|
| + inspectable_obj: a function to inspect instead of callable_obj.
|
| + Useful when callback_obj is wrapped.
|
| additional_args: kwargs to pass through to the callable.
|
| Note that the names of the arguments can correspond to
|
| positional arguments as well.
|
| @@ -568,18 +602,20 @@ def invoke_with_properties(callable_obj, all_props, prop_defs,
|
| """
|
| # Check that we got passed BoundProperties, and not Properties
|
|
|
| + inspectable_obj = inspectable_obj or callable_obj
|
| +
|
|
|
| # To detect when they didn't specify a property that they have as a
|
| # function argument, list the arguments, through inspection,
|
| # and then comparing this list to the provided properties. We use a list
|
| # instead of a dict because getargspec returns a list which we would have to
|
| # convert to a dictionary, and the benefit of the dictionary is pretty small.
|
| - if inspect.isclass(callable_obj):
|
| - arg_names = inspect.getargspec(callable_obj.__init__).args
|
| + if inspect.isclass(inspectable_obj):
|
| + arg_names = inspect.getargspec(inspectable_obj.__init__).args
|
|
|
| arg_names.pop(0)
|
| else:
|
| - arg_names = inspect.getargspec(callable_obj).args
|
| + arg_names = inspect.getargspec(inspectable_obj).args
|
| return _invoke_with_properties(callable_obj, all_props, prop_defs, arg_names,
|
| **additional_args)
|
|
|
|
|