variable.py 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. """Variable class for URITemplate."""
  2. from __future__ import annotations
  3. from .charset import Charset
  4. class VariableInvalidError(Exception):
  5. """Exception thrown for invalid variables."""
  6. variable: str
  7. def __init__(self, variable: str) -> None:
  8. self.variable = variable
  9. def __str__(self) -> str:
  10. """Convert to string."""
  11. return 'Bad variable: ' + self.variable
  12. class Variable:
  13. """
  14. A template variable.
  15. https://tools.ietf.org/html/rfc6570#section-2.3
  16. """
  17. name: str
  18. key: str
  19. max_length: int
  20. explode: bool
  21. array: bool
  22. default: (str | None)
  23. def __init__(self, var_spec: str) -> None:
  24. self.name = ''
  25. self.key = ''
  26. self.max_length = 0
  27. self.explode = False
  28. self.array = False
  29. self.default = None
  30. if (var_spec[0:1] not in Charset.VAR_START):
  31. raise VariableInvalidError(var_spec)
  32. if ('=' in var_spec):
  33. var_spec, self.default = var_spec.split('=', 1)
  34. if (':' in var_spec):
  35. var_spec, max_length = var_spec.split(':', 1)
  36. if ((0 < len(max_length)) and (len(max_length) < 4)):
  37. for digit in max_length:
  38. if (digit not in Charset.DIGIT):
  39. raise VariableInvalidError(var_spec + ':' + max_length)
  40. self.max_length = int(max_length)
  41. if (not self.max_length):
  42. raise VariableInvalidError(var_spec + ':' + max_length)
  43. else:
  44. raise VariableInvalidError(var_spec + ':' + max_length)
  45. elif ('*' == var_spec[-1]):
  46. var_spec = var_spec[:-1]
  47. self.explode = True
  48. elif ('[]' == var_spec[-2:]):
  49. var_spec = var_spec[:-2]
  50. self.array = True
  51. self.explode = True
  52. index = 0
  53. while (index < len(var_spec)):
  54. codepoint = var_spec[index]
  55. if (('%' == codepoint)
  56. and ((index + 2) < len(var_spec))
  57. and (var_spec[index + 1] in Charset.HEX_DIGIT)
  58. and (var_spec[index + 2] in Charset.HEX_DIGIT)):
  59. self.key += var_spec[index:index + 3]
  60. index += 2
  61. elif (codepoint in Charset.VAR_CHAR):
  62. self.key += codepoint
  63. elif ('/' == codepoint):
  64. self.name = self.key
  65. self.key = ''
  66. else:
  67. raise VariableInvalidError(var_spec + ((':' + str(self.max_length)) if (self.max_length) else '')
  68. + ('[]' if (self.array) else ('*' if (self.explode) else '')))
  69. index += 1
  70. self.name = (self.name or self.key)
  71. self.key = (self.key or self.name)
  72. def __str__(self) -> str:
  73. """Convert to string."""
  74. return (self.name + (f'/{self.key}' if (self.key and (self.key != self.name)) else '')
  75. + (f':{self.max_length}' if (self.max_length) else '')
  76. + ('*' if (self.explode and not self.array) else '') + ('[]' if (self.array) else '')
  77. + (f'={self.default}' if (self.default is not None) else ''))