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

Side by Side Diff: recipe_engine/loader.py

Issue 2864803003: Minor loader improvements. (Closed)
Patch Set: fix tests Created 3 years, 7 months 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/unittests/loader_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
28 class MalformedRecipeError(LoaderError):
29 """Raised when a recipe doesn't conform to the expected interface."""
30
31
28 class NoSuchRecipe(LoaderError): 32 class NoSuchRecipe(LoaderError):
29 """Raised by load_recipe is recipe is not found.""" 33 """Raised by load_recipe is recipe is not found."""
30 def __init__(self, recipe): 34 def __init__(self, recipe):
31 super(NoSuchRecipe, self).__init__('No such recipe: %s' % recipe) 35 super(NoSuchRecipe, self).__init__('No such recipe: %s' % recipe)
32 36
33 37
34 class RecipeScript(object): 38 class RecipeScript(object):
35 """Holds dict of an evaluated recipe script.""" 39 """Holds dict of an evaluated recipe script."""
36 40
37 def __init__(self, name, recipe_globals, package_name, path): 41 def __init__(self, name, recipe_globals, package_name, path):
38 self._path = path 42 self._path = path
39 self._name = name 43 self._name = name
40 self._recipe_globals = recipe_globals 44 self._recipe_globals = recipe_globals
41 45
42 self.run_steps, self.gen_tests = [ 46 self.run_steps, self.gen_tests = [
43 recipe_globals.get(k) for k in ('RunSteps', 'GenTests')] 47 recipe_globals.get(k) for k in ('RunSteps', 'GenTests')]
44 48
49 if not self.run_steps:
50 raise MalformedRecipeError('Missing or misspelled RunSteps function.')
51
52 if not self.gen_tests:
53 raise MalformedRecipeError('Missing or misspelled GenTests function.')
54
45 # Let each property object know about the property name. 55 # Let each property object know about the property name.
46 full_decl_name = '%s::%s' % (package_name, name) 56 full_decl_name = '%s::%s' % (package_name, name)
47 recipe_globals['PROPERTIES'] = { 57 recipe_globals['PROPERTIES'] = {
48 name: value.bind( 58 name: value.bind(
49 name, BoundProperty.RECIPE_PROPERTY, full_decl_name) 59 name, BoundProperty.RECIPE_PROPERTY, full_decl_name)
50 for name, value in recipe_globals.get('PROPERTIES', {}).items()} 60 for name, value in recipe_globals.get('PROPERTIES', {}).items()}
51 61
52 return_schema = recipe_globals.get('RETURN_SCHEMA') 62 return_schema = recipe_globals.get('RETURN_SCHEMA')
53 if return_schema and not isinstance(return_schema, ConfigGroupSchema): 63 if return_schema and not isinstance(return_schema, ConfigGroupSchema):
54 raise ValueError('Invalid RETURN_SCHEMA; must be an instance of ' 64 raise MalformedRecipeError(
55 'ConfigGroupSchema') 65 'Invalid RETURN_SCHEMA; must be an instance of ConfigGroupSchema')
66
56 67
57 @property 68 @property
58 def path(self): 69 def path(self):
59 return self._path 70 return self._path
60 71
61 @property 72 @property
62 def name(self): 73 def name(self):
63 return self._name 74 return self._name
64 75
65 @property 76 @property
(...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after
447 assert not submod.CONFIG_CTX, ( 458 assert not submod.CONFIG_CTX, (
448 'More than one configuration context: %s, %s' % 459 'More than one configuration context: %s, %s' %
449 (submod.config, submod.CONFIG_CTX)) 460 (submod.config, submod.CONFIG_CTX))
450 submod.CONFIG_CTX = v 461 submod.CONFIG_CTX = v
451 assert submod.CONFIG_CTX, 'Config file, but no config context?' 462 assert submod.CONFIG_CTX, 'Config file, but no config context?'
452 463
453 # Identify the RecipeApiPlain subclass as this module's API. 464 # Identify the RecipeApiPlain subclass as this module's API.
454 submod.API = getattr(submod, 'API', None) 465 submod.API = getattr(submod, 'API', None)
455 assert hasattr(submod, 'api'), '%s is missing api.py' % (name,) 466 assert hasattr(submod, 'api'), '%s is missing api.py' % (name,)
456 for v in submod.api.__dict__.itervalues(): 467 for v in submod.api.__dict__.itervalues():
468 # If the recipe has literally imported the RecipeApi, we don't wan't
469 # to consider that to be the real RecipeApi :)
470 if v is RecipeApiPlain or v is RecipeApi:
471 continue
457 if inspect.isclass(v) and issubclass(v, RecipeApiPlain): 472 if inspect.isclass(v) and issubclass(v, RecipeApiPlain):
458 assert not submod.API, ( 473 assert not submod.API, (
459 '%s has more than one RecipeApi subclass: %s, %s' % ( 474 '%s has more than one RecipeApi subclass: %s, %s' % (
460 name, v, submod.API)) 475 name, v, submod.API))
461 submod.API = v 476 submod.API = v
462 assert submod.API, 'Submodule has no api? %s' % (submod) 477 assert submod.API, 'Submodule has no api? %s' % (submod)
463 478
464 # Identify the (optional) RecipeTestApi subclass as this module's test API. 479 # Identify the (optional) RecipeTestApi subclass as this module's test API.
465 submod.TEST_API = getattr(submod, 'TEST_API', None) 480 submod.TEST_API = getattr(submod, 'TEST_API', None)
466 if hasattr(submod, 'test_api'): 481 if hasattr(submod, 'test_api'):
467 for v in submod.test_api.__dict__.itervalues(): 482 for v in submod.test_api.__dict__.itervalues():
483 # If the recipe has literally imported the RecipeTestApi, we don't wan't
484 # to consider that to be the real RecipeTestApi :)
485 if v is RecipeTestApi:
486 continue
468 if inspect.isclass(v) and issubclass(v, RecipeTestApi): 487 if inspect.isclass(v) and issubclass(v, RecipeTestApi):
469 assert not submod.TEST_API, ( 488 assert not submod.TEST_API, (
470 'More than one TestApi subclass: %s' % submod.api) 489 'More than one TestApi subclass: %s' % submod.api)
471 submod.TEST_API = v 490 submod.TEST_API = v
472 assert submod.API, ( 491 assert submod.API, (
473 'Submodule has test_api.py but no TestApi subclass? %s' 492 'Submodule has test_api.py but no TestApi subclass? %s'
474 % (submod) 493 % (submod)
475 ) 494 )
476 495
477 # Let each property object know about the property name. 496 # Let each property object know about the property name.
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
678 for k,v in toplevel_deps.iteritems(): 697 for k,v in toplevel_deps.iteritems():
679 setattr(api, k, mapper.instantiate(v)) 698 setattr(api, k, mapper.instantiate(v))
680 return api 699 return api
681 700
682 701
683 def _resolve_requirement(req, engine): 702 def _resolve_requirement(req, engine):
684 if req._typ == 'client': 703 if req._typ == 'client':
685 return engine._get_client(req._name) 704 return engine._get_client(req._name)
686 else: 705 else:
687 raise ValueError('Unknown requirement type [%s]' % (req._typ,)) 706 raise ValueError('Unknown requirement type [%s]' % (req._typ,))
OLDNEW
« no previous file with comments | « no previous file | recipe_engine/unittests/loader_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698