api_models.py revision 0529e5d033099cbfc42635f6f6183833b09dff6e
1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import posixpath 6 7from compiled_file_system import SingleFile, Unicode 8from extensions_paths import API_PATHS 9from file_system import FileNotFoundError 10from future import Collect, Future 11from schema_util import ProcessSchema 12from third_party.json_schema_compiler.model import Namespace, UnixName 13 14 15@SingleFile 16@Unicode 17def _CreateAPIModel(path, data): 18 schema = ProcessSchema(path, data)[0] 19 if not schema: 20 raise FileNotFoundError('No schema for %s' % path) 21 return Namespace(schema, schema['namespace']) 22 23 24class APIModels(object): 25 '''Tracks APIs and their Models. 26 ''' 27 28 def __init__(self, features_bundle, compiled_fs_factory, file_system): 29 self._features_bundle = features_bundle 30 self._model_cache = compiled_fs_factory.Create( 31 file_system, _CreateAPIModel, APIModels) 32 33 def GetNames(self): 34 # API names appear alongside some of their methods/events/etc in the 35 # features file. APIs are those which either implicitly or explicitly have 36 # no parent feature (e.g. app, app.window, and devtools.inspectedWindow are 37 # APIs; runtime.onConnectNative is not). 38 api_features = self._features_bundle.GetAPIFeatures().Get() 39 return [name for name, feature in api_features.iteritems() 40 if ('.' not in name or 41 name.rsplit('.', 1)[0] not in api_features or 42 feature.get('noparent'))] 43 44 def GetModel(self, api_name): 45 # By default |api_name| is assumed to be given without a path or extension, 46 # so combinations of known paths and extension types will be searched. 47 api_extensions = ('.json', '.idl') 48 api_paths = API_PATHS 49 50 # Callers sometimes include a file extension and/or prefix path with the 51 # |api_name| argument. We believe them and narrow the search space 52 # accordingly. 53 name, ext = posixpath.splitext(api_name) 54 if ext in api_extensions: 55 api_extensions = (ext,) 56 api_name = name 57 for api_path in api_paths: 58 if api_name.startswith(api_path): 59 api_name = api_name[len(api_path):] 60 api_paths = (api_path,) 61 break 62 63 # API names are given as declarativeContent and app.window but file names 64 # will be declarative_content and app_window. 65 file_name = UnixName(api_name).replace('.', '_') 66 # Devtools APIs are in API/devtools/ not API/, and have their 67 # "devtools" names removed from the file names. 68 basename = posixpath.basename(file_name) 69 if 'devtools_' in basename: 70 file_name = posixpath.join( 71 'devtools', file_name.replace(basename, 72 basename.replace('devtools_' , ''))) 73 74 futures = [self._model_cache.GetFromFile( 75 posixpath.join(path, '%s%s' % (file_name, ext))) 76 for ext in api_extensions 77 for path in api_paths] 78 def resolve(): 79 for future in futures: 80 try: 81 return future.Get() 82 except FileNotFoundError: pass 83 # Propagate the first FileNotFoundError if neither were found. 84 futures[0].Get() 85 return Future(callback=resolve) 86 87 def Cron(self): 88 futures = [self.GetModel(name) for name in self.GetNames()] 89 return Collect(futures, except_pass=FileNotFoundError) 90 91 def IterModels(self): 92 future_models = [(name, self.GetModel(name)) for name in self.GetNames()] 93 for name, future_model in future_models: 94 try: 95 model = future_model.Get() 96 except FileNotFoundError: 97 continue 98 if model: 99 yield name, model 100