Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/numpy/compat/_pep440.py: 32%

182 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1"""Utility to compare pep440 compatible version strings. 

2 

3The LooseVersion and StrictVersion classes that distutils provides don't 

4work; they don't recognize anything like alpha/beta/rc/dev versions. 

5""" 

6 

7# Copyright (c) Donald Stufft and individual contributors. 

8# All rights reserved. 

9 

10# Redistribution and use in source and binary forms, with or without 

11# modification, are permitted provided that the following conditions are met: 

12 

13# 1. Redistributions of source code must retain the above copyright notice, 

14# this list of conditions and the following disclaimer. 

15 

16# 2. Redistributions in binary form must reproduce the above copyright 

17# notice, this list of conditions and the following disclaimer in the 

18# documentation and/or other materials provided with the distribution. 

19 

20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 

21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 

22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 

23# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 

24# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 

25# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 

26# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 

27# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 

28# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 

29# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 

30# POSSIBILITY OF SUCH DAMAGE. 

31 

32import collections 

33import itertools 

34import re 

35 

36 

37__all__ = [ 

38 "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN", 

39] 

40 

41 

42# BEGIN packaging/_structures.py 

43 

44 

45class Infinity: 

46 def __repr__(self): 

47 return "Infinity" 

48 

49 def __hash__(self): 

50 return hash(repr(self)) 

51 

52 def __lt__(self, other): 

53 return False 

54 

55 def __le__(self, other): 

56 return False 

57 

58 def __eq__(self, other): 

59 return isinstance(other, self.__class__) 

60 

61 def __ne__(self, other): 

62 return not isinstance(other, self.__class__) 

63 

64 def __gt__(self, other): 

65 return True 

66 

67 def __ge__(self, other): 

68 return True 

69 

70 def __neg__(self): 

71 return NegativeInfinity 

72 

73 

74Infinity = Infinity() 

75 

76 

77class NegativeInfinity: 

78 def __repr__(self): 

79 return "-Infinity" 

80 

81 def __hash__(self): 

82 return hash(repr(self)) 

83 

84 def __lt__(self, other): 

85 return True 

86 

87 def __le__(self, other): 

88 return True 

89 

90 def __eq__(self, other): 

91 return isinstance(other, self.__class__) 

92 

93 def __ne__(self, other): 

94 return not isinstance(other, self.__class__) 

95 

96 def __gt__(self, other): 

97 return False 

98 

99 def __ge__(self, other): 

100 return False 

101 

102 def __neg__(self): 

103 return Infinity 

104 

105 

106# BEGIN packaging/version.py 

107 

108 

109NegativeInfinity = NegativeInfinity() 

110 

111_Version = collections.namedtuple( 

112 "_Version", 

113 ["epoch", "release", "dev", "pre", "post", "local"], 

114) 

115 

116 

117def parse(version): 

118 """ 

119 Parse the given version string and return either a :class:`Version` object 

120 or a :class:`LegacyVersion` object depending on if the given version is 

121 a valid PEP 440 version or a legacy version. 

122 """ 

123 try: 

124 return Version(version) 

125 except InvalidVersion: 

126 return LegacyVersion(version) 

127 

128 

129class InvalidVersion(ValueError): 

130 """ 

131 An invalid version was found, users should refer to PEP 440. 

132 """ 

133 

134 

135class _BaseVersion: 

136 

137 def __hash__(self): 

138 return hash(self._key) 

139 

140 def __lt__(self, other): 

141 return self._compare(other, lambda s, o: s < o) 

142 

143 def __le__(self, other): 

144 return self._compare(other, lambda s, o: s <= o) 

145 

146 def __eq__(self, other): 

147 return self._compare(other, lambda s, o: s == o) 

148 

149 def __ge__(self, other): 

150 return self._compare(other, lambda s, o: s >= o) 

151 

152 def __gt__(self, other): 

153 return self._compare(other, lambda s, o: s > o) 

154 

155 def __ne__(self, other): 

156 return self._compare(other, lambda s, o: s != o) 

157 

158 def _compare(self, other, method): 

159 if not isinstance(other, _BaseVersion): 

160 return NotImplemented 

161 

162 return method(self._key, other._key) 

163 

164 

165class LegacyVersion(_BaseVersion): 

166 

167 def __init__(self, version): 

168 self._version = str(version) 

169 self._key = _legacy_cmpkey(self._version) 

170 

171 def __str__(self): 

172 return self._version 

173 

174 def __repr__(self): 

175 return "<LegacyVersion({0})>".format(repr(str(self))) 

176 

177 @property 

178 def public(self): 

179 return self._version 

180 

181 @property 

182 def base_version(self): 

183 return self._version 

184 

185 @property 

186 def local(self): 

187 return None 

188 

189 @property 

190 def is_prerelease(self): 

191 return False 

192 

193 @property 

194 def is_postrelease(self): 

195 return False 

196 

197 

198_legacy_version_component_re = re.compile( 

199 r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, 

200) 

201 

202_legacy_version_replacement_map = { 

203 "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", 

204} 

205 

206 

207def _parse_version_parts(s): 

208 for part in _legacy_version_component_re.split(s): 

209 part = _legacy_version_replacement_map.get(part, part) 

210 

211 if not part or part == ".": 

212 continue 

213 

214 if part[:1] in "0123456789": 

215 # pad for numeric comparison 

216 yield part.zfill(8) 

217 else: 

218 yield "*" + part 

219 

220 # ensure that alpha/beta/candidate are before final 

221 yield "*final" 

222 

223 

224def _legacy_cmpkey(version): 

225 # We hardcode an epoch of -1 here. A PEP 440 version can only have an epoch 

226 # greater than or equal to 0. This will effectively put the LegacyVersion, 

227 # which uses the defacto standard originally implemented by setuptools, 

228 # as before all PEP 440 versions. 

229 epoch = -1 

230 

231 # This scheme is taken from pkg_resources.parse_version setuptools prior to 

232 # its adoption of the packaging library. 

233 parts = [] 

234 for part in _parse_version_parts(version.lower()): 

235 if part.startswith("*"): 

236 # remove "-" before a prerelease tag 

237 if part < "*final": 

238 while parts and parts[-1] == "*final-": 

239 parts.pop() 

240 

241 # remove trailing zeros from each series of numeric parts 

242 while parts and parts[-1] == "00000000": 

243 parts.pop() 

244 

245 parts.append(part) 

246 parts = tuple(parts) 

247 

248 return epoch, parts 

249 

250 

251# Deliberately not anchored to the start and end of the string, to make it 

252# easier for 3rd party code to reuse 

253VERSION_PATTERN = r""" 

254 v? 

255 (?: 

256 (?:(?P<epoch>[0-9]+)!)? # epoch 

257 (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment 

258 (?P<pre> # pre-release 

259 [-_\.]? 

260 (?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview)) 

261 [-_\.]? 

262 (?P<pre_n>[0-9]+)? 

263 )? 

264 (?P<post> # post release 

265 (?:-(?P<post_n1>[0-9]+)) 

266 | 

267 (?: 

268 [-_\.]? 

269 (?P<post_l>post|rev|r) 

270 [-_\.]? 

271 (?P<post_n2>[0-9]+)? 

272 ) 

273 )? 

274 (?P<dev> # dev release 

275 [-_\.]? 

276 (?P<dev_l>dev) 

277 [-_\.]? 

278 (?P<dev_n>[0-9]+)? 

279 )? 

280 ) 

281 (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version 

282""" 

283 

284 

285class Version(_BaseVersion): 

286 

287 _regex = re.compile( 

288 r"^\s*" + VERSION_PATTERN + r"\s*$", 

289 re.VERBOSE | re.IGNORECASE, 

290 ) 

291 

292 def __init__(self, version): 

293 # Validate the version and parse it into pieces 

294 match = self._regex.search(version) 

295 if not match: 

296 raise InvalidVersion("Invalid version: '{0}'".format(version)) 

297 

298 # Store the parsed out pieces of the version 

299 self._version = _Version( 

300 epoch=int(match.group("epoch")) if match.group("epoch") else 0, 

301 release=tuple(int(i) for i in match.group("release").split(".")), 

302 pre=_parse_letter_version( 

303 match.group("pre_l"), 

304 match.group("pre_n"), 

305 ), 

306 post=_parse_letter_version( 

307 match.group("post_l"), 

308 match.group("post_n1") or match.group("post_n2"), 

309 ), 

310 dev=_parse_letter_version( 

311 match.group("dev_l"), 

312 match.group("dev_n"), 

313 ), 

314 local=_parse_local_version(match.group("local")), 

315 ) 

316 

317 # Generate a key which will be used for sorting 

318 self._key = _cmpkey( 

319 self._version.epoch, 

320 self._version.release, 

321 self._version.pre, 

322 self._version.post, 

323 self._version.dev, 

324 self._version.local, 

325 ) 

326 

327 def __repr__(self): 

328 return "<Version({0})>".format(repr(str(self))) 

329 

330 def __str__(self): 

331 parts = [] 

332 

333 # Epoch 

334 if self._version.epoch != 0: 

335 parts.append("{0}!".format(self._version.epoch)) 

336 

337 # Release segment 

338 parts.append(".".join(str(x) for x in self._version.release)) 

339 

340 # Pre-release 

341 if self._version.pre is not None: 

342 parts.append("".join(str(x) for x in self._version.pre)) 

343 

344 # Post-release 

345 if self._version.post is not None: 

346 parts.append(".post{0}".format(self._version.post[1])) 

347 

348 # Development release 

349 if self._version.dev is not None: 

350 parts.append(".dev{0}".format(self._version.dev[1])) 

351 

352 # Local version segment 

353 if self._version.local is not None: 

354 parts.append( 

355 "+{0}".format(".".join(str(x) for x in self._version.local)) 

356 ) 

357 

358 return "".join(parts) 

359 

360 @property 

361 def public(self): 

362 return str(self).split("+", 1)[0] 

363 

364 @property 

365 def base_version(self): 

366 parts = [] 

367 

368 # Epoch 

369 if self._version.epoch != 0: 

370 parts.append("{0}!".format(self._version.epoch)) 

371 

372 # Release segment 

373 parts.append(".".join(str(x) for x in self._version.release)) 

374 

375 return "".join(parts) 

376 

377 @property 

378 def local(self): 

379 version_string = str(self) 

380 if "+" in version_string: 

381 return version_string.split("+", 1)[1] 

382 

383 @property 

384 def is_prerelease(self): 

385 return bool(self._version.dev or self._version.pre) 

386 

387 @property 

388 def is_postrelease(self): 

389 return bool(self._version.post) 

390 

391 

392def _parse_letter_version(letter, number): 

393 if letter: 

394 # We assume there is an implicit 0 in a pre-release if there is 

395 # no numeral associated with it. 

396 if number is None: 

397 number = 0 

398 

399 # We normalize any letters to their lower-case form 

400 letter = letter.lower() 

401 

402 # We consider some words to be alternate spellings of other words and 

403 # in those cases we want to normalize the spellings to our preferred 

404 # spelling. 

405 if letter == "alpha": 

406 letter = "a" 

407 elif letter == "beta": 

408 letter = "b" 

409 elif letter in ["c", "pre", "preview"]: 

410 letter = "rc" 

411 elif letter in ["rev", "r"]: 

412 letter = "post" 

413 

414 return letter, int(number) 

415 if not letter and number: 

416 # We assume that if we are given a number but not given a letter, 

417 # then this is using the implicit post release syntax (e.g., 1.0-1) 

418 letter = "post" 

419 

420 return letter, int(number) 

421 

422 

423_local_version_seperators = re.compile(r"[\._-]") 

424 

425 

426def _parse_local_version(local): 

427 """ 

428 Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). 

429 """ 

430 if local is not None: 

431 return tuple( 

432 part.lower() if not part.isdigit() else int(part) 

433 for part in _local_version_seperators.split(local) 

434 ) 

435 

436 

437def _cmpkey(epoch, release, pre, post, dev, local): 

438 # When we compare a release version, we want to compare it with all of the 

439 # trailing zeros removed. So we'll use a reverse the list, drop all the now 

440 # leading zeros until we come to something non-zero, then take the rest, 

441 # re-reverse it back into the correct order, and make it a tuple and use 

442 # that for our sorting key. 

443 release = tuple( 

444 reversed(list( 

445 itertools.dropwhile( 

446 lambda x: x == 0, 

447 reversed(release), 

448 ) 

449 )) 

450 ) 

451 

452 # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. 

453 # We'll do this by abusing the pre-segment, but we _only_ want to do this 

454 # if there is no pre- or a post-segment. If we have one of those, then 

455 # the normal sorting rules will handle this case correctly. 

456 if pre is None and post is None and dev is not None: 

457 pre = -Infinity 

458 # Versions without a pre-release (except as noted above) should sort after 

459 # those with one. 

460 elif pre is None: 

461 pre = Infinity 

462 

463 # Versions without a post-segment should sort before those with one. 

464 if post is None: 

465 post = -Infinity 

466 

467 # Versions without a development segment should sort after those with one. 

468 if dev is None: 

469 dev = Infinity 

470 

471 if local is None: 

472 # Versions without a local segment should sort before those with one. 

473 local = -Infinity 

474 else: 

475 # Versions with a local segment need that segment parsed to implement 

476 # the sorting rules in PEP440. 

477 # - Alphanumeric segments sort before numeric segments 

478 # - Alphanumeric segments sort lexicographically 

479 # - Numeric segments sort numerically 

480 # - Shorter versions sort before longer versions when the prefixes 

481 # match exactly 

482 local = tuple( 

483 (i, "") if isinstance(i, int) else (-Infinity, i) 

484 for i in local 

485 ) 

486 

487 return epoch, release, pre, post, dev, local