core: action and handler types, gen_proc; revert proc and handler args order

This commit is contained in:
Helmut Merz 2026-10-19 08:12:00 +02:00
parent 4b3af802cd
commit 49cac25cb7
5 changed files with 54 additions and 17 deletions

View file

@ -6,21 +6,26 @@ from __future__ import annotations # no check of type annotations at runtime
import asyncio
from collections.abc import Awaitable, Callable, Coroutine
from dataclasses import dataclass, field
from typing import Any
from typing import Any, Self
from scopes.core import actor
from scopes.core.message import Message
from scopes import util
from scopes.util import keywords as k, Symbol
type Mailbox = asyncio.Queue
type Categ = tuple[Symbol, Symbol]
type Proc = Callable[[Scope, Message], Awaitable[Scope]]
type Proc = Callable[[Message, Scope], Awaitable[Scope | None]]
type Program = Callable[[Scope], Proc]
type Syn = Callable[[Message], Message]
async def proc_dummy(scope: Scope, msg: Message) -> Scope:
type Handler = Callable[[Message, Scope],
Awaitable[Message | None | tuple[Message | None, Scope | None]]]
type Handlers = Handler | None | list[Handlers]
type Actions = dict[Symbol, Handlers]
async def proc_dummy(msg: Message, scope: Scope) -> None:
await scope.environ.put(msg)
return scope
global_mailbox: Mailbox = asyncio.Queue()
@ -38,19 +43,17 @@ class Scope:
def __post_init__(self):
self.set_proc()
def set_proc(self) -> Scope:
def set_proc(self) -> Self:
self.proc = self.program(self)
return self
def create_scope(env: Mailbox, cat=(k.csys, k.c00), **kw) -> Scope:
def create_scope(env: Mailbox, cat: Categ = (k.csys, k.c00), **kw) -> Scope:
return Scope(environ=env, categ=cat, **kw)
def neuron(scope: Scope) -> Mailbox:
async def process(msg: Message) -> actor.Behavior | None:
nonlocal scope
new_scope = await scope.proc(scope, msg)
if new_scope is not None:
scope = new_scope
scope = await scope.proc(msg, scope) or scope
return process
return actor.create(process)
@ -59,3 +62,19 @@ def create_zero(env: Mailbox) -> Mailbox:
zero = neuron(scope)
return zero
def gen_proc(actions: Actions, default: Handlers = None,
before: Handlers = None, after: Handlers = None) -> Proc:
async def proc(msg: Message, scope: Scope) -> Scope:
acts = actions.get(msg.head['action'], actions.get(k.default, default))
acts = util.flatten(before, acts, after, ignore_none=True)
for act in acts:
msg = await act(msg, scope)
if isinstance(msg, tuple):
msg, new_scope = msg
if new_scope is not None:
scope = new_scope
if msg is None:
break
return scope
return proc

View file

@ -4,13 +4,13 @@
from scopes import core
from scopes.core import actor
from scopes.core.message import Message
from scopes.util import keywords as k
async def proc_env(scope, msg):
async def proc_env(msg: Message, scope: core.Scope) -> None:
await scope.environ.put(msg)
#return scope
def create(meta_env):
def create(meta_env: core.Mailbox) -> core.Mailbox:
scope = core.create_scope(meta_env, (k.env, k.c00), program=lambda x: proc_env)
return core.neuron(scope)

View file

@ -1,6 +1,7 @@
# scopes.util
import base64
from collections.abc import Iterable
import hashlib
from secrets import choice
import string
@ -24,6 +25,20 @@ class Symbols:
keywords = Symbols()
# list / iterable utilities
def flatten(*it, ignore_none=False):
accu = []
def doit(it):
if ignore_none and it is None:
return
if isinstance(it, Iterable):
for t in it:
doit(t)
else:
accu.append(it)
doit(it)
return accu
# random strings, hashes, encodings
# for authentication, encryption, and other purposes

View file

@ -67,14 +67,12 @@ async def meta_env(collector):
@pytest.mark.anyio
async def test_core(meta_env, collector):
env = environ.create(meta_env)
await env.put(Message(k.env, k.hello))
await asyncio.sleep(0.01)
assert len(collector) == 1
assert collector[0].domain is k.env
zero = core.create_zero(env)
await env.put(Message(k.env, k.hello))
await zero.put(Message(k.csys, k.hello))
await asyncio.sleep(0.01)
assert len(collector) == 2
assert collector[0].domain is k.env
assert collector[1].domain is k.csys
#print(collector)

View file

@ -4,7 +4,7 @@
import pytest
from scopes.util import keywords as k
from scopes.util import keywords as k, flatten
def test_keywords():
assert k.dummy is k.dummy
@ -13,3 +13,8 @@ def test_keywords():
assert k('dummy') is k.dummy
assert k('new') is k('new')
def test_flatten():
lst = (1, (None, 2), (3, (4, 5)))
assert flatten(lst) == [1, None, 2, 3, 4, 5]
assert flatten(42, lst, (6, 7), ignore_none=True) == [42, 1, 2, 3, 4, 5, 6, 7]