[08:40:29] jack-e|away is now known as jack-e [08:40:31] re [09:49:02] re [09:49:11] * Maniac wonders what re means.... [09:52:54] re entrance .. [09:53:08] signalizing that i'm back [10:14:28] aha! [10:14:36] i figured something like that :) [10:14:43] :) [10:14:48] i thought re-turn [10:15:07] * jack-e is playing around with peak.web and is trying to get a pwt rendered ... [10:18:53] tell me how to use peak security ! [10:19:17] security.allow(method=security.Anybody) .. :-) [10:19:30] bah! [10:46:29] ** victorng has joined us [10:52:16] the unittests are too complicated for my tiny brain. simple example ! [11:00:09] ** victorng has joined us [12:11:05] ** victorng_ has joined us [12:11:52] can someone explain what binding.Obtain does differently than calling self.lookupComponent? [12:12:23] I don't understand why binding.Obtain(PropertyName('myApp.x')) needs to be a class attribute [12:13:01] you can't use binding.Obtain at runtime (e.g. something = binding.Obtain(config-key) will give you an instance of a OnceClass that needs to be activated) [12:13:13] you can use something = self.lookupComponent for this case [12:13:54] activated means basically doing a self.lookupComponent(key) + caching the result in the instances dict [12:14:59] does this help? [12:15:22] just trying it out [12:16:19] aha!, so lookupComponent activates? [12:16:27] nope [12:16:36] gah [12:16:37] the metaclass of binding.Component activates [12:16:41] * Maniac rereads [12:16:44] (or the initialization) [12:16:50] i'm not 100% sure [12:17:18] like binding.Obtain(PropertyName('somthing')) [12:17:27] like someThing = binding.Obtain(PropertyName('somthing')) [12:17:36] no .. in this case the __get__ call of the first attribute-access for this instance will activate the once-binding [12:17:42] someThing.blah you get Obtain has no function blah [12:18:09] right .. Obtain is a subclass of Once [12:18:44] once-classes use the __set__/__get__ protocol for their work [12:20:48] * jack-e is still fighting with p.w.t 's to be usable with publisher .... [12:20:57] ** martin___ has joined us [12:24:37] what's p.w.t? [12:24:47] peak.web.tempate(s) [12:25:12] peak uses zope3 for the publishing / http work [12:25:17] that requires ZopeX3 doesn't it? i can't get ZX3 to build on my computer [12:25:27] you need python2.3 [12:25:32] got it [12:25:37] what os you have ? [12:25:42] i think it's an OSX problem [12:25:49] you might ask hazmat :) [12:26:11] * victorng_ wonders if hazmat knows what's going on with ZX3 on OSX [12:26:15] hehe [12:26:59] is probably busy [12:27:23] how does it fail ? [12:27:30] all kinds of unit tests don't pass [12:27:49] does it start up ? [12:28:04] you know - i never even bothered to try. :) [12:28:40] if the compilation goes without failure (make) .. then you should be able to use the z3-parts you need even if some of the tests fail [12:28:49] in what siutations would you want to use binding.Obtain rather than lookupComponent ? [12:28:55] peak only uses zope.publisher and some minor other stuff [12:29:18] martin___: if you specify binding at class-level you use binding.* methods .. [12:29:33] if you want to lookup some url/key at runtime you use self.lookupComponent [12:31:17] understood, so then why declare at the class level and not at runtime ? Is it a performance issue ? [12:31:41] right .. the class-level bindings are cached automatically at first access [12:35:08] ** victorng_ has left us [12:35:28] thanks [12:35:34] np [12:36:40] ** victorng has joined us [12:38:15] ** martin___ has left us [12:39:19] ** martin___ has joined us [12:39:47] ** martin___ has left us [12:41:59] okay, why does one use binding.Require('some string') ? [12:42:29] to signalize that this component requires a binding for "some described by string" [12:42:40] it's like an enforced interface [12:43:11] it's a yet unkown value that needs to be supplied .. usually through ElementClass(parent, name, attr=something) [12:43:18] where attr is a required bindign [12:43:38] you can obmit parent, name then the Component get's created without context [12:43:52] you can then set context with inst.setParentComponent(parent, name) [12:44:21] works for all binding.Component [12:45:00] additionall a subclass can provide the binding as well [12:45:59] ok, so the point is if i try and use the component without the binding described by the string it will give an error [12:46:12] right [12:46:34] and it is also integrated into the initialization process [12:46:49] """Placeholder for a binding that should be (re)defined by a subclass""" [12:46:55] you can only specify keyword args for creating components where class-attrs or bindings exist [12:47:12] or at runtime .. that doesn't matter [12:52:41] it does not have to be (re)defined by a subclass though... [12:53:09] right .. you can use both approaches, which is very convinient [12:58:45] ** _jpl_ has joined us [13:02:21] <_jpl_> Greetings, all [13:03:49] howdee! [13:05:21] hey jpl [13:51:00] ** _jpl_ has left us [13:51:04] ** martin__ has joined us [13:52:20] I'm trying to bind a mock object and then offer it into my namespace, but I keep getting InvocationError. Any suggestions? [13:53:10] ** _jpl_ has joined us [14:03:00] ** martin___ has joined us [14:06:46] ** martin___ has left us [14:07:58] ** martin___ has joined us [14:39:10] <_jpl_> I've created a project for us at tigris.org [14:39:19] yay [14:39:42] <_jpl_> It's pending approval at the moment. I'll let you know if/when it gets approved. [14:39:49] ok [14:39:51] <_jpl_> http://peakplace.tigris.org/ [14:40:50] <_jpl_> Tigris is pretty nice. I was considering Savannah, but they down at the moment after being hacked. [14:40:59] <_jpl_> s/they down/they're down/ [14:41:06] does tigris work with svn ? [14:42:18] <_jpl_> I hoped it would, but apparently only for svn itself while everyone else has to use cvs. [14:42:56] ok .. i'ld like to try it out in development .. installed it a few times .. but i still stick with cvs cause of the view-cvs part [14:43:55] <_jpl_> viewcvs already works with svn. :) [14:44:29] ahh ... is there a possibility to import cvs-repositories into svn without loosing all information ? [14:44:35] <_jpl_> We're using svn here in our group (three people) and really like it a lot. The rest of the company uses cvs heavily. [14:44:54] <_jpl_> Yes there is. It's considered non-stable, but I've found it works quite well. [14:45:20] ok .. so it seems it's worth trying again soon :) [14:45:51] <_jpl_> HTTP for transport is really convenient, as is the way tags and branches are handled. [14:48:41] <_jpl_> Not to mention properties, and all operations are versioned, including file moves/renames, directory creation, etc. [14:48:51] yes yes yes :) [14:51:10] <_jpl_> Executable flags on files are handled as a property called 'svn:executable'. Setting or removing the property tells svn whether the checked out file will be executable or not. [14:52:40] <_jpl_> You can set any properties you want on a file, and property content can even be binary if desired. One example given for that is for managing image files, where you store a thumbnail for each image as an svn property on the file. [14:53:40] <_jpl_> svn can do deltas on binaries, too, which cvs can't do. [14:56:21] <_jpl_> Ok, about to add the latest Junction code to the project's CVS [14:56:26] my tigris uid is "ueck" [15:00:59] * Maniac uses svn for EVERYTHING [15:01:24] * Maniac svn add mybrain [15:01:42] svn commit -m 'upgrading cells' [15:02:13] <_jpl_> svn switch big-fat-brain mybrain [15:02:56] <_jpl_> "svn switch" is another handy feature not found in cvs [15:03:29] :) [15:03:45] <_jpl_> ulrich, go to peakplace and click on the "Request project membership/role" link [15:05:20] i can't even view the startpage .. i just registered as new user [15:05:24] and set my pwd [15:05:43] <_jpl_> Maybe it won't let you get to it until its approved. [15:06:15] probably .. it redirects me to the login-screen [15:06:47] <_jpl_> And you're already logged in? [15:07:06] yes [15:07:30] <_jpl_> alright... I'll let you know when(if?) they change the status [15:07:36] right [15:41:30] my tigris uid is "unknown user" [15:45:05] topic is 'peak security'..... debate ! [15:46:53] first you probably need some users :) [15:48:45] <_jpl_> Ok, about to add the latest Junction code to the project's CVS [15:48:49] which project? [15:50:41] peakplace.tigris.org [15:50:50] but there is nothing for non-proj.admins visible yet [16:07:30] tricky _jpl_, tricky [16:16:53] ** rdmurray has joined us [16:17:03] hey rdmurray :) [16:17:43] 'lo [16:21:48] I wonder if I can remember what I was doing in this project when I last looked at it [16:21:55] hehe [16:25:09] What should I do after a 'cvs up'? [16:25:22] how long did you not update ? [16:25:31] Two weeks? [16:25:47] ah .. just std. python setup.py install [16:26:02] then "peak test" and see if everything is ok [16:26:07] k [16:26:30] looks good [16:26:40] <_jpl_> No gigantic changes in the last two weeks [16:27:47] Ah, now I remember what I was doing: trying to learn how to use a model. [16:28:02] examples/bullettin [16:30:38] <_jpl_> examples/bulletins/src/bulletins/model.py [16:31:09] Yeah, I looked at that, and storage, and it's pretty unclear to me what happens in storage :) [16:31:25] But that's where I left off, so I should study it some more. [16:32:06] a datamanager behaves like a container with a configurable backend storage through (_load,_save,_new methods) [16:32:20] there is some caching and transaction suport builtin to DM's [16:32:46] additionally DM's use persistence/GHOST objects for lazyloading [16:33:01] you write these methods e.g. for your sqlconn or a file as you want [16:37:20] You know, just like with zpatterns, it bugs me that I have to write load and save code that repeats the column names. I know that flexability is needed for talking to pre-existing databases, but it's more than a bit of a pain when you are actually working with a database that's just being a dedicated backend. [16:37:50] But I don't know enough yet to write a layer to fix that issue :0 [16:37:58] i've worked out a SQLEntityDM that does a similar thing as PYRO (from skunkweb) [16:38:44] http://cvs.net-labs.de/cgi-bin/viewcvs.cgi/libs/nll/src/nll/database/sqldm/tests/test_datamanager.py?rev=1.5&content-type=text/vnd.viewcvs-markup [16:39:22] it's sufficient for simple things [16:40:04] ** vlado has joined us [16:40:22] * jack-e has not yet refactored nll* to use the new binding. API !! [16:46:39] is there a way to replace objects that are registered in the component? For example - in the bulletins application, the app.py module has Categories = binding.Make(...., offerAs=[storage.DMFor(Category)]) [16:46:55] i have a unit test where i want to substitute the CategoryDM for a mock DM [16:47:22] i was thinking that i should be able to overload the configuration key for the CategoryDM and put in my mock [16:47:37] any idea how to go about doing this? [16:47:38] you just supply Category= as keyword arg to component-creation [16:47:47] e.g. [16:48:02] modm = MockDM(app) [16:48:31] ** martin__ has joined us [16:48:40] bdm = BulletinsDM(ctx, Categories=modm) [16:55:27] i don't get it - can i just specify arguments to a component constructor and the keyword/value pairs will be treated as component attributes? [16:57:41] BulletinsDM doesn't have a Categories attribute [16:58:58] ** martin__ has joined us [17:01:17] <_jpl_> rdmurry: I've also written something called TableDM which does a lot of the SQL work for you. [17:02:13] That might be an interesting example for me to look at. [17:02:15] Can I? [17:02:36] <_jpl_> victorng: That's right (about keyword arguments to component constructors) [17:03:00] back [17:03:05] I have a feeling that peak is going to be really useful, but this initial learning cure sure is steep :) :) [17:03:24] <_jpl_> rdmurray: Yes, as soon as tigris.org accepts our project (peakplace.tigris.org) TableDM will be there. Hopefully within a day or two. [17:03:58] victorng: sorry .. i just typed without looking at the sources [17:04:09] * vlado is away, somewhere far beyond... (l!on)(p!off) [cRk/bx] [17:04:26] <_jpl_> Junction is almost ready for CVS (for real this time) [17:04:33] Categories is a binding of BulletinsApp (app.py) [17:04:53] replace BulletinsDM with BulletinsApp and it should do what you want [17:05:27] <_jpl_> I'm getting the little example app to work with all the refactoring I've done while separating Junction into its own self-contained package. [17:05:42] rdmurray: this is probably the case, because there is no uptodate documentation avaliable [17:06:20] <_jpl_> oops, unexpected meeting. bbl [17:06:25] * jack-e is looking forward to play with junction and contribute slp-support [17:06:30] jack-e: what's do i use for ctx? what is it? [17:07:05] jack-e: yes, I think so. [17:07:12] a component that has context. in case of the bulletins-app it is a command-object [17:07:21] you can create a rootcontext with: [17:07:32] ctx = config.makeRoot() if you don't have one .. [17:07:42] in unit-tests there is a testRoot function [17:07:51] rdmurray: i currently do all my development with peak [17:08:22] latest was a refactoring of an afsbackup - backend script for controlling a tape-library and scheduling backups .. [17:08:31] it was done in 1,5 days [17:08:37] based on bulletins example [17:08:52] http://cvs.net-labs.de/cgi-bin/viewcvs.cgi/apps/afsbackup/ [17:09:35] has no deps except current peak i think (it will probably only run if you're within an AFS-Cell Fileserver with mtx tapelibrary installed) [17:12:10] dumpinfo.py for example shows a DataManager whose backend is the AFS-Backup, accessed via os.popen .. [17:12:29] (readonly) [17:13:12] and there is no difference in accessing dumpinfos and sql-stored elements in your app at all [17:13:16] :))) [17:19:18] So the 'binding.Make(dumpInfo) is kind of like a computed-once attribute? [17:19:43] What extra magic does binding.Make do? [17:19:58] Ah, nevermind, I can read the source and answer my own question :) [17:20:02] :) [17:20:05] (when I really need to) [17:20:56] it's main purpose is to create an instance at the time you need it and cache it. [17:21:26] "you need" == first access to this attribute [17:25:18] OK, let me make sure I've got this right: to set up a storage all I have to do is subclass EntityDM and define appropriate _load, _save, and _new methods? [17:25:41] + an ElementClass that the DM Manages .. [17:26:10] Um. [17:26:23] So if I have several element classes I need multiple DMs. [17:26:50] probably .. how would you store a Person-record and a Room-record using the same queries? [17:27:27] you could write a DM that inspects the Class and write corresponding SQL .. but that's not available and probably won't be as it makes little sense [17:28:04] DM == persistence for an ElementClass [17:28:17] Well, I can't. But what I've been visualizing is a file containing 'transactions' that contain data, where the "parts" of the data are determined by internal flags. Expanding this into a Model, I end up with multiple Elements with appropriate cross references (or None values). [17:28:49] ok .. so what you need to do is: [17:28:52] Hmm. So I guess my DM is for my "Transaction" class, but it loads up all the other classes as well. [17:29:03] 1. create an index of id -> type-name [17:30:03] 2. overwrite _ghost to create a ghost of the right type (using id->typename) [17:30:25] 3. have flexible load/save/new methods that support persistence for different element-classes [17:30:54] * Maniac heads home, later all ! [17:30:58] Well, the only load and save operations will happen on a transaction as a whole. [17:30:59] cu [17:31:29] but if the file contains different datatypes .. you'll have to go this way probably .. [17:32:04] so you store only one type of data as "main" record in your file ? [17:32:23] I'm not sure what you mean. [17:32:30] Maybe I'm using Model wrong. [17:32:57] you store transaction records in your file, these might contain other data(e.g. other objects) [17:33:19] right ? [17:33:50] Let me explain further: my "example" application for learning peak is to develop a simple accounting system. The transaction is the fundamental data. A transaction has a date, plus debits/credits to various accounts, plus various possible types of auxiliary data. [17:34:06] A transaction is the fundemental entity. [17:34:36] In my Model file, I described it using subobjects for the groups of debits and credits, and the individual debits and credits, and the various possible types of aux data. [17:34:49] ok [17:35:25] so a Transaction - Component is basically a composite (Debits, etc) .. right ? [17:35:38] Yeah [17:35:41] Transaction Element i mean [17:35:44] right [17:35:58] so your DataManager manages TransactionElement's [17:36:06] That's what I figured. [17:36:16] they have proably an oid which is used to access/store them [17:36:22] right. [17:37:40] if an element is modified, it's dirty within the transaction [17:37:48] yes [17:38:12] Although in the first version there will be no mods, only new transactions. [17:38:12] (i'm not 100% sure about the details of creating the child-components - especially ther _p_jar setting) [17:38:19] right [17:38:38] _p_jar for model.Element == parentComponent [17:38:39] Actually, that may alwasy be true. [17:38:54] _p_jar is the datamanger that manages the persistent object [17:39:42] What's parentComponent? [17:39:43] if you commit, the save method needs to ensure that the whole transaction-record is written back to the file [17:40:01] every component in peak has context (or at least can have) [17:40:21] parentComponent is the parent of this component or None, then you're at the root .. similar to zope [17:40:31] aq_parent [17:40:40] So my subElements should set _p_jar to the element that references them? [17:40:50] i'm not sure about this .. [17:41:00] k [17:41:04] you'ld have to ask pje [17:41:49] Does the DM instantiate the class it is responsible for an somehow "load" the Element using the data returned by the _load method? [17:42:21] this is controlled by persistence.Persistent base-class [17:42:38] the DM creates a GHOST if you [oid] [17:43:05] if you access the .someAttr, the persistence-mechanism causes the DM to _load the state [17:43:58] if you modify, the persistence-mechanism signals this and the DM registers the with the current transaction, which ensures that the modified data get's stored with _save at commit [17:44:10] I think I see. [17:44:16] fine :) [17:44:38] Maybe I should consider separating the data. There might be some utility to that application wise. I have to think about it. [17:57:53] How does model.Collection fit in to this data load/save stuff? [17:58:37] your DM need's to "know" if your attribute is a Collection or Attribute .. and behave correctly [17:59:11] And if it is a collection it is a list of things? [17:59:24] What does the DM do if it is a collection? [17:59:31] basically a typed list .. except you have model.Any as referencedType ;-) [17:59:42] nothing that you haven't told him :) [17:59:55] a collection is an attribute-type [18:00:00] a attribute itself is [18:00:27] it just signals that you will store a list of in this attribute [18:00:38] schema definition basically [18:02:10] s/a attribute/as attribute [18:22:48] What I want is a collection whose referencedType is "one of the following list" [18:23:57] then you currently have to use model.Any as referencedType [18:24:48] or a common super-type [18:26:24] <_jpl_> back [18:26:53] <_jpl_> rdmurray: What are your model classes, and what are the corresponding tables? [18:27:37] Well, originally there was just one file containing transactions, but I'm leaning toward rethinking it to have separate tables. [18:27:49] <_jpl_> If your collections are of other model-based objects (that were loaded from a DM), they'll be stored automatically by their DM. [18:28:04] <_jpl_> So currently one model class and one table? [18:28:19] Ah, and the DM needs to no its a collection so it can iterate. [18:28:31] Well, I had many model classes and one table :) [18:28:59] But I've thought of reasons to separate the data, so I'm fixing that (in my head; I haven't written any DM code yet). [18:29:25] <_jpl_> No, the DM doesn't have to worry about the collection if its a collection of DM-managed model objects, since that collection wouldn't map to anything on the SQL side (it's more of a convenience feature on the model side). [18:29:56] <_jpl_> Ok, so basically if you have one table you need only one DM. [18:30:24] But it wasn't clear how to load all the Model classes. [18:30:35] But anyway, I'm switching to multiple tables :) [18:30:49] <_jpl_> In your _load method you'd create the various objects. [18:30:59] <_jpl_> based on the data in the table. [18:31:15] Ah. Right, I found an example of that with the model.Enumeration class. [18:31:24] <_jpl_> So when you switch to multiple tables you'll most likely need more than one DM. [18:32:25] <_jpl_> It's somewhat clearer in TableDM. You can see an early example of it in this post: http://www.eby-sarna.com/pipermail/peak/2003-October/000772.html [18:37:38] <_jpl_> So looking at the bulletins example we see how there are DMs for the User, Bulletin, and Category model classes, with corresponding "users", "bulletins", and "categories" tables on the SQL side. [18:50:10] <_jpl_> On the object side, the Bulletin class has an attribute called 'category' of type Category. When BulletinDM loads a Bulletin object, it also uses CategoryDM to load the corresponding Category object. [19:00:53] <_jpl_> And so on for other references like Bulletin.postedBy and Bulletin.editedBy (of type User, loaded/saved with UserDM). [19:08:39] That "load" of secondary objects is lazy, right? [19:08:54] yep [19:09:56] And circular references are OK? (ie: the category dela points to the transaction, but the transaction also points to the cetegory delta through a Collection) [19:10:13] afaik yes [19:14:10] <_jpl_> I'm not so sure about lazy loading, or else I haven't been using EntityDMs correctly. [19:14:38] Well, I thought that's what that whole thing about shadow was about :) [19:14:46] er, ghost [19:15:00] <_jpl_> But as far as I can see, when you lookup something in a secondary DM it's doing a lookup right then and there. [19:15:13] Hmm. [19:15:16] jpl: nopw [19:15:45] if you do a .__getitem__(oid) .. you do not necessarily cause the _load method to be called [19:15:54] so there is an object created but the state is not loaded [19:15:57] <_jpl_> I see [19:16:54] <_jpl_> Initially you'd get a ghost, which would only be filled in once you refer to something on the object? [19:17:05] yup [19:31:37] Another design question: I have these things I'm calling categories, where there are at least three different types of categories, each of which has slightly different data associated with it. But my 'delta' entities can belong to any of the three category types, so logically my delta Element has an Attribute whose value is a 'Category' superclass. Now, if I have three category DMs, one for each type, how do I hook things together? [19:39:29] Hmm. I guess I have to code a discriminator into the category id. [19:39:35] * jack-e can't answer this before he fells asleep .. that's why i get out of here now .. and get into my bed :) [19:39:44] 'night [19:39:51] nite [19:39:56] jack-e is now known as jack-e|away [19:50:25] <_jpl_> What do you mean by "slightly different"? [19:51:18] <_jpl_> If they're only "slightly different" you might not need separate DMs for each. [19:51:19] Well, one category type has an attribute "external id", but a different category type has an attribute "period". [19:51:44] <_jpl_> So the three categories are siblings? [19:51:49] Think superclass with some specialized subclasses, but each subclass logically wants to be in a different table. [19:51:51] Yeah. [19:52:36] I *think* I see how to do it, by using the category id to discriminate. But I'm open to better suggestions :) [19:52:47] <_jpl_> Why do you say logically? There are various approaches to handling object-relational mapping with regard to inheritance. [19:53:20] <_jpl_> If your classes are not terribly complex, it doesn't hurt to keep them all in a single table. [19:53:37] Well, the categories are more configuration than they are data, and I want the different types of categories to be dealt with separately. [19:54:01] Maybe I should be using the configuration facilities somehow, instead. [19:54:27] But I have no clue how they work, yet : [19:54:28] :) [19:54:31] <_jpl_> That's fine, you handle those differences on the object side. But there's a big difference between how you design objects and how you design tables to store those objects. [19:55:17] Well, I'm cheating, in that I'm intending on making the category 'database tables' hand editable... [19:55:40] <_jpl_> I don't understand. [19:55:52] Let's just say I'm not adhering to good OO design :) [19:56:34] <_jpl_> You're confusing me by jumping back and forth between tables and objects without being clear about which one you mean. [19:56:52] Hmm. [19:57:12] OK, so I have three classes, all with a common superclass. [19:57:31] Objects of any subclass can be used interchangeable most places in the ap. [19:58:28] Suppose that the data that will be loaded into these classes comes from three different outside database sources (forget why for the moment). How do I design my DM (perhaps a DM for the superclass) to handle this? [19:58:38] Ah, yes, I see I should be using a single DM. [19:58:57] But I still think I have to discriminate which source to pull the data from via the Oid. [20:01:00] <_jpl_> We're running into this same problem at the moment. You'll probably need a separate DM for each class, including the superclass, and you'd probably need a table for the superclass as well. You'd then make the superclass's DM return the right type of object (loaded from the appropriate DM) depending on a flag in the superclass record. [20:01:21] <_jpl_> er, the record from the superclass's table.. [20:01:56] Oh, yeah, I thought of that scheme a bit ago, I forgot. [20:02:00] <_jpl_> This is a really pain in the neck, so if your objects are not terribly complex you'd be better off keeping them all in a single table. [20:02:40] <_jpl_> And just have methods that load the data appropriately according to what type of object it should be. [20:05:25] <_jpl_> I'm not sure where you'd override the DM's defaultClass attribute, since _load seems to be expected to return a dictionary. [20:05:50] <_jpl_> sigh, another meeting. bbl. [20:05:56] see you [21:02:11] * _jpl_ wonders how long it takes tigris to improve a project. [21:02:16] <_jpl_> er, approve. :) [21:05:30] Do you understand the config/naming stuff? [21:05:52] Hmm. Actually I guess this is a binding issue. [21:06:15] I want don't want to hardcode filenames in my DM code... [21:23:53] <_jpl_> Yes, I could help with that. [21:25:14] <_jpl_> The easiest way to get started is by using .ini files and a main application class based on peak.running.commands.AbstractCommand. [21:25:42] At this point I guess my question is pretty simple: what do I put in my DM code so that the DM will (eventually, once I deal with the config stuff) get the filename from context? [21:26:18] I have this irrational aversion to using .ini files because I first encountered that file syntax in Windows :) [21:26:42] <_jpl_> Note that this is how the bulletins example works. Look at examples/bulletins/bulletins for an "executable" .ini file, and examples/bulletins/src/bulletins/app for the main class. [21:27:17] <_jpl_> You could use ZConfig if you prefer, but it takes a lot more work to setup. [21:28:11] <_jpl_> What is the filename that the DM needs to get? Do you mean database connection? [21:28:31] I'm implementing my "database" using flat files. [21:29:09] So there's a bunch of filenames involved. I suppose they could be hardcoded filenames within a configured 'database directory'. [21:30:07] <_jpl_> errr, ok. You don't want to just use something like SQLite? [21:30:15] No. :) [21:30:42] For one thing, all my data is 'append only'. [21:31:12] But to some extent, this is an exercise in seeing how flexible Peak is :) [21:31:37] ie: I should be able to implement it this way, and with minimal effort convert to sqllite or something else later. [21:44:20] <_jpl_> Ok, sure. [21:45:16] Hey, in that scheme where you have a superclass DM that calls subclass DMs as appropriate, what do you have the superclass DM's _load method return? [21:45:33] <_jpl_> You can write a DM which does just about anything in its _load/_save/etc. method. [21:45:58] <_jpl_> Ah, yes, I mentioned earlier that I wasn't sure what you'd do there... [21:46:39] Hmm. [21:46:42] this is tricky. [21:46:48] <_jpl_> ...since it looks like _load usually returns a dict. I'll look at the source for EntityDM in a sec. [21:47:04] The interface says what is returned gets passed to __setstate__ [21:47:10] (well, implies that) [21:50:27] <_jpl_> Ok, I see what it's doing. [21:51:07] On the other hand, the attribute being called 'defaultClass' would imply there is a way to change it. [21:51:25] <_jpl_> Right, _load should return something that's appropriate for __setstate__ on the object that's getting the data.. [21:52:17] If I can return a specific subclass rather than the default, that will work. But I don't know how to do that :) [21:54:49] <_jpl_> You'll need to override _ghost() to return the right object type. [21:55:03] Ah. [21:56:13] <_jpl_> Take a look in PEAK/src/peak/storage/data_managers.py at QueryDM._ghost [21:57:38] That looks simple enough. [21:57:59] I'd never have figured that out from the docstring in the Interface documentation for _ghost, though [21:58:03] <_jpl_> oids for your purposes could just be line numbers (that simplifies things a bit!)... [21:58:08] yes [21:58:23] <_jpl_> You mean the docstring that isn't there? [21:58:41] ? [21:59:03] I mean the docstring for _ghost under IDatamanager_SPI [21:59:16] It is *very* unclear. [21:59:22] <_jpl_> Oh, yeah, you said Interface. [22:00:17] <_jpl_> The first line seems pretty clear. [22:00:27] <_jpl_> The _p_deactivate part is a little fuzzier. [22:00:36] Right. [22:00:47] If it had only had the first line, I would have understood it :) :) [22:01:53] <_jpl_> I usually just go straight to the source (code). :) [22:02:00] I have a feeling the extra text is describing how the returned ghost is *used* by the DM or Persistence code, but I'm not sure. [22:03:38] <_jpl_> Yes, it's describing what will be done (in QueryDM.__getitem__) with what _ghost returns. [22:04:51] The confusing comes from saying 'if state is none', which one immediatly thinks refers to the state argument of the _ghost call, but it clearly doesn't. [22:05:21] <_jpl_> It does, actually. [22:05:29] Oh? [22:06:00] What should I be doing with state, anyway? QueryDM doesn't do anything. [22:07:22] <_jpl_> QueryDM.__getitem__ itself gets 'state' as an optional parameter (set to None if not supplied), passes it to _ghost, and later checks to see if it's None to do as the docstring indicates. [22:07:53] <_jpl_> I just don't know what _p_deactivate() does or means. [22:08:13] OK. [22:08:50] I wonder how state gets to be non-None when an object is first referenced (before its attributes are referenced). [22:08:58] <_jpl_> I'm guessing that the behavior allows you to immediately populate a ghost object when you look it up (of course, then it's no longer a ghost). [22:09:30] Oh, yeah, I have a fuzzy memory about that from pje's original discussions of the DM design. [22:09:30] <_jpl_> I don't know, since __getitem__ is usually called when you say something like dm[1]. [22:10:17] So state is normally None, and p_deactivate is normally called. Sometime I'll have to look up what that does :) [22:10:23] <_jpl_> Oh wait, I see... [22:10:39] <_jpl_> In QueryDM there's a line like so: preloadState = __getitem__ [22:11:11] <_jpl_> Normally __getitem__ will never have a second parameter. But when called as preloadState you can pass in the state parameter. [22:12:25] <_jpl_> I just realized that pje uses preloadState in the bulletins example. [22:15:05] interesting. I wonder when getAll gets called. [22:15:41] Hmm. What do I used to convert a datetime from a string? [22:15:51] I haven't worked with datetimes yet. [22:16:21] <_jpl_> You mean 2.3's datetime object? [22:17:24] Yeah. [22:17:53] In Bulletins he seems to just set the variable to what he got out of the sql call. Does that mean the sql thingy is doing the conversion? [22:19:47] <_jpl_> A SQL driver will usually return a date as a formatted string, yes. [22:20:27] So how does bulletins get that string turned into a datetime? [22:22:01] <_jpl_> BTW, you should look at peak.storage.files.EditableFile for working with your data files transactionally. [22:22:27] Yeah, pje mentioned that. But my files are append only, so that doesn't seem appropriate. [22:22:48] <_jpl_> A SQL driver will also usually accept a formatting date for a date field value. [22:23:20] <_jpl_> It's still appropriate. It means that you can write out all changes transactionally regardless of what type of changes you actually make. [22:23:21] Well, I can do the conversions easily enough, I just figured peak would have something slick build in :) [22:23:31] <_jpl_> It should ensure that your files will always be in a consistent state. [22:23:41] Um. [22:23:43] Good point. [22:23:45] That could be important. [22:23:58] Actually, that's very important. [22:25:42] <_jpl_> It also means that you can safely make whatever changes you want to the in-memory copy of the file (say, throughout a number of sub-transactions of some sort), then commit the whole thing at one time, or abort all changes if one of the sub-transactions is invalid. [22:26:52] Hmm. Looks like this means I need the EditableFile objects to be something the DM obtains from context. [22:27:04] I have no clue how to arrange for that :) [22:27:52] <_jpl_> That's pretty easy, actually. [22:28:16] <_jpl_> Do you see how the DMs are created in a top-level app component in bulletins/app.py? [22:29:32] Well, i see the code, but I don't understand what it is doing. [22:39:31] <_jpl_> As components of the top-level app object, the DMs will be able to access any config properties that the app object knows about. [22:42:16] <_jpl_> Have you looked at the bulletins/bulletins file? [22:49:30] <_jpl_> It's an "executable config file", which means you can run it (under *ix systems, anyway), and it'll instantiate and run whatever IRunnable-supporting object is indicated by the peak.running.app property. [22:50:02] <_jpl_> Though in this example the peak.running.app property is actually in src/bulletins/bulletins.ini. [22:50:30] I saw that. I missed this file. [22:51:01] <_jpl_> bulletins.ini is loaded in the [Load Settings From] section of the first config file. [22:52:05] I think I'm getting the general idea. [22:52:11] <_jpl_> You don't have to execute config files, either, if that's not an option for whatever reason. Just do something like 'peak runIni my-config-file' in that case. [22:53:03] <_jpl_> In any event, whatever you put into your config file will be available anywhere in the app using binding and PropertyName. [22:53:08] What does PropertyName do/mean? [22:53:55] Ah, wait, I think I get it. [22:54:06] <_jpl_> It looks up a config property. [22:54:35] <_jpl_> So you can set a binding in a component with something like: myFile = binding.Obtain(PropertyName('myapp.files.file1')) [22:54:53] <_jpl_> WIth an ini file that looks like: [22:55:21] <_jpl_> [myapp.files] [22:55:21] <_jpl_> file1 = "some-file.txt" [22:56:07] So PropertyName('myapp.files.file1') would return 'some-file.txt'? [22:56:19] What does bindings.Obtain do? [22:57:25] <_jpl_> It just obtains something from somewhere else in the application. [22:57:42] Oh, right the provide as thing, right? [22:58:21] <_jpl_> That's one way of making something available, yes. There are various other ways as well, like through the configuration system (as we're seeing with PropertyName). [22:58:58] I still feeling a little confused, but I'm not sure what to ask. [22:59:20] Maybe I'll put any more questions off until I actually try to get the app part working. [23:00:20] <_jpl_> Have you read the little blurb about binding in the PeakDatabaseApplications wiki entry? [23:00:42] yeah, but it was a few weeks ago :) [23:01:00] And I don't really understand the URI stuff. [23:01:54] <_jpl_> Which, the sqlite: string? [23:02:00] yeah [23:02:43] <_jpl_> binding.Obtain can obtain things for you by URL, too. [23:04:43] <_jpl_> In that case, it "obtains" a connection to a sqlite data file. [23:07:18] yeah, but it sure looks like magic :) [23:07:39] But I don't think I need to understand that just yet. [23:08:29] <_jpl_> Have you looked at the main peak.ini file? All the URL to class mappings are there. [23:10:46] <_jpl_> They're in the peak.naming.schemes section. [23:10:57] k [23:11:27] * rdmurray is feeling information overload and decides to ignore that stuff for the moment. [23:11:38] <_jpl_> sqlite maps to peak.storage.SQL.SqliteURL, which does the actual handling of the rest of the URL. [23:11:46] <_jpl_> Yeah, you don't need it right now. [23:12:59] <_jpl_> Just think of binding as a way of obtaining component objects from various different sources, or making objects to be potentially used elsewhere. [23:13:08] <_jpl_> It's more than that, though, of course. [23:16:10] Hmm. Ok, I'm confused again. IN storage.py, bulletins does PropertyName('bulletins.db'). But I don't see a 'db' defined in the bulletins section of the 'bulletins' file. [23:17:09] <_jpl_> That's because it's provided by the top-level application component (BulletinApp) in app.py. That's where an offerAs comes in. [23:18:20] Ah, I see, offerAs(PropertyName. I have no idea how that magic works, but I can accept that it does :) [23:20:37] <_jpl_> When you use offerAs in a component, you're saying that any other component below that component in the hierarchy will be able to obtain that property (using whatever you specified as a "config key" in offerAs). [23:22:38] <_jpl_> You can use various things as config keys, like PropertyName, an interface object, and some other things I don't know about. :) [23:23:21] storage.DMFor, whatever that is :) [23:23:33] <_jpl_> Oh, another possible config key is "storage.DMFor", which is also used in bulletins.app.BulletinApp. [23:23:39] I mean, I know what it is, I just don't know what kind of python thing it is. [23:38:48] Hmm. In one of my data types, I only want to allow a single field to be modifiable. How can I throw an error if any other field is modified? [23:44:16] <_jpl_> In a model class? [23:44:26] If that's where it's appropriate to do it. [23:45:05] I'd prefer the error get thrown as soon as anything attempts to modify the object. [23:46:40] <_jpl_> I think that depends a lot on where and how the object is used. [23:47:03] <_jpl_> And what type of object it is. [23:47:34] Hmm. I was hoping for some sort of 'read only property' flag :) [23:47:41] In the model. [23:47:44] <_jpl_> But a property of what? [23:47:51] <_jpl_> Oh, in a model class. [23:47:56] <_jpl_> That's what I asked a minute ago. :) [23:48:00] Yeah. An Attribute of an Element. [23:48:06] Oh, I thought I answered that, sorry. [23:52:23] I suppose I could always create subclasses of model.Immutable (ImmutableString, etc). [23:53:06] <_jpl_> Take a look at peak.model.datatypes, which is where things like model.String come from. You could subclass one of these to make something like, say, an ImmutableString. These classes can have a mdl_normalize method, which is called whenever the "feature" (as they're called) of the element is assigned a new value. You could throw your exception there, perhaps. [23:54:58] <_jpl_> I don't think subclassing Immutable will work in this context. [23:55:06] Yeah, maybe not. [23:55:30] <_jpl_> It's more of an Element class rather than a Feature class. [23:56:44] <_jpl_> You could subclass datatypes.Any [23:57:54] does mdl_fromString get called automatically if you assing a string to Feature attribute? [23:59:12] <_jpl_> No, mdl_* methods are factory methods, I think.