5

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

Richard J
  • 6,883
  • 4
  • 22
  • 27

3 Answers3

7

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
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 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 Aug 11 '10 at 16:52
  • @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 Aug 11 '10 at 18:06
  • arg - no code markup in comments! – Richard J Aug 13 '10 at 15:41
  • @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 Aug 13 '10 at 15:48
4

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:
    ...
Katriel
  • 120,462
  • 19
  • 136
  • 170
  • 2
    trying to write to the file might not be appropriate in a unit test context. – Ned Batchelder Aug 10 '10 at 15:58
  • +1 for classical and renaissance thought patterns – Wayne Werner Aug 10 '10 at 15:59
  • @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`. – Katriel Aug 10 '10 at 16:00
  • @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 Aug 10 '10 at 16:00
  • @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 Aug 10 '10 at 18:05
  • 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 Aug 11 '10 at 16:55
2

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
  • 40,948
  • 31
  • 128
  • 181