OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # |
| 3 # Copyright 2009 Google Inc. All Rights Reserved. |
| 4 # |
| 5 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 # you may not use this file except in compliance with the License. |
| 7 # You may obtain a copy of the License at |
| 8 # |
| 9 # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 # |
| 11 # Unless required by applicable law or agreed to in writing, software |
| 12 # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 # See the License for the specific language governing permissions and |
| 15 # limitations under the License. |
| 16 # |
| 17 # pylint: disable-msg=W0612,W0613,C6409 |
| 18 |
| 19 """A fake shutil module implementation that uses fake_filesystem for unit tests. |
| 20 |
| 21 Includes: |
| 22 FakeShutil: Uses a FakeFilesystem to provide a fake replacement for the |
| 23 shutil module. |
| 24 |
| 25 Usage: |
| 26 >>> import fake_filesystem |
| 27 >>> import fake_filesystem_shutil |
| 28 >>> filesystem = fake_filesystem.FakeFilesystem() |
| 29 >>> shutil_module = fake_filesystem_shutil.FakeShutilModule(filesystem) |
| 30 |
| 31 Copy a fake_filesystem directory tree: |
| 32 >>> new_file = filesystem.CreateFile('/src/new-file') |
| 33 >>> shutil_module.copytree('/src', '/dst') |
| 34 >>> filesystem.Exists('/dst/new-file') |
| 35 True |
| 36 |
| 37 Remove a fake_filesystem directory tree: |
| 38 >>> shutil_module.rmtree('/src') |
| 39 >>> filesystem.Exists('/src/new-file') |
| 40 False |
| 41 """ |
| 42 |
| 43 import errno |
| 44 import os |
| 45 import shutil |
| 46 import stat |
| 47 |
| 48 __pychecker__ = 'no-reimportself' |
| 49 |
| 50 _PERM_WRITE = 0o200 # Write permission bit. |
| 51 _PERM_READ = 0o400 # Read permission bit. |
| 52 _PERM_ALL = 0o7777 # All permission bits. |
| 53 |
| 54 |
| 55 class FakeShutilModule(object): |
| 56 """Uses a FakeFilesystem to provide a fake replacement for shutil module.""" |
| 57 |
| 58 def __init__(self, filesystem): |
| 59 """Construct fake shutil module using the fake filesystem. |
| 60 |
| 61 Args: |
| 62 filesystem: FakeFilesystem used to provide file system information |
| 63 """ |
| 64 self.filesystem = filesystem |
| 65 self._shutil_module = shutil |
| 66 |
| 67 def rmtree(self, path, ignore_errors=False, onerror=None): |
| 68 """Remove a directory and all its contents. |
| 69 |
| 70 Args: |
| 71 path: (str) Directory tree to remove. |
| 72 ignore_errors: (bool) unimplemented |
| 73 onerror: (func) unimplemented |
| 74 """ |
| 75 self.filesystem.RemoveObject(path) |
| 76 |
| 77 def copy(self, src, dst): |
| 78 """Copy data and mode bits ("cp src dst"). |
| 79 |
| 80 Args: |
| 81 src: (str) source file |
| 82 dst: (str) destination, may be a directory |
| 83 """ |
| 84 if self.filesystem.Exists(dst): |
| 85 if stat.S_ISDIR(self.filesystem.GetObject(dst).st_mode): |
| 86 dst = self.filesystem.JoinPaths(dst, os.path.basename(src)) |
| 87 self.copyfile(src, dst) |
| 88 src_object = self.filesystem.GetObject(src) |
| 89 dst_object = self.filesystem.GetObject(dst) |
| 90 dst_object.st_mode = ((dst_object.st_mode & ~_PERM_ALL) | |
| 91 (src_object.st_mode & _PERM_ALL)) |
| 92 |
| 93 def copyfile(self, src, dst): |
| 94 """Copy data from src to dst. |
| 95 |
| 96 Args: |
| 97 src: (str) source file |
| 98 dst: (dst) destination file |
| 99 |
| 100 Raises: |
| 101 IOError: if the file can't be copied |
| 102 shutil.Error: if the src and dst files are the same |
| 103 """ |
| 104 src_file_object = self.filesystem.GetObject(src) |
| 105 if not src_file_object.st_mode & _PERM_READ: |
| 106 raise IOError(errno.EACCES, 'Permission denied', src) |
| 107 if stat.S_ISDIR(src_file_object.st_mode): |
| 108 raise IOError(errno.EISDIR, 'Is a directory', src) |
| 109 |
| 110 dst_dir = os.path.dirname(dst) |
| 111 if dst_dir: |
| 112 if not self.filesystem.Exists(dst_dir): |
| 113 raise IOError(errno.ENOTDIR, 'Not a directory', dst) |
| 114 dst_dir_object = self.filesystem.GetObject(dst_dir) |
| 115 if not dst_dir_object.st_mode & _PERM_WRITE: |
| 116 raise IOError(errno.EACCES, 'Permission denied', dst_dir) |
| 117 |
| 118 abspath_src = self.filesystem.NormalizePath( |
| 119 self.filesystem.ResolvePath(src)) |
| 120 abspath_dst = self.filesystem.NormalizePath( |
| 121 self.filesystem.ResolvePath(dst)) |
| 122 if abspath_src == abspath_dst: |
| 123 raise shutil.Error('`%s` and `%s` are the same file' % (src, dst)) |
| 124 |
| 125 if self.filesystem.Exists(dst): |
| 126 dst_file_object = self.filesystem.GetObject(dst) |
| 127 if stat.S_ISDIR(dst_file_object.st_mode): |
| 128 raise IOError(errno.EISDIR, 'Is a directory', dst) |
| 129 if not dst_file_object.st_mode & _PERM_WRITE: |
| 130 raise IOError(errno.EACCES, 'Permission denied', dst) |
| 131 dst_file_object.SetContents(src_file_object.contents) |
| 132 |
| 133 else: |
| 134 self.filesystem.CreateFile(dst, contents=src_file_object.contents) |
| 135 |
| 136 def copystat(self, src, dst): |
| 137 """Copy all stat info (mode bits, atime, and mtime) from src to dst. |
| 138 |
| 139 Args: |
| 140 src: (str) source file |
| 141 dst: (str) destination file |
| 142 """ |
| 143 src_object = self.filesystem.GetObject(src) |
| 144 dst_object = self.filesystem.GetObject(dst) |
| 145 dst_object.st_mode = ((dst_object.st_mode & ~_PERM_ALL) | |
| 146 (src_object.st_mode & _PERM_ALL)) |
| 147 dst_object.st_uid = src_object.st_uid |
| 148 dst_object.st_gid = src_object.st_gid |
| 149 dst_object.st_atime = src_object.st_atime |
| 150 dst_object.st_mtime = src_object.st_mtime |
| 151 |
| 152 def copy2(self, src, dst): |
| 153 """Copy data and all stat info ("cp -p src dst"). |
| 154 |
| 155 Args: |
| 156 src: (str) source file |
| 157 dst: (str) destination, may be a directory |
| 158 """ |
| 159 if self.filesystem.Exists(dst): |
| 160 if stat.S_ISDIR(self.filesystem.GetObject(dst).st_mode): |
| 161 dst = self.filesystem.JoinPaths(dst, os.path.basename(src)) |
| 162 self.copyfile(src, dst) |
| 163 self.copystat(src, dst) |
| 164 |
| 165 def copytree(self, src, dst, symlinks=False): |
| 166 """Recursively copy a directory tree. |
| 167 |
| 168 Args: |
| 169 src: (str) source directory |
| 170 dst: (str) destination directory, must not already exist |
| 171 symlinks: (bool) copy symlinks as symlinks instead of copying the |
| 172 contents of the linked files. Currently unused. |
| 173 |
| 174 Raises: |
| 175 OSError: if src is missing or isn't a directory |
| 176 """ |
| 177 self.filesystem.CreateDirectory(dst) |
| 178 try: |
| 179 directory = self.filesystem.GetObject(src) |
| 180 except IOError as e: |
| 181 raise OSError(e.errno, e.message) |
| 182 if not stat.S_ISDIR(directory.st_mode): |
| 183 raise OSError(errno.ENOTDIR, |
| 184 'Fake os module: %r not a directory' % src) |
| 185 for name in directory.contents: |
| 186 srcname = self.filesystem.JoinPaths(src, name) |
| 187 dstname = self.filesystem.JoinPaths(dst, name) |
| 188 src_mode = self.filesystem.GetObject(srcname).st_mode |
| 189 if stat.S_ISDIR(src_mode): |
| 190 self.copytree(srcname, dstname, symlinks) |
| 191 else: |
| 192 self.copy2(srcname, dstname) |
| 193 |
| 194 def move(self, src, dst): |
| 195 """Rename a file or directory. |
| 196 |
| 197 Args: |
| 198 src: (str) source file or directory |
| 199 dst: (str) if the src is a directory, the dst must not already exist |
| 200 """ |
| 201 if stat.S_ISDIR(self.filesystem.GetObject(src).st_mode): |
| 202 self.copytree(src, dst, symlinks=True) |
| 203 else: |
| 204 self.copy2(src, dst) |
| 205 self.filesystem.RemoveObject(src) |
| 206 |
| 207 def __getattr__(self, name): |
| 208 """Forwards any non-faked calls to the standard shutil module.""" |
| 209 return getattr(self._shutil_module, name) |
| 210 |
| 211 |
| 212 def _RunDoctest(): |
| 213 # pylint: disable-msg=C6111,C6204,W0406 |
| 214 import doctest |
| 215 import fake_filesystem_shutil |
| 216 return doctest.testmod(fake_filesystem_shutil) |
| 217 |
| 218 |
| 219 if __name__ == '__main__': |
| 220 _RunDoctest() |
OLD | NEW |