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