Discussion:
[Tutor] How to return a list in an exception object?
David Aldrich
2015-06-17 12:49:38 UTC
Permalink
Hi

I have a function that compares a set of files with a reference set of files. All files are compared and then, if any differences were found, an exception is raised:

class Error(Exception): pass

def check_results(file_list):

<snip>

if isDifferent:
raise Error('One or more result files differ from the reference result files')

I would like to pass back to the caller a list of the files that failed. How would I include that list in the exception object?

Best regards

David

_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Peter Otten
2015-06-17 16:26:46 UTC
Permalink
Post by David Aldrich
Hi
I have a function that compares a set of files with a reference set of
files. All files are compared and then, if any differences were found, an
class Error(Exception): pass
<snip>
raise Error('One or more result files differ from the reference
result files')
I would like to pass back to the caller a list of the files that failed.
How would I include that list in the exception object?
While you can pass them as just another argument
... pass
...
... raise UnexpectedFileContents("Some files differ from the reference",
["foo.txt", "bar.py", "baz.rst"])
... except UnexpectedFileContents as err:
... print(err)
... print("The problematic files are")
... print("\n".join(err.args[1]))
...
('Some files differ from the reference', ['foo.txt', 'bar.py', 'baz.rst'])
The problematic files are
foo.txt
bar.py
baz.rst
... def __init__(self, message, files):
... super().__init__(message)
... self.files = files
...
... raise UnexpectedFileContents("Some files differ from the reference",
["foo.txt", "bar.py", "baz.rst"])
... except UnexpectedFileContents as err:
... print(err)
... print("The problematic files are")
... print("\n".join(err.files))
...
Some files differ from the reference
The problematic files are
foo.txt
bar.py
baz.rst
... def __init__(self, message="Some files differ from the reference",
files=None):
... self.message = message
... self.files = files
... def __str__(self):
... s = self.message
... if self.files is not None:
... s = s + ": " + ", ".join(self.files)
... return s
...
Post by David Aldrich
try: raise UnexpectedFileContents(files=["foo.txt", "bar.py",
"baz.rst"])
... except UnexpectedFileContents as err: print(err)
...
Some files differ from the reference: foo.txt, bar.py, baz.rst


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Martin A. Brown
2015-06-17 16:22:21 UTC
Permalink
Greetings David,
Post by David Aldrich
I have a function that compares a set of files with a reference
set of files. All files are compared and then, if any differences
If you had been trying to compare files, I would have suggested
examining difflib, but it sounds like you are more interested in set
math on the elements of your file sets.

See a suggestion below for how to make sets work for you.
Post by David Aldrich
class Error(Exception): pass
<snip>
raise Error('One or more result files differ from the reference result files')
I would like to pass back to the caller a list of the files that
failed. How would I include that list in the exception object?
While often, there is only text in the arguments supplied to
Exception, you can stuff whatever you like in the args. The first
argument to Exception [0] should always be a human-readable string (text).
Here are two examples, the first supplying a list in the arguments:

raise Exception("A hummingbird stole my boiled turnips.", range(4))
# -- import os
raise Exception("Where is my sock-eating goat?", dict(os.environ))

Now, back to the question of comparing file sets. When you have two
sets that you want to compare, there are many different ways to look
at the differences. I like computing once the full set of
differences between sets and then working with the results.

When comparing two sets for what is missing, or what is extra, you end
up with three different output sets for any given set called A (your
reference set, let's say) and set called B (the subject of the test).

same: There's a set of elements present in both A and B.
only_a: There's a set of elements present only in A.
only_b: There's a set of elements present only in B.

In this case, you are particularly interested in the 'only_a' set.
These are the files (in your problem) which are in your reference set,
but not the other set/list.

But, don't forget that there are files in B that may not be in A!
Depending on what you are doing with this file set, you may be
undertaking extra work, or risking loss. [I imagine a file deleter or
file archiver which handles extra files.]

This may be more than you needed or wanted for solving your problem. I
couldn't help having contributed my thoughts, though, because....I have
made this very mistake, myself. Anyway, here's a function to show you
what I mean.

def differing(a, b):
a = set(a)
b = set(b)
same = a.intersection(b)
a_only = a.difference(b)
b_only = b.difference(a)
return same, a_only, b_only

# -- and now to use it

import random
a = random.sample(range(17), 10)
b = random.sample(range(17), 10)
same, a_only, b_only = differing(a, b)

If you wanted to make sure that there were no extra files, you could:

assert 0 == len(b_only)

Anyway, there are many ways to use the above, if you like it.

I wish you exceptional luck,

-Martin

[0] https://docs.python.org/2/library/difflib.html
[1] https://docs.python.org/3/library/exceptions.html#Exception
--
Martin A. Brown
http://linux-ip.net/
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Alan Gauld
2015-06-17 18:35:18 UTC
Permalink
Post by David Aldrich
I have a function that compares a set of files with a reference set of files.
All files are compared and then, if any differences were found, an exception
That's a pretty horrible way to deal with things. Exceptions should be
used for exceptional circumstances not as a way of passing data around
for a perfectly predictable circumstance.

Why not just pass back the list and if its empty you know it succeeded.
And since an empty list if False in a boolean sense you can even use it
like:

if check_results(): ...

when you don't care about the result content.
Post by David Aldrich
class Error(Exception): pass
<snip>
raise Error('One or more result files differ from the reference result files')
Is there any reason why you want to use an exception?
Is there an API that require it or something?
Its certainly possible to do it, but its a pretty horrible
idiom. Functions should return any data as a return value
not tucked inside an exception.

Valid exceptions would be things like incompatible file
types (being asked to compare data values to jpegs or mp3s
for example) or, just possibly, mismatching file sets
(different numbers say).
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Steven D'Aprano
2015-06-17 22:00:31 UTC
Permalink
Post by David Aldrich
Hi
I have a function that compares a set of files with a reference set of
files. All files are compared and then, if any differences were
[...]
Post by David Aldrich
I would like to pass back to the caller a list of the files that
failed. How would I include that list in the exception object?
Others have already answered your question, but I'm completely with Alan
on this one: don't do this. Have your function return a list of
differences. If there are no differences, return an empty list. The
caller then decides what to do:

diffs = checkfiles()
if diffs:
for fname in diffs:
print("file name %s is different" % fname)
repair_file(fname)
else:
raise NoDifferencesError(
"expected some differences, but didn't find any"
)
--
Steve
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
David Aldrich
2015-06-18 10:31:55 UTC
Permalink
Thanks very much for all the answers to my question. I have taken the advice to return a list of differing files, rather than raise an exception.

I do have a follow-up question:

I have a list of paths that contain files I want to check.

paths = [pathA, pathB]

then I check the files in each path and get a list of files that differ in some way:

for path in paths
differing_files_list = compare_files(path)

I would like to store the differing_files_list with the associated path so that, after doing all comparisons, I can print the all differing files, sorted by path:

for path in paths
for file in differing_files_list
print(path + file)

How would I do that association? I guess I need a two-dimensional list but I don't know how to build it.

Best regards

David

_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Alan Gauld
2015-06-18 11:46:50 UTC
Permalink
Post by David Aldrich
I have a list of paths that contain files I want to check.
paths = [pathA, pathB]
for path in paths
differing_files_list = compare_files(path)
I would like to store the differing_files_list with the associated path
So do just that.
There is a data structure called an associative array or,
in Python terms a dictionary.

If you make the path the key you can associate the list
of bad files with it.

paths = {pathA:[], pathB:[].....} # initialize with empty lists

for path in paths:
paths[path] = compare_files(path)
Post by David Aldrich
for path in paths
for file in differing_files_list
print(path + file)
becomes:

for path in paths:
for file in paths[path]:
print(path+file) # you might want to use os.path.join() here

HTH
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Continue reading on narkive:
Loading...