usawa

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | Submodules | LICENSE

commit c5336f91fee5ea735ab3ec87b99bf0bfee942a4a
parent 53a3e887a2a29f7c49c9baf3baf335c42f2952c6
Author: lash <dev@holbrook.no>
Date:   Sat,  7 Mar 2026 21:35:15 -0600

Add accounts validator

Diffstat:
Adummy/tests/account.py | 44++++++++++++++++++++++++++++++++++++++++++++
Adummy/usawa/account.py | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdummy/usawa/error.py | 4++++
3 files changed, 117 insertions(+), 0 deletions(-)

diff --git a/dummy/tests/account.py b/dummy/tests/account.py @@ -0,0 +1,44 @@ +import logging +import datetime +import unittest +import os + +from usawa import UnitIndex +from usawa.account import AccountIndex +from usawa.error import AccountError + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +testdir = os.path.realpath(os.path.dirname(__file__)) + +class TestAccount(unittest.TestCase): + + def setUp(self): + self.uidx = UnitIndex('FOO') + self.uidx.add('BAR') + + def test_account_lock(self): + idx = AccountIndex(self.uidx) + idx.add('FOO', 'bar.baz') + idx.add('FOO', 'bar.baz') + with self.assertRaises(AccountError): + idx.add('FOO', 'bar.baz-') + with self.assertRaises(AccountError): + idx.add('BAZ', 'foo.bar') + self.assertFalse(idx.check('BAR', 'foo.baz')) + self.assertTrue(idx.check('FOO', 'bar.baz')) + + + def test_account_list(self): + idx = AccountIndex(self.uidx) + idx.add('FOO', 'bar.bar') + idx.add('FOO', 'bar.baz') + idx.add('BAR', 'foo.baz') + v = list(idx) + logg.debug('results {}'.format(v)) + self.assertEqual(len(v), 3) + + +if __name__ == '__main__': + unittest.main() diff --git a/dummy/usawa/account.py b/dummy/usawa/account.py @@ -0,0 +1,69 @@ +import logging + +from .error import AccountError + +logg = logging.getLogger('account') + + +def default_check(path): + parts = path.split('.') + for v in parts: + if not v.isalnum(): + raise AccountError('invalid part: ' + v) + return True + + +class AccountIndex: + + def __init__(self, unitindex, pathvalidator=default_check): + self.uidx = unitindex + self.accounts = {} + self.locked = False + self.validate = pathvalidator + self.iterval = None + + + def add(self, sym, path): + try: + sym = self.uidx.sym(sym) + except KeyError: + raise AccountError('unknown unit ' + sym) + if self.locked: + raise AccountError('account index locked') + self.validate(path) + if self.accounts.get(sym) == None: + self.accounts[sym] = [] + elif path in self.accounts[sym]: + logg.debug('Ignoring duplicate account: {}:{}'.format(sym, path)) + self.accounts[sym].append(path) + + + def lock(self): + self.locked = True + + + def check(self, sym, path): + try: + return path in self.accounts[sym] + except KeyError: + return False + + + def __iter__(self): + keys = list(self.uidx.syms()) + keys.sort() + for k in keys: + for v in self.accounts[k]: + if self.iterval == None: + self.iterval = [] + self.iterval.append(k + '/' + v) + return self + + + def __next__(self): + if len(self.iterval) == 0: + self.iverval = None + raise StopIteration() + v = self.iterval.pop(0) + return v + diff --git a/dummy/usawa/error.py b/dummy/usawa/error.py @@ -12,3 +12,7 @@ class ValidateError(Exception): class SocketError(Exception): pass + + +class AccountError(Exception): + pass