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