pip 25.1.1__py3-none-any.whl → 25.3__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.
Files changed (236) hide show
  1. pip/__init__.py +3 -3
  2. pip/_internal/__init__.py +2 -2
  3. pip/_internal/build_env.py +186 -94
  4. pip/_internal/cache.py +17 -15
  5. pip/_internal/cli/autocompletion.py +13 -4
  6. pip/_internal/cli/base_command.py +18 -7
  7. pip/_internal/cli/cmdoptions.py +57 -80
  8. pip/_internal/cli/command_context.py +4 -3
  9. pip/_internal/cli/index_command.py +11 -9
  10. pip/_internal/cli/main.py +3 -2
  11. pip/_internal/cli/main_parser.py +4 -3
  12. pip/_internal/cli/parser.py +24 -20
  13. pip/_internal/cli/progress_bars.py +19 -12
  14. pip/_internal/cli/req_command.py +57 -33
  15. pip/_internal/cli/spinners.py +81 -5
  16. pip/_internal/commands/__init__.py +5 -3
  17. pip/_internal/commands/cache.py +18 -15
  18. pip/_internal/commands/check.py +1 -2
  19. pip/_internal/commands/completion.py +1 -2
  20. pip/_internal/commands/configuration.py +26 -18
  21. pip/_internal/commands/debug.py +8 -6
  22. pip/_internal/commands/download.py +6 -10
  23. pip/_internal/commands/freeze.py +2 -3
  24. pip/_internal/commands/hash.py +1 -2
  25. pip/_internal/commands/help.py +1 -2
  26. pip/_internal/commands/index.py +15 -9
  27. pip/_internal/commands/inspect.py +4 -4
  28. pip/_internal/commands/install.py +63 -53
  29. pip/_internal/commands/list.py +35 -26
  30. pip/_internal/commands/lock.py +4 -8
  31. pip/_internal/commands/search.py +14 -12
  32. pip/_internal/commands/show.py +14 -11
  33. pip/_internal/commands/uninstall.py +1 -2
  34. pip/_internal/commands/wheel.py +7 -13
  35. pip/_internal/configuration.py +40 -27
  36. pip/_internal/distributions/base.py +6 -4
  37. pip/_internal/distributions/installed.py +8 -4
  38. pip/_internal/distributions/sdist.py +33 -27
  39. pip/_internal/distributions/wheel.py +6 -4
  40. pip/_internal/exceptions.py +78 -42
  41. pip/_internal/index/collector.py +24 -29
  42. pip/_internal/index/package_finder.py +73 -64
  43. pip/_internal/index/sources.py +17 -14
  44. pip/_internal/locations/__init__.py +18 -16
  45. pip/_internal/locations/_distutils.py +12 -11
  46. pip/_internal/locations/_sysconfig.py +5 -4
  47. pip/_internal/locations/base.py +4 -3
  48. pip/_internal/main.py +2 -2
  49. pip/_internal/metadata/__init__.py +14 -7
  50. pip/_internal/metadata/_json.py +5 -4
  51. pip/_internal/metadata/base.py +22 -27
  52. pip/_internal/metadata/importlib/_compat.py +6 -4
  53. pip/_internal/metadata/importlib/_dists.py +20 -19
  54. pip/_internal/metadata/importlib/_envs.py +9 -6
  55. pip/_internal/metadata/pkg_resources.py +11 -14
  56. pip/_internal/models/direct_url.py +24 -21
  57. pip/_internal/models/format_control.py +5 -5
  58. pip/_internal/models/installation_report.py +4 -3
  59. pip/_internal/models/link.py +39 -34
  60. pip/_internal/models/pylock.py +27 -22
  61. pip/_internal/models/search_scope.py +6 -7
  62. pip/_internal/models/selection_prefs.py +3 -3
  63. pip/_internal/models/target_python.py +10 -9
  64. pip/_internal/models/wheel.py +12 -71
  65. pip/_internal/network/auth.py +20 -22
  66. pip/_internal/network/cache.py +28 -17
  67. pip/_internal/network/download.py +169 -141
  68. pip/_internal/network/lazy_wheel.py +15 -10
  69. pip/_internal/network/session.py +32 -27
  70. pip/_internal/network/utils.py +2 -2
  71. pip/_internal/network/xmlrpc.py +2 -2
  72. pip/_internal/operations/build/build_tracker.py +10 -8
  73. pip/_internal/operations/build/wheel.py +7 -6
  74. pip/_internal/operations/build/wheel_editable.py +7 -6
  75. pip/_internal/operations/check.py +21 -26
  76. pip/_internal/operations/freeze.py +12 -9
  77. pip/_internal/operations/install/wheel.py +49 -41
  78. pip/_internal/operations/prepare.py +42 -31
  79. pip/_internal/pyproject.py +7 -69
  80. pip/_internal/req/__init__.py +12 -12
  81. pip/_internal/req/constructors.py +68 -62
  82. pip/_internal/req/req_dependency_group.py +7 -11
  83. pip/_internal/req/req_file.py +32 -36
  84. pip/_internal/req/req_install.py +64 -170
  85. pip/_internal/req/req_set.py +4 -5
  86. pip/_internal/req/req_uninstall.py +20 -17
  87. pip/_internal/resolution/base.py +3 -3
  88. pip/_internal/resolution/legacy/resolver.py +21 -20
  89. pip/_internal/resolution/resolvelib/base.py +16 -13
  90. pip/_internal/resolution/resolvelib/candidates.py +49 -37
  91. pip/_internal/resolution/resolvelib/factory.py +72 -50
  92. pip/_internal/resolution/resolvelib/found_candidates.py +11 -9
  93. pip/_internal/resolution/resolvelib/provider.py +24 -20
  94. pip/_internal/resolution/resolvelib/reporter.py +26 -11
  95. pip/_internal/resolution/resolvelib/requirements.py +8 -6
  96. pip/_internal/resolution/resolvelib/resolver.py +41 -29
  97. pip/_internal/self_outdated_check.py +19 -9
  98. pip/_internal/utils/appdirs.py +1 -2
  99. pip/_internal/utils/compat.py +7 -1
  100. pip/_internal/utils/compatibility_tags.py +17 -16
  101. pip/_internal/utils/deprecation.py +11 -9
  102. pip/_internal/utils/direct_url_helpers.py +2 -2
  103. pip/_internal/utils/egg_link.py +6 -5
  104. pip/_internal/utils/entrypoints.py +3 -2
  105. pip/_internal/utils/filesystem.py +20 -5
  106. pip/_internal/utils/filetypes.py +4 -6
  107. pip/_internal/utils/glibc.py +6 -5
  108. pip/_internal/utils/hashes.py +9 -6
  109. pip/_internal/utils/logging.py +8 -5
  110. pip/_internal/utils/misc.py +37 -45
  111. pip/_internal/utils/packaging.py +3 -2
  112. pip/_internal/utils/retry.py +7 -4
  113. pip/_internal/utils/subprocess.py +20 -17
  114. pip/_internal/utils/temp_dir.py +10 -12
  115. pip/_internal/utils/unpacking.py +31 -4
  116. pip/_internal/utils/urls.py +1 -1
  117. pip/_internal/utils/virtualenv.py +3 -2
  118. pip/_internal/utils/wheel.py +3 -4
  119. pip/_internal/vcs/bazaar.py +26 -8
  120. pip/_internal/vcs/git.py +59 -24
  121. pip/_internal/vcs/mercurial.py +34 -11
  122. pip/_internal/vcs/subversion.py +27 -16
  123. pip/_internal/vcs/versioncontrol.py +56 -51
  124. pip/_internal/wheel_builder.py +30 -101
  125. pip/_vendor/README.rst +180 -0
  126. pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  127. pip/_vendor/cachecontrol/__init__.py +1 -1
  128. pip/_vendor/certifi/LICENSE +20 -0
  129. pip/_vendor/certifi/__init__.py +1 -1
  130. pip/_vendor/certifi/cacert.pem +164 -261
  131. pip/_vendor/certifi/core.py +1 -32
  132. pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  133. pip/_vendor/distlib/LICENSE.txt +284 -0
  134. pip/_vendor/distlib/__init__.py +2 -2
  135. pip/_vendor/distlib/scripts.py +1 -1
  136. pip/_vendor/distro/LICENSE +202 -0
  137. pip/_vendor/idna/LICENSE.md +31 -0
  138. pip/_vendor/msgpack/COPYING +14 -0
  139. pip/_vendor/msgpack/__init__.py +2 -2
  140. pip/_vendor/packaging/LICENSE +3 -0
  141. pip/_vendor/packaging/LICENSE.APACHE +177 -0
  142. pip/_vendor/packaging/LICENSE.BSD +23 -0
  143. pip/_vendor/pkg_resources/LICENSE +17 -0
  144. pip/_vendor/pkg_resources/__init__.py +1 -1
  145. pip/_vendor/platformdirs/LICENSE +21 -0
  146. pip/_vendor/platformdirs/api.py +1 -1
  147. pip/_vendor/platformdirs/macos.py +10 -8
  148. pip/_vendor/platformdirs/version.py +16 -3
  149. pip/_vendor/pygments/LICENSE +25 -0
  150. pip/_vendor/pygments/__init__.py +1 -1
  151. pip/_vendor/pyproject_hooks/LICENSE +21 -0
  152. pip/_vendor/requests/LICENSE +175 -0
  153. pip/_vendor/requests/__version__.py +2 -2
  154. pip/_vendor/requests/adapters.py +17 -40
  155. pip/_vendor/requests/compat.py +12 -0
  156. pip/_vendor/requests/models.py +3 -1
  157. pip/_vendor/requests/sessions.py +1 -1
  158. pip/_vendor/requests/utils.py +6 -16
  159. pip/_vendor/resolvelib/LICENSE +13 -0
  160. pip/_vendor/resolvelib/__init__.py +3 -3
  161. pip/_vendor/resolvelib/reporters.py +1 -1
  162. pip/_vendor/resolvelib/resolvers/__init__.py +4 -4
  163. pip/_vendor/resolvelib/resolvers/abstract.py +3 -3
  164. pip/_vendor/resolvelib/resolvers/resolution.py +96 -10
  165. pip/_vendor/rich/LICENSE +19 -0
  166. pip/_vendor/rich/__main__.py +12 -40
  167. pip/_vendor/rich/_inspect.py +1 -1
  168. pip/_vendor/rich/_ratio.py +1 -7
  169. pip/_vendor/rich/align.py +1 -7
  170. pip/_vendor/rich/box.py +1 -7
  171. pip/_vendor/rich/console.py +25 -20
  172. pip/_vendor/rich/control.py +1 -7
  173. pip/_vendor/rich/diagnose.py +1 -0
  174. pip/_vendor/rich/emoji.py +1 -6
  175. pip/_vendor/rich/live.py +32 -7
  176. pip/_vendor/rich/live_render.py +1 -7
  177. pip/_vendor/rich/logging.py +1 -1
  178. pip/_vendor/rich/panel.py +3 -4
  179. pip/_vendor/rich/progress.py +15 -15
  180. pip/_vendor/rich/spinner.py +7 -13
  181. pip/_vendor/rich/style.py +7 -11
  182. pip/_vendor/rich/syntax.py +24 -5
  183. pip/_vendor/rich/traceback.py +32 -17
  184. pip/_vendor/tomli/LICENSE +21 -0
  185. pip/_vendor/tomli/__init__.py +1 -1
  186. pip/_vendor/tomli/_parser.py +28 -21
  187. pip/_vendor/tomli/_re.py +8 -5
  188. pip/_vendor/tomli_w/LICENSE +21 -0
  189. pip/_vendor/truststore/LICENSE +21 -0
  190. pip/_vendor/truststore/__init__.py +1 -1
  191. pip/_vendor/truststore/_api.py +15 -7
  192. pip/_vendor/truststore/_openssl.py +3 -1
  193. pip/_vendor/urllib3/LICENSE.txt +21 -0
  194. pip/_vendor/vendor.txt +11 -12
  195. {pip-25.1.1.dist-info → pip-25.3.dist-info}/METADATA +32 -11
  196. {pip-25.1.1.dist-info → pip-25.3.dist-info}/RECORD +221 -192
  197. {pip-25.1.1.dist-info → pip-25.3.dist-info}/WHEEL +1 -2
  198. pip-25.3.dist-info/entry_points.txt +4 -0
  199. {pip-25.1.1.dist-info → pip-25.3.dist-info}/licenses/AUTHORS.txt +21 -0
  200. pip-25.3.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  201. pip-25.3.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
  202. pip-25.3.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  203. pip-25.3.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
  204. pip-25.3.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
  205. pip-25.3.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
  206. pip-25.3.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
  207. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
  208. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
  209. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
  210. pip-25.3.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
  211. pip-25.3.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
  212. pip-25.3.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
  213. pip-25.3.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
  214. pip-25.3.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
  215. pip-25.3.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
  216. pip-25.3.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
  217. pip-25.3.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
  218. pip-25.3.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
  219. pip-25.3.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
  220. pip-25.3.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
  221. pip/_internal/operations/build/metadata_legacy.py +0 -73
  222. pip/_internal/operations/build/wheel_legacy.py +0 -118
  223. pip/_internal/operations/install/editable_legacy.py +0 -46
  224. pip/_internal/utils/setuptools_build.py +0 -147
  225. pip/_vendor/distlib/database.py +0 -1329
  226. pip/_vendor/distlib/index.py +0 -508
  227. pip/_vendor/distlib/locators.py +0 -1295
  228. pip/_vendor/distlib/manifest.py +0 -384
  229. pip/_vendor/distlib/markers.py +0 -162
  230. pip/_vendor/distlib/metadata.py +0 -1031
  231. pip/_vendor/distlib/version.py +0 -750
  232. pip/_vendor/distlib/wheel.py +0 -1100
  233. pip/_vendor/typing_extensions.py +0 -4584
  234. pip-25.1.1.dist-info/entry_points.txt +0 -3
  235. pip-25.1.1.dist-info/top_level.txt +0 -1
  236. {pip-25.1.1.dist-info → pip-25.3.dist-info}/licenses/LICENSE.txt +0 -0
@@ -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]