nntplib, huge xover object

Robin Munn rmunn at pobox.com
Wed Apr 9 18:08:40 EDT 2003


carroll at tjc.com <carroll at tjc.com> wrote:
> On Mon, 07 Apr 2003 20:39:13 GMT, Robin Munn <rmunn at pobox.com> wrote:
> 
>>The two approaches you describe in the paragraph above are really the
>>same approach, in a *slightly* different disguise. When you say "if the
>>caller invoked it with the wrong object type, he'll get an error," I'm
>>guessing that your idea of the "wrong object type" is an object that's
>>not a subclass of "file". But in Python, the "wrong object type" would
>>be... (drumroll)... Any object type that didn't have a method named
>>"write"!
> 
> I don't look at it that way.  The doc for the method specifies that
> the parameter is (if not a string) a file object.  That means that the
> wrong object type is any object type that isn't a file object.

But that is not the Python philosophy. The Python philosophy is "if it
quacks like a duck, then treat it like a duck". :-) In other words, you
can create a class that *behaves* like a file object, and you should be
able to pass it to any function that expects a file object. When I see
the words "a file object" in the documentation, I understand it to mean
any object that behaves like a file object. If this were not the case, I
would complain.

> Now, it happens that the only other method used for this object is the
> write method, but that's not guaranteed.  If someone called the method
> using an object that is not a file object, but that has a write method
> (e.g., a StringIO object), sure, it would work.  For now.  In this
> release.  But if the nntp method were changed in the future to do
> something else with the file object that actually depended on it being
> a file object, it would fail.  

But if it were changed to actually check the type of the object, so that
it refused (for instance) to accept a StringIO object, then I (and
dozens of other Python programmers) would complain bitterly, because we
would feel that the documentation contract had been broken.

Yes: I would feel that the documentation contract had been *broken* if
"this parameter is a file object" did not mean "this parameter behaves
like a file object". If what was meant was "a file object, AND NO OTHER
TYPE OF OBJECT, is required here" then I would expect to see that
wording explicitly stated.

The reason for all this is to support all kinds of nifty polymorphism.
Passing a StringIO object instead of a file object, for example, can be
very handy when you want to profile a piece of code and want to
eliminate the slowness of accessing a filesystem. Or maybe you want to
set up for some unit tests. Or you want to capture the output and do
some processing with it, but you don't want to have to make a temporary
file, write it, close it, read it, and delete it, all to get the data
back into a string. And that's just what I came up with in thirty
seconds of thinking!

> I submit that the failure is because the user is not passing it an
> object of the type that it is documented to require, because it says
> pretty plainly that it requires a file object, not merely any object
> that includes a method named write.  Using a non-file object is just
> something you can get away with.

You are right that the documentation could have been phrased better. At
the least it could have said "a file-like object". See, for instance,
the specifications for the functions dump() and load() in:

    http://www.python.org/doc/2.2/lib/node61.html

>>The key concept to understand is this: in Python, types usually don't
>>matter. What matters is interfaces. If you have a function that expects
>>to be passed a file object, for instance, someone could pass it a
>>StringIO object (http://www.python.org/doc/2.2/lib/module-StringIO.html)
>>instead. 
> 
> FWIW, nntplib will work with StringIO (or anything else that has a
> write method) -- not because it does interface checking on the object
> passed, but because, after it's type-checked whether it's a string
> (and if it's a string, it creates a file object with open() on the
> file named in the string), it assumes that any non-string object it's
> been passed is a file object (if you rely on the docs) or a object
> that supports write() (if you read the code), and goes ahead and uses
> write().  If the object doesn't have a write method, you'll get an
> exception.

Looking at the source for nntplib, I see what you mean. It checks
whether the file parameter is an instance of str (this does have one
failing, that it will fail to catch Unicode objects -- if your
filesystem accepts Unicode filenames, this might cause a problem), and
if it's not a string, then it treats it like a file.

Note that it does *not* explicitly check whether it was passed a file
object. This is how it should be. I couldn't find it off-hand in a
little bit of Googling, but I do remember coming across a tutorial on
writing in Python for those used to other languages. It had some good
advice: Never explicitly check the type of your parameters. Instead,
just treat them as the appropriate type, and allow AttributeError (or
whatever exception) to be raised if an inappropriate type is passed.

> In any event, I'm not planning on re-writing NNTPlib's file support --
> I'm just extending its existing file support to some additional NNTP
> commands.

And now that I look at it, I see you won't have to do any type-checking
code yourself. That's really all I was trying to do was warn you off of
doing explicit type-checking. I hope I've been able to explain properly
why that's not the Python way.

-- 
Robin Munn <rmunn at pobox.com>
http://www.rmunn.com/
PGP key ID: 0x6AFB6838    50FF 2478 CFFB 081A 8338  54F7 845D ACFD 6AFB 6838




More information about the Python-list mailing list