template.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. """
  2. uritemplate.template
  3. ====================
  4. This module contains the essential inner workings of uritemplate.
  5. What treasures await you:
  6. - URITemplate class
  7. You see a treasure chest of knowledge in front of you.
  8. What do you do?
  9. >
  10. """
  11. import re
  12. import typing as t
  13. from uritemplate import orderedset
  14. from uritemplate import variable
  15. template_re = re.compile("{([^}]+)}")
  16. def _merge(
  17. var_dict: t.Optional[variable.VariableValueDict],
  18. overrides: variable.VariableValueDict,
  19. ) -> variable.VariableValueDict:
  20. if var_dict:
  21. opts = var_dict.copy()
  22. opts.update(overrides)
  23. return opts
  24. return overrides
  25. class URITemplate:
  26. """This parses the template and will be used to expand it.
  27. This is the most important object as the center of the API.
  28. Example::
  29. from uritemplate import URITemplate
  30. import requests
  31. t = URITemplate(
  32. 'https://api.github.com/users/sigmavirus24/gists{/gist_id}'
  33. )
  34. uri = t.expand(gist_id=123456)
  35. resp = requests.get(uri)
  36. for gist in resp.json():
  37. print(gist['html_url'])
  38. Please note::
  39. str(t)
  40. # 'https://api.github.com/users/sigmavirus24/gists{/gistid}'
  41. repr(t) # is equivalent to
  42. # URITemplate(str(t))
  43. # Where str(t) is interpreted as the URI string.
  44. Also, ``URITemplates`` are hashable so they can be used as keys in
  45. dictionaries.
  46. """
  47. def __init__(self, uri: str):
  48. #: The original URI to be parsed.
  49. self.uri: str = uri
  50. #: A list of the variables in the URI. They are stored as
  51. #: :class:`~uritemplate.variable.URIVariable`\ s
  52. self.variables: t.List[variable.URIVariable] = [
  53. variable.URIVariable(m.groups()[0])
  54. for m in template_re.finditer(self.uri)
  55. ]
  56. #: A set of variable names in the URI.
  57. self.variable_names = orderedset.OrderedSet()
  58. for var in self.variables:
  59. for name in var.variable_names:
  60. self.variable_names.add(name)
  61. def __repr__(self) -> str:
  62. return 'URITemplate("%s")' % self
  63. def __str__(self) -> str:
  64. return self.uri
  65. def __eq__(self, other: object) -> bool:
  66. if not isinstance(other, URITemplate):
  67. return NotImplemented
  68. return self.uri == other.uri
  69. def __hash__(self) -> int:
  70. return hash(self.uri)
  71. def _expand(
  72. self, var_dict: variable.VariableValueDict, replace: bool
  73. ) -> str:
  74. if not self.variables:
  75. return self.uri
  76. expansion = var_dict
  77. expanded: t.Dict[str, str] = {}
  78. for v in self.variables:
  79. expanded.update(v.expand(expansion))
  80. def replace_all(match: "re.Match[str]") -> str:
  81. return expanded.get(match.groups()[0], "")
  82. def replace_partial(match: "re.Match[str]") -> str:
  83. match_group = match.groups()[0]
  84. var = "{%s}" % match_group
  85. return expanded.get(match_group) or var
  86. replace_func = replace_partial if replace else replace_all
  87. return template_re.sub(replace_func, self.uri)
  88. def expand(
  89. self,
  90. var_dict: t.Optional[variable.VariableValueDict] = None,
  91. **kwargs: variable.VariableValue,
  92. ) -> str:
  93. """Expand the template with the given parameters.
  94. :param dict var_dict: Optional dictionary with variables and values
  95. :param kwargs: Alternative way to pass arguments
  96. :returns: str
  97. Example::
  98. t = URITemplate('https://api.github.com{/end}')
  99. t.expand({'end': 'users'})
  100. t.expand(end='gists')
  101. .. note:: Passing values by both parts, may override values in
  102. ``var_dict``. For example::
  103. expand('https://{var}', {'var': 'val1'}, var='val2')
  104. ``val2`` will be used instead of ``val1``.
  105. """
  106. return self._expand(_merge(var_dict, kwargs), False)
  107. def partial(
  108. self,
  109. var_dict: t.Optional[variable.VariableValueDict] = None,
  110. **kwargs: variable.VariableValue,
  111. ) -> "URITemplate":
  112. """Partially expand the template with the given parameters.
  113. If all of the parameters for the template are not given, return a
  114. partially expanded template.
  115. :param dict var_dict: Optional dictionary with variables and values
  116. :param kwargs: Alternative way to pass arguments
  117. :returns: :class:`URITemplate`
  118. Example::
  119. t = URITemplate('https://api.github.com{/end}')
  120. t.partial() # => URITemplate('https://api.github.com{/end}')
  121. """
  122. return URITemplate(self._expand(_merge(var_dict, kwargs), True))