cygprofile: increase timeouts to allow showing web contents
[chromium-blink-merge.git] / chrome / common / extensions / docs / server2 / patched_file_system.py
blob52ad0bda118b861bd6902e2a6cf4fb8b0ec079d2
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.
5 from copy import deepcopy
7 from file_system import FileSystem, StatInfo, FileNotFoundError
8 from future import Future
11 class PatchedFileSystem(FileSystem):
12 ''' Class to fetch resources with a patch applied.
13 '''
14 def __init__(self, base_file_system, patcher):
15 self._base_file_system = base_file_system
16 self._patcher = patcher
18 def Read(self, paths, skip_not_found=False):
19 patched_files = set()
20 added, deleted, modified = self._patcher.GetPatchedFiles()
21 if set(paths) & set(deleted):
22 def raise_file_not_found():
23 raise FileNotFoundError('Files are removed from the patch.')
24 return Future(callback=raise_file_not_found)
26 patched_files |= (set(added) | set(modified))
27 dir_paths = set(path for path in paths if path.endswith('/'))
28 file_paths = set(paths) - dir_paths
29 patched_paths = file_paths & patched_files
30 unpatched_paths = file_paths - patched_files
32 def patch_directory_listing(path, original_listing):
33 added, deleted, modified = (
34 self._GetDirectoryListingFromPatch(path))
35 if original_listing is None:
36 if len(added) == 0:
37 raise FileNotFoundError('Directory %s not found in the patch.' % path)
38 return added
39 return list((set(original_listing) | set(added)) - set(deleted))
41 def next(files):
42 dirs_value = self._TryReadDirectory(dir_paths)
43 files.update(self._patcher.Apply(patched_paths,
44 self._base_file_system).Get())
45 files.update(dict((path, patch_directory_listing(path, dirs_value[path]))
46 for path in dirs_value))
47 return files
48 return self._base_file_system.Read(unpatched_paths,
49 skip_not_found=skip_not_found).Then(next)
51 def Refresh(self):
52 return self._base_file_system.Refresh()
54 ''' Given the list of patched files, it's not possible to determine whether
55 a directory to read exists in self._base_file_system. So try reading each one
56 and handle FileNotFoundError.
57 '''
58 def _TryReadDirectory(self, paths):
59 value = {}
60 for path in paths:
61 assert path.endswith('/')
62 try:
63 value[path] = self._base_file_system.ReadSingle(path).Get()
64 except FileNotFoundError:
65 value[path] = None
66 return value
68 def _GetDirectoryListingFromPatch(self, path):
69 assert path.endswith('/')
70 def _FindChildrenInPath(files, path):
71 result = []
72 for f in files:
73 if f.startswith(path):
74 child_path = f[len(path):]
75 if '/' in child_path:
76 child_name = child_path[0:child_path.find('/') + 1]
77 else:
78 child_name = child_path
79 result.append(child_name)
80 return result
82 added, deleted, modified = (tuple(
83 _FindChildrenInPath(files, path)
84 for files in self._patcher.GetPatchedFiles()))
86 # A patch applies to files only. It cannot delete directories.
87 deleted_files = [child for child in deleted if not child.endswith('/')]
88 # However, these directories are actually modified because their children
89 # are patched.
90 modified += [child for child in deleted if child.endswith('/')]
92 return (added, deleted_files, modified)
94 def _PatchStat(self, stat_info, version, added, deleted, modified):
95 assert len(added) + len(deleted) + len(modified) > 0
96 assert stat_info.child_versions is not None
98 # Deep copy before patching to make sure it doesn't interfere with values
99 # cached in memory.
100 stat_info = deepcopy(stat_info)
102 stat_info.version = version
103 for child in added + modified:
104 stat_info.child_versions[child] = version
105 for child in deleted:
106 if stat_info.child_versions.get(child):
107 del stat_info.child_versions[child]
109 return stat_info
111 def Stat(self, path):
112 version = self._patcher.GetVersion()
113 assert version is not None
114 version = 'patched_%s' % version
116 directory, filename = path.rsplit('/', 1)
117 added, deleted, modified = self._GetDirectoryListingFromPatch(
118 directory + '/')
120 if len(added) > 0:
121 # There are new files added. It's possible (if |directory| is new) that
122 # self._base_file_system.Stat will throw an exception.
123 try:
124 stat_info = self._PatchStat(
125 self._base_file_system.Stat(directory + '/'),
126 version,
127 added,
128 deleted,
129 modified)
130 except FileNotFoundError:
131 stat_info = StatInfo(
132 version,
133 dict((child, version) for child in added + modified))
134 elif len(deleted) + len(modified) > 0:
135 # No files were added.
136 stat_info = self._PatchStat(self._base_file_system.Stat(directory + '/'),
137 version,
138 added,
139 deleted,
140 modified)
141 else:
142 # No changes are made in this directory.
143 return self._base_file_system.Stat(path)
145 if stat_info.child_versions is not None:
146 if filename:
147 if filename in stat_info.child_versions:
148 stat_info = StatInfo(stat_info.child_versions[filename])
149 else:
150 raise FileNotFoundError('%s was not in child versions' % filename)
151 return stat_info
153 def GetIdentity(self):
154 return '%s(%s,%s)' % (self.__class__.__name__,
155 self._base_file_system.GetIdentity(),
156 self._patcher.GetIdentity())