Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Side by Side Diff: recipe_engine/loader.py

Issue 1421843006: Add simple depends_on API. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/recipes-py@master
Patch Set: Made it do properties and multi Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | recipe_engine/recipe_api.py » ('j') | recipe_engine/recipe_api.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | recipe_engine/recipe_api.py » ('j') | recipe_engine/recipe_api.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698