Chronaxis用户手册English
本页目录

chronaxis.configuration.json_parser

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""标准库负责JSON语法和数值;轻量第二遍只定位字段,不再构造第二份配置树。"""2from bisect import bisect_right3import json4from math import isfinite5import re67from .models import ConfigurationError, ConfigurationLocation89MAX_DEPTH=6410_SPACE=re.compile(r'[ \t\r\n]*')111213class _DuplicateKey(ValueError):14    """只用于让标准库停止覆盖重复键;随后位置扫描给出具体字段。"""151617def _unique_pairs(pairs):18    """建立唯一键对象。参数: 标准库键值对。返回: dict。异常: 重复键。"""19    result={}20    for key,value in pairs:21        if key in result:22            raise _DuplicateKey(key)23        result[key]=value24    return result252627class _Locations:28    """只记录JSON路径和偏移,不保留第二份值树;语法真值仍由json.loads决定。"""2930    def __init__(self, text, source):31        """建立本次扫描状态。参数: 文本/显示来源。返回: owner。异常: 分配失败。"""32        self.text,self.source=text,source33        self.positions={}34        self.lines=[match.start() for match in re.finditer('\n',text)]35        self.decoder=json.JSONDecoder()3637    def location(self, offset):38        """把字符偏移转换为1基行列。参数: 偏移。返回: 位置。异常: 无。"""39        line=bisect_right(self.lines,offset-1)40        previous=self.lines[line-1] if line else -141        return ConfigurationLocation(line+1,offset-previous)4243    def scan(self, offset=0, path=(), depth=0):44        """遍历合法JSON或已知含重复键的JSON。返回: 结束偏移。异常: 重复键/过深/语法错误。"""45        offset=_SPACE.match(self.text,offset).end()46        location=self.location(offset)47        if depth>MAX_DEPTH:48            raise ConfigurationError(self.source,path,location,'nesting_limit','配置嵌套不能超过64层')49        self.positions[path]=location50        first=self.text[offset]51        if first=='{':52            offset=_SPACE.match(self.text,offset+1).end()53            if self.text[offset]=='}':return offset+154            seen=set()55            while True:56                key_at=offset57                key,offset=json.decoder.scanstring(self.text,offset+1,True)58                if key in seen:59                    raise ConfigurationError(self.source,(*path,key),self.location(key_at),60                        'duplicate_key',f'重复键:{key}')61                seen.add(key)62                offset=_SPACE.match(self.text,offset).end()+1  # 已通过JSON语法检查的冒号。63                offset=self.scan(offset,(*path,key),depth+1)64                offset=_SPACE.match(self.text,offset).end()65                if self.text[offset]=='}':return offset+166                offset=_SPACE.match(self.text,offset+1).end()67        if first=='[':68            offset=_SPACE.match(self.text,offset+1).end()69            if self.text[offset]==']':return offset+170            index=071            while True:72                offset=self.scan(offset,(*path,index),depth+1)73                offset=_SPACE.match(self.text,offset).end()74                if self.text[offset]==']':return offset+175                offset=_SPACE.match(self.text,offset+1).end()76                index+=177        # raw_decode这里只接原子值,绝不重新解码整个容器。78        _,end=self.decoder.raw_decode(self.text,offset)79        return end808182def _finite_values(value, source, locations, path=()):83    """拒绝JSON宽松接受的NaN/Infinity及指数溢出。返回: None。异常: 定位到实际值。"""84    if type(value) is float and not isfinite(value):85        raise ConfigurationError(source,path,locations[path],'nonfinite_number','数值必须有限,不能使用NaN或Infinity')86    if type(value) is dict:87        for key,item in value.items():_finite_values(item,source,locations,(*path,key))88    elif type(value) is list:89        for index,item in enumerate(value):_finite_values(item,source,locations,(*path,index))909192def parse_json(text, source):93    """按标准JSON加载并附准确位置。参数: 文本/来源。返回: 数据和位置表。异常: ConfigurationError。"""94    try:95        try:96            data=json.loads(text,object_pairs_hook=_unique_pairs)97        except _DuplicateKey:98            # 标准库已找到实际重复;不从字符串搜索猜测它属于哪个嵌套对象。99            _Locations(text,source).scan()100            raise AssertionError('JSON重复键位置扫描没有发现重复')101        except json.JSONDecodeError:102            raise103        except (ValueError,OverflowError) as error:104            # Python的十进制整数安全长度限制等数值错误不能泄露为无文件定位的底层异常。105            raise ConfigurationError(source,(),None,'invalid_number','数字超出当前Python可解析的范围') from error106        scanner=_Locations(text,source)107        scanner.scan()108        _finite_values(data,source,scanner.positions)109        return data,scanner.positions110    except json.JSONDecodeError as error:111        raise ConfigurationError(source,(),ConfigurationLocation(error.lineno,error.colno),112            'invalid_json',error.msg) from error113    except RecursionError as error:114        raise ConfigurationError(source,(),None,'nesting_limit','配置嵌套不能超过64层') from error115
Chronaxis 用户手册 · 离线阅读