1 #=======================================================================
3 __version__
= '''0.0.01'''
4 __sub_version__
= '''20080518211107'''
5 __copyright__
= '''(c) Alex A. Naanou 2003'''
8 #-----------------------------------------------------------------------
10 import pli
.tags
.generic
as generic
11 import pli
.tags
.tag
as tag
12 import pli
.tags
.path
as path
13 import pli
.objutils
as objutils
14 import pli
.pattern
.mixin
.mapping
as mapping
19 #-----------------------------------------------------------------------
20 # XXX this needs to get cleaned, partially re-written and possibly
21 # split into several other modules...
22 # XXX write doc and examples...
24 # TODO generate an ID for each object in store!!! (do as a mixin and
25 # possibly not here...)
26 # TODO add support for different format tagchains...
28 #-----------------------------------------------------------------------
29 ##!!! move somewhere and revise !!!##
33 return 'OID_%s' % str(id(obj
)).replace('-', 'X')
36 #--------------------------------------------------------------public---
44 #-----------------------------------------------------------------------
45 #-----------------------------------------------------NodeConstructor---
46 # object constructor wraper...
47 # - when created will register and tag itself.
48 # - will proxy to the original constructor and tag the resulting object.
49 class NodeConstructor(object):
52 # most likely will not need to be changed manualy...
53 ## __object_tags__ = ('instance',)
60 # XXX move the defaults out of the code...
61 def __init__(self
, name
, constructor
, *tags
):
64 ## if ('constructor', name) in self.__tagset__:
65 chain
= self
.__tagset
__.tags2chain('constructor', name
)
66 if chain
in self
.__tagset
__:
67 raise TypeError, 'constructor id must be unique, id "%s" already exists!' % name
68 self
.constructor_name
= name
69 self
._constructor
= constructor
72 ##!!! revise the constructor/name pair format... (use tag groups?)
73 self
.__tagset
__.tag(self
, 'constructor', name
, chain
, *tags
)
74 # XXX would be good to add the "format" support.... (this might be
76 def __call__(self
, *p
, **n
):
80 res
= self
.__dict
__['_constructor'](*p
, **n
)
81 self
.__tagset
__.tag(res
, *(self
.__object
_tags
__ + self
._tags
))
83 def __getattr__(self
, name
):
86 return getattr(self
._constructor
, name
)
89 #-----------------------------------NodeConstructorWithOIDReturnMixin---
90 ##!!! IMO this functionality should be in the serializer... (if can't
91 ##!!! serialize an object then return it's OID...)
92 class NodeConstructorWithOIDReturnMixin(object):
95 def __call__(self
, *p
, **n
):
98 return {'oid': getoid(super(NodeConstructorWithOIDReturnMixin
, self
).__call
__(*p
, **n
))}
101 #------------------------------------NodeConstructorWithCallbackMixin---
102 class NodeConstructorWithCallbackMixin(object):
105 # defines the name of the inteface method called when the object is
108 # <method-name>(oid, tagset, tags)
109 __node_constructor_callback_name__
= '__ontreenodecreate__'
111 def __call__(self
, *p
, **n
):
114 res
= super(NodeConstructorWithOIDReturnMixin
, self
).__call
__(*p
, **n
)
115 # call the interface method...
116 if hasattr(res
, self
.__node
_constructor
_callback
_name
__):
117 getattr(res
, self
.__node
_constructor
_callback
_name
__)(getoid(res
), self
.__tagset
__, self
.__object
_tags
__ + self
._tags
)
122 #----------------------------------------------DynamicNodeConstructor---
123 class DynamicNodeConstructor(NodeConstructor
):
126 def __init__(self
, tagset
, name
, constructor
, common_tags
=(), object_tags
=()):
129 self
.__tagset
__ = tagset
130 self
.__object
_tags
__ = object_tags
132 super(DynamicNodeConstructor
, self
).__init
__(name
, constructor
, *common_tags
)
134 def __getstate__(self
):
138 def __setstate__(self
, state
):
141 self
.__dict
__.update(state
)
143 #---------------------------------DynamicNodeConstructorWithOIDReturn---
144 ##!!! does this need to get reorgonized?
145 class DynamicNodeConstructorWithOIDReturn(NodeConstructorWithOIDReturnMixin
, DynamicNodeConstructor
):
151 #------------------------DynamicNodeConstructorWithOIDReturnNCallback---
152 class DynamicNodeConstructorWithOIDReturnNCallback(NodeConstructorWithCallbackMixin
,
153 NodeConstructorWithOIDReturnMixin
,
154 DynamicNodeConstructor
):
160 #-----------------------------------------------------------------------
161 #----------------------------------------------------TagTreePathProxy---
162 # XXX add direct interface...
163 class TagTreePathProxy(path
.RecursiveAttrPathProxy
):
166 def __getattr__(self
, name
):
169 if name
.startswith('OID_'):
170 return self
._getobject
(name
)
171 ## if ('constructor', name) in self._root:
172 chain
= self
._root
.tags2chain('constructor', name
)
173 if chain
in self
._root
:
174 # XXX is this safe??? (constructors should be unique!)
175 ##!!! WARNING: here the select also returns "tag".... check this out!
176 ## return [ o for o in self._root.select(('constructor', name)) if o != generic.TAG_TAG][0]
177 return [ o
for o
in self
._root
.select(chain
, generic
.OBJECT_TAG
)\
178 if o
!= generic
.TAG_TAG
][0]
179 return super(TagTreePathProxy
, self
).__getattr
__(name
)
180 ## __getitem__ = __getattr__
182 # public interface...
183 # in general, this will form the args and call the corresponding
186 def relatedtags(self
, *tags
):
189 return self
._root
.relatedtags(*self
._path
+ tags
)
191 def chains(self
, *tags
):
194 return self
._root
.chains(*self
._path
+ tags
)
196 # XXX add efficient counting...
197 # XXX split this into two levels... one to return objects and
198 # another to format them into attrs... (???)
200 ## def list(self, form=None, to=None, count=None, *attrs):
201 def list(self
, *attrs
):
203 list the data form the objects in this subtree.
206 # XXX make this iterative...
207 objs
= self
._root
.select(generic
.OBJECT_TAG
, *self
._path
)
209 ##!!! the id here is a stub !!!##
210 ## data = {'oid': self._root._getoid(o)}
211 ## data = {'oid': 'OID_%s' % str(id(o)).replace('-', 'X')}
212 data
= {'oid': getoid(o
)}
215 # skip the oid as we added it already...
218 # skip attrs that do not exist in the object...
219 if not hasattr(o
, a
):
221 # XXX this is a good spot for ACL check...
222 data
[a
] = getattr(o
, a
)
225 # object interface...
226 def _getobject(self
, oid
):
229 ##!!! STUB... do a better direct object by oid select !!!##
230 objs
= self
._root
.select(generic
.OBJECT_TAG
, *self
._path
)
232 ##!!! the id here is a stub !!!##
233 ## if ('OID_%s' % str(id(o)).replace('-', 'X')) == oid:
236 raise TypeError, 'non-existant object id: %s' % oid
238 # shorthand object interface...
239 @objutils.classinstancemethod
240 def view(self
, oid
, *attrs
):
243 return self
._getobject
(oid
).view(*attrs
)
244 def update(self
, oid
, **attrs
):
247 return self
._getobject
(oid
).update(**attrs
)
250 #----------------------------------------TagTreePathProxyMappingMixin---
251 # XXX make this a tad more efficient...
252 class TagTreePathProxyMappingMixin(mapping
.Mapping
):
255 def __getitem__(self
, name
):
258 return self
._getobject
(name
)
259 def __setitem__(self
, name
, val
):
262 raise NotImplementedError
263 def __delitem__(self
, name
):
266 raise NotImplementedError
270 for o
in self
.list():
274 #---------------------------------------------TagTreePathProxyMapping---
275 # XXX add exclude support...
276 class TagTreePathProxyMapping(TagTreePathProxyMappingMixin
, TagTreePathProxy
):
282 #--------------------------------------------------------TagTreeMixin---
283 ##!!!! not yet very picklable...
284 class TagTreeMixin(tag
.AbstractTagSet
):
287 # this is needed here as the attr proxy try and get it othewise...
288 # ...another way to go is add an exception to the .__getattr__
289 # method, explicitly raising attrerror when this is not here...
290 ## __stored_set_constructor__ = None
292 __node_path_proxy__
= TagTreePathProxyMapping
294 def __getattr__(self
, name
):
298 ##!!! automate this... (might be a good idea to put this into path.py)
305 ## '__getnewargs__'):
306 if name
.startswith('__') and name
.endswith('__'):
307 return super(TagTreeMixin
, self
).__getattr
__(name
)
308 return getattr(self
.__node
_path
_proxy
__(self
, ()), name
)
310 ## ##!!! for type-checking to work need to split the tag method into two:
311 ## ##!!! one for internal use and one "public"...
313 ## def tag(self, obj, *tags):
317 ## if type(t) not in (str, unicode):
318 ## raise TypeError, 'tag type must be str, got %s.' % type(t)
319 ## return super(TagTreeMixin, self).tag(obj, *tags)
321 def relatedtags(self
, *tags
):
324 # skip "special" constructor IDs...
325 return set( t
for t
in super(TagTreeMixin
, self
).relatedtags(*tags
)
326 if type(t
) != tuple )
329 #-------------------------------------------------------------TagTree---
330 class TagTree(TagTreeMixin
, tag
.TagSet
):
333 __stored_set_constructor__
= set
335 ## def __getstate__(self):
339 ## def __setstate__(self, state):
346 #-----------------------------------------------------------------------
347 # XXX this can be considered app-level code... most likely should be
349 #--------------------------------------------------BaseTagTreeHandler---
350 class BaseTagTreeHandler(object):
352 this provides administrative tree handling, without affecting the tree itself.
354 __tree_constructor__
= None
355 __node_constructor_wrapper__
= None
357 __constructor_tags__
= ()
358 __instance_tags__
= ()
362 # XXX might be good to be able to thread some args to the tree
364 def __init__(self
, constructor_tags
=(), instance_tags
=()):
367 self
.__constructor
_tags
__ += constructor_tags
368 self
.__instance
_tags
__ += instance_tags
370 self
.tree
= self
.__tree
_constructor
__()
371 def constructor(self
, name
, constructor
, constructor_tags
=(), instance_tags
=(), ignore_default_constructor_tags
=False, ignore_default_instance_tags
=False):
373 create a custom constructor.
375 # construct tag lists...
376 if ignore_default_constructor_tags
:
377 c_tags
= constructor_tags
379 c_tags
= self
.__constructor
_tags
__ + constructor_tags
380 if ignore_default_instance_tags
:
381 i_tags
= instance_tags
383 i_tags
= self
.__instance
_tags
__ + instance_tags
385 return self
.__node
_constructor
_wrapper
__(self
.tree
, name
, constructor
,
390 #------------------------------------------------------TagTreeHandler---
391 class TagTreeHandler(BaseTagTreeHandler
):
394 __tree_constructor__
= TagTree
395 ## __node_constructor_wrapper__ = DynamicNodeConstructor
396 ## __node_constructor_wrapper__ = DynamicNodeConstructorWithOIDReturn
397 __node_constructor_wrapper__
= DynamicNodeConstructorWithOIDReturnNCallback
399 # XXX this is currently not needed as the NodeConstructor class
400 # takes care of this internally.... (should be customisable!)
401 ## __constructor_tags__ = ('constructor',)
402 __instance_tags__
= ('instance',)
404 # XXX this is currently not needed as the NodeConstructor class
405 # takes care of this internally.... (should be customisable!)
406 ## def constructor(self, name, constructor, constructor_tags=(), instance_tags=()):
409 ## return super(TagTreeHandler, self).constructor(
412 ## (name, ('constructor', name)) + constructor_tags,
417 #-----------------------------------------------------------------------
418 if __name__
== '__main__':
420 handler
= TagTreeHandler()
423 def constructor(name
, constructor
, *tags
):
424 return handler
.constructor(name
, constructor
, tags
)
432 print 'creating constructors...'
433 print constructor('A', A
, 'some_tag')
434 print constructor('B', B
, 'some_other_tag')
437 print tree
.constructor
.list()
439 print tree
.some_tag
.constructor
.list()
441 print tree
.some_tag
.relatedtags()
445 print tree
.some_tag
.A()
446 print tree
.some_tag
.constructor
.A()
448 print tree
.some_other_tag
.constructor
.B()
450 print tree
.instance
.list('attr', '__class__')
452 print tree
.some_tag
.instance
.list()
454 print tree
.some_other_tag
.list()
456 print tree
.relatedtags()
457 print tree
.some_other_tag
.relatedtags()
459 print tree
.addtags('xxx', 'yyy')
460 print tree
.xxx
.keys()
462 AA
= constructor('X', A
, 'fff:ggg')
465 print tree
['fff:ggg']
473 ## ##!!! pickle does not seem to work with recursive references... (2.5.1-specific?)
476 ## class X(object): pass
483 ## print pickle.dumps(d)
485 import cPickle
as pickle
487 s
= pickle
.dumps(tree
)
489 ss
= pickle
.dumps(tree
.some_tag
)
492 sss
= pickle
.dumps(AA
)
496 #=======================================================================
497 # vim:set ts=4 sw=4 nowrap :