From cc86745a4d6bdb78ac1b4ee080df36142233a2e0 Mon Sep 17 00:00:00 2001 From: sylvain Date: Mon, 13 May 2024 19:47:07 -0400 Subject: [PATCH 1/2] chore: refactor label parser Signed-off-by: Sylvain Witmeyer --- prometheus_client/parser.py | 56 ++++++++++--------------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/prometheus_client/parser.py b/prometheus_client/parser.py index dc8e30df..a1bcb8df 100644 --- a/prometheus_client/parser.py +++ b/prometheus_client/parser.py @@ -37,57 +37,29 @@ 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 - - def _parse_labels(labels_string: str) -> Dict[str, str]: labels: Dict[str, str] = {} # Return if we don't have valid labels 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().removeprefix(',').removesuffix(',') - # Copy original labels - sub_labels = labels_string + sub_labels = labels_string.split(",") 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 sub_labels: + label_name, label_value = label.split("=") + + normalized_value = label_value.strip() + # remove SINGLE leading and trailing double quotes + normalized_value = normalized_value.removeprefix('"').removesuffix('"') + + if "\\" in normalized_value: + normalized_value = _replace_escaping(normalized_value) + + labels[label_name.strip()] = normalized_value return labels From 139d601aa8af97e3171ee3ac990a32a9505d4f3d Mon Sep 17 00:00:00 2001 From: Sylvain Witmeyer Date: Mon, 13 May 2024 23:04:07 -0400 Subject: [PATCH 2/2] chore: compatibility with python 3.8 Signed-off-by: Sylvain Witmeyer --- prometheus_client/parser.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/prometheus_client/parser.py b/prometheus_client/parser.py index a1bcb8df..af59ebd6 100644 --- a/prometheus_client/parser.py +++ b/prometheus_client/parser.py @@ -37,6 +37,20 @@ def _replace_escaping(s: str) -> str: return ESCAPING_RE.sub(replace_escape_sequence, s) +# 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]: labels: Dict[str, str] = {} # Return if we don't have valid labels @@ -44,17 +58,19 @@ def _parse_labels(labels_string: str) -> Dict[str, str]: return labels # remove SINGLE leading and trailing commas - labels_string = labels_string.strip().removeprefix(',').removesuffix(',') + labels_string = labels_string.strip() + labels_string = _removeprefix(labels_string, ',') + labels_string = _removesuffix(labels_string, ',') - sub_labels = labels_string.split(",") try: # Process one label at a time - for label in sub_labels: + 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 = normalized_value.removeprefix('"').removesuffix('"') + normalized_value = _removeprefix(normalized_value, '"') + normalized_value = _removesuffix(normalized_value, '"') if "\\" in normalized_value: normalized_value = _replace_escaping(normalized_value)