pip 25.1__py3-none-any.whl → 25.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pip/__init__.py +3 -3
- pip/_internal/__init__.py +2 -2
- pip/_internal/build_env.py +118 -94
- pip/_internal/cache.py +16 -14
- pip/_internal/cli/autocompletion.py +13 -4
- pip/_internal/cli/base_command.py +18 -7
- pip/_internal/cli/cmdoptions.py +14 -9
- pip/_internal/cli/command_context.py +4 -3
- pip/_internal/cli/index_command.py +11 -9
- pip/_internal/cli/main.py +3 -2
- pip/_internal/cli/main_parser.py +4 -3
- pip/_internal/cli/parser.py +26 -22
- pip/_internal/cli/progress_bars.py +19 -12
- pip/_internal/cli/req_command.py +16 -12
- pip/_internal/cli/spinners.py +81 -5
- pip/_internal/commands/__init__.py +5 -3
- pip/_internal/commands/cache.py +18 -15
- pip/_internal/commands/check.py +1 -2
- pip/_internal/commands/completion.py +1 -2
- pip/_internal/commands/configuration.py +26 -18
- pip/_internal/commands/debug.py +8 -6
- pip/_internal/commands/download.py +2 -3
- pip/_internal/commands/freeze.py +2 -3
- pip/_internal/commands/hash.py +1 -2
- pip/_internal/commands/help.py +1 -2
- pip/_internal/commands/index.py +15 -9
- pip/_internal/commands/inspect.py +4 -4
- pip/_internal/commands/install.py +45 -40
- pip/_internal/commands/list.py +35 -26
- pip/_internal/commands/lock.py +1 -2
- pip/_internal/commands/search.py +14 -12
- pip/_internal/commands/show.py +14 -11
- pip/_internal/commands/uninstall.py +1 -2
- pip/_internal/commands/wheel.py +2 -3
- pip/_internal/configuration.py +39 -25
- pip/_internal/distributions/base.py +6 -4
- pip/_internal/distributions/installed.py +8 -4
- pip/_internal/distributions/sdist.py +20 -13
- pip/_internal/distributions/wheel.py +6 -4
- pip/_internal/exceptions.py +58 -39
- pip/_internal/index/collector.py +24 -29
- pip/_internal/index/package_finder.py +70 -61
- pip/_internal/index/sources.py +17 -14
- pip/_internal/locations/__init__.py +18 -16
- pip/_internal/locations/_distutils.py +12 -11
- pip/_internal/locations/_sysconfig.py +5 -4
- pip/_internal/locations/base.py +4 -3
- pip/_internal/main.py +2 -2
- pip/_internal/metadata/__init__.py +8 -6
- pip/_internal/metadata/_json.py +5 -4
- pip/_internal/metadata/base.py +22 -27
- pip/_internal/metadata/importlib/_compat.py +6 -4
- pip/_internal/metadata/importlib/_dists.py +12 -17
- pip/_internal/metadata/importlib/_envs.py +9 -6
- pip/_internal/metadata/pkg_resources.py +11 -14
- pip/_internal/models/direct_url.py +24 -21
- pip/_internal/models/format_control.py +5 -5
- pip/_internal/models/installation_report.py +4 -3
- pip/_internal/models/link.py +39 -34
- pip/_internal/models/pylock.py +27 -22
- pip/_internal/models/search_scope.py +6 -7
- pip/_internal/models/selection_prefs.py +3 -3
- pip/_internal/models/target_python.py +10 -9
- pip/_internal/models/wheel.py +7 -5
- pip/_internal/network/auth.py +20 -22
- pip/_internal/network/cache.py +22 -6
- pip/_internal/network/download.py +169 -141
- pip/_internal/network/lazy_wheel.py +10 -7
- pip/_internal/network/session.py +32 -27
- pip/_internal/network/utils.py +2 -2
- pip/_internal/network/xmlrpc.py +2 -2
- pip/_internal/operations/build/build_tracker.py +10 -8
- pip/_internal/operations/build/wheel.py +3 -2
- pip/_internal/operations/build/wheel_editable.py +3 -2
- pip/_internal/operations/build/wheel_legacy.py +9 -8
- pip/_internal/operations/check.py +21 -26
- pip/_internal/operations/freeze.py +12 -9
- pip/_internal/operations/install/editable_legacy.py +5 -3
- pip/_internal/operations/install/wheel.py +53 -44
- pip/_internal/operations/prepare.py +35 -30
- pip/_internal/pyproject.py +7 -10
- pip/_internal/req/__init__.py +12 -10
- pip/_internal/req/constructors.py +33 -31
- pip/_internal/req/req_dependency_group.py +9 -8
- pip/_internal/req/req_file.py +32 -35
- pip/_internal/req/req_install.py +37 -34
- pip/_internal/req/req_set.py +4 -5
- pip/_internal/req/req_uninstall.py +20 -17
- pip/_internal/resolution/base.py +3 -3
- pip/_internal/resolution/legacy/resolver.py +21 -20
- pip/_internal/resolution/resolvelib/base.py +16 -13
- pip/_internal/resolution/resolvelib/candidates.py +29 -26
- pip/_internal/resolution/resolvelib/factory.py +41 -50
- pip/_internal/resolution/resolvelib/found_candidates.py +11 -9
- pip/_internal/resolution/resolvelib/provider.py +15 -20
- pip/_internal/resolution/resolvelib/reporter.py +5 -3
- pip/_internal/resolution/resolvelib/requirements.py +8 -6
- pip/_internal/resolution/resolvelib/resolver.py +39 -23
- pip/_internal/self_outdated_check.py +8 -6
- pip/_internal/utils/appdirs.py +1 -2
- pip/_internal/utils/compat.py +7 -1
- pip/_internal/utils/compatibility_tags.py +17 -16
- pip/_internal/utils/deprecation.py +11 -9
- pip/_internal/utils/direct_url_helpers.py +2 -2
- pip/_internal/utils/egg_link.py +6 -5
- pip/_internal/utils/entrypoints.py +3 -2
- pip/_internal/utils/filesystem.py +8 -5
- pip/_internal/utils/filetypes.py +4 -6
- pip/_internal/utils/glibc.py +6 -5
- pip/_internal/utils/hashes.py +9 -6
- pip/_internal/utils/logging.py +8 -5
- pip/_internal/utils/misc.py +54 -44
- pip/_internal/utils/packaging.py +3 -2
- pip/_internal/utils/retry.py +7 -4
- pip/_internal/utils/setuptools_build.py +12 -10
- pip/_internal/utils/subprocess.py +20 -17
- pip/_internal/utils/temp_dir.py +10 -12
- pip/_internal/utils/unpacking.py +6 -4
- pip/_internal/utils/urls.py +1 -1
- pip/_internal/utils/virtualenv.py +3 -2
- pip/_internal/utils/wheel.py +3 -4
- pip/_internal/vcs/bazaar.py +26 -8
- pip/_internal/vcs/git.py +59 -24
- pip/_internal/vcs/mercurial.py +34 -11
- pip/_internal/vcs/subversion.py +27 -16
- pip/_internal/vcs/versioncontrol.py +56 -51
- pip/_internal/wheel_builder.py +14 -12
- pip/_vendor/cachecontrol/__init__.py +1 -1
- pip/_vendor/certifi/__init__.py +1 -1
- pip/_vendor/certifi/cacert.pem +102 -221
- pip/_vendor/certifi/core.py +1 -32
- pip/_vendor/dependency_groups/_implementation.py +7 -11
- pip/_vendor/distlib/__init__.py +2 -2
- pip/_vendor/distlib/scripts.py +1 -1
- pip/_vendor/msgpack/__init__.py +2 -2
- pip/_vendor/pkg_resources/__init__.py +1 -1
- pip/_vendor/platformdirs/version.py +2 -2
- pip/_vendor/pygments/__init__.py +1 -1
- pip/_vendor/requests/__version__.py +2 -2
- pip/_vendor/requests/compat.py +12 -0
- pip/_vendor/requests/models.py +3 -1
- pip/_vendor/requests/utils.py +6 -16
- pip/_vendor/resolvelib/__init__.py +3 -3
- pip/_vendor/resolvelib/reporters.py +1 -1
- pip/_vendor/resolvelib/resolvers/__init__.py +4 -4
- pip/_vendor/resolvelib/resolvers/resolution.py +91 -10
- pip/_vendor/rich/__main__.py +12 -40
- pip/_vendor/rich/_inspect.py +1 -1
- pip/_vendor/rich/_ratio.py +1 -7
- pip/_vendor/rich/align.py +1 -7
- pip/_vendor/rich/box.py +1 -7
- pip/_vendor/rich/console.py +25 -20
- pip/_vendor/rich/control.py +1 -7
- pip/_vendor/rich/diagnose.py +1 -0
- pip/_vendor/rich/emoji.py +1 -6
- pip/_vendor/rich/live.py +32 -7
- pip/_vendor/rich/live_render.py +1 -7
- pip/_vendor/rich/logging.py +1 -1
- pip/_vendor/rich/panel.py +3 -4
- pip/_vendor/rich/progress.py +15 -15
- pip/_vendor/rich/spinner.py +7 -13
- pip/_vendor/rich/syntax.py +24 -5
- pip/_vendor/rich/traceback.py +32 -17
- pip/_vendor/truststore/_api.py +1 -1
- pip/_vendor/vendor.txt +10 -11
- {pip-25.1.dist-info → pip-25.2.dist-info}/METADATA +26 -4
- {pip-25.1.dist-info → pip-25.2.dist-info}/RECORD +194 -181
- {pip-25.1.dist-info → pip-25.2.dist-info}/WHEEL +1 -1
- {pip-25.1.dist-info → pip-25.2.dist-info}/licenses/AUTHORS.txt +12 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER +3 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
- pip/_vendor/distlib/database.py +0 -1329
- pip/_vendor/distlib/index.py +0 -508
- pip/_vendor/distlib/locators.py +0 -1295
- pip/_vendor/distlib/manifest.py +0 -384
- pip/_vendor/distlib/markers.py +0 -162
- pip/_vendor/distlib/metadata.py +0 -1031
- pip/_vendor/distlib/version.py +0 -750
- pip/_vendor/distlib/wheel.py +0 -1100
- pip/_vendor/typing_extensions.py +0 -4584
- {pip-25.1.dist-info → pip-25.2.dist-info}/entry_points.txt +0 -0
- {pip-25.1.dist-info → pip-25.2.dist-info}/licenses/LICENSE.txt +0 -0
- {pip-25.1.dist-info → pip-25.2.dist-info}/top_level.txt +0 -0
pip/_vendor/distlib/version.py
DELETED
|
@@ -1,750 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2012-2023 The Python Software Foundation.
|
|
4
|
-
# See LICENSE.txt and CONTRIBUTORS.txt.
|
|
5
|
-
#
|
|
6
|
-
"""
|
|
7
|
-
Implementation of a flexible versioning scheme providing support for PEP-440,
|
|
8
|
-
setuptools-compatible and semantic versioning.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import logging
|
|
12
|
-
import re
|
|
13
|
-
|
|
14
|
-
from .compat import string_types
|
|
15
|
-
from .util import parse_requirement
|
|
16
|
-
|
|
17
|
-
__all__ = ['NormalizedVersion', 'NormalizedMatcher',
|
|
18
|
-
'LegacyVersion', 'LegacyMatcher',
|
|
19
|
-
'SemanticVersion', 'SemanticMatcher',
|
|
20
|
-
'UnsupportedVersionError', 'get_scheme']
|
|
21
|
-
|
|
22
|
-
logger = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class UnsupportedVersionError(ValueError):
|
|
26
|
-
"""This is an unsupported version."""
|
|
27
|
-
pass
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class Version(object):
|
|
31
|
-
def __init__(self, s):
|
|
32
|
-
self._string = s = s.strip()
|
|
33
|
-
self._parts = parts = self.parse(s)
|
|
34
|
-
assert isinstance(parts, tuple)
|
|
35
|
-
assert len(parts) > 0
|
|
36
|
-
|
|
37
|
-
def parse(self, s):
|
|
38
|
-
raise NotImplementedError('please implement in a subclass')
|
|
39
|
-
|
|
40
|
-
def _check_compatible(self, other):
|
|
41
|
-
if type(self) != type(other):
|
|
42
|
-
raise TypeError('cannot compare %r and %r' % (self, other))
|
|
43
|
-
|
|
44
|
-
def __eq__(self, other):
|
|
45
|
-
self._check_compatible(other)
|
|
46
|
-
return self._parts == other._parts
|
|
47
|
-
|
|
48
|
-
def __ne__(self, other):
|
|
49
|
-
return not self.__eq__(other)
|
|
50
|
-
|
|
51
|
-
def __lt__(self, other):
|
|
52
|
-
self._check_compatible(other)
|
|
53
|
-
return self._parts < other._parts
|
|
54
|
-
|
|
55
|
-
def __gt__(self, other):
|
|
56
|
-
return not (self.__lt__(other) or self.__eq__(other))
|
|
57
|
-
|
|
58
|
-
def __le__(self, other):
|
|
59
|
-
return self.__lt__(other) or self.__eq__(other)
|
|
60
|
-
|
|
61
|
-
def __ge__(self, other):
|
|
62
|
-
return self.__gt__(other) or self.__eq__(other)
|
|
63
|
-
|
|
64
|
-
# See http://docs.python.org/reference/datamodel#object.__hash__
|
|
65
|
-
def __hash__(self):
|
|
66
|
-
return hash(self._parts)
|
|
67
|
-
|
|
68
|
-
def __repr__(self):
|
|
69
|
-
return "%s('%s')" % (self.__class__.__name__, self._string)
|
|
70
|
-
|
|
71
|
-
def __str__(self):
|
|
72
|
-
return self._string
|
|
73
|
-
|
|
74
|
-
@property
|
|
75
|
-
def is_prerelease(self):
|
|
76
|
-
raise NotImplementedError('Please implement in subclasses.')
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
class Matcher(object):
|
|
80
|
-
version_class = None
|
|
81
|
-
|
|
82
|
-
# value is either a callable or the name of a method
|
|
83
|
-
_operators = {
|
|
84
|
-
'<': lambda v, c, p: v < c,
|
|
85
|
-
'>': lambda v, c, p: v > c,
|
|
86
|
-
'<=': lambda v, c, p: v == c or v < c,
|
|
87
|
-
'>=': lambda v, c, p: v == c or v > c,
|
|
88
|
-
'==': lambda v, c, p: v == c,
|
|
89
|
-
'===': lambda v, c, p: v == c,
|
|
90
|
-
# by default, compatible => >=.
|
|
91
|
-
'~=': lambda v, c, p: v == c or v > c,
|
|
92
|
-
'!=': lambda v, c, p: v != c,
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
# this is a method only to support alternative implementations
|
|
96
|
-
# via overriding
|
|
97
|
-
def parse_requirement(self, s):
|
|
98
|
-
return parse_requirement(s)
|
|
99
|
-
|
|
100
|
-
def __init__(self, s):
|
|
101
|
-
if self.version_class is None:
|
|
102
|
-
raise ValueError('Please specify a version class')
|
|
103
|
-
self._string = s = s.strip()
|
|
104
|
-
r = self.parse_requirement(s)
|
|
105
|
-
if not r:
|
|
106
|
-
raise ValueError('Not valid: %r' % s)
|
|
107
|
-
self.name = r.name
|
|
108
|
-
self.key = self.name.lower() # for case-insensitive comparisons
|
|
109
|
-
clist = []
|
|
110
|
-
if r.constraints:
|
|
111
|
-
# import pdb; pdb.set_trace()
|
|
112
|
-
for op, s in r.constraints:
|
|
113
|
-
if s.endswith('.*'):
|
|
114
|
-
if op not in ('==', '!='):
|
|
115
|
-
raise ValueError('\'.*\' not allowed for '
|
|
116
|
-
'%r constraints' % op)
|
|
117
|
-
# Could be a partial version (e.g. for '2.*') which
|
|
118
|
-
# won't parse as a version, so keep it as a string
|
|
119
|
-
vn, prefix = s[:-2], True
|
|
120
|
-
# Just to check that vn is a valid version
|
|
121
|
-
self.version_class(vn)
|
|
122
|
-
else:
|
|
123
|
-
# Should parse as a version, so we can create an
|
|
124
|
-
# instance for the comparison
|
|
125
|
-
vn, prefix = self.version_class(s), False
|
|
126
|
-
clist.append((op, vn, prefix))
|
|
127
|
-
self._parts = tuple(clist)
|
|
128
|
-
|
|
129
|
-
def match(self, version):
|
|
130
|
-
"""
|
|
131
|
-
Check if the provided version matches the constraints.
|
|
132
|
-
|
|
133
|
-
:param version: The version to match against this instance.
|
|
134
|
-
:type version: String or :class:`Version` instance.
|
|
135
|
-
"""
|
|
136
|
-
if isinstance(version, string_types):
|
|
137
|
-
version = self.version_class(version)
|
|
138
|
-
for operator, constraint, prefix in self._parts:
|
|
139
|
-
f = self._operators.get(operator)
|
|
140
|
-
if isinstance(f, string_types):
|
|
141
|
-
f = getattr(self, f)
|
|
142
|
-
if not f:
|
|
143
|
-
msg = ('%r not implemented '
|
|
144
|
-
'for %s' % (operator, self.__class__.__name__))
|
|
145
|
-
raise NotImplementedError(msg)
|
|
146
|
-
if not f(version, constraint, prefix):
|
|
147
|
-
return False
|
|
148
|
-
return True
|
|
149
|
-
|
|
150
|
-
@property
|
|
151
|
-
def exact_version(self):
|
|
152
|
-
result = None
|
|
153
|
-
if len(self._parts) == 1 and self._parts[0][0] in ('==', '==='):
|
|
154
|
-
result = self._parts[0][1]
|
|
155
|
-
return result
|
|
156
|
-
|
|
157
|
-
def _check_compatible(self, other):
|
|
158
|
-
if type(self) != type(other) or self.name != other.name:
|
|
159
|
-
raise TypeError('cannot compare %s and %s' % (self, other))
|
|
160
|
-
|
|
161
|
-
def __eq__(self, other):
|
|
162
|
-
self._check_compatible(other)
|
|
163
|
-
return self.key == other.key and self._parts == other._parts
|
|
164
|
-
|
|
165
|
-
def __ne__(self, other):
|
|
166
|
-
return not self.__eq__(other)
|
|
167
|
-
|
|
168
|
-
# See http://docs.python.org/reference/datamodel#object.__hash__
|
|
169
|
-
def __hash__(self):
|
|
170
|
-
return hash(self.key) + hash(self._parts)
|
|
171
|
-
|
|
172
|
-
def __repr__(self):
|
|
173
|
-
return "%s(%r)" % (self.__class__.__name__, self._string)
|
|
174
|
-
|
|
175
|
-
def __str__(self):
|
|
176
|
-
return self._string
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
PEP440_VERSION_RE = re.compile(r'^v?(\d+!)?(\d+(\.\d+)*)((a|alpha|b|beta|c|rc|pre|preview)(\d+)?)?'
|
|
180
|
-
r'(\.(post|r|rev)(\d+)?)?([._-]?(dev)(\d+)?)?'
|
|
181
|
-
r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$', re.I)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def _pep_440_key(s):
|
|
185
|
-
s = s.strip()
|
|
186
|
-
m = PEP440_VERSION_RE.match(s)
|
|
187
|
-
if not m:
|
|
188
|
-
raise UnsupportedVersionError('Not a valid version: %s' % s)
|
|
189
|
-
groups = m.groups()
|
|
190
|
-
nums = tuple(int(v) for v in groups[1].split('.'))
|
|
191
|
-
while len(nums) > 1 and nums[-1] == 0:
|
|
192
|
-
nums = nums[:-1]
|
|
193
|
-
|
|
194
|
-
if not groups[0]:
|
|
195
|
-
epoch = 0
|
|
196
|
-
else:
|
|
197
|
-
epoch = int(groups[0][:-1])
|
|
198
|
-
pre = groups[4:6]
|
|
199
|
-
post = groups[7:9]
|
|
200
|
-
dev = groups[10:12]
|
|
201
|
-
local = groups[13]
|
|
202
|
-
if pre == (None, None):
|
|
203
|
-
pre = ()
|
|
204
|
-
else:
|
|
205
|
-
if pre[1] is None:
|
|
206
|
-
pre = pre[0], 0
|
|
207
|
-
else:
|
|
208
|
-
pre = pre[0], int(pre[1])
|
|
209
|
-
if post == (None, None):
|
|
210
|
-
post = ()
|
|
211
|
-
else:
|
|
212
|
-
if post[1] is None:
|
|
213
|
-
post = post[0], 0
|
|
214
|
-
else:
|
|
215
|
-
post = post[0], int(post[1])
|
|
216
|
-
if dev == (None, None):
|
|
217
|
-
dev = ()
|
|
218
|
-
else:
|
|
219
|
-
if dev[1] is None:
|
|
220
|
-
dev = dev[0], 0
|
|
221
|
-
else:
|
|
222
|
-
dev = dev[0], int(dev[1])
|
|
223
|
-
if local is None:
|
|
224
|
-
local = ()
|
|
225
|
-
else:
|
|
226
|
-
parts = []
|
|
227
|
-
for part in local.split('.'):
|
|
228
|
-
# to ensure that numeric compares as > lexicographic, avoid
|
|
229
|
-
# comparing them directly, but encode a tuple which ensures
|
|
230
|
-
# correct sorting
|
|
231
|
-
if part.isdigit():
|
|
232
|
-
part = (1, int(part))
|
|
233
|
-
else:
|
|
234
|
-
part = (0, part)
|
|
235
|
-
parts.append(part)
|
|
236
|
-
local = tuple(parts)
|
|
237
|
-
if not pre:
|
|
238
|
-
# either before pre-release, or final release and after
|
|
239
|
-
if not post and dev:
|
|
240
|
-
# before pre-release
|
|
241
|
-
pre = ('a', -1) # to sort before a0
|
|
242
|
-
else:
|
|
243
|
-
pre = ('z',) # to sort after all pre-releases
|
|
244
|
-
# now look at the state of post and dev.
|
|
245
|
-
if not post:
|
|
246
|
-
post = ('_',) # sort before 'a'
|
|
247
|
-
if not dev:
|
|
248
|
-
dev = ('final',)
|
|
249
|
-
|
|
250
|
-
return epoch, nums, pre, post, dev, local
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
_normalized_key = _pep_440_key
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
class NormalizedVersion(Version):
|
|
257
|
-
"""A rational version.
|
|
258
|
-
|
|
259
|
-
Good:
|
|
260
|
-
1.2 # equivalent to "1.2.0"
|
|
261
|
-
1.2.0
|
|
262
|
-
1.2a1
|
|
263
|
-
1.2.3a2
|
|
264
|
-
1.2.3b1
|
|
265
|
-
1.2.3c1
|
|
266
|
-
1.2.3.4
|
|
267
|
-
TODO: fill this out
|
|
268
|
-
|
|
269
|
-
Bad:
|
|
270
|
-
1 # minimum two numbers
|
|
271
|
-
1.2a # release level must have a release serial
|
|
272
|
-
1.2.3b
|
|
273
|
-
"""
|
|
274
|
-
def parse(self, s):
|
|
275
|
-
result = _normalized_key(s)
|
|
276
|
-
# _normalized_key loses trailing zeroes in the release
|
|
277
|
-
# clause, since that's needed to ensure that X.Y == X.Y.0 == X.Y.0.0
|
|
278
|
-
# However, PEP 440 prefix matching needs it: for example,
|
|
279
|
-
# (~= 1.4.5.0) matches differently to (~= 1.4.5.0.0).
|
|
280
|
-
m = PEP440_VERSION_RE.match(s) # must succeed
|
|
281
|
-
groups = m.groups()
|
|
282
|
-
self._release_clause = tuple(int(v) for v in groups[1].split('.'))
|
|
283
|
-
return result
|
|
284
|
-
|
|
285
|
-
PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev'])
|
|
286
|
-
|
|
287
|
-
@property
|
|
288
|
-
def is_prerelease(self):
|
|
289
|
-
return any(t[0] in self.PREREL_TAGS for t in self._parts if t)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
def _match_prefix(x, y):
|
|
293
|
-
x = str(x)
|
|
294
|
-
y = str(y)
|
|
295
|
-
if x == y:
|
|
296
|
-
return True
|
|
297
|
-
if not x.startswith(y):
|
|
298
|
-
return False
|
|
299
|
-
n = len(y)
|
|
300
|
-
return x[n] == '.'
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
class NormalizedMatcher(Matcher):
|
|
304
|
-
version_class = NormalizedVersion
|
|
305
|
-
|
|
306
|
-
# value is either a callable or the name of a method
|
|
307
|
-
_operators = {
|
|
308
|
-
'~=': '_match_compatible',
|
|
309
|
-
'<': '_match_lt',
|
|
310
|
-
'>': '_match_gt',
|
|
311
|
-
'<=': '_match_le',
|
|
312
|
-
'>=': '_match_ge',
|
|
313
|
-
'==': '_match_eq',
|
|
314
|
-
'===': '_match_arbitrary',
|
|
315
|
-
'!=': '_match_ne',
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
def _adjust_local(self, version, constraint, prefix):
|
|
319
|
-
if prefix:
|
|
320
|
-
strip_local = '+' not in constraint and version._parts[-1]
|
|
321
|
-
else:
|
|
322
|
-
# both constraint and version are
|
|
323
|
-
# NormalizedVersion instances.
|
|
324
|
-
# If constraint does not have a local component,
|
|
325
|
-
# ensure the version doesn't, either.
|
|
326
|
-
strip_local = not constraint._parts[-1] and version._parts[-1]
|
|
327
|
-
if strip_local:
|
|
328
|
-
s = version._string.split('+', 1)[0]
|
|
329
|
-
version = self.version_class(s)
|
|
330
|
-
return version, constraint
|
|
331
|
-
|
|
332
|
-
def _match_lt(self, version, constraint, prefix):
|
|
333
|
-
version, constraint = self._adjust_local(version, constraint, prefix)
|
|
334
|
-
if version >= constraint:
|
|
335
|
-
return False
|
|
336
|
-
release_clause = constraint._release_clause
|
|
337
|
-
pfx = '.'.join([str(i) for i in release_clause])
|
|
338
|
-
return not _match_prefix(version, pfx)
|
|
339
|
-
|
|
340
|
-
def _match_gt(self, version, constraint, prefix):
|
|
341
|
-
version, constraint = self._adjust_local(version, constraint, prefix)
|
|
342
|
-
if version <= constraint:
|
|
343
|
-
return False
|
|
344
|
-
release_clause = constraint._release_clause
|
|
345
|
-
pfx = '.'.join([str(i) for i in release_clause])
|
|
346
|
-
return not _match_prefix(version, pfx)
|
|
347
|
-
|
|
348
|
-
def _match_le(self, version, constraint, prefix):
|
|
349
|
-
version, constraint = self._adjust_local(version, constraint, prefix)
|
|
350
|
-
return version <= constraint
|
|
351
|
-
|
|
352
|
-
def _match_ge(self, version, constraint, prefix):
|
|
353
|
-
version, constraint = self._adjust_local(version, constraint, prefix)
|
|
354
|
-
return version >= constraint
|
|
355
|
-
|
|
356
|
-
def _match_eq(self, version, constraint, prefix):
|
|
357
|
-
version, constraint = self._adjust_local(version, constraint, prefix)
|
|
358
|
-
if not prefix:
|
|
359
|
-
result = (version == constraint)
|
|
360
|
-
else:
|
|
361
|
-
result = _match_prefix(version, constraint)
|
|
362
|
-
return result
|
|
363
|
-
|
|
364
|
-
def _match_arbitrary(self, version, constraint, prefix):
|
|
365
|
-
return str(version) == str(constraint)
|
|
366
|
-
|
|
367
|
-
def _match_ne(self, version, constraint, prefix):
|
|
368
|
-
version, constraint = self._adjust_local(version, constraint, prefix)
|
|
369
|
-
if not prefix:
|
|
370
|
-
result = (version != constraint)
|
|
371
|
-
else:
|
|
372
|
-
result = not _match_prefix(version, constraint)
|
|
373
|
-
return result
|
|
374
|
-
|
|
375
|
-
def _match_compatible(self, version, constraint, prefix):
|
|
376
|
-
version, constraint = self._adjust_local(version, constraint, prefix)
|
|
377
|
-
if version == constraint:
|
|
378
|
-
return True
|
|
379
|
-
if version < constraint:
|
|
380
|
-
return False
|
|
381
|
-
# if not prefix:
|
|
382
|
-
# return True
|
|
383
|
-
release_clause = constraint._release_clause
|
|
384
|
-
if len(release_clause) > 1:
|
|
385
|
-
release_clause = release_clause[:-1]
|
|
386
|
-
pfx = '.'.join([str(i) for i in release_clause])
|
|
387
|
-
return _match_prefix(version, pfx)
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
_REPLACEMENTS = (
|
|
391
|
-
(re.compile('[.+-]$'), ''), # remove trailing puncts
|
|
392
|
-
(re.compile(r'^[.](\d)'), r'0.\1'), # .N -> 0.N at start
|
|
393
|
-
(re.compile('^[.-]'), ''), # remove leading puncts
|
|
394
|
-
(re.compile(r'^\((.*)\)$'), r'\1'), # remove parentheses
|
|
395
|
-
(re.compile(r'^v(ersion)?\s*(\d+)'), r'\2'), # remove leading v(ersion)
|
|
396
|
-
(re.compile(r'^r(ev)?\s*(\d+)'), r'\2'), # remove leading v(ersion)
|
|
397
|
-
(re.compile('[.]{2,}'), '.'), # multiple runs of '.'
|
|
398
|
-
(re.compile(r'\b(alfa|apha)\b'), 'alpha'), # misspelt alpha
|
|
399
|
-
(re.compile(r'\b(pre-alpha|prealpha)\b'),
|
|
400
|
-
'pre.alpha'), # standardise
|
|
401
|
-
(re.compile(r'\(beta\)$'), 'beta'), # remove parentheses
|
|
402
|
-
)
|
|
403
|
-
|
|
404
|
-
_SUFFIX_REPLACEMENTS = (
|
|
405
|
-
(re.compile('^[:~._+-]+'), ''), # remove leading puncts
|
|
406
|
-
(re.compile('[,*")([\\]]'), ''), # remove unwanted chars
|
|
407
|
-
(re.compile('[~:+_ -]'), '.'), # replace illegal chars
|
|
408
|
-
(re.compile('[.]{2,}'), '.'), # multiple runs of '.'
|
|
409
|
-
(re.compile(r'\.$'), ''), # trailing '.'
|
|
410
|
-
)
|
|
411
|
-
|
|
412
|
-
_NUMERIC_PREFIX = re.compile(r'(\d+(\.\d+)*)')
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
def _suggest_semantic_version(s):
|
|
416
|
-
"""
|
|
417
|
-
Try to suggest a semantic form for a version for which
|
|
418
|
-
_suggest_normalized_version couldn't come up with anything.
|
|
419
|
-
"""
|
|
420
|
-
result = s.strip().lower()
|
|
421
|
-
for pat, repl in _REPLACEMENTS:
|
|
422
|
-
result = pat.sub(repl, result)
|
|
423
|
-
if not result:
|
|
424
|
-
result = '0.0.0'
|
|
425
|
-
|
|
426
|
-
# Now look for numeric prefix, and separate it out from
|
|
427
|
-
# the rest.
|
|
428
|
-
# import pdb; pdb.set_trace()
|
|
429
|
-
m = _NUMERIC_PREFIX.match(result)
|
|
430
|
-
if not m:
|
|
431
|
-
prefix = '0.0.0'
|
|
432
|
-
suffix = result
|
|
433
|
-
else:
|
|
434
|
-
prefix = m.groups()[0].split('.')
|
|
435
|
-
prefix = [int(i) for i in prefix]
|
|
436
|
-
while len(prefix) < 3:
|
|
437
|
-
prefix.append(0)
|
|
438
|
-
if len(prefix) == 3:
|
|
439
|
-
suffix = result[m.end():]
|
|
440
|
-
else:
|
|
441
|
-
suffix = '.'.join([str(i) for i in prefix[3:]]) + result[m.end():]
|
|
442
|
-
prefix = prefix[:3]
|
|
443
|
-
prefix = '.'.join([str(i) for i in prefix])
|
|
444
|
-
suffix = suffix.strip()
|
|
445
|
-
if suffix:
|
|
446
|
-
# import pdb; pdb.set_trace()
|
|
447
|
-
# massage the suffix.
|
|
448
|
-
for pat, repl in _SUFFIX_REPLACEMENTS:
|
|
449
|
-
suffix = pat.sub(repl, suffix)
|
|
450
|
-
|
|
451
|
-
if not suffix:
|
|
452
|
-
result = prefix
|
|
453
|
-
else:
|
|
454
|
-
sep = '-' if 'dev' in suffix else '+'
|
|
455
|
-
result = prefix + sep + suffix
|
|
456
|
-
if not is_semver(result):
|
|
457
|
-
result = None
|
|
458
|
-
return result
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
def _suggest_normalized_version(s):
|
|
462
|
-
"""Suggest a normalized version close to the given version string.
|
|
463
|
-
|
|
464
|
-
If you have a version string that isn't rational (i.e. NormalizedVersion
|
|
465
|
-
doesn't like it) then you might be able to get an equivalent (or close)
|
|
466
|
-
rational version from this function.
|
|
467
|
-
|
|
468
|
-
This does a number of simple normalizations to the given string, based
|
|
469
|
-
on observation of versions currently in use on PyPI. Given a dump of
|
|
470
|
-
those version during PyCon 2009, 4287 of them:
|
|
471
|
-
- 2312 (53.93%) match NormalizedVersion without change
|
|
472
|
-
with the automatic suggestion
|
|
473
|
-
- 3474 (81.04%) match when using this suggestion method
|
|
474
|
-
|
|
475
|
-
@param s {str} An irrational version string.
|
|
476
|
-
@returns A rational version string, or None, if couldn't determine one.
|
|
477
|
-
"""
|
|
478
|
-
try:
|
|
479
|
-
_normalized_key(s)
|
|
480
|
-
return s # already rational
|
|
481
|
-
except UnsupportedVersionError:
|
|
482
|
-
pass
|
|
483
|
-
|
|
484
|
-
rs = s.lower()
|
|
485
|
-
|
|
486
|
-
# part of this could use maketrans
|
|
487
|
-
for orig, repl in (('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'),
|
|
488
|
-
('beta', 'b'), ('rc', 'c'), ('-final', ''),
|
|
489
|
-
('-pre', 'c'),
|
|
490
|
-
('-release', ''), ('.release', ''), ('-stable', ''),
|
|
491
|
-
('+', '.'), ('_', '.'), (' ', ''), ('.final', ''),
|
|
492
|
-
('final', '')):
|
|
493
|
-
rs = rs.replace(orig, repl)
|
|
494
|
-
|
|
495
|
-
# if something ends with dev or pre, we add a 0
|
|
496
|
-
rs = re.sub(r"pre$", r"pre0", rs)
|
|
497
|
-
rs = re.sub(r"dev$", r"dev0", rs)
|
|
498
|
-
|
|
499
|
-
# if we have something like "b-2" or "a.2" at the end of the
|
|
500
|
-
# version, that is probably beta, alpha, etc
|
|
501
|
-
# let's remove the dash or dot
|
|
502
|
-
rs = re.sub(r"([abc]|rc)[\-\.](\d+)$", r"\1\2", rs)
|
|
503
|
-
|
|
504
|
-
# 1.0-dev-r371 -> 1.0.dev371
|
|
505
|
-
# 0.1-dev-r79 -> 0.1.dev79
|
|
506
|
-
rs = re.sub(r"[\-\.](dev)[\-\.]?r?(\d+)$", r".\1\2", rs)
|
|
507
|
-
|
|
508
|
-
# Clean: 2.0.a.3, 2.0.b1, 0.9.0~c1
|
|
509
|
-
rs = re.sub(r"[.~]?([abc])\.?", r"\1", rs)
|
|
510
|
-
|
|
511
|
-
# Clean: v0.3, v1.0
|
|
512
|
-
if rs.startswith('v'):
|
|
513
|
-
rs = rs[1:]
|
|
514
|
-
|
|
515
|
-
# Clean leading '0's on numbers.
|
|
516
|
-
# TODO: unintended side-effect on, e.g., "2003.05.09"
|
|
517
|
-
# PyPI stats: 77 (~2%) better
|
|
518
|
-
rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs)
|
|
519
|
-
|
|
520
|
-
# Clean a/b/c with no version. E.g. "1.0a" -> "1.0a0". Setuptools infers
|
|
521
|
-
# zero.
|
|
522
|
-
# PyPI stats: 245 (7.56%) better
|
|
523
|
-
rs = re.sub(r"(\d+[abc])$", r"\g<1>0", rs)
|
|
524
|
-
|
|
525
|
-
# the 'dev-rNNN' tag is a dev tag
|
|
526
|
-
rs = re.sub(r"\.?(dev-r|dev\.r)\.?(\d+)$", r".dev\2", rs)
|
|
527
|
-
|
|
528
|
-
# clean the - when used as a pre delimiter
|
|
529
|
-
rs = re.sub(r"-(a|b|c)(\d+)$", r"\1\2", rs)
|
|
530
|
-
|
|
531
|
-
# a terminal "dev" or "devel" can be changed into ".dev0"
|
|
532
|
-
rs = re.sub(r"[\.\-](dev|devel)$", r".dev0", rs)
|
|
533
|
-
|
|
534
|
-
# a terminal "dev" can be changed into ".dev0"
|
|
535
|
-
rs = re.sub(r"(?![\.\-])dev$", r".dev0", rs)
|
|
536
|
-
|
|
537
|
-
# a terminal "final" or "stable" can be removed
|
|
538
|
-
rs = re.sub(r"(final|stable)$", "", rs)
|
|
539
|
-
|
|
540
|
-
# The 'r' and the '-' tags are post release tags
|
|
541
|
-
# 0.4a1.r10 -> 0.4a1.post10
|
|
542
|
-
# 0.9.33-17222 -> 0.9.33.post17222
|
|
543
|
-
# 0.9.33-r17222 -> 0.9.33.post17222
|
|
544
|
-
rs = re.sub(r"\.?(r|-|-r)\.?(\d+)$", r".post\2", rs)
|
|
545
|
-
|
|
546
|
-
# Clean 'r' instead of 'dev' usage:
|
|
547
|
-
# 0.9.33+r17222 -> 0.9.33.dev17222
|
|
548
|
-
# 1.0dev123 -> 1.0.dev123
|
|
549
|
-
# 1.0.git123 -> 1.0.dev123
|
|
550
|
-
# 1.0.bzr123 -> 1.0.dev123
|
|
551
|
-
# 0.1a0dev.123 -> 0.1a0.dev123
|
|
552
|
-
# PyPI stats: ~150 (~4%) better
|
|
553
|
-
rs = re.sub(r"\.?(dev|git|bzr)\.?(\d+)$", r".dev\2", rs)
|
|
554
|
-
|
|
555
|
-
# Clean '.pre' (normalized from '-pre' above) instead of 'c' usage:
|
|
556
|
-
# 0.2.pre1 -> 0.2c1
|
|
557
|
-
# 0.2-c1 -> 0.2c1
|
|
558
|
-
# 1.0preview123 -> 1.0c123
|
|
559
|
-
# PyPI stats: ~21 (0.62%) better
|
|
560
|
-
rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs)
|
|
561
|
-
|
|
562
|
-
# Tcl/Tk uses "px" for their post release markers
|
|
563
|
-
rs = re.sub(r"p(\d+)$", r".post\1", rs)
|
|
564
|
-
|
|
565
|
-
try:
|
|
566
|
-
_normalized_key(rs)
|
|
567
|
-
except UnsupportedVersionError:
|
|
568
|
-
rs = None
|
|
569
|
-
return rs
|
|
570
|
-
|
|
571
|
-
#
|
|
572
|
-
# Legacy version processing (distribute-compatible)
|
|
573
|
-
#
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
_VERSION_PART = re.compile(r'([a-z]+|\d+|[\.-])', re.I)
|
|
577
|
-
_VERSION_REPLACE = {
|
|
578
|
-
'pre': 'c',
|
|
579
|
-
'preview': 'c',
|
|
580
|
-
'-': 'final-',
|
|
581
|
-
'rc': 'c',
|
|
582
|
-
'dev': '@',
|
|
583
|
-
'': None,
|
|
584
|
-
'.': None,
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
def _legacy_key(s):
|
|
589
|
-
def get_parts(s):
|
|
590
|
-
result = []
|
|
591
|
-
for p in _VERSION_PART.split(s.lower()):
|
|
592
|
-
p = _VERSION_REPLACE.get(p, p)
|
|
593
|
-
if p:
|
|
594
|
-
if '0' <= p[:1] <= '9':
|
|
595
|
-
p = p.zfill(8)
|
|
596
|
-
else:
|
|
597
|
-
p = '*' + p
|
|
598
|
-
result.append(p)
|
|
599
|
-
result.append('*final')
|
|
600
|
-
return result
|
|
601
|
-
|
|
602
|
-
result = []
|
|
603
|
-
for p in get_parts(s):
|
|
604
|
-
if p.startswith('*'):
|
|
605
|
-
if p < '*final':
|
|
606
|
-
while result and result[-1] == '*final-':
|
|
607
|
-
result.pop()
|
|
608
|
-
while result and result[-1] == '00000000':
|
|
609
|
-
result.pop()
|
|
610
|
-
result.append(p)
|
|
611
|
-
return tuple(result)
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
class LegacyVersion(Version):
|
|
615
|
-
def parse(self, s):
|
|
616
|
-
return _legacy_key(s)
|
|
617
|
-
|
|
618
|
-
@property
|
|
619
|
-
def is_prerelease(self):
|
|
620
|
-
result = False
|
|
621
|
-
for x in self._parts:
|
|
622
|
-
if (isinstance(x, string_types) and x.startswith('*') and x < '*final'):
|
|
623
|
-
result = True
|
|
624
|
-
break
|
|
625
|
-
return result
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
class LegacyMatcher(Matcher):
|
|
629
|
-
version_class = LegacyVersion
|
|
630
|
-
|
|
631
|
-
_operators = dict(Matcher._operators)
|
|
632
|
-
_operators['~='] = '_match_compatible'
|
|
633
|
-
|
|
634
|
-
numeric_re = re.compile(r'^(\d+(\.\d+)*)')
|
|
635
|
-
|
|
636
|
-
def _match_compatible(self, version, constraint, prefix):
|
|
637
|
-
if version < constraint:
|
|
638
|
-
return False
|
|
639
|
-
m = self.numeric_re.match(str(constraint))
|
|
640
|
-
if not m:
|
|
641
|
-
logger.warning('Cannot compute compatible match for version %s '
|
|
642
|
-
' and constraint %s', version, constraint)
|
|
643
|
-
return True
|
|
644
|
-
s = m.groups()[0]
|
|
645
|
-
if '.' in s:
|
|
646
|
-
s = s.rsplit('.', 1)[0]
|
|
647
|
-
return _match_prefix(version, s)
|
|
648
|
-
|
|
649
|
-
#
|
|
650
|
-
# Semantic versioning
|
|
651
|
-
#
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
_SEMVER_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)'
|
|
655
|
-
r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?'
|
|
656
|
-
r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I)
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
def is_semver(s):
|
|
660
|
-
return _SEMVER_RE.match(s)
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
def _semantic_key(s):
|
|
664
|
-
def make_tuple(s, absent):
|
|
665
|
-
if s is None:
|
|
666
|
-
result = (absent,)
|
|
667
|
-
else:
|
|
668
|
-
parts = s[1:].split('.')
|
|
669
|
-
# We can't compare ints and strings on Python 3, so fudge it
|
|
670
|
-
# by zero-filling numeric values so simulate a numeric comparison
|
|
671
|
-
result = tuple([p.zfill(8) if p.isdigit() else p for p in parts])
|
|
672
|
-
return result
|
|
673
|
-
|
|
674
|
-
m = is_semver(s)
|
|
675
|
-
if not m:
|
|
676
|
-
raise UnsupportedVersionError(s)
|
|
677
|
-
groups = m.groups()
|
|
678
|
-
major, minor, patch = [int(i) for i in groups[:3]]
|
|
679
|
-
# choose the '|' and '*' so that versions sort correctly
|
|
680
|
-
pre, build = make_tuple(groups[3], '|'), make_tuple(groups[5], '*')
|
|
681
|
-
return (major, minor, patch), pre, build
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
class SemanticVersion(Version):
|
|
685
|
-
def parse(self, s):
|
|
686
|
-
return _semantic_key(s)
|
|
687
|
-
|
|
688
|
-
@property
|
|
689
|
-
def is_prerelease(self):
|
|
690
|
-
return self._parts[1][0] != '|'
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
class SemanticMatcher(Matcher):
|
|
694
|
-
version_class = SemanticVersion
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
class VersionScheme(object):
|
|
698
|
-
def __init__(self, key, matcher, suggester=None):
|
|
699
|
-
self.key = key
|
|
700
|
-
self.matcher = matcher
|
|
701
|
-
self.suggester = suggester
|
|
702
|
-
|
|
703
|
-
def is_valid_version(self, s):
|
|
704
|
-
try:
|
|
705
|
-
self.matcher.version_class(s)
|
|
706
|
-
result = True
|
|
707
|
-
except UnsupportedVersionError:
|
|
708
|
-
result = False
|
|
709
|
-
return result
|
|
710
|
-
|
|
711
|
-
def is_valid_matcher(self, s):
|
|
712
|
-
try:
|
|
713
|
-
self.matcher(s)
|
|
714
|
-
result = True
|
|
715
|
-
except UnsupportedVersionError:
|
|
716
|
-
result = False
|
|
717
|
-
return result
|
|
718
|
-
|
|
719
|
-
def is_valid_constraint_list(self, s):
|
|
720
|
-
"""
|
|
721
|
-
Used for processing some metadata fields
|
|
722
|
-
"""
|
|
723
|
-
# See issue #140. Be tolerant of a single trailing comma.
|
|
724
|
-
if s.endswith(','):
|
|
725
|
-
s = s[:-1]
|
|
726
|
-
return self.is_valid_matcher('dummy_name (%s)' % s)
|
|
727
|
-
|
|
728
|
-
def suggest(self, s):
|
|
729
|
-
if self.suggester is None:
|
|
730
|
-
result = None
|
|
731
|
-
else:
|
|
732
|
-
result = self.suggester(s)
|
|
733
|
-
return result
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
_SCHEMES = {
|
|
737
|
-
'normalized': VersionScheme(_normalized_key, NormalizedMatcher,
|
|
738
|
-
_suggest_normalized_version),
|
|
739
|
-
'legacy': VersionScheme(_legacy_key, LegacyMatcher, lambda self, s: s),
|
|
740
|
-
'semantic': VersionScheme(_semantic_key, SemanticMatcher,
|
|
741
|
-
_suggest_semantic_version),
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
_SCHEMES['default'] = _SCHEMES['normalized']
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
def get_scheme(name):
|
|
748
|
-
if name not in _SCHEMES:
|
|
749
|
-
raise ValueError('unknown scheme name: %r' % name)
|
|
750
|
-
return _SCHEMES[name]
|