Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/requests_toolbelt/_compat.py: 32%

167 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:19 +0000

1"""Private module full of compatibility hacks. 

2 

3Primarily this is for downstream redistributions of requests that unvendor 

4urllib3 without providing a shim. 

5 

6.. warning:: 

7 

8 This module is private. If you use it, and something breaks, you were 

9 warned 

10""" 

11import sys 

12 

13import requests 

14 

15try: 

16 from requests.packages.urllib3 import fields 

17 from requests.packages.urllib3 import filepost 

18 from requests.packages.urllib3 import poolmanager 

19except ImportError: 

20 from urllib3 import fields 

21 from urllib3 import filepost 

22 from urllib3 import poolmanager 

23 

24try: 

25 from requests.packages.urllib3.connection import HTTPConnection 

26 from requests.packages.urllib3 import connection 

27except ImportError: 

28 try: 

29 from urllib3.connection import HTTPConnection 

30 from urllib3 import connection 

31 except ImportError: 

32 HTTPConnection = None 

33 connection = None 

34 

35 

36if requests.__build__ < 0x020300: 

37 timeout = None 

38else: 

39 try: 

40 from requests.packages.urllib3.util import timeout 

41 except ImportError: 

42 from urllib3.util import timeout 

43 

44if requests.__build__ < 0x021000: 

45 gaecontrib = None 

46else: 

47 try: 

48 from requests.packages.urllib3.contrib import appengine as gaecontrib 

49 except ImportError: 

50 from urllib3.contrib import appengine as gaecontrib 

51 

52PY3 = sys.version_info > (3, 0) 

53 

54if PY3: 

55 from collections.abc import Mapping, MutableMapping 

56 import queue 

57 from urllib.parse import urlencode, urljoin 

58else: 

59 from collections import Mapping, MutableMapping 

60 import Queue as queue 

61 from urllib import urlencode 

62 from urlparse import urljoin 

63 

64try: 

65 basestring = basestring 

66except NameError: 

67 basestring = (str, bytes) 

68 

69 

70class HTTPHeaderDict(MutableMapping): 

71 """ 

72 :param headers: 

73 An iterable of field-value pairs. Must not contain multiple field names 

74 when compared case-insensitively. 

75 

76 :param kwargs: 

77 Additional field-value pairs to pass in to ``dict.update``. 

78 

79 A ``dict`` like container for storing HTTP Headers. 

80 

81 Field names are stored and compared case-insensitively in compliance with 

82 RFC 7230. Iteration provides the first case-sensitive key seen for each 

83 case-insensitive pair. 

84 

85 Using ``__setitem__`` syntax overwrites fields that compare equal 

86 case-insensitively in order to maintain ``dict``'s api. For fields that 

87 compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add`` 

88 in a loop. 

89 

90 If multiple fields that are equal case-insensitively are passed to the 

91 constructor or ``.update``, the behavior is undefined and some will be 

92 lost. 

93 

94 >>> headers = HTTPHeaderDict() 

95 >>> headers.add('Set-Cookie', 'foo=bar') 

96 >>> headers.add('set-cookie', 'baz=quxx') 

97 >>> headers['content-length'] = '7' 

98 >>> headers['SET-cookie'] 

99 'foo=bar, baz=quxx' 

100 >>> headers['Content-Length'] 

101 '7' 

102 """ 

103 

104 def __init__(self, headers=None, **kwargs): 

105 super(HTTPHeaderDict, self).__init__() 

106 self._container = {} 

107 if headers is not None: 

108 if isinstance(headers, HTTPHeaderDict): 

109 self._copy_from(headers) 

110 else: 

111 self.extend(headers) 

112 if kwargs: 

113 self.extend(kwargs) 

114 

115 def __setitem__(self, key, val): 

116 self._container[key.lower()] = (key, val) 

117 return self._container[key.lower()] 

118 

119 def __getitem__(self, key): 

120 val = self._container[key.lower()] 

121 return ', '.join(val[1:]) 

122 

123 def __delitem__(self, key): 

124 del self._container[key.lower()] 

125 

126 def __contains__(self, key): 

127 return key.lower() in self._container 

128 

129 def __eq__(self, other): 

130 if not isinstance(other, Mapping) and not hasattr(other, 'keys'): 

131 return False 

132 if not isinstance(other, type(self)): 

133 other = type(self)(other) 

134 return ({k.lower(): v for k, v in self.itermerged()} == 

135 {k.lower(): v for k, v in other.itermerged()}) 

136 

137 def __ne__(self, other): 

138 return not self.__eq__(other) 

139 

140 if not PY3: # Python 2 

141 iterkeys = MutableMapping.iterkeys 

142 itervalues = MutableMapping.itervalues 

143 

144 __marker = object() 

145 

146 def __len__(self): 

147 return len(self._container) 

148 

149 def __iter__(self): 

150 # Only provide the originally cased names 

151 for vals in self._container.values(): 

152 yield vals[0] 

153 

154 def pop(self, key, default=__marker): 

155 """D.pop(k[,d]) -> v, remove specified key and return its value. 

156 

157 If key is not found, d is returned if given, otherwise KeyError is 

158 raised. 

159 """ 

160 # Using the MutableMapping function directly fails due to the private 

161 # marker. 

162 # Using ordinary dict.pop would expose the internal structures. 

163 # So let's reinvent the wheel. 

164 try: 

165 value = self[key] 

166 except KeyError: 

167 if default is self.__marker: 

168 raise 

169 return default 

170 else: 

171 del self[key] 

172 return value 

173 

174 def discard(self, key): 

175 try: 

176 del self[key] 

177 except KeyError: 

178 pass 

179 

180 def add(self, key, val): 

181 """Adds a (name, value) pair, doesn't overwrite the value if it already 

182 exists. 

183 

184 >>> headers = HTTPHeaderDict(foo='bar') 

185 >>> headers.add('Foo', 'baz') 

186 >>> headers['foo'] 

187 'bar, baz' 

188 """ 

189 key_lower = key.lower() 

190 new_vals = key, val 

191 # Keep the common case aka no item present as fast as possible 

192 vals = self._container.setdefault(key_lower, new_vals) 

193 if new_vals is not vals: 

194 # new_vals was not inserted, as there was a previous one 

195 if isinstance(vals, list): 

196 # If already several items got inserted, we have a list 

197 vals.append(val) 

198 else: 

199 # vals should be a tuple then, i.e. only one item so far 

200 # Need to convert the tuple to list for further extension 

201 self._container[key_lower] = [vals[0], vals[1], val] 

202 

203 def extend(self, *args, **kwargs): 

204 """Generic import function for any type of header-like object. 

205 Adapted version of MutableMapping.update in order to insert items 

206 with self.add instead of self.__setitem__ 

207 """ 

208 if len(args) > 1: 

209 raise TypeError("extend() takes at most 1 positional " 

210 "arguments ({} given)".format(len(args))) 

211 other = args[0] if len(args) >= 1 else () 

212 

213 if isinstance(other, HTTPHeaderDict): 

214 for key, val in other.iteritems(): 

215 self.add(key, val) 

216 elif isinstance(other, Mapping): 

217 for key in other: 

218 self.add(key, other[key]) 

219 elif hasattr(other, "keys"): 

220 for key in other.keys(): 

221 self.add(key, other[key]) 

222 else: 

223 for key, value in other: 

224 self.add(key, value) 

225 

226 for key, value in kwargs.items(): 

227 self.add(key, value) 

228 

229 def getlist(self, key): 

230 """Returns a list of all the values for the named field. Returns an 

231 empty list if the key doesn't exist.""" 

232 try: 

233 vals = self._container[key.lower()] 

234 except KeyError: 

235 return [] 

236 else: 

237 if isinstance(vals, tuple): 

238 return [vals[1]] 

239 else: 

240 return vals[1:] 

241 

242 # Backwards compatibility for httplib 

243 getheaders = getlist 

244 getallmatchingheaders = getlist 

245 iget = getlist 

246 

247 def __repr__(self): 

248 return "%s(%s)" % (type(self).__name__, dict(self.itermerged())) 

249 

250 def _copy_from(self, other): 

251 for key in other: 

252 val = other.getlist(key) 

253 if isinstance(val, list): 

254 # Don't need to convert tuples 

255 val = list(val) 

256 self._container[key.lower()] = [key] + val 

257 

258 def copy(self): 

259 clone = type(self)() 

260 clone._copy_from(self) 

261 return clone 

262 

263 def iteritems(self): 

264 """Iterate over all header lines, including duplicate ones.""" 

265 for key in self: 

266 vals = self._container[key.lower()] 

267 for val in vals[1:]: 

268 yield vals[0], val 

269 

270 def itermerged(self): 

271 """Iterate over all headers, merging duplicate ones together.""" 

272 for key in self: 

273 val = self._container[key.lower()] 

274 yield val[0], ', '.join(val[1:]) 

275 

276 def items(self): 

277 return list(self.iteritems()) 

278 

279 @classmethod 

280 def from_httplib(cls, message): # Python 2 

281 """Read headers from a Python 2 httplib message object.""" 

282 # python2.7 does not expose a proper API for exporting multiheaders 

283 # efficiently. This function re-reads raw lines from the message 

284 # object and extracts the multiheaders properly. 

285 headers = [] 

286 

287 for line in message.headers: 

288 if line.startswith((' ', '\t')): 

289 key, value = headers[-1] 

290 headers[-1] = (key, value + '\r\n' + line.rstrip()) 

291 continue 

292 

293 key, value = line.split(':', 1) 

294 headers.append((key, value.strip())) 

295 

296 return cls(headers) 

297 

298 

299__all__ = ( 

300 'basestring', 

301 'connection', 

302 'fields', 

303 'filepost', 

304 'poolmanager', 

305 'timeout', 

306 'HTTPHeaderDict', 

307 'queue', 

308 'urlencode', 

309 'gaecontrib', 

310 'urljoin', 

311)