From 15eb4a388f1bee48bbf40194dabeeea8d5ad8495 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Tue, 26 May 2026 09:13:20 +0200 Subject: [PATCH] variant with config as mixin --- __init__.py | 34 +++++++++++----------- base.py | 31 ++++++++++++++------ frappy.py | 82 ++++++++++++++++++++++++++++++++++------------------- 3 files changed, 92 insertions(+), 55 deletions(-) diff --git a/__init__.py b/__init__.py index 1373654..35295d4 100644 --- a/__init__.py +++ b/__init__.py @@ -17,20 +17,20 @@ # Markus Zolliker # ***************************************************************************** -from configparser import ConfigParser - - -def read_config(filename): - parser = ConfigParser() - parser.optxform = str - parser.read([str(filename)]) - return {k: dict(parser[k].items()) for k in parser.sections()} - - -def write_config(filename, newvalues): - parser = ConfigParser() - parser.optxform = str - parser.read([str(filename)]) - parser.read_dict(newvalues) - with open(filename, 'w') as f: - parser.write(f) +# from configparser import ConfigParser +# +# +# def read_config(filename): +# parser = ConfigParser() +# parser.optxform = str +# parser.read([str(filename)]) +# return {k: dict(parser[k].items()) for k in parser.sections()} +# +# +# def write_config(filename, newvalues): +# parser = ConfigParser() +# parser.optxform = str +# parser.read([str(filename)]) +# parser.read_dict(newvalues) +# with open(filename, 'w') as f: +# parser.write(f) diff --git a/base.py b/base.py index ad85336..acab412 100644 --- a/base.py +++ b/base.py @@ -31,6 +31,7 @@ frappy_ports = 15000-15009 MARCHESRC = ['/home/software/marche', '/home/l_samenv/marche'] CFGDIRS = ['/home/linse/config', '/home/l_samenv/linse_config'] + def read_config(filename): parser = ConfigParser() parser.optxform = str @@ -94,12 +95,15 @@ class ArgError(ValueError): class Config: + """merge info from linsetools.cfg and instrument.cfg""" this = None + single_instrument = None - def __init__(self): + def __init__(self, instrument=None): configfile = Path('/sq_sw/linse/etc/linsetools.cfg') self.sections = read_config(configfile) if Path('/home/l_samenv').is_dir(): + # TODO: make it more elegant (this is a hack) for section_name, section in self.sections.items(): for key, value in section.items(): if value.startswith('/home/linse'): @@ -118,10 +122,23 @@ class Config: write_config(instconfig, self.instruments) except PermissionError: pass + if len(self.instruments) == 1: + self.single_instrument = list(self.instruments)[0] this = self.instruments.pop('this', None) if this: self.this = this['instrument'] self.instruments[self.this] = this + if not self.this: + self.this = self.single_instrument + + if instrument: + self.instance = instrument or 'this' + self.instrument = self.this if self.instance == 'this' else self.instance + self.ins_config = self.instruments[self.instrument] + self.init() + + def init(self): + pass def parse_args(self, argmap, args, other=None): result = {} @@ -198,17 +215,14 @@ def wait_status(cl, service): return True -class MarcheControl: +class MarcheControl(Config): port = 8124 otherarg = None - argmap = {k: 'action' for k in ('start', 'restart', 'stop', 'gui', 'run', 'cli', 'list', 'listcfg')} + argmap = {k: 'action' for k in ('start', 'restart', 'stop', 'gui', 'run', 'cli', 'list', 'listcfg', 'running')} - def __init__(self, instrument=None, host='localhost', port=None, user=None, config=None): - self.config = config or Config() + def __init__(self, instrument=None, host='localhost', port=None, user=None): + super().__init__(instrument) self.host = host - self.instance = instrument or 'this' - self.instrument = self.config.this if self.instance == 'this' else self.instance - self.ins_config = self.config.instruments[self.instrument] self.user = user or self.instrument # SINQ instruments if not (Path('/home') / self.user).is_dir(): self.user = 'l_samenv' @@ -291,6 +305,5 @@ class MarcheControl: else: instruments = [instrument] for ins in instruments: - print('---', ins) control = cls(ins, config=config) control.do(**argdict) diff --git a/frappy.py b/frappy.py index ee2ab0b..c2351a6 100644 --- a/frappy.py +++ b/frappy.py @@ -1,7 +1,7 @@ import re import os from pathlib import Path -from .base import MarcheControl, Logger, ArgError, write_content +from .base import MarcheControl, Logger, ArgError, write_content, Config WRAPPER_CFG = """interface = '{port}' @@ -11,12 +11,30 @@ overrideNode(interface=interface) WRAPPER_PAT = re.compile(r"interface\s=\s*'(\d*)'\s*\n") -class FrappyConfig: +class FrappyConfig(Config): log = None process_file = None + def init(self, host='localhost'): + superconfig = self.sections['superfrappy'] + self.wrapperdir = superconfig.get('wrapperdir') + if not Path(self.wrapperdir).is_dir(): + raise ValueError(f'{self.wrapperdir} does not exist') + self.cfgdirs = superconfig.get('cfgdirs') + frappy_ports = self.ins_config.get('frappy_ports', '0') + ports = frappy_ports.split('-') + self.frappy_ports = list(range(int(ports[0]), int(ports[-1]))) + self.frappy_servers = [f'{host}:{p}' for p in self.frappy_ports] + + def service_from_uri(self, uri): + try: + idx = self.frappy_servers.index(uri) + return {0: 'main', 1: 'stick'}.get(idx, 'addons') + except ValueError: + return '' + @classmethod - def get(cls, cfgfile): + def read_config(cls, cfgfile): if not cls.process_file: import logging from frappy.config import process_file @@ -32,30 +50,26 @@ class FrappyControl(MarcheControl): argmap = dict(MarcheControl.argmap, all='service', **services) otherarg = 'cfg' - def __init__(self, instance, host='localhost', port=None, user=None, config=None): - super().__init__(instance, host, port, user, config) - superconfig = self.config.sections['superfrappy'] - self.instance = instance - - self.wrapperdir = superconfig.get('wrapperdir') - self.cfgdirs = superconfig.get('cfgdirs') - frappy_ports = self.ins_config.get('frappy_ports', '0') - ports = frappy_ports.split('-') - self.frappy_ports = list(range(int(ports[0]), int(ports[-1]))) - self.frappy_servers = [f'{host}:{p}' for p in self.frappy_ports] - - if not Path(self.wrapperdir).is_dir(): - raise ValueError(f'{self.wrapperdir} does not exist') + def __init__(self, instance, host='localhost', port=None, user=None): + super().__init__(instance, host, port, user) + FrappyConfig.init(self, host) self.get_cfg_info() # do we need to update this from time to time? + service_from_uri = FrappyConfig.service_from_uri + def get_service(self, instance): return f'frappy.{instance}' if self.instance == 'this' else f'frappy.{self.instrument}-{instance}' def wrapper_file(self, cfg): - prefix = "" if self.instrument == 'this' else f'{self.instrument}-' + prefix = "" if self.single_instrument else f'{self.instrument}-' return Path(self.wrapperdir) / f'{prefix}{cfg}_cfg.py' def cfg_file(self, cfgdirs, service, cfg): + """find config file + + if service is empty, accept configuration files for all services + return (the found or given service, Path(cfgfile)) + """ if '/' in cfg: return Path(cfg) cfgpy = f'{cfg}_cfg.py' @@ -86,12 +100,13 @@ class FrappyControl(MarcheControl): self.get_cfg_info() run_state = run_state or self.status('frappy') result = {} + prefix = '' if self.single_instrument else f'{self.instrument}-' for host_port, cfg in self.cfg_info.items(): host, port = host_port.split(':') if host == 'localhost': - on, busy, _ = run_state.get(cfg, (0,0,0)) + on, busy, _ = run_state.get(prefix+cfg, (0,0,0)) if on or busy: - result.setdefault(int(port), []).append((on, busy, cfg)) + result.setdefault(int(port), []).append((on, busy, prefix+cfg)) return {sorted(v)[-1][-1]: k for k, v in result.items()} def get_port(self, service): @@ -110,18 +125,19 @@ class FrappyControl(MarcheControl): def add_frappy_service(self, service, cfg, port, log=None): if log is None: log = Logger('info') - log.info('add %r port=%r', cfg, port) - servicedir, cfgfile = self.cfg_file(self.cfgdirs, service, cfg) + service, cfgfile = self.cfg_file(self.cfgdirs, service, cfg) if not port: if not service: raise ArgError('service is not given and can not be determined from cfg file location') port = self.get_port(service) + log.info('add %r port=%r', cfg, port) wrapper_content = WRAPPER_CFG.format(cfg=str(cfgfile), port=port) cfgname = cfgfile.stem.removesuffix('_cfg') - write_content(self.wrapper_file(cfgname), wrapper_content, group='+rw') + wrapper_file = self.wrapper_file(cfgname) + write_content(wrapper_file, wrapper_content, group='+rw') self.get_cfg_info() - log.info('wrapper %r %r', self.wrapper_file(cfgname), wrapper_content) + log.info('wrapper %s %r', wrapper_file, wrapper_content) self.reload() log.info('registered %r', cfgname) return f'localhost:{port}' @@ -130,7 +146,10 @@ class FrappyControl(MarcheControl): """get info from wrapper dir""" result = {} for cfgfile in Path(self.wrapperdir).glob('*_cfg.py'): - cfg = cfgfile.stem[:-4] + ins, _, cfg = cfgfile.stem[:-4].rpartition('-') + ins = ins or self.single_instrument + if ins != self.instrument: + continue match = WRAPPER_PAT.match(cfgfile.read_text()) if match: result[f'localhost:{match.group(1)}'] = cfg @@ -148,10 +167,11 @@ class FrappyControl(MarcheControl): run_state = self.status('frappy') ports = self.get_local_ports(run_state) result = {} - for cfg, port in ports.items(): - on, busy, _ = run_state.pop(cfg) + for ins_cfg, port in ports.items(): + cfg = ins_cfg.split('-')[-1] + on, busy, _ = run_state.pop(ins_cfg) if on: - port = ports.get(cfg) + # port = ports.get(cfg) kind = (self.frappy_ports + [port]).index(port) result[cfg] = kind, f'localhost:{port}' return result @@ -188,7 +208,7 @@ class FrappyControl(MarcheControl): @staticmethod def get_cfg_details(cfgfile): - mods = FrappyConfig.get(cfgfile) + mods = FrappyConfig.read_config(cfgfile) node = mods.pop('node') or {} sea_cfg = None for mod, config in mods.items(): @@ -288,6 +308,10 @@ class FrappyControl(MarcheControl): self.list(service or 'frappy') elif action == 'listcfg': self.listcfg(service) + elif action == 'running': + kinds = ['main', 'stick', 'addons'] + for cfg, (kind, uri) in self.running().items(): + print(cfg, kinds[kind], uri) elif action == 'gui': self.gui() elif action in ('cli', None):