• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2010 Google Inc. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Fake tempfile module.
16
17Fake implementation of the python2.4.1 tempfile built-in module that works with
18a FakeFilesystem object.
19"""
20#pylint: disable-all
21
22import errno
23import logging
24import os
25import stat
26import tempfile
27
28import fake_filesystem
29
30try:
31  import StringIO as io  # pylint: disable-msg=C6204
32except ImportError:
33  import io  # pylint: disable-msg=C6204
34
35
36class FakeTempfileModule(object):
37  """Uses a FakeFilesystem to provide a mock for the tempfile 2.4.1 module.
38
39  Common usage:
40  filesystem = fake_filesystem.FakeFilesystem()
41  my_tempfile_module = mock_tempfile.FakeTempfileModule(filesystem)
42
43  See also: default keyword arguments for Dependency Injection on
44  http://go/tott-episode-12
45  """
46
47  def __init__(self, filesystem):
48    self._filesystem = filesystem
49    self._tempfile = tempfile
50    self.tempdir = None  # initialized by mktemp(), others
51    self._temp_prefix = 'tmp'
52    self._mktemp_retvals = []
53
54  # pylint: disable-msg=W0622
55  def _TempFilename(self, suffix='', prefix=None, dir=None):
56    """Create a temporary filename that does not exist.
57
58    This is a re-implementation of how tempfile creates random filenames,
59    and is probably different.
60
61    Does not modify self._filesystem, that's your job.
62
63    Output: self.tempdir is initialized if unset
64    Args:
65      suffix: filename suffix
66      prefix: filename prefix
67      dir: dir to put filename in
68    Returns:
69      string, temp filename that does not exist
70    """
71    if dir is None:
72      dir = self._filesystem.JoinPaths(self._filesystem.root.name, 'tmp')
73    filename = None
74    if prefix is None:
75      prefix = self._temp_prefix
76    while not filename or self._filesystem.Exists(filename):
77      # pylint: disable-msg=W0212
78      filename = self._filesystem.JoinPaths(dir, '%s%s%s' % (
79          prefix,
80          next(self._tempfile._RandomNameSequence()),
81          suffix))
82    return filename
83
84  # pylint: disable-msg=W0622,W0613
85  def TemporaryFile(self, mode='w+b', bufsize=-1,
86                    suffix='', prefix=None, dir=None):
87    """Return a file-like object deleted on close().
88
89    Python 2.4.1 tempfile.TemporaryFile.__doc__ =
90    >Return a file (or file-like) object that can be used as a temporary
91    >storage area. The file is created using mkstemp. It will be destroyed as
92    >soon as it is closed (including an implicit close when the object is
93    >garbage collected). Under Unix, the directory entry for the file is
94    >removed immediately after the file is created. Other platforms do not
95    >support this; your code should not rely on a temporary file created using
96    >this function having or not having a visible name in the file system.
97    >
98    >The mode parameter defaults to 'w+b' so that the file created can be read
99    >and written without being closed. Binary mode is used so that it behaves
100    >consistently on all platforms without regard for the data that is stored.
101    >bufsize defaults to -1, meaning that the operating system default is used.
102    >
103    >The dir, prefix and suffix parameters are passed to mkstemp()
104
105    Args:
106      mode: optional string, see above
107      bufsize: optional int, see above
108      suffix: optional string, see above
109      prefix: optional string, see above
110      dir: optional string, see above
111    Returns:
112      a file-like object.
113    """
114    # pylint: disable-msg=C6002
115    # TODO: prefix, suffix, bufsize, dir, mode unused?
116    # cannot be cStringIO due to .name requirement below
117    retval = io.StringIO()
118    retval.name = '<fdopen>'  # as seen on 2.4.3
119    return retval
120
121  # pylint: disable-msg=W0622,W0613
122  def NamedTemporaryFile(self, mode='w+b', bufsize=-1,
123                         suffix='', prefix=None, dir=None, delete=True):
124    """Return a file-like object with name that is deleted on close().
125
126    Python 2.4.1 tempfile.NamedTemporaryFile.__doc__ =
127    >This function operates exactly as TemporaryFile() does, except that
128    >the file is guaranteed to have a visible name in the file system. That
129    >name can be retrieved from the name member of the file object.
130
131    Args:
132      mode: optional string, see above
133      bufsize: optional int, see above
134      suffix: optional string, see above
135      prefix: optional string, see above
136      dir: optional string, see above
137      delete: optional bool, see above
138    Returns:
139      a file-like object including obj.name
140    """
141    # pylint: disable-msg=C6002
142    # TODO: bufsiz unused?
143    temp = self.mkstemp(suffix=suffix, prefix=prefix, dir=dir)
144    filename = temp[1]
145    mock_open = fake_filesystem.FakeFileOpen(
146        self._filesystem, delete_on_close=delete)
147    obj = mock_open(filename, mode)
148    obj.name = filename
149    return obj
150
151  # pylint: disable-msg=C6409
152  def mkstemp(self, suffix='', prefix=None, dir=None, text=False):
153    """Create temp file, returning a 2-tuple: (9999, filename).
154
155    Important: Returns 9999 instead of a real file descriptor!
156
157    Python 2.4.1 tempfile.mkstemp.__doc__ =
158    >mkstemp([suffix, [prefix, [dir, [text]]]])
159    >
160    >User-callable function to create and return a unique temporary file.
161    >The return value is a pair (fd, name) where fd is the file descriptor
162    >returned by os.open, and name is the filename.
163    >
164    >...[snip args]...
165    >
166    >The file is readable and writable only by the creating user ID.
167    >If the operating system uses permission bits to indicate whether
168    >a file is executable, the file is executable by no one. The file
169    >descriptor is not inherited by children of this process.
170    >
171    >Caller is responsible for deleting the file when done with it.
172
173    NOTE: if dir is unspecified, this call creates a directory.
174
175    Output: self.tempdir is initialized if unset
176    Args:
177      suffix: optional string, filename suffix
178      prefix: optional string, filename prefix
179      dir: optional string, directory for temp file; must exist before call
180      text: optional boolean, True = open file in text mode.
181          default False = open file in binary mode.
182    Returns:
183      2-tuple containing
184      [0] = int, file descriptor number for the file object
185      [1] = string, absolute pathname of a file
186    Raises:
187      OSError: when dir= is specified but does not exist
188    """
189    # pylint: disable-msg=C6002
190    # TODO: optional boolean text is unused?
191    # default dir affected by "global"
192    filename = self._TempEntryname(suffix, prefix, dir)
193    fh = self._filesystem.CreateFile(filename, st_mode=stat.S_IFREG|0o600)
194    fd = self._filesystem.AddOpenFile(fh)
195
196    self._mktemp_retvals.append(filename)
197    return (fd, filename)
198
199  # pylint: disable-msg=C6409
200  def mkdtemp(self, suffix='', prefix=None, dir=None):
201    """Create temp directory, returns string, absolute pathname.
202
203    Python 2.4.1 tempfile.mkdtemp.__doc__ =
204    >mkdtemp([suffix[, prefix[, dir]]])
205    >Creates a temporary directory in the most secure manner
206    >possible. [...]
207    >
208    >The user of mkdtemp() is responsible for deleting the temporary
209    >directory and its contents when done with it.
210    > [...]
211    >mkdtemp() returns the absolute pathname of the new directory. [...]
212
213    Args:
214      suffix: optional string, filename suffix
215      prefix: optional string, filename prefix
216      dir: optional string, directory for temp dir. Must exist before call
217    Returns:
218      string, directory name
219    """
220    dirname = self._TempEntryname(suffix, prefix, dir)
221    self._filesystem.CreateDirectory(dirname, perm_bits=0o700)
222
223    self._mktemp_retvals.append(dirname)
224    return dirname
225
226  def _TempEntryname(self, suffix, prefix, dir):
227    """Helper function for mk[ds]temp.
228
229    Args:
230      suffix: string, filename suffix
231      prefix: string, filename prefix
232      dir: string, directory for temp dir. Must exist before call
233    Returns:
234      string, entry name
235    """
236    # default dir affected by "global"
237    if dir is None:
238      call_mkdir = True
239      dir = self.gettempdir()
240    else:
241      call_mkdir = False
242
243    entryname = None
244    while not entryname or self._filesystem.Exists(entryname):
245      entryname = self._TempFilename(suffix=suffix, prefix=prefix, dir=dir)
246    if not call_mkdir:
247      # This is simplistic. A bad input of suffix=/f will cause tempfile
248      # to blow up, but this mock won't.  But that's already a broken
249      # corner case
250      parent_dir = os.path.dirname(entryname)
251      try:
252        self._filesystem.GetObject(parent_dir)
253      except IOError as err:
254        assert 'No such file or directory' in str(err)
255        # python -c 'import tempfile; tempfile.mkstemp(dir="/no/such/dr")'
256        # OSError: [Errno 2] No such file or directory: '/no/such/dr/tmpFBuqjO'
257        raise OSError(
258            errno.ENOENT,
259            'No such directory in mock filesystem',
260            parent_dir)
261    return entryname
262
263  # pylint: disable-msg=C6409
264  def gettempdir(self):
265    """Get default temp dir.  Sets default if unset."""
266    if self.tempdir:
267      return self.tempdir
268    # pylint: disable-msg=C6002
269    # TODO: environment variables TMPDIR TEMP TMP, or other dirs?
270    self.tempdir = '/tmp'
271    return self.tempdir
272
273  # pylint: disable-msg=C6409
274  def gettempprefix(self):
275    """Get temp filename prefix.
276
277    NOTE: This has no effect on py2.4
278
279    Returns:
280      string, prefix to use in temporary filenames
281    """
282    return self._temp_prefix
283
284  # pylint: disable-msg=C6409
285  def mktemp(self, suffix=''):
286    """mktemp is deprecated in 2.4.1, and is thus unimplemented."""
287    raise NotImplementedError
288
289  def _SetTemplate(self, template):
290    """Setter for 'template' property."""
291    self._temp_prefix = template
292    logging.error('tempfile.template= is a NOP in python2.4')
293
294  def __SetTemplate(self, template):
295    """Indirect setter for 'template' property."""
296    self._SetTemplate(template)
297
298  def __DeprecatedTemplate(self):
299    """template property implementation."""
300    raise NotImplementedError
301
302  # reading from template is deprecated, setting is ok.
303  template = property(__DeprecatedTemplate, __SetTemplate,
304                      doc="""Set the prefix for temp filenames""")
305
306  def FakeReturnedMktempValues(self):
307    """For validation purposes, mktemp()'s return values are stored."""
308    return self._mktemp_retvals
309
310  def FakeMktempReset(self):
311    """Clear the stored mktemp() values."""
312    self._mktemp_retvals = []
313