10f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved. 20f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 30f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)# found in the LICENSE file. 40f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 50f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)import logging 65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import os 75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import traceback 80f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 90f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)from chroot_file_system import ChrootFileSystem 100f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)from content_provider import ContentProvider 115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import environment 125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from extensions_paths import CONTENT_PROVIDERS, LOCAL_DEBUG_DIR 13effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfrom future import Future 1403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)from gitiles_file_system import GitilesFileSystem 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from local_file_system import LocalFileSystem 160f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)from third_party.json_schema_compiler.memoize import memoize 170f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 180f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)_IGNORE_MISSING_CONTENT_PROVIDERS = [False] 205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def IgnoreMissingContentProviders(fn): 235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) '''Decorates |fn| to ignore missing content providers during its run. 245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ''' 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def run(*args, **optargs): 265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) saved = _IGNORE_MISSING_CONTENT_PROVIDERS[0] 275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) _IGNORE_MISSING_CONTENT_PROVIDERS[0] = True 285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) try: 295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return fn(*args, **optargs) 305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) finally: 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) _IGNORE_MISSING_CONTENT_PROVIDERS[0] = saved 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return run 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 350f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)class ContentProviders(object): 360f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) '''Implements the content_providers.json configuration; see 370f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) chrome/common/extensions/docs/templates/json/content_providers.json for its 380f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) current state and a description of the format. 390f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 400f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) Returns ContentProvider instances based on how they're configured there. 410f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) ''' 420f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def __init__(self, 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) object_store_creator, 45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) compiled_fs_factory, 46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) host_file_system, 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) github_file_system_provider, 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gcs_file_system_provider): 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) self._object_store_creator = object_store_creator 500f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) self._compiled_fs_factory = compiled_fs_factory 510f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) self._host_file_system = host_file_system 52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self._github_file_system_provider = github_file_system_provider 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) self._gcs_file_system_provider = gcs_file_system_provider 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) self._cache = None 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # If running the devserver and there is a LOCAL_DEBUG_DIR, we 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # will read the content_provider configuration from there instead 581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # of fetching it from Gitiles or patch. 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if environment.IsDevServer() and os.path.exists(LOCAL_DEBUG_DIR): 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) local_fs = LocalFileSystem(LOCAL_DEBUG_DIR) 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) conf_stat = None 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) try: 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) conf_stat = local_fs.Stat(CONTENT_PROVIDERS) 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) except: 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) pass 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if conf_stat: 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) logging.warn(("Using local debug folder (%s) for " 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "content_provider.json configuration") % LOCAL_DEBUG_DIR) 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) self._cache = compiled_fs_factory.ForJson(local_fs) 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if not self._cache: 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) self._cache = compiled_fs_factory.ForJson(host_file_system) 740f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 750f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) @memoize 760f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) def GetByName(self, name): 770f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) '''Gets the ContentProvider keyed by |name| in content_providers.json, or 780f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) None of there is no such content provider. 790f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) ''' 800f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) config = self._GetConfig().get(name) 810f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) if config is None: 820f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) logging.error('No content provider found with name "%s"' % name) 830f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return None 840f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return self._CreateContentProvider(name, config) 850f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 860f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) @memoize 870f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) def GetByServeFrom(self, path): 885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) '''Gets a (content_provider, serve_from, path_in_content_provider) tuple, 895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) where content_provider is the ContentProvider with the longest "serveFrom" 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) property that is a subpath of |path|, serve_from is that property, and 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) path_in_content_provider is the remainder of |path|. 920f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 930f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) For example, if content provider A serves from "foo" and content provider B 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) serves from "foo/bar", GetByServeFrom("foo/bar/baz") will return (B, 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "foo/bar", "baz"). 960f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) Returns (None, '', |path|) if no ContentProvider serves from |path|. 980f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) ''' 990f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) serve_from_to_config = dict( 1000f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) (config['serveFrom'], (name, config)) 1010f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) for name, config in self._GetConfig().iteritems()) 1020f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) path_parts = path.split('/') 1030f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) for i in xrange(len(path_parts), -1, -1): 1040f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) name_and_config = serve_from_to_config.get('/'.join(path_parts[:i])) 1050f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) if name_and_config is not None: 1060f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return (self._CreateContentProvider(name_and_config[0], 1070f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) name_and_config[1]), 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) '/'.join(path_parts[:i]), 1090f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) '/'.join(path_parts[i:])) 1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return None, '', path 1110f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 1120f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) def _GetConfig(self): 113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return self._cache.GetFromFile(CONTENT_PROVIDERS).Get() 1140f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 1150f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) def _CreateContentProvider(self, name, config): 1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) default_extensions = config.get('defaultExtensions', ()) 1170f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) supports_templates = config.get('supportsTemplates', False) 1180f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) supports_zip = config.get('supportsZip', False) 1190f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 1200f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) if 'chromium' in config: 1210f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) chromium_config = config['chromium'] 1220f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) if 'dir' not in chromium_config: 123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) logging.error('%s: "chromium" must have a "dir" property' % name) 1240f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return None 1250f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) file_system = ChrootFileSystem(self._host_file_system, 1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1270f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) chromium_config['dir']) 1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # TODO(rockot): Remove this in a future patch. It should not be needed once 1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # the new content_providers.json is committed. 13003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) elif 'gitiles' in config: 1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci chromium_config = config['gitiles'] 1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if 'dir' not in chromium_config: 1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci logging.error('%s: "chromium" must have a "dir" property' % name) 13403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return None 1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci file_system = ChrootFileSystem(self._host_file_system, 1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci chromium_config['dir']) 1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elif 'gcs' in config: 1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gcs_config = config['gcs'] 1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if 'bucket' not in gcs_config: 1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) logging.error('%s: "gcs" must have a "bucket" property' % name) 1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return None 1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bucket = gcs_config['bucket'] 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if not bucket.startswith('gs://'): 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) logging.error('%s: bucket %s should start with gs://' % (name, bucket)) 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return None 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bucket = bucket[len('gs://'):] 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) file_system = self._gcs_file_system_provider.Create(bucket) 1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if 'dir' in gcs_config: 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) file_system = ChrootFileSystem(file_system, gcs_config['dir']) 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) elif 'github' in config: 152f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) github_config = config['github'] 153f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if 'owner' not in github_config or 'repo' not in github_config: 154f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) logging.error('%s: "github" must provide an "owner" and "repo"' % name) 155f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return None 156f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) file_system = self._github_file_system_provider.Create( 157f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) github_config['owner'], github_config['repo']) 158f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if 'dir' in github_config: 159f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) file_system = ChrootFileSystem(file_system, github_config['dir']) 1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) else: 1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) logging.error('%s: content provider type not supported' % name) 1630f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return None 1640f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 1650f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return ContentProvider(name, 1660f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) self._compiled_fs_factory, 1670f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) file_system, 1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) self._object_store_creator, 1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) default_extensions=default_extensions, 1700f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) supports_templates=supports_templates, 1710f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) supports_zip=supports_zip) 1720f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def GetRefreshPaths(self): 1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return self._GetConfig().keys() 1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def Refresh(self, path): 1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def safe(name, action, callback): 1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) '''Safely runs |callback| for a ContentProvider called |name| by 1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) swallowing exceptions and turning them into a None return value. It's 1801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci important to run all ContentProvider Refreshes even if some of them fail. 1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ''' 1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) try: 1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return callback() 1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) except: 1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if not _IGNORE_MISSING_CONTENT_PROVIDERS[0]: 1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci logging.error('Error %s Refresh for ContentProvider "%s":\n%s' % 1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) (action, name, traceback.format_exc())) 1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return None 1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci config = self._GetConfig()[path] 1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci provider = self._CreateContentProvider(path, config) 1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci future = safe(path, 1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 'initializing', 1941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._CreateContentProvider(path, config).Refresh) 1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if future is None: 1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return Future(callback=lambda: True) 1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return Future(callback=lambda: safe(path, 'resolving', future.Get)) 198