usawa

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

commit 357ae58af2a0510fea4564eec90ff808b5809c4a
parent 7f577b7fcd33cd43c41f2ab92a557e943664161e
Author: lash <dev@holbrook.no>
Date:   Sat, 10 Jan 2026 21:16:13 +0100

WIP consolidate args parsing and context building

Diffstat:
Mdummy/usawa/ledger.py | 2++
Mdummy/usawa/runnable/add.py | 13+++++--------
Mdummy/usawa/runnable/create.py | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mdummy/usawa/unit.py | 3+--
4 files changed, 112 insertions(+), 50 deletions(-)

diff --git a/dummy/usawa/ledger.py b/dummy/usawa/ledger.py @@ -222,6 +222,8 @@ class Ledger: self.cur = self.base self.entries[self.uidx.base] = [] tree = lxml.etree.XML('<ledger xmlns="http://usawa.defalsify.org/" version="{}"></ledger>'.format(XML_FORMAT_VERSION)) + if self.tree == None: + self.tree = tree o = lxml.etree.SubElement(tree, NSPREFIX + 'topic', nsmap=nsmap()) if topic == None: if self.topic == None: diff --git a/dummy/usawa/runnable/add.py b/dummy/usawa/runnable/add.py @@ -48,7 +48,7 @@ class Context: def from_args(args): ctx = Context() ctx.unit = args.unit - ctx.uidx = UnitIndex(ctx.unit) + ctx.uidx = UnitIndex(ctx.unit, precision=args.unit_precision) if args.description != None: ctx.description = args.description if args.r != None: @@ -76,8 +76,6 @@ class Context: for v in self.dst: if v == None: raise ValueError('invalid dst') - #if self.topic == None: - # raise ValueError('invalid topic') if self.ref == None: raise ValueError('invalid ref') @@ -120,9 +118,10 @@ argp.add_argument('-o', type=str, dest='output', help='output file for updated X argp.add_argument('--src-type', dest='src_type', type=str, choices=CATEGORIES, default='expense', help='source type') argp.add_argument('--dst-type', dest='dst_type', type=str, choices=CATEGORIES, default='asset', help='dest type') argp.add_argument('-d', '--description', dest='description', type=str, help='interactive edit') -argp.add_argument('-u', '--unit', type=str, default='BTC', help='Unit to use for transaction') -argp.add_argument('--unit-precision', type=int, default=2, help='Unit precision') -argp.add_argument('--unit-rate', type=float, default=1.0, help='Unit exchange rate') +# TODO: read default from xml if not defined +argp.add_argument('-u', '--unit', type=str, default=UnixIndex.default_unit, help='Unit to use for transaction') +argp.add_argument('--unit-precision', dest='unit_precision', type=int, default=UnixIndex.default_precision, help='Unit precision') +argp.add_argument('--unit-rate', dest='unit_precision', type=float, default=1.0, help='Unit exchange rate') argp.add_argument('ledger_xml_file', type=str, help='load ledger metadata from XML file') arg = argp.parse_args() ctx = Context.from_args(arg) @@ -139,8 +138,6 @@ store = LedgerStore(db, ledger) pk = store.get_key() wallet = DemoWallet(privatekey=pk) dt = datetime.datetime.now() -close_fn = None -f = None def do_interactive(ctx): diff --git a/dummy/usawa/runnable/create.py b/dummy/usawa/runnable/create.py @@ -1,6 +1,9 @@ import os +import sys import logging import urllib.parse +import argparse +import datetime from usawa import Ledger, DemoWallet, UnitIndex from usawa.store import LedgerStore @@ -10,19 +13,65 @@ logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() +class Context: + + def __init__(self): + self.unit = None + self.unit_precision = None + self.uidx = None + self.topic = None + self.uri = None + self.output = None + self.f = None + + + def close(self): + if self.f and self.f != sys.stdout: + self.f.close() + + + def open(self, output): + if output == '<stdout>': + self.f = sys.stdout.buffer + logg.debug('output is stdout') + else: + self.f = open(output, 'wb') + return self + + + @staticmethod + def from_args(args): + ctx = Context() + ctx.unit = args.unit + ctx.unit_precision = args.unit_precision + ctx.uidx = UnitIndex(ctx.unit, precision=ctx.unit_precision) + ctx.topic = args.topic + ctx.uri = args.src_uri + if args.output != None: + ctx.output = os.path.realpath(args.output) + else: + ctx.output = '<stdout>' + return ctx + + + def validate(self): + self.topic = parse_topic(self.topic) + self.uri = parse_uri(self.uri) + self.unit = parse_unit(self.unit) + self.unit_precision = parse_unit_precision(self.unit_precision) + return self + + def parse_topic(v): topic = None if len(v) > 2: if v[:2] == '0x': v = v[2:] - try: - bytes.fromhex(v) - topic = v - except ValueError: - if not isinstance(v, str): - raise ValueError('invalid topic') - topic = v.encode('utf-8').hex() - + topic = bytes.fromhex(v) + elif isinstance(v, str): + topic = v.encode('utf-8').hex().encode('utf-8') + else: + raise ValueError('invalid topic') return topic @@ -32,47 +81,64 @@ def parse_unit(v): return v.upper() - -print("Creating new ledger") -v = input("Topic: ") -topic = None -if len(v) > 0: - r = parse_topic(v) - topic = bytes.fromhex(r) -logg.debug('topic {} -> {}'.format(v, topic)) +def parse_uri(v): + o = urllib.parse.urlparse(v) + return urllib.parse.urlunparse(o) -v = input("Default unit: (default: BTC): ") -if len(v) == 0: - v = 'BTC' -unit = parse_unit(v) -v = input("Unit decimals (default: 2): ") -if len(v) == 0: - v = 2 -dec = int(v) +def parse_unit_precision(v): + return int(v) -uidx = UnitIndex(unit, precision=dec) -v = input("Source URI: ") -src = None -if len(v) > 0: - o = urllib.parse.urlparse(v) - src = urllib.parse.urlunparse(o) +def input_or_default(prompt, default=None, postfix=': ', validate_fn=None): + if default != None: + postfix = ' [{}]'.format(default) + postfix + v = input(prompt + postfix) + if len(v) == 0: + if default == None: + raise ValueError('empty value and no default') + v = default + if validate_fn != None: + validate_fn(v) + return v + -v = input("XML output filename (default: start.xml):") -fp = None -if len(v) == 0: - fp = os.path.join('.', 'start.xml') -fp = os.path.realpath(fp) +argp = argparse.ArgumentParser() +argp.add_argument('-i', action='store_true', help='interactive edit') +argp.add_argument('-t', dest='topic', type=str, help='ledger topic') +argp.add_argument('-u', '--unit', type=str, default=UnitIndex.default_unit, help='Unit to use for transaction') +argp.add_argument('-o', type=str, dest='output', help='output file for updated XML document') +argp.add_argument('-l', type=str, dest='src_uri', help='URI for data source') +argp.add_argument('--unit-precision', type=int, default=UnitIndex.default_precision, help='Unit precision') +argp.add_argument('--unit-rate', type=float, default=1.0, help='Unit exchange rate') +arg = argp.parse_args() +ctx = Context.from_args(arg) -ledger = Ledger(uidx, topic=topic, src=src) +def do_interactive(ctx): + logg.debug("Creating new ledger") + v = input_or_default("Topic", ctx.topic) + ctx.unit = input_or_default("Default unit", ctx.unit) + ctx.unit_precision = input_or_default("Unit decimals", ctx.unit_precision) + ctx.uri = input_or_default("Source URI", ctx.uri) + input_or_default("XML output filename", ctx.output) + return ctx + + +if arg.i: + ctx = do_interactive(ctx) +ctx = ctx.open(ctx.output) +ctx.validate() + +ledger = Ledger(ctx.uidx, topic=ctx.topic, src=ctx.uri) db = ValkeyStore('') store = LedgerStore(db, ledger) pk = None wallet = None +dt = datetime.datetime.now() + try: pk = store.get_key() except FileNotFoundError: @@ -83,7 +149,5 @@ if wallet == None: wallet = DemoWallet(privatekey=pk) logg.info('loaded existing key. {}'.format(wallet.pubkey().hex())) -logg.info('writing XML to file: {}'.format(fp)) -f = open(fp, 'wb') -f.write(ledger.to_string()) -f.close() +ctx.f.write(ledger.to_string()) +ctx.close() diff --git a/dummy/usawa/unit.py b/dummy/usawa/unit.py @@ -5,12 +5,11 @@ from .xml import nsmap logg = logging.getLogger('usawa.unit') -BASE_UNIT = 'BTC' - class UnitIndex: default_precision = 2 + default_unit = 'BTC' """UnitIndex holds metadata for units of account.