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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

162 statements  

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 

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

45 

46if PY3: 

47 from collections.abc import Mapping, MutableMapping 

48 import queue 

49 from urllib.parse import urlencode, urljoin 

50else: 

51 from collections import Mapping, MutableMapping 

52 import Queue as queue 

53 from urllib import urlencode 

54 from urlparse import urljoin 

55 

56try: 

57 basestring = basestring 

58except NameError: 

59 basestring = (str, bytes) 

60 

61 

62class HTTPHeaderDict(MutableMapping): 

63 """ 

64 :param headers: 

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

66 when compared case-insensitively. 

67 

68 :param kwargs: 

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

70 

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

72 

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

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

75 case-insensitive pair. 

76 

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

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

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

80 in a loop. 

81 

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

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

84 lost. 

85 

86 >>> headers = HTTPHeaderDict() 

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

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

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

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

91 'foo=bar, baz=quxx' 

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

93 '7' 

94 """ 

95 

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

97 super(HTTPHeaderDict, self).__init__() 

98 self._container = {} 

99 if headers is not None: 

100 if isinstance(headers, HTTPHeaderDict): 

101 self._copy_from(headers) 

102 else: 

103 self.extend(headers) 

104 if kwargs: 

105 self.extend(kwargs) 

106 

107 def __setitem__(self, key, val): 

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

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

110 

111 def __getitem__(self, key): 

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

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

114 

115 def __delitem__(self, key): 

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

117 

118 def __contains__(self, key): 

119 return key.lower() in self._container 

120 

121 def __eq__(self, other): 

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

123 return False 

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

125 other = type(self)(other) 

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

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

128 

129 def __ne__(self, other): 

130 return not self.__eq__(other) 

131 

132 if not PY3: # Python 2 

133 iterkeys = MutableMapping.iterkeys 

134 itervalues = MutableMapping.itervalues 

135 

136 __marker = object() 

137 

138 def __len__(self): 

139 return len(self._container) 

140 

141 def __iter__(self): 

142 # Only provide the originally cased names 

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

144 yield vals[0] 

145 

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

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

148 

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

150 raised. 

151 """ 

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

153 # marker. 

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

155 # So let's reinvent the wheel. 

156 try: 

157 value = self[key] 

158 except KeyError: 

159 if default is self.__marker: 

160 raise 

161 return default 

162 else: 

163 del self[key] 

164 return value 

165 

166 def discard(self, key): 

167 try: 

168 del self[key] 

169 except KeyError: 

170 pass 

171 

172 def add(self, key, val): 

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

174 exists. 

175 

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

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

178 >>> headers['foo'] 

179 'bar, baz' 

180 """ 

181 key_lower = key.lower() 

182 new_vals = key, val 

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

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

185 if new_vals is not vals: 

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

187 if isinstance(vals, list): 

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

189 vals.append(val) 

190 else: 

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

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

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

194 

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

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

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

198 with self.add instead of self.__setitem__ 

199 """ 

200 if len(args) > 1: 

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

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

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

204 

205 if isinstance(other, HTTPHeaderDict): 

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

207 self.add(key, val) 

208 elif isinstance(other, Mapping): 

209 for key in other: 

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

211 elif hasattr(other, "keys"): 

212 for key in other.keys(): 

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

214 else: 

215 for key, value in other: 

216 self.add(key, value) 

217 

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

219 self.add(key, value) 

220 

221 def getlist(self, key): 

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

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

224 try: 

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

226 except KeyError: 

227 return [] 

228 else: 

229 if isinstance(vals, tuple): 

230 return [vals[1]] 

231 else: 

232 return vals[1:] 

233 

234 # Backwards compatibility for httplib 

235 getheaders = getlist 

236 getallmatchingheaders = getlist 

237 iget = getlist 

238 

239 def __repr__(self): 

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

241 

242 def _copy_from(self, other): 

243 for key in other: 

244 val = other.getlist(key) 

245 if isinstance(val, list): 

246 # Don't need to convert tuples 

247 val = list(val) 

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

249 

250 def copy(self): 

251 clone = type(self)() 

252 clone._copy_from(self) 

253 return clone 

254 

255 def iteritems(self): 

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

257 for key in self: 

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

259 for val in vals[1:]: 

260 yield vals[0], val 

261 

262 def itermerged(self): 

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

264 for key in self: 

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

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

267 

268 def items(self): 

269 return list(self.iteritems()) 

270 

271 @classmethod 

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

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

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

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

276 # object and extracts the multiheaders properly. 

277 headers = [] 

278 

279 for line in message.headers: 

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

281 key, value = headers[-1] 

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

283 continue 

284 

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

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

287 

288 return cls(headers) 

289 

290 

291__all__ = ( 

292 'basestring', 

293 'connection', 

294 'fields', 

295 'filepost', 

296 'poolmanager', 

297 'timeout', 

298 'HTTPHeaderDict', 

299 'queue', 

300 'urlencode', 

301 'urljoin', 

302)