diff options
Diffstat (limited to 'lib/fixtures/_fixtures/tempdir.py')
-rw-r--r-- | lib/fixtures/_fixtures/tempdir.py | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/lib/fixtures/_fixtures/tempdir.py b/lib/fixtures/_fixtures/tempdir.py index fd5502c..3af8d00 100644 --- a/lib/fixtures/_fixtures/tempdir.py +++ b/lib/fixtures/_fixtures/tempdir.py @@ -18,6 +18,8 @@ __all__ = [ 'TempDir', ] +import errno +import os import shutil import tempfile @@ -43,6 +45,20 @@ class TempDir(fixtures.Fixture): self.path = tempfile.mkdtemp(dir=self.rootdir) self.addCleanup(shutil.rmtree, self.path, ignore_errors=True) + def make_tree(self, *shape): + """Make a tree of files and directories underneath this temp dir. + + :param shape: A list of descriptions of files and directories to make. + Generally directories are described as ``"directory/"`` and + files are described as ``("filename", contents)``. Filenames can + also be specified without contents, in which case we'll make + something up. + + Directories can also be specified as ``(directory, None)`` or + ``(directory,)``. + """ + create_normal_shape(self.path, normalize_shape(shape)) + class NestedTempfile(fixtures.Fixture): """Nest all temporary files and directories inside another directory. @@ -58,3 +74,74 @@ class NestedTempfile(fixtures.Fixture): tempdir = self.useFixture(TempDir()).path patch = fixtures.MonkeyPatch("tempfile.tempdir", tempdir) self.useFixture(patch) + + +def normalize_entry(entry): + """Normalize a file shape entry. + + 'Normal' entries are either ("file", "content") or ("directory/", None). + + Standalone strings get turned into 2-tuples, with files getting made-up + contents. Singletons are treated the same. + + If something that looks like a file has no content, or something that + looks like a directory has content, we raise an error, as we don't know + whether the developer really intends a file or really intends a directory. + + :return: A list of 2-tuples containing paths and contents. + """ + if isinstance(entry, basestring): + if entry[-1] == '/': + return (entry, None) + else: + return (entry, "The file '%s'." % (entry,)) + else: + if len(entry) == 1: + return normalize_entry(entry[0]) + elif len(entry) == 2: + name, content = entry + is_dir = (name[-1] == '/') + if ((is_dir and content is not None) + or (not is_dir and content is None)): + raise ValueError( + "Directories must end with '/' and have no content, " + "files do not end with '/' and must have content, got %r" + % (entry,)) + return entry + else: + raise ValueError( + "Invalid file or directory description: %r" % (entry,)) + + +def normalize_shape(shape): + """Normalize a shape of a file tree to create. + + Normalizes each entry and returns a sorted list of entries. + """ + return sorted(map(normalize_entry, shape)) + + +def create_normal_shape(base_directory, shape): + """Create a file tree from 'shape' in 'base_directory'. + + 'shape' must be a list of 2-tuples of (name, contents). If name ends with + '/', then contents must be None, as it will be created as a directory. + Otherwise, contents must be provided. + + If either a file or directory is specified but the parent directory + doesn't exist, will create the parent directory. + """ + for name, contents in shape: + name = os.path.join(base_directory, name) + if name[-1] == '/': + os.makedirs(name) + else: + base_dir = os.path.dirname(name) + try: + os.makedirs(base_dir) + except OSError, e: + if e.errno != errno.EEXIST: + raise + f = open(name, 'w') + f.write(contents) + f.close() |