api_models.py revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 os
6import posixpath
7
8from compiled_file_system import SingleFile, Unicode
9from extensions_paths import API
10from file_system import FileNotFoundError
11from future import Gettable, Future
12from schema_util import ProcessSchema
13from third_party.json_schema_compiler.model import Namespace, UnixName
14
15
16@SingleFile
17@Unicode
18def _CreateAPIModel(path, data):
19  schema = ProcessSchema(path, data)[0]
20  if not schema: return None
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    # Callers sometimes specify a filename which includes .json or .idl - if
46    # so, believe them. They may even include the 'api/' prefix.
47    if os.path.splitext(api_name)[1] in ('.json', '.idl'):
48      if not api_name.startswith(API):
49        api_name = posixpath.join(API, api_name)
50      return self._model_cache.GetFromFile(api_name)
51
52    assert not api_name.startswith(API)
53
54    # API names are given as declarativeContent and app.window but file names
55    # will be declarative_content and app_window.
56    file_name = UnixName(api_name).replace('.', '_')
57    # Devtools APIs are in API/devtools/ not API/, and have their
58    # "devtools" names removed from the file names.
59    basename = posixpath.basename(file_name)
60    if 'devtools_' in basename:
61      file_name = posixpath.join(
62          'devtools', file_name.replace(basename,
63                                        basename.replace('devtools_' , '')))
64
65    futures = [self._model_cache.GetFromFile(
66                   posixpath.join(API, '%s.%s' % (file_name, ext)))
67               for ext in ('json', 'idl')]
68    def resolve():
69      for future in futures:
70        try:
71          return future.Get()
72        except FileNotFoundError: pass
73      # Propagate the first FileNotFoundError if neither were found.
74      futures[0].Get()
75    return Future(delegate=Gettable(resolve))
76
77  def IterModels(self):
78    future_models = [(name, self.GetModel(name)) for name in self.GetNames()]
79    for name, future_model in future_models:
80      try:
81        model = future_model.Get()
82      except FileNotFoundError:
83        continue
84      if model:
85        yield name, model
86