spectator/meter/meter_id.py (51 lines of code) (raw):
import logging
import re
from typing import Dict, Optional
from spectator.common_tags import validate_tags
class MeterId:
"""The name and tags which uniquely identify a Meter instance. The tags are key-value pairs of
strings. This class should NOT be used directly. Instead, use the Registry.new_id() method, to
ensure that any extra common tags are properly applied to the Meter."""
INVALID_CHARS = re.compile("[^-._A-Za-z0-9~^]")
def __init__(self, name: str, tags: Optional[Dict[str, str]] = None) -> None:
if tags is None:
tags = {}
self._logger = logging.getLogger(__name__)
self._name = name
self._tags = self._validate_tags(tags)
self.spectatord_id = self._to_spectatord_id(self._name, self._tags)
def _validate_tags(self, tags: Dict[str, str]) -> Dict[str, str]:
valid_tags = validate_tags(tags)
if valid_tags != tags:
msg = "MeterId(name=%s, tags=%s) is invalid due to tag keys or values which are not strings " \
"or are zero-length strings; proceeding with truncated tags MeterId(name=%s, tags=%s)"
self._logger.warning(msg, self._name, tags, self._name, valid_tags)
return valid_tags
def _replace_invalid_chars(self, s: str) -> str:
return self.INVALID_CHARS.sub("_", s)
def _to_spectatord_id(self, name: str, tags: Optional[Dict[str, str]]) -> str:
result = self._replace_invalid_chars(name)
for k, v in sorted(tags.items()):
k = self._replace_invalid_chars(k)
v = self._replace_invalid_chars(v)
result += f",{k}={v}"
return result
def name(self) -> str:
return self._name
def tags(self) -> Dict[str, str]:
return self._tags.copy()
def with_tag(self, k: str, v: str) -> "MeterId":
new_tags = self._tags.copy()
new_tags[k] = v
return MeterId(self._name, new_tags)
def with_tags(self, tags: Dict[str, str]) -> "MeterId":
if len(tags) == 0:
return self
new_tags = self._tags.copy()
new_tags.update(tags)
return MeterId(self._name, new_tags)
def __hash__(self):
return hash((self._name, frozenset(self._tags.items())))
def __eq__(self, other) -> bool:
if isinstance(self, other.__class__):
return self.__dict__ == other.__dict__
return False
def __str__(self) -> str:
return f"MeterId(name={self._name}, tags={self._tags})"