diff --git a/prometheus_client/parser.py b/prometheus_client/parser.py index dc8e30df..af59ebd6 100644 --- a/prometheus_client/parser.py +++ b/prometheus_client/parser.py @@ -37,12 +37,18 @@ def _replace_escaping(s: str) -> str: return ESCAPING_RE.sub(replace_escape_sequence, s) -def _is_character_escaped(s: str, charpos: int) -> bool: - num_bslashes = 0 - while (charpos > num_bslashes - and s[charpos - 1 - num_bslashes] == '\\'): - num_bslashes += 1 - return num_bslashes % 2 == 1 +# for python < 3.9 compatibility +def _removeprefix(data: str, prefix: str) -> str: + if data.startswith(prefix): + return data[len(prefix):] + return data + + +# for python < 3.9 compatibility +def _removesuffix(data: str, suffix: str) -> str: + if data.endswith(suffix): + return data[:-len(suffix)] + return data def _parse_labels(labels_string: str) -> Dict[str, str]: @@ -51,43 +57,25 @@ def _parse_labels(labels_string: str) -> Dict[str, str]: if "=" not in labels_string: return labels - escaping = False - if "\\" in labels_string: - escaping = True + # remove SINGLE leading and trailing commas + labels_string = labels_string.strip() + labels_string = _removeprefix(labels_string, ',') + labels_string = _removesuffix(labels_string, ',') - # Copy original labels - sub_labels = labels_string try: # Process one label at a time - while sub_labels: - # The label name is before the equal - value_start = sub_labels.index("=") - label_name = sub_labels[:value_start] - sub_labels = sub_labels[value_start + 1:].lstrip() - # Find the first quote after the equal - quote_start = sub_labels.index('"') + 1 - value_substr = sub_labels[quote_start:] - - # Find the last unescaped quote - i = 0 - while i < len(value_substr): - i = value_substr.index('"', i) - if not _is_character_escaped(value_substr, i): - break - i += 1 - - # The label value is between the first and last quote - quote_end = i + 1 - label_value = sub_labels[quote_start:quote_end] - # Replace escaping if needed - if escaping: - label_value = _replace_escaping(label_value) - labels[label_name.strip()] = label_value - - # Remove the processed label from the sub-slice for next iteration - sub_labels = sub_labels[quote_end + 1:] - next_comma = sub_labels.find(",") + 1 - sub_labels = sub_labels[next_comma:].lstrip() + for label in labels_string.split(","): + label_name, label_value = label.split("=") + + normalized_value = label_value.strip() + # remove SINGLE leading and trailing double quotes + normalized_value = _removeprefix(normalized_value, '"') + normalized_value = _removesuffix(normalized_value, '"') + + if "\\" in normalized_value: + normalized_value = _replace_escaping(normalized_value) + + labels[label_name.strip()] = normalized_value return labels