Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2016 The LUCI Authors. All rights reserved. | 1 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import collections | 5 import collections |
| 6 import imp | 6 import imp |
| 7 import inspect | 7 import inspect |
| 8 import os | 8 import os |
| 9 import sys | 9 import sys |
| 10 | 10 |
| 11 from . import env | 11 from . import env |
| 12 | 12 |
| 13 from .config import ConfigContext, ConfigGroupSchema | 13 from .config import ConfigContext, ConfigGroupSchema |
| 14 from .config_types import Path, ModuleBasePath, PackageRepoBasePath | 14 from .config_types import Path, ModuleBasePath, PackageRepoBasePath |
| 15 from .config_types import RecipeScriptBasePath | 15 from .config_types import RecipeScriptBasePath |
| 16 from .config_types import RECIPE_MODULE_PREFIX | 16 from .config_types import RECIPE_MODULE_PREFIX |
| 17 from .recipe_api import RecipeApiPlain, RecipeScriptApi | 17 from .recipe_api import RecipeApi, RecipeApiPlain, RecipeScriptApi |
| 18 from .recipe_api import _UnresolvedRequirement | 18 from .recipe_api import _UnresolvedRequirement |
| 19 from .recipe_api import BoundProperty | 19 from .recipe_api import BoundProperty |
| 20 from .recipe_api import UndefinedPropertyException, PROPERTY_SENTINEL | 20 from .recipe_api import UndefinedPropertyException, PROPERTY_SENTINEL |
| 21 from .recipe_test_api import RecipeTestApi, DisabledTestData | 21 from .recipe_test_api import RecipeTestApi, DisabledTestData |
| 22 | 22 |
| 23 | 23 |
| 24 class LoaderError(Exception): | 24 class LoaderError(Exception): |
| 25 """Raised when something goes wrong loading recipes or modules.""" | 25 """Raised when something goes wrong loading recipes or modules.""" |
| 26 | 26 |
| 27 | 27 |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 99 | 99 |
| 100 recipe_globals = {} | 100 recipe_globals = {} |
| 101 recipe_globals['__file__'] = script_path | 101 recipe_globals['__file__'] = script_path |
| 102 | 102 |
| 103 with env.temp_sys_path(): | 103 with env.temp_sys_path(): |
| 104 execfile(script_path, recipe_globals) | 104 execfile(script_path, recipe_globals) |
| 105 | 105 |
| 106 recipe_globals['LOADED_DEPS'] = universe_view.deps_from_spec( | 106 recipe_globals['LOADED_DEPS'] = universe_view.deps_from_spec( |
| 107 recipe_globals.get('DEPS', [])) | 107 recipe_globals.get('DEPS', [])) |
| 108 | 108 |
| 109 if 'RunSteps' not in recipe_globals: | |
| 110 raise Exception('Missing or misspelled RunSteps function.') | |
|
dnj
2017/05/06 02:37:14
Let's make this "MalformedRecipeError" or somethin
iannucci
2017/05/06 02:54:55
Done, added test too :)
| |
| 111 | |
| 112 if 'GenTests' not in recipe_globals: | |
| 113 raise Exception('Missing or misspelled GenTests function.') | |
| 114 | |
| 109 return cls(name, recipe_globals, universe_view.package.name, script_path) | 115 return cls(name, recipe_globals, universe_view.package.name, script_path) |
| 110 | 116 |
| 111 | 117 |
| 112 class RecipeUniverse(object): | 118 class RecipeUniverse(object): |
| 113 def __init__(self, package_deps, config_file): | 119 def __init__(self, package_deps, config_file): |
| 114 self._loaded = {} | 120 self._loaded = {} |
| 115 self._package_deps = package_deps | 121 self._package_deps = package_deps |
| 116 self._config_file = config_file | 122 self._config_file = config_file |
| 117 | 123 |
| 118 @property | 124 @property |
| (...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 447 assert not submod.CONFIG_CTX, ( | 453 assert not submod.CONFIG_CTX, ( |
| 448 'More than one configuration context: %s, %s' % | 454 'More than one configuration context: %s, %s' % |
| 449 (submod.config, submod.CONFIG_CTX)) | 455 (submod.config, submod.CONFIG_CTX)) |
| 450 submod.CONFIG_CTX = v | 456 submod.CONFIG_CTX = v |
| 451 assert submod.CONFIG_CTX, 'Config file, but no config context?' | 457 assert submod.CONFIG_CTX, 'Config file, but no config context?' |
| 452 | 458 |
| 453 # Identify the RecipeApiPlain subclass as this module's API. | 459 # Identify the RecipeApiPlain subclass as this module's API. |
| 454 submod.API = getattr(submod, 'API', None) | 460 submod.API = getattr(submod, 'API', None) |
| 455 assert hasattr(submod, 'api'), '%s is missing api.py' % (name,) | 461 assert hasattr(submod, 'api'), '%s is missing api.py' % (name,) |
| 456 for v in submod.api.__dict__.itervalues(): | 462 for v in submod.api.__dict__.itervalues(): |
| 463 # If the recipe has literally imported the RecipeApi, we don't wan't | |
| 464 # to consider that to be the real RecipeApi :) | |
| 465 if v is RecipeApiPlain or v is RecipeApi: | |
| 466 continue | |
| 457 if inspect.isclass(v) and issubclass(v, RecipeApiPlain): | 467 if inspect.isclass(v) and issubclass(v, RecipeApiPlain): |
| 458 assert not submod.API, ( | 468 assert not submod.API, ( |
| 459 '%s has more than one RecipeApi subclass: %s, %s' % ( | 469 '%s has more than one RecipeApi subclass: %s, %s' % ( |
| 460 name, v, submod.API)) | 470 name, v, submod.API)) |
| 461 submod.API = v | 471 submod.API = v |
| 462 assert submod.API, 'Submodule has no api? %s' % (submod) | 472 assert submod.API, 'Submodule has no api? %s' % (submod) |
| 463 | 473 |
| 464 # Identify the (optional) RecipeTestApi subclass as this module's test API. | 474 # Identify the (optional) RecipeTestApi subclass as this module's test API. |
| 465 submod.TEST_API = getattr(submod, 'TEST_API', None) | 475 submod.TEST_API = getattr(submod, 'TEST_API', None) |
| 466 if hasattr(submod, 'test_api'): | 476 if hasattr(submod, 'test_api'): |
| 467 for v in submod.test_api.__dict__.itervalues(): | 477 for v in submod.test_api.__dict__.itervalues(): |
| 478 # If the recipe has literally imported the RecipeTestApi, we don't wan't | |
| 479 # to consider that to be the real RecipeTestApi :) | |
| 480 if v is RecipeTestApi: | |
| 481 continue | |
| 468 if inspect.isclass(v) and issubclass(v, RecipeTestApi): | 482 if inspect.isclass(v) and issubclass(v, RecipeTestApi): |
| 469 assert not submod.TEST_API, ( | 483 assert not submod.TEST_API, ( |
| 470 'More than one TestApi subclass: %s' % submod.api) | 484 'More than one TestApi subclass: %s' % submod.api) |
| 471 submod.TEST_API = v | 485 submod.TEST_API = v |
| 472 assert submod.API, ( | 486 assert submod.API, ( |
| 473 'Submodule has test_api.py but no TestApi subclass? %s' | 487 'Submodule has test_api.py but no TestApi subclass? %s' |
| 474 % (submod) | 488 % (submod) |
| 475 ) | 489 ) |
| 476 | 490 |
| 477 # Let each property object know about the property name. | 491 # Let each property object know about the property name. |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 678 for k,v in toplevel_deps.iteritems(): | 692 for k,v in toplevel_deps.iteritems(): |
| 679 setattr(api, k, mapper.instantiate(v)) | 693 setattr(api, k, mapper.instantiate(v)) |
| 680 return api | 694 return api |
| 681 | 695 |
| 682 | 696 |
| 683 def _resolve_requirement(req, engine): | 697 def _resolve_requirement(req, engine): |
| 684 if req._typ == 'client': | 698 if req._typ == 'client': |
| 685 return engine._get_client(req._name) | 699 return engine._get_client(req._name) |
| 686 else: | 700 else: |
| 687 raise ValueError('Unknown requirement type [%s]' % (req._typ,)) | 701 raise ValueError('Unknown requirement type [%s]' % (req._typ,)) |
| OLD | NEW |