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 |