tags:

views:

2041

answers:

5

I'm trying to write a simple python script that will copy a index.tpl to index.html in all of the subdirectories (with a few exceptions). But I'm getting bogged down by trying to get the list of subdirectories

+9  A: 
import os
def get_immediate_subdirectories(dir):
    return [name for name in os.listdir(dir)
            if os.path.isdir(os.path.join(dir, name))]
RichieHindle
The question is about getting a list of subdirectories, this will return all files in the current directory.
Shane C. Mason
Gah! Sorry, now fixed.
RichieHindle
+4  A: 

os.walk is your friend in this situation.

Straight from the doc

walk() generates the file names in a directory tree, by walking the tree either top down or bottom up. For each directory in the tree rooted at directory top (including top itself), it yields a 3-tuple (dirpath, dirnames, filenames).

Andrew Cox
+3  A: 

Using Twisted's FilePath module:

from twisted.python.filepath import FilePath

def subdirs(pathObj):
    for subpath in pathObj.walk():
        if subpath.isdir():
            yield subpath

if __name__ == '__main__':
    for subdir in subdirs(FilePath(".")):
        print "Subdirectory:", subdir

Since some commenters have asked what the advantages of using Twisted's libraries for this is, I'll go a bit beyond the original question here.


There's some improved documentation in a branch that explains the advantages of FilePath; you might want to read that.

More specifically in this example: unlike the standard library version, this function can be implemented with no imports. The "subdirs" function is totally generic, in that it operates on nothing but its argument. In order to copy and move the files using the standard library, you need to depend on the "open" builtin, "listdir", perhaps "isdir" or "os.walk" or "shutil.copy". Maybe "os.path.join" too. Not to mention the fact that you need a string passed an argument to identify the actual file. Let's take a look at the full implementation which will copy each directory's "index.tpl" to "index.html":

def copyTemplates(topdir):
    for subdir in subdirs(topdir):
        tpl = subdir.child("index.tpl")
        if tpl.exists():
            tpl.copyTo(subdir.child("index.html"))

The "subdirs" function above can work on any FilePath-like object. Which means, among other things, ZipPath objects. Unfortunately ZipPath is read-only right now, but it could be extended to support writing.

You can also pass your own objects for testing purposes. In order to test the os.path-using APIs suggested here, you have to monkey with imported names and implicit dependencies and generally perform black magic to get your tests to work. With FilePath, you do something like this:

class MyFakePath:
    def child(self, name):
        "Return an appropriate child object"

    def walk(self):
        "Return an iterable of MyFakePath objects"

    def exists(self):
        "Return true or false, as appropriate to the test"

    def isdir(self):
        "Return true or false, as appropriate to the test"
...
subdirs(MyFakePath(...))
Glyph
Since I have little exposure to Twisted, I always welcome additional info and examples; this answer is nice to see for that. Having said that, since this approach appears to require substantially more work than using the built-in python modules, and a Twisted install, are there any advantages to using this that you could add to the answer?
Jarret Hardie
Glyph's answer was probably inspired by the fact that TwistedLore also uses .tpl files.
Constantin
Well, clearly I don't expect the Spanish inquisition :-) I assumed "*.tpl" was a generic reference to some abstract extension meaning "template", and not a specific Twisted template (I've seen .tpl used in many languages after all). Good to know.
Jarret Hardie
+1 therefore for twigging to the possible Twisted angle, though I'd still like to understand what Twisted'd 'FilePath' object and 'walk()' function add to the standard API.
Jarret Hardie
Personally I find "FilePath.walk() yields path objects" a lot easier to remember than "os.walk yields 3-tuples of dir, dirs, files".But there are other benefits. FilePath allows for polymorphism, which means you can traverse things other than filesystems. For example, you could pass a twisted.python.zippath.ZipArchive to my 'subdirs' function and get a generator of ZipPaths out instead of FilePaths; your logic doesn't change, but your application now magically handles zip files. If you want to test it, you just have to supply an object, you don't have to write real files.
Glyph
A: 

Here's one way:

import os
import shutil

def copy_over(path, from_name, to_name):
  for path, dirname, fnames in os.walk(path):
    for fname in fnames:
      if fname == from_name:
        shutil.copy(os.path.join(path, from_name), os.path.join(path, to_name))


copy_over('.', 'index.tpl', 'index.html')
Scott Kirkwood
-1: won't work, since shutil.copy will copy to the current dir, so you'll end up overwriting 'index.html' in the current dir once for each 'index.tpl' you find in the subdirectory tree.
nosklo
Fixed the code, thanks!
Scott Kirkwood
+1  A: 

I just wrote some code to move vmware virtual machines around, and ended up using os.path and shutil to accomplish file copying between sub-directories.

def copy_client_files (file_src, file_dst):
    for file in os.listdir(file_src):
            print "Copying file: %s" % file
            shutil.copy(os.path.join(file_src, file), os.path.join(file_dst, file))

It's not terribly elegant, but it does work.