Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Me: I am running Python 2.3.3 without possibility to upgrade and i don't have much experience with Python. My method for learning is googling and reading tons of stackoverflow.

Background: I am creating a python script whose purpose is to take two directories as arguments and then perform comparisons/diff of all the files found within the two directories. The directories have sub-directories that also have to be included in the diff. Each directory is a List and sub-directories are nested Lists and so on...

the two directories:
oldfiles/
    a_tar_ball.tar
    a_text_file.txt
    nest1/
        file_in_nest
        nest1a/
            file_in_nest

newfiles/
    a_tar_ball.tar
    a_text_file.txt
    nest1/
        file_in_nest
        nest1a/

Problem: Normally all should go fine as all files in oldfiles should exist in newfiles but in the above example one of the 'file_in_nest' is missing in 'newfiles/'. I wish to print an error message telling me which file that is missing but when i'm using the code structure below the current instance of my 'compare' function doesn't know any other directories but the closest one. I wonder if there is a built in error handling that can send information about files and directory up in the recursion ladder adding info to it as we go. If i would just print the filename of the missing file i would not know which one of them it might be as there are two 'file_in_nest' in 'oldfiles'

def compare(file_tree)
    for counter, entry in enumerate(file_tree[0][1:]):
        if not entry in file_tree[1]
            # raise "some" error and send information about file back to the 
            # function calling this compare, might be another compare.
        elif not isinstance(entry, basestring):
            os.chdir(entry[0])
            compare(entry)
            os.chdir('..')
        else:
            # perform comparison (not relevant to the problem)

        # detect if "some" error has been raised
            # prepend current directory found in entry[0] to file information
            break

def main()
    file_tree = [['/oldfiles', 'a_tar_ball.tar', 'a_text_file.txt', \
                [/nest1', 'file_in_nest', [/nest1a', 'file_in_nest']], \
                'yet_another_file'], \
                ['/newfiles', 'a_tar_ball.tar', 'a_text_file.txt', \
                [/nest1', 'file_in_nest', [/nest1a']], \
                'yet_another_file']]

    compare(file_tree)

    # detect if "some" error has been raised and print error message

This is my first activity on stackoverflow other than reading som please tell me if i should improve on the question!

// Stefan

share|improve this question
1  
You do keep track of the full path via the current working directory, so you could use os.getcwd() to reconstruct the full file name. That said, modifying global state in a recursive function is generally considered a bad thing, so consider passing in absolute paths to the compare() function and not changing the working directory. os.walk() might also help. –  Sven Marnach Nov 12 at 17:39
 
@SvenMarnach I will take a look at os.walk() and compare it to os.chdir(), thanks. –  Enok82 2 days ago
 
I just saw that my code above will not do what i want it to so if you really want to see a working recursive example look at val's deep-compare of two lists below. –  Enok82 2 days ago

1 Answer

up vote 0 down vote accepted

Well, it depends whether you want to report an error as an exception or as some form of status.

Let's say you want to go the 'exception' way and have the whole program crash if one file is missing, you can define your own exception saving the state from the callee to the caller:

class PathException(Exception):
    def __init__(self, path):
        self.path = path
        Exception.__init__(self)

def compare(filetree):
    old, new = filetree
    for counter, entry in enumerate(old[1:]):
        if entry not in new:
            raise PathException(entry)
        elif not isinstance(entry, basestring):
            os.chdir(entry[0])
            try:
                compare(entry)
                os.chdir("..")
            except PathException as e:
                os.chdir("..")
                raise PathException(os.path.join(entry, e.path))
        else:
            ...

Where you try a recursive call, and update any incoming exception with the information of the caller.

To see it on a smaller example, let's try to deep-compare two lists, and raise an exception if they are not equal:

class MyException(Exception):
    def __init__(self, path):
        self.path = path
        Exception.__init__(self)

def assertEq(X, Y):
    if hasattr(X, '__iter__') and hasattr(Y, '__iter__'):
        for i, (x, y) in enumerate(zip(X, Y)):
            try:
                assertEq(x, y)
            except MyException as e:
                raise MyException([i] + e.path)
    elif X != Y:
        raise MyException([]) # Empty path -> Base case

This gives us:

>>> L1 = [[[1,2,3],[4,5],[[6,7,8],[7,9]]],[3,5,[7,8]]]
>>> assertEq(L1, L1)

Nothing happens (lists are similar), and:

>>> L1 = [[[1,2,3],[4,5],[[6,7,8],[7,9]]],[3,5,[7,8]]]
>>> L2 = [[[1,2,3],[4,5],[[6,7,8],[7,5]]],[3,5,[7,8]]] # Note the [7,9] -> [7,5]
>>> try:
...     assertEq(L1, L2)
... except MyException as e: 
...     print "Diff at",e.path
Diff at [0, 2, 1, 1]
>>> print L1[0][2][1][1], L2[0][2][1][1]
9 5

Which gives the full path.

As recursive lists or paths are basically the same thing, it is easy to adapt it to your use case.

Another simple way of solving this would be to report this difference in files as a simple diff, similar to the others: you can return it as a difference between the old file and the (non-existent) new file, or return both the list of differences in files and the list of differences of files, in which case it is easy to update recursively the values as they are returned by the recursive calls.

share|improve this answer
 
Thank you this was exactly what i was looking for! –  Enok82 2 days ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.