convertors.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import math
  2. import typing
  3. import uuid
  4. T = typing.TypeVar("T")
  5. class Convertor(typing.Generic[T]):
  6. regex: typing.ClassVar[str] = ""
  7. def convert(self, value: str) -> T:
  8. raise NotImplementedError() # pragma: no cover
  9. def to_string(self, value: T) -> str:
  10. raise NotImplementedError() # pragma: no cover
  11. class StringConvertor(Convertor):
  12. regex = "[^/]+"
  13. def convert(self, value: str) -> str:
  14. return value
  15. def to_string(self, value: str) -> str:
  16. value = str(value)
  17. assert "/" not in value, "May not contain path separators"
  18. assert value, "Must not be empty"
  19. return value
  20. class PathConvertor(Convertor):
  21. regex = ".*"
  22. def convert(self, value: str) -> str:
  23. return str(value)
  24. def to_string(self, value: str) -> str:
  25. return str(value)
  26. class IntegerConvertor(Convertor):
  27. regex = "[0-9]+"
  28. def convert(self, value: str) -> int:
  29. return int(value)
  30. def to_string(self, value: int) -> str:
  31. value = int(value)
  32. assert value >= 0, "Negative integers are not supported"
  33. return str(value)
  34. class FloatConvertor(Convertor):
  35. regex = r"[0-9]+(\.[0-9]+)?"
  36. def convert(self, value: str) -> float:
  37. return float(value)
  38. def to_string(self, value: float) -> str:
  39. value = float(value)
  40. assert value >= 0.0, "Negative floats are not supported"
  41. assert not math.isnan(value), "NaN values are not supported"
  42. assert not math.isinf(value), "Infinite values are not supported"
  43. return ("%0.20f" % value).rstrip("0").rstrip(".")
  44. class UUIDConvertor(Convertor):
  45. regex = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
  46. def convert(self, value: str) -> uuid.UUID:
  47. return uuid.UUID(value)
  48. def to_string(self, value: uuid.UUID) -> str:
  49. return str(value)
  50. CONVERTOR_TYPES = {
  51. "str": StringConvertor(),
  52. "path": PathConvertor(),
  53. "int": IntegerConvertor(),
  54. "float": FloatConvertor(),
  55. "uuid": UUIDConvertor(),
  56. }
  57. def register_url_convertor(key: str, convertor: Convertor) -> None:
  58. CONVERTOR_TYPES[key] = convertor