16b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# Copyright (C) 2009 Google Inc. All rights reserved. 26b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# 36b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# Redistribution and use in source and binary forms, with or without 46b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# modification, are permitted provided that the following conditions are 56b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# met: 66b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# 76b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# * Redistributions of source code must retain the above copyright 86b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# notice, this list of conditions and the following disclaimer. 96b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# * Redistributions in binary form must reproduce the above 106b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# copyright notice, this list of conditions and the following disclaimer 116b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# in the documentation and/or other materials provided with the 126b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# distribution. 136b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# * Neither the name of Google Inc. nor the names of its 146b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# contributors may be used to endorse or promote products derived from 156b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# this software without specific prior written permission. 166b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# 176b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 186b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 196b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 206b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 216b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 226b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 236b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 246b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 256b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 266b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 276b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 286b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 296b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brennerimport errno 306b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brennerimport os 31cad810f21b803229eb11403f9209855525a25d57Steve Blockimport re 326b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 332fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockfrom webkitpy.common.system import path 342fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockfrom webkitpy.common.system import ospath 352fc2651226baac27029e38c9d6ef883fa32084dbSteve Block 366b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 376b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brennerclass MockFileSystem(object): 382daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch def __init__(self, files=None, cwd='/'): 396b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner """Initializes a "mock" filesystem that can be used to completely 406b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner stub out a filesystem. 416b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 426b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner Args: 436b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner files: a dict of filenames -> file contents. A file contents 446b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner value of None is used to indicate that the file should 454576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang not exist. 466b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner """ 47f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch self.files = files or {} 48ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.written_files = {} 492fc2651226baac27029e38c9d6ef883fa32084dbSteve Block self._sep = '/' 50ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.current_tmpno = 0 512daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch self.cwd = cwd 522daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch self.dirs = {} 53ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 542fc2651226baac27029e38c9d6ef883fa32084dbSteve Block def _get_sep(self): 552fc2651226baac27029e38c9d6ef883fa32084dbSteve Block return self._sep 562fc2651226baac27029e38c9d6ef883fa32084dbSteve Block 572fc2651226baac27029e38c9d6ef883fa32084dbSteve Block sep = property(_get_sep, doc="pathname separator") 582fc2651226baac27029e38c9d6ef883fa32084dbSteve Block 59ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def _raise_not_found(self, path): 60ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch raise IOError(errno.ENOENT, path, os.strerror(errno.ENOENT)) 61ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 62ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def _split(self, path): 632fc2651226baac27029e38c9d6ef883fa32084dbSteve Block return path.rsplit(self.sep, 1) 64ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 65ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def abspath(self, path): 662daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch if os.path.isabs(path): 672daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch return self.normpath(path) 682daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch return self.abspath(self.join(self.cwd, path)) 69ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 70ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def basename(self, path): 71ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return self._split(path)[1] 72ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 732daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch def chdir(self, path): 742daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch path = self.normpath(path) 752daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch if not self.isdir(path): 762daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch raise OSError(errno.ENOENT, path, os.strerror(errno.ENOENT)) 772daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch self.cwd = path 782daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch 79ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def copyfile(self, source, destination): 80ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if not self.exists(source): 81ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self._raise_not_found(source) 82ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if self.isdir(source): 83ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch raise IOError(errno.EISDIR, source, os.strerror(errno.ISDIR)) 84ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if self.isdir(destination): 85ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch raise IOError(errno.EISDIR, destination, os.strerror(errno.ISDIR)) 86ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 87ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.files[destination] = self.files[source] 882fc2651226baac27029e38c9d6ef883fa32084dbSteve Block self.written_files[destination] = self.files[source] 89ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 90ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def dirname(self, path): 91ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return self._split(path)[0] 926b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 936b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner def exists(self, path): 94f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch return self.isfile(path) or self.isdir(path) 95f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch 96ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def files_under(self, path, dirs_to_skip=[], file_filter=None): 97ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def filter_all(fs, dirpath, basename): 98ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return True 99ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 100ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch file_filter = file_filter or filter_all 101ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch files = [] 102ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if self.isfile(path): 103ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if file_filter(self, self.dirname(path), self.basename(path)): 104ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch files.append(path) 105ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return files 106ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 107ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if self.basename(path) in dirs_to_skip: 108ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return [] 109ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 1102fc2651226baac27029e38c9d6ef883fa32084dbSteve Block if not path.endswith(self.sep): 1112fc2651226baac27029e38c9d6ef883fa32084dbSteve Block path += self.sep 112ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 1132fc2651226baac27029e38c9d6ef883fa32084dbSteve Block dir_substrings = [self.sep + d + self.sep for d in dirs_to_skip] 114ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch for filename in self.files: 115ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if not filename.startswith(path): 116ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch continue 117ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 118ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch suffix = filename[len(path) - 1:] 119ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if any(dir_substring in suffix for dir_substring in dir_substrings): 120ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch continue 121ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 122ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch dirpath, basename = self._split(filename) 123ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if file_filter(self, dirpath, basename): 124ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch files.append(filename) 125ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 126ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return files 127ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 1282daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch def getcwd(self, path): 1292daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch return self.cwd 1302daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch 131ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def glob(self, path): 132ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch # FIXME: This only handles a wildcard '*' at the end of the path. 133ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch # Maybe it should handle more? 134ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if path[-1] == '*': 135ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return [f for f in self.files if f.startswith(path[:-1])] 136ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch else: 137ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return [f for f in self.files if f == path] 138ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 139ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def isabs(self, path): 1402fc2651226baac27029e38c9d6ef883fa32084dbSteve Block return path.startswith(self.sep) 141ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 142f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch def isfile(self, path): 143f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch return path in self.files and self.files[path] is not None 144f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch 145f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch def isdir(self, path): 1466b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner if path in self.files: 147f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch return False 1482daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch path = self.normpath(path) 1492daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch if path in self.dirs: 1502daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch return True 151ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 152ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch # We need to use a copy of the keys here in order to avoid switching 153ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch # to a different thread and potentially modifying the dict in 154ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch # mid-iteration. 155ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch files = self.files.keys()[:] 1562daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch result = any(f.startswith(path) for f in files) 1572daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch if result: 1582daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch self.dirs[path] = True 1592daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch return result 1606b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 1616b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner def join(self, *comps): 1622fc2651226baac27029e38c9d6ef883fa32084dbSteve Block # FIXME: might want tests for this and/or a better comment about how 1632fc2651226baac27029e38c9d6ef883fa32084dbSteve Block # it works. 1642fc2651226baac27029e38c9d6ef883fa32084dbSteve Block return re.sub(re.escape(os.path.sep), self.sep, os.path.join(*comps)) 165f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch 166f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch def listdir(self, path): 167f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch if not self.isdir(path): 168f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch raise OSError("%s is not a directory" % path) 169f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch 1702fc2651226baac27029e38c9d6ef883fa32084dbSteve Block if not path.endswith(self.sep): 1712fc2651226baac27029e38c9d6ef883fa32084dbSteve Block path += self.sep 172f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch 173f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch dirs = [] 174f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch files = [] 175f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch for f in self.files: 176f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch if self.exists(f) and f.startswith(path): 177f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch remaining = f[len(path):] 1782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block if self.sep in remaining: 1792fc2651226baac27029e38c9d6ef883fa32084dbSteve Block dir = remaining[:remaining.index(self.sep)] 180f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch if not dir in dirs: 181f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch dirs.append(dir) 182f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch else: 183f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch files.append(remaining) 184f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch return dirs + files 1856b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 186ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def mtime(self, path): 187ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if self.exists(path): 188ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return 0 189ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self._raise_not_found(path) 190ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 191ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def _mktemp(self, suffix='', prefix='tmp', dir=None, **kwargs): 192ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if dir is None: 1932fc2651226baac27029e38c9d6ef883fa32084dbSteve Block dir = self.sep + '__im_tmp' 194ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch curno = self.current_tmpno 195ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.current_tmpno += 1 196ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return self.join(dir, "%s_%u_%s" % (prefix, curno, suffix)) 197ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 198ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def mkdtemp(self, **kwargs): 199ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch class TemporaryDirectory(object): 200ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def __init__(self, fs, **kwargs): 201ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self._kwargs = kwargs 202ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self._filesystem = fs 203ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self._directory_path = fs._mktemp(**kwargs) 204ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch fs.maybe_make_directory(self._directory_path) 205ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 206ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def __str__(self): 207ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return self._directory_path 208ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 209ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def __enter__(self): 210ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return self._directory_path 211ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 212ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def __exit__(self, type, value, traceback): 213ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch # Only self-delete if necessary. 214ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 215ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch # FIXME: Should we delete non-empty directories? 216ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if self._filesystem.exists(self._directory_path): 217ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self._filesystem.rmtree(self._directory_path) 218ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 219ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return TemporaryDirectory(fs=self, **kwargs) 220ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 2216b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner def maybe_make_directory(self, *path): 2222daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch norm_path = self.normpath(self.join(*path)) 2232daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch if not self.isdir(norm_path): 2242daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch self.dirs[norm_path] = True 2256b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 2262fc2651226baac27029e38c9d6ef883fa32084dbSteve Block def move(self, source, destination): 2272fc2651226baac27029e38c9d6ef883fa32084dbSteve Block if self.files[source] is None: 2282fc2651226baac27029e38c9d6ef883fa32084dbSteve Block self._raise_not_found(source) 2292fc2651226baac27029e38c9d6ef883fa32084dbSteve Block self.files[destination] = self.files[source] 2302fc2651226baac27029e38c9d6ef883fa32084dbSteve Block self.written_files[destination] = self.files[destination] 2312fc2651226baac27029e38c9d6ef883fa32084dbSteve Block self.files[source] = None 2322fc2651226baac27029e38c9d6ef883fa32084dbSteve Block self.written_files[source] = None 233ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 234ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def normpath(self, path): 2352daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch # Like join(), relies on os.path functionality but normalizes the 2362daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch # path separator to the mock one. 2372daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch return re.sub(re.escape(os.path.sep), self.sep, os.path.normpath(path)) 238ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 2392fc2651226baac27029e38c9d6ef883fa32084dbSteve Block def open_binary_tempfile(self, suffix=''): 240ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch path = self._mktemp(suffix) 2412fc2651226baac27029e38c9d6ef883fa32084dbSteve Block return (WritableFileObject(self, path), path) 242ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 243ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def open_text_file_for_writing(self, path, append=False): 244ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return WritableFileObject(self, path, append) 245ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 2466b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner def read_text_file(self, path): 2472fc2651226baac27029e38c9d6ef883fa32084dbSteve Block return self.read_binary_file(path).decode('utf-8') 2486b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 2492bde8e466a4451c7319e3a072d118917957d6554Steve Block def open_binary_file_for_reading(self, path): 2502bde8e466a4451c7319e3a072d118917957d6554Steve Block if self.files[path] is None: 2512bde8e466a4451c7319e3a072d118917957d6554Steve Block self._raise_not_found(path) 2522bde8e466a4451c7319e3a072d118917957d6554Steve Block return ReadableFileObject(self, path, self.files[path]) 2532bde8e466a4451c7319e3a072d118917957d6554Steve Block 2546b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner def read_binary_file(self, path): 255ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch # Intentionally raises KeyError if we don't recognize the path. 256ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if self.files[path] is None: 257ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self._raise_not_found(path) 258ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return self.files[path] 259ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 2602fc2651226baac27029e38c9d6ef883fa32084dbSteve Block def relpath(self, path, start='.'): 2612fc2651226baac27029e38c9d6ef883fa32084dbSteve Block return ospath.relpath(path, start, self.abspath, self.sep) 2622fc2651226baac27029e38c9d6ef883fa32084dbSteve Block 263ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def remove(self, path): 264ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if self.files[path] is None: 265ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self._raise_not_found(path) 266ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.files[path] = None 2672fc2651226baac27029e38c9d6ef883fa32084dbSteve Block self.written_files[path] = None 268ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 269ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def rmtree(self, path): 2702fc2651226baac27029e38c9d6ef883fa32084dbSteve Block if not path.endswith(self.sep): 2712fc2651226baac27029e38c9d6ef883fa32084dbSteve Block path += self.sep 272ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 273ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch for f in self.files: 274ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if f.startswith(path): 275ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.files[f] = None 276ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 277ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def splitext(self, path): 278ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch idx = path.rfind('.') 279ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if idx == -1: 280ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch idx = 0 281ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return (path[0:idx], path[idx:]) 2826b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 2836b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner def write_text_file(self, path, contents): 2842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block return self.write_binary_file(path, contents.encode('utf-8')) 2856b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner 2866b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner def write_binary_file(self, path, contents): 2876b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner self.files[path] = contents 288ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.written_files[path] = contents 289f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch 290f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch 291ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdochclass WritableFileObject(object): 292ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def __init__(self, fs, path, append=False, encoding=None): 293ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.fs = fs 294ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.path = path 295ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.closed = False 296ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch if path not in self.fs.files or not append: 297ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.fs.files[path] = "" 298cad810f21b803229eb11403f9209855525a25d57Steve Block 299ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def __enter__(self): 300ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch return self 301cad810f21b803229eb11403f9209855525a25d57Steve Block 302ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def __exit__(self, type, value, traceback): 303ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.close() 304ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch 305ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def close(self): 306ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.closed = True 30765f03d4f644ce73618e5f4f50dd694b26f55ae12Ben Murdoch 308ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch def write(self, str): 309ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.fs.files[self.path] += str 310ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch self.fs.written_files[self.path] = self.fs.files[self.path] 3112bde8e466a4451c7319e3a072d118917957d6554Steve Block 3122bde8e466a4451c7319e3a072d118917957d6554Steve Block 3132bde8e466a4451c7319e3a072d118917957d6554Steve Blockclass ReadableFileObject(object): 3142bde8e466a4451c7319e3a072d118917957d6554Steve Block def __init__(self, fs, path, data=""): 3152bde8e466a4451c7319e3a072d118917957d6554Steve Block self.fs = fs 3162bde8e466a4451c7319e3a072d118917957d6554Steve Block self.path = path 3172bde8e466a4451c7319e3a072d118917957d6554Steve Block self.closed = False 3182bde8e466a4451c7319e3a072d118917957d6554Steve Block self.data = data 3192bde8e466a4451c7319e3a072d118917957d6554Steve Block self.offset = 0 3202bde8e466a4451c7319e3a072d118917957d6554Steve Block 3212bde8e466a4451c7319e3a072d118917957d6554Steve Block def __enter__(self): 3222bde8e466a4451c7319e3a072d118917957d6554Steve Block return self 3232bde8e466a4451c7319e3a072d118917957d6554Steve Block 3242bde8e466a4451c7319e3a072d118917957d6554Steve Block def __exit__(self, type, value, traceback): 3252bde8e466a4451c7319e3a072d118917957d6554Steve Block self.close() 3262bde8e466a4451c7319e3a072d118917957d6554Steve Block 3272bde8e466a4451c7319e3a072d118917957d6554Steve Block def close(self): 3282bde8e466a4451c7319e3a072d118917957d6554Steve Block self.closed = True 3292bde8e466a4451c7319e3a072d118917957d6554Steve Block 3302bde8e466a4451c7319e3a072d118917957d6554Steve Block def read(self, bytes=None): 3312bde8e466a4451c7319e3a072d118917957d6554Steve Block if not bytes: 3322bde8e466a4451c7319e3a072d118917957d6554Steve Block return self.data[self.offset:] 3332bde8e466a4451c7319e3a072d118917957d6554Steve Block start = self.offset 3342bde8e466a4451c7319e3a072d118917957d6554Steve Block self.offset += bytes 3352bde8e466a4451c7319e3a072d118917957d6554Steve Block return self.data[start:self.offset] 336