[Python-Dev] Placement of os.fdopen functionality

Guido van Rossum guido@python.org
Thu, 10 Apr 2003 15:57:30 -0400

>   It occurred to me this afternoon (after answering aquestion about creating
> file objects from file descriptors) that perhaps os.fdopen would be more
> logically placed someplace else - of course it could also remain as
> os.fdopen() for whatever deprecation period is warrented.
>   Perhaps as a class method of the file type, file.fromfd()?
>   Should I file a feature request for this on sf, or would it be considered
> too much of a mindless twiddle to bother with?

The latter.

If I had to do it over again, your suggestion would make sense; class
methods are a good way to provide alternative constructors, and we're
doing this e.g. for the new datetime class/module.

But having this in the os module, which deals with such low-level file
descriptors, still strikes me as a pretty decent place to put it as
well, and I don't think it's worth the bother of updating
documentation and so on.

The social cost of deprecating a feature is pretty high.  In general,
I'm open to fixing design bugs if keeping the buggy design means
forever having to explain a wart to new users, or forever having to
debug bad code written because of a misunderstanding perpetuated by
the buggy design (like int division).  But in this case, I see no
compelling reason; explaining how to do this isn't particularly easier
or harder one way or the other.

Responses to other messages in this thread:

[Greg Ewing]
> Not all OSes have the notion of a file descriptor, which is probably
> why it's in the os module.

Perhaps, but note that file objects have a method fileno(), which
returns a file descriptor.  Its implementation is not #ifdefed in any
way -- the C stdio library requires fileno() to exist!

Even if fdopen() really did need an #ifdef, it would be just as simple
only to have the file.fdopen() class method when the C library defines
fdopen() as it is to only have os.fdopen() under those conditions.

[Oren Tirosh]
> I don't see much point in moving it around just because the place 
> doesn't seem right but the fact that it's a function rather than a
> method means that some things cannot be done in pure Python.
> I can create an uninitialized instance of a subclass of 'file' using
> file.__new__(filesubclass) but the only way to open it is by name
> using file.__init__(filesubclassinstance, 'filename'). A file
> subclass cannot be opened from a file descriptor because fdopen
> always returns a new instance of 'file'.
> If there was some way to open an uninitialized file object from a
> file descriptor it would be possible, for example, to write a
> version of popen that returns a subclass of file.  It could add a
> method for retrieving the exit code of the process, do something
> interesting on __del__, etc.

You have a point, but it's mostly theoretical: anything involving
popen() should be done in C anyway, and this is no problem in C.

> Here are some alternatives of where this could be implemented,
> followed by what a Python implementation of os.fdopen would look
> like:
> 1. New form of file.__new__ with more arguments:
>    def fdopen(fd, mode='r', buffering=-1):
>        return file.__new__('(fdopen)', mode, buffering, fd)

This violates the current invariant that __new__ doesn't initialize
the file with a C-level FILE *.

> 2. Optional argument to file.__init__:
>    def fdopen(fd, mode='r', buffering=-1):
>        return file('(fdopen)', mode, buffering, fd)
> 3. Instance method (NOT a class method):
>    def fdopen(fd, mode='r', buffering=-1):
>        f = file.__new__()
>        f.fdopen(fd, mode, buffering, '(fdopen)')
>        return f

Hm, you seem to be implying that it should not be a class method
because it should be possible to first create an uninitialized
instance with __new__ (possibly of a subclass) and then initialize it
separately.  Perhaps.  But since class methods also work for
subclasses, I'm not sure I see the use case for this distinction.

In any case I think this should wait until a future redesign of the
stdio library, which will probably do some other refactoring (while
staying compatible with the existing API).  I've checked in some rough
ideas in nondist/sandbox/sio/.

--Guido van Rossum (home page: http://www.python.org/~guido/)