extend tracking for loops.organize; changed tracks query to return a generator instead of a list; minor extensions for namespace/config stuff

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@2508 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2008-04-09 10:01:27 +00:00
parent 671e15ddd5
commit 3ddcf63821
8 changed files with 193 additions and 27 deletions

View file

@ -14,7 +14,7 @@ working with the current run of a task.)
>>> tracks = TrackingStorage() >>> tracks = TrackingStorage()
>>> tracks.saveUserTrack('a001', 0, 'u1', {'somekey': 'somevalue'}) >>> tracks.saveUserTrack('a001', 0, 'u1', {'somekey': 'somevalue'})
'0000001' '0000001'
>>> t1 = tracks.getUserTracks('a001', 0, 'u1') >>> t1 = list(tracks.getUserTracks('a001', 0, 'u1'))
>>> len(t1) >>> len(t1)
1 1
>>> t1[0].data >>> t1[0].data
@ -30,18 +30,18 @@ We can query the tracking storage using the tracks' metadata. These
are mapped to btree indexes, so we get fast access to the resulting are mapped to btree indexes, so we get fast access to the resulting
track data. track data.
>>> tracks.query(taskId='a001') >>> list(tracks.query(taskId='a001'))
[<Track ['a001', 1, 'u1', '...-...-... ...:...']: {'somekey': 'somevalue'}>] [<Track ['a001', 1, 'u1', '...-...-... ...:...']: {'somekey': 'somevalue'}>]
>>> tracks.saveUserTrack('a002', 0, 'u1', {'somekey': 'anothervalue'}) >>> tracks.saveUserTrack('a002', 0, 'u1', {'somekey': 'anothervalue'})
'0000002' '0000002'
>>> result = tracks.query(userName='u1') >>> result = list(tracks.query(userName='u1'))
>>> len(result) >>> len(result)
2 2
By supplying a list we can also search for more than one value in one query. By supplying a list we can also search for more than one value in one query.
>>> result = tracks.query(taskId=('a001', 'a002')) >>> result = list(tracks.query(taskId=('a001', 'a002')))
>>> len(result) >>> len(result)
2 2
@ -70,7 +70,7 @@ for a given set of keys.
>>> tracks.saveUserTrack('a001', 0, 'u2', {'somekey': 'user2'}, update=True) >>> tracks.saveUserTrack('a001', 0, 'u2', {'somekey': 'user2'}, update=True)
'0000004' '0000004'
>>> t4 = tracks.getUserTracks('a001', 0, 'u2') >>> t4 = list(tracks.getUserTracks('a001', 0, 'u2'))
>>> [t.data for t in t4] >>> [t.data for t in t4]
[{'somekey': 'user2'}] [{'somekey': 'user2'}]

View file

@ -217,6 +217,7 @@ class TrackingStorage(BTreeContainer):
self.taskUsers[taskId].update([userName]) self.taskUsers[taskId].update([userName])
def reindexTracks(self): def reindexTracks(self):
# TODO: clear indexes
for trackId in self: for trackId in self:
trackNum = int(trackId) trackNum = int(trackId)
self.indexTrack(trackNum, self[trackId]) self.indexTrack(trackNum, self[trackId])
@ -254,7 +255,8 @@ class TrackingStorage(BTreeContainer):
start, end = value start, end = value
result = self.intersect(result, result = self.intersect(result,
self.indexes['timeStamp'].apply((start, end))) self.indexes['timeStamp'].apply((start, end)))
return result and [self[self.idFromNum(r)] for r in result] or set() #return result and [self[self.idFromNum(r)] for r in result] or set()
return result and (self[self.idFromNum(r)] for r in result) or set()
def intersect(self, r1, r2): def intersect(self, r1, r2):
return r1 is None and r2 or intersection(r1, r2) return r1 is None and r2 or intersection(r1, r2)

View file

@ -20,7 +20,7 @@ Storing and Retrieving Notifications
>>> manager.notify('obj01', 'user01', 'object_changed') >>> manager.notify('obj01', 'user01', 'object_changed')
>>> ntf01 = manager.query(userName='user01')[0] >>> ntf01 = list(manager.query(userName='user01'))[0]
>>> ntf01 >>> ntf01
<Notification ['obj01', 1, 'user01', '...']: <Notification ['obj01', 1, 'user01', '...']:
{'media': ['inbox'], 'state': 'new', 'type': 'object_changed'}> {'media': ['inbox'], 'state': 'new', 'type': 'object_changed'}>

View file

@ -40,10 +40,11 @@ class Configurator(dict):
self.filename = kw.get('filename') self.filename = kw.get('filename')
def __getitem__(self, key): def __getitem__(self, key):
item = getattr(self, key, _not_found) return getattr(self, key)
if item is _not_found:
item = ConfigSection(key) def __getattr__(self, key):
setattr(self, key, item) item = ConfigSection(key)
setattr(self, key, item)
return item return item
def load(self, p=None, filename=None): def load(self, p=None, filename=None):
@ -138,6 +139,6 @@ class ConfigSection(list):
for name, value in self.__dict__.items(): for name, value in self.__dict__.items():
if isinstance(value, ConfigSection): if isinstance(value, ConfigSection):
value.collect('%s.%s' % (ident, name), result) value.collect('%s.%s' % (ident, name), result)
elif name != '__name__' and isinstance(value, (str, int)): elif name != '__name__' and isinstance(value, (str, int, list, tuple)):
result.append('%s.%s = %s' % (ident, name, repr(value))) result.append('%s.%s = %r' % (ident, name, value))

View file

@ -110,11 +110,15 @@ class Jeep(object):
key = getattr(obj, '__name__', getattr(obj, 'name', _notfound)) key = getattr(obj, '__name__', getattr(obj, 'name', _notfound))
if key is _notfound: if key is _notfound:
raise AttributeError("No name attribute present") raise AttributeError("No name attribute present")
if key in self: if key in self.keys():
raise ValueError("Object already present") raise ValueError("Object already present")
self._sequence.insert(idx, key) self._sequence.insert(idx, key)
object.__setattr__(self, key, obj) object.__setattr__(self, key, obj)
def update(self, mapping):
for key, value in mapping.items():
self[key] = value
def pop(self, key=-1): def pop(self, key=-1):
value = self[key] value = self[key]
if type(key) in (int, long): if type(key) in (int, long):

View file

@ -89,10 +89,14 @@ More Dictionary Methods
>>> len(jeep) >>> len(jeep)
4 4
>>> del jeep['fourth'] >>> jeep.update(dict(first='another first value', fifth='5th value'))
>>> len(jeep) >>> len(jeep)
3 5
>>> del jeep['fourth']
>>> del jeep['fifth']
>>> len(jeep)
4
>>> del jeep['fifth']
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: ...not in list ValueError: ...not in list
@ -103,6 +107,8 @@ More Methods and Operators
>>> 'third' in jeep >>> 'third' in jeep
True True
>>> jeep.pop()
'new fourth value'
>>> jeep.pop() >>> jeep.pop()
'third value' 'third value'
>>> len(jeep) >>> len(jeep)

View file

@ -26,6 +26,8 @@ $Id$
import traceback import traceback
from cybertools.util.jeep import Jeep
_not_found = object() _not_found = object()
@ -60,29 +62,108 @@ class BaseNamespace(dict):
return result return result
class Symbol(object): class Element(object):
def __init__(self, namespace, name): posArgs = ('name',)
def __init__(self, namespace, name, collection=None, parent=None):
self.namespace = namespace self.namespace = namespace
self.name = name self.name = name
self.collection = collection
self.parent = parent
self.subElements = Jeep()
def __call__(self, *args, **kw):
elem = self.__class__(self.namespace, '', parent=self)
for idx, v in enumerate(args):
if idx < len(self.posArgs):
setattr(elem, self.posArgs[idx], v)
for k, v in kw.items():
setattr(elem, k, v)
if not elem.name:
elem.name = self.name
if self.collection is not None:
self.collection.append(elem)
return elem
def __getitem__(self, key):
if isinstance(key, (list, tuple)):
return tuple(self[k] for k in key)
elif isinstance(key, Element):
self.subElements.append(key)
return key
elif isinstance(key, (int, long, basestring)):
return self.subElements[key]
else:
print '*** Error', key
def __str__(self): def __str__(self):
return self.name return self.name
def __repr__(self): def __repr__(self):
return "<Symbol '%s'>" % self.name return "<Element '%s'>" % self.name
class AutoNamespace(BaseNamespace): class AutoNamespace(BaseNamespace):
symbolFactory = Symbol elementFactory = Element
def __getitem__(self, key): def __getitem__(self, key):
result = self.get(key, _not_found) result = self.get(key, _not_found)
if result is _not_found: if result is _not_found:
result = getattr(self, key, _not_found) result = getattr(self, key, _not_found)
if result is _not_found: if result is _not_found:
sym = Symbol(self, key) elem = Element(self, key)
self[key] = sym self[key] = elem
return sym return elem
return result return result
class Executor(object):
def __init__(self, namespace):
self.namespace = namespace
def execute(self, text):
error = ''
try:
exec text in self.namespace
except:
error = traceback.format_exc()
return error
class Evaluator(Executor):
def __init__(self, namespace, allowExec=False):
self.namespace = namespace
self.allowExec = allowExec
def evaluate(self, text):
""" Evaluate the text as a Python expression given and return the
result. If the text is not an expression try to execute it
as a statement (resulting in a None value).
The return value is a tuple with the evaluation result and
an error traceback if there was an error.
"""
if self.allowExec:
return self.evalutateOrExecute(text)
result = None
error = ''
try:
result = eval(text, self.namespace)
except:
error = traceback.format_exc()
return result, error
def evalutateOrExecute(self, text):
result = None
error = ''
try:
result = eval(text, self.namespace)
except SyntaxError:
error = self.execute(text)
except:
error = traceback.format_exc()
return result, error

View file

@ -50,9 +50,35 @@ namespace provides a secure restricted execution environment.
... ...
NameError: open NameError: open
Elements and sub-elements
-------------------------
A Namespace Automatically Generating Symbols >>> code = """
============================================ ... topic('zope3', title='Zope 3')[
... annotation(author='jim')]
... topic('python')[
... annotation(author='guido'),
... child('zope3')]
... """
>>> from cybertools.util.jeep import Jeep
>>> topics = Jeep()
>>> symbols = namespace.BaseNamespace()
>>> symbols['topic'] = namespace.Element(symbols, 'topic', topics)
>>> symbols['annotation'] = namespace.Element(symbols, 'annotation')
>>> symbols['child'] = namespace.Element(symbols, 'child')
>>> exec code in symbols
>>> dict(topics)
{'python': <Element 'python'>, 'zope3': <Element 'zope3'>}
>>> dict(topics['python'].subElements)
{'zope3': <Element 'zope3'>, 'annotation': <Element 'annotation'>}
A Namespace Automatically Generating Elements
=============================================
>>> auto = namespace.AutoNamespace() >>> auto = namespace.AutoNamespace()
@ -60,4 +86,50 @@ A Namespace Automatically Generating Symbols
something something
>>> auto.something >>> auto.something
<Symbol 'something'> <Element 'something'>
Execution of Python Code in a Namespace
=======================================
To simplify some standard use cases for working with namespaces the
module provides two classes for execution of Python code with appropriate
simple error handling.
Evaluation of Python expressions
--------------------------------
When evaluating an expression we always get a pair with the resulting
value and an - hopefully empty - error string.
>>> ev = namespace.Evaluator(auto)
>>> ev.evaluate('25 * 25')
(625, '')
>>> ev.evaluate('30/0')
(None, 'Traceback...ZeroDivisionError...')
Trying to execute a statement leads to a syntax error.
>>> ev.evaluate('print something_else')
(None, 'Traceback...SyntaxError...')
But we can explicitly allow the execution of statements. The result
of executing a statement is None.
>>> ev = namespace.Evaluator(auto, allowExec=True)
>>> ev.evaluate('print something_else')
something_else
(None, '')
>>> ev.evaluate('25 * 25')
(625, '')
Execution of Statements
-----------------------
>>> ex = namespace.Executor(auto)
>>> ex.execute('number = 25')
''
>>> ex.execute('30/0')
'Traceback...ZeroDivisionError...'