1# -*- coding: utf-8 -*-
2import re
3import six
4from json import dumps
5
6from webtest.compat import urlencode
7
8
9class NoDefault(object):
10    """Sentinel to uniquely represent no default value."""
11
12    def __repr__(self):
13        return '<NoDefault>'
14
15NoDefault = NoDefault()
16
17
18def json_method(method):
19    """Do a %(method)s request.  Very like the
20    :class:`~webtest.TestApp.%(lmethod)s` method.
21
22    ``params`` are dumped to json and put in the body of the request.
23    Content-Type is set to ``application/json``.
24
25    Returns a :class:`webtest.TestResponse` object.
26    """
27
28    def wrapper(self, url, params=NoDefault, **kw):
29        content_type = 'application/json'
30        if params is not NoDefault:
31            params = dumps(params, cls=self.JSONEncoder)
32        kw.update(
33            params=params,
34            content_type=content_type,
35            upload_files=None,
36        )
37        return self._gen_request(method, url, **kw)
38
39    subst = dict(lmethod=method.lower(), method=method)
40    wrapper.__doc__ = json_method.__doc__ % subst
41    wrapper.__name__ = str('%(lmethod)s_json' % subst)
42
43    return wrapper
44
45
46def stringify(value):
47    if isinstance(value, six.text_type):
48        return value
49    elif isinstance(value, six.binary_type):
50        return value.decode('utf8')
51    else:
52        return str(value)
53
54
55entity_pattern = re.compile(r"&(\w+|#\d+|#[xX][a-fA-F0-9]+);")
56
57
58def encode_params(params, content_type):
59    if params is NoDefault:
60        return ''
61    if isinstance(params, dict) or hasattr(params, 'items'):
62        params = list(params.items())
63    if isinstance(params, (list, tuple)):
64        if content_type:
65            content_type = content_type.lower()
66            if 'charset=' in content_type:
67                charset = content_type.split('charset=')[1]
68                charset = charset.strip('; ').lower()
69                encoded_params = []
70                for k, v in params:
71                    if isinstance(v, six.text_type):
72                        v = v.encode(charset)
73                    encoded_params.append((k, v))
74                params = encoded_params
75        params = urlencode(params, doseq=True)
76    return params
77
78
79def make_pattern(pat):
80    """Find element pattern can be a regex or a callable."""
81    if pat is None:
82        return None
83    if isinstance(pat, six.binary_type):
84        pat = pat.decode('utf8')
85    if isinstance(pat, six.text_type):
86        pat = re.compile(pat)
87    if hasattr(pat, 'search'):
88        return pat.search
89    if hasattr(pat, '__call__'):
90        return pat
91    raise ValueError(
92        "Cannot make callable pattern object out of %r" % pat)
93
94
95class _RequestCookieAdapter(object):
96    """
97    cookielib.CookieJar support for webob.Request
98    """
99    def __init__(self, request):
100        self._request = request
101        self.origin_req_host = request.host
102
103    def is_unverifiable(self):
104        return True  # sure? Why not?
105
106    @property
107    def unverifiable(self):  # NOQA
108        # This is undocumented method that Python 3 cookielib uses
109        return True
110
111    def get_full_url(self):
112        return self._request.url
113
114    def get_host(self):
115        return self.origin_req_host
116    get_origin_req_host = get_host
117
118    def add_unredirected_header(self, key, header):
119        self._request.headers[key] = header
120
121    def has_header(self, key):
122        return key in self._request.headers
123
124    def get_type(self):
125        return self._request.scheme
126
127    @property
128    def type(self):  # NOQA
129        # This is undocumented method that Python 3 cookielib uses
130        return self.get_type()
131
132    def header_items(self):  # pragma: no cover
133        # This is unused on most python versions
134        return self._request.headers.items()
135
136
137class _ResponseCookieAdapter(object):
138    """
139    cookielib.CookieJar support for webob.Response
140    """
141    def __init__(self, response):
142        self._response = response
143
144    def info(self):
145        return self
146
147    def getheaders(self, header):
148        return self._response.headers.getall(header)
149
150    def get_all(self, headers, default):  # NOQA
151        # This is undocumented method that Python 3 cookielib uses
152        return self._response.headers.getall(headers)
153