In article <14cf8b45-a3c0-489f-8aa9-a75f0f326...@n3g2000yqb.googlegroups.com> Rock <rocco.ro...@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
-- http://mail.python.org/mailman/listinfo/python-list