| OLD | NEW |
| (Empty) | |
| 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 |
| 5 from copy import deepcopy |
| 6 import logging |
| 7 |
| 8 from file_system import FileSystem, StatInfo, FileNotFoundError |
| 9 from future import Future |
| 10 |
| 11 class _AsyncFetchFuture(object): |
| 12 def __init__(self, |
| 13 unpatched_files_future, |
| 14 patched_files_future, |
| 15 svn_dirs_future, |
| 16 dir_paths, |
| 17 patched_file_system): |
| 18 self._unpatched_files_future = unpatched_files_future |
| 19 self._patched_files_future = patched_files_future |
| 20 self._svn_dirs_future = svn_dirs_future |
| 21 self._dir_paths = dir_paths |
| 22 self._patched_file_system = patched_file_system |
| 23 |
| 24 def Get(self): |
| 25 files = self._unpatched_files_future.Get() |
| 26 files.update(self._patched_files_future.Get()) |
| 27 dirs = self._svn_dirs_future.Get() |
| 28 files.update({path: self._PatchDirectoryListing(path, dirs[path]) |
| 29 for path in self._dir_paths}) |
| 30 return files |
| 31 |
| 32 def _PatchDirectoryListing(self, path, original_listing): |
| 33 added, deleted, modified = ( |
| 34 self._patched_file_system._GetDirectoryListingFromPatch(path)) |
| 35 return list(set(original_listing) | set(added) - set(deleted)) |
| 36 |
| 37 class PatchedFileSystem(FileSystem): |
| 38 ''' Class to fetch resources with a patch applied. |
| 39 ''' |
| 40 def __init__(self, host_file_system, patcher): |
| 41 self._host_file_system = host_file_system |
| 42 self._patcher = patcher |
| 43 |
| 44 def Read(self, paths, binary=False): |
| 45 patched_files = set() |
| 46 for files in self._patcher.GetPatchedFiles(): |
| 47 patched_files |= set(files) |
| 48 dir_paths = {path for path in paths if path.endswith('/')} |
| 49 file_paths = set(paths) - dir_paths |
| 50 patched_paths = file_paths & patched_files |
| 51 unpatched_paths = file_paths - patched_files |
| 52 return Future(delegate=_AsyncFetchFuture( |
| 53 self._host_file_system.Read(unpatched_paths, binary), |
| 54 self._patcher.Apply(patched_paths, self._host_file_system, binary), |
| 55 self._host_file_system.Read(dir_paths, binary), |
| 56 dir_paths, |
| 57 self)) |
| 58 |
| 59 def _GetDirectoryListingFromPatch(self, path): |
| 60 assert path.endswith('/') |
| 61 def _FindChildrenInPath(files, path): |
| 62 result = [] |
| 63 for f in files: |
| 64 if f.startswith(path): |
| 65 child_path = f[len(path):] |
| 66 if '/' in child_path: |
| 67 child_name = child_path[0:child_path.find('/') + 1] |
| 68 else: |
| 69 child_name = child_path |
| 70 result.append(child_name) |
| 71 return result |
| 72 |
| 73 return (tuple(_FindChildrenInPath(files, path) |
| 74 for files in self._patcher.GetPatchedFiles())) |
| 75 |
| 76 def _PatchStat(self, stat_info, version, added, deleted, modified): |
| 77 assert len(added) + len(deleted) + len(modified) > 0 |
| 78 |
| 79 # Deep copy before patching to make sure it doesn't interfere with values |
| 80 # cached in memory. |
| 81 stat_info = deepcopy(stat_info) |
| 82 |
| 83 stat_info.version = version |
| 84 if stat_info.child_versions is not None: |
| 85 for child in added + modified: |
| 86 stat_info.child_versions[child] = version |
| 87 for child in deleted: |
| 88 if stat_info.child_versions.get(child): |
| 89 del stat_info.child_versions[child] |
| 90 |
| 91 return stat_info |
| 92 |
| 93 def Stat(self, path): |
| 94 version = self._patcher.GetVersion() |
| 95 if version is None: |
| 96 return self._host_file_system.Stat(path) |
| 97 version = 'patched_%s' % version |
| 98 |
| 99 directory, filename = path.rsplit('/', 1) |
| 100 added, deleted, modified = self._GetDirectoryListingFromPatch( |
| 101 directory + '/') |
| 102 |
| 103 if len(added) > 0: |
| 104 # There are new files added. It's possible (if |directory| is new) that |
| 105 # self._host_file_system.Stat will throw an exception. |
| 106 try: |
| 107 stat_info = self._PatchStat( |
| 108 self._host_file_system.Stat(directory + '/'), |
| 109 version, |
| 110 added, |
| 111 deleted, |
| 112 modified) |
| 113 except FileNotFoundError: |
| 114 stat_info = StatInfo(version, {child: version |
| 115 for child in added + modified}) |
| 116 elif len(deleted) + len(modified) > 0: |
| 117 # No files were added. |
| 118 stat_info = self._PatchStat(self._host_file_system.Stat(path), |
| 119 version, |
| 120 added, |
| 121 deleted, |
| 122 modified) |
| 123 else: |
| 124 # No changes are made in this directory. |
| 125 return self._host_file_system.Stat(path) |
| 126 |
| 127 if stat_info.child_versions is not None: |
| 128 if filename: |
| 129 if filename in stat_info.child_versions: |
| 130 stat_info.version = stat_info.child_versions[filename] |
| 131 else: |
| 132 raise FileNotFoundError('%s was not in child versions' % filename) |
| 133 return stat_info |
| OLD | NEW |