From 480bf8eb28eeecac5fee08e793131eb46863bff0 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Wed, 26 Aug 2026 15:42:31 +0200 Subject: [PATCH] ask 'did you mean?' for a missing frappy cfg --- frappy.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/frappy.py b/frappy.py index 831c04b..7ea668e 100644 --- a/frappy.py +++ b/frappy.py @@ -2,6 +2,7 @@ import re import os from socket import gethostname, gethostbyname from pathlib import Path +from difflib import SequenceMatcher from .base import MarcheControl, Logger, ArgError, write_content, Config from .normalizeuri import normalizeuri @@ -13,6 +14,26 @@ overrideNode(interface=interface) WRAPPER_PAT = re.compile(r"interface\s=\s*'(\d*)'\s*\n") +class NotFound(FileNotFoundError): + pass + +def best_guesses(target, names, max_results=5, case_penalty=0.05): + """find best matches, case insensitive""" + target_l = target.lower() + # invert score instead of reverse -> this will sort names with same score alphabetically + scored = sorted(((1-SequenceMatcher(None, target_l, name.lower()).ratio(), name) + for name in names)) + scorelimit = scored[0][0] + 0.1 + result = [] + for i, (score, name) in enumerate(scored): + if score > scorelimit: + break + if i > 1: # + scorelimit = score + 0.01 + result.append(name) + return result + + class FrappyConfig(Config): log = None process_file = None @@ -27,7 +48,7 @@ class FrappyConfig(Config): 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') + self.cfgdirs = superconfig.get('cfgdirs').replace('', instrument) if self.ins_config: frappy_ports = self.ins_config.get('frappy_ports', '0') ports = frappy_ports.split('-') @@ -84,14 +105,23 @@ class FrappyControl(MarcheControl): services = [service] if service else list(self.services) services.append('') cfgdirs = cfgdirs or self.config.cfgdirs + allcfg = [] for servicedir in services: for cfgdir in cfgdirs.split(':'): cfgfile = Path(cfgdir) / servicedir / cfgpy - tries.append(cfgfile) + tries.append(str(cfgfile)) if cfgfile.is_file(): return servicedir, cfgfile else: + names = set() + for servicedir in services: + for cfgdir in cfgdirs.split(':'): + names.update((f.stem[:-4] for f in (Path(cfgdir) / servicedir).glob('*_cfg.py'))) + guesses = best_guesses(cfg, names) + if guesses: + suggest = '/'.join(guesses) + raise NotFound(f'{cfg} not found, do you mean {suggest}') raise FileNotFoundError(f'can not find {cfgpy} in {tries}') def get_std_port(self, service):