Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2013-2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2013-2015 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 collections | 5 import collections |
| 6 import contextlib | 6 import contextlib |
| 7 import imp | 7 import imp |
| 8 import inspect | 8 import inspect |
| 9 import os | 9 import os |
| 10 import sys | 10 import sys |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 124 # TODO(luqui): Forbid depending on a module from a (locally) undeclared | 124 # TODO(luqui): Forbid depending on a module from a (locally) undeclared |
| 125 # dependency. | 125 # dependency. |
| 126 def __init__(self, package, module, local_name, universe): | 126 def __init__(self, package, module, local_name, universe): |
| 127 mod_path = ( | 127 mod_path = ( |
| 128 universe.package_deps.get_package(package).module_path(module)) | 128 universe.package_deps.get_package(package).module_path(module)) |
| 129 super(PackageDependency, self).__init__( | 129 super(PackageDependency, self).__init__( |
| 130 mod_path, local_name, universe=universe) | 130 mod_path, local_name, universe=universe) |
| 131 | 131 |
| 132 | 132 |
| 133 class RecipeUniverse(object): | 133 class RecipeUniverse(object): |
| 134 def __init__(self, package_deps): | 134 def __init__(self, package_deps, config_file): |
| 135 self._loaded = {} | 135 self._loaded = {} |
| 136 self._package_deps = package_deps | 136 self._package_deps = package_deps |
| 137 self._config_file = config_file | |
|
luqui
2015/11/16 20:47:59
I don't see where ._config_file is used.
martiniss
2015/11/18 01:00:00
Used for something else. Not needed, so I deleted.
| |
| 138 | |
| 139 def copy(self): | |
| 140 return RecipeUniverse(self._package_deps, self._config_file) | |
| 137 | 141 |
| 138 @property | 142 @property |
| 139 def module_dirs(self): | 143 def module_dirs(self): |
| 140 return self._package_deps.all_module_dirs | 144 return self._package_deps.all_module_dirs |
| 141 | 145 |
| 142 @property | 146 @property |
| 143 def recipe_dirs(self): | 147 def recipe_dirs(self): |
| 144 return self._package_deps.all_recipe_dirs | 148 return self._package_deps.all_recipe_dirs |
| 145 | 149 |
| 146 @property | 150 @property |
| (...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 399 self._instances = {} | 403 self._instances = {} |
| 400 | 404 |
| 401 def instantiate(self, mod): | 405 def instantiate(self, mod): |
| 402 if mod in self._instances: | 406 if mod in self._instances: |
| 403 return self._instances[mod] | 407 return self._instances[mod] |
| 404 deps_dict = { name: self.instantiate(dep) | 408 deps_dict = { name: self.instantiate(dep) |
| 405 for name, dep in mod.LOADED_DEPS.iteritems() } | 409 for name, dep in mod.LOADED_DEPS.iteritems() } |
| 406 self._instances[mod] = self._instantiator(mod, deps_dict) | 410 self._instances[mod] = self._instantiator(mod, deps_dict) |
| 407 return self._instances[mod] | 411 return self._instances[mod] |
| 408 | 412 |
| 413 def _invoke_with_properties(callable_obj, all_props, prop_defs, arg_names, | |
| 414 **additional_args): | |
| 415 for name, prop in prop_defs.items(): | |
| 416 if not isinstance(prop, BoundProperty): | |
| 417 raise ValueError( | |
| 418 "You tried to invoke {} with an unbound Property {} named {}".format( | |
| 419 callable, prop, name)) | |
| 420 | |
| 421 props = [] | |
| 422 for arg in arg_names: | |
| 423 if arg in additional_args: | |
| 424 props.append(additional_args.pop(arg)) | |
| 425 continue | |
| 426 | |
| 427 if arg not in prop_defs: | |
| 428 raise UndefinedPropertyException( | |
| 429 "Missing property definition for '{}'.".format(arg)) | |
| 430 | |
| 431 prop = prop_defs[arg] | |
| 432 props.append(prop.interpret(all_props.get( | |
| 433 prop.param_name, PROPERTY_SENTINEL))) | |
| 434 | |
| 435 return callable_obj(*props, **additional_args) | |
| 436 | |
| 409 def invoke_with_properties(callable_obj, all_props, prop_defs, | 437 def invoke_with_properties(callable_obj, all_props, prop_defs, |
| 410 **additional_args): | 438 **additional_args): |
| 411 """ | 439 """ |
| 412 Invokes callable with filtered, type-checked properties. | 440 Invokes callable with filtered, type-checked properties. |
| 413 | 441 |
| 414 Args: | 442 Args: |
| 415 callable_obj: The function to call, or class to instantiate. | 443 callable_obj: The function to call, or class to instantiate. |
| 416 This supports passing in either RunSteps, or a recipe module, | 444 This supports passing in either RunSteps, or a recipe module, |
| 417 which is a class. | 445 which is a class. |
| 418 all_props: A dictionary containing all the properties (instances of BoundPro perty) | 446 all_props: A dictionary containing all the properties (instances of BoundPro perty) |
| 419 currently defined in the system. | 447 currently defined in the system. |
| 420 prop_defs: A dictionary of name to property definitions for this callable. | 448 prop_defs: A dictionary of name to property definitions for this callable. |
| 421 additional_args: kwargs to pass through to the callable. | 449 additional_args: kwargs to pass through to the callable. |
| 422 Note that the names of the arguments can correspond to | 450 Note that the names of the arguments can correspond to |
| 423 positional arguments as well. | 451 positional arguments as well. |
| 424 | 452 |
| 425 Returns: | 453 Returns: |
| 426 The result of calling callable with the filtered properties | 454 The result of calling callable with the filtered properties |
| 427 and additional arguments. | 455 and additional arguments. |
| 428 """ | 456 """ |
| 429 # Check that we got passed BoundProperties, and not Properties | 457 # Check that we got passed BoundProperties, and not Properties |
| 430 | 458 |
| 431 for name, prop in prop_defs.items(): | |
| 432 if not isinstance(prop, BoundProperty): | |
| 433 raise ValueError( | |
| 434 "You tried to invoke {} with an unbound Property {} named {}".format( | |
| 435 callable, prop, name)) | |
| 436 | 459 |
| 437 # To detect when they didn't specify a property that they have as a | 460 # To detect when they didn't specify a property that they have as a |
| 438 # function argument, list the arguments, through inspection, | 461 # function argument, list the arguments, through inspection, |
| 439 # and then comparing this list to the provided properties. We use a list | 462 # and then comparing this list to the provided properties. We use a list |
| 440 # instead of a dict because getargspec returns a list which we would have to | 463 # instead of a dict because getargspec returns a list which we would have to |
| 441 # convert to a dictionary, and the benefit of the dictionary is pretty small. | 464 # convert to a dictionary, and the benefit of the dictionary is pretty small. |
| 442 props = [] | |
| 443 if inspect.isclass(callable_obj): | 465 if inspect.isclass(callable_obj): |
| 444 arg_names = inspect.getargspec(callable_obj.__init__).args | 466 arg_names = inspect.getargspec(callable_obj.__init__).args |
| 445 | 467 |
| 446 arg_names.pop(0) | 468 arg_names.pop(0) |
| 447 else: | 469 else: |
| 448 arg_names = inspect.getargspec(callable_obj).args | 470 arg_names = inspect.getargspec(callable_obj).args |
| 471 return _invoke_with_properties(callable_obj, all_props, prop_defs, arg_names, | |
| 472 **additional_args) | |
| 449 | 473 |
| 450 for arg in arg_names: | |
| 451 if arg in additional_args: | |
| 452 props.append(additional_args.pop(arg)) | |
| 453 continue | |
| 454 | |
| 455 if arg not in prop_defs: | |
| 456 raise UndefinedPropertyException( | |
| 457 "Missing property definition for '{}'.".format(arg)) | |
| 458 | |
| 459 prop = prop_defs[arg] | |
| 460 props.append(prop.interpret(all_props.get( | |
| 461 prop.param_name, PROPERTY_SENTINEL))) | |
| 462 | |
| 463 return callable_obj(*props, **additional_args) | |
| 464 | 474 |
| 465 def create_recipe_api(toplevel_deps, engine, test_data=DisabledTestData()): | 475 def create_recipe_api(toplevel_deps, engine, test_data=DisabledTestData()): |
| 466 def instantiator(mod, deps): | 476 def instantiator(mod, deps): |
| 467 kwargs = { | 477 kwargs = { |
| 468 'module': mod, | 478 'module': mod, |
| 469 'engine': engine, | 479 'engine': engine, |
| 470 # TODO(luqui): test_data will need to use canonical unique names. | 480 # TODO(luqui): test_data will need to use canonical unique names. |
| 471 'test_data': test_data.get_module_test_data(mod.NAME) | 481 'test_data': test_data.get_module_test_data(mod.NAME) |
| 472 } | 482 } |
| 473 prop_defs = mod.PROPERTIES | 483 prop_defs = mod.PROPERTIES |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 496 return modapi | 506 return modapi |
| 497 | 507 |
| 498 mapper = DependencyMapper(instantiator) | 508 mapper = DependencyMapper(instantiator) |
| 499 api = RecipeTestApi(module=None) | 509 api = RecipeTestApi(module=None) |
| 500 for k,v in toplevel_deps.iteritems(): | 510 for k,v in toplevel_deps.iteritems(): |
| 501 setattr(api, k, mapper.instantiate(v)) | 511 setattr(api, k, mapper.instantiate(v)) |
| 502 return api | 512 return api |
| 503 | 513 |
| 504 | 514 |
| 505 | 515 |
| OLD | NEW |