Upstream version 7.36.149.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     list_future = self.ReadSingle(ToDirectory(parent))
110     def resolve():
111       try:
112         return base in list_future.Get()
113       except FileNotFoundError:
114         return False
115     return Future(callback=resolve)
116
117   def Refresh(self):
118     '''Asynchronously refreshes the content of the FileSystem, returning a
119     future to its completion.
120     '''
121     raise NotImplementedError(self.__class__)
122
123   # TODO(cduvall): Allow Stat to take a list of paths like Read.
124   def Stat(self, path):
125     '''Returns a |StatInfo| object containing the version of |path|. If |path|
126     is a directory, |StatInfo| will have the versions of all the children of
127     the directory in |StatInfo.child_versions|.
128
129     If the path cannot be found, raises a FileNotFoundError.
130     For any other failure, raises a FileSystemError.
131     '''
132     raise NotImplementedError(self.__class__)
133
134   def StatAsync(self, path):
135     '''Bandaid for a lack of an async Stat function. Stat() should be async
136     by default but for now just let implementations override this if they like.
137     '''
138     return Future(callback=lambda: self.Stat(path))
139
140   def GetIdentity(self):
141     '''The identity of the file system, exposed for caching classes to
142     namespace their caches. this will usually depend on the configuration of
143     that file system - e.g. a LocalFileSystem with a base path of /var is
144     different to that of a SubversionFileSystem with a base path of /bar, is
145     different to a LocalFileSystem with a base path of /usr.
146     '''
147     raise NotImplementedError(self.__class__)
148
149   def Walk(self, root):
150     '''Recursively walk the directories in a file system, starting with root.
151
152     Behaviour is very similar to os.walk from the standard os module, yielding
153     (base, dirs, files) recursively, where |base| is the base path of |files|,
154     |dirs| relative to |root|, and |files| and |dirs| the list of files/dirs in
155     |base| respectively.
156
157     Note that directories will always end with a '/', files never will.
158
159     If |root| cannot be found, raises a FileNotFoundError.
160     For any other failure, raises a FileSystemError.
161     '''
162     AssertIsDirectory(root)
163     basepath = root
164
165     def walk(root):
166       AssertIsDirectory(root)
167       dirs, files = [], []
168
169       for f in self.ReadSingle(root).Get():
170         if IsDirectory(f):
171           dirs.append(f)
172         else:
173           files.append(f)
174
175       yield root[len(basepath):].rstrip('/'), dirs, files
176
177       for d in dirs:
178         for walkinfo in walk(root + d):
179           yield walkinfo
180
181     for walkinfo in walk(root):
182       yield walkinfo
183
184   def __eq__(self, other):
185     return (isinstance(other, FileSystem) and
186             self.GetIdentity() == other.GetIdentity())
187
188   def __ne__(self, other):
189     return not (self == other)
190
191   def __repr__(self):
192     return '<%s>' % type(self).__name__
193
194   def __str__(self):
195     return repr(self)