CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
configfile.py200 linesDownload Raw Back to pyqtgraph
1"""2configfile.py - Human-readable text configuration file library 3Copyright 2010  Luke Campagnola4Distributed under MIT/X11 license. See license.txt for more information.5 6Used for reading and writing dictionary objects to a python-like configuration7file format. Data structures may be nested and contain any data type as long8as it can be converted to/from a string using repr and eval.9"""10 11 12import contextlib13import datetime14import os15import re16import sys17from collections import OrderedDict18 19import numpy20 21from . import units22from .colormap import ColorMap23from .Point import Point24from .Qt import QtCore25 26GLOBAL_PATH = None  # so not thread safe.27 28 29class ParseError(Exception):30    def __init__(self, message, lineNum, line, fileName=None):31        self.lineNum = lineNum32        self.line = line33        self.message = message34        self.fileName = fileName35        Exception.__init__(self, message)36 37    def __str__(self):38        if self.fileName is None:39            msg = f"Error parsing string at line {self.lineNum:d}:\n"40        else:41            msg = f"Error parsing config file '{self.fileName}' at line {self.lineNum:d}:\n"42        msg += f"{self.line}\n{Exception.__str__(self)}"43        return msg44 45 46def writeConfigFile(data, fname):47    s = genString(data)48    with open(fname, 'wt') as fd:49        fd.write(s)50 51 52def readConfigFile(fname, **scope):53    global GLOBAL_PATH54    if GLOBAL_PATH is not None:55        fname2 = os.path.join(GLOBAL_PATH, fname)56        if os.path.exists(fname2):57            fname = fname258 59    GLOBAL_PATH = os.path.dirname(os.path.abspath(fname))60 61    local = {62        **scope,63        **units.allUnits,64        'OrderedDict': OrderedDict,65        'readConfigFile': readConfigFile,66        'Point': Point,67        'QtCore': QtCore,68        'ColorMap': ColorMap,69        'datetime': datetime,70        # Needed for reconstructing numpy arrays71        'array': numpy.array,72    }73    for dtype in ['int8', 'uint8',74                  'int16', 'uint16', 'float16',75                  'int32', 'uint32', 'float32',76                  'int64', 'uint64', 'float64']:77        local[dtype] = getattr(numpy, dtype)78 79    try:80        with open(fname, "rt") as fd:81            s = fd.read()82        s = s.replace("\r\n", "\n")83        s = s.replace("\r", "\n")84        data = parseString(s, **local)[1]85    except ParseError:86        sys.exc_info()[1].fileName = fname87        raise88    except:89        print(f"Error while reading config file {fname}:")90        raise91    return data92 93 94def appendConfigFile(data, fname):95    s = genString(data)96    with open(fname, 'at') as fd:97        fd.write(s)98 99 100def genString(data, indent=''):101    s = ''102    for k in data:103        sk = str(k)104        if not sk:105            print(data)106            raise ValueError('blank dict keys not allowed (see data above)')107        if sk[0] == ' ' or ':' in sk:108            print(data)109            raise ValueError(110                f'dict keys must not contain ":" or start with spaces [offending key is "{sk}"]'111            )112        if isinstance(data[k], dict):113            s += f"{indent}{sk}:\n"114            s += genString(data[k], f'{indent}    ')115        else:116            line = repr(data[k]).replace("\n", "\\\n")117            s += f"{indent}{sk}: {line}\n"118    return s119 120 121def parseString(lines, start=0, **scope):122    data = OrderedDict()123    if isinstance(lines, str):124        lines = lines.replace("\\\n", "")125        lines = lines.split('\n')126 127    indent = None128    ln = start - 1129    l = ''130 131    try:132        while True:133            ln += 1134            if ln >= len(lines):135                break136 137            l = lines[ln]138 139            ## Skip blank lines or lines starting with #140            if not _line_is_real(l):141                continue142 143            ## Measure line indentation, make sure it is correct for this level144            lineInd = measureIndent(l)145            if indent is None:146                indent = lineInd147            if lineInd < indent:148                ln -= 1149                break150            if lineInd > indent:151                raise ParseError(f'Indentation is incorrect. Expected {indent:d}, got {lineInd:d}', ln + 1, l)152 153            if ':' not in l:154                raise ParseError('Missing colon', ln + 1, l)155 156            k, _, v = l.partition(':')157            k = k.strip()158            v = v.strip()159 160            ## set up local variables to use for eval161            if len(k) < 1:162                raise ParseError('Missing name preceding colon', ln + 1, l)163            if k[0] == '(' and k[-1] == ')':  # If the key looks like a tuple, try evaluating it.164                with contextlib.suppress(Exception):  # If tuple conversion fails, keep the string165                    k1 = eval(k, scope)166                    if type(k1) is tuple:167                        k = k1168            if _line_is_real(v):  # eval the value169                try:170                    val = eval(v, scope)171                except Exception as ex:172                    raise ParseError(173                        f"Error evaluating expression '{v}': [{ex.__class__.__name__}: {ex}]", ln + 1, l174                    ) from ex175            else:176                next_real_ln = next((i for i in range(ln + 1, len(lines)) if _line_is_real(lines[i])), len(lines))177                if ln + 1 >= len(lines) or measureIndent(lines[next_real_ln]) <= indent:178                    val = {}179                else:180                    ln, val = parseString(lines, start=ln + 1, **scope)181            if k in data:182                raise ParseError(f'Duplicate key: {k}', ln + 1, l)183            data[k] = val184    except ParseError:185        raise186    except Exception as ex:187        raise ParseError(f"{ex.__class__.__name__}: {ex}", ln + 1, l) from ex188    return ln, data189 190 191def _line_is_real(line):192    return not re.match(r'\s*#', line) and re.search(r'\S', line)193 194 195def measureIndent(s):196    n = 0197    while n < len(s) and s[n] == ' ':198        n += 1199    return n200 
Aluode/PerceptionLabPortable · CoolFace