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

121 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:04 +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: 

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 _validator(self): 

61 v = getattr(self, "_cached_validator", None) 

62 if v is not None: 

63 return v 

64 self._cached_validator = validators.Validator().require_presence_of( 

65 "scheme" 

66 ) 

67 return self._cached_validator 

68 

69 @property 

70 def host(self): 

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

72 try: 

73 authority = self.authority_info() 

74 except exc.InvalidAuthority: 

75 return None 

76 return authority["host"] 

77 

78 @property 

79 def port(self): 

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

81 try: 

82 authority = self.authority_info() 

83 except exc.InvalidAuthority: 

84 return None 

85 return authority["port"] 

86 

87 @property 

88 def userinfo(self): 

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

90 try: 

91 authority = self.authority_info() 

92 except exc.InvalidAuthority: 

93 return None 

94 return authority["userinfo"] 

95 

96 def is_absolute(self): 

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

98 

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

100 

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

102 :rtype: bool 

103 """ 

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

105 

106 def is_valid(self, **kwargs): 

107 """Determine if the URI is valid. 

108 

109 .. deprecated:: 1.1.0 

110 

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

112 

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

114 presence of the scheme component. 

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

116 the presence of the authority component. 

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

118 presence of the path component. 

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

120 presence of the query component. 

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

122 the presence of the fragment component. 

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

124 :rtype: bool 

125 """ 

126 warnings.warn( 

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

128 "This method will be eventually removed.", 

129 DeprecationWarning, 

130 ) 

131 validators = [ 

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

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

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

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

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

137 ] 

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

139 

140 def authority_is_valid(self, require=False): 

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

142 

143 .. deprecated:: 1.1.0 

144 

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

146 

147 :param bool require: 

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

149 :returns: 

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

151 :rtype: 

152 bool 

153 """ 

154 warnings.warn( 

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

156 "This method will be eventually removed.", 

157 DeprecationWarning, 

158 ) 

159 try: 

160 self.authority_info() 

161 except exc.InvalidAuthority: 

162 return False 

163 

164 return validators.authority_is_valid( 

165 self.authority, 

166 host=self.host, 

167 require=require, 

168 ) 

169 

170 def scheme_is_valid(self, require=False): 

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

172 

173 .. deprecated:: 1.1.0 

174 

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

176 

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

178 component. 

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

180 :rtype: bool 

181 """ 

182 warnings.warn( 

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

184 "This method will be eventually removed.", 

185 DeprecationWarning, 

186 ) 

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

188 

189 def path_is_valid(self, require=False): 

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

191 

192 .. deprecated:: 1.1.0 

193 

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

195 

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

197 component. 

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

199 :rtype: bool 

200 """ 

201 warnings.warn( 

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

203 "This method will be eventually removed.", 

204 DeprecationWarning, 

205 ) 

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

207 

208 def query_is_valid(self, require=False): 

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

210 

211 .. deprecated:: 1.1.0 

212 

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

214 

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

216 component. 

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

218 :rtype: bool 

219 """ 

220 warnings.warn( 

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

222 "This method will be eventually removed.", 

223 DeprecationWarning, 

224 ) 

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

226 

227 def fragment_is_valid(self, require=False): 

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

229 

230 .. deprecated:: 1.1.0 

231 

232 Use the Validator object instead. 

233 

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

235 component. 

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

237 :rtype: bool 

238 """ 

239 warnings.warn( 

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

241 "This method will be eventually removed.", 

242 DeprecationWarning, 

243 ) 

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

245 

246 def normalized_equality(self, other_ref): 

247 """Compare this URIReference to another URIReference. 

248 

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

250 we're comparing. 

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

252 :rtype: bool 

253 """ 

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

255 

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

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

258 

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

260 use the provided base URI to resolve it. 

261 

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

263 

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

265 absolute URI or it will raise an exception. 

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

267 reference using ``base_uri``. 

268 :rtype: :class:`URIReference` 

269 :raises rfc3986.exceptions.ResolutionError: 

270 If the ``base_uri`` does not at least have a scheme. 

271 """ 

272 if not isinstance(base_uri, URIMixin): 

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

274 

275 try: 

276 self._validator.validate(base_uri) 

277 except exc.ValidationError: 

278 raise exc.ResolutionError(base_uri) 

279 

280 # This is optional per 

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

282 base_uri = base_uri.normalize() 

283 

284 # The reference we're resolving 

285 resolving = self 

286 

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

288 resolving = resolving.copy_with(scheme=None) 

289 

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

291 if resolving.scheme is not None: 

292 target = resolving.copy_with( 

293 path=normalizers.normalize_path(resolving.path) 

294 ) 

295 else: 

296 if resolving.authority is not None: 

297 target = resolving.copy_with( 

298 scheme=base_uri.scheme, 

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

300 ) 

301 else: 

302 if resolving.path is None: 

303 if resolving.query is not None: 

304 query = resolving.query 

305 else: 

306 query = base_uri.query 

307 target = resolving.copy_with( 

308 scheme=base_uri.scheme, 

309 authority=base_uri.authority, 

310 path=base_uri.path, 

311 query=query, 

312 ) 

313 else: 

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

315 path = normalizers.normalize_path(resolving.path) 

316 else: 

317 path = normalizers.normalize_path( 

318 misc.merge_paths(base_uri, resolving.path) 

319 ) 

320 target = resolving.copy_with( 

321 scheme=base_uri.scheme, 

322 authority=base_uri.authority, 

323 path=path, 

324 query=resolving.query, 

325 ) 

326 return target 

327 

328 def unsplit(self): 

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

330 

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

332 :rtype: str 

333 """ 

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

335 result_list = [] 

336 if self.scheme: 

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

338 if self.authority: 

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

340 if self.path: 

341 result_list.append(self.path) 

342 if self.query is not None: 

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

344 if self.fragment is not None: 

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

346 return "".join(result_list) 

347 

348 def copy_with( 

349 self, 

350 scheme=misc.UseExisting, 

351 authority=misc.UseExisting, 

352 path=misc.UseExisting, 

353 query=misc.UseExisting, 

354 fragment=misc.UseExisting, 

355 ): 

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

357 

358 :param str scheme: 

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

360 :param str authority: 

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

362 :param str path: 

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

364 :param str query: 

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

366 :param str fragment: 

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

368 :returns: 

369 New URIReference with provided components. 

370 :rtype: 

371 URIReference 

372 """ 

373 attributes = { 

374 "scheme": scheme, 

375 "authority": authority, 

376 "path": path, 

377 "query": query, 

378 "fragment": fragment, 

379 } 

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

381 if value is misc.UseExisting: 

382 del attributes[key] 

383 uri = self._replace(**attributes) 

384 uri.encoding = self.encoding 

385 return uri