Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/rfc3986/_mixin.py: 23%

112 statements  

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

1"""Module containing the implementation of the URIMixin class.""" 

2import warnings 

3 

4from . import exceptions as exc 

5from . import misc 

6from . import normalizers 

7from . import validators 

8 

9 

10class URIMixin(object): 

11 """Mixin with all shared methods for URIs and IRIs.""" 

12 

13 __hash__ = tuple.__hash__ 

14 

15 def authority_info(self): 

16 """Return a dictionary with the ``userinfo``, ``host``, and ``port``. 

17 

18 If the authority is not valid, it will raise a 

19 :class:`~rfc3986.exceptions.InvalidAuthority` Exception. 

20 

21 :returns: 

22 ``{'userinfo': 'username:password', 'host': 'www.example.com', 

23 'port': '80'}`` 

24 :rtype: dict 

25 :raises rfc3986.exceptions.InvalidAuthority: 

26 If the authority is not ``None`` and can not be parsed. 

27 """ 

28 if not self.authority: 

29 return {"userinfo": None, "host": None, "port": None} 

30 

31 match = self._match_subauthority() 

32 

33 if match is None: 

34 # In this case, we have an authority that was parsed from the URI 

35 # Reference, but it cannot be further parsed by our 

36 # misc.SUBAUTHORITY_MATCHER. In this case it must not be a valid 

37 # authority. 

38 raise exc.InvalidAuthority(self.authority.encode(self.encoding)) 

39 

40 # We had a match, now let's ensure that it is actually a valid host 

41 # address if it is IPv4 

42 matches = match.groupdict() 

43 host = matches.get("host") 

44 

45 if ( 

46 host 

47 and misc.IPv4_MATCHER.match(host) 

48 and not validators.valid_ipv4_host_address(host) 

49 ): 

50 # If we have a host, it appears to be IPv4 and it does not have 

51 # valid bytes, it is an InvalidAuthority. 

52 raise exc.InvalidAuthority(self.authority.encode(self.encoding)) 

53 

54 return matches 

55 

56 def _match_subauthority(self): 

57 return misc.SUBAUTHORITY_MATCHER.match(self.authority) 

58 

59 @property 

60 def host(self): 

61 """If present, a string representing the host.""" 

62 try: 

63 authority = self.authority_info() 

64 except exc.InvalidAuthority: 

65 return None 

66 return authority["host"] 

67 

68 @property 

69 def port(self): 

70 """If present, the port extracted from the authority.""" 

71 try: 

72 authority = self.authority_info() 

73 except exc.InvalidAuthority: 

74 return None 

75 return authority["port"] 

76 

77 @property 

78 def userinfo(self): 

79 """If present, the userinfo extracted from the authority.""" 

80 try: 

81 authority = self.authority_info() 

82 except exc.InvalidAuthority: 

83 return None 

84 return authority["userinfo"] 

85 

86 def is_absolute(self): 

87 """Determine if this URI Reference is an absolute URI. 

88 

89 See http://tools.ietf.org/html/rfc3986#section-4.3 for explanation. 

90 

91 :returns: ``True`` if it is an absolute URI, ``False`` otherwise. 

92 :rtype: bool 

93 """ 

94 return bool(misc.ABSOLUTE_URI_MATCHER.match(self.unsplit())) 

95 

96 def is_valid(self, **kwargs): 

97 """Determine if the URI is valid. 

98 

99 .. deprecated:: 1.1.0 

100 

101 Use the :class:`~rfc3986.validators.Validator` object instead. 

102 

103 :param bool require_scheme: Set to ``True`` if you wish to require the 

104 presence of the scheme component. 

105 :param bool require_authority: Set to ``True`` if you wish to require 

106 the presence of the authority component. 

107 :param bool require_path: Set to ``True`` if you wish to require the 

108 presence of the path component. 

109 :param bool require_query: Set to ``True`` if you wish to require the 

110 presence of the query component. 

111 :param bool require_fragment: Set to ``True`` if you wish to require 

112 the presence of the fragment component. 

113 :returns: ``True`` if the URI is valid. ``False`` otherwise. 

114 :rtype: bool 

115 """ 

116 warnings.warn( 

117 "Please use rfc3986.validators.Validator instead. " 

118 "This method will be eventually removed.", 

119 DeprecationWarning, 

120 ) 

121 validators = [ 

122 (self.scheme_is_valid, kwargs.get("require_scheme", False)), 

123 (self.authority_is_valid, kwargs.get("require_authority", False)), 

124 (self.path_is_valid, kwargs.get("require_path", False)), 

125 (self.query_is_valid, kwargs.get("require_query", False)), 

126 (self.fragment_is_valid, kwargs.get("require_fragment", False)), 

127 ] 

128 return all(v(r) for v, r in validators) 

129 

130 def authority_is_valid(self, require=False): 

131 """Determine if the authority component is valid. 

132 

133 .. deprecated:: 1.1.0 

134 

135 Use the :class:`~rfc3986.validators.Validator` object instead. 

136 

137 :param bool require: 

138 Set to ``True`` to require the presence of this component. 

139 :returns: 

140 ``True`` if the authority is valid. ``False`` otherwise. 

141 :rtype: 

142 bool 

143 """ 

144 warnings.warn( 

145 "Please use rfc3986.validators.Validator instead. " 

146 "This method will be eventually removed.", 

147 DeprecationWarning, 

148 ) 

149 try: 

150 self.authority_info() 

151 except exc.InvalidAuthority: 

152 return False 

153 

154 return validators.authority_is_valid( 

155 self.authority, 

156 host=self.host, 

157 require=require, 

158 ) 

159 

160 def scheme_is_valid(self, require=False): 

161 """Determine if the scheme component is valid. 

162 

163 .. deprecated:: 1.1.0 

164 

165 Use the :class:`~rfc3986.validators.Validator` object instead. 

166 

167 :param str require: Set to ``True`` to require the presence of this 

168 component. 

169 :returns: ``True`` if the scheme is valid. ``False`` otherwise. 

170 :rtype: bool 

171 """ 

172 warnings.warn( 

173 "Please use rfc3986.validators.Validator instead. " 

174 "This method will be eventually removed.", 

175 DeprecationWarning, 

176 ) 

177 return validators.scheme_is_valid(self.scheme, require) 

178 

179 def path_is_valid(self, require=False): 

180 """Determine if the path component is valid. 

181 

182 .. deprecated:: 1.1.0 

183 

184 Use the :class:`~rfc3986.validators.Validator` object instead. 

185 

186 :param str require: Set to ``True`` to require the presence of this 

187 component. 

188 :returns: ``True`` if the path is valid. ``False`` otherwise. 

189 :rtype: bool 

190 """ 

191 warnings.warn( 

192 "Please use rfc3986.validators.Validator instead. " 

193 "This method will be eventually removed.", 

194 DeprecationWarning, 

195 ) 

196 return validators.path_is_valid(self.path, require) 

197 

198 def query_is_valid(self, require=False): 

199 """Determine if the query component is valid. 

200 

201 .. deprecated:: 1.1.0 

202 

203 Use the :class:`~rfc3986.validators.Validator` object instead. 

204 

205 :param str require: Set to ``True`` to require the presence of this 

206 component. 

207 :returns: ``True`` if the query is valid. ``False`` otherwise. 

208 :rtype: bool 

209 """ 

210 warnings.warn( 

211 "Please use rfc3986.validators.Validator instead. " 

212 "This method will be eventually removed.", 

213 DeprecationWarning, 

214 ) 

215 return validators.query_is_valid(self.query, require) 

216 

217 def fragment_is_valid(self, require=False): 

218 """Determine if the fragment component is valid. 

219 

220 .. deprecated:: 1.1.0 

221 

222 Use the Validator object instead. 

223 

224 :param str require: Set to ``True`` to require the presence of this 

225 component. 

226 :returns: ``True`` if the fragment is valid. ``False`` otherwise. 

227 :rtype: bool 

228 """ 

229 warnings.warn( 

230 "Please use rfc3986.validators.Validator instead. " 

231 "This method will be eventually removed.", 

232 DeprecationWarning, 

233 ) 

234 return validators.fragment_is_valid(self.fragment, require) 

235 

236 def normalized_equality(self, other_ref): 

237 """Compare this URIReference to another URIReference. 

238 

239 :param URIReference other_ref: (required), The reference with which 

240 we're comparing. 

241 :returns: ``True`` if the references are equal, ``False`` otherwise. 

242 :rtype: bool 

243 """ 

244 return tuple(self.normalize()) == tuple(other_ref.normalize()) 

245 

246 def resolve_with(self, base_uri, strict=False): 

247 """Use an absolute URI Reference to resolve this relative reference. 

248 

249 Assuming this is a relative reference that you would like to resolve, 

250 use the provided base URI to resolve it. 

251 

252 See http://tools.ietf.org/html/rfc3986#section-5 for more information. 

253 

254 :param base_uri: Either a string or URIReference. It must be an 

255 absolute URI or it will raise an exception. 

256 :returns: A new URIReference which is the result of resolving this 

257 reference using ``base_uri``. 

258 :rtype: :class:`URIReference` 

259 :raises rfc3986.exceptions.ResolutionError: 

260 If the ``base_uri`` is not an absolute URI. 

261 """ 

262 if not isinstance(base_uri, URIMixin): 

263 base_uri = type(self).from_string(base_uri) 

264 

265 if not base_uri.is_absolute(): 

266 raise exc.ResolutionError(base_uri) 

267 

268 # This is optional per 

269 # http://tools.ietf.org/html/rfc3986#section-5.2.1 

270 base_uri = base_uri.normalize() 

271 

272 # The reference we're resolving 

273 resolving = self 

274 

275 if not strict and resolving.scheme == base_uri.scheme: 

276 resolving = resolving.copy_with(scheme=None) 

277 

278 # http://tools.ietf.org/html/rfc3986#page-32 

279 if resolving.scheme is not None: 

280 target = resolving.copy_with( 

281 path=normalizers.normalize_path(resolving.path) 

282 ) 

283 else: 

284 if resolving.authority is not None: 

285 target = resolving.copy_with( 

286 scheme=base_uri.scheme, 

287 path=normalizers.normalize_path(resolving.path), 

288 ) 

289 else: 

290 if resolving.path is None: 

291 if resolving.query is not None: 

292 query = resolving.query 

293 else: 

294 query = base_uri.query 

295 target = resolving.copy_with( 

296 scheme=base_uri.scheme, 

297 authority=base_uri.authority, 

298 path=base_uri.path, 

299 query=query, 

300 ) 

301 else: 

302 if resolving.path.startswith("/"): 

303 path = normalizers.normalize_path(resolving.path) 

304 else: 

305 path = normalizers.normalize_path( 

306 misc.merge_paths(base_uri, resolving.path) 

307 ) 

308 target = resolving.copy_with( 

309 scheme=base_uri.scheme, 

310 authority=base_uri.authority, 

311 path=path, 

312 query=resolving.query, 

313 ) 

314 return target 

315 

316 def unsplit(self): 

317 """Create a URI string from the components. 

318 

319 :returns: The URI Reference reconstituted as a string. 

320 :rtype: str 

321 """ 

322 # See http://tools.ietf.org/html/rfc3986#section-5.3 

323 result_list = [] 

324 if self.scheme: 

325 result_list.extend([self.scheme, ":"]) 

326 if self.authority: 

327 result_list.extend(["//", self.authority]) 

328 if self.path: 

329 result_list.append(self.path) 

330 if self.query is not None: 

331 result_list.extend(["?", self.query]) 

332 if self.fragment is not None: 

333 result_list.extend(["#", self.fragment]) 

334 return "".join(result_list) 

335 

336 def copy_with( 

337 self, 

338 scheme=misc.UseExisting, 

339 authority=misc.UseExisting, 

340 path=misc.UseExisting, 

341 query=misc.UseExisting, 

342 fragment=misc.UseExisting, 

343 ): 

344 """Create a copy of this reference with the new components. 

345 

346 :param str scheme: 

347 (optional) The scheme to use for the new reference. 

348 :param str authority: 

349 (optional) The authority to use for the new reference. 

350 :param str path: 

351 (optional) The path to use for the new reference. 

352 :param str query: 

353 (optional) The query to use for the new reference. 

354 :param str fragment: 

355 (optional) The fragment to use for the new reference. 

356 :returns: 

357 New URIReference with provided components. 

358 :rtype: 

359 URIReference 

360 """ 

361 attributes = { 

362 "scheme": scheme, 

363 "authority": authority, 

364 "path": path, 

365 "query": query, 

366 "fragment": fragment, 

367 } 

368 for key, value in list(attributes.items()): 

369 if value is misc.UseExisting: 

370 del attributes[key] 

371 uri = self._replace(**attributes) 

372 uri.encoding = self.encoding 

373 return uri