[TransWarp] PEAK Applications Code and Concepts

Phillip J. Eby pje at telecommunity.com
Wed Feb 26 17:26:55 EST 2003


At 09:09 PM 2/26/03 +0100, Ulrich Eck wrote:
>Hi Phillip,
>
>thank you very much for your exessive comment on our first steps with peak.

I hope that by "excessive" you mean helpful and plentiful, as opposed to 
annoying or overwhelming.  :)


>first there is a typo in model/elements.py in line 502:
>
>        def _doGet(ob):

Thanks.  I'm checking in a fix now.


>>* The acmgr.connection.imapmodel.MessageHeaderField class has some 
>>issues.  First, you're breaking persistence
>>notification by modifying a stored mutable, unless the mutable itself is 
>>Persistent.  Second, the proper place to
>>perform format conversions of this sort is in the DM _load() method, 
>>either by performing the computation or
>>inserting a LazyLoader.  Third, you're breaking validation/observation 
>>capabilities by bypassing _link()/_unlink() -
>>this might be okay if you know that the 'data' attribute is implementing 
>>all the validation and observation you need.
>>Fourth, if you *really* need to redefine how an object stores its 
>>bindings, you can always override '_setBinding()'
>>in the element class.  All in all, the MessageHeaderField class is very 
>>bad for separation of implementation issues
>>from the domain model.
>
>Not Done: I'm not 100% shure how i should handle this situation... 
>message-header is basically like a dict
>that has standard + optional keys. if i restrict the Element to 
>From/To/Subject/+xxx Features i can easily
>load the data into the state-attributes one for each - but then i loose 
>the capability to access optional
>header fields ..

You could always define methods to retrieve or manipulate headers.  Or just 
declare a simple attribute called "headers" or something, that's the mail 
header or email message object, and expose that as part of the model.


>a Dict-Like Feature is basically missing :) but i have no clue how to make 
>such a thing real, if it would
>be possible and make sense ...

Since in this case you have a dict-like object to start with, why not just 
use a plain 'model.Attribute' whose type is the email message type?



>>* Most of your features don't declare a 'referencedType'.  *This will 
>>break soon*.  I expect to add a validation hook
>>to the StructuralFeature._link() method soon, that will expect to call a 
>>'mdl_normalize()' class method on the
>>referenced type, to normalize the value of a supplied input object.  This 
>>will let you do things like:
>
>Partly Done
>
>hmm .. what type should e.g. a collection of messages get ???

If the message type is based on a peak.model base class, do this:

class messages(model.Collection):
     referencedType = 'path/to/messagetype'

Otherwise, create a message type of your own:

class Message(model.PrimitiveType):     # suppress all peak.model direct 
support

     def mdl_normalize(klass, value):
         # code to verify that 'value' is a message or convert it to one
         return value

Optionally, the type can implement mdl_fromString() and mdl_typeCode, but 
those things are only needed for loading from XMI at the moment.  The 
minimum requirement right now is just to implement an mdl_normalize() 
method, and even there I think you might be able to use the 
default!  Anyway, you would then be able to use
'Message' as your feature type.

>or the message.data (if it'll stay alive) that is an email.Message object ??

See above; just use a model.Attribute whose referencedType is an object 
representing the type.


>do i need to define a type for each new feature .. will you accept 
>contributions ??

You need to define types for anything that's important to your 
application.  I'll consider contributions based on applicability and 
usefulness, but by intention this is an area many apps will want to flesh 
out for themselves.  For new types, mostly you should use 
'model.Immutable', 'model.Struct', or 'model.Element' as a base class, and 
actually implement the type.  For types reused from elsewhere, you just 
need the metadata supplied by a "dummy" class based on 
'model.PrimitiveType'.  You can see examples of this in 
'peak.model.datatypes', where I declare a bunch of string and integer 
types.  PrimitiveTypes are not themselves ever instantiated; there is never 
an instance of 'model.Integer', for example.  The PrimitiveType is a 
stand-in for the *real* integer type, for purposes of supplying model 
metadata and behavior like a standard conversion protocol, typecodes, etc.


>e.g. type that i miss:
>- DateTime (preferable with the new datetime module)

Just make a simple PrimitiveType for it, and you're set.


>- List/Collection Type (does this make sense ??)

Not really.  Define the *feature* to be a collection, and set its type to 
the type of thing that goes *in* the collection.


>how to handle "References" to e.g. Parents that could be of different type ??

Different from what?  See below:


class File(model.Element):

     class parent(model.Attribute):
         referencedType = 'Folder'
         referencedEnd  = 'contents'


class Folder(File):
     class contents(model.Collection):
         referencedType = 'File'
         referencedEnd  = 'parent'

Does that answer your question?  In this case, File has a parent of Folder, 
because it has no contents.  Folders can contain files, so the contents is 
of type 'File'.  But, a folder *is* a file, so it has a parent, which is 
another folder.

Maybe I don't understand your question.  :)


>>* I recommend the idiom 'isChangeable = False' for clarity, in place of 
>>using '0' for false.  I'm also curious
>>whether the status fields should really be represented as derived 
>>attributes or methods instead of as read-only
>>attributes, but I don't know/remember enough about IMAP to give an 
>>informed opinion.
>
>what exactly are derived attributes ( perhaps my (still limited) english 
>vocabulary prevents me from understanding ...)

A derived attribute (model.DerivedFeature) is one which is computed rather 
than stored.  To implement this, you set 'isDerived=True' or subclass 
'model.DerivedFeature', and then override the 'get()' method so it computes 
and returns the value.  'peak.metamodels.UML13.Foundation.Core' has a 
couple of examples, like adding a 'qualifiedName' feature to UML model 
elements.


>Why doesn't a binding provide an Interface automatically it the instance 
>bound defines one ??

Because it would then have to know the instance ahead of time, and the 
whole point of a binding is to be lazy.  By predeclaring the interface, the 
class can keep a registry of what attributes will supply what interfaces, 
and when a request for one comes in, it just activates that 
attribute.  Also, this allows you to declare that an object supports an 
interface that is independent of what it claims.  For example, the 
component might supply an 'ILDAPConnection', but I might declare it to 
provide 'IUserDB_LDAPConnection', to say that it fills a particular 
application role.  As far as the connection knows, it is just a plain LDAP 
connection.  As far as the application is concerned, it's the *user 
database* LDAP connection, though, and not the *mail routing* LDAP 
connection instead.


>today we started work on the generic LDAPEntityDM - wouldn't that one 
>belong to peak.storage when finished and
>our code quality/implementation is acceptable ??

It would be nice to have one.  For my "day job" it may have more stringent 
requirements and I'd prefer to maintain only one, but if it can be done by 
enhancing yours (if needed) rather than creating one from scratch I'm good 
with that too.

Note for example that one particular requirement we have is for 
normalization of DN's...  we work with different LDAP servers that behave 
somewhat quirkily as to their DN syntax, so I want to make sure that any DN 
used as an oid has been normalized to a standard format.


>(note on LDAPConnection: the case of keys e.g. objectclass vs. objectClass 
>in LDAP is making life hard,
>would it make sense to store ldap-keys in the result allways lower case 
>for example ??)

Yes, it would.


>I also started a prototype of a SQLEntityDM that get's a field/index-spec 
>and build the needed queries
>(once). it's inspired by PyDO (from skunkweb) .. and i have a question on 
>this:
>
>peak tries hard to decouple different ascpects of your app.
>wouldn't it make sense to give the SQLConnections some more methods that 
>hide implementation details
>like Sequences, ParameterPlaceholders or StoredProcedure-Calls that are 
>not defined in the python dbapi2.

We'll see.  One thing I've (almost) learned is that it's better to wait 
until closer to when you need something rather than to just go out and 
write things like that.  The reason I say "almost learned" is that there's 
a *lot* in PEAK right now that isn't actually needed yet...  like my recent 
addition of RLock and ResourceManager to 'peak.util.threads'.  I wrote them 
to support a vision I had of how to do a generally useful FileDM, but I'm 
actually thinking I might not actually use them for the first FileDM I 
write, which will be very basic and aimed mainly at getting a primitive 
ability to write XMI files.


>again .. thanks and keep going this project and poke on us if you have 
>tasks where we could help.

Hmmm...  want to work out a way to speed up peak.model attribute 
access?  :)  Currently reading a 'model.Attribute' is from 13 to 39 times 
slower than reading regular Python object attributes.  I'd be happier with 
5 to 10 times, so that's a 3-4x performance increase needed.




More information about the PEAK mailing list