*** empty log message ***
[pli.git] / pli / logictypes.py
blob9872d64775c276577f6fa325dbfcc1c25c745919
1 #=======================================================================
3 __version__ = '''0.1.19'''
4 __sub_version__ = '''20050902235933'''
5 __copyright__ = '''(c) Alex A. Naanou 2003'''
7 __doc__ = '''\
8 this module defines a number of utilities and objects to assist advanced
9 usage of standard python types.
10 '''
12 #-----------------------------------------------------------------------
14 import pli.pattern.mixin.mapping as mapping
17 #-----------------------------------------------------------------------
18 # TODO create a logic proxy, with adapters....
19 # UNION(*p), INTERSECTION(*n), ...
21 #------------------------------------------------------------_Compare---
22 class _Compare(object):
23 '''
24 '''
25 def __init__(self, eq):
26 self._eq = eq
27 def __cmp__(self, other):
28 return self._eq
29 def __eq__(self, other):
30 return self._eq == 0
31 def __ne__(self, other):
32 return self._eq != 0
33 def __gt__(self, other):
34 return self._eq > 0
35 def __ge__(self, other):
36 return self._eq >= 0
37 def __lt__(self, other):
38 return self._eq < 0
39 def __le__(self, other):
40 return self._eq <= 0
42 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
43 # this will compare to any value as equel (almost oposite to None)
44 Any = ANY = _Compare(0)
46 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
47 # this is bigger than any value...
48 MAXIMUM = _Compare(1)
50 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
51 # this is smaller than any value...
52 MINIMUM = _Compare(-1)
56 #-----------------------------------------------------------------------
57 # this is to define basic pattern combination mathematics.
58 # Ex:
59 # Pab = Pa | Pb -- Pab is a pattern that will match either Pa
60 # or Pb...
62 # Operations:
63 # |
64 # &
65 # +
66 # rep
67 # not
70 class Pattern(object):
71 '''
72 '''
73 # cmp
74 def __eq__(self, other):
75 '''
76 '''
77 pass
78 def __ne__(self, other):
79 '''
80 '''
81 pass
82 def __gt__(self, other):
83 '''
84 '''
85 pass
86 def __ge__(self, other):
87 '''
88 '''
89 pass
90 def __lt__(self, other):
91 '''
92 '''
93 pass
94 def __le__(self, other):
95 '''
96 '''
97 pass
98 # op
99 ##!!!
102 #--------------------------------------------------------------oftype---
103 class oftype(Pattern):
105 this will create an object that can be used as a predicate to test type,
106 and it will copare True to objects of that type.
108 def __init__(self, *types, **n):
111 self._types = types
112 if 'doc' in n:
113 self.__doc__ = n['doc']
114 def __call__(self, other):
116 test if the the other object object is of type.
118 return isinstance(other, self._types) is True
119 __eq__ = __call__
120 def __ne__(self, other):
121 return not isinstance(other, self._types) is True
122 def __gt__(self, other):
123 return False
124 def __ge__(self, other):
125 return True
126 def __lt__(self, other):
127 return False
128 def __le__(self, other):
129 return True
131 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
132 # simple type predicates...
133 isint = oftype(int)
134 isfloat = oftype(float)
135 iscomplex = oftype(complex)
137 isstr = oftype(str)
138 isunicode = oftype(unicode)
140 islist = oftype(list)
141 istuple = oftype(tuple)
142 isdict = oftype(dict)
144 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
145 # general type groups predicates...
146 isnumber = oftype(int, float, complex)
147 isstring = oftype(str, unicode)
148 issequence = oftype(list, tuple)
150 isiterabletype = oftype(str, unicode, list, tuple, dict)
154 ###-----------------------------------------------------------------------
155 ##class isLike(Pattern):
156 ## '''
157 ## '''
158 ## def __init__(self, *types, **n):
159 ## '''
160 ## '''
161 ## self._types = types
162 ## if 'doc' in n:
163 ## self.__doc__ = n['doc']
164 ## def __call__(self, other):
165 ## '''
166 ## test if the the other object object is of type.
167 ## '''
168 ## return isinstance(other, self._types) is True
169 ## __eq__ = __call__
170 ## def __ne__(self, other):
171 ## return not isinstance(other, self._types) is True
172 ## def __gt__(self, other):
173 ## return False
174 ## def __ge__(self, other):
175 ## return True
176 ## def __lt__(self, other):
177 ## return False
178 ## def __le__(self, other):
179 ## return True
182 #-----------------------------------------------------------------------
183 #-------------------------------------------------------dictcopyunite---
184 def dictcopyunite(*members):
187 res = {}
188 for m in members:
189 res.update(m)
190 return res
193 #---------------------------------------------------AbstractDictChainMixin---
194 # TODO split this into several mix-ins....
195 # NOTE: this was not designed to be used directly...
196 class AbstractDictChainMixin(mapping.Mapping):
199 # mapping interface...
200 def __getitem__(self, name):
203 for m in self.__iterchainmembers__():
204 if name in m:
205 return m[name]
206 raise KeyError, 'key "%s" is not present in any of the members.' % name
207 def __setitem__(self, name, value):
210 # find source...
211 # set
212 raise TypeError, 'can\'t add values to an AbstractDictChainMixin object.'
213 def __delitem__(self, name):
216 raise TypeError, 'can\'t delete values from an AbstractDictChainMixin object.'
217 def __contains__(self, name):
220 for m in self.__iterchainmembers__():
221 if name in m:
222 return True
223 return False
224 def __iter__(self):
227 seen = []
228 for m in self.__iterchainmembers__():
229 for n in m:
230 if n not in seen:
231 seen += [n]
232 yield n
233 # AbstractDictChainMixin specific extension interface...
234 ## def __iterchainmembers__(self):
235 ## '''
236 ## this will yield dicts, elements of the chain.
237 ## '''
238 ## raise NotImplementedError
239 # AbstractDictChainMixin specific methods...
240 def todict(self):
242 this will return a dict copy of the DictUnion object.
244 return dict(self.items())
247 #-----------------------------------------------------------DictUnion---
248 # XXX might be good to combine this and DictChain... (this will be
249 # quite easy using the itermembers instead of self._members)
250 class DictUnion(AbstractDictChainMixin):
252 this is a dict like object, that acts as a union of its members but
253 without modifieng its members in any way.
255 this is similar to a sequential update of all members, but retains
256 the container information for each item, and does not have the
257 overhead of creating a new resulting dict.
259 NOTE: because of the nature of dicts, the later added members (unite)
260 have higher priority than the former.
261 NOTE: the members added in one call (be it __init__, unite or tailunite)
262 have descending priority -- first highest last lowest.
263 NOTE: due to ints nature this object is *live*, e.g. it emidiatly
264 reflects all modifications to its members as they are modified.
266 _members = ()
268 def __init__(self, *members):
271 members = list(members)
272 members.reverse()
273 self._members = tuple(members)
274 # AbstractDictChainMixin specific extension interface...
275 def __iterchainmembers__(self):
278 for m in self._members:
279 yield m
280 # the DictUnion specific interface...
281 # XXX should these depend on __iterchainmembers__ or directly on
282 # _members????
283 def unite(self, *others):
285 add members to the union object.
287 NOTE: this method will add members to the top of the pririty
288 stack.
290 others = list(others)
291 others.reverse()
292 self._members = tuple(others) + self._members
293 def tailunite(self, *others):
295 this is the same as unite but adds low priority members (to the
296 botom of the priority stack).
298 others = list(others)
299 others.reverse()
300 self._members = self._members + tuple(others)
301 def memberindex(self, obj):
304 return list(self._members).index(obj)
305 def removemember(self, obj):
308 if obj in self._members:
309 self._members = tuple(list(self._members).remove(obj))
310 return
311 raise TypeError, '%s does not contain %s as a member.' % (self, obj)
312 def clearmembers(self):
315 del self._members[:]
316 def members(self):
319 return self._members
320 def popmember(self, index=0):
323 NOTE: this by default will remove the highest priority member.
325 m = list(self._members)
326 res = m.pop(index)
327 self._members = tuple(m)
328 return res
329 itermembers = __iterchainmembers__
330 def getcontainerof(self, name):
333 for m in self._members:
334 if name in m:
335 return m
336 raise KeyError, '%s does not contain "%s"' % (self, name)
337 def getallcontainersof(self, name):
340 res = []
341 for m in self._members:
342 if name in m:
343 res += [m]
344 if res == []:
345 raise KeyError, '%s does not contain "%s"' % (self, name)
346 return res
349 #---------------------------------------------------WritableDictUnion---
350 # modes:
351 # get the last occurance...
352 GET_LOCAL_LAST = 0
353 # get the first occurance...
354 GET_LOCAL_FIRST = 1
356 # if key is not contained in union, add it to the last member...
357 WRITE_NEW_TO_LAST = 0
358 # if key is not contained in union, add it to the first member...
359 WRITE_NEW_TO_FIRST = 2
360 # if the key is present in the union more than once, override the last
361 # occurance...
362 WRITE_TO_LAST_OWNER = 0
363 # if the key is present in the union more than once, override the first
364 # occurance...
365 WRITE_TO_FIRST_OWNER = 4
366 # write to local (temporary) dict...
367 # NOTE: if this is set, other write opts will be ignored...
368 WRITE_LOCAL = 8
369 # disable write...
370 WRITE_DISABLED = 16
372 # delete the last occurance of key...
373 DELETE_LAST = 0
374 # delete the first occurance of key...
375 DELETE_FIRST = 32
376 # enable ocal key delete...
377 DELETE_LOCAL = 64
378 # disable deletion of anything but local keys...
379 DELETE_LOCAL_ONLY = 128
380 # disable delete...
381 DELETE_DISABLED = 256
383 class WritableDictUnion(DictUnion):
386 __modify_props__ = GET_LOCAL_FIRST \
387 | WRITE_NEW_TO_FIRST \
388 | WRITE_TO_FIRST_OWNER \
389 | DELETE_FIRST \
390 | DELETE_LOCAL
392 def __init__(self, *members):
395 super(WritableDictUnion, self).__init__(*members)
396 self._locals = {}
397 def __getitem__(self, name):
400 props = getattr(self, '__modify_props__', 0)
401 if props & GET_LOCAL_FIRST:
402 return self._locals.get(name, super(WritableDictUnion, self).__getitem__(name))
403 try:
404 return super(WritableDictUnion, self).__getitem__(name)
405 except KeyError:
406 if name in self._locals:
407 return self._locals[name]
408 raise KeyError, name
409 def __setitem__(self, name, value):
412 props = getattr(self, '__modify_props__', 0)
413 if props & WRITE_DISABLED:
414 raise TypeError, 'can\'t add values to a dict union object (writing is disabled).'
415 elif props & WRITE_LOCAL:
416 self._locals[name] = value
417 else:
418 try:
419 if props & WRITE_TO_FIRST_OWNER:
420 self.getallcontainersof(name)[0][name] = value
421 else:
422 self.getallcontainersof(name)[-1][name] = value
423 except KeyError:
424 if props & WRITE_NEW_TO_FIRST:
425 self.members()[0][name] = value
426 else:
427 self.members()[-1][name] = value
428 def __contains__(self, name):
431 if name in self._locals or super(WritableDictUnion, self).__contains__(name):
432 return True
433 return False
434 def __delitem__(self, name):
437 props = getattr(self, '__modify_props__', 0)
438 if props & DELETE_DISABLED:
439 raise TypeError, 'can\'t delete items (deletion is disabled).'
440 if props & DELETE_LOCAL and name in self._locals:
441 del self._locals[name]
442 elif not props & DELETE_LOCAL_ONLY:
443 if props & DELETE_FIRST:
444 del self.getallcontainersof(name)[0][name]
445 else:
446 del self.getallcontainersof(name)[-1][name]
447 else:
448 raise TypeError, 'can\'t delete from contained dicts.'
449 def __iter__(self):
452 seen = []
453 props = getattr(self, '__modify_props__', 0)
454 if props & GET_LOCAL_FIRST:
455 for k in self._locals:
456 yield k
457 seen += [k]
458 for k in super(WritableDictUnion, self).__iter__():
459 if k not in seen:
460 yield k
461 seen += [k]
462 if not props & GET_LOCAL_FIRST:
463 for k in self._locals:
464 if k not in seen:
465 yield k
469 #-------------------------------------------------------DictTypeUnion---
470 # WARNING: this is not done!
471 class DictTypeUnion(DictUnion, dict):
473 this is a diriviation from dict that can contain oun data.
475 ##!!!
476 pass
479 #------------------------------------------------------BasicDictChain---
480 class BasicDictChain(AbstractDictChainMixin, mapping.DictLike):
483 NOTE: this class was designed as a basic base class (atleast the
484 __iterchainmembers__ method should be overloaded).
485 NOTE: do not forget to call the original __iterchainmembers__.
486 NOTE: this, if used as is will not differ from a dict.
488 def __init__(self, *p, **n):
491 self._dct_data = {}
492 super(BasicDictChain, self).__init__(*p, **n)
493 def __setitem__(self, name, value):
496 self._dct_data[name] = value
497 def __delitem__(self, name):
500 del self._dct_data[name]
501 def __iterchainmembers__(self):
504 yield self._dct_data
508 #-----------------------------------------------------------------------
509 #---------------------------------------------------------ObjectUnion---
510 # TODO rename....
511 # TODO split to various mix-ins...
512 ##class BasicObjectUnion(object):
513 class ObjectUnion(object):
515 this represents the union of the attr of several objects.
517 def __init__(self, *members):
520 members = list(members)
521 members.reverse()
522 self._members = tuple(members)
523 ##!!!
524 def __getattr__(self, name):
527 for o in self._members:
528 if hasattr(o, name):
529 return getattr(o, name)
530 ## # Q: which is faster, this or the "if hasattr..."???
531 ## try:
532 ## return getattr(o, name)
533 ## except AttributeError:
534 ## pass
535 raise AttributeError, name
536 ## def __setattr__(self, name, value):
537 ## '''
538 ## '''
539 ## raise TypeError, 'ca\'t write attributes of a ObjectUnion object.'
540 ## def __delattr__(self, name, value):
541 ## '''
542 ## '''
543 ## pass
544 def __hasattr__(self, name):
547 for o in self._members:
548 if hasattr(o, name):
549 return True
550 return False
551 # interface methods...
552 ##!!!
555 #=======================================================================
556 if __name__ == '__main__':
557 d0 = {'a':1, 'b':2}
558 d1 = {'c':'!!!!!', 'b':'x'}
560 D = WritableDictUnion(d0, d1)
562 D['b'] = '*****'
564 print D.todict()
565 print d0, d1
567 print 'a' in D
572 #=======================================================================
573 # vim:set ts=4 sw=4 nowrap :