R
rossabri
This topic has been addressed in limited detail in other threads:
[1] "sockets don't play nice with new style classes " May 14 2005.
http://groups.google.com/group/comp...read/thread/76d27388b0d286fa/c9849013e37c995b
[2] "Subclassing socket" Dec 20 2005 - Jan 14 2006.
http://groups.google.com/group/comp...read/thread/391728cd442339c8/c0581b9ee5e7ceaf
Briefly, the socket module ("socket.py") provides a wrapper for python
extension code ("_socket.so","Modules/socketmodule.*" in the source)
that is built on top of the system socket library ("socket.h",etc).
This wrapper attempts to provide a SocketType we can instantiate and
treat as a typical Python object. It does this by forwarding some of
its function calls (send, recv, etc.) to an object that it references
as self._sock. This "delegation" pattern has been used in the past to
allow built-in types to be treated like normal types.
The problem is that, in this instance, delegation fails to maintain the
inheritance behavior that we expect from normal types. (See [1] and [2]
for examples.) My question is: What can be done about this?
The issue is somewhat complicated. It seems to me that the crux of the
problem is that sockets can spawn other sockets via the .dup and
..accept methods. These calls pass right down to the system level and
return references to objects of the built-in type (ie, _socket.socket)
to interpreted code. All the interpreted code can do it try to "cast"
(in the sense of Java) the object to a non-built-in type. If "newsock"
is a reference to a _socket.socket object, the socket module does this
by calling (notionally) "SocketType(_sock=newsock)". The new object of
type SocketType delegates to the object of type _socket.socket via
self._sock. Please see the source code for a clearer picture.
Now I'll have a little chat with myself.
Q1) Can't we just have _socket.socket.dup and _socket.socket.accept
return something of type(self) rather than type _socket.socket?
A1) I don't think you can "tell" the compiled code about what "type" of
PyObject to return. Maybe the Python gods will bless us with a complete
answer here.
Q2) Ok, so it looks like we are stuck with delegation at some level or
another then, huh?
A2) Well, yeah, but in some sense Python is all about delegating data
and operations to compiled code. It's just a matter of how and where.
We want to maintain a clear object model in the interpreted code and
push the dirty work down to compiled code. The problem is that in the
case of the socket module, the "how and where" are not obvious because
of the way the accept and dup system calls work.
Q3) Are you saying that the way that sockets work is fundamentally
different than the way other built-in objects work?
A3) Yeah, I guess I am. There aren't really any other built-ins that
can reach down to compiled code and spawn themselves like that. When
stuff like that happens in Python, it is usually handled with a copy,
deepcopy, or some kind of factory function. Sockets just do some things
that are hard to shoe-horn into a standard Python idiom.
The easiest solution to this problem is probably to fix the delegation
mechanism so that it transparently supports inheritance. The .accept
and .dup methods would pass a _socket.socket object into
type(self).__init__. Some __getattribute__ magic would likely be
involved. It's not immediately obvious to me how to do this, but my
intuition is that it can be done fairly painlessly.
The ideal solution is to redesign both the _socket and socket modules
so that we don't have to resort to delegation. However, due to the
argument that I have given, this will likely result in an interface
change. Interface changes are painful, yes, but at least things become
more pythonic.
What does the peanut gallery think? How should we go about fixing this
problem?
[1] "sockets don't play nice with new style classes " May 14 2005.
http://groups.google.com/group/comp...read/thread/76d27388b0d286fa/c9849013e37c995b
[2] "Subclassing socket" Dec 20 2005 - Jan 14 2006.
http://groups.google.com/group/comp...read/thread/391728cd442339c8/c0581b9ee5e7ceaf
Briefly, the socket module ("socket.py") provides a wrapper for python
extension code ("_socket.so","Modules/socketmodule.*" in the source)
that is built on top of the system socket library ("socket.h",etc).
This wrapper attempts to provide a SocketType we can instantiate and
treat as a typical Python object. It does this by forwarding some of
its function calls (send, recv, etc.) to an object that it references
as self._sock. This "delegation" pattern has been used in the past to
allow built-in types to be treated like normal types.
The problem is that, in this instance, delegation fails to maintain the
inheritance behavior that we expect from normal types. (See [1] and [2]
for examples.) My question is: What can be done about this?
The issue is somewhat complicated. It seems to me that the crux of the
problem is that sockets can spawn other sockets via the .dup and
..accept methods. These calls pass right down to the system level and
return references to objects of the built-in type (ie, _socket.socket)
to interpreted code. All the interpreted code can do it try to "cast"
(in the sense of Java) the object to a non-built-in type. If "newsock"
is a reference to a _socket.socket object, the socket module does this
by calling (notionally) "SocketType(_sock=newsock)". The new object of
type SocketType delegates to the object of type _socket.socket via
self._sock. Please see the source code for a clearer picture.
Now I'll have a little chat with myself.
Q1) Can't we just have _socket.socket.dup and _socket.socket.accept
return something of type(self) rather than type _socket.socket?
A1) I don't think you can "tell" the compiled code about what "type" of
PyObject to return. Maybe the Python gods will bless us with a complete
answer here.
Q2) Ok, so it looks like we are stuck with delegation at some level or
another then, huh?
A2) Well, yeah, but in some sense Python is all about delegating data
and operations to compiled code. It's just a matter of how and where.
We want to maintain a clear object model in the interpreted code and
push the dirty work down to compiled code. The problem is that in the
case of the socket module, the "how and where" are not obvious because
of the way the accept and dup system calls work.
Q3) Are you saying that the way that sockets work is fundamentally
different than the way other built-in objects work?
A3) Yeah, I guess I am. There aren't really any other built-ins that
can reach down to compiled code and spawn themselves like that. When
stuff like that happens in Python, it is usually handled with a copy,
deepcopy, or some kind of factory function. Sockets just do some things
that are hard to shoe-horn into a standard Python idiom.
The easiest solution to this problem is probably to fix the delegation
mechanism so that it transparently supports inheritance. The .accept
and .dup methods would pass a _socket.socket object into
type(self).__init__. Some __getattribute__ magic would likely be
involved. It's not immediately obvious to me how to do this, but my
intuition is that it can be done fairly painlessly.
The ideal solution is to redesign both the _socket and socket modules
so that we don't have to resort to delegation. However, due to the
argument that I have given, this will likely result in an interface
change. Interface changes are painful, yes, but at least things become
more pythonic.
What does the peanut gallery think? How should we go about fixing this
problem?