[00:00:24] <_jpl_> Ok. Does it need to subclass ApplicationService? [00:00:26] <_Maniac> like i said, i probably have this all wired up in weird way, hence my desire to learn a better way [00:02:04] <_jpl_> Ok, so to PEAKify it you'd probably start with an application class based on peak.running.commands.AbstractCommand. Call it whatever you want. [00:03:27] <_Maniac> yes [00:05:16] <_jpl_> Then you could put the various pieces (XRNoteReceiver, Site, HempService, etc.) as attributes of this application class. For example: [00:05:28] <_jpl_> receiver = binding.Make(XRNoteReceiver) [00:05:41] <_jpl_> def site(self): [00:06:15] <_jpl_> Wait, scratch that. Maybe this: [00:06:40] <_jpl_> site = binding.Make(lambda self: importString('twisted.web.server.Site')(self.receiver)) [00:07:15] <_jpl_> port = binding.Obtain(PropertyName('receiver-port')) [00:07:40] <_jpl_> jabberBot = binding.Make(HempService) [00:09:07] <_jpl_> user = binding.Obtain(PropertyName('user')) [repeat for all the other bits used by BasicAuthenticator [00:09:09] <_jpl_> ] [00:10:23] <_Maniac> how does one make use of the twisted reactor? [00:11:06] <_jpl_> PEAK applications are very declarative. You declare all the various subcomponents you want to work with (or offer to others), then use them or allow them to make use of each other. [00:13:25] <_jpl_> You could use the EventDriven class, but that's a bit more complicated (and I haven't done it yet myself) -- so for now in the run() method of your application class just put reactor.run() [00:14:06] <_jpl_> We'll make an executable config file to run this in a minute. [00:14:32] <_Maniac> ah [00:14:32] <_jpl_> That will automatically call run() on your app object. [00:15:39] <_jpl_> Alright, so HempService needs to find the BasicAuthenticator, and XRNoteReceiver needs to find the HempService, right? [00:15:44] <_Maniac> yes [00:16:45] <_jpl_> Add binding.Component to XRNoteReceiver's parents (i.e. along with xmlrpc.XMLRPC) [00:17:21] <_jpl_> Get rid of its __init__ method. [00:17:59] <_jpl_> And add a binding for service, something like: service = binding.Obtain('../jabberBot') [00:18:34] <_jpl_> This isn't great, since it makes the two components tightly coupled, but it'll do for now. [00:19:26] <_jpl_> The alternative would be to 'offer' jabberBot as something (most likely a PropertyName), but that may be overkill right now. [00:21:31] <_jpl_> But then again you said you wanted to be able to add a lot of little services, so maybe we should make this more flexible from the start... [00:21:46] <_Maniac> yes, like instead of jabber maybe IRC etc. [00:22:12] <_jpl_> So change the jabberBot binding in the app class to look like this: jabberBot = binding.Make(HempService, offerAs=[PropertyName('jabberBot')]) [00:22:30] <_Maniac> <_jpl_> jabberBot = binding.Make(HempService) <--- this is obtained from a config file ? [00:22:34] <_Maniac> grr [00:22:53] <_Maniac> port = binding.Obtain(PropertyName('receiver-port')) [00:22:55] <_Maniac> that one [00:23:04] <_jpl_> Yes, that'll be in the config file. [00:23:08] <_Maniac> ok [00:24:11] * _Maniac just made sure that peak-logbot was logging this for me :) [00:27:56] <_jpl_> So your app class might look something like this: [00:28:01] <_jpl_> class Application(AbstractCommand): [00:28:02] <_jpl_> jabberBot = binding.Make( [00:28:02] <_jpl_> HempService, [00:28:02] <_jpl_> offerAs=[PropertyName('jabberBot')] [00:28:04] <_jpl_> ) [00:28:06] <_jpl_> receiver = binding.Make(XRNoteReceiver) [00:28:08] <_jpl_> site = binding.Make( [00:28:10] <_jpl_> lambda self: importString('twisted.web.server.Site')(self.receiver) [00:28:12] <_jpl_> ) [00:28:14] <_jpl_> port = binding.Obtain(PropertyName('myapp.receiver-port')) [00:28:16] <_jpl_> user = binding.Obtain(PropertyName('myapp.user')) [00:28:18] <_jpl_> password = binding.Obtain(PropertyName('myapp.password')) [00:28:20] <_jpl_> resource = binding.Obtain(PropertyName('myapp.resource')) [00:28:22] <_jpl_> host = binding.Obtain(PropertyName('myapp.host')) [00:28:24] <_jpl_> def run(self): [00:28:26] <_jpl_> reactor.run() [00:28:42] <_jpl_> (with a few blank lines for clarity, which didn't get sent) [00:30:26] <_jpl_> And XRNoteReceiver might look like: [00:30:33] <_jpl_> class XRNoteReceiver(binding.Component, xmlrpc.XMLRPC): [00:30:33] <_jpl_> service = binding.Obtain(PropertyName('jabberBot')) [00:30:33] <_jpl_> note = binding.Obtain(PropertyName('myapp.note')) [00:30:34] <_jpl_> def xmlrpc_receive(self,note): [00:30:34] <_jpl_> print 'received', note [00:30:36] <_jpl_> self.service.sendmsg(note,"Maniac@jabber.vandorp.ca/Psi") [00:30:38] <_jpl_> return xmlrpc.Boolean(1) [00:30:40] <_jpl_> oops [00:31:15] <_jpl_> I wasn't done. More like this: [00:31:18] <_jpl_> class XRNoteReceiver(binding.Component, xmlrpc.XMLRPC): [00:31:18] <_jpl_> service = binding.Obtain(PropertyName('jabberBot')) [00:31:18] <_jpl_> message = binding.Obtain(PropertyName('myapp.note')) [00:31:19] <_jpl_> def xmlrpc_receive(self,note): [00:31:19] <_jpl_> print 'received', note [00:31:20] <_jpl_> self.service.sendmsg(note, self.message) [00:31:20] <_jpl_> return xmlrpc.Boolean(1) [00:31:36] <_jpl_> er, change myapp.note to myapp.message [00:32:35] <_Maniac> ok i think i see 'binding fever' [00:34:27] <_jpl_> So client.BasicAuthenticator is twisted.jabber.client.BasicAuthenticator? [00:34:32] <_Maniac> yes [00:35:19] <_Maniac> but it's cruft left over from when it wasnt' in jabber yet http://jabberstudio.org/projects/hemp (hence the name HEMP in my stuff) [00:35:48] <_jpl_> It only seems to accept two parameters in its constructor, rather than the list of keyword arguments you're using... [00:36:18] <_Maniac> dizzyd may have changed it around when he migrated over to jabber [00:36:27] <_Maniac> i have'nt updated it to the jabber stuff [00:36:37] <_Maniac> er twisted implementation [00:36:51] <_Maniac> er when he migrated over to twisted [00:36:57] * _Maniac is tired.... [00:38:24] <_jpl_> Ok, in Twisted they use something called a jid which combines the various pieces into a single object. [00:39:15] <_jpl_> Hmm, they do some fairly rudimentary parsing of a string representation of the jid to make a JID object. [00:39:37] <_jpl_> This could be done much better with peak.naming, but we can leave that for another day. [00:40:50] <_jpl_> Oh, I see, what I called 'myapp.message' is really a jid. [00:42:08] <_Maniac> ok yeah you used to have to pass everything to the authenticator now you just pass Maniac@jabber.vandorp.ca [00:42:13] <_Maniac> JID [00:43:56] <_jpl_> Right, JID('string') [00:46:37] <_Maniac> may i ask, what are you using pb for? [00:46:52] _Maniac is now known as Maniac [00:47:39] <_jpl_> Ok, here's the Application class again: [00:47:48] <_jpl_> class Application(AbstractCommand): [00:47:48] <_jpl_> jabberBot = binding.Make( [00:47:48] <_jpl_> HempService, [00:47:49] <_jpl_> offerAs=[PropertyName('jabberBot')] [00:47:49] <_jpl_> ) [00:47:50] <_jpl_> def jid(self): [00:47:50] <_jpl_> jid = self.lookupComponent(PropertyName('myapp.jid')) [00:47:52] <_jpl_> return importString('twisted.protocols.jabber.jid.Jib')(jid) [00:47:54] <_jpl_> jid = binding.Make(jid, offerAs=[PropertyName('jid')]) [00:47:56] <_jpl_> receiver = binding.Make(XRNoteReceiver) [00:47:58] <_jpl_> site = binding.Make( [00:48:00] <_jpl_> lambda self: importString('twisted.web.server.Site')(self.receiver) [00:48:02] <_jpl_> ) [00:48:04] <_jpl_> port = binding.Obtain(PropertyName('myapp.receiver-port')) [00:48:06] <_jpl_> user = binding.Obtain(PropertyName('myapp.user')) [00:48:08] <_jpl_> password = binding.Obtain(PropertyName('myapp.password')) [00:48:10] <_jpl_> resource = binding.Obtain(PropertyName('myapp.resource')) [00:48:12] <_jpl_> host = binding.Obtain(PropertyName('myapp.host')) [00:48:14] <_jpl_> def run(self): [00:48:16] <_jpl_> reactor.run() [00:48:49] <_jpl_> Take a look at the logs from earlier today where Ulrich (jack-e) and I were talking about the PEAK/Twisted/PB stuff. [00:49:20] ah [00:49:37] now i just need a config file .... [00:55:20] <_jpl_> Right, so that part is easy. Something like: [00:55:25] <_jpl_> [myapp] [00:55:37] <_jpl_> receiver-port = 19191 [00:55:41] <_jpl_> user = 'foo' [00:55:45] <_jpl_> password='bar' [00:55:49] <_jpl_> ...etc. [00:57:56] <_jpl_> Put the following at the bottom of the file: [00:57:58] <_jpl_> [peak.logs] [00:57:58] <_jpl_> * = logs.LogStream(stream=importString('sys.stderr'), level=logs.DEBUG) [00:58:00] <_jpl_> [peak.running] [00:58:02] <_jpl_> app = importString('myapp:Application') [00:58:15] <_jpl_> But change 'myapp' to the name of the module you saved all this in. [00:58:33] <_jpl_> Now you should be able to run it like so: peak runIni myapp.ini [00:59:06] <_jpl_> But it's not going to do much yet. [00:59:35] it will do as much as it's doing now [00:59:59] <_jpl_> Put this in Application.run just before reactor.run(): reactor.listenTCP(self.port, self.site) [01:00:09] oh yeah :) [01:01:00] <_jpl_> And you'll probably need something somewhere to startup the jabber bot. [01:02:37] yes. it was in HempService.startService [01:03:10] <_jpl_> Ok, then just put "self.jabberBot.startService()" before reactor.run(). [01:03:55] <_jpl_> But we haven't wired up HempService and the jid object yet, but that should be pretty easy to do. [01:04:13] yes, hopefully i can figure it from here... [01:04:56] adding a pb based listener as opposed to just xmlrpc would be pretty easy too then [01:05:11] etc, etc, [01:05:32] <_jpl_> Sure, though server-side PB has a few more objects to worry about. [01:05:51] <_jpl_> Which my Junction class takes care of for you. [01:05:56] oh, well for another day(month/week) [01:06:18] the forthcoming open-sourced junction class ? :) [01:06:40] <_jpl_> Yep [01:07:50] i REALLY have to go to bed, thanks for your help ! [01:07:51] <_jpl_> So have you tried running it yet? [01:08:15] i am barely awake i shall try when i have wakefullness [01:08:46] thanks again [01:08:53] * Maniac turns off the lights and leaves the building [01:10:58] <_jpl_> See you later. [03:04:33] ** vlado has joined us [03:15:16] * vlado is away, somewhere far beyond... (l!on)(p!off) [cRk/bx] [03:49:47] ** vlado_ has joined us [06:52:01] jack-e|away is now known as jack-e [08:21:25] greetings jack-e [08:21:40] hey maniac .. how're things going ? [08:22:54] good. [08:23:04] * Maniac just woke up less than 10 minutes ago :) [08:23:18] hey .. stayed up late yesterday ? [08:23:27] :) [08:24:36] i guess [08:24:43] _jpl_, was tutoring me [08:25:02] fine [08:27:09] * Maniac heads to work [09:49:41] ** vlado__ has joined us [09:56:58] Maniac: nll.net.mail.IMAPMailbox now also supports twisted.protocol.imap4.IMAP4Client for connections [10:09:43] ** Maniac_ has joined us [10:10:01] * Maniac_ returns (at work) [11:48:34] ** SteveA|home has joined us [11:55:09] ** SteveA|home has joined us [12:01:09] ** pje has joined us [12:01:14] Hey all. [12:01:29] hey phillip :) [12:01:33] Just wanted to pop in and add a few ideas to JPL's Twisted-PEAK tutorial. [12:01:48] If you have a bunch of properties in a namespace that you need to bind to, you can do something like this: [12:02:14] myapp = binding.Make(lambda: config.Namespace('myapp')) [12:02:26] And then all the things that use the property namespace can bind to it, e.g.: [12:02:44] port = binding.Obtain('myapp/receiver-port') [12:03:05] user = binding.Obtain('myapp/user') [12:03:08] etc. [12:03:33] In fact, if your items are all in a "flat" namespace like that, and the items map precisely to the same property names, you can actually use Delegate: [12:03:50] user = password = resource = host = binding.Delegate('myapp') [12:04:15] So that was tip #1. [12:04:43] Tip #2... don't import the Twisted reactor directly. [12:05:02] Instead, use reactor = binding.Obtain(running.ITwistedReactor) [12:05:39] This will ensure that any PEAK components will end up using the Twisted reactor, instead of PEAK's mini-reactor. [12:06:09] E.g., if you use AdaptiveTask, or FastCGIRunner, or stuff like that. [12:06:24] (only if no binding.Obtain(running.IBasicReactor) has loaded the other reactor before) [12:06:30] Correct. [12:06:42] But if one *has* loaded the other before, you'll get a helpful error message. :) [12:06:52] right :) [12:06:58] Whereas if you use it directly, stuff will just quietly break. :) [12:07:12] Tip #3... don't override AbstractCommand.run(), override the _run() method instead. [12:07:30] run() now has handy error trapping, and it may someday have some other helpful hooks. [12:07:45] You can just override _run() to define the "meat" of your command-line app. [12:09:54] Tip #4, and this is kind of specific to Twisted... [12:10:07] Use assembly events to set up listeners and tasks. [12:11:21] Specifically, in the case of the Jabber and Site objects... [12:11:49] You can set up binding.Make(lambda self: self.doSomething(), uponAssembly=True) [12:12:06] bindings in these kinds of services that will cause them to register or start or listenTCP or whatever. [12:12:35] They should have a 'reactor = binding.Obtain(running.ITWistedReactor)' as well, and use that to hook up to the reactor. [12:13:11] A component that uses them can either delay creation (by not touching the binding), or define the binding as uponAssembly, in which case they'll start ASAP. [12:14:32] Tip #5... instead of creating a custom app class, use running.commands.EventDriven. [12:15:31] This can be as simple as creating an instance of EventDriven(components=[svc1(), svc2(), ...]) [12:16:10] EventDriven has configuration properties for things like automatically shutting down after a time period, or based on how long the app's been idle. [12:16:42] There's also a ZConfig schema for EventDriven, which means that you can create ZConfig schema extensions to describe your new services. [12:17:07] And then the app Maniac's working on could just be a ZConfig file listing each of the services it's to run. [12:17:59] So, there'd be no "main program" anywhere, actually. [12:18:44] ** lex has joined us [12:18:56] Actually, I think I've overstated the case for Maniac's app. His services need to talk to one another, as part of a component that connects them. [12:19:13] You can still do that with ZConfig, of course, but it's a tad more work to set up. [12:19:53] Whew. I think that's about it for now. :) [12:23:34] * jack-e is off for the weekend .. yippie - again it's time for our clubnite on saturday (www.fet.org) .. cu all [12:24:04] jack-e is now known as jack-e|weekendin [12:24:51] jack-e|weekendin is now known as jack-e|O [12:25:06] jack-e|O is now known as jack-e|away [12:26:16] Bye! [12:31:44] >>> class test(binding.Component): [12:31:44] ... pass [12:31:44] ... [12:31:44] Traceback (most recent call last): [12:31:44] File "", line 1, in ? [12:31:45] AttributeError: 'module' object has no attribute 'Component' [12:31:47] >>> [12:33:16] am i dumb? [12:33:34] * Maniac_ reads pje's tips [12:33:35] yay! [12:36:30] ok that was a dumb mistake, from peak.api import * NOT from peak import * [12:41:17] * pje smiles [12:41:37] What's the "yay!" about? The tips, or getting the import to work? :) [12:41:47] the tips [12:42:41] Ah. So, does using PEAK and Twisted still seem like more trouble than Twisted, or is it becoming apparent what PEAK offers in that context? [12:43:37] mostly twisted has a fair bit of documentation that is relatively easy to comprehend that describes how to wire things together and manage service dependencies etc. [12:44:01] so even while twisted has a relatively steep learning curve it has more tools to get there then peak does (so far) [12:44:44] that being said, i like the peak approach better [12:44:46] Tools, or docs? :) [12:44:49] docs [12:44:54] tools == docs [12:45:28] PEAK actually has fewer artifacts to document than Twisted, but Twisted has a bigger core team. [12:45:33] one quote i like, "I'm a programmer, you guys are computer scientists", taken further, "I'm a hobbiest, you guys are computer scientists" [12:45:39] == more doc writers. [12:45:50] Quote? Where'd you see that? [12:45:53] pje, absolutely, and I dont' mean it as a critisicim [12:45:59] pje, someone on #twisted [12:46:05] or possibly the mailng list [12:46:22] re: Twisted, I presume. [12:46:31] correct,but the same applies here [12:46:40] Well, I *am* a computer scientist. :) [12:46:41] i need things spelled out pretty clearly : [12:46:44] ;) [12:46:53] pje, yes you are ;-) [12:47:33] Now all we need to do is figure out how to steal Twisted's doc writers. ;) [12:47:48] * pje sighs [12:48:06] Of course, that's not going to help much when so much of the critical information is in my head. [12:48:20] Worse, most of the critical information is *implicit* in my head. [12:48:29] I only know what it is when people do things wrong. :) [12:48:49] So I guess I need lots of you to go out and do wrong things and show them to me. ;) [12:48:53] mmm [12:49:17] Then I will explain what the things are really intended for, and then somebody can turn that into docs. :) [12:49:28] File Edit Options Buffers Tools IM-Python Python Help [12:49:28] from peak.api import * [12:49:28] from peak.running.commands import AbstractCommand [12:49:28] from twisted.web import xmlrpc, server [12:49:28] from twisted.internet import reactor [12:49:31] class XRNoteReceiver(binding.Component,xmlrpc.XMLRPC): [12:49:33] def xmlrpc_receive(self,note): [12:49:43] print 'received', note [12:49:45] return xmlrpc.Boolean(1) [12:49:49] class Application(AbstractCommand): [12:49:51] receiver = binding.Make(XRNoteReceiver) [12:49:55] site = binding.Make( [12:49:57] lambda self:importString('twisted.web.server.Site')(self.receiver) [12:49:59] ) [12:50:01] port = binding.Obtain(PropertyName('peakify:receiver-port') [12:50:03] ) [12:50:05] reactor.listenTCP(7080,site) [12:50:08] [12:50:09] in just trying to get this to work.... i get : File "/home/darryl/projects/sandbox/hemp/peakify.py", line 25, in Application [12:50:12] reactor.listenTCP(7080,site) [12:50:14] File "/usr/local/lib/python2.2/site-packages/twisted/internet/default.py", line 269, in listenTCP [12:50:16] File "/usr/local/lib/python2.2/site-packages/twisted/internet/tcp.py", line 568, in startListening [12:50:18] self.factory.doStart() [12:50:20] AttributeError: 'Make' object has no attribute 'doStart' [12:50:38] First, Kill the 'from twisted.internet import reactor' bit. [12:50:50] Then get rid of the reactor.listenTCP bit. [12:51:02] Add to the Application class: [12:51:13] reactor = binding.Obtain(running.ITwistedReactor) [12:51:21] def _run(self): [12:51:22] ** vlado has joined us [12:51:34] self.reactor.listenTCP(7080,self.site) [12:51:39] self.reactor.run() [12:51:40] ** lex has left us [12:51:56] ok [12:52:04] Next, what's with the peakify: thing? [12:52:08] i have to go out for lunch, more on this in an hour [12:52:13] peakify is my ini file [12:52:19] peakify.ini [12:52:34] But why's that part of a PropertyName? [12:52:40] [peakify] [12:52:40] receiver-port = 7080 [12:52:40] [peak.logs] [12:52:41] * = logs.LogStream(stream=importString('sys.stderr'),level=logs.DEBUG) [12:52:43] [peak.running] [12:52:46] app = importString('peakify:Application' [12:52:47] i'll be back! [12:53:16] Ah, you need a '.', not a ':' in the PropertyName, or that won't work. [12:53:21] See ya. [13:49:47] ug [13:50:12] that's why i didn't end up using the obtained value [13:54:40] Yep. [13:55:20] now it says it needs an integer :( [13:56:39] What needs an integer? [13:56:48] port [13:56:56] which i'm getting from the config file [13:57:15] It looks like an integer to me. [13:57:30] Where are you using self.port? [13:57:38] It's not referenced in the source you pasted here. [13:58:16] one sec [14:03:26] grr :) [14:04:54] importString <-- [14:05:01] File Edit Options Buffers Tools IM-Python Python Help [14:05:01] from peak.api import * [14:05:01] from peak.running.commands import AbstractCommand [14:05:01] from twisted.web import xmlrpc, server [14:05:04] class XRNoteReceiver(binding.Component,xmlrpc.XMLRPC): [14:05:06] def xmlrpc_receive(self,note): [14:05:08] print 'received', note [14:05:12] return xmlrpc.Boolean(1) [14:05:16] class Application(AbstractCommand): [14:05:18] receiver = binding.Make(XRNoteReceiver) [14:05:20] reactor = binding.Obtain(running.ITwistedReactor) [14:05:22] site = binding.Make( [14:05:24] lambda self: importString('twisted.web.server.Site')(self.receiver) [14:05:26] ) [14:05:28] port = binding.Obtain(PropertyName('peakify.receiver-port') [14:05:30] ) [14:05:32] def _run(self): [14:05:34] self.reactor.listenTCP(self.port,self.site) [14:05:46] self.reactor.run() [14:07:29] And the above doesn't work? [14:07:41] What happens if you "print `self.port`" ? [14:07:49] (in _run) [14:08:29] File "/home/darryl/projects/sandbox/hemp/peakify.py", line 15, in [14:08:30] lambda self: importString('twisted.web.server.Site')(self.receiver) [14:08:30] NameError: global name 'importString' is not defined [14:08:35] that's the traceback [14:08:54] what magic is importString? [14:16:14] from peak.util.imports import importString [14:16:25] You don't need it, anyway, you could just import Site directly. [14:16:32] I'm not sure why JPL suggested that. [14:17:25] importString() is for dynamic stuff. [14:22:56] ok [14:24:35] peak runIni peakify.ini [14:24:35] Traceback (most recent call last): [14:24:35] File "/usr/local/bin/peak", line 7, in ? [14:24:35] sys.exit( [14:24:35] File "/usr/local/lib/python2.2/site-packages/peak/running/commands.py", line 221, in __call__ [14:24:44] return cmd.interpret(cmd.argv[1]) [14:24:46] File "/usr/local/lib/python2.2/site-packages/peak/running/commands.py", line 548, in interpret [14:24:48] raise InvocationError( [14:24:50] peak.running.commands.InvocationError: ('Invalid command object', , 'found at', 'config:peak.running.shortcuts.runIni/') [14:29:23] Wha? [14:30:10] Try 'peak runIni' [14:30:33] missing arguments [14:31:03] Okay.... try 'peak import:peakify.Application' [14:31:10] Or 'python peakify.py' [14:31:26] Or both. :) [14:31:42] I'm guessing you've got a syntax error or other problem in peakify.py... [14:31:59] Or else in the '[peak.running] app = ' section of peakify.ini [14:32:24] Or else it's something simple like peakify not being on the PYTHONPATH... [14:37:42] * Maniac_ is looking [14:42:29] ** als has joined us [14:44:04] Howdy. [14:47:47] i got it to work ! [14:48:00] class Application(AbstractCommand): [14:48:00] receiver = binding.Make(XRNoteReceiver) [14:48:00] reactor = binding.Obtain(running.ITwistedReactor) [14:48:00] port = binding.Obtain(PropertyName('peakify.receiver-port') [14:48:00] ) [14:48:00] def _run(self): [14:48:02] self.reactor.listenTCP(self.port,server.Site(self.receiver)) [14:48:04] self.reactor.run() [14:48:28] pje: you wrote: "Without knowing more about your app it's hard to suggest...". would you like to? i really do not want to bother you without real need. i am very happy with what you've done for us yet. [14:49:47] but the self.reactor.listenTCP doesn't seem right [14:50:29] als, not really. [14:50:51] I was just saying I couldn't be more detailed about what would be right for your situation. [14:51:32] Of course, if you run into problems, I would need more info to answer questions. [14:51:43] But I don't need to know in advance of problems. :) [14:52:29] Maniac: you should be able to add 'site = binding.Make(lambda self: server.Site(self.receiver))' [14:52:42] And then use 'self.reactor.listenTCP(self.port,self.site)' [14:54:24] ok that's better [14:55:03] now to implement the rest of your tips [14:55:58] ** Maniac_ has joined us [14:56:12] ouch! i fell off the internet [15:02:43] Don't hurt yourself. :) [15:03:59] looks like tips 4 and 5 would make it like the twistedpeak example in the wiki [15:04:12] class Echo2Runner(binding.Component): [15:04:13] """ [15:04:13] The more cannonical way to make a PEAK-ified [15:04:13] twisted protocol handler. This objects essentially [15:04:13] takes the place of a twisted Application object. It [15:04:13] can be configured using ZConfig (the port and prefix vars). [15:04:15] """ [15:04:17] [15:04:19] log = binding.bindTo('logging.logger:twistedpeak.echoserver2') [15:04:21] reactor = binding.bindTo(running.ITwistedReactor) [15:04:23] protocol = Echo2 [15:04:25] port = 8091 [15:04:27] prefix = "You said: " [15:04:29] [15:04:31] def __onStart(self, d, a): [15:04:33] self.log.info("log.Echo2Runner __onStart()...") [15:04:37] f = Echo2Factory(self) [15:04:39] f.prefix = self.prefix [15:04:41] f.protocol = self.protocol [15:04:43] self.reactor.listenTCP(self.port, f) [15:04:45] [15:04:47] __onStart = binding.whenAssembled(__onStart) [15:04:53] class Echo2Command(EventDriven): [15:04:55] """ [15:05:03] The quick way to do it. the script run-tp calls this object [15:05:05] """ [15:05:09] commands = binding.New(Echo2Runner, activateUponAssembly=True) [15:05:11] * Maniac_ waits to get flood-kicked [15:05:16] so in other words dont' do a _run() at all ? [15:08:57] Yep. [15:09:15] 4 and 5 seem very closely related :) [15:09:41] Yes... but not inherently so. [15:09:47] 5 depends on 4, yes. [15:09:51] But 4 is useful without 5. [15:10:13] Because the "self-starting" components can be used in anything that wants them, not just EventDriven. [15:11:57] so taking the xmlrpc server: [15:11:58] class Echo2Runner(binding.Component): [15:11:58] """ [15:11:58] The more cannonical way to make a PEAK-ified [15:11:58] twisted protocol handler. This objects essentially [15:11:59] takes the place of a twisted Application object. It [15:12:01] can be configured using ZConfig (the port and prefix vars). [15:12:03] """ [15:12:05] [15:12:07] log = binding.bindTo('logging.logger:twistedpeak.echoserver2') [15:12:09] reactor = binding.bindTo(running.ITwistedReactor) [15:12:11] protocol = Echo2 [15:12:15] port = 8091 [15:12:17] prefix = "You said: " [15:12:19] [15:12:21] def __onStart(self, d, a): [15:12:23] self.log.info("log.Echo2Runner __onStart()...") [15:12:25] f = Echo2Factory(self) [15:12:27] f.prefix = self.prefix [15:12:29] f.protocol = self.protocol [15:12:31] self.reactor.listenTCP(self.port, f) [15:12:33] [15:12:35] __onStart = binding.whenAssembled(__onStart) [15:12:41] class Echo2Command(EventDriven): [15:12:43] """ [15:12:47] The quick way to do it. the script run-tp calls this object [15:12:49] """ [15:12:51] commands = binding.New(Echo2Runner, activateUponAssembly=True) [15:12:53] OOPS [15:13:03] class XRNoteReceiver(binding.Component,xmlrpc.XMLRPC): [15:13:05] def xmlrpc_receive(self,note): [15:13:07] print 'received', note [15:13:09] return xmlrpc.Boolean(1) [15:13:35] if done that way, don't the components have to know what port to listen to etc? [15:14:50] Sure, but they can get that info lots of different ways. [15:14:59] They can bind to config settings, which their parent can provide. [15:15:20] For example, the XMLRPC receiver could have 'port = binding.Obtain(PropertyName('xmlrpc.port'))' [15:15:48] And then a container using it can say 'xmlrpc_port = binding.Make(lambda: 80, offerAs=['xmlrpc.port'])' [15:16:02] (The name of the attribute is unimportant, btw) [15:16:31] ok that's still ok [15:18:38] ZConfig is of course another way. [15:19:09] * Maniac_ is still scared of Zconfig [15:19:18] one thing at a time ! :P [15:19:54] can you give me an example of what you mean by tip#4 with respect to xmlrpc [15:19:58] ? [15:21:07] like binding.Make(lambda self: self.reactor.listenTCP(), uponAssembly=True) [15:21:09] ? [15:21:41] What's wrong with that? [15:21:51] no, is that what you mean? [15:21:54] I mean, you probably need self.port in the listenTCP, right? [15:22:01] yeah, abbreviated :) [15:22:28] Okay. So, if you do that in your XMLRPC receiver, then it'll autostart, right? [15:22:48] Oh, by the way, there's still another way to configure it... [15:23:23] Somebody can explicitly do 'myXRNoteRecv = binding.Make(lambda: XRNoteReceiver(port=80,...))' [15:23:45] (That's in addition to ZConfig and configuration properties loaded from a component or a file.) [15:25:02] how do mean autostart? [15:26:57] uponAssembly means the binding will be activated when the component knows its (assembled) parent. [15:27:30] So, as soon as a newly created XRNoteReceiver is told what its parent component is (happens when the containing binding is invoked), then all the uponAssembly bindings are invoked. [15:27:42] Which means the receiver will start. [15:27:53] That's what I mean by autostart. [15:28:14] so, then yes i guess it will autostart [15:28:41] Autostart of this kind is suggested for components that provide external services. [15:29:05] And/or that have to register with a central authority (e.g. the reactor). [15:29:39] Of course, this may mean the parent component needs to autostart too. [15:30:00] E.g. my_listener = binding.Make(XRNoteReceiver, uponAssembly=True) [15:30:20] So that as soon as the parent is assembled, it will create and start the XRNoteReceiver. [15:30:45] (and so on, up through parent components, if applicable.) [15:31:47] ok, using EventDriven i can still use an executable config file ? ( i assume) [15:31:59] You can always use executable config files. [15:32:25] Just set peak.running.app to be the object that you want to run. [15:32:43] (I'm assuming here you mean .ini-style config files, as opposed to executable ZConfig.) [15:32:53] (Both are considered executable config files.) [15:32:55] yes i still like ini style files [15:35:22] Well, they certainly require less "type declaration" overhead when prototyping. [15:35:36] Maniac_: you may be interested in the way we've made our tcp services: in our application, each service is a chain of components connected via pipes. each component is a PEAK Command, reading from stdin and writing to stdout. upon incoming TCP connection, the server instantiates special component working with the socket and having it's stdin and stdout connected to a command doing real work. [15:35:40] But they're not something to sic on some poor end user. [15:38:06] we make command factories from ZConfig, and the server has a mapping from (client_ip, listening_port) to the command factory for this client [15:39:59] * Maniac_ agrees with pje [15:40:11] als, that sound interesting but far too complex for me :) [15:40:29] * Maniac_ attempts to convert to tip 4 and 5 [15:43:57] Wow, als, you guys are doing more complex stuff with this than I have, yet. :) [15:44:23] I'm not doing any networked servers with PEAK/Twisted yet. [15:44:31] ok, now i have all my declaritive bindings setup in my application class, i can just move those to my EventDriven class? [15:44:48] pje: but that's really cool! both of us are excited [15:46:55] Maniac: yes, if all they are is uponAssembly bindings, then you should be able to just move them to an EventDriven subclass. [15:46:58] if only we could use os pipes and select() instead of periodic tasks... select() does not work with pipes on windows. [15:47:17] And then the EventDriven subclass is suitable in place of your app class. [15:47:32] als: Have you looked at Twisted's win32 select replacement? [15:47:45] I think it's still experimental, but it does support pipes IIRC. [15:49:35] pje: thanks. i will, when we start to distribute our application as py2exe executable. without that, each external module makes a problem for our support staff. [15:50:15] * pje nods [15:50:41] Can't you make a distutils installer, though? [15:50:55] Or is it that Python isn't installed on the target machines? [16:01:13] pje: we do make a distutils installer (including PEAK distutils installer as a file, btw ). still, they have to install or update python, and win32extensions (to run windows service), and MetaKit (for data storage), and each of these dependencies make a problem. [16:03:21] Couldn't you extend PEAK's setup file to include the other dependencies? [16:03:38] I guess it'd be a lot of work for all that stuff, though. [16:04:04] If you get py2exe to work with PEAK easily though, I'll be quite surprised. [16:04:26] It doesn't play well with lazily-loaded modules. [16:04:49] Not that I've spent more than an hour or so trying to get it to work. [16:04:50] * als has no idea how to update python from distutils [16:05:04] I don't think you can. [16:05:07] Only modules. [16:06:33] we do not have enough of resources to test each new application version with all the zoo of python and package versions installed on customer servers [16:08:08] * pje nods [16:08:20] I'm surprised your customers have Python installed on their machines at all. [16:08:45] i hope we will manage to list all lazily-loaded modules in py2exe setup script. dropping the need to test with all different python package versions, we will spare a lot of time [16:09:41] pje: they don't. but their servers are pretty dedicated to our application, so we are free to install python there [16:11:17] (if you think that we should change windows to *nix: we can't. our application is written in FoxPro. ) [16:12:46] * pje didn't suggest that... [16:12:58] In fact, you'll notice I suggested getting a win32-useful select. :) [16:13:30] I want PEAK to be able to play on desktop/client-oriented operating systems too. ;) [16:15:54] you mean, TCP-loopbacks? somehow i am not ready to go this way [16:18:05] but maybe i'll reconsider, after some investigation that may be done only after we finish urgent tasks [16:19:49] No, not TCP-loopbacks. [16:19:59] 'WaitForObjects' or whatever it's called. [16:20:14] The proper Win32 API call for waiting for pipes and files and sockets and such. [16:20:27] Python's 'select()' only uses winsocket select API. [16:21:02] Ah, here it is... [16:21:04] ahahaha i'm lost agin [16:21:05] win32event.WaitForMultipleObjects [16:21:06] again [16:21:28] Maniac_, don't worry, you don't need to know this unless you're doing something really ambitious with Windows. [16:21:50] no with regards to using EventDriven [16:21:51] :) [16:21:55] pje: wow! thanks. [16:22:19] Maniac_: oh. :) [16:22:40] * als is running very quickly to the last trolleybus going home [16:22:50] als, Twisted has a reactor module based on win32event.WaitForMultipleObjects [16:22:57] So, you might want to look at that. [16:23:01] Have fun! [16:23:08] the XRNoteReceiver doesn't know about server.Site [16:23:20] Maniac_, why not? [16:23:20] or shouldn't [16:24:00] http://twisted.sourceforge.net/TwistedDocs-1.0.0rc3/api/twisted.internet.win32eventreactor.html [16:24:21] Or actually... http://twisted.sourceforge.net/TwistedDocs-1.1.0/api/twisted.internet.win32eventreactor.html [16:25:50] again, it was a pleasure to talk to you. bye. [16:26:34] my_xmlrpc = XRNoteReceiver() [16:26:48] site = server.Site(my_xmlrpc) [16:27:13] reactor.listenTCP(port,site) [16:28:02] how can i do all this INSIDE the XRNoteReceiver component [16:28:49] i would need XRNoteRevieverRunner(binding.Component) [16:29:26] XRNoteReceiverFactory() [16:29:39] Why not just 'reactor.listenTCP(port,server.Site(self))', then? [16:29:54] Or do you want the site wrapper customizable? [16:30:08] (by a user of the XRNoteReceiver) [16:30:19] hmm [16:30:25] i like your idea, you smart [16:31:14] If you want it customizable, give it a 'siteFactory = server.Site' attribute, and [16:31:26] reactor.listenTCP(port, self.siteFactory(self)) [16:31:42] to use it. Now it can be configured via ZConfig or constructor keyword. [16:32:06] Or, use siteFactory = binding.Obtain(PropertyName('something'), default=server.Site) [16:34:51] ** _jpl_ has joined us [16:36:10] well blow me down, it worked [16:38:16] that's the first time i made changes like that and it worked the FIRST time [16:38:30] from peak.api import * [16:38:30] from peak.running.commands import EventDriven [16:38:30] from twisted.web import xmlrpc, server [16:38:31] class XRNoteReceiver(binding.Component,xmlrpc.XMLRPC): [16:38:33] reactor = binding.Obtain(running.ITwistedReactor) [16:38:35] site = binding.Make(lambda self: server.Site(self),uponAssembly=True) [16:38:39] def xmlrpc_receive(self,note): [16:38:41] print 'received', note [16:38:43] return xmlrpc.Boolean(1) [16:38:49] def __onStart(self): [16:38:51] self.reactor.listenTCP(7080,self.site) [16:38:53] self.reactor.run() [16:39:00] You don't need site to have uponAssembly=True. [16:39:05] __onStart = binding.whenAssembled(__onStart) [16:39:09] class Listener(EventDriven): [16:39:11] receiver = binding.Make(XRNoteReceiver,uponAssembly=True) [16:39:13] comments/critisisms accepted and encouraged [16:39:23] And you probably don't want to have the site attribute at all, since it creates an unnecessary circular reference. [16:39:38] See my earlier suggestion of using a 'siteFactory' attribute instead. [16:39:52] yeah, that's what i was having a problem with [16:56:07] site doesn't have to be configurable [16:56:26] * Maniac_ just had a customer rake him over the coals... YAY! weekend [17:00:42] Why the raking? [17:08:05] they claim we shipped them bad parts [17:08:33] * Maniac_ works in manufacturing [17:08:41] i'm a PHB.... [17:13:20] Seriously? [17:13:26] PHB, I mean. [17:13:35] Obviously you're serious about the manufacturing. :) [17:14:00] * pje doesn't think that you can be a PHB if you know you're a PHB [17:14:13] It goes against the true spirit of PHB-ism. :) [17:16:13] <_jpl_> Hi all. [17:16:21] yeah, but i'm crawling my way up to middle-management :) [17:16:26] <_jpl_> Reading the logs, lots of good stuff. [17:16:37] and i wasted a day playing with peak instead of dealing with customers and stuff [17:16:55] question: given the following: [17:16:59] class Listener(EventDriven): [17:16:59] log = binding.Obtain('logging.logger:eventdriven.peakify',offerAs=['app.log']) [17:16:59] port = binding.Make(lambda: 7080, offerAs=['xmlrpc.port']) [17:16:59] receiver = binding.Make(XRNoteReceiver,uponAssembly=True) [17:17:06] can i log something right away? [17:17:25] like log.info('some stuff') [17:17:45] log.log [17:17:46] ug [17:18:37] Obtain has no attribute info [17:19:17] <_jpl_> Regarding importString, the reason I used it in the example is because I've grown accustomed to using it. Some of my .py files have a number of components in them that may or may not run (depending on configuration by the user), so I like to import from strings where a module is used only by one component. None of this applies to Maniac's example, though, so it would have been clearer not to use it there. [17:19:56] Maniac, not in the class. [17:20:02] You need self.log. [17:20:24] <_jpl_> (I think I took the code with importString directly from an example I had just written, actually) [17:20:26] If you don't have 'self', you have no component, and therefore no parent component, and therefore no way to get configuration... [17:20:36] And without configuration, the naming system doesn't work, so you can't get a logger. [17:24:50] <_jpl_> Phillip, about setting up the Twisted reactor. How can you ensure that an EventDriven-based app gets the Twisted reactor first? [17:28:41] so, no :) [17:28:41] reactor = binding.Obtain(running.ITwistedReactor, offerAs=[running.IBasicReactor]) [17:29:18] That way, any subcomponents trying to get IBasicReactor will actually load the twisted reactor. :) [17:30:04] _jpl_, you see i got it all working (excluding jabber, which i will do tonight) [17:36:34] so even if a twisted component tries to get IBasicReactor it will get a ITwisted one? [17:38:38] Maniac: only subcomponents of the component that declares that. [17:39:04] <_jpl_> Ok... so put that in the top-level app component and make sure all components, including things like AdaptiveTasks, are subcomponents. [17:39:07] The parent is saying, "here's what I offer as running.IBasicReactor", so the children accept it. [17:39:42] JPL: yep. It's only needed to be above any auto-starting components that want a reactor. [17:40:19] So, you can easily make an EventDriven ZConfig schema extension that uses your replacement type as the top-level component. [17:40:46] <_jpl_> And with EventDriven or a subclass thereof you never need to run reactor.start() since it's done for you by MainLoop... [17:41:26] <_jpl_> How would a schema extension override the top-level component? [17:42:21] [17:42:27] Er, make that /> [17:42:34] That would be the whole schema, I think. :) [17:42:48] I bet even Maniac can deal with a ZConfig schema that's only one line. ;) [17:43:16] * Maniac_ growls [17:44:27] <_jpl_> :) [17:44:28] What? You said you hated ZConfig. [17:44:50] <_jpl_> So that extends= is new to ZConfig 2.0? [17:45:02] Yeah, and to the ZConfig provided by PEAK. [17:45:13] I'm not completely sync'ed to 2.0 at the moment. [17:45:17] <_jpl_> That's really handy. [17:45:47] Yep. That one was my idea; Fred Drake also implemented an %import directive in ZConfig files that can be used to add package extensions. [17:46:09] So, if you were using the standard EventDriven schema, but wanted to add your own AdaptiveTask types, you could use %import mypackage [17:46:22] in the config file, to automatically load mypackage/component.xml. [17:46:52] Between the two mechanisms, schemas are much more flexible in 2.0 than in 1.0. [17:51:21] * pje pauses a moment to check in the new peak.util.mockdb subsystem... [17:51:49] what happens if you do binding.Obtain(PropertyName('somename')) [17:52:29] and somename doesn't exist [17:52:55] <_jpl_> Oh, speaking of PHB's, has anyone seen The Office? [17:54:02] <_jpl_> Yes, I'm coming back around to ZConfig after a bit of a hiatus with .ini files. Which are, as you pointed out, much easier to use when prototyping. [17:54:42] Maniac: you get a NameNotFound error, I believe. [17:54:56] can you wrap it with try: except:? [17:54:56] That's why there's a 'default' keyword argument you can supply to Obtain. [17:55:01] ah [17:55:44] FYI... [17:55:52] Added 'peak.util.mockdb', a "mock object" implementation of a DBAPI 2.0 [17:55:52] driver module. 'mockdb' connections can be told to 'expect()' queries [17:55:52] and 'provide()' data to their callers, and will raise AssertionErrors when [17:55:52] they are used in a way that doesn't conform to your supplied expectations. [17:55:52] This is intended to be used for unit testing components that depend on [17:55:53] a database connection: you can verify that they send the right SQL, and [17:55:55] you can provide them with dummy data to use. There is also a 'mockdb:' URL [17:55:57] and peak.storage driver, so you can easily use a mock DB connection in place [17:56:01] of a real one within a PEAK application, for testing purposes. Note, [17:56:03] however, that 'peak.util.mockdb' is a DBAPI 2.0 driver in itself, and thus [17:56:05] can also be used to test DBAPI usage outside of PEAK. [17:56:07] Sorry, that should've been in quotes or something. :) [17:56:22] It's the CHANGES blurb for the mockdb thing I've been working on all day while talking to you guys. :) [17:56:30] cool [17:56:41] * Maniac_ heads home to pick up his experimentation [17:57:37] BBC's "The Office"? [18:05:47] <_jpl_> Yes, the boss there is a bit of a PHB, especially in the sense of not knowing about his PHBness. [18:06:35] Ah. Haven't seen it. [18:06:38] Only ads. [18:06:48] It looked too depressing for me. :) [18:07:09] <_jpl_> It's pretty hysterical. [18:07:23] <_jpl_> The first season is available on DVD. [18:10:29] * Maniac returns [18:11:05] so even if we aren't to evengelisise(sp) peak can i make weblog postings without offending you mr. pje? [18:13:14] Sure. Weblogs are fine. [18:13:30] I'm only concerned about proselytization in "push" forms. [18:13:56] E.g. mailing lists, IRC channels, and forums owned by non-PEAK enthusiasts. [18:14:22] On your own weblog is fine, and feel free to link to it from the Wiki. [18:15:58] cool [18:32:06] since you've been extremely tolerant and helpful (you too _jpl_) i dont' want to offend :) [18:44:58] if i'm using a twisted reactor i can still call reactor.callLater() etc? [18:47:06] Yes. [18:47:21] Twisted offers many more reactor functions than PEAK's basic reactor. [18:47:29] PEAK's is a subset of Twisted's. [18:48:28] Someday I might add some more functionality to the basic reactor, if I need something extra for the PEAK core that Twisted seems too big for. [20:48:04] * pje is about ready to go home. [20:48:07] Finally. [20:54:05] Adios. [20:54:10] ** pje has left us [21:01:57] goodnight pje [22:08:36] ** netopy has joined us [22:08:51] ** netopy has left us [22:55:31] rowr [23:07:00] i can't get importString to work, i must be insane [23:51:48] comments/critiscims appreciated : http://randomthoughts.vandorp.ca/evendriven.py