[kamaelia-list] Re: Helpful Utilities
Hi Michael, Thank you very much for such a carefully thought out reply. I particularly enjoyed your examples illustrating your thought process. It shed light on considerations which I had not previously thought about. Clearly it is a balancing act between what is best for simplistic usages like I'm accustomed to and what scales cleanly for more enterprise style use cases. So this led me to this alternative set of possibilities: class TCPClient(Axon.Component.component): delay = 0 connect_timeout = 60 def __init__(self, host, port, **kwargs): super(TCPClient, self).__init__(**kwargs) self.CSA = None self.sock = None self.howDied = None self.host = host self.port = port class Port80Client_Declarative(TCPClient): port = 80 def __init__(self, host, **kwargs): port = kwargs.pop(port, self.port) super(Port80Client_Declarative, self).__init__(host, port, **kwargs) class KamaeliaWebsiteClient_Declarative(Port80Client): host = www.kamaelia.org def __init__(self, **kwargs): host = kwargs.pop(host, self.host) super(KamaeliaWebsiteClient_Declarative, self).__init__(host, **kwargs) ... I do prefer this more explicit alternative. Mainly it is the more explicit nature of actually instantiating TCPClient or Port80Client or KamaeliaWebsiteClient that appeals to me. I don't necessarily find value in the more explicit parameters passed to the super().__init__ call. [More on that after a quick question] The only downside of all this really is that it prevents this: TCPClient(host=www.kamaelia.org, port=80) Why is this not possible? My eyeball interpreter doesn't see any reason why that would not work with what you've written. I have indeed, and as you'll see I've taken your thoughts on board, and I think reached a similar conclusion :-) Thank you very much indeed. I greatly appreciate your explanations. May I suggest a slight variant on your proposed alternative? This variant retains the more explicit instantiations (__init__ calls from user code) while allowing a bit of the magic back in for inheritance (__init__ calls via super). class TCPClient(Axon.Component.component): delay = 0 connect_timeout = 60 def __init__(self, host, port, **kwargs): super(TCPClient, self).__init__(**kwargs) self.CSA = None self.sock = None self.howDied = None self.host = host self.port = port class Port80Client_Declarative(TCPClient): port = 80 def __init__(self, host, **kwargs): kwargs.setdefault(port, self.port) kwargs['host'] = host # Or if = py 2.4 # kwargs.update(host=host) super(Port80Client_Declarative, self).__init__(**kwargs) class KamaeliaWebsiteClient_Declarative(Port80Client): # this variation moves the default declaration fully into the init def __init__(self, **kwargs): kwargs.setdefault(host, www.kamaelia.org) super(KamaeliaWebsiteClient_Declarative, self).__init__ (**kwargs) ... This alternative would help users understand the fundamentally required arguments (from reading or IDE tooltips / autocompletion) while allowing relatively easy parent initialization. Apologies for the delay, but I really did need a holiday ;-) :-) No worries! I really need a holiday myself so I can fully appreciate how necessary some away time can be. Thanks, Steve --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups kamaelia group. To post to this group, send email to kamaelia@googlegroups.com To unsubscribe from this group, send email to kamaelia+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/kamaelia?hl=en -~--~~~~--~~--~--~---
[kamaelia-list] Re: Helpful Utilities
Michael, def say(start, finish, **kwargs): ... print kwargs ... say(finish=world, start=hello) {} Yes, I'm a big fan of named parameters. I've often wished that what you wrote above worked as well. It would often be nice if the required parameters were also included in the kwargs dictionary. But as much as that would simplify some scenarios, I can see how it would certainly complicate others. and jumping Kamaelia's support to 2.5 onwards by default. (Not suggesting 2.6 at present since there's a wide variety of libs we use that are still at 2.5 as well) Compatibility is good. Being up to date is nice too. I've been doing less with Kamaelia lately and a lot with Google App Engine (2.5.2) and I've been really missing the built-in named tuples from 2.6. class Port80Client_Declarative(TCPClient): port = 80 def __init__(self, host, **kwargs): kwargs.setdefault(port, self.port) kwargs['host'] = host # Or if = py 2.4 # kwargs.update(host=host) super(Port80Client_Declarative, self).__init__(**kwargs) class KamaeliaWebsiteClient_Declarative(Port80Client): # this variation moves the default declaration fully into the init def __init__(self, **kwargs): kwargs.setdefault(host, www.kamaelia.org) super(KamaeliaWebsiteClient_Declarative, self).__init__ (**kwargs) ... I'd like to fix up my utilities to use these idioms. Stylistically, do you prefer the first or second version of class provided defaults. To simplify: class A(Parent): default = 'default' def __init__(self, required, **kwargs) kwargs.setdefault('default', self.default) kwargs['required'] = required super(A, self).__init__(*kwargs) #OR class B(Parent): def __init__(self, required, **kwargs) kwargs.setdefault('default', 'default') kwargs['required'] = required super(A, self).__init__(*kwargs) I think B requires less typing and removes a mental indirection, but I think style A might make it easier to spot the defaults. Kamaelia/Apps/SA/ ... Incidentally, I view this namespace Kamaelia.Apps.SA - as owned by you - so if you don't like the names here, please let me know. If you're happy with these as a starting point though, let me know, and I'll merge those into trunk in that location. If you're not and want to modify it, please either mirror locally and send patches or let me know your google code ID, and I'll grant you access for editting what is essentially your code :-) I think that location is fine. Thank you. I would like to get SVN access to the branch though, if possible. I'm pretty tickled with set up I have for pulling SVN repos into my mercurial tree. I'd like SVN access if for no other reason that to give me an excuse to get the mercurial - SVN process working. (This is part of a wider plan intended to make code contribution simpler, easier, more diverse and less dependent on me - with more than a little flagrant idea stealing from CPAN/CTAN :-) Sounds good to me. And thank you! --Steve --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups kamaelia group. To post to this group, send email to kamaelia@googlegroups.com To unsubscribe from this group, send email to kamaelia+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/kamaelia?hl=en -~--~~~~--~~--~--~---
[kamaelia-list] Re: Helpful Utilities
Hi Michael, Thank you for the great feedback. I'm in the process of updating all the classes to incorporate many of your suggestions. Before I'm finished with that though, I have to tell you I'm really struggling with finding peace with the Inheritable Default Values approach. On Sun, Apr 5, 2009 at 12:53 PM, Michael Sparks - spark...@gmail.com wrote: tickmessage = True delay = 1 check_interval = None def __init__(self, **argd): super(SingleTick, self).__init__(**argd) if self.check_interval is None or self.check_interval self.delay: self.check_interval = self.delay ... * Use of class attributes to provide default values, and enabling the **argd code to override the default values. - cf http://www.kamaelia.org/InheritableDefaultValues First let me say that I read that link before my first submission and chose not to go that way because I didn't feel comfortable with it. After your feedback, I went back and re-read the wiki entry several more times. I'm feeling more comfortable, but there's still some reluctance. If possible, I'd like you to try to help me understand how to use this more comfortably. The most obvious problem I have with this approach is what you've already admitted is the major downside. You lose the self-documenting nature of the __init__ routines. If I have to go to the extra trouble to document these optional[*] parameters, where is this best place to do that? The class docstring? The init method docstring? Notice my [*] on the word optional? That is the second and more philosophical problem I have with the Inheritable Default Values approach. The name of the approach implies to me that we are going to use inheritance for default value parameters. And parameters that have default values are what I call optional. But, your wiki entry shows using this approach for ALL parameters; in effect making all parameters optional. And this really sticks in my craw. Closely related to that pain is what I see in your wiki entry of conflating two different scenarios: subclassing and factory functions. First let me address the all parameters are optional pain. I have found peace with using this inheritance method for limited decoupling of honestly optional parameters for the purposes of subclassing. Let's take my simple class SingleTick. Normally init would take 3 parameters: a delay, a tick_mesg, and check_interval. To me, it makes perfect sense to make the tick_mesg and check_interval optional as there are sensible defaults. But the delay parameter should IMO be required. It doesn't make sense to me to provide a default nor to decouple future subclasses from that core parameter. What message to send, how often to check for early termination, maybe a callback(?) for early termination - those kind of things seem nebulous enough to warrant the inheritance method. So I would suggest an init signature of: tick_mesg = True check_interval = None __init__(self, delay, **kwargs) But I don't think it makes sense to move delay into the **kwargs as well. And in your wiki example, I think the port and protocol parameters falls in this same category. It seems like those should be non-optional arguments broken out of more flexible parameters like socket options. While keeping basic necessary core parameters as non-optional does add slightly to the typing for factory functions, I don't think it's an unreasonable trade-off to stay in line with the principle of explicit is better than implicit. And while more esoteric parameters might change over time, I don't see a major risk is coupling those fundamentally required parameters. To boil it down, I can see this approach useful for optional parameters with defaults, but I think it sacrifices too much clarity for negligible gain when used for fundamentally necessary parameters. That said, it seems you came to a different internal equilibrium and I would really appreciate it if you could help me see it from your perspective. Thanks, Steve --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups kamaelia group. To post to this group, send email to kamaelia@googlegroups.com To unsubscribe from this group, send email to kamaelia+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/kamaelia?hl=en -~--~~~~--~~--~--~---
[kamaelia-list] Re: Helpful Utilities
Hi Michael, Attached is an updated python file which includes those same four utility components. I've incorporated most of your suggestions. In fact, I liked your suggested main loop simplification so much, that I took it a step further and made them simpler yet. I have also employed the inheritance mechanism for the default value parameters. I'm not dead set against using that mechanism for all the parameters, but I'd like you to take a look at this way first. And like my previous email said, I'd like you to try to help me find acceptance with using it for non-optional parameters like you seem to prefer. Thanks, Steve --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups kamaelia group. To post to this group, send email to kamaelia@googlegroups.com To unsubscribe from this group, send email to kamaelia+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/kamaelia?hl=en -~--~~~~--~~--~--~--- echofuncs.py Description: Binary data
[kamaelia-list] Re: Helpful Utilities
Hi Steve, On Saturday 04 April 2009 23:30:14 Steve wrote: I look forward to any thoughts or comments you might have on these components. For various (personal) reasons I won't be able to review this until Friday this week. Generally they look good though and would be welcome additions. There are a variety of changes and improvements I'd make, some which are stylistic, but some others would improve reuse. As an example of what I mean, I'd probably change SingleTick to be more like this: class SingleTick(Axon.ThreadedComponent.threadedcomponent): ''' This threaded component will wait delay seconds then send True out it's outbox and shutdown. You can specify an optional check_interval which will cause the component to periodically check it's control inbox for early termination signals. ''' Inboxes = { inbox: Ignored, control: Sending a message here will cause the component to terminate, } Outboxes = { outbox : If the delay is reached uninterrupted the tickmessage - default 'True' is sent here, signal : producerFinished() if not interrrupted, otherwise the interruption/termination message, } tickmessage = True delay = 1 check_interval = None def __init__(self, **argd): super(SingleTick, self).__init__(**argd) if self.check_interval is None or self.check_interval self.delay: self.check_interval = self.delay def main(self): delay_until = time.time() + self.delay self.pause(self.check_interval) now = time.time() while not self.dataReady('control') now delay_until: remainder = delay_until - now if remainder self.check_interval and remainder 0: self.pause(remainder) else: self.pause(self.check_interval) now = time.time() if self.dataReady('control'): self.send(self.recv('control'), 'signal') else: self.send(self.tickmessage, 'outbox') self.send(producerFinished(self), 'signal') The differences here include: * More meta data around use of inboxes outboxes. (Enabling the doc generator to extract this) - eg http://www.kamaelia.org/Components/pydoc/Kamaelia.File.Append.Append * Use of class attributes to provide default values, and enabling the **argd code to override the default values. - cf http://www.kamaelia.org/InheritableDefaultValues * Logic change in loop to make the code paths clearer. Regards, Michael. -- http://yeoldeclue.com/blog http://twitter.com/kamaelian http://www.kamaelia.org/Home --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups kamaelia group. To post to this group, send email to kamaelia@googlegroups.com To unsubscribe from this group, send email to kamaelia+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/kamaelia?hl=en -~--~~~~--~~--~--~---