Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / server2 / file_system.py
1 # Copyright (c) 2012 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 import posixpath
6 import traceback
7
8 from future import Future
9 from path_util import (
10     AssertIsDirectory, AssertIsValid, IsDirectory, IsValid, SplitParent,
11     ToDirectory)
12
13
14 def IsFileSystemThrottledError(error):
15   return type(error).__name__ == 'FileSystemThrottledError'
16
17
18 class _BaseFileSystemException(Exception):
19   def __init__(self, message):
20     Exception.__init__(self, message)
21
22   @classmethod
23   def RaiseInFuture(cls, message):
24     stack = traceback.format_stack()
25     def boom(): raise cls('%s. Creation stack:\n%s' % (message, ''.join(stack)))
26     return Future(callback=boom)
27
28
29 class FileNotFoundError(_BaseFileSystemException):
30   '''Raised when a file isn't found for read or stat.
31   '''
32   def __init__(self, filename):
33     _BaseFileSystemException.__init__(self, filename)
34
35
36 class FileSystemThrottledError(_BaseFileSystemException):
37   '''Raised when access to a file system resource is temporarily unavailable
38   due to service throttling.
39   '''
40   def __init__(self, filename):
41     _BaseFileSystemException.__init__(self, filename)
42
43
44 class FileSystemError(_BaseFileSystemException):
45   '''Raised on when there are errors reading or statting files, such as a
46   network timeout.
47   '''
48   def __init__(self, filename):
49     _BaseFileSystemException.__init__(self, filename)
50
51
52 class StatInfo(object):
53   '''The result of calling Stat on a FileSystem.
54   '''
55   def __init__(self, version, child_versions=None):
56     if child_versions:
57       assert all(IsValid(path) for path in child_versions.iterkeys()), \
58              child_versions
59     self.version = version
60     self.child_versions = child_versions
61
62   def __eq__(self, other):
63     return (isinstance(other, StatInfo) and
64             self.version == other.version and
65             self.child_versions == other.child_versions)
66
67   def __ne__(self, other):
68     return not (self == other)
69
70   def __str__(self):
71     return '{version: %s, child_versions: %s}' % (self.version,
72                                                   self.child_versions)
73
74   def __repr__(self):
75     return str(self)
76
77
78 class FileSystem(object):
79   '''A FileSystem interface that can read files and directories.
80   '''
81   def Read(self, paths, skip_not_found=False):
82     '''Reads each file in paths and returns a dictionary mapping the path to the
83     contents. If a path in paths ends with a '/', it is assumed to be a
84     directory, and a list of files in the directory is mapped to the path.
85
86     The contents will be a str.
87
88     If any path cannot be found:
89       - If |skip_not_found| is True, the resulting object will not contain any
90         mapping for that path.
91       - Otherwise, and by default, a FileNotFoundError is raised. This is
92         guaranteed to only happen once the Future has been resolved (Get()
93         called).
94
95     For any other failure, raises a FileSystemError.
96     '''
97     raise NotImplementedError(self.__class__)
98
99   def ReadSingle(self, path, skip_not_found=False):
100     '''Reads a single file from the FileSystem. Returns a Future with the same
101     rules as Read(). If |path| is not found raise a FileNotFoundError on Get(),
102     or if |skip_not_found| is True then return None.
103     '''
104     AssertIsValid(path)
105     read_single = self.Read([path], skip_not_found=skip_not_found)
106     return Future(callback=lambda: read_single.Get().get(path, None))
107
108   def Exists(self, path):
109     '''Returns a Future to the existence of |path|; True if |path| exists,
110     False if not. This method will not throw a FileNotFoundError unlike
111     the Read* methods, however it may still throw a FileSystemError.
112
113     There are several ways to implement this method via the interface but this
114     method exists to do so in a canonical and most efficient way for caching.
115     '''
116     AssertIsValid(path)
117     if path == '':
118       # There is always a root directory.
119       return Future(value=True)
120
121     parent, base = SplitParent(path)
122     def handle(error):
123       if isinstance(error, FileNotFoundError):
124         return False
125       raise error
126     return self.ReadSingle(ToDirectory(parent)).Then(lambda l: base in l,
127                                                      handle)
128
129   def Refresh(self):
130     '''Asynchronously refreshes the content of the FileSystem, returning a
131     future to its completion.
132     '''
133     raise NotImplementedError(self.__class__)
134
135   # TODO(cduvall): Allow Stat to take a list of paths like Read.
136   def Stat(self, path):
137     '''DEPRECATED: Please try to use StatAsync instead.
138
139     Returns a |StatInfo| object containing the version of |path|. If |path|
140     is a directory, |StatInfo| will have the versions of all the children of
141     the directory in |StatInfo.child_versions|.
142
143     If the path cannot be found, raises a FileNotFoundError.
144     For any other failure, raises a FileSystemError.
145     '''
146     # Delegate to this implementation's StatAsync if it has been implemented.
147     if type(self).StatAsync != FileSystem.StatAsync:
148       return self.StatAsync(path).Get()
149     raise NotImplementedError(self.__class__)
150
151   def StatAsync(self, path):
152     '''An async version of Stat. Returns a Future to a StatInfo rather than a
153     raw StatInfo.
154
155     This is a bandaid for a lack of an async Stat function. Stat() should be
156     async by default but for now just let implementations override this if they
157     like.
158     '''
159     return Future(callback=lambda: self.Stat(path))
160
161   def GetIdentity(self):
162     '''The identity of the file system, exposed for caching classes to
163     namespace their caches. this will usually depend on the configuration of
164     that file system - e.g. a LocalFileSystem with a base path of /var is
165     different to that of a SubversionFileSystem with a base path of /bar, is
166     different to a LocalFileSystem with a base path of /usr.
167     '''
168     raise NotImplementedError(self.__class__)
169
170   def Walk(self, root, depth=-1, file_lister=None):
171     '''Recursively walk the directories in a file system, starting with root.
172
173     Behaviour is very similar to os.walk from the standard os module, yielding
174     (base, dirs, files) recursively, where |base| is the base path of |files|,
175     |dirs| relative to |root|, and |files| and |dirs| the list of files/dirs in
176     |base| respectively. If |depth| is specified and greater than 0, Walk will
177     only recurse |depth| times.
178
179     |file_lister|, if specified, should be a callback of signature
180
181       def my_file_lister(root):,
182
183     which returns a tuple (dirs, files), where |dirs| is a list of directory
184     names under |root|, and |files| is a list of file names under |root|. Note
185     that the listing of files and directories should be for a *single* level
186     only, i.e. it should not recursively list anything.
187
188     Note that directories will always end with a '/', files never will.
189
190     If |root| cannot be found, raises a FileNotFoundError.
191     For any other failure, raises a FileSystemError.
192     '''
193     AssertIsDirectory(root)
194     basepath = root
195
196     def walk(root, depth):
197       if depth == 0:
198         return
199       AssertIsDirectory(root)
200
201       if file_lister:
202         dirs, files = file_lister(root)
203       else:
204         dirs, files = [], []
205         for f in self.ReadSingle(root).Get():
206           if IsDirectory(f):
207             dirs.append(f)
208           else:
209             files.append(f)
210
211       yield root[len(basepath):].rstrip('/'), dirs, files
212
213       for d in dirs:
214         for walkinfo in walk(root + d, depth - 1):
215           yield walkinfo
216
217     for walkinfo in walk(root, depth):
218       yield walkinfo
219
220   def __eq__(self, other):
221     return (isinstance(other, FileSystem) and
222             self.GetIdentity() == other.GetIdentity())
223
224   def __ne__(self, other):
225     return not (self == other)
226
227   def __repr__(self):
228     return '<%s>' % type(self).__name__
229
230   def __str__(self):
231     return repr(self)