def _interpolate_some(self, parser, option, accum, rest, section, map, depth): rawval = parser.get(section, option, raw=True, fallback=rest) if depth > MAX_INTERPOLATION_DEPTH: raise InterpolationDepthError(option, section, rawval) while rest: p = rest.find("$") if p < 0: accum.append(rest) return if p > 0: accum.append(rest[:p]) rest = rest[p:] # p is no longer used c = rest[1:2] if c == "$": accum.append("$") rest = rest[2:] elif c == "{": m = self._KEYCRE.match(rest) if m is None: raise InterpolationSyntaxError( option, section, "bad interpolation variable reference %r" % rest) path = m.group(1).split(':') rest = rest[m.end():] sect = section opt = option try: if len(path) == 1: opt = parser.optionxform(path[0]) v = self._resolve_option(opt, map) elif len(path) == 2: sect = path[0] opt = path[1] v = self._resolve_section_option(sect, opt, parser) else: raise InterpolationSyntaxError( option, section, "More than one ':' found: %r" % (rest, )) except (KeyError, NoSectionError, NoOptionError): raise InterpolationMissingOptionError( option, section, rawval, ":".join(path)) from None if "$" in v: self._interpolate_some(parser, opt, accum, v, sect, dict(parser.items(sect, raw=True)), depth + 1) else: accum.append(v) else: raise InterpolationSyntaxError( option, section, "'$' must be followed by '$' or '{', " "found: %r" % (rest, ))
def _interpolate_ext(self, dst, parser, section, option, rawval, defaults, loop_detect): if not rawval: return if len(loop_detect) > MAX_INTERPOLATION_DEPTH: raise InterpolationDepthError(option, section, rawval) xloop = (section, option) if xloop in loop_detect: raise InterpolationError( section, option, 'Loop detected: %r in %r' % (xloop, loop_detect)) loop_detect.add(xloop) parts = self._var_rc.split(rawval) for i, frag in enumerate(parts): fullkey = None use_vars = defaults if i % 2 == 0: dst.append(frag) continue if frag in ('$$', '%%'): dst.append(frag[0]) continue if frag.startswith('${') and frag.endswith('}'): fullkey = frag[2:-1] # use section access only for new-style keys if ':' in fullkey: ksect, key = fullkey.split(':', 1) use_vars = None else: ksect, key = section, fullkey elif frag.startswith('%(') and frag.endswith(')s'): fullkey = frag[2:-2] ksect, key = section, fullkey else: raise InterpolationError(section, option, 'Internal parse error: %r' % frag) key = parser.optionxform(key) newpart = parser.get(ksect, key, raw=True, vars=use_vars) if newpart is None: raise InterpolationError(ksect, key, 'Key referenced is None') self._interpolate_ext(dst, parser, ksect, key, newpart, defaults, loop_detect) loop_detect.remove(xloop)
def _interpolate_ext_new(self, dst, parser, section, option, rawval, defaults, loop_detect): if not rawval: return rawval if len(loop_detect) > MAX_INTERPOLATION_DEPTH: raise InterpolationDepthError(option, section, rawval) xloop = (section, option) if xloop in loop_detect: raise InterpolationError( option, section, 'Loop detected: %r in %r' % (xloop, loop_detect)) loop_detect.add(xloop) def lookup_helper(lk_section, lk_option): if '!' in lk_option: funcname, val = lk_option.split('!', 1) func = None if self._func_map: func = self._func_map.get(funcname.strip()) if not func: raise InterpolationError( option, section, 'Unknown interpolation function: %r' % funcname) return func(val.strip(), lk_section, lk_option) # normal fetch if ':' in lk_option: ksect, key = lk_option.split(':', 1) ksect, key = ksect.strip(), key.strip() use_vars = None else: ksect, key = lk_section, lk_option.strip() use_vars = defaults key = parser.optionxform(key) newpart = parser.get(ksect, key, raw=True, vars=use_vars) if newpart is None: raise InterpolationError(key, ksect, 'Key referenced is None') dst = [] self._interpolate_ext_new(dst, parser, ksect, key, newpart, defaults, loop_detect) return ''.join(dst) val = new_interpolate(section, option, rawval, lookup_helper) dst.append(val) loop_detect.remove(xloop)
def _interpolate(self, section, option, rawval, vars): # do the string interpolation value = rawval depth = MAX_INTERPOLATION_DEPTH while depth: # Loop through this until it's done depth -= 1 if "%(" in value: try: value = value % vars except KeyError as e: raise InterpolationMissingOptionError( option, section, rawval, e.args[0]) else: break if value.find("%(") != -1: raise InterpolationDepthError(option, section, rawval) return value
def _interpolate_some(self, option, accum, rest, section, map, depth): if depth > MAX_INTERPOLATION_DEPTH: raise InterpolationDepthError(option, section, rest) while rest: p = rest.find("%") if p < 0: accum.append(rest) return if p > 0: accum.append(rest[:p]) rest = rest[p:] # p is no longer used c = rest[1:2] if c == "%": accum.append("%") rest = rest[2:] elif c == "(": m = self._interpvar_match(rest) if m is None: raise InterpolationSyntaxError( option, section, "bad interpolation variable reference %r" % rest) var = m.group(1) rest = rest[m.end():] try: v = map[var] except KeyError: raise InterpolationMissingOptionError( option, section, rest, var) if "%" in v: self._interpolate_some(option, accum, v, section, map, depth + 1) else: accum.append(v) else: raise InterpolationSyntaxError( option, section, "'%' must be followed by '%' or '(', found: " + repr(rest))
def interpolate(self, parser, option, accum, rest, section, map, depth): # Mostly copy-pasted from the built-in configparser implementation. # We need to overwrite this method so we can add special handling for # block references :( All values produced here should be strings – # we need to wait until the whole config is interpreted anyways so # filling in incomplete values here is pointless. All we need is the # section reference so we can fetch it later. rawval = parser.get(section, option, raw=True, fallback=rest) if depth > MAX_INTERPOLATION_DEPTH: raise InterpolationDepthError(option, section, rawval) while rest: p = rest.find("$") if p < 0: accum.append(rest) return if p > 0: accum.append(rest[:p]) rest = rest[p:] # p is no longer used c = rest[1:2] if c == "$": accum.append("$") rest = rest[2:] elif c == "{": # We want to treat both ${a:b} and ${a.b} the same m = self._KEYCRE.match(rest) if m is None: err = f"bad interpolation variable reference {rest}" raise InterpolationSyntaxError(option, section, err) path = m.group(1).replace(":", ".").rsplit(".", 1) rest = rest[m.end():] sect = section opt = option try: if len(path) == 1: opt = parser.optionxform(path[0]) if opt in map: v = map[opt] else: # We have block reference, store it as a special key section_name = parser[parser.optionxform( path[0])]._name v = self._get_section_name(section_name) elif len(path) == 2: sect = path[0] opt = parser.optionxform(path[1]) fallback = "__FALLBACK__" v = parser.get(sect, opt, raw=True, fallback=fallback) # If a variable doesn't exist, try again and treat the # reference as a section if v == fallback: v = self._get_section_name( parser[f"{sect}.{opt}"]._name) else: err = f"More than one ':' found: {rest}" raise InterpolationSyntaxError(option, section, err) except (KeyError, NoSectionError, NoOptionError): raise InterpolationMissingOptionError( option, section, rawval, ":".join(path)) from None if "$" in v: new_map = dict(parser.items(sect, raw=True)) self.interpolate(parser, opt, accum, v, sect, new_map, depth + 1) else: accum.append(v) else: err = "'$' must be followed by '$' or '{', " "found: %r" % ( rest, ) raise InterpolationSyntaxError(option, section, err)
def interpolate(self, parser, section, option, value, _, depth=0): if depth > MAX_INTERPOLATION_DEPTH: raise InterpolationDepthError(option, section, value) # short cut operations if empty or a normal string if (value == ""): # print("interpol: SHORT -> empty string") return "" elif (("$" not in value) and ("%" not in value)): # print("interpol: SHORT -> {0}".format(value)) return value # print("interpol: PREPARE section={0} option={1} value='{2}'".format(section, option, value)) rawValue = value rest = "" while (len(rawValue) > 0): beginPos = rawValue.find("%") if (beginPos < 0): rest += rawValue rawValue = "" else: rest += rawValue[:beginPos] if (rawValue[beginPos + 1] == "%"): rest += "%" rawValue = rawValue[1:] elif (rawValue[beginPos + 1] == "{"): endPos = rawValue.find("}", beginPos) if (endPos < 0): raise InterpolationSyntaxError( option, section, "bad interpolation variable reference {0!r}". format(rawValue)) path = rawValue[beginPos + 2:endPos] rawValue = rawValue[endPos + 1:] rest += self.GetSpecial(section, option, path) # print("interpol: BEGIN section={0} option={1} value='{2}'".format(section, option, rest)) result = "" while (len(rest) > 0): # print("interpol: LOOP rest='{0}'".format(rest)) beginPos = rest.find("$") if (beginPos < 0): result += rest rest = "" else: result += rest[:beginPos] if (rest[beginPos + 1] == "$"): result += "$" rest = rest[1:] elif (rest[beginPos + 1] == "{"): endPos = rest.find("}", beginPos) nextPos = rest.rfind("$", beginPos, endPos) if (endPos < 0): raise InterpolationSyntaxError( option, section, "bad interpolation variable reference {0!r}". format(rest)) if ((nextPos > 0) and (nextPos < endPos)): # an embedded $-sign path = rest[nextPos + 2:endPos] # print("interpol: path='{0}'".format(path)) innervalue = self.GetValue(parser, section, option, path) # innervalue = self.interpolate(parser, section, option, path, map, depth + 1) # print("interpol: innervalue='{0}'".format(innervalue)) rest = rest[beginPos:nextPos] + innervalue + rest[ endPos + 1:] # print("interpol: new rest='{0}'".format(rest)) else: path = rest[beginPos + 2:endPos] rest = rest[endPos + 1:] result += self.GetValue(parser, section, option, path) # print("interpol: LOOP END - result='{0}'".format(result)) # print("interpol: RESULT => '{0}'".format(result)) return result