Inheritance and name clashes
Chris Torek
nospam at torek.net
Sun Oct 3 22:31:12 EDT 2010
In article <14cf8b45-a3c0-489f-8aa9-a75f0f326eaa at n3g2000yqb.googlegroups.com>
Rock <rocco.rossi at gmail.com> wrote:
>I've really been wondering about the following lately. The question is
>this: if there are no (real) private or protected members in Python,
>how can you be sure, when inheriting from another class, that you
>won't wind up overriding, and possibly clobbering some important data
>field of the parent class, which might compromise its entire
>functionality?
You are right, but this is a double-edged feature/bug as it allows
you to override stuff deliberately, including things the original
code-writer did not think you should be able to override. You
cannot get this particular benefit without a corresponding cost.
For instance (note, this may have been changed in a later version,
I am using rather old code for this particular project) I ran into
an issue with the SimpleXMLRPCServer code (really BaseHTTPServer),
which I fixed with the pipewrap() code below. I also needed a
number of extra features; see comments. To do this I had to make
use of a number of things that were not officially exported, nor
really designed to be override-able.
(I've snipped out some [I think] irrelevant code below, but left
enough to illustrate all this.)
--------
import socket, SocketServer, SimpleXMLRPCServer, xmlrpclib
import errno
def format_ipaddr(addr, do_reverse_lookup = True):
(host, port) = addr[:2]
fqdn = socket.getfqdn(host) if do_reverse_lookup else ''
return '%s[%s]' % (fqdn, host)
# For exceptions to be passed back to rpc caller (other exceptions
# are caught in the MgrServer and logged), use MgrError (or xmlrpclib.Fault).
class MgrError:
def __init__(self, exc_type = None, exc_val = None):
self.exc_type = exc_type
self.exc_val = exc_val
if exc_type is None or exc_val is None:
i = sys.exc_info()[:2]
if exc_type is None:
self.exc_type = i[0]
if exc_val is None:
self.exc_val = i[1]
# This gives us an opportunity to fix the "broken pipe" error that
# occurs when a client disconnects in mid-RPC.
# XXX I think this should really be done in BaseHTTPServer or similar.
# See also <http://trac.edgewall.org/ticket/1183>.
#
# However, we'd still want to override do_POST so that we can
# sneak the client address to the _dispatch function in self.server.
class pipe_eating_rqh(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
def pipewrap(self, f):
try:
f(self)
except socket.error, (code, msg):
if (code == errno.EPIPE or code == errno.ECONNRESET or
code == 10053): # 10053 for Windows
# self.log_message('Lost connection to client: %s',
# self.address_string())
logger.info('Lost connection to client: %s',
format_ipaddr(self.client_address))
else:
raise
def do_POST(self):
self.server.client_address = self.client_address
return self.pipewrap(
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST)
def report_404(self):
return self.pipewrap(
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.report_404)
class MgrServer(SocketServer.ThreadingMixIn,
SimpleXMLRPCServer.SimpleXMLRPCServer):
"""
The "Manager Server" adds a few things over a basic XML-RPC server:
- Uses the threading mix-in to run each rpc request in a
separate thread.
- Runs an (optional) periodic handler.
- Logs all requests with the logger.
- Handles "admin" requests specially.
- Logs "unexpected" exceptions, so that we catch server bugs
in the server log.
"""
def __init__(self, addr, periodic = None, *args, **kwargs):
SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, addr,
requestHandler = pipe_eating_rqh,
*args, **kwargs)
# Note, can't just change self.funcs[] into a dict of
# tuples without overriding system_methodHelp() too,
# so we'll use a separate parallel dictionary.
self.admin_label = {}
self.periodic = periodic
if periodic:
self.socket.settimeout(periodic[0])
# see __nonzero__ below
self.register_function(self.__nonzero__)
def get_request(self):
while True:
try:
result = self.socket.accept()
except socket.timeout:
self.periodic[1](*self.periodic[2])
else:
return result
# not reached
def _dispatch(self, method, params):
# Taken from SimpleXMLRPCServer.py but then stripped down and
# modified.
if method in self.admin_label:
... stuff snipped out here ...
try:
func = self.funcs[method]
except KeyError:
# regular SimpleXMLRPCServer checks for self.instance
# and if so, for its _dispatch here ... we're not using
# that so I omit it
func = None
if func is not None:
logger.debug('%s: %s%s',
format_ipaddr(self.client_address), method, str(params))
try:
return func(*params)
except MgrError, e:
# Given, e.g., MgrError(ValueError('bad value'))),
# send the corresponding exc_type / exc_val back
# via xmlrpclib, which transforms it into a Fault.
raise e.exc_type, e.exc_val
except xmlrpclib.Fault:
# Already a Fault, pass it back unchanged.
raise
except TypeError, e:
# If the parameter count did not match, we will get
# a TypeError with the traceback ending with our own
# call at "func(*params)". We want to pass that back,
# rather than logging it.
#
# If the TypeError happened inside func() or one of
# its sub-functions, the traceback will continue beyond
# here, i.e., its tb_next will not be None.
if sys.exc_info()[2].tb_next is None:
raise
# else fall through to error-logging code
except:
pass # fall through to error-logging code
# Any other exception is assumed to be a bug in the server.
# Log a traceback for server debugging.
# is logger.error exc_info thread-safe? let's assume so
logger.error('internal failure in %s', method, exc_info = True)
# traceback.format_exc().rstrip()
raise xmlrpclib.Fault(2000, 'internal failure in ' + method)
else:
logger.info('%s: bad request: %s%s',
format_ipaddr(self.client_address), method, str(params))
raise Exception('method "%s" is not supported' % method)
# Tests of the form:
# c = new_class_object(params)
# if c: ...
# are turned into calls to the class's __nonzero__ method.
# We don't do "if server:" in our own server code, but if we did
# this would get called, and it's reasonable to just define it as
# True. Probably the existing SimpleXMLRPCServer (or one of its
# base classes) should have done this, but they did not.
#
# For whatever reason, the xml-rpc library routines also pass
# a client's __nonzero__ (on his server proxy connection) to us,
# which reaches our dispatcher above. By registering this in
# our __init__, clients can do "if server:" to see if their
# connection is up. It's a frill, I admit....
def __nonzero__(self):
return True
def register_admin_function(self, f, name = None):
... more stuff snipped out ...
# --END-- threading XML RPC server code
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: gmail (figure it out) http://web.torek.net/torek/index.html
More information about the Python-list
mailing list