[08:03:19] --> jack-e has joined #peak [09:52:59] * jack-e just created an Adapter for zope3 PageTemplateFile objects to be used with peak.web :)))) [09:53:22] --- jack-e is now known as jack-e|customers [09:53:31] --- jack-e|customers is now known as jack-e|away [10:54:00] * jack-e|away is gone (12:54PM): .. autoaway .. [12:12:35] --- jack-e|away is now known as jack-e [12:19:45] jack-e: cool [12:22:55] Maniac: just 10 lines of code ..i'll post it as recipe to the wiki when it's polished a bit [12:28:42] jack-e: hey-- that's cool! (ZPT) [12:35:08] Hmmmm-- ok, I'm sure I'm missing something obvious, but I'll fire away anyways... [12:35:12] jolby: yup zope3 pagetemplatefile with some basic context [12:35:41] I'm trying to expand on the example app I posted yesterday to the wiki: [12:37:00] If either of you have seen the code, There is a PEAK component (EchoRunner) that instantiates a Twisted Factory object (EchoFactory). I'm trying to make EchoFactory a child component using a call: [12:37:34] f = binding.New(EchoFactory) [12:38:09] but when I run the code I get: AttributeError: 'Once' object has no attribute 'doStart' [12:39:56] binding.XXX methods can only be used on class-definitions .. they procude a once-object (more detailed a descriptor), that is a class-attribute [12:40:10] I am probably just misunderstanding how components trees should be composed, but I was sort of thinking that binding.New(... would instantiate the component you pass to it, and bind it as an attribute in the containting obj [12:40:30] jack-e: Ahhh.. ok. [12:40:32] when it is accessed it replaces itself with the value that is returned by computeValue and that is stored in the instance as value [12:41:06] you would just say ef = EchoFactory(parent) if EchoFactory is a binding.Component [12:41:41] 1. argument for binding.Component.__init__ is allways the parent-component [12:42:01] Oh, ok. I'll try that now. Thanks [12:42:18] then only keyword-args are allowed. they will set instance variables - but only if you have defined them as binding or classattr [12:42:26] i'll have to do some (non-computer) work right now .. i'll be back in about an hour [12:43:28] jolby: you're basically right .. but f = binding.XXX within a method does not assign a class-attr [12:43:35] e.g. this is ok: [12:43:43] class AComponent(binding.Component): [12:43:50] f = binding.New(EchoFactory) [12:43:54] this not: [12:44:02] class BComponent(binding.Component): [12:44:08] def create(self): [12:44:15] f = binding.New(EchoFactory) [12:44:29] does this make sense to you ?? [12:45:08] jack-e: yes -- It just worked. cool! thanks. [12:46:00] --- jack-e is now known as jack-e|work [13:44:44] --> neaj has joined #peak [13:46:01] * jack-e|work is gone (3:46PM): .. autoaway .. [13:50:38] --> _Maniac has joined #peak [13:51:15] --- _Maniac is now known as Maniac [13:54:29] --- Maniac is now known as Maniac__ [13:54:40] --- Maniac__ is now known as Maniac [13:55:24] * Maniac waits [14:01:23] --- jack-e|work is now known as jack-e [14:03:43] * jack-e wonders what maniac is waiting for ... [14:11:09] * Maniac waits some more [14:12:27] morning jack-e [14:12:34] (or after noon as the case maybe) [14:13:36] right .. i'm thinking about the upcoming weekend more than about the work i have to do currently ;-) [14:14:09] * Maniac saw the zpt adapter [14:14:28] from zope.pagetemplate.pagetemplate import PageTemplate [14:14:28] from zope.pagetemplate.pagetemplatefile import PageTemplateFile [14:14:28] # This adapter does care about security at all yet [14:14:28] class PageTemplateAsWebMethod(protocols.Adapter): [14:14:33] """Make PageTemplates play nicely with peak.web""" [14:14:33] protocols.advise( [14:14:33] instancesProvide = [IWebMethod], [14:14:33] asAdapterForTypes = [PageTemplate, PageTemplateFile] [14:14:35] ) [14:14:37] def render(self, interaction): [14:14:39] request = interaction.request [14:14:41] try: [14:14:43] obj = self.subject [14:14:45] ctx = obj.pt_getContext(request.getPositionalArguments()) [14:14:47] ctx['request'] = request [14:14:49] ctx['template'] = obj [14:14:51] ctx['app'] = interaction.app [14:14:53] # XXX more context elements ?? [14:14:55] data = obj.pt_render(ctx) [14:14:57] ret = data.encode('utf-8') [14:14:59] except Exception, e: [14:15:01] ret = str(e) [14:15:03] return ret [14:15:16] is all that is needed to be able to do something like this (shamelessly copied from trivial_web/* [14:15:25] class ZPTTestApp(binding.Component): [14:15:25] security.allow( [14:15:25] index_html = [security.Anybody] [14:15:25] ) [14:15:29] index_html = PageTemplateFile('index.zpt') [14:15:29] [14:15:29] [14:15:30] interactionClass = binding.Constant( [14:15:32] AnonInteraction, offerAs=[web.INTERACTION_CLASS] [14:15:34] ) [14:15:56] and put a file index.zpt near your module and you'll be able to render ZPT's with peak in context of your app [14:17:08] i'll put it in the wiki as well [14:18:29] so it appears to be relatively trivial to add template support to peak? [14:18:44] caring about security might be important [14:18:55] i saw somewhere in your cvs some references to wxPython [14:19:05] do you develope graphical interfaces with peak? [14:19:21] maniac: i'm going to .. yes [14:19:44] i'm working on a component-model for wxPython to make it more pythonic/peakish to use [14:21:16] seems peak is getting a little flood of enthusiasm :) [14:21:59] i've started all this about 1.5 years ago with Transwarp .. still not finished with refactoring (e.g. WfMC Workflow Engine) [14:23:18] ah, quite a lifecycle then [14:23:25] yup [14:23:29] but jolby and _jpl_ are new [14:23:34] (i guess) [14:23:45] i'm new, but i'm a tinkerer :) [14:23:53] ;-) [14:24:41] right now i'm kinda/sorta working on a little jabber bot to check my email and let me know when i have new mail (from various accounts) which is why i was looing at your imap support [14:25:16] I'ld happily accept patches that make it work with uw .. [14:26:23] some of pje's "functionality gaps" make the code a bit messy .. but i could introduce you into how it all works [14:27:53] how the imap stuff works? [14:29:32] there are 3 EntityDM's and Element-Classes for IMAPFolder, IMAPMessage, IMAPFolderACL [14:29:44] there is a IMAPConnection similar to a SQLConnection [14:30:26] when you get a Folder from an IMAPFolderDM it asks the server via the connection for the details and it's subfolders [14:30:48] all done in the exact same way as writing an SQL-based EntityDM [14:31:07] then there are a few QueryDM's that find Messages, Folders [14:31:22] the only thing that needs to be changed is the following i think: [14:31:59] all FolderNames are currently composed like "%s.%s" % (parent-name, name) [14:32:24] that is .. the _p_oid of a Folder is this string and that references it in the Storage (the imap-server) [14:32:58] one would need to find out (via the .namespace method) what the server uses as namespace and hierarchy-seperator [14:33:29] then this information needs to be taken and converted to the way uw-imap needs it# [14:34:03] i don't know it it makes sense to have different ways of referencing a folder (e.g. user.jack-e.spam vs. /home/jack-e/mail/spam) [14:34:37] visible for the user or just hide it from the user and allways use the "." as seperator and "user" as root .. i'm not shure about this one yet [14:34:44] can you follow roughly ?? [14:35:17] * Maniac is digesting and needs a few minutes to finish some paperwork [14:35:23] you need to look at nll/src/nll/net/mail/imap.py and imapmodel.py [14:35:53] ok .. look at the imap.py a bit .. i need to do some paperwork as well .. i'll be back in about 30 mins [15:03:15] namespace (on my server) for uw: [15:03:17] >>> conn.namespace() [15:03:17] ('OK', ['(("" "/")("#mhinbox" NIL)("#mh/" "/")) (("~" "/")) (("#shared/" "/")("# [15:03:17] ftp/" "/")("#news." ".")("#public/" "/"))']) [15:03:38] namespace (on my server) for courier: [15:03:38] >>> hydraConn.namespace() [15:03:39] ('OK', ['(("INBOX." ".")) NIL (("shared." "."))']) [15:03:39] >>> [15:06:28] personally i like one way for referencing a folder [15:08:06] same for me .. but then we might loose some functionality .. e.g. i need to create mailboxes in a cyurs-server and need the "user" path for that .. it makes no sense for uw-imap for example [15:08:26] there is a method that can be used to parse the result [15:09:30] it usually looks like "info = makemap(CreateStruct(b)[0][1])" [15:09:56] ok [15:10:08] but you need to try that - body would be the list-part of what you posted [15:10:17] e.g. status, body = .namespace() [15:10:31] if not status == 'OK': raise something [15:11:21] there are a few parts where it concats folder-names or split('.') .. all these parts would need to take care of the Hierarchy-sep [15:11:37] the second part of the tuple you get .. the first part is the namespace [15:11:55] i'ld be just like os.sep for imap [15:12:43] blabla.split(imap_sep) or "%s%s%s" % (parent_name, imap_sep, folder_name) [15:14:03] one of the next steps for imap is, that i want to create functionality tests with twisted-imap server as test-backend, simulating uw and cyrus .. but that'll take a while ;-) [15:23:22] how do you mean there is a method that can be used to parse result? [15:23:48] makemap and CreateStruct help you to parse the result that you get from the imap-server [15:24:26] where are these methods? in your modules? [15:24:40] if you look at imap.py line 420 (in IMAPFolderACLDM._load .. you see how it works [15:25:12] these are imported from imaputil (taken from worldpilot sources) [15:26:02] got it ? [15:26:33] ok yes [15:27:52] so your saying all these calls: new_oid = '%s.%s' % (self.oidFor(ob.parent),ob.name) etc would need to handle the seperator issue [15:28:25] right [15:28:43] i guess you would set the seperate on _open [15:29:15] or as a once-attribute on the connection e.g. [15:29:29] def seperator(self,d,a): [15:29:33] do something [15:29:41] seperator = binding.Once(seperator) [15:29:52] then say conn.seperator if you need the information [15:30:38] :) [15:31:41] i dont follow that at all [15:31:59] what ? [15:32:10] or as a once-attribute on the connection e.g. [15:32:10] def seperator(self,d,a): [15:32:10] do something [15:32:10] seperator = binding.Once(seperator) [15:32:10] then say conn.seperator if you need the information [15:32:59] within the IMAPConnection class you would do that [15:33:18] you replace the def seperator with a Once-Instance [15:33:43] when you first access this attribute the seperator-method is called once and the return-value is cached as instance-variable [15:34:00] ergo .. seperator is only calculated once :) [15:34:20] meaning you only conn.namespace() <--- process once [15:35:09] the call to conn.namespace and the parsing of the result would be the content of the def seperator(..) method .. then it would return "." or "/" as value [15:35:39] yes, that's what i meant [15:35:41] ok [15:36:15] you can try all this in simple classes that inherit from binding.Component in the commandline .. that's how i understood all this :) [15:36:58] (how binding.Once works i mean) [15:38:46] * Maniac has a meeting [16:30:33] ok your regex i think would need changing too [16:31:16] yep .. you're right (for folder-list parsing you mean ??) [16:31:21] yes [16:31:25] correct [16:32:01] afaik there are only 2 hierarchy-seperators possible "." and "/" (not shure about "\" on windows perhaps) [16:32:28] co you might end up with selecting the right one from a dict (key -> ./, value compiled regex) [16:32:32] s/co/so [16:33:01] or you can create the regex dynamically .. i'm not 100% shure what's the better way right now [16:33:14] in re ((?P.*)\)\s <--- what is [16:33:57] you get e.g. \\HasChildren or similar flags when you do a folder-list [16:34:10] these are needed to decide if the subfolder shoud be queried [16:34:18] ok, but 'flags' is a placeholder or a way to name a group? [16:34:35] name a group [16:34:37] ok [16:34:57] well, off the bat uw-imap uses \\NoInferiors instead of \\HasNoChildren :) [16:35:31] hrm [16:36:14] i don't know if there's some sort of standard.... [16:36:34] so you'ld need to collect the differences between uw and others and then do something like [16:36:58] NO_CHILDREN_FLAG = ['\\HasNoChildren','\\NoInferiors'] [16:37:19] and in IMAPFolderDM instead of if not 'HasNoChildren' in flags: [16:37:45] try all of those in NO_CHILDREN_FLAG [16:37:51] \HasNoChildren - The presence of this attribute indicates that the [16:37:51] mailbox has NO child mailboxes that are accessible to the currently [16:37:51] authenticated user. If a mailbox has the \Noinferiors attribute, the [16:37:51] \HasNoChildren attribute is redundant and SHOULD be omitted in the [16:37:51] LIST response. [16:38:03] goofy RFC's [16:39:38] (that's why i stated that i won't make it compatible soon) .. but if you find the differences, we can work together on changing the implementation i think [16:39:54] if you need help then .. [16:40:41] i may poke at it. it's an interesting problem [16:41:31] --> pje has joined #peak [16:41:38] * Maniac greets pje [16:41:41] Aloha [16:41:49] hey phillip [16:41:57] Jack: just saw your ZPT adapter... got a couple questions for you. [16:42:09] go ahead [16:42:18] First, why the try/except block? [16:42:26] Second, why the encode('utf-8')? [16:42:38] Shouldn't the response object handle the encoding? (Not to mention errors?) [16:42:47] it's a first working draft [16:42:51] * pje nods [16:43:09] the problem was, that i didn't get meaningfull error-messages back to the browser [16:43:31] Third, to make the page template act as a "method" on something, shouldn't there be another entry in the ctx[]? [16:43:33] so i added the try/except and return a stringified variant of the error to get it working first [16:44:05] Or does PageTemplateFile() act as a method descriptor on its own? [16:44:31] what other entry in ctx ?? [16:44:43] I dunno, you're the Zope 3 expert. :) [16:44:58] In a page template, how do you normally refer to the object that the template is a method of? [16:45:02] context is what you can use as variables in your tal/tales expression [16:45:22] e.g. request/something would to a __getitem__ on request for "something" [16:45:41] Yeah, but what about the object that the template is a method on? [16:46:03] i added the "app" as context and a comment about that there should probably more ;-) [16:46:18] Yeah, I was trying to figure out the "more" part. :) [16:46:40] I'm also not clear on how page template paths will work in relation to PEAK names/paths/etc. [16:47:03] ok .. i must admit that i have no clue how you have planned to realize the MVC part with your templating stuff .. you'ld usually get a "view" in your context as well [16:47:29] PageTemplateFile('index.zpt') looks up index.zpt within the package/near the module [16:47:49] you can add prefix="/some/path/" as kwarg to change that [16:48:24] Ah. 'view' should map to the parent location, then. [16:48:25] too many questions at once ;-) [16:49:35] Is there some place that lists the context variables for Zope 3? [16:50:09] I can then work out how they should map in PEAK, now that you've given me the scaffolding to make it run at all... [16:50:28] in the sources src/zope/pagetemplate/pagetemplate.py and src/zope/app/pagetemplate/zopepagetemplate.py [16:50:41] the pt_getContext creates the PT namespace [16:50:56] :) [16:52:43] there is usually "request", "view", "context", "modules", "nothing", "template", "options", "args" and "modules" in the root-namespace of a ZPT [16:52:52] - 1x modules [16:55:02] sheesh [16:55:03] Currently, a limitation of the UW IMAP server is that a folder cannot contain both messages and subfolders. That is, a folder can either contain subfolders, or messages, but not both. To specify a folder that contains subfolders, you need to add a / to the end of its name. [16:55:37] maniac: autsch [16:56:06] ? [16:56:20] .. "ouch" in english ?? [16:56:32] that was german [16:56:35] ah [16:56:35] lol [16:57:16] i don't know how to handle this situation without divin into my code right now :( [16:58:04] an yet again, i belive (at least as i understand it) by default uw-imap does not ever have subfolders under inbox [16:58:55] how do you manage all you emails you save in you imap-mailbox ?? [16:59:40] you can create a folder messages/ [16:59:47] and then have subfolders under that [17:00:01] but because you can't have messages in 'INBOX' and have subfolders [17:00:24] basically i have a whole list of 'folders' of which INBOX is just one [17:00:39] right [17:00:59] i think it would take a more than minor overhaul to get uw to work properly [17:01:15] at least in terms of my sense of minor :) [17:01:31] too bad .. (you should consider writing a migration-script to another imap-server ;-) [17:01:56] ha [17:08:12] * pje is still reading through zope.app.pagetemplate stuff... [17:09:21] can i help you somehow getting into it faster ?? [17:10:39] Nah... I think I've got the key points now. [17:10:47] ok [17:11:04] zope.app.pagetemplate.viewpagetemplatefile looks to be the key to things. [17:11:21] yup [17:11:42] we would probably need to subclass and extend it to work with peak.web perfectly .. [17:11:54] it == zope.pagetemplate [17:11:57] I'm not sure how much time I should spend on it, though, since that's time I could be working on our own templating system. [17:12:21] don't care too much .. it was just for my own interest [17:12:34] I don't really want to have to learn to use ZPT itself. OTOH, people with ZPT experience could start doing stuff with peak.web sooner. [17:12:53] (learn = become proficient/productive at) [17:13:16] i'll spend some time on it next week to enhance it abit .. (I'm zpt-experience .. that's why i started ;-) [17:13:24] * pje nods. [17:15:12] To really get it integrated, it looks like you'll need a PEAKEngine (ala ZopeEngine in zope.app.pagetemplate.engine) [17:15:37] there are the expression-types registered .. [17:15:51] ala path-expression and the like you mean ?? [17:16:12] we'ld need to make them peak.security aware for example [17:16:29] Yeah, that's a bit more difficult. [17:16:43] The path expression is the main thing that actually needs changing. [17:16:57] We don't need so many other hooks... [17:18:50] Except maybe something for the 'translate()' function. [17:19:28] Yeah, paths, translation, and binding support... (__get__/BoundPageTemplate) [17:19:42] Chief amongst the hooks we support are... wait, I'll come in again. :) [17:19:54] :) [17:21:09] i found out, that there is no NOT_FOUND Exception raised right now .. is this an error, or is it just not yet implemented in the right way ? [17:21:24] Note that BoundPageTemplate can implement the location and behavior protocols directly, without needing an adapter. [17:21:34] Eh? [17:21:53] if nextOb is NOT_FOUND: [17:21:53] return self.notFound(ob, name) [17:21:59] def notFound(self, ob, name): [17:22:00] from zope.publisher.interfaces import NotFound [17:22:00] raise NotFound(ob, name, self.request) # XXX [17:24:37] i saw this .. [17:25:11] i have the trivial_web cgi in my browser at /test/trivial.cgi [17:25:38] when i traverse to /test/trivial.cgi/some/path/to/nowhere i get an empty page instead of an error [17:26:31] and the apache-log says that TypeError: string argument expected for write [17:26:43] at File "/data/develop/Zope3/src/zope/publisher/http.py", line 945, in output [17:26:43] self._outstream.write(data) [17:29:31] and because of not getting any errors out, i added the try/except block to my adapter for now ;-) [17:29:45] Is that trivial_cgi or trivial_web? [17:30:27] trivial_web [17:31:07] the trivial_cgi does not care about paths .. right ?? [17:31:40] as it doesn't use the zope.publisher [17:32:02] Yeah, just checking. [17:32:16] I'm looking at it now, it looks like there must be a problem with handleException... [17:32:37] handleException is logging the "unauthorized access"... [17:33:38] what's the log-destination ?? [17:33:50] peak-standard: stderr ? [17:35:29] That's in a hacked copy of peak.web.publish, not the CVS version, sorry. [17:35:35] But I think I found the problem, anyway... [17:35:38] ok [17:35:52] Or maybe not. [17:36:03] Hmmm... [17:36:32] i've never used fastcgi before. i get tracebacks in the apache/error.log .. where do they come from ?? stderr ?? [17:36:33] It's breaking on self._body, which might be because there's unicode in it... [17:36:54] Jack: yes, or to be precise, the errors stream passed to runCGI(). [17:37:04] ok [17:37:16] i think peak.web should only use unicode as well [17:37:41] all strings basically that might ever be subject of translation or input/output [17:39:29] Aha... it's a unicode problem, all right. [17:39:49] request._charset defaults to None, so there's no encoding available for output. [17:40:58] I guess BaseInteraction.handleException() should response.setCharset('UTF-8') [17:41:12] sounds right [17:42:51] I'm not sure we shouldn't do it all the time. [17:43:32] hm .. is the http-client allowed to set the encoding ?? [17:43:41] utf-8 is not a bad default though [17:44:38] Yeah. [17:45:35] Hmmm... actually, I see the *real* problem now... [17:45:53] The request automatically does the charset negotiation... [17:45:59] It's the response.reset() that clears it. [17:48:15] Strange... I wonder why zope.app.publication doesn't break, since it does response.reset() also? [17:50:38] Perhaps it's just not tested. [17:50:49] i have no clue .. i ripped of the http-server and replaced it with a pyro-daemon .. i never touched the http-server and related publication/publisher stuff [17:55:30] Interesting. Using setCharsetUsingRequest() leaves _charset as None. [17:56:57] are you using HTTPResponse directly ?? [17:57:06] envadapter = queryAdapter(request, IUserPreferredCharsets) [17:57:06] if envadapter is None: [17:57:06] return [17:57:26] will probably do nothing if you don't register an adapter for IUserPreferredCharsets [17:58:15] Yeah, that's the problem. [17:58:28] And it has to be registered via zope.component. Crud. [17:58:58] * jack-e sees somewhere behind the clouds, that pje will replace Z3 CA soon ;-) [17:59:01] Why doesn't HTTPRequest just implement IUserPreferredCharsets directly? [17:59:47] I suppose it's in principle possible that someone might want a different character-set detection algorithm. :( [18:00:36] you could just add an adapter for IUserPreferredCharsets to HTTPCharsets from publisher/http.py ? [18:00:52] err .. no .. because they use getAdapter :(( [18:01:00] You know, the really sick thing about this is that zope.publisher.http *only* uses the Z3CA to do this one thing! [18:01:18] queryAdapter, actually, and this is the only thing it's used for. [18:01:22] sick .. yeah [18:03:43] I suppose I could always deal with it in the old-fashioned way... subclass 'em all. [18:04:40] but that's what should be avoided when using CA or PyProtocols .. right ?? [18:05:03] what about just poking in some configurations for get/queryAdapter to the CA's Registries ?? [18:06:12] from zope.component.adapter import provideAdapter [18:07:07] Alas, it's the other way around, here... if I register the adapter, I might interfere with a Zope installation that somebody's using PEAK with. [18:07:28] Whereas if I subclass the Request/Response classes for use w/PEAK, they can't interfere. [18:07:40] how can they intefer ?? [18:07:48] you don't load the zcml-configurations [18:07:53] Because I'll be overriding whatever is registered w/Zope. [18:07:59] True. [18:08:26] if the config would have been loaded .. you wouldn't run into this problem [18:09:11] Also true. [18:09:37] Perhaps this means peak.web should replace the Z3CA hookables, then. [18:10:09] Although that means there'd be no *way* to register an adapter for IUserPreferredCharsets, then... sigh. [18:10:10] (that's what i meant with me seeing you replacing the Z3 CA soon ;-) [18:10:15] Can't win for losing, as they say. [18:10:42] (Since IUserPreferredCharsets is a Zope interface and can't register adapters directly) [18:11:13] Hmmm... maybe it's time to unleash my secret weapon... "sticky adapters". :) [18:11:21] :) [18:12:09] With a sticky adapter, I could force Zope interfaces to support the full IOpenProtocol interface, including direct adapter registration. [18:13:42] jack-e: does you jabber stuff doing anything yet ? [18:14:01] * jack-e needs to stop talking about interesting coding stuff and needs to clear his table to be able to take of for the weekend [18:14:25] For now, though, I'm going to put in a setCharset('UTF-8') in handleException() so that errors will work correctly. [18:14:27] maniac: the jabberqueuemanager responded to a jabber:iq:register [18:14:31] (If that's not an oxymoron...) [18:15:11] and there was some work started at joap (jabber object access protocol) that did something as well ;-) [18:15:57] maniac: the client/component-connections should work fine and try to lookupComponent for Iq/Message/PresenceHandlers [18:16:45] i need to stop chattin now .. i need to have finished my paperwork in an hour .. [18:16:55] have a nice weekend .. see you next week [18:19:44] ...bye ? [18:32:03] pje: Hey Phillip, If you're still around, I have a couple questions about acquisition. The good news is, with jack-e's help, I was able to get my 2nd example of the echo app to work. [18:32:44] For me, the bad news is, I'm not totally solid on *how* its working, and I'm not sure if I'm using it the preferred way. [18:33:47] Okay. [18:34:38] Extending the first echo server example, I get a hold of a log object (declared in the .ini file as: * = logs.LogStream(stream=importString('sys.stderr'), level=logs.DEBUG) [18:35:12] * pje nods [18:35:35] I do this in the class-var (component-var) declaration section of EchoRunner as: log = binding.bindTo('logging.logger:twistedpeak.echoserver2') [18:35:52] (basically ripped out of your bulletins example) [18:36:12] * pje nods [18:36:57] I've also made EchoFactory a subclass of binding.Component and Factory), so in the __onStart method of echo runner I hook it together like so: [18:37:23] f = Echo2Factory(self) [18:38:14] then in EchoFactory buildProtocol I use the log object like so: [18:38:20] self.getParentComponent().log.info("log.Echo2Factory.buildProtocol()...") [18:38:59] Okay. You could also have the factory include: log=binding.bindTo('log') [18:39:08] And then do self.log.info()... [18:39:37] Explicit traversal in code isn't advised, unless it's in a Once binding. [18:39:51] The idea being that bindings are easier to replace in a subclass, than operations buried in code. [18:39:57] LoD and all that. [18:40:23] Ok, that's sort of what I was wondering. [18:40:49] Yeah, any time you find yourself going places to get objects, you should generally factor them into bindings. [18:41:07] It makes the component more reusable. [18:42:17] Ok, its getting clearer now, and that code would look a lot nice. Basically the child component is just saying "hey-- I need someone in my parent-path to provide a utility called [18:42:22] 'log" [18:43:04] Is that right? Also-- is a log object a "utility". I've seen that term used in Zope3 and PEAK stuff. [18:45:02] Yes, it is. But a crude one, since it's acquired by name. [18:45:29] Normally, utilities are registered via a key other than their attribute name, such as an interface or property name. [18:45:37] This prevents any unintended collisions. [18:47:57] Oh, ok, so log=binding.bindTo('log') isn't the preferred way to get access to a logging (or db, or... ) utility? [18:48:59] Not really. [18:49:36] It's mainly intended for referring to peer attributes. (See peak.storage.SQL.SQLConnection for an example of what I mean.) [18:53:00] That just made me remember another question: in the bulletin app when you declare (bind) the log var like so: log = binding.bindTo('logging.logger:bulletins.app') [18:53:39] what do the parts surrounding the ":" mean? is it an Interface/property-name registration? [18:54:05] No, that's binding to a peak.naming URL. [18:54:36] A bindTo() string is parsed as a component name or URL. [18:54:53] For it to be interpreted as a property name, you have to wrap it with PropertyName(). [18:55:22] ok, I've seen that (propertyname) construct in the code. [18:57:58] so does the peak.naming.url construct looks for a "logging.logger" schema handler, and the schema handler then deals w/ the bulletins.app part? [18:58:22] Something like that. [18:58:46] naming.getURLContext() is used to look up either a naming context factory or address factory for that URL scheme. [18:59:03] Then the factory is used to create a context that can look up the URL. [19:00:22] ok, that all feels fairly familiar to using JNDI in java (simpler though) [19:00:34] which is good. [19:01:12] The big difference between JNDI and PEAK in this respect, is that JNDI uses a series of import prefixes to look up URL context factories... [19:01:33] But PEAK uses a property namespace (see [peak.naming.schemes] in peak.ini) [19:01:53] Also, you may have noticed that peak.naming doesn't require you to set up an explicit Environment the way JNDI does. [19:02:26] peak.naming just uses configuration properties, which already have the hierarchical nature expected of contexts' environment in JNDI. [19:02:47] (i.e. hierarchy of naming and hierarchy of inherited values) [19:03:05] (just got done looking at peak.ini)-- Ahh-- that's where logging.logger is.. cool. [19:04:07] And now you see one reason PEAK_CONFIG is important... you can register other URL schemes there, for example. [19:04:28] I see [19:06:16] so, if in my own site.ini (pointed to in PEAK_CONFIG), under the section of [peak.naming.schemes], I did my own logging.logger = ... line, would that override what is peak.ini? [19:06:28] Yes. [19:06:53] Also, for a given component, you could do something like: [19:07:18] foo = binding.Constant(..., offerAs=[PropertyName('peak.naming.schemes.foo')]) [19:07:48] And when you did any lookups via that component or its child components, the 'foo:' URL scheme would be available and as defined by the '...' part. [19:08:30] the ... being the foo schema handler component? [19:08:39] Yep. [19:08:44] Scheme, not schema, btw. [19:09:12] A string or a factory, just like in peak.ini [19:09:42] (The binding needn't be *called* foo, mind you... it could be called __bar or something.) [19:10:10] The important part is what it's 'offerAs'-d. You could include multiple names in the offerAs clause, and make that the factory for more than one scheme. [19:12:04] Hmmm. ok. That probably wouldn't be that common of a use-case would it? I would imagine that most Scheme handlers would map to one scheme [19:12:21] my imagination could be wrong tho' :-) [19:13:33] btw: thanks for all the help on irc the last couple days. Between you and jack-e and all the others I feel like I'm getting up to speed on PEAK fairly quickly. [19:17:12] I had one more question about the example echo app. I sort of imagine PEAK components being the more "stable" or permanent parts of the application. For that reason, I avoided making the EchoProtocol class a PEAK component. [19:18:01] (That's the part that handles the physical network connection). [19:18:41] Is that the right way of thinking of how to use Components? [19:21:14] It seems to me that if I made the EchoProtocol object a Component, It may make it hard to garbage collect the object once the connection is done. [19:22:14] Don't be afraid to use a component. [19:22:30] Hmmm. although it *could* remove (unbind) itself from its parent in the connectionLost(... callback method. [19:22:39] They're easy to GC as long as their parent doesn't hold a reference to them. [19:22:49] It's not their reference to the parent that you need to worry about. [19:23:14] pje: Its the parent's reference to them? [19:23:17] As long as the component exists, it needs to have access to its parent. If the parent doesn't have a reference to the child, then the child goes away once it's not needed any more. [19:23:57] Make sense? [19:25:20] so (from w/in EchoProtocol) I would do something like (I don't know the exact method call) self.getParentComponent().removeChild(self) ...? [19:26:08] No... just don't ever assign the child to an attribute of the parent. I don't think you're doing that now, so there's nothing special to do. [19:26:13] Look at it this way... [19:26:21] class Foo(binding.Component): pass [19:26:30] myFoo = Foo(someParent) [19:26:33] del myFoo [19:26:40] POOF! [19:27:05] ok, makes sense. [19:27:07] Do you see what I'm getting at? If you don't save myFoo somewhere, it just goes away when you no longer have a reference. [19:27:22] If you did someParent.itsFoo = myFoo, then it would be a cycle. [19:27:33] And the garbage collector would have to be called in to break it up. :) [19:27:51] That works fine then for the EchoProtocol. I'll make it a component then for the updated example. [19:29:46] For the jabber protocol, I actually do keep a weakDict mapping each Protocol object to a JID. But I can just continue to do that after I make them components as well. Should work fine. [19:30:27] Yep. [19:31:17] Ok, well this is cool. Thanks again. [19:45:21] i have done no work [19:45:32] today [19:45:40] i have guilt [19:50:55] yet here i stay [19:50:57] :) [19:51:01] Maniac: don't fall into the guilt-trap-- its summer. You *should* have a few no-work days in the summer :-) [19:51:38] i'm taking monday off, so why start something friday if you can't finish it monday? [19:51:57] hehe [19:51:59] i figured out how to send a 'jabber:x:oob' message using jabberpy...... [19:52:20] of course i'm a quality assurance manager for a manufacturing plant so that really had NOTHING to do with my job [19:52:30] so very productive i must say [19:52:34] Maniac: very cool. [19:52:36] lol [19:52:42] .... also put in incredible dent into our cofee supply [19:53:03] .... signed some reports [19:53:07] .... chatted on IRC [19:53:48] I'm hoping to start the PEAK-ification of what I have for my twisted-jabber server today. [19:53:59] ya, so opensource it so i can see the code ! [19:54:24] I think I have PEAK clear enough in my head to cause some trouble... [19:55:50] RE: opensource. Ya-- I'm planning on making the core of it LGPL. I just don't want to take the time to do all the setup on SF (or some other OS hosting place). [19:56:39] Plus-- I'm not quite ready for public scruitiny of some of the ugly hacking going on... [19:56:43] :-) [20:23:41] ah cool [20:23:46] * Maniac just returned from yacb [20:23:55] 'yet another cofee break' [20:36:36] mmmmmm.... coffee....... [21:10:01] pje: If you're still around, and have time, I still have a Q: about the "log = binding.bindTo('logging.logger:bulletins.app')" line in app.py in the bulletins example. Particularly, the "bulletins.app" section. I've grepped the source, and its the only place its listed in all the sourcefiles. [21:10:27] What, bulletins.app? [21:10:36] yes. [21:10:47] So? [21:10:58] That's specifying a logging category. [21:11:21] From my reading of peak.running.logs. peakLoggerContext it looks like it would make it a property [21:11:32] i.e., you can control where that log will go to by defining peak.logs.bulletins.app. [21:12:05] you mean sys.stdout, file, etc... [21:12:07] ? [21:12:09] Right. [21:12:53] --> _jpl_ has joined #peak [21:13:07] <_jpl_> Hi all [21:13:36] so if I defined peak.running.logs.bulletins.app = someFileObj it would log to that file? [21:13:51] _jpl_: Howdy [21:14:49] No, you'd need to make it something that supported the ILogger interface. [21:14:53] E.g. LogFile [21:16:11] ok, that makes it clearer. thanks [21:16:43] <_jpl_> Is this in peak.ini? [21:16:57] Well, the default i.s [21:17:00] er, is. [21:17:17] peak.logs.* is bound to stderr [21:17:27] actually a LogStream over stderr [21:18:15] <_jpl_> I tried replacing stderr with open('/tmp/foo','w') and it seemed to work... [21:19:10] JPL: try using something like naming.lookup(targetObj,'logfile:/tmp/foo?LEVEL=DEBUG') [21:19:44] Or for that matter just 'logs.LogFile(...)' [21:19:47] <_jpl_> Ok. A bit more verbose though. [21:19:59] <_jpl_> Oh, that second one looks nice. [21:25:45] cool-- the logging stuff makes more sense now. I got it to switch to a logfile using the logs.LogFile(...) construct. [21:32:53] hometime [21:33:02] cioa [21:33:11] <-- Maniac has quit #peak [21:33:11] Maniac: bye [21:33:44] <_jpl_> Jolby, shall we call you Jolby Chatzilla? :) [21:34:12] Isn't that what I'm already called? [21:38:12] <_jpl_> Yes, but we just call you Jolby. [21:41:28] oh-- jolby is just fine w/ me. I just changed it to jolby_chatzilla when I was testing two irc clients at the same time. I liked chatzilla better. :-) [21:43:31] --- jolby_chatzilla is now known as jolby [21:43:44] there we go. [21:45:54] <_jpl_> I guess on the reasoning, just teasing you for leaving it that way. :) [21:47:51] <_jpl_> er, guessed [21:49:32] Grrr. [21:49:48] Don't you hate it when you spend hours debugging something that turns out to be something completely stupid? [21:50:07] (In this case, a one-line edit that got lost when my editor crashed and I didn't notice to retype it.) [21:51:10] ugh-- I hate it too. I wish they would come out with a stupidchecker.py script we could run on our codebase like we can w/ pychecker... [21:54:23] If we were smart enough to be able to write something like that, we wouldn't need it. ;) [21:54:37] true [21:55:07] Damn... after all that debugging, it finally works, and now I don't believe it. :) [21:55:22] It seems too easy that it should just work, darn it. :) [21:56:52] what did you get to work? [21:57:05] sticky adapters for Zope interfaces. [21:57:44] sticky adaptors? [21:57:57] They lift some of the limitations on Zope interfaces when used with the PyProtocols API. [21:58:33] A sticky adapter is one that keeps state across adapt() calls. [21:58:38] cool. Is this related to getting jack-e's ZPT port to work? [21:58:45] Sort of. [21:58:58] Well, not really. [21:59:26] It's to do with getting peak.web to get zope.publisher to handle charset negotiaion. [21:59:52] And also just lifting some of Zope's limitations wrt PyProtocols. [22:01:15] oh, ok. [22:01:50] I originally came up with the idea in order to make it possible to cache adapters in certain circumstances. [22:02:18] I didn't think of using it to boost Zope interfaces into full protocols until this morning. [22:03:16] what do you mean by "cache adapters" -- you mean cache lookups of what adapter adapts object x to interface y? [22:03:38] Yes; specifically the adapter instance - not just what class of adapter. [22:04:01] I see. [22:04:14] Thus, the expression 'adapt(x,y) is adapt(x,y)' should be true for such an adapter. [22:04:21] So it can retain state across adapt() calls. [22:05:28] I need to get more familiar w/ adaptors [22:06:08] They sort of feel like some of the dynamic mixin/interceptor stuff I've seen in some of the java AOP toolkits [22:09:01] Sort of. [22:09:14] They don't really "intercept" as such. [22:09:59] I mean, you could get them to, if you wanted, it's just not very convenient. [22:12:00] so more of the dynamic mixin part than the intercept. [22:14:42] I wrote a quick-n-dirty toolkit that plays around with interceptors and some other AOP-ish concepts a few months back. It was pretty fun and a good way for me to learn python. [22:29:27] * pje has gotta get going. [22:29:42] See y'all later. [22:30:18] <_jpl_> Adios [22:30:53] Ciao [23:46:34] --- Maniac- is now known as Maniac