diff --git a/scopes/api/core/message.py b/scopes/api/core/message.py new file mode 100644 index 0000000..bb75762 --- /dev/null +++ b/scopes/api/core/message.py @@ -0,0 +1,5 @@ +# scopes.api.core.message + +"""routes and views for Message objects""" + + diff --git a/scopes/core/actor.py b/scopes/core/actor.py index ad6cdd6..32c02dd 100644 --- a/scopes/core/actor.py +++ b/scopes/core/actor.py @@ -2,14 +2,12 @@ import asyncio -from scopes import util - -quit = util.Symbol("quit") +from scopes.util import keywords as k async def loop(q, bhv): while True: msg = await q.get() - if msg is quit: + if msg is k.quit: break bhv = await bhv(msg) or bhv diff --git a/scopes/core/message.py b/scopes/core/message.py new file mode 100644 index 0000000..9e79e53 --- /dev/null +++ b/scopes/core/message.py @@ -0,0 +1,15 @@ +# scopes.core.message + +"""Generic message definition""" + +from scopes import shape + +class Message(shape.Record): + + headFields = ['domain', 'action', 'class', 'item'] + + customer = None + + def __repr__(self): + return f'<{self.__class__.__name__}: {self.headValues}, {self.data}>' + diff --git a/scopes/shape.py b/scopes/shape.py index bc7ac9e..2dc790e 100644 --- a/scopes/shape.py +++ b/scopes/shape.py @@ -19,3 +19,31 @@ class Record: setattr(self, k, self.head[k]) self.data = data or {} + @property + def headValues(self): + return [self.head[k] for k in self.headFields] + + def set(self, attr, value): + if attr in self.headFields: + if value is None: + value = '' + self.head[attr] = value + setattr(self, attr, value) + else: + raise AttributeError(attr) + + def update(self, data, overwrite=False): + if data is None: + return + if overwrite: + self.data = data + else: + self.data.update(data) + + def __repr__(self): + return '<%s: %s>' % (self.__class__.__name__, self.asDict()) + + def asDict(self): + return dict(head=self.head, data=self.data) + + diff --git a/scopes/storage/message.py b/scopes/storage/message.py index 8990318..7182e65 100644 --- a/scopes/storage/message.py +++ b/scopes/storage/message.py @@ -2,13 +2,14 @@ """Generic messages (or events) to be stored in SQL database.""" +from scopes.core.message import Message as BaseMessage from scopes.storage.common import registerContainerClass -from scopes.storage.tracking import Container, Track +#from scopes.storage.tracking import Container, Track +from scopes.storage.tracking import Container, Record -class Message(Track): +class Message(Record, BaseMessage): - headFields = ['domain', 'action', 'class', 'item'] prefix = 'msg' diff --git a/scopes/storage/tracking.py b/scopes/storage/tracking.py index 9d039c5..f5c43be 100644 --- a/scopes/storage/tracking.py +++ b/scopes/storage/tracking.py @@ -12,47 +12,22 @@ from sqlalchemy import Table, Column, Index from sqlalchemy import DateTime, Text, func from sqlalchemy import and_ +from scopes import shape from scopes.storage.common import registerContainerClass -class Track: +class Record(shape.Record): - headFields = ['taskId', 'userName'] - prefix = 'rec' - def __init__(self, *headValues, data=None, timeStamp=None, trackId=None, container=None, **kw): - self.head = {} - for k, v in kw.items(): - if k in self.headFields: - self.head[k] = kw.pop(k) - for ix, hv in enumerate(headValues): - self.head[self.headFields[ix]] = hv - for k in self.headFields: - if self.head.get(k) is None: - self.head[k] = '' - setattr(self, k, self.head[k]) - self.data = data or {} + super().__init__(*headValues, data=data, **kw) self.timeStamp = timeStamp self.trackId = trackId self.container = container - def set(self, attr, value): - if attr in self.headFields: - if value is None: - value = '' - self.head[attr] = value - setattr(self, attr, value) - else: - raise AttributeError(attr) - - def update(self, data, overwrite=False): - if data is None: - return - if overwrite: - self.data = data - else: - self.data.update(data) + def asDict(self): + return dict(uid=self.uid, head=self.head, data=self.data, + timeStamp=str(self.timeStamp)[:19]) @property def uid(self): @@ -66,13 +41,6 @@ class Track: return '' return str(self.trackId) - def __repr__(self): - return '<%s: %s>' % (self.__class__.__name__, self.asDict()) - - def asDict(self): - return dict(uid=self.uid, head=self.head, data=self.data, - timeStamp=str(self.timeStamp)[:19]) - # shortcuts for compatibility with existing software: def updateIndex(self, **kw): @@ -83,6 +51,11 @@ class Track: def storeTrack(self): self.container.update(self) +class Track(Record): + + headFields = ['taskId', 'userName'] + prefix = 'rec' + @registerContainerClass class Container: diff --git a/scopes/util.py b/scopes/util.py index 52f353d..e28a69a 100644 --- a/scopes/util.py +++ b/scopes/util.py @@ -10,7 +10,7 @@ import string class Symbol(str): def __repr__(self): - return f'' + return f'<{self}>' class Symbols: diff --git a/tests/test_actor.py b/tests/test_actor.py index 0942612..c544bf7 100644 --- a/tests/test_actor.py +++ b/tests/test_actor.py @@ -5,7 +5,7 @@ import asyncio import pytest -from scopes.core import actor +from scopes.core import actor, message from scopes.util import keywords as k collector = [] @@ -29,17 +29,17 @@ async def test_actor(): async with asyncio.TaskGroup() as group: q1 = actor.create(handle_basic, group) q2 = actor.create(handle_basic, group) - await q1.put("actor 1") + await q1.put(message.Message(k.test, k.hello)) await q1.put(k.show) await q1.put(k.switch) await q1.put(k.show) await q1.put("actor 1: Goodbye") await q2.put("actor 2") await q2.put(k.show) - await q1.put(actor.quit) - await q2.put(actor.quit) + await q1.put(k.quit) + await q2.put(k.quit) #await asyncio.wait([t1, t2]) - #print(collector) + print(collector) assert len(collector) == 6 assert collector[-1] == 'actor 1: Goodbye' diff --git a/tests/test_api.py b/tests/test_api.py index 1d0493b..b65e28c 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -44,7 +44,7 @@ def async_client(): #async with async_client() as clt: #response = await clt.get('/') -def test_root(storage): +def test_root(): with TestClient(app) as clt: response = clt.get('/') assert response.status_code == 200