[06:53:29] jack-e|away is now known as jack-e [07:47:02] ** vlado_ has joined us [10:15:02] ** Maniac has joined us [10:15:40] greetings peakers [10:20:56] peaker greets back [10:22:18] my xscreensaver brought my whole system down ! [10:22:20] * Maniac curses [10:24:46] http://randomthoughts.vandorp.ca/jabberclient.py <-- [10:27:57] just remembered that one and loaded it .. but i still need to finish my zope2-with-ads-bugfixing-work [12:02:51] ** pje has joined us [12:03:00] Howdy. [12:03:16] * pje just took a quick look at Seaside. [12:03:40] AFAICT it's actually a pretty sucky way to do web interactions. [12:04:42] hey phihllip [12:04:44] ups [12:04:48] phillip .. [12:04:52] Of course, that's probably because I believe using sessions in HTTP is inherently evil. :) [12:04:59] Hey Jack. [12:05:19] how can you build larger sites without sessions ? [12:05:48] jack-e: do you need *sessions*, or do you need *session-specific objects* ? [12:05:54] E.g. ShoppingCart. [12:06:30] In peak.web, you create bindings in your Interaction subclass to load such objects. [12:06:44] You might do it using some kind of cookie, of course. [12:06:51] i don't need it at all, cause i'm not doing http currently .. but the sites i worked on needed information about the user, his recebt interactions, wizard-stuff, etc [12:07:24] Okay, so isn't that what a User object is for? [12:07:29] (User info) [12:07:53] yes [12:08:11] So, you put a binding for user in the Interaction subclass. [12:08:18] And then just use interaction.user to get at it. [12:09:02] I just think that talking about "sessions" is evil because there is no such thing as a session. [12:09:19] There are cookies. There are persistent objects. You can use one to get the other. [12:09:45] "Session" sounds like there's one thing -- and it's usually implemented as just a "bag of data". [12:09:57] So, it tends to subvert proper domain analysis. [12:09:57] i think of a session as a container, that is assigned to the principal that has logged in [12:10:21] where i can leave things at one request and get it from at another [12:10:51] Using "data" objects like that tends to put locus of control in other objects, though. [12:11:17] You end up writing code to manipulate this "container", instead of putting these domain methods on a "cart" or "user", or whatever sort of object it should be. [12:11:37] In peak.web, you'll get those objects from a DM, just like you get any other object. [12:12:09] But, so that it's simple, you will put a binding in the interaction that fetches from the DM, using whatever keys are provided by the interaction.request cookies or form variables. [12:12:47] So, then e.g interaction.cart or interaction.user or interaction.wizardInfo or whatever will retrieve it. [12:13:24] The same binding would also have to deal with assigning a key and creating a new object, if no key is present in the request. [12:13:30] But that might be just: [12:13:38] if 'myKey' in self.request: [12:13:52] return self.cartDM[self.request.myKey] [12:13:54] else: [12:14:06] newCart = self.cartDM.newItem() [12:14:22] key = self.cartDM.oidFor(newCart) [12:14:33] moment .. to many parallel chats and real life talks at once ... brb [12:14:35] self.response.setCookie('myKey',key) [12:14:43] return newCart [12:18:35] * jack-e is back [12:19:27] sounds good .. it's just that you then have to write other code to ensure that the objects created in your DM's but aren't actually used, cause the user just didn't do the "next" click [12:19:42] What? [12:19:52] But you have that anyway. [12:19:55] are deleted again [12:20:00] ? [12:20:10] You have to have a deletion strategy anyway. [12:20:38] Surely you don't expect that *all* sessionable objects last the same amount of time? [12:20:45] Consider e.g. Amazon, Yahoo, etc. [12:21:04] They have different "session" durations for "secured" vs. "not secured" states. [12:21:44] Actually, Yahoo doesn't seem to use sessions for anything *but* login control, but I digress. :) [12:22:26] Anyway, deletion is just an IPeriodicTask. [12:22:36] true [12:22:50] So, as long as you have a query or routine for whatever backend where your data is stored, you just add the task to the main application based on EventDriven. [12:23:03] And then cleanup will run automatically between web hits. [12:23:25] (Assuming you either use FastCGI or enable the fork-on-exit cleanup facility) [12:23:57] * jack-e is happy that he's not in charge to design web-apps :)) [12:24:21] One other thing that sessions do (that's bad) is that they tend to create a separate infrastructure for session management. [12:24:52] That is, instead of application data being stored together, data gets split between webserver session files and application databases. [12:25:12] How can you run queries on current session data if they're split across a bunch of webservers? [12:25:49] Backups are harder... and not all session systems guarantee transactional integrity, or sync properly to the webapp transactions. [12:26:16] The approach used by peak.web has none of these issues, which IMO are very important in the "enterprise" environment. [12:27:22] And it's not really any harder to use; you just have to be explicit about the mapping from cookies to your model objects, and about the cleanup of model objects. [12:27:44] But, again, this explicitness is a benefit in an enterprise environment, even if it's a bit of a pain for smaller apps. [12:27:54] right [12:28:16] However, there's nothing stopping you from creating a 'session' binding on your interaction class, and having a DM that writes them to files. [12:28:31] (that's why contributing simple but complete examples for peak is somewhat hard :) [12:28:49] they tend to be not intuitiv for non-peakers [12:28:52] And if I have a sufficiently quick-and-dirty need, I *might* even use the 'session' trick. [12:28:58] (While holding my nose.) [12:29:06] jack-e, yeah. [12:29:09] I know. [12:29:11] .) [12:29:32] The more beautiful the example, the less intuitive, sorta. [12:29:39] yep [12:29:49] A little too much like Lisp in that respect. :) [12:30:12] * jack-e has never touched lisp (except editing some emacs-files) [12:30:40] I'm going off the lisp examples that Lispers have claimed as things of beauty, but made my head hurt. :) [12:30:52] hehe [12:30:57] And I do understand the beauty, I just don't understand the *code*. :) [12:33:28] * pje is glad he's not the only one having trouble coming up with examples. [12:33:32] No, wait. [12:33:38] I'm *not* glad. :( [12:33:55] 'cause that means I don't get any *help*. :) [12:34:31] yes [12:38:20] ** lex has joined us [12:44:39] pje, http://randomthoughts.vandorp.ca/jabberclient.py < --- so does this look like total crap or not? [12:45:56] ** lex_ has joined us [12:46:11] * pje thinks it looks like a URL. :) [12:46:41] Btw, you don't need 'from protocols import interfaces'. [12:46:56] (You didn't actually use it, anyway.) [12:47:56] For the interfaces, you should be using offerAs=[IJabberIqHandler] [12:48:07] *not* offerAs=['IJabberIqHandler'] [12:48:29] The latter is offering PropertyName('IJabberIqHandler') [12:49:34] Which means you could Obtain(IJabberIqHandler) without needing to use the PropertyName stuff. [12:49:44] Interfaces are suitable for use as configuration keys. [12:50:11] In fact, if you can write an adapter to IConfigKey for something, you can use it in an Obtain or offerAs=. [12:50:45] (Technically, Obtain() wants an IComponentKey instance, but there is a default adaptation from IConfigKey -> IComponentKey. [12:52:06] I would also change the XmlStreamFactory = None to: [12:52:12] def XmlStreamFctory(self): [12:52:35] factory = client.basicClientFactory(self.jid,self.secret) [12:52:51] factory.addBootstrap('//event/stream/authd',self.authd) [12:53:11] self.reactor.connectTCP(self.jabberhost, self.jabberport, factory) [12:53:16] return factory [12:53:36] XmlStreamFactory = binding.Make(XmlStreamFactory, uponAssembly=True) [12:53:40] And then you don' [12:53:47] Er, don't need __onStart. [12:55:18] Oh, and there's a problem with your handler defaults. [12:55:27] Defaults are *shared* between instances. [12:55:41] So, if you write an app that uses more than one instance of this class with the default, you're sunk. [12:56:32] What you need to do here is to separate the notion of a factory from the instance, e.g.: [12:56:56] is it true, that defaults are shared, even if you reassign them within the class with e.g. self.XmlStreamFactory = Foo() ? [12:57:28] iqHandlerFactory = binding.Obtain(PropertyName('jabber.iqHandlerFactory'), default=IqHandler) [12:57:56] i thought this would only be a problem if you e.g. do: [12:58:01] iqHandler = binding.Make(lambda self: self.iqHandlerFactory(), offerAs=[IJabberIqHandler]) [12:58:02] class SomeClass(object): [12:58:12] test = dict() [12:58:24] and then say self.test[key] = val [12:58:29] jack-e, I'm talking about the 'default=' keyword argument to binding.Obtain. [12:58:36] ahh .. ok [12:58:47] Whatever value you assign to that keyword, is shared by all instances of the class that end up using the default. [12:58:53] So, the default is only for constants. [12:59:01] ah .. good to know :) [12:59:21] Maybe I should also allow a 'defaultFactory' that you could give a lambda to. [12:59:37] sounds reasonable [12:59:54] great feedback pje ! [12:59:55] Then 'defaultFactory = lambda: IqHandler()' would be an acceptable replacement for default=IqHandler() [13:02:49] * Maniac has to grab lunch back in 15 [13:03:51] Of course, at that point it's just like binding.Make! [13:04:10] Interesting. [13:14:50] * Maniac returns [13:19:35] mm i need to provide some sort of feedback if it cant' connect as well [13:26:47] bye [13:26:53] jack-e is now known as jack-e|away [13:43:20] * Maniac doesn't follow the factory instance thing [13:46:15] Maniac, when you do default=IqHandler(), you're creating an IqHandler *instance*, and passing it to default. [13:46:43] So, each instance of the class that contains that binding, if it fails to find something via the Obtain, it will use 'default'. [13:46:51] And 'default' is that instance of IqHandler you created. [13:47:13] Thus, that instance can be shared between multiple instances. [13:47:16] This is bad. [13:47:32] * pje thinks a second. [13:47:39] Or did you mean you didn't understand the *solution*? [13:49:26] i mean the solution :) [13:50:16] but i think i got it [13:51:02] no, i dont' [13:52:53] my latest version is up. i obviously have something wrong. i end up with multiple iqHandlers, i.e. everytime i get an iq ANOTHER iq handler get instantiated [13:54:13] * pje doesn't see how. [13:55:22] authd registers self.iqHandler.handle, so there could be only one iqHandler, right? [13:55:37] Unless authd gets called more than once, and even then it should still be the *same* iqHandler. [13:56:03] I do notice, btw, that your IqHandler class doesn't have a 'log' binding. [13:56:20] Its handle() says 'self.log.debug()', but there's no self.log [13:56:40] ok, no it looks like gaim is crazy [13:58:24] yes, gaim sends WAAY to many presence packets. works fine with PSI [13:59:19] wrt IqHandler log binding, i was debating whether to provide logging with the default handlers [14:01:49] this is cleaner, the __onStart was just from the twistedpeak example on the wiki [14:03:31] pje, also you mentioned the other day that you could do: myXRNoteRecv = binding.Make(lambda: XRNoteReceiver(port=80,...)) [14:04:17] or in this case myJabberClient = binding.Make(lambda: JabberClient(jabberport=5222,...)) [14:04:24] right? [14:09:44] sorry, looked away there... [14:10:07] Yes, you can use a no-arg lambda in a Make. [14:10:17] As long as you don't need any arguments in it. :) [14:11:50] ** mish has joined us [14:12:40] Hi. Are you aware of tut.pdf printing problems? [14:14:14] mish: Use Acrobat 4, Acrobat 6, or Ghostscript. [14:14:15] Acrobat 5 is broken. [14:14:29] The PDF itself is fine. :) [14:14:48] Ough, OK Thanks [14:30:05] i guess what i'm asking is the only way to obtain the confiuration values is if they are in a config file or specifically offerAs'd ? [14:33:24] Eh? [14:33:29] heh [14:33:32] There are many many ways to configure components. [14:33:42] someComponent.someAttr = aValue [14:33:47] [15:23:23] Somebody can explicitly do 'myXRNoteRecv = binding.Make(lambda: XRNoteReceiver(port=80,...)) <- i dont' understand this [14:33:56] aComponent = ComponentClass(someAttr=aValue) [14:34:40] Am I addressing the right question? [14:34:48] then from python interpret i should be able to run the jabberclient right? [14:35:06] python interpreter [14:36:25] Yeah... [14:36:36] root = config.makeRoot() [14:36:54] client = JabberClient(root) [14:37:08] Or just, JabberClient(config.makeRoot()) [14:37:39] Except then you'd have no way to tell the reactor to stop... [14:37:51] OTOH, you're not telling the reactor to *start* either. [14:38:11] So I guess you need to at least do client=JabberClient(config.makeRoot()) [14:38:18] And then client.reactor.start(). [14:45:25] cool, that worked [14:49:56] danka! [14:53:57] * pje goes into marketspeak and blathers something about the "magic of components"... [14:54:03] :) [14:59:33] i like components [14:59:38] they make me feel nice [14:59:50] see, i present my mistakes, you fix, i add to wiki ! [15:42:23] ** lex has joined us [15:56:13] ok latest version is up: http://randomthoughts.vandorp.ca/jabberclient.py new improved with feedback! [16:06:10] ** lex has joined us [16:08:05] y Winows y Winows achin is ying a slow an painul ath... [16:09:36] and your keyboard is broken apparently [16:12:21] ** pje has joined us [16:13:03] re [16:13:03] * pje 's machine went down [16:13:03] Win98, what can I say? [16:13:44] you USE win98? your a cwazy wabbit [16:14:17] at least use win2k [16:14:52] * Maniac talks, epiphany-bin just died and won't restart ! [16:27:46] * pje plans to try Win2K tomorrow actually... [16:28:09] I just upgraded machines at home, so I have a duplicate of my existing setup on a now-disposable box. [16:28:17] So, I can experiment without fear. :) [16:44:21] * Maniac wonders what magical thing he can create with peak next (besides a very basic jabber client 'component') [17:03:46] * pje is trying to design a Unix process management facility for PEAK [17:03:46] It's to use with http://www.eby-sarna.com/pipermail/peak/2003-August/000697.html [17:03:46] I think the basic concept is that there's a ProcessFactory, ProcessProxy, and ProcessStub. [17:03:46] bah, that sounds too complicated [17:03:46] I wasn't saying you had to do it. :) [17:03:46] The ProcessFactory will create the proxy and stub as children of itself, along with any needed pipes. [17:03:46] KISS <-- keep it simple for the stupid (me) [17:03:46] The Proxy and Stub will have bindings for each end of the pipe. [17:03:46] pipe(s), rather. [17:03:46] And the Proxy will have other process control bindings like, "is this process live", and ability to subscribe for events. [17:04:03] A ProcessManager in the parent will have a table of pids -> Proxy objects, so that it can process signals and reaping, etc. [17:04:12] (in the parent process, I mean.) [17:05:56] So, the way it ends up is that you'll just subclass ProcessFactory or create customized instances, in order to define things you want to manage. [17:06:02] Hm. Maybe the Stub side could be any PEAK IExecutable. [17:10:07] And I imagine it makes sense to make the ProcessFactory be a Command. [17:10:38] Maybe there isn't really a need for a proxy separate from the factory, either. [17:11:12] So, the command just needs a subcommand, that gets run on the other side, perhaps with redirections. [17:11:24] (other side = child process) [17:18:01] I guess I need to define the process management interfaces first, and then worry about some of these API details. [17:19:54] Maybe all I need to do is have the process manager have an API that takes a command or command factory, and runs it in a different process, returning a process proxy. [17:21:39] That sounds good, except for how to define pre- and post- fork activities for parent and child. [17:22:13] Maybe the command could be adapted to IParentProcess and IChildProcess... [17:22:23] Or IProcessProxy and IProcessStub. [17:22:46] Yes... this is sounding good, actually. It's almost like a VERY high-level "pipe()" function. :) [17:23:26] So you do proxy = processManager.invoke(commandObject) in the parent, and get a proxy to manage the process with. [17:23:50] Or, I guess you get None in the child. [17:24:04] Hm. [17:24:23] Messy. [17:24:30] ** pyneak has joined us [17:24:33] ~/projects/sandbox/twisted)> peak runIni bot.ini [17:24:41] * Maniac greets pyneak [17:25:11] my irc bot is a runnable ini file ! yay peak ! [17:25:29] Nice. [17:25:33] I haven't even done that. :) [17:25:44] it's just the simple log bot (minus the logging) [17:25:57] peak-logbot is just a hacked version of the Twisted logbot, without any PEAK mods. [17:26:18] you just hacked the log function no? [17:26:39] Yeah; I use 'print >>file,' :) [17:27:03] With automatic rollover by the inelegant means of opening a fresh file handle for every line. :) [17:27:38] I also fixed it to auto-reconnect if there's a connect failure, which was what kept causing it to die. [17:28:07] ah, and the file is just todays date [17:28:13] Yep. [17:28:16] it needs to log to html :) [17:28:52] Submit a patch. :) [17:29:03] heh, my peakification is very hackish anyways [17:29:12] Weren't you the guy who was logging with Unix timestamps? :) [17:29:44] yes, but that was the bot not me! [17:29:59] You know what I mean. [17:30:32] http://randomthoughts.vandorp.ca/ircbot.py [17:32:21] FYI, you could make your channel processing simpler if you just expect channels to be a sequence. [17:32:28] Then, in the .ini you do [17:32:38] channels = 'foo', 'bar', 'baz' [17:32:45] This is much better. [17:33:03] Also, btw, just a reminder, but it is really good practice to use a non-root namespace for your property names. [17:33:19] how-so? [17:33:24] e.g. 'ircbot.nickname' instead of 'nickname' [17:33:40] Because your components won't be reusable in contexts where other people are using the same name. [17:33:52] It's like Zope acquisition problems all over again. [17:33:53] oh yea good point [17:34:09] That's why .ini files have sections, so you can do, e.g.: [17:34:12] [ircbot] [17:34:18] channels = 'foo','bar','baz' [17:34:26] nickname = 'pyneak' [17:34:29] etc. [17:34:57] It's also then easier for a parent component to redirect the properties. [17:35:09] For example, now that everything's under 'ircbot', a parent component can do *this*: [17:35:52] botdata = binding.Make( lambda: config.Namespace('mybot.*') , offerAs=['ircbot.*']) [17:36:30] And now, a BotFactory created underneath that component would use [mybot] instead. [17:36:42] So, you could have more than one active instance, each with different configs this way. [17:37:29] meaning i could have: [17:37:36] [mybot] [17:37:40] channels = blah blah [17:37:44] Right. [17:37:44] [anotherbot] [17:37:48] channels = blah blah [17:37:50] Yep. [17:38:38] and using the technique described earlier today i can get rid of __onStart [17:38:39] Also, btw, you can get rid of BotRunner, and merge it into BotFactory, AFAICT. [17:39:04] Nah, just move the __onStart to BotFactory. [17:39:30] ok [17:39:34] You'll just connectTCP(...,self) instead of connectTCP(...,f) [17:39:56] And then the Runner class will make BotFactory instead of BotRunner. [17:40:13] i was also trying to think of inteligent ways to change transport, i.e. connectSSL vs connectTCP [17:40:33] meaning make it configurable, especially wrt the jabberclient [17:41:00] Okay, so make a "transport" object with a 'connect(factory)' method. [17:41:04] and then there has to be some clever peakish way to do plugins' [17:41:17] I should've said "transport interface". [17:41:28] Anyway, yes, there is. [17:41:37] First, you can in the config file use: [17:42:07] transport = importString('mytransports.SSLTransport')(someport,whatever) [17:42:42] But, what perhaps you'd be better off with, is making address types. [17:42:46] Using peak.naming. [17:43:01] Then, you can have: [17:43:21] transportURL = binding.Obtain(PropertyName('ircbot.connectURL')) [17:43:37] transport = binding.Make(lambda self: self.lookupComponent(self.transportURL)) [17:44:14] And then you can have __onStart invoke transport.connect(self.reactor, self) [17:44:17] Or something like that. [17:44:25] There's a lot of possible ways to do it. [17:44:34] interesting [17:45:09] (one of the mantras in twisted is keep the protocol seperate from the transport) [17:45:13] Of course, you've got to have the URL schemes defined in your .ini or a site-level .ini. [17:45:28] Yeah, but that's because they've got no good way to configure it. :) [17:46:20] From the PEAK POV, reusability shouldn't interfere with usability. :) [17:46:54] And since you will always need *some* transport to go with your protocol, it should just be a collaborator component. [17:47:21] Which in PEAK means a configurable setting, controllable by the parent component or otherwise through the config system. [17:50:04] Now if only I had somebody to help me with the process thingy. ;) [17:50:10] heh [17:50:18] * Maniac mentions Ty's name [17:51:15] Yeah, I actually will consult with him before this is done, as there is a work project that's dependent on it. [17:51:23] But I will likely next see him for only a couple of days next week. [17:51:35] ah [17:51:50] And meanwhile I can't wait till then, since again there's a big work project that's dependent on having it done ASAP. [17:53:18] well, i have to go home to the kids [17:53:24] thanks pje [17:53:28] later [17:53:30] See ya... [17:53:30] ** Maniac has left us [18:15:29] * pje is drafting a ProcessManager implementation [18:22:26] I think this is going to need a global signal manager, not a signal stack. :( [18:25:34] * pje thinks some more [18:27:16] * _Maniac has to investigate peak.naming [18:27:59] * _Maniac has to figure out how to implement pje's wise advise [18:38:14] Maniac: coupla things on your IRC bot... [18:38:33] There's no need for Runner to define all those things that it does... [18:39:07] Just put the bindings in BotFactory and ircBot for anything they need, using e.g. 'ircbot.nickname', etc. [18:39:22] Runner should literally just look like: [18:39:29] class Runner(EventDriven): [18:39:44] bot = binding.Make(BotRunner, uponAssembly=True) [18:39:48] That's it. [18:40:47] As for your loggers, I'd recommend just using 'log = binding.Obtain('logging.logger:bots.irc')' [18:40:50] Or something like that. [18:41:04] There's a property namespace for redirecting logger: URLs to physical logs. [18:41:26] And it's hierarchical, so if somebody defines a 'bots.*' logger spec, then it would be used for all IRC bots. [18:42:11] If a parent component wants to redirect, it can do this similarly by defining an offerAs for 'peak.logs.bots.irc' [18:43:32] <_Maniac> ok [18:43:33] Oh, also your BotFactory should say 'self.reactor.stop()', etc. [18:43:42] Currently it references reactor directly. [18:44:19] So obviously that line hasn't been executed yet, or you'd have seen the error. :) [18:44:45] <_Maniac> no, very experimental, i have no bot shutdown command yet except for Ctrl-C [18:45:21] Btw, the 'buildProtocol' method wouldn't need 'p.factory = self' if you define 'factory = binding.Obtain("..")' in ircBot. [18:45:58] So buildProtocol() would then just read 'return self.protocol(self)' [18:46:43] <_Maniac> all the names were defined in runner so that i didn't have to depend on an ini file (that was my thinking) [18:48:25] Ah. [18:48:39] Well, as long as you have defaults for all values, you're still OK. [18:49:06] And I would see something like this as going to ZConfig eventually anyway, without even using the Runner class at all. [18:49:23] All you need is a ZConfig schema for BotFactory. [18:49:39] And then you can use it with the built-in EventDriven stuff. [18:49:58] You'd just have something like: [18:50:09] [18:50:14] Channels foo bar baz [18:50:23] Server irc.freenode.net [18:50:27] [18:50:40] added to a basic EventDriven ZConfig file. [18:50:49] Whoops, forgot keys to say where to log stuff to. [18:50:51] But you get the idea. [19:11:16] <_Maniac> ja [19:13:44] * pje has just finished a rough-draft SignalManager and ProcessManager [19:14:12] But alas, no process factories or proxies yet. [19:14:19] Still, not bad for an afternoon's work. [19:19:04] <_Maniac> cool ! [19:19:13] <_Maniac> what timezone you in? are you at work late? [19:19:32] * _Maniac is just about to start playing video games with his bratty kid [19:50:32] US/Eastern... and I'm still at work. [19:50:41] I usually work 11:30-7:30... ish. [19:54:14] * pje checks in the draft code [19:54:24] I've gotta get out of here. [19:55:03] See y'all later. [19:55:08] ** pje has left us [22:34:44] * _Maniac imagines legions of peak irc bots [23:57:34] ** vlado__ has joined us