Skip to content

Commit 90e8e04

Browse files
authored
MAINT: update vendored version util from packaging (#59558)
* MAINT: update vendored version util from packaging * fix docstring * fix docstring * skip docstring validation * ignore * fix validation ignore * remove docstring * rollback * add comments
1 parent fe42b3b commit 90e8e04

File tree

1 file changed

+51
-187
lines changed

1 file changed

+51
-187
lines changed

pandas/util/version/__init__.py

+51-187
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
1-
# Vendored from https://github.com/pypa/packaging/blob/main/packaging/_structures.py
2-
# and https://github.com/pypa/packaging/blob/main/packaging/_structures.py
3-
# changeset ae891fd74d6dd4c6063bb04f2faeadaac6fc6313
4-
# 04/30/2021
1+
# Vendored from https://github.com/pypa/packaging/blob/main/src/packaging/_structures.py
2+
# and https://github.com/pypa/packaging/blob/main/src/packaging/version.py
3+
# changeset 24e5350b2ff3c5c7a36676c2af5f2cb39fd1baf8
54

65
# This file is dual licensed under the terms of the Apache License, Version
76
# 2.0, and the BSD License. Licence at LICENSES/PACKAGING_LICENSE
87
from __future__ import annotations
98

10-
import collections
11-
from collections.abc import (
12-
Callable,
13-
Iterator,
14-
)
9+
from collections.abc import Callable
1510
import itertools
1611
import re
1712
from typing import (
13+
Any,
14+
NamedTuple,
1815
SupportsInt,
19-
Tuple,
2016
Union,
2117
)
22-
import warnings
2318

24-
__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"]
19+
__all__ = ["VERSION_PATTERN", "InvalidVersion", "Version", "parse"]
2520

2621

2722
class InfinityType:
@@ -40,9 +35,6 @@ def __le__(self, other: object) -> bool:
4035
def __eq__(self, other: object) -> bool:
4136
return isinstance(other, type(self))
4237

43-
def __ne__(self, other: object) -> bool:
44-
return not isinstance(other, type(self))
45-
4638
def __gt__(self, other: object) -> bool:
4739
return True
4840

@@ -72,9 +64,6 @@ def __le__(self, other: object) -> bool:
7264
def __eq__(self, other: object) -> bool:
7365
return isinstance(other, type(self))
7466

75-
def __ne__(self, other: object) -> bool:
76-
return not isinstance(other, type(self))
77-
7867
def __gt__(self, other: object) -> bool:
7968
return False
8069

@@ -88,45 +77,39 @@ def __neg__(self: object) -> InfinityType:
8877
NegativeInfinity = NegativeInfinityType()
8978

9079

91-
InfiniteTypes = Union[InfinityType, NegativeInfinityType]
92-
PrePostDevType = Union[InfiniteTypes, tuple[str, int]]
93-
SubLocalType = Union[InfiniteTypes, int, str]
94-
LocalType = Union[
80+
LocalType = tuple[Union[int, str], ...]
81+
82+
CmpPrePostDevType = Union[InfinityType, NegativeInfinityType, tuple[str, int]]
83+
CmpLocalType = Union[
9584
NegativeInfinityType,
96-
tuple[
97-
Union[
98-
SubLocalType,
99-
tuple[SubLocalType, str],
100-
tuple[NegativeInfinityType, SubLocalType],
101-
],
102-
...,
103-
],
85+
tuple[Union[tuple[int, str], tuple[NegativeInfinityType, Union[int, str]]], ...],
10486
]
10587
CmpKey = tuple[
106-
int, tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType
107-
]
108-
LegacyCmpKey = tuple[int, tuple[str, ...]]
109-
VersionComparisonMethod = Callable[
110-
[Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool
88+
int,
89+
tuple[int, ...],
90+
CmpPrePostDevType,
91+
CmpPrePostDevType,
92+
CmpPrePostDevType,
93+
CmpLocalType,
11194
]
95+
VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool]
11296

113-
_Version = collections.namedtuple(
114-
"_Version", ["epoch", "release", "dev", "pre", "post", "local"]
115-
)
11697

98+
class _Version(NamedTuple):
99+
epoch: int
100+
release: tuple[int, ...]
101+
dev: tuple[str, int] | None
102+
pre: tuple[str, int] | None
103+
post: tuple[str, int] | None
104+
local: LocalType | None
117105

118-
def parse(version: str) -> LegacyVersion | Version:
119-
"""
120-
Parse the given version string and return either a :class:`Version` object
121-
or a :class:`LegacyVersion` object depending on if the given version is
122-
a valid PEP 440 version or a legacy version.
123-
"""
124-
try:
125-
return Version(version)
126-
except InvalidVersion:
127-
return LegacyVersion(version)
106+
107+
def parse(version: str) -> Version:
108+
return Version(version)
128109

129110

111+
# The docstring is from an older version of the packaging library to avoid
112+
# errors in the docstring validation.
130113
class InvalidVersion(ValueError):
131114
"""
132115
An invalid version was found, users should refer to PEP 440.
@@ -140,7 +123,7 @@ class InvalidVersion(ValueError):
140123

141124

142125
class _BaseVersion:
143-
_key: CmpKey | LegacyCmpKey
126+
_key: tuple[Any, ...]
144127

145128
def __hash__(self) -> int:
146129
return hash(self._key)
@@ -185,132 +168,16 @@ def __ne__(self, other: object) -> bool:
185168
return self._key != other._key
186169

187170

188-
class LegacyVersion(_BaseVersion):
189-
def __init__(self, version: str) -> None:
190-
self._version = str(version)
191-
self._key = _legacy_cmpkey(self._version)
192-
193-
warnings.warn(
194-
"Creating a LegacyVersion has been deprecated and will be "
195-
"removed in the next major release.",
196-
DeprecationWarning,
197-
)
198-
199-
def __str__(self) -> str:
200-
return self._version
201-
202-
def __repr__(self) -> str:
203-
return f"<LegacyVersion('{self}')>"
204-
205-
@property
206-
def public(self) -> str:
207-
return self._version
208-
209-
@property
210-
def base_version(self) -> str:
211-
return self._version
212-
213-
@property
214-
def epoch(self) -> int:
215-
return -1
216-
217-
@property
218-
def release(self) -> None:
219-
return None
220-
221-
@property
222-
def pre(self) -> None:
223-
return None
224-
225-
@property
226-
def post(self) -> None:
227-
return None
228-
229-
@property
230-
def dev(self) -> None:
231-
return None
232-
233-
@property
234-
def local(self) -> None:
235-
return None
236-
237-
@property
238-
def is_prerelease(self) -> bool:
239-
return False
240-
241-
@property
242-
def is_postrelease(self) -> bool:
243-
return False
244-
245-
@property
246-
def is_devrelease(self) -> bool:
247-
return False
248-
249-
250-
_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE)
251-
252-
_legacy_version_replacement_map = {
253-
"pre": "c",
254-
"preview": "c",
255-
"-": "final-",
256-
"rc": "c",
257-
"dev": "@",
258-
}
259-
260-
261-
def _parse_version_parts(s: str) -> Iterator[str]:
262-
for part in _legacy_version_component_re.split(s):
263-
mapped_part = _legacy_version_replacement_map.get(part, part)
264-
265-
if not mapped_part or mapped_part == ".":
266-
continue
267-
268-
if mapped_part[:1] in "0123456789":
269-
# pad for numeric comparison
270-
yield mapped_part.zfill(8)
271-
else:
272-
yield "*" + mapped_part
273-
274-
# ensure that alpha/beta/candidate are before final
275-
yield "*final"
276-
277-
278-
def _legacy_cmpkey(version: str) -> LegacyCmpKey:
279-
# We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch
280-
# greater than or equal to 0. This will effectively put the LegacyVersion,
281-
# which uses the defacto standard originally implemented by setuptools,
282-
# as before all PEP 440 versions.
283-
epoch = -1
284-
285-
# This scheme is taken from pkg_resources.parse_version setuptools prior to
286-
# it's adoption of the packaging library.
287-
parts: list[str] = []
288-
for part in _parse_version_parts(version.lower()):
289-
if part.startswith("*"):
290-
# remove "-" before a prerelease tag
291-
if part < "*final":
292-
while parts and parts[-1] == "*final-":
293-
parts.pop()
294-
295-
# remove trailing zeros from each series of numeric parts
296-
while parts and parts[-1] == "00000000":
297-
parts.pop()
298-
299-
parts.append(part)
300-
301-
return epoch, tuple(parts)
302-
303-
304171
# Deliberately not anchored to the start and end of the string, to make it
305172
# easier for 3rd party code to reuse
306-
VERSION_PATTERN = r"""
173+
_VERSION_PATTERN = r"""
307174
v?
308175
(?:
309176
(?:(?P<epoch>[0-9]+)!)? # epoch
310177
(?P<release>[0-9]+(?:\.[0-9]+)*) # release segment
311178
(?P<pre> # pre-release
312179
[-_\.]?
313-
(?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview))
180+
(?P<pre_l>alpha|a|beta|b|preview|pre|c|rc)
314181
[-_\.]?
315182
(?P<pre_n>[0-9]+)?
316183
)?
@@ -334,9 +201,12 @@ def _legacy_cmpkey(version: str) -> LegacyCmpKey:
334201
(?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
335202
"""
336203

204+
VERSION_PATTERN = _VERSION_PATTERN
205+
337206

338207
class Version(_BaseVersion):
339208
_regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
209+
_key: CmpKey
340210

341211
def __init__(self, version: str) -> None:
342212
# Validate the version and parse it into pieces
@@ -377,11 +247,11 @@ def __str__(self) -> str:
377247
parts.append(f"{self.epoch}!")
378248

379249
# Release segment
380-
parts.append(".".join([str(x) for x in self.release]))
250+
parts.append(".".join(str(x) for x in self.release))
381251

382252
# Pre-release
383253
if self.pre is not None:
384-
parts.append("".join([str(x) for x in self.pre]))
254+
parts.append("".join(str(x) for x in self.pre))
385255

386256
# Post-release
387257
if self.post is not None:
@@ -399,18 +269,15 @@ def __str__(self) -> str:
399269

400270
@property
401271
def epoch(self) -> int:
402-
_epoch: int = self._version.epoch
403-
return _epoch
272+
return self._version.epoch
404273

405274
@property
406275
def release(self) -> tuple[int, ...]:
407-
_release: tuple[int, ...] = self._version.release
408-
return _release
276+
return self._version.release
409277

410278
@property
411279
def pre(self) -> tuple[str, int] | None:
412-
_pre: tuple[str, int] | None = self._version.pre
413-
return _pre
280+
return self._version.pre
414281

415282
@property
416283
def post(self) -> int | None:
@@ -423,7 +290,7 @@ def dev(self) -> int | None:
423290
@property
424291
def local(self) -> str | None:
425292
if self._version.local:
426-
return ".".join([str(x) for x in self._version.local])
293+
return ".".join(str(x) for x in self._version.local)
427294
else:
428295
return None
429296

@@ -440,7 +307,7 @@ def base_version(self) -> str:
440307
parts.append(f"{self.epoch}!")
441308

442309
# Release segment
443-
parts.append(".".join([str(x) for x in self.release]))
310+
parts.append(".".join(str(x) for x in self.release))
444311

445312
return "".join(parts)
446313

@@ -470,7 +337,7 @@ def micro(self) -> int:
470337

471338

472339
def _parse_letter_version(
473-
letter: str, number: str | bytes | SupportsInt
340+
letter: str | None, number: str | bytes | SupportsInt | None
474341
) -> tuple[str, int] | None:
475342
if letter:
476343
# We consider there to be an implicit 0 in a pre-release if there is
@@ -507,10 +374,7 @@ def _parse_letter_version(
507374
_local_version_separators = re.compile(r"[\._-]")
508375

509376

510-
def _parse_local_version(local: str) -> LocalType | None:
511-
"""
512-
Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
513-
"""
377+
def _parse_local_version(local: str | None) -> LocalType | None:
514378
if local is not None:
515379
return tuple(
516380
part.lower() if not part.isdigit() else int(part)
@@ -525,7 +389,7 @@ def _cmpkey(
525389
pre: tuple[str, int] | None,
526390
post: tuple[str, int] | None,
527391
dev: tuple[str, int] | None,
528-
local: tuple[SubLocalType] | None,
392+
local: LocalType | None,
529393
) -> CmpKey:
530394
# When we compare a release version, we want to compare it with all of the
531395
# trailing zeros removed. So we'll use a reverse the list, drop all the now
@@ -541,7 +405,7 @@ def _cmpkey(
541405
# if there is not a pre or a post segment. If we have one of those then
542406
# the normal sorting rules will handle this case correctly.
543407
if pre is None and post is None and dev is not None:
544-
_pre: PrePostDevType = NegativeInfinity
408+
_pre: CmpPrePostDevType = NegativeInfinity
545409
# Versions without a pre-release (except as noted above) should sort after
546410
# those with one.
547411
elif pre is None:
@@ -551,21 +415,21 @@ def _cmpkey(
551415

552416
# Versions without a post segment should sort before those with one.
553417
if post is None:
554-
_post: PrePostDevType = NegativeInfinity
418+
_post: CmpPrePostDevType = NegativeInfinity
555419

556420
else:
557421
_post = post
558422

559423
# Versions without a development segment should sort after those with one.
560424
if dev is None:
561-
_dev: PrePostDevType = Infinity
425+
_dev: CmpPrePostDevType = Infinity
562426

563427
else:
564428
_dev = dev
565429

566430
if local is None:
567431
# Versions without a local segment should sort before those with one.
568-
_local: LocalType = NegativeInfinity
432+
_local: CmpLocalType = NegativeInfinity
569433
else:
570434
# Versions with a local segment need that segment parsed to implement
571435
# the sorting rules in PEP440.

0 commit comments

Comments
 (0)