tags:

views:

115

answers:

4

Hi,

I'm writing some unit tests (using the unittest module) for my application, and want to write something which can verify that a method I'm calling returns a "file-like" object. Since this isn't a simple isinstance call, I wonder what the best-practice would be for determining this?

So, in outline:

possible_file = self.dao.get_file("anotherfile.pdf")
self.assertTrue(possible_file is file-like)

Perhaps I have to care which specific interface this file object implements, or which methods that make it file-like I want to support?

Thanks,

R

+2  A: 

Check, if returned object provides interface you are looking for. Like this for example:

self.assert_(hasattr(possible_file, 'write'))
self.assert_(hasattr(possible_file, 'read'))
gruszczy
Don't do this unless you're _sure_ that you want it.
katrielalex
Yeah, well I wouldn't do that. But he asked for a way to check, if something is file like, not to use it as a file.
gruszczy
+4  A: 

The classical Python mentality is that it's easier to ask forgiveness than permission. In other words, don't check, catch the exception caused by writeing to it.

The new way is to use an IO abstract base class in an isinstance check. This was introduced when people realised that duck typing is awesome, but sometimes you really do want an instance check.

In your case (unittesting), you probably want to try it and see:

thingy = ...
try:
    thingy.write( ... )
    thingy.writeline( ... )
    ...
    thingy.read( )
except AttributeError:
    ...
katrielalex
trying to write to the file might not be appropriate in a unit test context.
Ned Batchelder
+1 for classical and renaissance thought patterns
Wayne Werner
@Ned: really? If you're implementing a file-like class, surely you should test that it *does* write down what you give it? But if you really don't want that, then you can use `hasattr`.
katrielalex
@Ned, wouldn't that depend on what the file writing is actually doing? i.e. shouldn't a unit test "clean up" after testing to see if that part of the program really does write?
Wayne Werner
@Wayne, @katrielalex: yes, you might want to write to it, and you might get clean up automatically depending on what the file is. My point was that the logic that goes in typical programming (ask forgiveness) might not be appropriate in a unit test, depending on what the test was trying to accomplish. That's all.
Ned Batchelder
Thanks for this - just getting my head around ABCs, and combined with another answer looks like this is the way. Will do some more reading ...
Richard J
+4  A: 

There is no "official definition" of what objects are "sufficiently file-like", because the various uses of file-like objects have such different requirements -- e.g., some only require read or write methods, other require some subset of the various line-reading methods... all the ways to some requiring the fileno method, which can't even be supplied by the "very file-like objects" offered by StringIO and cStringIO modules in the standard library. It's definitely a question of "shades of gray", not a black-and-white taxonomy!

So, you need to determine which methods you need. To check for them, I recommended defining your own FileLikeEnoughForMe abstract base class with abstractmethod decorators, and checking the object with an isinstance for that class, if you're on Python 2.6 or better: this is the recommended idiom these days, rather than a bunch of hasattr checks which would be less readable and more complex (when properly beefed up with checks that those attributes are actually methods, etc;-).

Alex Martelli
Hi Alex, Thanks for the pointer, this is interesting stuff, although am still getting my head around it. Do you suggest to just use this for the unittest or should this be a general approach to managing file-like objects inside an application?
Richard J
@Richard, I'm barely "getting my feet wet" myself with production use of ABCs in Python (though the analogies with Haskell typeclasses, C++'s abstract base classes, Java interfaces, etc, do help me;-) so I can't (yet) solidly recommend them or otherwise in that context. I _think_ they'll be just fine (again by analogy with alternatives I do know very well) but I need a little more real-world tussling with them "under my belt" to feel really confident either way;-). But, for testing purposes, I already _do_ know they're "the cat's pajamas", i.e., excellent and useful indeed.
Alex Martelli
@Alex, sorry to bug you again, but I think I'm missing something. It looks like the ABC is great if you want to just define an interface to implement against, but given some arbitrary object which may or may not be file-like it doesn't seem to help in the type checking. For example: `class FileLikeEnoughForMe(metaclass=ABCMeta):` `@abstractmethod` `def not_a_file_method(self): pass` `FileLikeEnoughForMe.register(file)` `isinstance(possible_file, FileLikeEnoughForMe)`-> True, despite there being no not_a_file_method available. What am I doing wrong?
Richard J
arg - no code markup in comments!
Richard J
@Richard, have you looked at the code examples at http://docs.python.org/library/abc.html? Specifically the `MyIterable` example and its override of `__subclasshook__` -- that shows how to check for the presence of certain methods (though it's a simplified example that doesn't check they _are_ methods). It's not the best way to use ABCs, in general, because the lack of any explicit registration (or subclassing) robs you of the crucial advantage of verifying **intent** (the famous "problem of the `draw` method"), but if you're checking for presence that's the way to do it in 2.6+.
Alex Martelli
A: 

You should check to see if the fileno() callable is present. Here is a method that will do that.

def isfile(obj):
  return callable( getattr(obj, 'fileno') )

and you can use this in an assert

self.assertTrue(isfile(possible_file))
kashif
It seems that even this isn't reliable. According to the documentation at http://docs.python.org/library/stdtypes.html#bltin-file-objects :"File-like objects which do not have a real file descriptor should not provide this method!"So it seems that you can be file-like without implementing this method.
Richard J
Yes, that is correct. In my experience I haven't ever had to deal with such a 'file-like' object. In any case, this should still check for files and should suffice for your use case.
kashif