AXONVERTEX-AI-RESEARCH commited on
Commit
dd1ebd3
·
verified ·
1 Parent(s): e1b4af0

Publish GraphShieldMistral interactive explorer (part 4)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/_musllinux.py +85 -0
  2. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/_parser.py +393 -0
  3. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/_structures.py +33 -0
  4. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/_tokenizer.py +193 -0
  5. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/dependency_groups.py +302 -0
  6. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/direct_url.py +325 -0
  7. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/errors.py +94 -0
  8. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/licenses/__init__.py +186 -0
  9. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/licenses/_spdx.py +799 -0
  10. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/markers.py +492 -0
  11. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/metadata.py +964 -0
  12. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/py.typed +0 -0
  13. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/pylock.py +905 -0
  14. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/requirements.py +129 -0
  15. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/specifiers.py +1943 -0
  16. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/tags.py +932 -0
  17. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/utils.py +296 -0
  18. .venv/lib/python3.14/site-packages/pip/_vendor/packaging/version.py +1231 -0
  19. .venv/lib/python3.14/site-packages/pip/_vendor/pkg_resources/LICENSE +17 -0
  20. .venv/lib/python3.14/site-packages/pip/_vendor/pkg_resources/__init__.py +0 -0
  21. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/LICENSE +21 -0
  22. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/__init__.py +917 -0
  23. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/__main__.py +66 -0
  24. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/_xdg.py +171 -0
  25. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/android.py +300 -0
  26. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/api.py +463 -0
  27. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/macos.py +223 -0
  28. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/py.typed +0 -0
  29. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/unix.py +321 -0
  30. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/version.py +24 -0
  31. .venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/windows.py +395 -0
  32. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/LICENSE +25 -0
  33. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/__init__.py +82 -0
  34. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/__main__.py +17 -0
  35. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/console.py +70 -0
  36. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/filter.py +70 -0
  37. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/filters/__init__.py +942 -0
  38. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/formatter.py +129 -0
  39. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/formatters/__init__.py +157 -0
  40. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/formatters/_mapping.py +23 -0
  41. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/lexer.py +965 -0
  42. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/lexers/__init__.py +362 -0
  43. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/lexers/_mapping.py +603 -0
  44. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/lexers/python.py +1204 -0
  45. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/modeline.py +43 -0
  46. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/plugin.py +74 -0
  47. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/regexopt.py +102 -0
  48. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/scanner.py +104 -0
  49. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/sphinxext.py +247 -0
  50. .venv/lib/python3.14/site-packages/pip/_vendor/pygments/style.py +203 -0
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/_musllinux.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """PEP 656 support.
2
+
3
+ This module implements logic to detect if the currently running Python is
4
+ linked against musl, and what musl version is used.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import functools
10
+ import re
11
+ import subprocess
12
+ import sys
13
+ from typing import Iterator, NamedTuple, Sequence
14
+
15
+ from ._elffile import ELFFile
16
+
17
+
18
+ class _MuslVersion(NamedTuple):
19
+ major: int
20
+ minor: int
21
+
22
+
23
+ def _parse_musl_version(output: str) -> _MuslVersion | None:
24
+ lines = [n for n in (n.strip() for n in output.splitlines()) if n]
25
+ if len(lines) < 2 or lines[0][:4] != "musl":
26
+ return None
27
+ m = re.match(r"Version (\d+)\.(\d+)", lines[1])
28
+ if not m:
29
+ return None
30
+ return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2)))
31
+
32
+
33
+ @functools.lru_cache
34
+ def _get_musl_version(executable: str) -> _MuslVersion | None:
35
+ """Detect currently-running musl runtime version.
36
+
37
+ This is done by checking the specified executable's dynamic linking
38
+ information, and invoking the loader to parse its output for a version
39
+ string. If the loader is musl, the output would be something like::
40
+
41
+ musl libc (x86_64)
42
+ Version 1.2.2
43
+ Dynamic Program Loader
44
+ """
45
+ try:
46
+ with open(executable, "rb") as f:
47
+ ld = ELFFile(f).interpreter
48
+ except (OSError, TypeError, ValueError):
49
+ return None
50
+ if ld is None or "musl" not in ld:
51
+ return None
52
+ proc = subprocess.run([ld], check=False, stderr=subprocess.PIPE, text=True)
53
+ return _parse_musl_version(proc.stderr)
54
+
55
+
56
+ def platform_tags(archs: Sequence[str]) -> Iterator[str]:
57
+ """Generate musllinux tags compatible to the current platform.
58
+
59
+ :param archs: Sequence of compatible architectures.
60
+ The first one shall be the closest to the actual architecture and be the part of
61
+ platform tag after the ``linux_`` prefix, e.g. ``x86_64``.
62
+ The ``linux_`` prefix is assumed as a prerequisite for the current platform to
63
+ be musllinux-compatible.
64
+
65
+ :returns: An iterator of compatible musllinux tags.
66
+ """
67
+ sys_musl = _get_musl_version(sys.executable)
68
+ if sys_musl is None: # Python not dynamically linked against musl.
69
+ return
70
+ for arch in archs:
71
+ for minor in range(sys_musl.minor, -1, -1):
72
+ yield f"musllinux_{sys_musl.major}_{minor}_{arch}"
73
+
74
+
75
+ if __name__ == "__main__": # pragma: no cover
76
+ import sysconfig
77
+
78
+ plat = sysconfig.get_platform()
79
+ assert plat.startswith("linux-"), "not linux"
80
+
81
+ print("plat:", plat)
82
+ print("musl:", _get_musl_version(sys.executable))
83
+ print("tags:", end=" ")
84
+ for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])):
85
+ print(t, end="\n ")
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/_parser.py ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Handwritten parser of dependency specifiers.
2
+
3
+ The docstring for each __parse_* function contains EBNF-inspired grammar representing
4
+ the implementation.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import ast
10
+ from typing import List, Literal, NamedTuple, Sequence, Tuple, Union
11
+
12
+ from ._tokenizer import DEFAULT_RULES, Tokenizer
13
+
14
+
15
+ class Node:
16
+ __slots__ = ("value",)
17
+
18
+ def __init__(self, value: str) -> None:
19
+ self.value = value
20
+
21
+ def __str__(self) -> str:
22
+ return self.value
23
+
24
+ def __repr__(self) -> str:
25
+ return f"<{self.__class__.__name__}({self.value!r})>"
26
+
27
+ def serialize(self) -> str:
28
+ raise NotImplementedError
29
+
30
+ def __getstate__(self) -> str:
31
+ # Return just the value string for compactness and stability.
32
+ return self.value
33
+
34
+ def _restore_value(self, value: object) -> None:
35
+ if not isinstance(value, str):
36
+ raise TypeError(
37
+ f"Cannot restore {self.__class__.__name__} value from {value!r}"
38
+ )
39
+ self.value = value
40
+
41
+ def __setstate__(self, state: object) -> None:
42
+ if isinstance(state, str):
43
+ # New format (26.2+): just the value string.
44
+ self._restore_value(state)
45
+ return
46
+ if isinstance(state, tuple) and len(state) == 2:
47
+ # Old format (packaging <= 26.0, __slots__): (None, {slot: value}).
48
+ _, slot_dict = state
49
+ if isinstance(slot_dict, dict) and "value" in slot_dict:
50
+ self._restore_value(slot_dict["value"])
51
+ return
52
+ if isinstance(state, dict) and "value" in state:
53
+ # Old format (packaging <= 25.0, no __slots__): plain __dict__.
54
+ self._restore_value(state["value"])
55
+ return
56
+ raise TypeError(f"Cannot restore {self.__class__.__name__} from {state!r}")
57
+
58
+
59
+ class Variable(Node):
60
+ __slots__ = ()
61
+
62
+ def serialize(self) -> str:
63
+ return str(self)
64
+
65
+
66
+ class Value(Node):
67
+ __slots__ = ()
68
+
69
+ def serialize(self) -> str:
70
+ return f'"{self}"'
71
+
72
+
73
+ class Op(Node):
74
+ __slots__ = ()
75
+
76
+ def serialize(self) -> str:
77
+ return str(self)
78
+
79
+
80
+ MarkerLogical = Literal["and", "or"]
81
+ MarkerVar = Union[Variable, Value]
82
+ MarkerItem = Tuple[MarkerVar, Op, MarkerVar]
83
+ MarkerAtom = Union[MarkerItem, Sequence["MarkerAtom"]]
84
+ MarkerList = List[Union["MarkerList", MarkerAtom, MarkerLogical]]
85
+
86
+
87
+ class ParsedRequirement(NamedTuple):
88
+ name: str
89
+ url: str
90
+ extras: list[str]
91
+ specifier: str
92
+ marker: MarkerList | None
93
+
94
+
95
+ # --------------------------------------------------------------------------------------
96
+ # Recursive descent parser for dependency specifier
97
+ # --------------------------------------------------------------------------------------
98
+ def parse_requirement(source: str) -> ParsedRequirement:
99
+ return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES))
100
+
101
+
102
+ def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement:
103
+ """
104
+ requirement = WS? IDENTIFIER WS? extras WS? requirement_details
105
+ """
106
+ tokenizer.consume("WS")
107
+
108
+ name_token = tokenizer.expect(
109
+ "IDENTIFIER", expected="package name at the start of dependency specifier"
110
+ )
111
+ name = name_token.text
112
+ tokenizer.consume("WS")
113
+
114
+ extras = _parse_extras(tokenizer)
115
+ tokenizer.consume("WS")
116
+
117
+ url, specifier, marker = _parse_requirement_details(tokenizer)
118
+ tokenizer.expect("END", expected="end of dependency specifier")
119
+
120
+ return ParsedRequirement(name, url, extras, specifier, marker)
121
+
122
+
123
+ def _parse_requirement_details(
124
+ tokenizer: Tokenizer,
125
+ ) -> tuple[str, str, MarkerList | None]:
126
+ """
127
+ requirement_details = AT URL (WS requirement_marker?)?
128
+ | specifier WS? (requirement_marker)?
129
+ """
130
+
131
+ specifier = ""
132
+ url = ""
133
+ marker = None
134
+
135
+ if tokenizer.check("AT"):
136
+ tokenizer.read()
137
+ tokenizer.consume("WS")
138
+
139
+ url_start = tokenizer.position
140
+ url = tokenizer.expect("URL", expected="URL after @").text
141
+ if tokenizer.check("END", peek=True):
142
+ return (url, specifier, marker)
143
+
144
+ tokenizer.expect("WS", expected="whitespace after URL")
145
+
146
+ # The input might end after whitespace.
147
+ if tokenizer.check("END", peek=True):
148
+ return (url, specifier, marker)
149
+
150
+ marker = _parse_requirement_marker(
151
+ tokenizer,
152
+ span_start=url_start,
153
+ expected="semicolon (after URL and whitespace)",
154
+ )
155
+ else:
156
+ specifier_start = tokenizer.position
157
+ specifier = _parse_specifier(tokenizer)
158
+ tokenizer.consume("WS")
159
+
160
+ if tokenizer.check("END", peek=True):
161
+ return (url, specifier, marker)
162
+
163
+ marker = _parse_requirement_marker(
164
+ tokenizer,
165
+ span_start=specifier_start,
166
+ expected=(
167
+ "comma (within version specifier), semicolon (after version specifier)"
168
+ if specifier
169
+ else "semicolon (after name with no version specifier)"
170
+ ),
171
+ )
172
+
173
+ return (url, specifier, marker)
174
+
175
+
176
+ def _parse_requirement_marker(
177
+ tokenizer: Tokenizer, *, span_start: int, expected: str
178
+ ) -> MarkerList:
179
+ """
180
+ requirement_marker = SEMICOLON marker WS?
181
+ """
182
+
183
+ if not tokenizer.check("SEMICOLON"):
184
+ tokenizer.raise_syntax_error(
185
+ f"Expected {expected} or end",
186
+ span_start=span_start,
187
+ span_end=None,
188
+ )
189
+ tokenizer.read()
190
+
191
+ marker = _parse_marker(tokenizer)
192
+ tokenizer.consume("WS")
193
+
194
+ return marker
195
+
196
+
197
+ def _parse_extras(tokenizer: Tokenizer) -> list[str]:
198
+ """
199
+ extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)?
200
+ """
201
+ if not tokenizer.check("LEFT_BRACKET", peek=True):
202
+ return []
203
+
204
+ with tokenizer.enclosing_tokens(
205
+ "LEFT_BRACKET",
206
+ "RIGHT_BRACKET",
207
+ around="extras",
208
+ ):
209
+ tokenizer.consume("WS")
210
+ extras = _parse_extras_list(tokenizer)
211
+ tokenizer.consume("WS")
212
+
213
+ return extras
214
+
215
+
216
+ def _parse_extras_list(tokenizer: Tokenizer) -> list[str]:
217
+ """
218
+ extras_list = identifier (wsp* ',' wsp* identifier)*
219
+ """
220
+ extras: list[str] = []
221
+
222
+ if not tokenizer.check("IDENTIFIER"):
223
+ return extras
224
+
225
+ extras.append(tokenizer.read().text)
226
+
227
+ while True:
228
+ tokenizer.consume("WS")
229
+ if tokenizer.check("IDENTIFIER", peek=True):
230
+ tokenizer.raise_syntax_error("Expected comma between extra names")
231
+ elif not tokenizer.check("COMMA"):
232
+ break
233
+
234
+ tokenizer.read()
235
+ tokenizer.consume("WS")
236
+
237
+ extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma")
238
+ extras.append(extra_token.text)
239
+
240
+ return extras
241
+
242
+
243
+ def _parse_specifier(tokenizer: Tokenizer) -> str:
244
+ """
245
+ specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS
246
+ | WS? version_many WS?
247
+ """
248
+ with tokenizer.enclosing_tokens(
249
+ "LEFT_PARENTHESIS",
250
+ "RIGHT_PARENTHESIS",
251
+ around="version specifier",
252
+ ):
253
+ tokenizer.consume("WS")
254
+ parsed_specifiers = _parse_version_many(tokenizer)
255
+ tokenizer.consume("WS")
256
+
257
+ return parsed_specifiers
258
+
259
+
260
+ def _parse_version_many(tokenizer: Tokenizer) -> str:
261
+ """
262
+ version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)?
263
+ """
264
+ parsed_specifiers = ""
265
+ while tokenizer.check("SPECIFIER"):
266
+ span_start = tokenizer.position
267
+ parsed_specifiers += tokenizer.read().text
268
+ if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True):
269
+ tokenizer.raise_syntax_error(
270
+ ".* suffix can only be used with `==` or `!=` operators",
271
+ span_start=span_start,
272
+ span_end=tokenizer.position + 1,
273
+ )
274
+ if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True):
275
+ tokenizer.raise_syntax_error(
276
+ "Local version label can only be used with `==` or `!=` operators",
277
+ span_start=span_start,
278
+ span_end=tokenizer.position,
279
+ )
280
+ tokenizer.consume("WS")
281
+ if not tokenizer.check("COMMA"):
282
+ break
283
+ parsed_specifiers += tokenizer.read().text
284
+ tokenizer.consume("WS")
285
+
286
+ return parsed_specifiers
287
+
288
+
289
+ # --------------------------------------------------------------------------------------
290
+ # Recursive descent parser for marker expression
291
+ # --------------------------------------------------------------------------------------
292
+ def parse_marker(source: str) -> MarkerList:
293
+ return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES))
294
+
295
+
296
+ def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList:
297
+ retval = _parse_marker(tokenizer)
298
+ tokenizer.expect("END", expected="end of marker expression")
299
+ return retval
300
+
301
+
302
+ def _parse_marker(tokenizer: Tokenizer) -> MarkerList:
303
+ """
304
+ marker = marker_atom (BOOLOP marker_atom)+
305
+ """
306
+ expression = [_parse_marker_atom(tokenizer)]
307
+ while tokenizer.check("BOOLOP"):
308
+ token = tokenizer.read()
309
+ expr_right = _parse_marker_atom(tokenizer)
310
+ expression.extend((token.text, expr_right))
311
+ return expression
312
+
313
+
314
+ def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom:
315
+ """
316
+ marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS?
317
+ | WS? marker_item WS?
318
+ """
319
+
320
+ tokenizer.consume("WS")
321
+ if tokenizer.check("LEFT_PARENTHESIS", peek=True):
322
+ with tokenizer.enclosing_tokens(
323
+ "LEFT_PARENTHESIS",
324
+ "RIGHT_PARENTHESIS",
325
+ around="marker expression",
326
+ ):
327
+ tokenizer.consume("WS")
328
+ marker: MarkerAtom = _parse_marker(tokenizer)
329
+ tokenizer.consume("WS")
330
+ else:
331
+ marker = _parse_marker_item(tokenizer)
332
+ tokenizer.consume("WS")
333
+ return marker
334
+
335
+
336
+ def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem:
337
+ """
338
+ marker_item = WS? marker_var WS? marker_op WS? marker_var WS?
339
+ """
340
+ tokenizer.consume("WS")
341
+ marker_var_left = _parse_marker_var(tokenizer)
342
+ tokenizer.consume("WS")
343
+ marker_op = _parse_marker_op(tokenizer)
344
+ tokenizer.consume("WS")
345
+ marker_var_right = _parse_marker_var(tokenizer)
346
+ tokenizer.consume("WS")
347
+ return (marker_var_left, marker_op, marker_var_right)
348
+
349
+
350
+ def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: # noqa: RET503
351
+ """
352
+ marker_var = VARIABLE | QUOTED_STRING
353
+ """
354
+ if tokenizer.check("VARIABLE"):
355
+ return process_env_var(tokenizer.read().text.replace(".", "_"))
356
+ elif tokenizer.check("QUOTED_STRING"):
357
+ return process_python_str(tokenizer.read().text)
358
+ else:
359
+ tokenizer.raise_syntax_error(
360
+ message="Expected a marker variable or quoted string"
361
+ )
362
+
363
+
364
+ def process_env_var(env_var: str) -> Variable:
365
+ if env_var in ("platform_python_implementation", "python_implementation"):
366
+ return Variable("platform_python_implementation")
367
+ else:
368
+ return Variable(env_var)
369
+
370
+
371
+ def process_python_str(python_str: str) -> Value:
372
+ value = ast.literal_eval(python_str)
373
+ return Value(str(value))
374
+
375
+
376
+ def _parse_marker_op(tokenizer: Tokenizer) -> Op:
377
+ """
378
+ marker_op = IN | NOT IN | OP
379
+ """
380
+ if tokenizer.check("IN"):
381
+ tokenizer.read()
382
+ return Op("in")
383
+ elif tokenizer.check("NOT"):
384
+ tokenizer.read()
385
+ tokenizer.expect("WS", expected="whitespace after 'not'")
386
+ tokenizer.expect("IN", expected="'in' after 'not'")
387
+ return Op("not in")
388
+ elif tokenizer.check("OP"):
389
+ return Op(tokenizer.read().text)
390
+ else:
391
+ return tokenizer.raise_syntax_error(
392
+ "Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in"
393
+ )
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/_structures.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file is dual licensed under the terms of the Apache License, Version
2
+ # 2.0, and the BSD License. See the LICENSE file in the root of this repository
3
+ # for complete details.
4
+
5
+ """Backward-compatibility shim for unpickling Version objects serialized before
6
+ packaging 26.1.
7
+
8
+ Old pickles reference ``packaging._structures.InfinityType`` and
9
+ ``packaging._structures.NegativeInfinityType``. This module provides minimal
10
+ stand-in classes so that ``pickle.loads()`` can resolve those references.
11
+ The deserialized objects are not used for comparisons — ``Version.__setstate__``
12
+ discards the stale ``_key`` cache and recomputes it from the core version fields.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+
18
+ class InfinityType:
19
+ """Stand-in for the removed ``InfinityType`` used in old comparison keys."""
20
+
21
+ def __repr__(self) -> str:
22
+ return "Infinity"
23
+
24
+
25
+ class NegativeInfinityType:
26
+ """Stand-in for the removed ``NegativeInfinityType`` used in old comparison keys."""
27
+
28
+ def __repr__(self) -> str:
29
+ return "-Infinity"
30
+
31
+
32
+ Infinity = InfinityType()
33
+ NegativeInfinity = NegativeInfinityType()
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/_tokenizer.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import re
5
+ from dataclasses import dataclass
6
+ from typing import Generator, Mapping, NoReturn
7
+
8
+ from .specifiers import Specifier
9
+
10
+
11
+ @dataclass
12
+ class Token:
13
+ name: str
14
+ text: str
15
+ position: int
16
+
17
+
18
+ class ParserSyntaxError(Exception):
19
+ """The provided source text could not be parsed correctly."""
20
+
21
+ def __init__(
22
+ self,
23
+ message: str,
24
+ *,
25
+ source: str,
26
+ span: tuple[int, int],
27
+ ) -> None:
28
+ self.span = span
29
+ self.message = message
30
+ self.source = source
31
+
32
+ super().__init__()
33
+
34
+ def __str__(self) -> str:
35
+ marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^"
36
+ return f"{self.message}\n {self.source}\n {marker}"
37
+
38
+
39
+ DEFAULT_RULES: dict[str, re.Pattern[str]] = {
40
+ "LEFT_PARENTHESIS": re.compile(r"\("),
41
+ "RIGHT_PARENTHESIS": re.compile(r"\)"),
42
+ "LEFT_BRACKET": re.compile(r"\["),
43
+ "RIGHT_BRACKET": re.compile(r"\]"),
44
+ "SEMICOLON": re.compile(r";"),
45
+ "COMMA": re.compile(r","),
46
+ "QUOTED_STRING": re.compile(
47
+ r"""
48
+ (
49
+ ('[^']*')
50
+ |
51
+ ("[^"]*")
52
+ )
53
+ """,
54
+ re.VERBOSE,
55
+ ),
56
+ "OP": re.compile(r"(===|==|~=|!=|<=|>=|<|>)"),
57
+ "BOOLOP": re.compile(r"\b(or|and)\b"),
58
+ "IN": re.compile(r"\bin\b"),
59
+ "NOT": re.compile(r"\bnot\b"),
60
+ "VARIABLE": re.compile(
61
+ r"""
62
+ \b(
63
+ python_version
64
+ |python_full_version
65
+ |os[._]name
66
+ |sys[._]platform
67
+ |platform_(release|system)
68
+ |platform[._](version|machine|python_implementation)
69
+ |python_implementation
70
+ |implementation_(name|version)
71
+ |extras?
72
+ |dependency_groups
73
+ )\b
74
+ """,
75
+ re.VERBOSE,
76
+ ),
77
+ "SPECIFIER": re.compile(
78
+ Specifier._specifier_regex_str,
79
+ re.VERBOSE | re.IGNORECASE,
80
+ ),
81
+ "AT": re.compile(r"\@"),
82
+ "URL": re.compile(r"[^ \t]+"),
83
+ "IDENTIFIER": re.compile(r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b"),
84
+ "VERSION_PREFIX_TRAIL": re.compile(r"\.\*"),
85
+ "VERSION_LOCAL_LABEL_TRAIL": re.compile(r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*"),
86
+ "WS": re.compile(r"[ \t]+"),
87
+ "END": re.compile(r"$"),
88
+ }
89
+
90
+
91
+ class Tokenizer:
92
+ """Context-sensitive token parsing.
93
+
94
+ Provides methods to examine the input stream to check whether the next token
95
+ matches.
96
+ """
97
+
98
+ def __init__(
99
+ self,
100
+ source: str,
101
+ *,
102
+ rules: Mapping[str, re.Pattern[str]],
103
+ ) -> None:
104
+ self.source = source
105
+ self.rules = rules
106
+ self.next_token: Token | None = None
107
+ self.position = 0
108
+
109
+ def consume(self, name: str) -> None:
110
+ """Move beyond provided token name, if at current position."""
111
+ if self.check(name):
112
+ self.read()
113
+
114
+ def check(self, name: str, *, peek: bool = False) -> bool:
115
+ """Check whether the next token has the provided name.
116
+
117
+ By default, if the check succeeds, the token *must* be read before
118
+ another check. If `peek` is set to `True`, the token is not loaded and
119
+ would need to be checked again.
120
+ """
121
+ assert self.next_token is None, (
122
+ f"Cannot check for {name!r}, already have {self.next_token!r}"
123
+ )
124
+ assert name in self.rules, f"Unknown token name: {name!r}"
125
+
126
+ expression = self.rules[name]
127
+
128
+ match = expression.match(self.source, self.position)
129
+ if match is None:
130
+ return False
131
+ if not peek:
132
+ self.next_token = Token(name, match[0], self.position)
133
+ return True
134
+
135
+ def expect(self, name: str, *, expected: str) -> Token:
136
+ """Expect a certain token name next, failing with a syntax error otherwise.
137
+
138
+ The token is *not* read.
139
+ """
140
+ if not self.check(name):
141
+ raise self.raise_syntax_error(f"Expected {expected}")
142
+ return self.read()
143
+
144
+ def read(self) -> Token:
145
+ """Consume the next token and return it."""
146
+ token = self.next_token
147
+ assert token is not None
148
+
149
+ self.position += len(token.text)
150
+ self.next_token = None
151
+
152
+ return token
153
+
154
+ def raise_syntax_error(
155
+ self,
156
+ message: str,
157
+ *,
158
+ span_start: int | None = None,
159
+ span_end: int | None = None,
160
+ ) -> NoReturn:
161
+ """Raise ParserSyntaxError at the given position."""
162
+ span = (
163
+ self.position if span_start is None else span_start,
164
+ self.position if span_end is None else span_end,
165
+ )
166
+ raise ParserSyntaxError(
167
+ message,
168
+ source=self.source,
169
+ span=span,
170
+ )
171
+
172
+ @contextlib.contextmanager
173
+ def enclosing_tokens(
174
+ self, open_token: str, close_token: str, *, around: str
175
+ ) -> Generator[None, None, None]:
176
+ if self.check(open_token):
177
+ open_position = self.position
178
+ self.read()
179
+ else:
180
+ open_position = None
181
+
182
+ yield
183
+
184
+ if open_position is None:
185
+ return
186
+
187
+ if not self.check(close_token):
188
+ self.raise_syntax_error(
189
+ f"Expected matching {close_token} for {open_token}, after {around}",
190
+ span_start=open_position,
191
+ )
192
+
193
+ self.read()
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/dependency_groups.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from collections.abc import Mapping, Sequence
5
+
6
+ from .errors import _ErrorCollector
7
+ from .requirements import Requirement
8
+
9
+ __all__ = [
10
+ "CyclicDependencyGroup",
11
+ "DependencyGroupInclude",
12
+ "DependencyGroupResolver",
13
+ "DuplicateGroupNames",
14
+ "InvalidDependencyGroupObject",
15
+ "resolve_dependency_groups",
16
+ ]
17
+
18
+
19
+ def __dir__() -> list[str]:
20
+ return __all__
21
+
22
+
23
+ # -----------
24
+ # Error Types
25
+ # -----------
26
+
27
+
28
+ class DuplicateGroupNames(ValueError):
29
+ """
30
+ The same dependency groups were defined twice, with different non-normalized names.
31
+ """
32
+
33
+
34
+ class CyclicDependencyGroup(ValueError):
35
+ """
36
+ The dependency group includes form a cycle.
37
+ """
38
+
39
+ def __init__(self, requested_group: str, group: str, include_group: str) -> None:
40
+ self.requested_group = requested_group
41
+ self.group = group
42
+ self.include_group = include_group
43
+
44
+ if include_group == group:
45
+ reason = f"{group} includes itself"
46
+ else:
47
+ reason = f"{include_group} -> {group}, {group} -> {include_group}"
48
+ super().__init__(
49
+ "Cyclic dependency group include while resolving "
50
+ f"{requested_group}: {reason}"
51
+ )
52
+
53
+
54
+ # in the PEP 735 spec, the tables in dependency group lists were described as
55
+ # "Dependency Object Specifiers", but the only defined type of object was a
56
+ # "Dependency Group Include" -- hence the naming of this error as "Object"
57
+ class InvalidDependencyGroupObject(ValueError):
58
+ """
59
+ A member of a dependency group was identified as a dict, but was not in a valid
60
+ format.
61
+ """
62
+
63
+
64
+ # ------------------------
65
+ # Object Model & Interface
66
+ # ------------------------
67
+
68
+
69
+ class DependencyGroupInclude:
70
+ __slots__ = ("include_group",)
71
+
72
+ def __init__(self, include_group: str) -> None:
73
+ """
74
+ Initialize a DependencyGroupInclude.
75
+
76
+ :param include_group: The name of the group referred to by this include.
77
+ """
78
+ self.include_group = include_group
79
+
80
+ def __repr__(self) -> str:
81
+ return f"{self.__class__.__name__}({self.include_group!r})"
82
+
83
+
84
+ class DependencyGroupResolver:
85
+ """
86
+ A resolver for Dependency Group data.
87
+
88
+ This class handles caching, name normalization, cycle detection, and other
89
+ parsing requirements. There are only two public methods for exploring the data:
90
+ ``lookup()`` and ``resolve()``.
91
+
92
+ :param dependency_groups: A mapping, as provided via pyproject
93
+ ``[dependency-groups]``.
94
+ """
95
+
96
+ def __init__(
97
+ self,
98
+ dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]],
99
+ ) -> None:
100
+ errors = _ErrorCollector()
101
+
102
+ self.dependency_groups = _normalize_group_names(dependency_groups, errors)
103
+
104
+ # a map of group names to parsed data
105
+ self._parsed_groups: dict[
106
+ str, tuple[Requirement | DependencyGroupInclude, ...]
107
+ ] = {}
108
+ # a map of group names to their ancestors, used for cycle detection
109
+ self._include_graph_ancestors: dict[str, tuple[str, ...]] = {}
110
+ # a cache of completed resolutions to Requirement lists
111
+ self._resolve_cache: dict[str, tuple[Requirement, ...]] = {}
112
+
113
+ errors.finalize("[dependency-groups] data was invalid")
114
+
115
+ def lookup(self, group: str) -> tuple[Requirement | DependencyGroupInclude, ...]:
116
+ """
117
+ Lookup a group name, returning the parsed dependency data for that group.
118
+ This will not resolve includes.
119
+
120
+ :param group: the name of the group to lookup
121
+ """
122
+ group = _normalize_name(group)
123
+
124
+ with _ErrorCollector().on_exit(
125
+ f"[dependency-groups] data for {group!r} was malformed"
126
+ ) as errors:
127
+ return self._parse_group(group, errors)
128
+
129
+ def resolve(self, group: str) -> tuple[Requirement, ...]:
130
+ """
131
+ Resolve a dependency group to a list of requirements.
132
+
133
+ :param group: the name of the group to resolve
134
+ """
135
+ group = _normalize_name(group)
136
+
137
+ with _ErrorCollector().on_exit(
138
+ f"[dependency-groups] data for {group!r} was malformed"
139
+ ) as errors:
140
+ return self._resolve(group, group, errors)
141
+
142
+ def _resolve(
143
+ self, group: str, requested_group: str, errors: _ErrorCollector
144
+ ) -> tuple[Requirement, ...]:
145
+ """
146
+ This is a helper for cached resolution to strings. It preserves the name of the
147
+ group which the user initially requested in order to present a clearer error in
148
+ the event that a cycle is detected.
149
+
150
+ :param group: The normalized name of the group to resolve.
151
+ :param requested_group: The group which was used in the original, user-facing
152
+ request.
153
+ """
154
+ if group in self._resolve_cache:
155
+ return self._resolve_cache[group]
156
+
157
+ parsed = self._parse_group(group, errors)
158
+
159
+ resolved_group = []
160
+
161
+ for item in parsed:
162
+ if isinstance(item, Requirement):
163
+ resolved_group.append(item)
164
+ elif isinstance(item, DependencyGroupInclude):
165
+ include_group = _normalize_name(item.include_group)
166
+
167
+ # if a group is cyclic, record the error
168
+ # otherwise, follow the include_group reference
169
+ #
170
+ # this allows us to examine all includes in a group, even in the
171
+ # presence of errors
172
+ if include_group in self._include_graph_ancestors.get(group, ()):
173
+ errors.error(
174
+ CyclicDependencyGroup(
175
+ requested_group, group, item.include_group
176
+ )
177
+ )
178
+ else:
179
+ self._include_graph_ancestors[include_group] = (
180
+ *self._include_graph_ancestors.get(group, ()),
181
+ group,
182
+ )
183
+ resolved_group.extend(
184
+ self._resolve(include_group, requested_group, errors)
185
+ )
186
+ else: # pragma: no cover
187
+ raise NotImplementedError(
188
+ f"Invalid dependency group item after parse: {item}"
189
+ )
190
+
191
+ # in the event that errors were detected, present the group as empty and do not
192
+ # cache the result
193
+ # this ensures that repeated access to a cyclic group will raise multiple errors
194
+ if errors.errors:
195
+ return ()
196
+
197
+ self._resolve_cache[group] = tuple(resolved_group)
198
+ return self._resolve_cache[group]
199
+
200
+ def _parse_group(
201
+ self, group: str, errors: _ErrorCollector
202
+ ) -> tuple[Requirement | DependencyGroupInclude, ...]:
203
+ # short circuit -- never do the work twice
204
+ if group in self._parsed_groups:
205
+ return self._parsed_groups[group]
206
+
207
+ if group not in self.dependency_groups:
208
+ errors.error(LookupError(f"Dependency group '{group}' not found"))
209
+ return ()
210
+
211
+ raw_group = self.dependency_groups[group]
212
+ if isinstance(raw_group, str):
213
+ errors.error(
214
+ TypeError(
215
+ f"Dependency group {group!r} contained a string rather than a list."
216
+ )
217
+ )
218
+ return ()
219
+
220
+ if not isinstance(raw_group, Sequence):
221
+ errors.error(
222
+ TypeError(f"Dependency group {group!r} is not a sequence type.")
223
+ )
224
+ return ()
225
+
226
+ elements: list[Requirement | DependencyGroupInclude] = []
227
+ for item in raw_group:
228
+ if isinstance(item, str):
229
+ # packaging.requirements.Requirement parsing ensures that this is a
230
+ # valid PEP 508 Dependency Specifier
231
+ # raises InvalidRequirement on failure
232
+ elements.append(Requirement(item))
233
+ elif isinstance(item, Mapping):
234
+ if tuple(item.keys()) != ("include-group",):
235
+ errors.error(
236
+ InvalidDependencyGroupObject(
237
+ f"Invalid dependency group item: {item!r}"
238
+ )
239
+ )
240
+ else:
241
+ include_group = item["include-group"]
242
+ elements.append(DependencyGroupInclude(include_group=include_group))
243
+ else:
244
+ errors.error(TypeError(f"Invalid dependency group item: {item!r}"))
245
+
246
+ self._parsed_groups[group] = tuple(elements)
247
+ return self._parsed_groups[group]
248
+
249
+
250
+ # --------------------
251
+ # Functional Interface
252
+ # --------------------
253
+
254
+
255
+ def resolve_dependency_groups(
256
+ dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]], /, *groups: str
257
+ ) -> tuple[str, ...]:
258
+ """
259
+ Resolve a dependency group to a tuple of requirements, as strings.
260
+
261
+ :param dependency_groups: the parsed contents of the ``[dependency-groups]`` table
262
+ from ``pyproject.toml``
263
+ :param groups: the name of the group(s) to resolve
264
+ """
265
+ resolver = DependencyGroupResolver(dependency_groups)
266
+ return tuple(str(r) for group in groups for r in resolver.resolve(group))
267
+
268
+
269
+ # ----------------
270
+ # internal helpers
271
+ # ----------------
272
+
273
+
274
+ _NORMALIZE_PATTERN = re.compile(r"[-_.]+")
275
+
276
+
277
+ def _normalize_name(name: str) -> str:
278
+ return _NORMALIZE_PATTERN.sub("-", name).lower()
279
+
280
+
281
+ def _normalize_group_names(
282
+ dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]],
283
+ errors: _ErrorCollector,
284
+ ) -> dict[str, Sequence[str | Mapping[str, str]]]:
285
+ original_names: dict[str, list[str]] = {}
286
+ normalized_groups: dict[str, Sequence[str | Mapping[str, str]]] = {}
287
+
288
+ for group_name, value in dependency_groups.items():
289
+ normed_group_name = _normalize_name(group_name)
290
+ original_names.setdefault(normed_group_name, []).append(group_name)
291
+ normalized_groups[normed_group_name] = value
292
+
293
+ for normed_name, names in original_names.items():
294
+ if len(names) > 1:
295
+ errors.error(
296
+ DuplicateGroupNames(
297
+ "Duplicate dependency group names: "
298
+ f"{normed_name} ({', '.join(names)})"
299
+ )
300
+ )
301
+
302
+ return normalized_groups
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/direct_url.py ADDED
@@ -0,0 +1,325 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import re
5
+ import urllib.parse
6
+ from collections.abc import Mapping
7
+ from typing import TYPE_CHECKING, Any, Protocol, TypeVar
8
+
9
+ if TYPE_CHECKING: # pragma: no cover
10
+ import sys
11
+ from collections.abc import Collection
12
+
13
+ if sys.version_info >= (3, 11):
14
+ from typing import Self
15
+ else:
16
+ from typing_extensions import Self
17
+
18
+ __all__ = [
19
+ "ArchiveInfo",
20
+ "DirInfo",
21
+ "DirectUrl",
22
+ "DirectUrlValidationError",
23
+ "VcsInfo",
24
+ ]
25
+
26
+
27
+ def __dir__() -> list[str]:
28
+ return __all__
29
+
30
+
31
+ _T = TypeVar("_T")
32
+
33
+
34
+ class _FromMappingProtocol(Protocol): # pragma: no cover
35
+ @classmethod
36
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self: ...
37
+
38
+
39
+ _FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol)
40
+
41
+
42
+ def _json_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]:
43
+ return {key: value for key, value in data if value is not None}
44
+
45
+
46
+ def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None:
47
+ """Get a value from the dictionary and verify it's the expected type."""
48
+ if (value := d.get(key)) is None:
49
+ return None
50
+ if not isinstance(value, expected_type):
51
+ raise DirectUrlValidationError(
52
+ f"Unexpected type {type(value).__name__} "
53
+ f"(expected {expected_type.__name__})",
54
+ context=key,
55
+ )
56
+ return value
57
+
58
+
59
+ def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T:
60
+ """Get a required value from the dictionary and verify it's the expected type."""
61
+ if (value := _get(d, expected_type, key)) is None:
62
+ raise _DirectUrlRequiredKeyError(key)
63
+ return value
64
+
65
+
66
+ def _get_object(
67
+ d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str
68
+ ) -> _FromMappingProtocolT | None:
69
+ """Get a dictionary value from the dictionary and convert it to a dataclass."""
70
+ if (value := _get(d, Mapping, key)) is None: # type: ignore[type-abstract]
71
+ return None
72
+ try:
73
+ return target_type._from_dict(value)
74
+ except Exception as e:
75
+ raise DirectUrlValidationError(e, context=key) from e
76
+
77
+
78
+ _PEP610_USER_PASS_ENV_VARS_REGEX = re.compile(
79
+ r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$"
80
+ )
81
+
82
+
83
+ def _strip_auth_from_netloc(netloc: str, safe_user_passwords: Collection[str]) -> str:
84
+ if "@" not in netloc:
85
+ return netloc
86
+ user_pass, netloc_no_user_pass = netloc.split("@", 1)
87
+ if user_pass in safe_user_passwords:
88
+ return netloc
89
+ if _PEP610_USER_PASS_ENV_VARS_REGEX.match(user_pass):
90
+ return netloc
91
+ return netloc_no_user_pass
92
+
93
+
94
+ def _strip_url(url: str, safe_user_passwords: Collection[str]) -> str:
95
+ """url with user:password part removed unless it is formed with
96
+ environment variables as specified in PEP 610, or it is a safe user:password
97
+ such as `git`.
98
+ """
99
+ parsed_url = urllib.parse.urlsplit(url)
100
+ netloc = _strip_auth_from_netloc(parsed_url.netloc, safe_user_passwords)
101
+ return urllib.parse.urlunsplit(
102
+ (
103
+ parsed_url.scheme,
104
+ netloc,
105
+ parsed_url.path,
106
+ parsed_url.query,
107
+ parsed_url.fragment,
108
+ )
109
+ )
110
+
111
+
112
+ class DirectUrlValidationError(Exception):
113
+ """Raised when when input data is not spec-compliant."""
114
+
115
+ context: str | None = None
116
+ message: str
117
+
118
+ def __init__(
119
+ self,
120
+ cause: str | Exception,
121
+ *,
122
+ context: str | None = None,
123
+ ) -> None:
124
+ if isinstance(cause, DirectUrlValidationError):
125
+ if cause.context:
126
+ self.context = (
127
+ f"{context}.{cause.context}" if context else cause.context
128
+ )
129
+ else:
130
+ self.context = context # pragma: no cover
131
+ self.message = cause.message
132
+ else:
133
+ self.context = context
134
+ self.message = str(cause)
135
+
136
+ def __str__(self) -> str:
137
+ if self.context:
138
+ return f"{self.message} in {self.context!r}"
139
+ return self.message
140
+
141
+
142
+ class _DirectUrlRequiredKeyError(DirectUrlValidationError):
143
+ def __init__(self, key: str) -> None:
144
+ super().__init__("Missing required value", context=key)
145
+
146
+
147
+ @dataclasses.dataclass(frozen=True, init=False)
148
+ class VcsInfo:
149
+ vcs: str
150
+ commit_id: str
151
+ requested_revision: str | None = None
152
+
153
+ def __init__(
154
+ self,
155
+ *,
156
+ vcs: str,
157
+ commit_id: str,
158
+ requested_revision: str | None = None,
159
+ ) -> None:
160
+ object.__setattr__(self, "vcs", vcs)
161
+ object.__setattr__(self, "commit_id", commit_id)
162
+ object.__setattr__(self, "requested_revision", requested_revision)
163
+
164
+ @classmethod
165
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
166
+ # We can't validate vcs value because is not closed.
167
+ return cls(
168
+ vcs=_get_required(d, str, "vcs"),
169
+ requested_revision=_get(d, str, "requested_revision"),
170
+ commit_id=_get_required(d, str, "commit_id"),
171
+ )
172
+
173
+
174
+ @dataclasses.dataclass(frozen=True, init=False)
175
+ class ArchiveInfo:
176
+ hashes: Mapping[str, str] | None = None
177
+
178
+ def __init__(
179
+ self,
180
+ *,
181
+ hashes: Mapping[str, str] | None = None,
182
+ ) -> None:
183
+ object.__setattr__(self, "hashes", hashes)
184
+
185
+ @classmethod
186
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
187
+ hashes = _get(d, Mapping, "hashes") # type: ignore[type-abstract]
188
+ if hashes is not None and not all(isinstance(h, str) for h in hashes.values()):
189
+ raise DirectUrlValidationError(
190
+ "Hash values must be strings", context="hashes"
191
+ )
192
+ legacy_hash = _get(d, str, "hash")
193
+ if legacy_hash is not None:
194
+ if "=" not in legacy_hash:
195
+ raise DirectUrlValidationError(
196
+ "Invalid hash format (expected '<algorithm>=<hash>')",
197
+ context="hash",
198
+ )
199
+ hash_algorithm, hash_value = legacy_hash.split("=", 1)
200
+ if hashes is None:
201
+ # if `hashes` are not present, we can derive it from the legacy `hash`
202
+ hashes = {hash_algorithm: hash_value}
203
+ else:
204
+ # if `hashes` are present, the legacy `hash` must match one of them
205
+ if hash_algorithm not in hashes:
206
+ raise DirectUrlValidationError(
207
+ f"Algorithm {hash_algorithm!r} used in hash field "
208
+ f"is not present in hashes field",
209
+ context="hashes",
210
+ )
211
+ if hashes[hash_algorithm] != hash_value:
212
+ raise DirectUrlValidationError(
213
+ f"Algorithm {hash_algorithm!r} used in hash field "
214
+ f"has different value in hashes field",
215
+ context="hash",
216
+ )
217
+ return cls(hashes=hashes)
218
+
219
+
220
+ @dataclasses.dataclass(frozen=True, init=False)
221
+ class DirInfo:
222
+ editable: bool | None = None
223
+
224
+ def __init__(
225
+ self,
226
+ *,
227
+ editable: bool | None = None,
228
+ ) -> None:
229
+ object.__setattr__(self, "editable", editable)
230
+
231
+ @classmethod
232
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
233
+ return cls(
234
+ editable=_get(d, bool, "editable"),
235
+ )
236
+
237
+
238
+ @dataclasses.dataclass(frozen=True, init=False)
239
+ class DirectUrl:
240
+ """A class representing a direct URL."""
241
+
242
+ url: str
243
+ archive_info: ArchiveInfo | None = None
244
+ vcs_info: VcsInfo | None = None
245
+ dir_info: DirInfo | None = None
246
+ subdirectory: str | None = None # XXX Path or str?
247
+
248
+ def __init__(
249
+ self,
250
+ *,
251
+ url: str,
252
+ archive_info: ArchiveInfo | None = None,
253
+ vcs_info: VcsInfo | None = None,
254
+ dir_info: DirInfo | None = None,
255
+ subdirectory: str | None = None,
256
+ ) -> None:
257
+ object.__setattr__(self, "url", url)
258
+ object.__setattr__(self, "archive_info", archive_info)
259
+ object.__setattr__(self, "vcs_info", vcs_info)
260
+ object.__setattr__(self, "dir_info", dir_info)
261
+ object.__setattr__(self, "subdirectory", subdirectory)
262
+
263
+ @classmethod
264
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
265
+ direct_url = cls(
266
+ url=_get_required(d, str, "url"),
267
+ archive_info=_get_object(d, ArchiveInfo, "archive_info"),
268
+ vcs_info=_get_object(d, VcsInfo, "vcs_info"),
269
+ dir_info=_get_object(d, DirInfo, "dir_info"),
270
+ subdirectory=_get(d, str, "subdirectory"),
271
+ )
272
+ if (
273
+ bool(direct_url.vcs_info)
274
+ + bool(direct_url.archive_info)
275
+ + bool(direct_url.dir_info)
276
+ ) != 1:
277
+ raise DirectUrlValidationError(
278
+ "Exactly one of vcs_info, archive_info, dir_info must be present"
279
+ )
280
+ if direct_url.dir_info is not None and not direct_url.url.startswith("file://"):
281
+ raise DirectUrlValidationError(
282
+ "URL scheme must be file:// when dir_info is present",
283
+ context="url",
284
+ )
285
+ # XXX subdirectory must be relative, can we, should we validate that here?
286
+ return direct_url
287
+
288
+ @classmethod
289
+ def from_dict(cls, d: Mapping[str, Any], /) -> Self:
290
+ """Create and validate a DirectUrl instance from a JSON dictionary."""
291
+ return cls._from_dict(d)
292
+
293
+ def to_dict(
294
+ self,
295
+ *,
296
+ generate_legacy_hash: bool = False,
297
+ strip_user_password: bool = True,
298
+ safe_user_passwords: Collection[str] = ("git",),
299
+ ) -> Mapping[str, Any]:
300
+ """Convert the DirectUrl instance to a JSON dictionary.
301
+
302
+ :param generate_legacy_hash: If True, include a legacy `hash` field in
303
+ `archive_info` for backward compatibility with tools that don't
304
+ support the `hashes` field.
305
+ :param strip_user_password: If True, strip user:password from the URL
306
+ unless it is formed with environment variables as specified in PEP
307
+ 610, or it is a safe user:password such as `git`.
308
+ :param safe_user_passwords: A collection of user:password strings that
309
+ should not be stripped from the URL even if `strip_user_password` is
310
+ True.
311
+ """
312
+ res = dataclasses.asdict(self, dict_factory=_json_dict_factory)
313
+ if generate_legacy_hash and self.archive_info and self.archive_info.hashes:
314
+ hash_algorithm, hash_value = next(iter(self.archive_info.hashes.items()))
315
+ res["archive_info"]["hash"] = f"{hash_algorithm}={hash_value}"
316
+ if strip_user_password:
317
+ res["url"] = _strip_url(self.url, safe_user_passwords)
318
+ return res
319
+
320
+ def validate(self) -> None:
321
+ """Validate the DirectUrl instance against the specification.
322
+
323
+ Raises :class:`DirectUrlValidationError` if invalid.
324
+ """
325
+ self.from_dict(self.to_dict())
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/errors.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import dataclasses
5
+ import sys
6
+ import typing
7
+
8
+ __all__ = ["ExceptionGroup"]
9
+
10
+
11
+ def __dir__() -> list[str]:
12
+ return __all__
13
+
14
+
15
+ if sys.version_info >= (3, 11): # pragma: no cover
16
+ from builtins import ExceptionGroup
17
+ else: # pragma: no cover
18
+
19
+ class ExceptionGroup(Exception):
20
+ """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11.
21
+
22
+ If :external:exc:`ExceptionGroup` is already defined by Python itself,
23
+ that version is used instead.
24
+ """
25
+
26
+ message: str
27
+ exceptions: list[Exception]
28
+
29
+ def __init__(self, message: str, exceptions: list[Exception]) -> None:
30
+ self.message = message
31
+ self.exceptions = exceptions
32
+
33
+ def __repr__(self) -> str:
34
+ return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})"
35
+
36
+
37
+ @dataclasses.dataclass
38
+ class _ErrorCollector:
39
+ """
40
+ Collect errors into ExceptionGroups.
41
+
42
+ Used like this:
43
+
44
+ collector = _ErrorCollector()
45
+ # Add a single exception
46
+ collector.error(ValueError("one"))
47
+
48
+ # Supports nesting, including combining ExceptionGroups
49
+ with collector.collect():
50
+ raise ValueError("two")
51
+ collector.finalize("Found some errors")
52
+
53
+ Since making a collector and then calling finalize later is a common pattern,
54
+ a convenience method ``on_exit`` is provided.
55
+ """
56
+
57
+ errors: list[Exception] = dataclasses.field(default_factory=list, init=False)
58
+
59
+ def finalize(self, msg: str) -> None:
60
+ """Raise a group exception if there are any errors."""
61
+ if self.errors:
62
+ raise ExceptionGroup(msg, self.errors)
63
+
64
+ @contextlib.contextmanager
65
+ def on_exit(self, msg: str) -> typing.Generator[_ErrorCollector, None, None]:
66
+ """
67
+ Calls finalize if no uncollected errors were present.
68
+
69
+ Uncollected errors are raised normally.
70
+ """
71
+ yield self
72
+ self.finalize(msg)
73
+
74
+ @contextlib.contextmanager
75
+ def collect(self, *err_cls: type[Exception]) -> typing.Generator[None, None, None]:
76
+ """
77
+ Context manager to collect errors into the error list.
78
+
79
+ Must be inside loops, as only one error can be collected at a time.
80
+ """
81
+ error_classes = err_cls or (Exception,)
82
+ try:
83
+ yield
84
+ except ExceptionGroup as error:
85
+ self.errors.extend(error.exceptions)
86
+ except error_classes as error:
87
+ self.errors.append(error)
88
+
89
+ def error(
90
+ self,
91
+ error: Exception,
92
+ ) -> None:
93
+ """Add an error to the list."""
94
+ self.errors.append(error)
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/licenses/__init__.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #######################################################################################
2
+ #
3
+ # Adapted from:
4
+ # https://github.com/pypa/hatch/blob/5352e44/backend/src/hatchling/licenses/parse.py
5
+ #
6
+ # MIT License
7
+ #
8
+ # Copyright (c) 2017-present Ofek Lev <oss@ofek.dev>
9
+ #
10
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this
11
+ # software and associated documentation files (the "Software"), to deal in the Software
12
+ # without restriction, including without limitation the rights to use, copy, modify,
13
+ # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to the following
15
+ # conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be included in all copies
18
+ # or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
21
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
22
+ # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24
+ # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
25
+ # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
+ #
27
+ #
28
+ # With additional allowance of arbitrary `LicenseRef-` identifiers, not just
29
+ # `LicenseRef-Public-Domain` and `LicenseRef-Proprietary`.
30
+ #
31
+ #######################################################################################
32
+ from __future__ import annotations
33
+
34
+ import re
35
+ from typing import NewType, cast
36
+
37
+ from ._spdx import EXCEPTIONS, LICENSES
38
+
39
+ __all__ = [
40
+ "InvalidLicenseExpression",
41
+ "NormalizedLicenseExpression",
42
+ "canonicalize_license_expression",
43
+ ]
44
+
45
+
46
+ # Simple __dir__ implementation since there are no public submodules
47
+ def __dir__() -> list[str]:
48
+ return __all__
49
+
50
+
51
+ license_ref_allowed = re.compile("^[A-Za-z0-9.-]*$")
52
+
53
+ NormalizedLicenseExpression = NewType("NormalizedLicenseExpression", str)
54
+ """
55
+ A :class:`typing.NewType` of :class:`str`, representing a normalized
56
+ License-Expression.
57
+ """
58
+
59
+
60
+ class InvalidLicenseExpression(ValueError):
61
+ """Raised when a license-expression string is invalid
62
+
63
+ >>> from packaging.licenses import canonicalize_license_expression
64
+ >>> canonicalize_license_expression("invalid")
65
+ Traceback (most recent call last):
66
+ ...
67
+ packaging.licenses.InvalidLicenseExpression: Invalid license expression: 'invalid'
68
+ """
69
+
70
+
71
+ def canonicalize_license_expression(
72
+ raw_license_expression: str,
73
+ ) -> NormalizedLicenseExpression:
74
+ """
75
+ This function takes a valid License-Expression, and returns the normalized
76
+ form of it.
77
+
78
+ The return type is typed as :class:`NormalizedLicenseExpression`. This
79
+ allows type checkers to help require that a string has passed through this
80
+ function before use.
81
+
82
+ :param str raw_license_expression: The License-Expression to canonicalize.
83
+ :raises InvalidLicenseExpression: If the License-Expression is invalid due to an
84
+ invalid/unknown license identifier or invalid syntax.
85
+
86
+ .. doctest::
87
+
88
+ >>> from packaging.licenses import canonicalize_license_expression
89
+ >>> canonicalize_license_expression("mit")
90
+ 'MIT'
91
+ >>> canonicalize_license_expression("mit and (apache-2.0 or bsd-2-clause)")
92
+ 'MIT AND (Apache-2.0 OR BSD-2-Clause)'
93
+ >>> canonicalize_license_expression("(mit")
94
+ Traceback (most recent call last):
95
+ ...
96
+ InvalidLicenseExpression: Invalid license expression: '(mit'
97
+ >>> canonicalize_license_expression("Use-it-after-midnight")
98
+ Traceback (most recent call last):
99
+ ...
100
+ InvalidLicenseExpression: Unknown license: 'Use-it-after-midnight'
101
+ """
102
+ if not raw_license_expression:
103
+ message = f"Invalid license expression: {raw_license_expression!r}"
104
+ raise InvalidLicenseExpression(message)
105
+
106
+ # Pad any parentheses so tokenization can be achieved by merely splitting on
107
+ # whitespace.
108
+ license_expression = raw_license_expression.replace("(", " ( ").replace(")", " ) ")
109
+ licenseref_prefix = "LicenseRef-"
110
+ license_refs = {
111
+ ref.lower(): "LicenseRef-" + ref[len(licenseref_prefix) :]
112
+ for ref in license_expression.split()
113
+ if ref.lower().startswith(licenseref_prefix.lower())
114
+ }
115
+
116
+ # Normalize to lower case so we can look up licenses/exceptions
117
+ # and so boolean operators are Python-compatible.
118
+ license_expression = license_expression.lower()
119
+
120
+ tokens = license_expression.split()
121
+
122
+ # Rather than implementing a parenthesis/boolean logic parser, create an
123
+ # expression that Python can parse. Everything that is not involved with the
124
+ # grammar itself is replaced with the placeholder `False` and the resultant
125
+ # expression should become a valid Python expression.
126
+ python_tokens = []
127
+ for token in tokens:
128
+ if token not in {"or", "and", "with", "(", ")"}:
129
+ python_tokens.append("False")
130
+ elif token == "with":
131
+ python_tokens.append("or")
132
+ elif (
133
+ token == "("
134
+ and python_tokens
135
+ and python_tokens[-1] not in {"or", "and", "("}
136
+ ) or (token == ")" and python_tokens and python_tokens[-1] == "("):
137
+ message = f"Invalid license expression: {raw_license_expression!r}"
138
+ raise InvalidLicenseExpression(message)
139
+ else:
140
+ python_tokens.append(token)
141
+
142
+ python_expression = " ".join(python_tokens)
143
+ try:
144
+ compile(python_expression, "", "eval")
145
+ except SyntaxError:
146
+ message = f"Invalid license expression: {raw_license_expression!r}"
147
+ raise InvalidLicenseExpression(message) from None
148
+
149
+ # Take a final pass to check for unknown licenses/exceptions.
150
+ normalized_tokens = []
151
+ for token in tokens:
152
+ if token in {"or", "and", "with", "(", ")"}:
153
+ normalized_tokens.append(token.upper())
154
+ continue
155
+
156
+ if normalized_tokens and normalized_tokens[-1] == "WITH":
157
+ if token not in EXCEPTIONS:
158
+ message = f"Unknown license exception: {token!r}"
159
+ raise InvalidLicenseExpression(message)
160
+
161
+ normalized_tokens.append(EXCEPTIONS[token]["id"])
162
+ else:
163
+ if token.endswith("+"):
164
+ final_token = token[:-1]
165
+ suffix = "+"
166
+ else:
167
+ final_token = token
168
+ suffix = ""
169
+
170
+ if final_token.startswith("licenseref-"):
171
+ if not license_ref_allowed.match(final_token):
172
+ message = f"Invalid licenseref: {final_token!r}"
173
+ raise InvalidLicenseExpression(message)
174
+ normalized_tokens.append(license_refs[final_token] + suffix)
175
+ else:
176
+ if final_token not in LICENSES:
177
+ message = f"Unknown license: {final_token!r}"
178
+ raise InvalidLicenseExpression(message)
179
+ normalized_tokens.append(LICENSES[final_token]["id"] + suffix)
180
+
181
+ normalized_expression = " ".join(normalized_tokens)
182
+
183
+ return cast(
184
+ "NormalizedLicenseExpression",
185
+ normalized_expression.replace("( ", "(").replace(" )", ")"),
186
+ )
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/licenses/_spdx.py ADDED
@@ -0,0 +1,799 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from __future__ import annotations
3
+
4
+ from typing import TypedDict
5
+
6
+ class SPDXLicense(TypedDict):
7
+ id: str
8
+ deprecated: bool
9
+
10
+ class SPDXException(TypedDict):
11
+ id: str
12
+ deprecated: bool
13
+
14
+
15
+ VERSION = '3.27.0'
16
+
17
+ LICENSES: dict[str, SPDXLicense] = {
18
+ '0bsd': {'id': '0BSD', 'deprecated': False},
19
+ '3d-slicer-1.0': {'id': '3D-Slicer-1.0', 'deprecated': False},
20
+ 'aal': {'id': 'AAL', 'deprecated': False},
21
+ 'abstyles': {'id': 'Abstyles', 'deprecated': False},
22
+ 'adacore-doc': {'id': 'AdaCore-doc', 'deprecated': False},
23
+ 'adobe-2006': {'id': 'Adobe-2006', 'deprecated': False},
24
+ 'adobe-display-postscript': {'id': 'Adobe-Display-PostScript', 'deprecated': False},
25
+ 'adobe-glyph': {'id': 'Adobe-Glyph', 'deprecated': False},
26
+ 'adobe-utopia': {'id': 'Adobe-Utopia', 'deprecated': False},
27
+ 'adsl': {'id': 'ADSL', 'deprecated': False},
28
+ 'afl-1.1': {'id': 'AFL-1.1', 'deprecated': False},
29
+ 'afl-1.2': {'id': 'AFL-1.2', 'deprecated': False},
30
+ 'afl-2.0': {'id': 'AFL-2.0', 'deprecated': False},
31
+ 'afl-2.1': {'id': 'AFL-2.1', 'deprecated': False},
32
+ 'afl-3.0': {'id': 'AFL-3.0', 'deprecated': False},
33
+ 'afmparse': {'id': 'Afmparse', 'deprecated': False},
34
+ 'agpl-1.0': {'id': 'AGPL-1.0', 'deprecated': True},
35
+ 'agpl-1.0-only': {'id': 'AGPL-1.0-only', 'deprecated': False},
36
+ 'agpl-1.0-or-later': {'id': 'AGPL-1.0-or-later', 'deprecated': False},
37
+ 'agpl-3.0': {'id': 'AGPL-3.0', 'deprecated': True},
38
+ 'agpl-3.0-only': {'id': 'AGPL-3.0-only', 'deprecated': False},
39
+ 'agpl-3.0-or-later': {'id': 'AGPL-3.0-or-later', 'deprecated': False},
40
+ 'aladdin': {'id': 'Aladdin', 'deprecated': False},
41
+ 'amd-newlib': {'id': 'AMD-newlib', 'deprecated': False},
42
+ 'amdplpa': {'id': 'AMDPLPA', 'deprecated': False},
43
+ 'aml': {'id': 'AML', 'deprecated': False},
44
+ 'aml-glslang': {'id': 'AML-glslang', 'deprecated': False},
45
+ 'ampas': {'id': 'AMPAS', 'deprecated': False},
46
+ 'antlr-pd': {'id': 'ANTLR-PD', 'deprecated': False},
47
+ 'antlr-pd-fallback': {'id': 'ANTLR-PD-fallback', 'deprecated': False},
48
+ 'any-osi': {'id': 'any-OSI', 'deprecated': False},
49
+ 'any-osi-perl-modules': {'id': 'any-OSI-perl-modules', 'deprecated': False},
50
+ 'apache-1.0': {'id': 'Apache-1.0', 'deprecated': False},
51
+ 'apache-1.1': {'id': 'Apache-1.1', 'deprecated': False},
52
+ 'apache-2.0': {'id': 'Apache-2.0', 'deprecated': False},
53
+ 'apafml': {'id': 'APAFML', 'deprecated': False},
54
+ 'apl-1.0': {'id': 'APL-1.0', 'deprecated': False},
55
+ 'app-s2p': {'id': 'App-s2p', 'deprecated': False},
56
+ 'apsl-1.0': {'id': 'APSL-1.0', 'deprecated': False},
57
+ 'apsl-1.1': {'id': 'APSL-1.1', 'deprecated': False},
58
+ 'apsl-1.2': {'id': 'APSL-1.2', 'deprecated': False},
59
+ 'apsl-2.0': {'id': 'APSL-2.0', 'deprecated': False},
60
+ 'arphic-1999': {'id': 'Arphic-1999', 'deprecated': False},
61
+ 'artistic-1.0': {'id': 'Artistic-1.0', 'deprecated': False},
62
+ 'artistic-1.0-cl8': {'id': 'Artistic-1.0-cl8', 'deprecated': False},
63
+ 'artistic-1.0-perl': {'id': 'Artistic-1.0-Perl', 'deprecated': False},
64
+ 'artistic-2.0': {'id': 'Artistic-2.0', 'deprecated': False},
65
+ 'artistic-dist': {'id': 'Artistic-dist', 'deprecated': False},
66
+ 'aspell-ru': {'id': 'Aspell-RU', 'deprecated': False},
67
+ 'aswf-digital-assets-1.0': {'id': 'ASWF-Digital-Assets-1.0', 'deprecated': False},
68
+ 'aswf-digital-assets-1.1': {'id': 'ASWF-Digital-Assets-1.1', 'deprecated': False},
69
+ 'baekmuk': {'id': 'Baekmuk', 'deprecated': False},
70
+ 'bahyph': {'id': 'Bahyph', 'deprecated': False},
71
+ 'barr': {'id': 'Barr', 'deprecated': False},
72
+ 'bcrypt-solar-designer': {'id': 'bcrypt-Solar-Designer', 'deprecated': False},
73
+ 'beerware': {'id': 'Beerware', 'deprecated': False},
74
+ 'bitstream-charter': {'id': 'Bitstream-Charter', 'deprecated': False},
75
+ 'bitstream-vera': {'id': 'Bitstream-Vera', 'deprecated': False},
76
+ 'bittorrent-1.0': {'id': 'BitTorrent-1.0', 'deprecated': False},
77
+ 'bittorrent-1.1': {'id': 'BitTorrent-1.1', 'deprecated': False},
78
+ 'blessing': {'id': 'blessing', 'deprecated': False},
79
+ 'blueoak-1.0.0': {'id': 'BlueOak-1.0.0', 'deprecated': False},
80
+ 'boehm-gc': {'id': 'Boehm-GC', 'deprecated': False},
81
+ 'boehm-gc-without-fee': {'id': 'Boehm-GC-without-fee', 'deprecated': False},
82
+ 'borceux': {'id': 'Borceux', 'deprecated': False},
83
+ 'brian-gladman-2-clause': {'id': 'Brian-Gladman-2-Clause', 'deprecated': False},
84
+ 'brian-gladman-3-clause': {'id': 'Brian-Gladman-3-Clause', 'deprecated': False},
85
+ 'bsd-1-clause': {'id': 'BSD-1-Clause', 'deprecated': False},
86
+ 'bsd-2-clause': {'id': 'BSD-2-Clause', 'deprecated': False},
87
+ 'bsd-2-clause-darwin': {'id': 'BSD-2-Clause-Darwin', 'deprecated': False},
88
+ 'bsd-2-clause-first-lines': {'id': 'BSD-2-Clause-first-lines', 'deprecated': False},
89
+ 'bsd-2-clause-freebsd': {'id': 'BSD-2-Clause-FreeBSD', 'deprecated': True},
90
+ 'bsd-2-clause-netbsd': {'id': 'BSD-2-Clause-NetBSD', 'deprecated': True},
91
+ 'bsd-2-clause-patent': {'id': 'BSD-2-Clause-Patent', 'deprecated': False},
92
+ 'bsd-2-clause-pkgconf-disclaimer': {'id': 'BSD-2-Clause-pkgconf-disclaimer', 'deprecated': False},
93
+ 'bsd-2-clause-views': {'id': 'BSD-2-Clause-Views', 'deprecated': False},
94
+ 'bsd-3-clause': {'id': 'BSD-3-Clause', 'deprecated': False},
95
+ 'bsd-3-clause-acpica': {'id': 'BSD-3-Clause-acpica', 'deprecated': False},
96
+ 'bsd-3-clause-attribution': {'id': 'BSD-3-Clause-Attribution', 'deprecated': False},
97
+ 'bsd-3-clause-clear': {'id': 'BSD-3-Clause-Clear', 'deprecated': False},
98
+ 'bsd-3-clause-flex': {'id': 'BSD-3-Clause-flex', 'deprecated': False},
99
+ 'bsd-3-clause-hp': {'id': 'BSD-3-Clause-HP', 'deprecated': False},
100
+ 'bsd-3-clause-lbnl': {'id': 'BSD-3-Clause-LBNL', 'deprecated': False},
101
+ 'bsd-3-clause-modification': {'id': 'BSD-3-Clause-Modification', 'deprecated': False},
102
+ 'bsd-3-clause-no-military-license': {'id': 'BSD-3-Clause-No-Military-License', 'deprecated': False},
103
+ 'bsd-3-clause-no-nuclear-license': {'id': 'BSD-3-Clause-No-Nuclear-License', 'deprecated': False},
104
+ 'bsd-3-clause-no-nuclear-license-2014': {'id': 'BSD-3-Clause-No-Nuclear-License-2014', 'deprecated': False},
105
+ 'bsd-3-clause-no-nuclear-warranty': {'id': 'BSD-3-Clause-No-Nuclear-Warranty', 'deprecated': False},
106
+ 'bsd-3-clause-open-mpi': {'id': 'BSD-3-Clause-Open-MPI', 'deprecated': False},
107
+ 'bsd-3-clause-sun': {'id': 'BSD-3-Clause-Sun', 'deprecated': False},
108
+ 'bsd-4-clause': {'id': 'BSD-4-Clause', 'deprecated': False},
109
+ 'bsd-4-clause-shortened': {'id': 'BSD-4-Clause-Shortened', 'deprecated': False},
110
+ 'bsd-4-clause-uc': {'id': 'BSD-4-Clause-UC', 'deprecated': False},
111
+ 'bsd-4.3reno': {'id': 'BSD-4.3RENO', 'deprecated': False},
112
+ 'bsd-4.3tahoe': {'id': 'BSD-4.3TAHOE', 'deprecated': False},
113
+ 'bsd-advertising-acknowledgement': {'id': 'BSD-Advertising-Acknowledgement', 'deprecated': False},
114
+ 'bsd-attribution-hpnd-disclaimer': {'id': 'BSD-Attribution-HPND-disclaimer', 'deprecated': False},
115
+ 'bsd-inferno-nettverk': {'id': 'BSD-Inferno-Nettverk', 'deprecated': False},
116
+ 'bsd-protection': {'id': 'BSD-Protection', 'deprecated': False},
117
+ 'bsd-source-beginning-file': {'id': 'BSD-Source-beginning-file', 'deprecated': False},
118
+ 'bsd-source-code': {'id': 'BSD-Source-Code', 'deprecated': False},
119
+ 'bsd-systemics': {'id': 'BSD-Systemics', 'deprecated': False},
120
+ 'bsd-systemics-w3works': {'id': 'BSD-Systemics-W3Works', 'deprecated': False},
121
+ 'bsl-1.0': {'id': 'BSL-1.0', 'deprecated': False},
122
+ 'busl-1.1': {'id': 'BUSL-1.1', 'deprecated': False},
123
+ 'bzip2-1.0.5': {'id': 'bzip2-1.0.5', 'deprecated': True},
124
+ 'bzip2-1.0.6': {'id': 'bzip2-1.0.6', 'deprecated': False},
125
+ 'c-uda-1.0': {'id': 'C-UDA-1.0', 'deprecated': False},
126
+ 'cal-1.0': {'id': 'CAL-1.0', 'deprecated': False},
127
+ 'cal-1.0-combined-work-exception': {'id': 'CAL-1.0-Combined-Work-Exception', 'deprecated': False},
128
+ 'caldera': {'id': 'Caldera', 'deprecated': False},
129
+ 'caldera-no-preamble': {'id': 'Caldera-no-preamble', 'deprecated': False},
130
+ 'catharon': {'id': 'Catharon', 'deprecated': False},
131
+ 'catosl-1.1': {'id': 'CATOSL-1.1', 'deprecated': False},
132
+ 'cc-by-1.0': {'id': 'CC-BY-1.0', 'deprecated': False},
133
+ 'cc-by-2.0': {'id': 'CC-BY-2.0', 'deprecated': False},
134
+ 'cc-by-2.5': {'id': 'CC-BY-2.5', 'deprecated': False},
135
+ 'cc-by-2.5-au': {'id': 'CC-BY-2.5-AU', 'deprecated': False},
136
+ 'cc-by-3.0': {'id': 'CC-BY-3.0', 'deprecated': False},
137
+ 'cc-by-3.0-at': {'id': 'CC-BY-3.0-AT', 'deprecated': False},
138
+ 'cc-by-3.0-au': {'id': 'CC-BY-3.0-AU', 'deprecated': False},
139
+ 'cc-by-3.0-de': {'id': 'CC-BY-3.0-DE', 'deprecated': False},
140
+ 'cc-by-3.0-igo': {'id': 'CC-BY-3.0-IGO', 'deprecated': False},
141
+ 'cc-by-3.0-nl': {'id': 'CC-BY-3.0-NL', 'deprecated': False},
142
+ 'cc-by-3.0-us': {'id': 'CC-BY-3.0-US', 'deprecated': False},
143
+ 'cc-by-4.0': {'id': 'CC-BY-4.0', 'deprecated': False},
144
+ 'cc-by-nc-1.0': {'id': 'CC-BY-NC-1.0', 'deprecated': False},
145
+ 'cc-by-nc-2.0': {'id': 'CC-BY-NC-2.0', 'deprecated': False},
146
+ 'cc-by-nc-2.5': {'id': 'CC-BY-NC-2.5', 'deprecated': False},
147
+ 'cc-by-nc-3.0': {'id': 'CC-BY-NC-3.0', 'deprecated': False},
148
+ 'cc-by-nc-3.0-de': {'id': 'CC-BY-NC-3.0-DE', 'deprecated': False},
149
+ 'cc-by-nc-4.0': {'id': 'CC-BY-NC-4.0', 'deprecated': False},
150
+ 'cc-by-nc-nd-1.0': {'id': 'CC-BY-NC-ND-1.0', 'deprecated': False},
151
+ 'cc-by-nc-nd-2.0': {'id': 'CC-BY-NC-ND-2.0', 'deprecated': False},
152
+ 'cc-by-nc-nd-2.5': {'id': 'CC-BY-NC-ND-2.5', 'deprecated': False},
153
+ 'cc-by-nc-nd-3.0': {'id': 'CC-BY-NC-ND-3.0', 'deprecated': False},
154
+ 'cc-by-nc-nd-3.0-de': {'id': 'CC-BY-NC-ND-3.0-DE', 'deprecated': False},
155
+ 'cc-by-nc-nd-3.0-igo': {'id': 'CC-BY-NC-ND-3.0-IGO', 'deprecated': False},
156
+ 'cc-by-nc-nd-4.0': {'id': 'CC-BY-NC-ND-4.0', 'deprecated': False},
157
+ 'cc-by-nc-sa-1.0': {'id': 'CC-BY-NC-SA-1.0', 'deprecated': False},
158
+ 'cc-by-nc-sa-2.0': {'id': 'CC-BY-NC-SA-2.0', 'deprecated': False},
159
+ 'cc-by-nc-sa-2.0-de': {'id': 'CC-BY-NC-SA-2.0-DE', 'deprecated': False},
160
+ 'cc-by-nc-sa-2.0-fr': {'id': 'CC-BY-NC-SA-2.0-FR', 'deprecated': False},
161
+ 'cc-by-nc-sa-2.0-uk': {'id': 'CC-BY-NC-SA-2.0-UK', 'deprecated': False},
162
+ 'cc-by-nc-sa-2.5': {'id': 'CC-BY-NC-SA-2.5', 'deprecated': False},
163
+ 'cc-by-nc-sa-3.0': {'id': 'CC-BY-NC-SA-3.0', 'deprecated': False},
164
+ 'cc-by-nc-sa-3.0-de': {'id': 'CC-BY-NC-SA-3.0-DE', 'deprecated': False},
165
+ 'cc-by-nc-sa-3.0-igo': {'id': 'CC-BY-NC-SA-3.0-IGO', 'deprecated': False},
166
+ 'cc-by-nc-sa-4.0': {'id': 'CC-BY-NC-SA-4.0', 'deprecated': False},
167
+ 'cc-by-nd-1.0': {'id': 'CC-BY-ND-1.0', 'deprecated': False},
168
+ 'cc-by-nd-2.0': {'id': 'CC-BY-ND-2.0', 'deprecated': False},
169
+ 'cc-by-nd-2.5': {'id': 'CC-BY-ND-2.5', 'deprecated': False},
170
+ 'cc-by-nd-3.0': {'id': 'CC-BY-ND-3.0', 'deprecated': False},
171
+ 'cc-by-nd-3.0-de': {'id': 'CC-BY-ND-3.0-DE', 'deprecated': False},
172
+ 'cc-by-nd-4.0': {'id': 'CC-BY-ND-4.0', 'deprecated': False},
173
+ 'cc-by-sa-1.0': {'id': 'CC-BY-SA-1.0', 'deprecated': False},
174
+ 'cc-by-sa-2.0': {'id': 'CC-BY-SA-2.0', 'deprecated': False},
175
+ 'cc-by-sa-2.0-uk': {'id': 'CC-BY-SA-2.0-UK', 'deprecated': False},
176
+ 'cc-by-sa-2.1-jp': {'id': 'CC-BY-SA-2.1-JP', 'deprecated': False},
177
+ 'cc-by-sa-2.5': {'id': 'CC-BY-SA-2.5', 'deprecated': False},
178
+ 'cc-by-sa-3.0': {'id': 'CC-BY-SA-3.0', 'deprecated': False},
179
+ 'cc-by-sa-3.0-at': {'id': 'CC-BY-SA-3.0-AT', 'deprecated': False},
180
+ 'cc-by-sa-3.0-de': {'id': 'CC-BY-SA-3.0-DE', 'deprecated': False},
181
+ 'cc-by-sa-3.0-igo': {'id': 'CC-BY-SA-3.0-IGO', 'deprecated': False},
182
+ 'cc-by-sa-4.0': {'id': 'CC-BY-SA-4.0', 'deprecated': False},
183
+ 'cc-pddc': {'id': 'CC-PDDC', 'deprecated': False},
184
+ 'cc-pdm-1.0': {'id': 'CC-PDM-1.0', 'deprecated': False},
185
+ 'cc-sa-1.0': {'id': 'CC-SA-1.0', 'deprecated': False},
186
+ 'cc0-1.0': {'id': 'CC0-1.0', 'deprecated': False},
187
+ 'cddl-1.0': {'id': 'CDDL-1.0', 'deprecated': False},
188
+ 'cddl-1.1': {'id': 'CDDL-1.1', 'deprecated': False},
189
+ 'cdl-1.0': {'id': 'CDL-1.0', 'deprecated': False},
190
+ 'cdla-permissive-1.0': {'id': 'CDLA-Permissive-1.0', 'deprecated': False},
191
+ 'cdla-permissive-2.0': {'id': 'CDLA-Permissive-2.0', 'deprecated': False},
192
+ 'cdla-sharing-1.0': {'id': 'CDLA-Sharing-1.0', 'deprecated': False},
193
+ 'cecill-1.0': {'id': 'CECILL-1.0', 'deprecated': False},
194
+ 'cecill-1.1': {'id': 'CECILL-1.1', 'deprecated': False},
195
+ 'cecill-2.0': {'id': 'CECILL-2.0', 'deprecated': False},
196
+ 'cecill-2.1': {'id': 'CECILL-2.1', 'deprecated': False},
197
+ 'cecill-b': {'id': 'CECILL-B', 'deprecated': False},
198
+ 'cecill-c': {'id': 'CECILL-C', 'deprecated': False},
199
+ 'cern-ohl-1.1': {'id': 'CERN-OHL-1.1', 'deprecated': False},
200
+ 'cern-ohl-1.2': {'id': 'CERN-OHL-1.2', 'deprecated': False},
201
+ 'cern-ohl-p-2.0': {'id': 'CERN-OHL-P-2.0', 'deprecated': False},
202
+ 'cern-ohl-s-2.0': {'id': 'CERN-OHL-S-2.0', 'deprecated': False},
203
+ 'cern-ohl-w-2.0': {'id': 'CERN-OHL-W-2.0', 'deprecated': False},
204
+ 'cfitsio': {'id': 'CFITSIO', 'deprecated': False},
205
+ 'check-cvs': {'id': 'check-cvs', 'deprecated': False},
206
+ 'checkmk': {'id': 'checkmk', 'deprecated': False},
207
+ 'clartistic': {'id': 'ClArtistic', 'deprecated': False},
208
+ 'clips': {'id': 'Clips', 'deprecated': False},
209
+ 'cmu-mach': {'id': 'CMU-Mach', 'deprecated': False},
210
+ 'cmu-mach-nodoc': {'id': 'CMU-Mach-nodoc', 'deprecated': False},
211
+ 'cnri-jython': {'id': 'CNRI-Jython', 'deprecated': False},
212
+ 'cnri-python': {'id': 'CNRI-Python', 'deprecated': False},
213
+ 'cnri-python-gpl-compatible': {'id': 'CNRI-Python-GPL-Compatible', 'deprecated': False},
214
+ 'coil-1.0': {'id': 'COIL-1.0', 'deprecated': False},
215
+ 'community-spec-1.0': {'id': 'Community-Spec-1.0', 'deprecated': False},
216
+ 'condor-1.1': {'id': 'Condor-1.1', 'deprecated': False},
217
+ 'copyleft-next-0.3.0': {'id': 'copyleft-next-0.3.0', 'deprecated': False},
218
+ 'copyleft-next-0.3.1': {'id': 'copyleft-next-0.3.1', 'deprecated': False},
219
+ 'cornell-lossless-jpeg': {'id': 'Cornell-Lossless-JPEG', 'deprecated': False},
220
+ 'cpal-1.0': {'id': 'CPAL-1.0', 'deprecated': False},
221
+ 'cpl-1.0': {'id': 'CPL-1.0', 'deprecated': False},
222
+ 'cpol-1.02': {'id': 'CPOL-1.02', 'deprecated': False},
223
+ 'cronyx': {'id': 'Cronyx', 'deprecated': False},
224
+ 'crossword': {'id': 'Crossword', 'deprecated': False},
225
+ 'cryptoswift': {'id': 'CryptoSwift', 'deprecated': False},
226
+ 'crystalstacker': {'id': 'CrystalStacker', 'deprecated': False},
227
+ 'cua-opl-1.0': {'id': 'CUA-OPL-1.0', 'deprecated': False},
228
+ 'cube': {'id': 'Cube', 'deprecated': False},
229
+ 'curl': {'id': 'curl', 'deprecated': False},
230
+ 'cve-tou': {'id': 'cve-tou', 'deprecated': False},
231
+ 'd-fsl-1.0': {'id': 'D-FSL-1.0', 'deprecated': False},
232
+ 'dec-3-clause': {'id': 'DEC-3-Clause', 'deprecated': False},
233
+ 'diffmark': {'id': 'diffmark', 'deprecated': False},
234
+ 'dl-de-by-2.0': {'id': 'DL-DE-BY-2.0', 'deprecated': False},
235
+ 'dl-de-zero-2.0': {'id': 'DL-DE-ZERO-2.0', 'deprecated': False},
236
+ 'doc': {'id': 'DOC', 'deprecated': False},
237
+ 'docbook-dtd': {'id': 'DocBook-DTD', 'deprecated': False},
238
+ 'docbook-schema': {'id': 'DocBook-Schema', 'deprecated': False},
239
+ 'docbook-stylesheet': {'id': 'DocBook-Stylesheet', 'deprecated': False},
240
+ 'docbook-xml': {'id': 'DocBook-XML', 'deprecated': False},
241
+ 'dotseqn': {'id': 'Dotseqn', 'deprecated': False},
242
+ 'drl-1.0': {'id': 'DRL-1.0', 'deprecated': False},
243
+ 'drl-1.1': {'id': 'DRL-1.1', 'deprecated': False},
244
+ 'dsdp': {'id': 'DSDP', 'deprecated': False},
245
+ 'dtoa': {'id': 'dtoa', 'deprecated': False},
246
+ 'dvipdfm': {'id': 'dvipdfm', 'deprecated': False},
247
+ 'ecl-1.0': {'id': 'ECL-1.0', 'deprecated': False},
248
+ 'ecl-2.0': {'id': 'ECL-2.0', 'deprecated': False},
249
+ 'ecos-2.0': {'id': 'eCos-2.0', 'deprecated': True},
250
+ 'efl-1.0': {'id': 'EFL-1.0', 'deprecated': False},
251
+ 'efl-2.0': {'id': 'EFL-2.0', 'deprecated': False},
252
+ 'egenix': {'id': 'eGenix', 'deprecated': False},
253
+ 'elastic-2.0': {'id': 'Elastic-2.0', 'deprecated': False},
254
+ 'entessa': {'id': 'Entessa', 'deprecated': False},
255
+ 'epics': {'id': 'EPICS', 'deprecated': False},
256
+ 'epl-1.0': {'id': 'EPL-1.0', 'deprecated': False},
257
+ 'epl-2.0': {'id': 'EPL-2.0', 'deprecated': False},
258
+ 'erlpl-1.1': {'id': 'ErlPL-1.1', 'deprecated': False},
259
+ 'etalab-2.0': {'id': 'etalab-2.0', 'deprecated': False},
260
+ 'eudatagrid': {'id': 'EUDatagrid', 'deprecated': False},
261
+ 'eupl-1.0': {'id': 'EUPL-1.0', 'deprecated': False},
262
+ 'eupl-1.1': {'id': 'EUPL-1.1', 'deprecated': False},
263
+ 'eupl-1.2': {'id': 'EUPL-1.2', 'deprecated': False},
264
+ 'eurosym': {'id': 'Eurosym', 'deprecated': False},
265
+ 'fair': {'id': 'Fair', 'deprecated': False},
266
+ 'fbm': {'id': 'FBM', 'deprecated': False},
267
+ 'fdk-aac': {'id': 'FDK-AAC', 'deprecated': False},
268
+ 'ferguson-twofish': {'id': 'Ferguson-Twofish', 'deprecated': False},
269
+ 'frameworx-1.0': {'id': 'Frameworx-1.0', 'deprecated': False},
270
+ 'freebsd-doc': {'id': 'FreeBSD-DOC', 'deprecated': False},
271
+ 'freeimage': {'id': 'FreeImage', 'deprecated': False},
272
+ 'fsfap': {'id': 'FSFAP', 'deprecated': False},
273
+ 'fsfap-no-warranty-disclaimer': {'id': 'FSFAP-no-warranty-disclaimer', 'deprecated': False},
274
+ 'fsful': {'id': 'FSFUL', 'deprecated': False},
275
+ 'fsfullr': {'id': 'FSFULLR', 'deprecated': False},
276
+ 'fsfullrsd': {'id': 'FSFULLRSD', 'deprecated': False},
277
+ 'fsfullrwd': {'id': 'FSFULLRWD', 'deprecated': False},
278
+ 'fsl-1.1-alv2': {'id': 'FSL-1.1-ALv2', 'deprecated': False},
279
+ 'fsl-1.1-mit': {'id': 'FSL-1.1-MIT', 'deprecated': False},
280
+ 'ftl': {'id': 'FTL', 'deprecated': False},
281
+ 'furuseth': {'id': 'Furuseth', 'deprecated': False},
282
+ 'fwlw': {'id': 'fwlw', 'deprecated': False},
283
+ 'game-programming-gems': {'id': 'Game-Programming-Gems', 'deprecated': False},
284
+ 'gcr-docs': {'id': 'GCR-docs', 'deprecated': False},
285
+ 'gd': {'id': 'GD', 'deprecated': False},
286
+ 'generic-xts': {'id': 'generic-xts', 'deprecated': False},
287
+ 'gfdl-1.1': {'id': 'GFDL-1.1', 'deprecated': True},
288
+ 'gfdl-1.1-invariants-only': {'id': 'GFDL-1.1-invariants-only', 'deprecated': False},
289
+ 'gfdl-1.1-invariants-or-later': {'id': 'GFDL-1.1-invariants-or-later', 'deprecated': False},
290
+ 'gfdl-1.1-no-invariants-only': {'id': 'GFDL-1.1-no-invariants-only', 'deprecated': False},
291
+ 'gfdl-1.1-no-invariants-or-later': {'id': 'GFDL-1.1-no-invariants-or-later', 'deprecated': False},
292
+ 'gfdl-1.1-only': {'id': 'GFDL-1.1-only', 'deprecated': False},
293
+ 'gfdl-1.1-or-later': {'id': 'GFDL-1.1-or-later', 'deprecated': False},
294
+ 'gfdl-1.2': {'id': 'GFDL-1.2', 'deprecated': True},
295
+ 'gfdl-1.2-invariants-only': {'id': 'GFDL-1.2-invariants-only', 'deprecated': False},
296
+ 'gfdl-1.2-invariants-or-later': {'id': 'GFDL-1.2-invariants-or-later', 'deprecated': False},
297
+ 'gfdl-1.2-no-invariants-only': {'id': 'GFDL-1.2-no-invariants-only', 'deprecated': False},
298
+ 'gfdl-1.2-no-invariants-or-later': {'id': 'GFDL-1.2-no-invariants-or-later', 'deprecated': False},
299
+ 'gfdl-1.2-only': {'id': 'GFDL-1.2-only', 'deprecated': False},
300
+ 'gfdl-1.2-or-later': {'id': 'GFDL-1.2-or-later', 'deprecated': False},
301
+ 'gfdl-1.3': {'id': 'GFDL-1.3', 'deprecated': True},
302
+ 'gfdl-1.3-invariants-only': {'id': 'GFDL-1.3-invariants-only', 'deprecated': False},
303
+ 'gfdl-1.3-invariants-or-later': {'id': 'GFDL-1.3-invariants-or-later', 'deprecated': False},
304
+ 'gfdl-1.3-no-invariants-only': {'id': 'GFDL-1.3-no-invariants-only', 'deprecated': False},
305
+ 'gfdl-1.3-no-invariants-or-later': {'id': 'GFDL-1.3-no-invariants-or-later', 'deprecated': False},
306
+ 'gfdl-1.3-only': {'id': 'GFDL-1.3-only', 'deprecated': False},
307
+ 'gfdl-1.3-or-later': {'id': 'GFDL-1.3-or-later', 'deprecated': False},
308
+ 'giftware': {'id': 'Giftware', 'deprecated': False},
309
+ 'gl2ps': {'id': 'GL2PS', 'deprecated': False},
310
+ 'glide': {'id': 'Glide', 'deprecated': False},
311
+ 'glulxe': {'id': 'Glulxe', 'deprecated': False},
312
+ 'glwtpl': {'id': 'GLWTPL', 'deprecated': False},
313
+ 'gnuplot': {'id': 'gnuplot', 'deprecated': False},
314
+ 'gpl-1.0': {'id': 'GPL-1.0', 'deprecated': True},
315
+ 'gpl-1.0+': {'id': 'GPL-1.0+', 'deprecated': True},
316
+ 'gpl-1.0-only': {'id': 'GPL-1.0-only', 'deprecated': False},
317
+ 'gpl-1.0-or-later': {'id': 'GPL-1.0-or-later', 'deprecated': False},
318
+ 'gpl-2.0': {'id': 'GPL-2.0', 'deprecated': True},
319
+ 'gpl-2.0+': {'id': 'GPL-2.0+', 'deprecated': True},
320
+ 'gpl-2.0-only': {'id': 'GPL-2.0-only', 'deprecated': False},
321
+ 'gpl-2.0-or-later': {'id': 'GPL-2.0-or-later', 'deprecated': False},
322
+ 'gpl-2.0-with-autoconf-exception': {'id': 'GPL-2.0-with-autoconf-exception', 'deprecated': True},
323
+ 'gpl-2.0-with-bison-exception': {'id': 'GPL-2.0-with-bison-exception', 'deprecated': True},
324
+ 'gpl-2.0-with-classpath-exception': {'id': 'GPL-2.0-with-classpath-exception', 'deprecated': True},
325
+ 'gpl-2.0-with-font-exception': {'id': 'GPL-2.0-with-font-exception', 'deprecated': True},
326
+ 'gpl-2.0-with-gcc-exception': {'id': 'GPL-2.0-with-GCC-exception', 'deprecated': True},
327
+ 'gpl-3.0': {'id': 'GPL-3.0', 'deprecated': True},
328
+ 'gpl-3.0+': {'id': 'GPL-3.0+', 'deprecated': True},
329
+ 'gpl-3.0-only': {'id': 'GPL-3.0-only', 'deprecated': False},
330
+ 'gpl-3.0-or-later': {'id': 'GPL-3.0-or-later', 'deprecated': False},
331
+ 'gpl-3.0-with-autoconf-exception': {'id': 'GPL-3.0-with-autoconf-exception', 'deprecated': True},
332
+ 'gpl-3.0-with-gcc-exception': {'id': 'GPL-3.0-with-GCC-exception', 'deprecated': True},
333
+ 'graphics-gems': {'id': 'Graphics-Gems', 'deprecated': False},
334
+ 'gsoap-1.3b': {'id': 'gSOAP-1.3b', 'deprecated': False},
335
+ 'gtkbook': {'id': 'gtkbook', 'deprecated': False},
336
+ 'gutmann': {'id': 'Gutmann', 'deprecated': False},
337
+ 'haskellreport': {'id': 'HaskellReport', 'deprecated': False},
338
+ 'hdf5': {'id': 'HDF5', 'deprecated': False},
339
+ 'hdparm': {'id': 'hdparm', 'deprecated': False},
340
+ 'hidapi': {'id': 'HIDAPI', 'deprecated': False},
341
+ 'hippocratic-2.1': {'id': 'Hippocratic-2.1', 'deprecated': False},
342
+ 'hp-1986': {'id': 'HP-1986', 'deprecated': False},
343
+ 'hp-1989': {'id': 'HP-1989', 'deprecated': False},
344
+ 'hpnd': {'id': 'HPND', 'deprecated': False},
345
+ 'hpnd-dec': {'id': 'HPND-DEC', 'deprecated': False},
346
+ 'hpnd-doc': {'id': 'HPND-doc', 'deprecated': False},
347
+ 'hpnd-doc-sell': {'id': 'HPND-doc-sell', 'deprecated': False},
348
+ 'hpnd-export-us': {'id': 'HPND-export-US', 'deprecated': False},
349
+ 'hpnd-export-us-acknowledgement': {'id': 'HPND-export-US-acknowledgement', 'deprecated': False},
350
+ 'hpnd-export-us-modify': {'id': 'HPND-export-US-modify', 'deprecated': False},
351
+ 'hpnd-export2-us': {'id': 'HPND-export2-US', 'deprecated': False},
352
+ 'hpnd-fenneberg-livingston': {'id': 'HPND-Fenneberg-Livingston', 'deprecated': False},
353
+ 'hpnd-inria-imag': {'id': 'HPND-INRIA-IMAG', 'deprecated': False},
354
+ 'hpnd-intel': {'id': 'HPND-Intel', 'deprecated': False},
355
+ 'hpnd-kevlin-henney': {'id': 'HPND-Kevlin-Henney', 'deprecated': False},
356
+ 'hpnd-markus-kuhn': {'id': 'HPND-Markus-Kuhn', 'deprecated': False},
357
+ 'hpnd-merchantability-variant': {'id': 'HPND-merchantability-variant', 'deprecated': False},
358
+ 'hpnd-mit-disclaimer': {'id': 'HPND-MIT-disclaimer', 'deprecated': False},
359
+ 'hpnd-netrek': {'id': 'HPND-Netrek', 'deprecated': False},
360
+ 'hpnd-pbmplus': {'id': 'HPND-Pbmplus', 'deprecated': False},
361
+ 'hpnd-sell-mit-disclaimer-xserver': {'id': 'HPND-sell-MIT-disclaimer-xserver', 'deprecated': False},
362
+ 'hpnd-sell-regexpr': {'id': 'HPND-sell-regexpr', 'deprecated': False},
363
+ 'hpnd-sell-variant': {'id': 'HPND-sell-variant', 'deprecated': False},
364
+ 'hpnd-sell-variant-mit-disclaimer': {'id': 'HPND-sell-variant-MIT-disclaimer', 'deprecated': False},
365
+ 'hpnd-sell-variant-mit-disclaimer-rev': {'id': 'HPND-sell-variant-MIT-disclaimer-rev', 'deprecated': False},
366
+ 'hpnd-uc': {'id': 'HPND-UC', 'deprecated': False},
367
+ 'hpnd-uc-export-us': {'id': 'HPND-UC-export-US', 'deprecated': False},
368
+ 'htmltidy': {'id': 'HTMLTIDY', 'deprecated': False},
369
+ 'ibm-pibs': {'id': 'IBM-pibs', 'deprecated': False},
370
+ 'icu': {'id': 'ICU', 'deprecated': False},
371
+ 'iec-code-components-eula': {'id': 'IEC-Code-Components-EULA', 'deprecated': False},
372
+ 'ijg': {'id': 'IJG', 'deprecated': False},
373
+ 'ijg-short': {'id': 'IJG-short', 'deprecated': False},
374
+ 'imagemagick': {'id': 'ImageMagick', 'deprecated': False},
375
+ 'imatix': {'id': 'iMatix', 'deprecated': False},
376
+ 'imlib2': {'id': 'Imlib2', 'deprecated': False},
377
+ 'info-zip': {'id': 'Info-ZIP', 'deprecated': False},
378
+ 'inner-net-2.0': {'id': 'Inner-Net-2.0', 'deprecated': False},
379
+ 'innosetup': {'id': 'InnoSetup', 'deprecated': False},
380
+ 'intel': {'id': 'Intel', 'deprecated': False},
381
+ 'intel-acpi': {'id': 'Intel-ACPI', 'deprecated': False},
382
+ 'interbase-1.0': {'id': 'Interbase-1.0', 'deprecated': False},
383
+ 'ipa': {'id': 'IPA', 'deprecated': False},
384
+ 'ipl-1.0': {'id': 'IPL-1.0', 'deprecated': False},
385
+ 'isc': {'id': 'ISC', 'deprecated': False},
386
+ 'isc-veillard': {'id': 'ISC-Veillard', 'deprecated': False},
387
+ 'jam': {'id': 'Jam', 'deprecated': False},
388
+ 'jasper-2.0': {'id': 'JasPer-2.0', 'deprecated': False},
389
+ 'jove': {'id': 'jove', 'deprecated': False},
390
+ 'jpl-image': {'id': 'JPL-image', 'deprecated': False},
391
+ 'jpnic': {'id': 'JPNIC', 'deprecated': False},
392
+ 'json': {'id': 'JSON', 'deprecated': False},
393
+ 'kastrup': {'id': 'Kastrup', 'deprecated': False},
394
+ 'kazlib': {'id': 'Kazlib', 'deprecated': False},
395
+ 'knuth-ctan': {'id': 'Knuth-CTAN', 'deprecated': False},
396
+ 'lal-1.2': {'id': 'LAL-1.2', 'deprecated': False},
397
+ 'lal-1.3': {'id': 'LAL-1.3', 'deprecated': False},
398
+ 'latex2e': {'id': 'Latex2e', 'deprecated': False},
399
+ 'latex2e-translated-notice': {'id': 'Latex2e-translated-notice', 'deprecated': False},
400
+ 'leptonica': {'id': 'Leptonica', 'deprecated': False},
401
+ 'lgpl-2.0': {'id': 'LGPL-2.0', 'deprecated': True},
402
+ 'lgpl-2.0+': {'id': 'LGPL-2.0+', 'deprecated': True},
403
+ 'lgpl-2.0-only': {'id': 'LGPL-2.0-only', 'deprecated': False},
404
+ 'lgpl-2.0-or-later': {'id': 'LGPL-2.0-or-later', 'deprecated': False},
405
+ 'lgpl-2.1': {'id': 'LGPL-2.1', 'deprecated': True},
406
+ 'lgpl-2.1+': {'id': 'LGPL-2.1+', 'deprecated': True},
407
+ 'lgpl-2.1-only': {'id': 'LGPL-2.1-only', 'deprecated': False},
408
+ 'lgpl-2.1-or-later': {'id': 'LGPL-2.1-or-later', 'deprecated': False},
409
+ 'lgpl-3.0': {'id': 'LGPL-3.0', 'deprecated': True},
410
+ 'lgpl-3.0+': {'id': 'LGPL-3.0+', 'deprecated': True},
411
+ 'lgpl-3.0-only': {'id': 'LGPL-3.0-only', 'deprecated': False},
412
+ 'lgpl-3.0-or-later': {'id': 'LGPL-3.0-or-later', 'deprecated': False},
413
+ 'lgpllr': {'id': 'LGPLLR', 'deprecated': False},
414
+ 'libpng': {'id': 'Libpng', 'deprecated': False},
415
+ 'libpng-1.6.35': {'id': 'libpng-1.6.35', 'deprecated': False},
416
+ 'libpng-2.0': {'id': 'libpng-2.0', 'deprecated': False},
417
+ 'libselinux-1.0': {'id': 'libselinux-1.0', 'deprecated': False},
418
+ 'libtiff': {'id': 'libtiff', 'deprecated': False},
419
+ 'libutil-david-nugent': {'id': 'libutil-David-Nugent', 'deprecated': False},
420
+ 'liliq-p-1.1': {'id': 'LiLiQ-P-1.1', 'deprecated': False},
421
+ 'liliq-r-1.1': {'id': 'LiLiQ-R-1.1', 'deprecated': False},
422
+ 'liliq-rplus-1.1': {'id': 'LiLiQ-Rplus-1.1', 'deprecated': False},
423
+ 'linux-man-pages-1-para': {'id': 'Linux-man-pages-1-para', 'deprecated': False},
424
+ 'linux-man-pages-copyleft': {'id': 'Linux-man-pages-copyleft', 'deprecated': False},
425
+ 'linux-man-pages-copyleft-2-para': {'id': 'Linux-man-pages-copyleft-2-para', 'deprecated': False},
426
+ 'linux-man-pages-copyleft-var': {'id': 'Linux-man-pages-copyleft-var', 'deprecated': False},
427
+ 'linux-openib': {'id': 'Linux-OpenIB', 'deprecated': False},
428
+ 'loop': {'id': 'LOOP', 'deprecated': False},
429
+ 'lpd-document': {'id': 'LPD-document', 'deprecated': False},
430
+ 'lpl-1.0': {'id': 'LPL-1.0', 'deprecated': False},
431
+ 'lpl-1.02': {'id': 'LPL-1.02', 'deprecated': False},
432
+ 'lppl-1.0': {'id': 'LPPL-1.0', 'deprecated': False},
433
+ 'lppl-1.1': {'id': 'LPPL-1.1', 'deprecated': False},
434
+ 'lppl-1.2': {'id': 'LPPL-1.2', 'deprecated': False},
435
+ 'lppl-1.3a': {'id': 'LPPL-1.3a', 'deprecated': False},
436
+ 'lppl-1.3c': {'id': 'LPPL-1.3c', 'deprecated': False},
437
+ 'lsof': {'id': 'lsof', 'deprecated': False},
438
+ 'lucida-bitmap-fonts': {'id': 'Lucida-Bitmap-Fonts', 'deprecated': False},
439
+ 'lzma-sdk-9.11-to-9.20': {'id': 'LZMA-SDK-9.11-to-9.20', 'deprecated': False},
440
+ 'lzma-sdk-9.22': {'id': 'LZMA-SDK-9.22', 'deprecated': False},
441
+ 'mackerras-3-clause': {'id': 'Mackerras-3-Clause', 'deprecated': False},
442
+ 'mackerras-3-clause-acknowledgment': {'id': 'Mackerras-3-Clause-acknowledgment', 'deprecated': False},
443
+ 'magaz': {'id': 'magaz', 'deprecated': False},
444
+ 'mailprio': {'id': 'mailprio', 'deprecated': False},
445
+ 'makeindex': {'id': 'MakeIndex', 'deprecated': False},
446
+ 'man2html': {'id': 'man2html', 'deprecated': False},
447
+ 'martin-birgmeier': {'id': 'Martin-Birgmeier', 'deprecated': False},
448
+ 'mcphee-slideshow': {'id': 'McPhee-slideshow', 'deprecated': False},
449
+ 'metamail': {'id': 'metamail', 'deprecated': False},
450
+ 'minpack': {'id': 'Minpack', 'deprecated': False},
451
+ 'mips': {'id': 'MIPS', 'deprecated': False},
452
+ 'miros': {'id': 'MirOS', 'deprecated': False},
453
+ 'mit': {'id': 'MIT', 'deprecated': False},
454
+ 'mit-0': {'id': 'MIT-0', 'deprecated': False},
455
+ 'mit-advertising': {'id': 'MIT-advertising', 'deprecated': False},
456
+ 'mit-click': {'id': 'MIT-Click', 'deprecated': False},
457
+ 'mit-cmu': {'id': 'MIT-CMU', 'deprecated': False},
458
+ 'mit-enna': {'id': 'MIT-enna', 'deprecated': False},
459
+ 'mit-feh': {'id': 'MIT-feh', 'deprecated': False},
460
+ 'mit-festival': {'id': 'MIT-Festival', 'deprecated': False},
461
+ 'mit-khronos-old': {'id': 'MIT-Khronos-old', 'deprecated': False},
462
+ 'mit-modern-variant': {'id': 'MIT-Modern-Variant', 'deprecated': False},
463
+ 'mit-open-group': {'id': 'MIT-open-group', 'deprecated': False},
464
+ 'mit-testregex': {'id': 'MIT-testregex', 'deprecated': False},
465
+ 'mit-wu': {'id': 'MIT-Wu', 'deprecated': False},
466
+ 'mitnfa': {'id': 'MITNFA', 'deprecated': False},
467
+ 'mmixware': {'id': 'MMIXware', 'deprecated': False},
468
+ 'motosoto': {'id': 'Motosoto', 'deprecated': False},
469
+ 'mpeg-ssg': {'id': 'MPEG-SSG', 'deprecated': False},
470
+ 'mpi-permissive': {'id': 'mpi-permissive', 'deprecated': False},
471
+ 'mpich2': {'id': 'mpich2', 'deprecated': False},
472
+ 'mpl-1.0': {'id': 'MPL-1.0', 'deprecated': False},
473
+ 'mpl-1.1': {'id': 'MPL-1.1', 'deprecated': False},
474
+ 'mpl-2.0': {'id': 'MPL-2.0', 'deprecated': False},
475
+ 'mpl-2.0-no-copyleft-exception': {'id': 'MPL-2.0-no-copyleft-exception', 'deprecated': False},
476
+ 'mplus': {'id': 'mplus', 'deprecated': False},
477
+ 'ms-lpl': {'id': 'MS-LPL', 'deprecated': False},
478
+ 'ms-pl': {'id': 'MS-PL', 'deprecated': False},
479
+ 'ms-rl': {'id': 'MS-RL', 'deprecated': False},
480
+ 'mtll': {'id': 'MTLL', 'deprecated': False},
481
+ 'mulanpsl-1.0': {'id': 'MulanPSL-1.0', 'deprecated': False},
482
+ 'mulanpsl-2.0': {'id': 'MulanPSL-2.0', 'deprecated': False},
483
+ 'multics': {'id': 'Multics', 'deprecated': False},
484
+ 'mup': {'id': 'Mup', 'deprecated': False},
485
+ 'naist-2003': {'id': 'NAIST-2003', 'deprecated': False},
486
+ 'nasa-1.3': {'id': 'NASA-1.3', 'deprecated': False},
487
+ 'naumen': {'id': 'Naumen', 'deprecated': False},
488
+ 'nbpl-1.0': {'id': 'NBPL-1.0', 'deprecated': False},
489
+ 'ncbi-pd': {'id': 'NCBI-PD', 'deprecated': False},
490
+ 'ncgl-uk-2.0': {'id': 'NCGL-UK-2.0', 'deprecated': False},
491
+ 'ncl': {'id': 'NCL', 'deprecated': False},
492
+ 'ncsa': {'id': 'NCSA', 'deprecated': False},
493
+ 'net-snmp': {'id': 'Net-SNMP', 'deprecated': True},
494
+ 'netcdf': {'id': 'NetCDF', 'deprecated': False},
495
+ 'newsletr': {'id': 'Newsletr', 'deprecated': False},
496
+ 'ngpl': {'id': 'NGPL', 'deprecated': False},
497
+ 'ngrep': {'id': 'ngrep', 'deprecated': False},
498
+ 'nicta-1.0': {'id': 'NICTA-1.0', 'deprecated': False},
499
+ 'nist-pd': {'id': 'NIST-PD', 'deprecated': False},
500
+ 'nist-pd-fallback': {'id': 'NIST-PD-fallback', 'deprecated': False},
501
+ 'nist-software': {'id': 'NIST-Software', 'deprecated': False},
502
+ 'nlod-1.0': {'id': 'NLOD-1.0', 'deprecated': False},
503
+ 'nlod-2.0': {'id': 'NLOD-2.0', 'deprecated': False},
504
+ 'nlpl': {'id': 'NLPL', 'deprecated': False},
505
+ 'nokia': {'id': 'Nokia', 'deprecated': False},
506
+ 'nosl': {'id': 'NOSL', 'deprecated': False},
507
+ 'noweb': {'id': 'Noweb', 'deprecated': False},
508
+ 'npl-1.0': {'id': 'NPL-1.0', 'deprecated': False},
509
+ 'npl-1.1': {'id': 'NPL-1.1', 'deprecated': False},
510
+ 'nposl-3.0': {'id': 'NPOSL-3.0', 'deprecated': False},
511
+ 'nrl': {'id': 'NRL', 'deprecated': False},
512
+ 'ntia-pd': {'id': 'NTIA-PD', 'deprecated': False},
513
+ 'ntp': {'id': 'NTP', 'deprecated': False},
514
+ 'ntp-0': {'id': 'NTP-0', 'deprecated': False},
515
+ 'nunit': {'id': 'Nunit', 'deprecated': True},
516
+ 'o-uda-1.0': {'id': 'O-UDA-1.0', 'deprecated': False},
517
+ 'oar': {'id': 'OAR', 'deprecated': False},
518
+ 'occt-pl': {'id': 'OCCT-PL', 'deprecated': False},
519
+ 'oclc-2.0': {'id': 'OCLC-2.0', 'deprecated': False},
520
+ 'odbl-1.0': {'id': 'ODbL-1.0', 'deprecated': False},
521
+ 'odc-by-1.0': {'id': 'ODC-By-1.0', 'deprecated': False},
522
+ 'offis': {'id': 'OFFIS', 'deprecated': False},
523
+ 'ofl-1.0': {'id': 'OFL-1.0', 'deprecated': False},
524
+ 'ofl-1.0-no-rfn': {'id': 'OFL-1.0-no-RFN', 'deprecated': False},
525
+ 'ofl-1.0-rfn': {'id': 'OFL-1.0-RFN', 'deprecated': False},
526
+ 'ofl-1.1': {'id': 'OFL-1.1', 'deprecated': False},
527
+ 'ofl-1.1-no-rfn': {'id': 'OFL-1.1-no-RFN', 'deprecated': False},
528
+ 'ofl-1.1-rfn': {'id': 'OFL-1.1-RFN', 'deprecated': False},
529
+ 'ogc-1.0': {'id': 'OGC-1.0', 'deprecated': False},
530
+ 'ogdl-taiwan-1.0': {'id': 'OGDL-Taiwan-1.0', 'deprecated': False},
531
+ 'ogl-canada-2.0': {'id': 'OGL-Canada-2.0', 'deprecated': False},
532
+ 'ogl-uk-1.0': {'id': 'OGL-UK-1.0', 'deprecated': False},
533
+ 'ogl-uk-2.0': {'id': 'OGL-UK-2.0', 'deprecated': False},
534
+ 'ogl-uk-3.0': {'id': 'OGL-UK-3.0', 'deprecated': False},
535
+ 'ogtsl': {'id': 'OGTSL', 'deprecated': False},
536
+ 'oldap-1.1': {'id': 'OLDAP-1.1', 'deprecated': False},
537
+ 'oldap-1.2': {'id': 'OLDAP-1.2', 'deprecated': False},
538
+ 'oldap-1.3': {'id': 'OLDAP-1.3', 'deprecated': False},
539
+ 'oldap-1.4': {'id': 'OLDAP-1.4', 'deprecated': False},
540
+ 'oldap-2.0': {'id': 'OLDAP-2.0', 'deprecated': False},
541
+ 'oldap-2.0.1': {'id': 'OLDAP-2.0.1', 'deprecated': False},
542
+ 'oldap-2.1': {'id': 'OLDAP-2.1', 'deprecated': False},
543
+ 'oldap-2.2': {'id': 'OLDAP-2.2', 'deprecated': False},
544
+ 'oldap-2.2.1': {'id': 'OLDAP-2.2.1', 'deprecated': False},
545
+ 'oldap-2.2.2': {'id': 'OLDAP-2.2.2', 'deprecated': False},
546
+ 'oldap-2.3': {'id': 'OLDAP-2.3', 'deprecated': False},
547
+ 'oldap-2.4': {'id': 'OLDAP-2.4', 'deprecated': False},
548
+ 'oldap-2.5': {'id': 'OLDAP-2.5', 'deprecated': False},
549
+ 'oldap-2.6': {'id': 'OLDAP-2.6', 'deprecated': False},
550
+ 'oldap-2.7': {'id': 'OLDAP-2.7', 'deprecated': False},
551
+ 'oldap-2.8': {'id': 'OLDAP-2.8', 'deprecated': False},
552
+ 'olfl-1.3': {'id': 'OLFL-1.3', 'deprecated': False},
553
+ 'oml': {'id': 'OML', 'deprecated': False},
554
+ 'openpbs-2.3': {'id': 'OpenPBS-2.3', 'deprecated': False},
555
+ 'openssl': {'id': 'OpenSSL', 'deprecated': False},
556
+ 'openssl-standalone': {'id': 'OpenSSL-standalone', 'deprecated': False},
557
+ 'openvision': {'id': 'OpenVision', 'deprecated': False},
558
+ 'opl-1.0': {'id': 'OPL-1.0', 'deprecated': False},
559
+ 'opl-uk-3.0': {'id': 'OPL-UK-3.0', 'deprecated': False},
560
+ 'opubl-1.0': {'id': 'OPUBL-1.0', 'deprecated': False},
561
+ 'oset-pl-2.1': {'id': 'OSET-PL-2.1', 'deprecated': False},
562
+ 'osl-1.0': {'id': 'OSL-1.0', 'deprecated': False},
563
+ 'osl-1.1': {'id': 'OSL-1.1', 'deprecated': False},
564
+ 'osl-2.0': {'id': 'OSL-2.0', 'deprecated': False},
565
+ 'osl-2.1': {'id': 'OSL-2.1', 'deprecated': False},
566
+ 'osl-3.0': {'id': 'OSL-3.0', 'deprecated': False},
567
+ 'padl': {'id': 'PADL', 'deprecated': False},
568
+ 'parity-6.0.0': {'id': 'Parity-6.0.0', 'deprecated': False},
569
+ 'parity-7.0.0': {'id': 'Parity-7.0.0', 'deprecated': False},
570
+ 'pddl-1.0': {'id': 'PDDL-1.0', 'deprecated': False},
571
+ 'php-3.0': {'id': 'PHP-3.0', 'deprecated': False},
572
+ 'php-3.01': {'id': 'PHP-3.01', 'deprecated': False},
573
+ 'pixar': {'id': 'Pixar', 'deprecated': False},
574
+ 'pkgconf': {'id': 'pkgconf', 'deprecated': False},
575
+ 'plexus': {'id': 'Plexus', 'deprecated': False},
576
+ 'pnmstitch': {'id': 'pnmstitch', 'deprecated': False},
577
+ 'polyform-noncommercial-1.0.0': {'id': 'PolyForm-Noncommercial-1.0.0', 'deprecated': False},
578
+ 'polyform-small-business-1.0.0': {'id': 'PolyForm-Small-Business-1.0.0', 'deprecated': False},
579
+ 'postgresql': {'id': 'PostgreSQL', 'deprecated': False},
580
+ 'ppl': {'id': 'PPL', 'deprecated': False},
581
+ 'psf-2.0': {'id': 'PSF-2.0', 'deprecated': False},
582
+ 'psfrag': {'id': 'psfrag', 'deprecated': False},
583
+ 'psutils': {'id': 'psutils', 'deprecated': False},
584
+ 'python-2.0': {'id': 'Python-2.0', 'deprecated': False},
585
+ 'python-2.0.1': {'id': 'Python-2.0.1', 'deprecated': False},
586
+ 'python-ldap': {'id': 'python-ldap', 'deprecated': False},
587
+ 'qhull': {'id': 'Qhull', 'deprecated': False},
588
+ 'qpl-1.0': {'id': 'QPL-1.0', 'deprecated': False},
589
+ 'qpl-1.0-inria-2004': {'id': 'QPL-1.0-INRIA-2004', 'deprecated': False},
590
+ 'radvd': {'id': 'radvd', 'deprecated': False},
591
+ 'rdisc': {'id': 'Rdisc', 'deprecated': False},
592
+ 'rhecos-1.1': {'id': 'RHeCos-1.1', 'deprecated': False},
593
+ 'rpl-1.1': {'id': 'RPL-1.1', 'deprecated': False},
594
+ 'rpl-1.5': {'id': 'RPL-1.5', 'deprecated': False},
595
+ 'rpsl-1.0': {'id': 'RPSL-1.0', 'deprecated': False},
596
+ 'rsa-md': {'id': 'RSA-MD', 'deprecated': False},
597
+ 'rscpl': {'id': 'RSCPL', 'deprecated': False},
598
+ 'ruby': {'id': 'Ruby', 'deprecated': False},
599
+ 'ruby-pty': {'id': 'Ruby-pty', 'deprecated': False},
600
+ 'sax-pd': {'id': 'SAX-PD', 'deprecated': False},
601
+ 'sax-pd-2.0': {'id': 'SAX-PD-2.0', 'deprecated': False},
602
+ 'saxpath': {'id': 'Saxpath', 'deprecated': False},
603
+ 'scea': {'id': 'SCEA', 'deprecated': False},
604
+ 'schemereport': {'id': 'SchemeReport', 'deprecated': False},
605
+ 'sendmail': {'id': 'Sendmail', 'deprecated': False},
606
+ 'sendmail-8.23': {'id': 'Sendmail-8.23', 'deprecated': False},
607
+ 'sendmail-open-source-1.1': {'id': 'Sendmail-Open-Source-1.1', 'deprecated': False},
608
+ 'sgi-b-1.0': {'id': 'SGI-B-1.0', 'deprecated': False},
609
+ 'sgi-b-1.1': {'id': 'SGI-B-1.1', 'deprecated': False},
610
+ 'sgi-b-2.0': {'id': 'SGI-B-2.0', 'deprecated': False},
611
+ 'sgi-opengl': {'id': 'SGI-OpenGL', 'deprecated': False},
612
+ 'sgp4': {'id': 'SGP4', 'deprecated': False},
613
+ 'shl-0.5': {'id': 'SHL-0.5', 'deprecated': False},
614
+ 'shl-0.51': {'id': 'SHL-0.51', 'deprecated': False},
615
+ 'simpl-2.0': {'id': 'SimPL-2.0', 'deprecated': False},
616
+ 'sissl': {'id': 'SISSL', 'deprecated': False},
617
+ 'sissl-1.2': {'id': 'SISSL-1.2', 'deprecated': False},
618
+ 'sl': {'id': 'SL', 'deprecated': False},
619
+ 'sleepycat': {'id': 'Sleepycat', 'deprecated': False},
620
+ 'smail-gpl': {'id': 'SMAIL-GPL', 'deprecated': False},
621
+ 'smlnj': {'id': 'SMLNJ', 'deprecated': False},
622
+ 'smppl': {'id': 'SMPPL', 'deprecated': False},
623
+ 'snia': {'id': 'SNIA', 'deprecated': False},
624
+ 'snprintf': {'id': 'snprintf', 'deprecated': False},
625
+ 'sofa': {'id': 'SOFA', 'deprecated': False},
626
+ 'softsurfer': {'id': 'softSurfer', 'deprecated': False},
627
+ 'soundex': {'id': 'Soundex', 'deprecated': False},
628
+ 'spencer-86': {'id': 'Spencer-86', 'deprecated': False},
629
+ 'spencer-94': {'id': 'Spencer-94', 'deprecated': False},
630
+ 'spencer-99': {'id': 'Spencer-99', 'deprecated': False},
631
+ 'spl-1.0': {'id': 'SPL-1.0', 'deprecated': False},
632
+ 'ssh-keyscan': {'id': 'ssh-keyscan', 'deprecated': False},
633
+ 'ssh-openssh': {'id': 'SSH-OpenSSH', 'deprecated': False},
634
+ 'ssh-short': {'id': 'SSH-short', 'deprecated': False},
635
+ 'ssleay-standalone': {'id': 'SSLeay-standalone', 'deprecated': False},
636
+ 'sspl-1.0': {'id': 'SSPL-1.0', 'deprecated': False},
637
+ 'standardml-nj': {'id': 'StandardML-NJ', 'deprecated': True},
638
+ 'sugarcrm-1.1.3': {'id': 'SugarCRM-1.1.3', 'deprecated': False},
639
+ 'sul-1.0': {'id': 'SUL-1.0', 'deprecated': False},
640
+ 'sun-ppp': {'id': 'Sun-PPP', 'deprecated': False},
641
+ 'sun-ppp-2000': {'id': 'Sun-PPP-2000', 'deprecated': False},
642
+ 'sunpro': {'id': 'SunPro', 'deprecated': False},
643
+ 'swl': {'id': 'SWL', 'deprecated': False},
644
+ 'swrule': {'id': 'swrule', 'deprecated': False},
645
+ 'symlinks': {'id': 'Symlinks', 'deprecated': False},
646
+ 'tapr-ohl-1.0': {'id': 'TAPR-OHL-1.0', 'deprecated': False},
647
+ 'tcl': {'id': 'TCL', 'deprecated': False},
648
+ 'tcp-wrappers': {'id': 'TCP-wrappers', 'deprecated': False},
649
+ 'termreadkey': {'id': 'TermReadKey', 'deprecated': False},
650
+ 'tgppl-1.0': {'id': 'TGPPL-1.0', 'deprecated': False},
651
+ 'thirdeye': {'id': 'ThirdEye', 'deprecated': False},
652
+ 'threeparttable': {'id': 'threeparttable', 'deprecated': False},
653
+ 'tmate': {'id': 'TMate', 'deprecated': False},
654
+ 'torque-1.1': {'id': 'TORQUE-1.1', 'deprecated': False},
655
+ 'tosl': {'id': 'TOSL', 'deprecated': False},
656
+ 'tpdl': {'id': 'TPDL', 'deprecated': False},
657
+ 'tpl-1.0': {'id': 'TPL-1.0', 'deprecated': False},
658
+ 'trustedqsl': {'id': 'TrustedQSL', 'deprecated': False},
659
+ 'ttwl': {'id': 'TTWL', 'deprecated': False},
660
+ 'ttyp0': {'id': 'TTYP0', 'deprecated': False},
661
+ 'tu-berlin-1.0': {'id': 'TU-Berlin-1.0', 'deprecated': False},
662
+ 'tu-berlin-2.0': {'id': 'TU-Berlin-2.0', 'deprecated': False},
663
+ 'ubuntu-font-1.0': {'id': 'Ubuntu-font-1.0', 'deprecated': False},
664
+ 'ucar': {'id': 'UCAR', 'deprecated': False},
665
+ 'ucl-1.0': {'id': 'UCL-1.0', 'deprecated': False},
666
+ 'ulem': {'id': 'ulem', 'deprecated': False},
667
+ 'umich-merit': {'id': 'UMich-Merit', 'deprecated': False},
668
+ 'unicode-3.0': {'id': 'Unicode-3.0', 'deprecated': False},
669
+ 'unicode-dfs-2015': {'id': 'Unicode-DFS-2015', 'deprecated': False},
670
+ 'unicode-dfs-2016': {'id': 'Unicode-DFS-2016', 'deprecated': False},
671
+ 'unicode-tou': {'id': 'Unicode-TOU', 'deprecated': False},
672
+ 'unixcrypt': {'id': 'UnixCrypt', 'deprecated': False},
673
+ 'unlicense': {'id': 'Unlicense', 'deprecated': False},
674
+ 'unlicense-libtelnet': {'id': 'Unlicense-libtelnet', 'deprecated': False},
675
+ 'unlicense-libwhirlpool': {'id': 'Unlicense-libwhirlpool', 'deprecated': False},
676
+ 'upl-1.0': {'id': 'UPL-1.0', 'deprecated': False},
677
+ 'urt-rle': {'id': 'URT-RLE', 'deprecated': False},
678
+ 'vim': {'id': 'Vim', 'deprecated': False},
679
+ 'vostrom': {'id': 'VOSTROM', 'deprecated': False},
680
+ 'vsl-1.0': {'id': 'VSL-1.0', 'deprecated': False},
681
+ 'w3c': {'id': 'W3C', 'deprecated': False},
682
+ 'w3c-19980720': {'id': 'W3C-19980720', 'deprecated': False},
683
+ 'w3c-20150513': {'id': 'W3C-20150513', 'deprecated': False},
684
+ 'w3m': {'id': 'w3m', 'deprecated': False},
685
+ 'watcom-1.0': {'id': 'Watcom-1.0', 'deprecated': False},
686
+ 'widget-workshop': {'id': 'Widget-Workshop', 'deprecated': False},
687
+ 'wsuipa': {'id': 'Wsuipa', 'deprecated': False},
688
+ 'wtfpl': {'id': 'WTFPL', 'deprecated': False},
689
+ 'wwl': {'id': 'wwl', 'deprecated': False},
690
+ 'wxwindows': {'id': 'wxWindows', 'deprecated': True},
691
+ 'x11': {'id': 'X11', 'deprecated': False},
692
+ 'x11-distribute-modifications-variant': {'id': 'X11-distribute-modifications-variant', 'deprecated': False},
693
+ 'x11-swapped': {'id': 'X11-swapped', 'deprecated': False},
694
+ 'xdebug-1.03': {'id': 'Xdebug-1.03', 'deprecated': False},
695
+ 'xerox': {'id': 'Xerox', 'deprecated': False},
696
+ 'xfig': {'id': 'Xfig', 'deprecated': False},
697
+ 'xfree86-1.1': {'id': 'XFree86-1.1', 'deprecated': False},
698
+ 'xinetd': {'id': 'xinetd', 'deprecated': False},
699
+ 'xkeyboard-config-zinoviev': {'id': 'xkeyboard-config-Zinoviev', 'deprecated': False},
700
+ 'xlock': {'id': 'xlock', 'deprecated': False},
701
+ 'xnet': {'id': 'Xnet', 'deprecated': False},
702
+ 'xpp': {'id': 'xpp', 'deprecated': False},
703
+ 'xskat': {'id': 'XSkat', 'deprecated': False},
704
+ 'xzoom': {'id': 'xzoom', 'deprecated': False},
705
+ 'ypl-1.0': {'id': 'YPL-1.0', 'deprecated': False},
706
+ 'ypl-1.1': {'id': 'YPL-1.1', 'deprecated': False},
707
+ 'zed': {'id': 'Zed', 'deprecated': False},
708
+ 'zeeff': {'id': 'Zeeff', 'deprecated': False},
709
+ 'zend-2.0': {'id': 'Zend-2.0', 'deprecated': False},
710
+ 'zimbra-1.3': {'id': 'Zimbra-1.3', 'deprecated': False},
711
+ 'zimbra-1.4': {'id': 'Zimbra-1.4', 'deprecated': False},
712
+ 'zlib': {'id': 'Zlib', 'deprecated': False},
713
+ 'zlib-acknowledgement': {'id': 'zlib-acknowledgement', 'deprecated': False},
714
+ 'zpl-1.1': {'id': 'ZPL-1.1', 'deprecated': False},
715
+ 'zpl-2.0': {'id': 'ZPL-2.0', 'deprecated': False},
716
+ 'zpl-2.1': {'id': 'ZPL-2.1', 'deprecated': False},
717
+ }
718
+
719
+ EXCEPTIONS: dict[str, SPDXException] = {
720
+ '389-exception': {'id': '389-exception', 'deprecated': False},
721
+ 'asterisk-exception': {'id': 'Asterisk-exception', 'deprecated': False},
722
+ 'asterisk-linking-protocols-exception': {'id': 'Asterisk-linking-protocols-exception', 'deprecated': False},
723
+ 'autoconf-exception-2.0': {'id': 'Autoconf-exception-2.0', 'deprecated': False},
724
+ 'autoconf-exception-3.0': {'id': 'Autoconf-exception-3.0', 'deprecated': False},
725
+ 'autoconf-exception-generic': {'id': 'Autoconf-exception-generic', 'deprecated': False},
726
+ 'autoconf-exception-generic-3.0': {'id': 'Autoconf-exception-generic-3.0', 'deprecated': False},
727
+ 'autoconf-exception-macro': {'id': 'Autoconf-exception-macro', 'deprecated': False},
728
+ 'bison-exception-1.24': {'id': 'Bison-exception-1.24', 'deprecated': False},
729
+ 'bison-exception-2.2': {'id': 'Bison-exception-2.2', 'deprecated': False},
730
+ 'bootloader-exception': {'id': 'Bootloader-exception', 'deprecated': False},
731
+ 'cgal-linking-exception': {'id': 'CGAL-linking-exception', 'deprecated': False},
732
+ 'classpath-exception-2.0': {'id': 'Classpath-exception-2.0', 'deprecated': False},
733
+ 'clisp-exception-2.0': {'id': 'CLISP-exception-2.0', 'deprecated': False},
734
+ 'cryptsetup-openssl-exception': {'id': 'cryptsetup-OpenSSL-exception', 'deprecated': False},
735
+ 'digia-qt-lgpl-exception-1.1': {'id': 'Digia-Qt-LGPL-exception-1.1', 'deprecated': False},
736
+ 'digirule-foss-exception': {'id': 'DigiRule-FOSS-exception', 'deprecated': False},
737
+ 'ecos-exception-2.0': {'id': 'eCos-exception-2.0', 'deprecated': False},
738
+ 'erlang-otp-linking-exception': {'id': 'erlang-otp-linking-exception', 'deprecated': False},
739
+ 'fawkes-runtime-exception': {'id': 'Fawkes-Runtime-exception', 'deprecated': False},
740
+ 'fltk-exception': {'id': 'FLTK-exception', 'deprecated': False},
741
+ 'fmt-exception': {'id': 'fmt-exception', 'deprecated': False},
742
+ 'font-exception-2.0': {'id': 'Font-exception-2.0', 'deprecated': False},
743
+ 'freertos-exception-2.0': {'id': 'freertos-exception-2.0', 'deprecated': False},
744
+ 'gcc-exception-2.0': {'id': 'GCC-exception-2.0', 'deprecated': False},
745
+ 'gcc-exception-2.0-note': {'id': 'GCC-exception-2.0-note', 'deprecated': False},
746
+ 'gcc-exception-3.1': {'id': 'GCC-exception-3.1', 'deprecated': False},
747
+ 'gmsh-exception': {'id': 'Gmsh-exception', 'deprecated': False},
748
+ 'gnat-exception': {'id': 'GNAT-exception', 'deprecated': False},
749
+ 'gnome-examples-exception': {'id': 'GNOME-examples-exception', 'deprecated': False},
750
+ 'gnu-compiler-exception': {'id': 'GNU-compiler-exception', 'deprecated': False},
751
+ 'gnu-javamail-exception': {'id': 'gnu-javamail-exception', 'deprecated': False},
752
+ 'gpl-3.0-389-ds-base-exception': {'id': 'GPL-3.0-389-ds-base-exception', 'deprecated': False},
753
+ 'gpl-3.0-interface-exception': {'id': 'GPL-3.0-interface-exception', 'deprecated': False},
754
+ 'gpl-3.0-linking-exception': {'id': 'GPL-3.0-linking-exception', 'deprecated': False},
755
+ 'gpl-3.0-linking-source-exception': {'id': 'GPL-3.0-linking-source-exception', 'deprecated': False},
756
+ 'gpl-cc-1.0': {'id': 'GPL-CC-1.0', 'deprecated': False},
757
+ 'gstreamer-exception-2005': {'id': 'GStreamer-exception-2005', 'deprecated': False},
758
+ 'gstreamer-exception-2008': {'id': 'GStreamer-exception-2008', 'deprecated': False},
759
+ 'harbour-exception': {'id': 'harbour-exception', 'deprecated': False},
760
+ 'i2p-gpl-java-exception': {'id': 'i2p-gpl-java-exception', 'deprecated': False},
761
+ 'independent-modules-exception': {'id': 'Independent-modules-exception', 'deprecated': False},
762
+ 'kicad-libraries-exception': {'id': 'KiCad-libraries-exception', 'deprecated': False},
763
+ 'lgpl-3.0-linking-exception': {'id': 'LGPL-3.0-linking-exception', 'deprecated': False},
764
+ 'libpri-openh323-exception': {'id': 'libpri-OpenH323-exception', 'deprecated': False},
765
+ 'libtool-exception': {'id': 'Libtool-exception', 'deprecated': False},
766
+ 'linux-syscall-note': {'id': 'Linux-syscall-note', 'deprecated': False},
767
+ 'llgpl': {'id': 'LLGPL', 'deprecated': False},
768
+ 'llvm-exception': {'id': 'LLVM-exception', 'deprecated': False},
769
+ 'lzma-exception': {'id': 'LZMA-exception', 'deprecated': False},
770
+ 'mif-exception': {'id': 'mif-exception', 'deprecated': False},
771
+ 'mxml-exception': {'id': 'mxml-exception', 'deprecated': False},
772
+ 'nokia-qt-exception-1.1': {'id': 'Nokia-Qt-exception-1.1', 'deprecated': True},
773
+ 'ocaml-lgpl-linking-exception': {'id': 'OCaml-LGPL-linking-exception', 'deprecated': False},
774
+ 'occt-exception-1.0': {'id': 'OCCT-exception-1.0', 'deprecated': False},
775
+ 'openjdk-assembly-exception-1.0': {'id': 'OpenJDK-assembly-exception-1.0', 'deprecated': False},
776
+ 'openvpn-openssl-exception': {'id': 'openvpn-openssl-exception', 'deprecated': False},
777
+ 'pcre2-exception': {'id': 'PCRE2-exception', 'deprecated': False},
778
+ 'polyparse-exception': {'id': 'polyparse-exception', 'deprecated': False},
779
+ 'ps-or-pdf-font-exception-20170817': {'id': 'PS-or-PDF-font-exception-20170817', 'deprecated': False},
780
+ 'qpl-1.0-inria-2004-exception': {'id': 'QPL-1.0-INRIA-2004-exception', 'deprecated': False},
781
+ 'qt-gpl-exception-1.0': {'id': 'Qt-GPL-exception-1.0', 'deprecated': False},
782
+ 'qt-lgpl-exception-1.1': {'id': 'Qt-LGPL-exception-1.1', 'deprecated': False},
783
+ 'qwt-exception-1.0': {'id': 'Qwt-exception-1.0', 'deprecated': False},
784
+ 'romic-exception': {'id': 'romic-exception', 'deprecated': False},
785
+ 'rrdtool-floss-exception-2.0': {'id': 'RRDtool-FLOSS-exception-2.0', 'deprecated': False},
786
+ 'sane-exception': {'id': 'SANE-exception', 'deprecated': False},
787
+ 'shl-2.0': {'id': 'SHL-2.0', 'deprecated': False},
788
+ 'shl-2.1': {'id': 'SHL-2.1', 'deprecated': False},
789
+ 'stunnel-exception': {'id': 'stunnel-exception', 'deprecated': False},
790
+ 'swi-exception': {'id': 'SWI-exception', 'deprecated': False},
791
+ 'swift-exception': {'id': 'Swift-exception', 'deprecated': False},
792
+ 'texinfo-exception': {'id': 'Texinfo-exception', 'deprecated': False},
793
+ 'u-boot-exception-2.0': {'id': 'u-boot-exception-2.0', 'deprecated': False},
794
+ 'ubdl-exception': {'id': 'UBDL-exception', 'deprecated': False},
795
+ 'universal-foss-exception-1.0': {'id': 'Universal-FOSS-exception-1.0', 'deprecated': False},
796
+ 'vsftpd-openssl-exception': {'id': 'vsftpd-openssl-exception', 'deprecated': False},
797
+ 'wxwindows-exception-3.1': {'id': 'WxWindows-exception-3.1', 'deprecated': False},
798
+ 'x11vnc-openssl-exception': {'id': 'x11vnc-openssl-exception', 'deprecated': False},
799
+ }
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/markers.py ADDED
@@ -0,0 +1,492 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file is dual licensed under the terms of the Apache License, Version
2
+ # 2.0, and the BSD License. See the LICENSE file in the root of this repository
3
+ # for complete details.
4
+
5
+ from __future__ import annotations
6
+
7
+ import operator
8
+ import os
9
+ import platform
10
+ import sys
11
+ from typing import AbstractSet, Callable, Literal, Mapping, TypedDict, Union, cast
12
+
13
+ from ._parser import MarkerAtom, MarkerList, Op, Value, Variable
14
+ from ._parser import parse_marker as _parse_marker
15
+ from ._tokenizer import ParserSyntaxError
16
+ from .specifiers import InvalidSpecifier, Specifier
17
+ from .utils import canonicalize_name
18
+
19
+ __all__ = [
20
+ "Environment",
21
+ "EvaluateContext",
22
+ "InvalidMarker",
23
+ "Marker",
24
+ "UndefinedComparison",
25
+ "UndefinedEnvironmentName",
26
+ "default_environment",
27
+ ]
28
+
29
+
30
+ def __dir__() -> list[str]:
31
+ return __all__
32
+
33
+
34
+ Operator = Callable[[str, Union[str, AbstractSet[str]]], bool]
35
+ EvaluateContext = Literal["metadata", "lock_file", "requirement"]
36
+ """A ``typing.Literal`` enumerating valid marker evaluation contexts.
37
+
38
+ Valid values for the ``context`` passed to :meth:`Marker.evaluate` are:
39
+
40
+ * ``"metadata"`` (for core metadata; default)
41
+ * ``"lock_file"`` (for lock files)
42
+ * ``"requirement"`` (i.e. all other situations)
43
+ """
44
+
45
+ MARKERS_ALLOWING_SET = {"extras", "dependency_groups"}
46
+ MARKERS_REQUIRING_VERSION = {
47
+ "implementation_version",
48
+ "platform_release",
49
+ "python_full_version",
50
+ "python_version",
51
+ }
52
+
53
+
54
+ class InvalidMarker(ValueError):
55
+ """Raised when attempting to create a :class:`Marker` from invalid input.
56
+
57
+ This error indicates that the given marker string does not conform to the
58
+ :ref:`specification of dependency specifiers <pypug:dependency-specifiers>`.
59
+ """
60
+
61
+
62
+ class UndefinedComparison(ValueError):
63
+ """Raised when evaluating an unsupported marker comparison.
64
+
65
+ This can happen when marker values are compared as versions but do not
66
+ conform to the :ref:`specification of version specifiers
67
+ <pypug:version-specifiers>`.
68
+ """
69
+
70
+
71
+ class UndefinedEnvironmentName(ValueError):
72
+ """Raised when evaluating a marker that references a missing environment key."""
73
+
74
+
75
+ class Environment(TypedDict):
76
+ """
77
+ A dictionary that represents a Python environment as captured by
78
+ :func:`default_environment`. All fields are required.
79
+ """
80
+
81
+ implementation_name: str
82
+ """The implementation's identifier, e.g. ``'cpython'``."""
83
+
84
+ implementation_version: str
85
+ """
86
+ The implementation's version, e.g. ``'3.13.0a2'`` for CPython 3.13.0a2, or
87
+ ``'7.3.13'`` for PyPy3.10 v7.3.13.
88
+ """
89
+
90
+ os_name: str
91
+ """
92
+ The value of :py:data:`os.name`. The name of the operating system dependent module
93
+ imported, e.g. ``'posix'``.
94
+ """
95
+
96
+ platform_machine: str
97
+ """
98
+ Returns the machine type, e.g. ``'i386'``.
99
+
100
+ An empty string if the value cannot be determined.
101
+ """
102
+
103
+ platform_release: str
104
+ """
105
+ The system's release, e.g. ``'2.2.0'`` or ``'NT'``.
106
+
107
+ An empty string if the value cannot be determined.
108
+ """
109
+
110
+ platform_system: str
111
+ """
112
+ The system/OS name, e.g. ``'Linux'``, ``'Windows'`` or ``'Java'``.
113
+
114
+ An empty string if the value cannot be determined.
115
+ """
116
+
117
+ platform_version: str
118
+ """
119
+ The system's release version, e.g. ``'#3 on degas'``.
120
+
121
+ An empty string if the value cannot be determined.
122
+ """
123
+
124
+ python_full_version: str
125
+ """
126
+ The Python version as string ``'major.minor.patchlevel'``.
127
+
128
+ Note that unlike the Python :py:data:`sys.version`, this value will always include
129
+ the patchlevel (it defaults to 0).
130
+ """
131
+
132
+ platform_python_implementation: str
133
+ """
134
+ A string identifying the Python implementation, e.g. ``'CPython'``.
135
+ """
136
+
137
+ python_version: str
138
+ """The Python version as string ``'major.minor'``."""
139
+
140
+ sys_platform: str
141
+ """
142
+ This string contains a platform identifier that can be used to append
143
+ platform-specific components to :py:data:`sys.path`, for instance.
144
+
145
+ For Unix systems, except on Linux and AIX, this is the lowercased OS name as
146
+ returned by ``uname -s`` with the first part of the version as returned by
147
+ ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, at the time when Python
148
+ was built.
149
+ """
150
+
151
+
152
+ def _normalize_extras(
153
+ result: MarkerList | MarkerAtom | str,
154
+ ) -> MarkerList | MarkerAtom | str:
155
+ if not isinstance(result, tuple):
156
+ return result
157
+
158
+ lhs, op, rhs = result
159
+ if isinstance(lhs, Variable) and lhs.value == "extra":
160
+ normalized_extra = canonicalize_name(rhs.value)
161
+ rhs = Value(normalized_extra)
162
+ elif isinstance(rhs, Variable) and rhs.value == "extra":
163
+ normalized_extra = canonicalize_name(lhs.value)
164
+ lhs = Value(normalized_extra)
165
+ return lhs, op, rhs
166
+
167
+
168
+ def _normalize_extra_values(results: MarkerList) -> MarkerList:
169
+ """
170
+ Normalize extra values.
171
+ """
172
+
173
+ return [_normalize_extras(r) for r in results]
174
+
175
+
176
+ def _format_marker(
177
+ marker: list[str] | MarkerAtom | str, first: bool | None = True
178
+ ) -> str:
179
+ assert isinstance(marker, (list, tuple, str))
180
+
181
+ # Sometimes we have a structure like [[...]] which is a single item list
182
+ # where the single item is itself it's own list. In that case we want skip
183
+ # the rest of this function so that we don't get extraneous () on the
184
+ # outside.
185
+ if (
186
+ isinstance(marker, list)
187
+ and len(marker) == 1
188
+ and isinstance(marker[0], (list, tuple))
189
+ ):
190
+ return _format_marker(marker[0])
191
+
192
+ if isinstance(marker, list):
193
+ inner = (_format_marker(m, first=False) for m in marker)
194
+ if first:
195
+ return " ".join(inner)
196
+ else:
197
+ return "(" + " ".join(inner) + ")"
198
+ elif isinstance(marker, tuple):
199
+ return " ".join([m.serialize() for m in marker])
200
+ else:
201
+ return marker
202
+
203
+
204
+ _operators: dict[str, Operator] = {
205
+ "in": lambda lhs, rhs: lhs in rhs,
206
+ "not in": lambda lhs, rhs: lhs not in rhs,
207
+ "<": lambda _lhs, _rhs: False,
208
+ "<=": operator.eq,
209
+ "==": operator.eq,
210
+ "!=": operator.ne,
211
+ ">=": operator.eq,
212
+ ">": lambda _lhs, _rhs: False,
213
+ }
214
+
215
+
216
+ def _eval_op(lhs: str, op: Op, rhs: str | AbstractSet[str], *, key: str) -> bool:
217
+ op_str = op.serialize()
218
+ if key in MARKERS_REQUIRING_VERSION:
219
+ try:
220
+ spec = Specifier(f"{op_str}{rhs}")
221
+ except InvalidSpecifier:
222
+ pass
223
+ else:
224
+ return spec.contains(lhs, prereleases=True)
225
+
226
+ oper: Operator | None = _operators.get(op_str)
227
+ if oper is None:
228
+ raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.")
229
+
230
+ return oper(lhs, rhs)
231
+
232
+
233
+ def _normalize(
234
+ lhs: str, rhs: str | AbstractSet[str], key: str
235
+ ) -> tuple[str, str | AbstractSet[str]]:
236
+ # PEP 685 - Comparison of extra names for optional distribution dependencies
237
+ # https://peps.python.org/pep-0685/
238
+ # > When comparing extra names, tools MUST normalize the names being
239
+ # > compared using the semantics outlined in PEP 503 for names
240
+ if key == "extra":
241
+ assert isinstance(rhs, str), "extra value must be a string"
242
+ # Both sides are normalized at this point already
243
+ return (lhs, rhs)
244
+ if key in MARKERS_ALLOWING_SET:
245
+ if isinstance(rhs, str): # pragma: no cover
246
+ return (canonicalize_name(lhs), canonicalize_name(rhs))
247
+ else:
248
+ return (canonicalize_name(lhs), {canonicalize_name(v) for v in rhs})
249
+
250
+ # other environment markers don't have such standards
251
+ return lhs, rhs
252
+
253
+
254
+ def _evaluate_markers(
255
+ markers: MarkerList, environment: dict[str, str | AbstractSet[str]]
256
+ ) -> bool:
257
+ groups: list[list[bool]] = [[]]
258
+
259
+ for marker in markers:
260
+ if isinstance(marker, list):
261
+ groups[-1].append(_evaluate_markers(marker, environment))
262
+ elif isinstance(marker, tuple):
263
+ lhs, op, rhs = marker
264
+
265
+ if isinstance(lhs, Variable):
266
+ environment_key = lhs.value
267
+ lhs_value = environment[environment_key]
268
+ rhs_value = rhs.value
269
+ else:
270
+ lhs_value = lhs.value
271
+ environment_key = rhs.value
272
+ rhs_value = environment[environment_key]
273
+
274
+ assert isinstance(lhs_value, str), "lhs must be a string"
275
+ lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key)
276
+ groups[-1].append(_eval_op(lhs_value, op, rhs_value, key=environment_key))
277
+ elif marker == "or":
278
+ groups.append([])
279
+ elif marker == "and":
280
+ pass
281
+ else: # pragma: nocover
282
+ raise TypeError(f"Unexpected marker {marker!r}")
283
+
284
+ return any(all(item) for item in groups)
285
+
286
+
287
+ def _format_full_version(info: sys._version_info) -> str:
288
+ version = f"{info.major}.{info.minor}.{info.micro}"
289
+ kind = info.releaselevel
290
+ if kind != "final":
291
+ version += kind[0] + str(info.serial)
292
+ return version
293
+
294
+
295
+ def default_environment() -> Environment:
296
+ """Return the default marker environment for the current Python process.
297
+
298
+ This is the base environment used by :meth:`Marker.evaluate`.
299
+ """
300
+ iver = _format_full_version(sys.implementation.version)
301
+ implementation_name = sys.implementation.name
302
+ return {
303
+ "implementation_name": implementation_name,
304
+ "implementation_version": iver,
305
+ "os_name": os.name,
306
+ "platform_machine": platform.machine(),
307
+ "platform_release": platform.release(),
308
+ "platform_system": platform.system(),
309
+ "platform_version": platform.version(),
310
+ "python_full_version": platform.python_version(),
311
+ "platform_python_implementation": platform.python_implementation(),
312
+ "python_version": ".".join(platform.python_version_tuple()[:2]),
313
+ "sys_platform": sys.platform,
314
+ }
315
+
316
+
317
+ class Marker:
318
+ """Represents a parsed dependency marker expression.
319
+
320
+ Marker expressions are parsed according to the
321
+ :ref:`specification of dependency specifiers <pypug:dependency-specifiers>`.
322
+
323
+ :param marker: The string representation of a marker expression.
324
+ :raises InvalidMarker: If ``marker`` cannot be parsed.
325
+
326
+ Instances are safe to serialize with :mod:`pickle`. They use a stable
327
+ format so the same pickle can be loaded in future packaging releases.
328
+
329
+ .. versionchanged:: 26.2
330
+
331
+ Added a stable pickle format. Pickles created with packaging 26.2+ can
332
+ be unpickled with future releases. Backward compatibility with pickles
333
+ from pip._vendor.packaging < 26.2 is supported but may be removed in a future
334
+ release.
335
+ """
336
+
337
+ __slots__ = ("_markers",)
338
+
339
+ def __init__(self, marker: str) -> None:
340
+ # Note: We create a Marker object without calling this constructor in
341
+ # packaging.requirements.Requirement. If any additional logic is
342
+ # added here, make sure to mirror/adapt Requirement.
343
+
344
+ # If this fails and throws an error, the repr still expects _markers to
345
+ # be defined.
346
+ self._markers: MarkerList = []
347
+
348
+ try:
349
+ self._markers = _normalize_extra_values(_parse_marker(marker))
350
+ # The attribute `_markers` can be described in terms of a recursive type:
351
+ # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]]
352
+ #
353
+ # For example, the following expression:
354
+ # python_version > "3.6" or (python_version == "3.6" and os_name == "unix")
355
+ #
356
+ # is parsed into:
357
+ # [
358
+ # (<Variable('python_version')>, <Op('>')>, <Value('3.6')>),
359
+ # 'and',
360
+ # [
361
+ # (<Variable('python_version')>, <Op('==')>, <Value('3.6')>),
362
+ # 'or',
363
+ # (<Variable('os_name')>, <Op('==')>, <Value('unix')>)
364
+ # ]
365
+ # ]
366
+ except ParserSyntaxError as e:
367
+ raise InvalidMarker(str(e)) from e
368
+
369
+ @classmethod
370
+ def _from_markers(cls, markers: MarkerList) -> Marker:
371
+ """Create a Marker instance from a pre-parsed marker tree.
372
+
373
+ This avoids re-parsing serialised marker strings when combining markers.
374
+ """
375
+ new = cls.__new__(cls)
376
+ new._markers = markers
377
+ return new
378
+
379
+ def __str__(self) -> str:
380
+ return _format_marker(self._markers)
381
+
382
+ def __repr__(self) -> str:
383
+ return f"<{self.__class__.__name__}({str(self)!r})>"
384
+
385
+ def __hash__(self) -> int:
386
+ return hash(str(self))
387
+
388
+ def __eq__(self, other: object) -> bool:
389
+ if not isinstance(other, Marker):
390
+ return NotImplemented
391
+
392
+ return str(self) == str(other)
393
+
394
+ def __getstate__(self) -> str:
395
+ # Return the marker expression string for compactness and stability.
396
+ # Internal Node objects are excluded; the string is re-parsed on load.
397
+ return str(self)
398
+
399
+ def __setstate__(self, state: object) -> None:
400
+ if isinstance(state, str):
401
+ # New format (26.2+): just the marker expression string.
402
+ try:
403
+ self._markers = _normalize_extra_values(_parse_marker(state))
404
+ except ParserSyntaxError as exc:
405
+ raise TypeError(f"Cannot restore Marker from {state!r}") from exc
406
+ return
407
+ if isinstance(state, dict) and "_markers" in state:
408
+ # Old format (packaging <= 26.1, no __slots__): plain __dict__.
409
+ markers = state["_markers"]
410
+ if isinstance(markers, list):
411
+ self._markers = markers
412
+ return
413
+ if isinstance(state, tuple) and len(state) == 2:
414
+ # Old format (packaging <= 26.1, __slots__): (None, {slot: value}).
415
+ _, slot_dict = state
416
+ if isinstance(slot_dict, dict) and "_markers" in slot_dict:
417
+ markers = slot_dict["_markers"]
418
+ if isinstance(markers, list):
419
+ self._markers = markers
420
+ return
421
+ raise TypeError(f"Cannot restore Marker from {state!r}")
422
+
423
+ def __and__(self, other: Marker) -> Marker:
424
+ if not isinstance(other, Marker):
425
+ return NotImplemented
426
+ return self._from_markers([self._markers, "and", other._markers])
427
+
428
+ def __or__(self, other: Marker) -> Marker:
429
+ if not isinstance(other, Marker):
430
+ return NotImplemented
431
+ return self._from_markers([self._markers, "or", other._markers])
432
+
433
+ def evaluate(
434
+ self,
435
+ environment: Mapping[str, str | AbstractSet[str]] | None = None,
436
+ context: EvaluateContext = "metadata",
437
+ ) -> bool:
438
+ """Evaluate a marker.
439
+
440
+ Return the boolean from evaluating this marker against the environment.
441
+ The environment is determined from the current Python process unless
442
+ passed in explicitly.
443
+
444
+ :param environment: Mapping containing keys and values to override the
445
+ detected environment.
446
+ :param EvaluateContext context: The context in which the marker is
447
+ evaluated, which influences what marker names are considered valid.
448
+ Accepted values are ``"metadata"`` (for core metadata; default),
449
+ ``"lock_file"``, and ``"requirement"`` (i.e. all other situations).
450
+ :raises UndefinedComparison: If the marker uses a comparison on values
451
+ that are not valid versions per the :ref:`specification of version
452
+ specifiers <pypug:version-specifiers>`.
453
+ :raises UndefinedEnvironmentName: If the marker references a value that
454
+ is missing from the evaluation environment.
455
+ :returns: ``True`` if the marker matches, otherwise ``False``.
456
+
457
+ """
458
+ current_environment = cast(
459
+ "dict[str, str | AbstractSet[str]]", default_environment()
460
+ )
461
+ if context == "lock_file":
462
+ current_environment.update(
463
+ extras=frozenset(), dependency_groups=frozenset()
464
+ )
465
+ elif context == "metadata":
466
+ current_environment["extra"] = ""
467
+
468
+ if environment is not None:
469
+ current_environment.update(environment)
470
+ if "extra" in current_environment:
471
+ # The API used to allow setting extra to None. We need to handle
472
+ # this case for backwards compatibility. Also skip running
473
+ # normalize name if extra is empty.
474
+ extra = cast("str | None", current_environment["extra"])
475
+ current_environment["extra"] = canonicalize_name(extra) if extra else ""
476
+
477
+ return _evaluate_markers(
478
+ self._markers, _repair_python_full_version(current_environment)
479
+ )
480
+
481
+
482
+ def _repair_python_full_version(
483
+ env: dict[str, str | AbstractSet[str]],
484
+ ) -> dict[str, str | AbstractSet[str]]:
485
+ """
486
+ Work around platform.python_version() returning something that is not PEP 440
487
+ compliant for non-tagged Python builds.
488
+ """
489
+ python_full_version = cast("str", env["python_full_version"])
490
+ if python_full_version.endswith("+"):
491
+ env["python_full_version"] = f"{python_full_version}local"
492
+ return env
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/metadata.py ADDED
@@ -0,0 +1,964 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import email.header
4
+ import email.message
5
+ import email.parser
6
+ import email.policy
7
+ import keyword
8
+ import pathlib
9
+ import typing
10
+ from typing import (
11
+ Any,
12
+ Callable,
13
+ Generic,
14
+ Literal,
15
+ TypedDict,
16
+ cast,
17
+ )
18
+
19
+ from . import licenses, requirements, specifiers, utils
20
+ from . import version as version_module
21
+ from .errors import ExceptionGroup, _ErrorCollector
22
+
23
+ if typing.TYPE_CHECKING:
24
+ from .licenses import NormalizedLicenseExpression
25
+
26
+ T = typing.TypeVar("T")
27
+
28
+
29
+ __all__ = [
30
+ "ExceptionGroup", # Keep this for a bit (makes mypy happy w/ 26.0 compat)
31
+ "InvalidMetadata",
32
+ "Metadata",
33
+ "RFC822Message",
34
+ "RFC822Policy",
35
+ "RawMetadata",
36
+ "parse_email",
37
+ ]
38
+
39
+
40
+ def __dir__() -> list[str]:
41
+ return __all__
42
+
43
+
44
+ class InvalidMetadata(ValueError):
45
+ """A metadata field contains invalid data."""
46
+
47
+ field: str
48
+ """The name of the field that contains invalid data."""
49
+
50
+ def __init__(self, field: str, message: str) -> None:
51
+ self.field = field
52
+ super().__init__(message)
53
+
54
+
55
+ # The RawMetadata class attempts to make as few assumptions about the underlying
56
+ # serialization formats as possible. The idea is that as long as a serialization
57
+ # formats offer some very basic primitives in *some* way then we can support
58
+ # serializing to and from that format.
59
+ class RawMetadata(TypedDict, total=False):
60
+ """A dictionary of raw core metadata.
61
+
62
+ Each field in core metadata maps to a key of this dictionary (when data is
63
+ provided). The key is lower-case and underscores are used instead of dashes
64
+ compared to the equivalent core metadata field. Any core metadata field that
65
+ can be specified multiple times or can hold multiple values in a single
66
+ field have a key with a plural name. See :class:`Metadata` whose attributes
67
+ match the keys of this dictionary.
68
+
69
+ Core metadata fields that can be specified multiple times are stored as a
70
+ list or dict depending on which is appropriate for the field. Any fields
71
+ which hold multiple values in a single field are stored as a list. All fields
72
+ are considered optional.
73
+ """
74
+
75
+ # Metadata 1.0 - PEP 241
76
+ metadata_version: str
77
+ name: str
78
+ version: str
79
+ platforms: list[str]
80
+ summary: str
81
+ description: str
82
+ keywords: list[str]
83
+ home_page: str
84
+ author: str
85
+ author_email: str
86
+ license: str
87
+
88
+ # Metadata 1.1 - PEP 314
89
+ supported_platforms: list[str]
90
+ download_url: str
91
+ classifiers: list[str]
92
+ requires: list[str]
93
+ provides: list[str]
94
+ obsoletes: list[str]
95
+
96
+ # Metadata 1.2 - PEP 345
97
+ maintainer: str
98
+ maintainer_email: str
99
+ requires_dist: list[str]
100
+ provides_dist: list[str]
101
+ obsoletes_dist: list[str]
102
+ requires_python: str
103
+ requires_external: list[str]
104
+ project_urls: dict[str, str]
105
+
106
+ # Metadata 2.0
107
+ # PEP 426 attempted to completely revamp the metadata format
108
+ # but got stuck without ever being able to build consensus on
109
+ # it and ultimately ended up withdrawn.
110
+ #
111
+ # However, a number of tools had started emitting METADATA with
112
+ # `2.0` Metadata-Version, so for historical reasons, this version
113
+ # was skipped.
114
+
115
+ # Metadata 2.1 - PEP 566
116
+ description_content_type: str
117
+ provides_extra: list[str]
118
+
119
+ # Metadata 2.2 - PEP 643
120
+ dynamic: list[str]
121
+
122
+ # Metadata 2.3 - PEP 685
123
+ # No new fields were added in PEP 685, just some edge case were
124
+ # tightened up to provide better interoperability.
125
+
126
+ # Metadata 2.4 - PEP 639
127
+ license_expression: str
128
+ license_files: list[str]
129
+
130
+ # Metadata 2.5 - PEP 794
131
+ import_names: list[str]
132
+ import_namespaces: list[str]
133
+
134
+
135
+ # 'keywords' is special as it's a string in the core metadata spec, but we
136
+ # represent it as a list.
137
+ _STRING_FIELDS = {
138
+ "author",
139
+ "author_email",
140
+ "description",
141
+ "description_content_type",
142
+ "download_url",
143
+ "home_page",
144
+ "license",
145
+ "license_expression",
146
+ "maintainer",
147
+ "maintainer_email",
148
+ "metadata_version",
149
+ "name",
150
+ "requires_python",
151
+ "summary",
152
+ "version",
153
+ }
154
+
155
+ _LIST_FIELDS = {
156
+ "classifiers",
157
+ "dynamic",
158
+ "license_files",
159
+ "obsoletes",
160
+ "obsoletes_dist",
161
+ "platforms",
162
+ "provides",
163
+ "provides_dist",
164
+ "provides_extra",
165
+ "requires",
166
+ "requires_dist",
167
+ "requires_external",
168
+ "supported_platforms",
169
+ "import_names",
170
+ "import_namespaces",
171
+ }
172
+
173
+ _DICT_FIELDS = {
174
+ "project_urls",
175
+ }
176
+
177
+
178
+ def _parse_keywords(data: str) -> list[str]:
179
+ """Split a string of comma-separated keywords into a list of keywords."""
180
+ return [k.strip() for k in data.split(",")]
181
+
182
+
183
+ def _parse_project_urls(data: list[str]) -> dict[str, str]:
184
+ """Parse a list of label/URL string pairings separated by a comma."""
185
+ urls = {}
186
+ for pair in data:
187
+ # Our logic is slightly tricky here as we want to try and do
188
+ # *something* reasonable with malformed data.
189
+ #
190
+ # The main thing that we have to worry about, is data that does
191
+ # not have a ',' at all to split the label from the Value. There
192
+ # isn't a singular right answer here, and we will fail validation
193
+ # later on (if the caller is validating) so it doesn't *really*
194
+ # matter, but since the missing value has to be an empty str
195
+ # and our return value is dict[str, str], if we let the key
196
+ # be the missing value, then they'd have multiple '' values that
197
+ # overwrite each other in a accumulating dict.
198
+ #
199
+ # The other potential issue is that it's possible to have the
200
+ # same label multiple times in the metadata, with no solid "right"
201
+ # answer with what to do in that case. As such, we'll do the only
202
+ # thing we can, which is treat the field as unparsable and add it
203
+ # to our list of unparsed fields.
204
+ #
205
+ # TODO: The spec doesn't say anything about if the keys should be
206
+ # considered case sensitive or not... logically they should
207
+ # be case-preserving and case-insensitive, but doing that
208
+ # would open up more cases where we might have duplicate
209
+ # entries.
210
+ label, _, url = (s.strip() for s in pair.partition(","))
211
+
212
+ if label in urls:
213
+ # The label already exists in our set of urls, so this field
214
+ # is unparsable, and we can just add the whole thing to our
215
+ # unparsable data and stop processing it.
216
+ raise KeyError("duplicate labels in project urls")
217
+ urls[label] = url
218
+
219
+ return urls
220
+
221
+
222
+ def _get_payload(msg: email.message.Message, source: bytes | str) -> str:
223
+ """Get the body of the message."""
224
+ # If our source is a str, then our caller has managed encodings for us,
225
+ # and we don't need to deal with it.
226
+ if isinstance(source, str):
227
+ payload = msg.get_payload()
228
+ assert isinstance(payload, str)
229
+ return payload
230
+ # If our source is a bytes, then we're managing the encoding and we need
231
+ # to deal with it.
232
+ else:
233
+ bpayload = msg.get_payload(decode=True)
234
+ assert isinstance(bpayload, bytes)
235
+ try:
236
+ return bpayload.decode("utf8", "strict")
237
+ except UnicodeDecodeError as exc:
238
+ raise ValueError("payload in an invalid encoding") from exc
239
+
240
+
241
+ # The various parse_FORMAT functions here are intended to be as lenient as
242
+ # possible in their parsing, while still returning a correctly typed
243
+ # RawMetadata.
244
+ #
245
+ # To aid in this, we also generally want to do as little touching of the
246
+ # data as possible, except where there are possibly some historic holdovers
247
+ # that make valid data awkward to work with.
248
+ #
249
+ # While this is a lower level, intermediate format than our ``Metadata``
250
+ # class, some light touch ups can make a massive difference in usability.
251
+
252
+ # Map METADATA fields to RawMetadata.
253
+ _EMAIL_TO_RAW_MAPPING = {
254
+ "author": "author",
255
+ "author-email": "author_email",
256
+ "classifier": "classifiers",
257
+ "description": "description",
258
+ "description-content-type": "description_content_type",
259
+ "download-url": "download_url",
260
+ "dynamic": "dynamic",
261
+ "home-page": "home_page",
262
+ "import-name": "import_names",
263
+ "import-namespace": "import_namespaces",
264
+ "keywords": "keywords",
265
+ "license": "license",
266
+ "license-expression": "license_expression",
267
+ "license-file": "license_files",
268
+ "maintainer": "maintainer",
269
+ "maintainer-email": "maintainer_email",
270
+ "metadata-version": "metadata_version",
271
+ "name": "name",
272
+ "obsoletes": "obsoletes",
273
+ "obsoletes-dist": "obsoletes_dist",
274
+ "platform": "platforms",
275
+ "project-url": "project_urls",
276
+ "provides": "provides",
277
+ "provides-dist": "provides_dist",
278
+ "provides-extra": "provides_extra",
279
+ "requires": "requires",
280
+ "requires-dist": "requires_dist",
281
+ "requires-external": "requires_external",
282
+ "requires-python": "requires_python",
283
+ "summary": "summary",
284
+ "supported-platform": "supported_platforms",
285
+ "version": "version",
286
+ }
287
+ _RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()}
288
+
289
+
290
+ # This class is for writing RFC822 messages
291
+ class RFC822Policy(email.policy.EmailPolicy):
292
+ """
293
+ This is :class:`email.policy.EmailPolicy`, but with a simple ``header_store_parse``
294
+ implementation that handles multi-line values, and some nice defaults.
295
+ """
296
+
297
+ utf8 = True
298
+ mangle_from_ = False
299
+ max_line_length = 0
300
+
301
+ def header_store_parse(self, name: str, value: str) -> tuple[str, str]:
302
+ size = len(name) + 2
303
+ value = value.replace("\n", "\n" + " " * size)
304
+ return (name, value)
305
+
306
+
307
+ # This class is for writing RFC822 messages
308
+ class RFC822Message(email.message.EmailMessage):
309
+ """
310
+ This is :class:`email.message.EmailMessage` with two small changes: it defaults to
311
+ our `RFC822Policy`, and it correctly writes unicode when being called
312
+ with `bytes()`.
313
+ """
314
+
315
+ def __init__(self) -> None:
316
+ super().__init__(policy=RFC822Policy())
317
+
318
+ def as_bytes(
319
+ self, unixfrom: bool = False, policy: email.policy.Policy | None = None
320
+ ) -> bytes:
321
+ """
322
+ Return the bytes representation of the message.
323
+
324
+ This handles unicode encoding.
325
+ """
326
+ return self.as_string(unixfrom, policy=policy).encode("utf-8")
327
+
328
+
329
+ def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]:
330
+ """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``).
331
+
332
+ This function returns a two-item tuple of dicts. The first dict is of
333
+ recognized fields from the core metadata specification. Fields that can be
334
+ parsed and translated into Python's built-in types are converted
335
+ appropriately. All other fields are left as-is. Fields that are allowed to
336
+ appear multiple times are stored as lists.
337
+
338
+ The second dict contains all other fields from the metadata. This includes
339
+ any unrecognized fields. It also includes any fields which are expected to
340
+ be parsed into a built-in type but were not formatted appropriately. Finally,
341
+ any fields that are expected to appear only once but are repeated are
342
+ included in this dict.
343
+
344
+ """
345
+ raw: dict[str, str | list[str] | dict[str, str]] = {}
346
+ unparsed: dict[str, list[str]] = {}
347
+
348
+ if isinstance(data, str):
349
+ parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data)
350
+ else:
351
+ parsed = email.parser.BytesParser(policy=email.policy.compat32).parsebytes(data)
352
+
353
+ # We have to wrap parsed.keys() in a set, because in the case of multiple
354
+ # values for a key (a list), the key will appear multiple times in the
355
+ # list of keys, but we're avoiding that by using get_all().
356
+ for name_with_case in frozenset(parsed.keys()):
357
+ # Header names in RFC are case insensitive, so we'll normalize to all
358
+ # lower case to make comparisons easier.
359
+ name = name_with_case.lower()
360
+
361
+ # We use get_all() here, even for fields that aren't multiple use,
362
+ # because otherwise someone could have e.g. two Name fields, and we
363
+ # would just silently ignore it rather than doing something about it.
364
+ headers = parsed.get_all(name) or []
365
+
366
+ # The way the email module works when parsing bytes is that it
367
+ # unconditionally decodes the bytes as ascii using the surrogateescape
368
+ # handler. When you pull that data back out (such as with get_all() ),
369
+ # it looks to see if the str has any surrogate escapes, and if it does
370
+ # it wraps it in a Header object instead of returning the string.
371
+ #
372
+ # As such, we'll look for those Header objects, and fix up the encoding.
373
+ value = []
374
+ # Flag if we have run into any issues processing the headers, thus
375
+ # signalling that the data belongs in 'unparsed'.
376
+ valid_encoding = True
377
+ for h in headers:
378
+ # It's unclear if this can return more types than just a Header or
379
+ # a str, so we'll just assert here to make sure.
380
+ assert isinstance(h, (email.header.Header, str))
381
+
382
+ # If it's a header object, we need to do our little dance to get
383
+ # the real data out of it. In cases where there is invalid data
384
+ # we're going to end up with mojibake, but there's no obvious, good
385
+ # way around that without reimplementing parts of the Header object
386
+ # ourselves.
387
+ #
388
+ # That should be fine since, if mojibacked happens, this key is
389
+ # going into the unparsed dict anyways.
390
+ if isinstance(h, email.header.Header):
391
+ # The Header object stores it's data as chunks, and each chunk
392
+ # can be independently encoded, so we'll need to check each
393
+ # of them.
394
+ chunks: list[tuple[bytes, str | None]] = []
395
+ for binary, _encoding in email.header.decode_header(h):
396
+ try:
397
+ binary.decode("utf8", "strict")
398
+ except UnicodeDecodeError:
399
+ # Enable mojibake.
400
+ encoding = "latin1"
401
+ valid_encoding = False
402
+ else:
403
+ encoding = "utf8"
404
+ chunks.append((binary, encoding))
405
+
406
+ # Turn our chunks back into a Header object, then let that
407
+ # Header object do the right thing to turn them into a
408
+ # string for us.
409
+ value.append(str(email.header.make_header(chunks)))
410
+ # This is already a string, so just add it.
411
+ else:
412
+ value.append(h)
413
+
414
+ # We've processed all of our values to get them into a list of str,
415
+ # but we may have mojibake data, in which case this is an unparsed
416
+ # field.
417
+ if not valid_encoding:
418
+ unparsed[name] = value
419
+ continue
420
+
421
+ raw_name = _EMAIL_TO_RAW_MAPPING.get(name)
422
+ if raw_name is None:
423
+ # This is a bit of a weird situation, we've encountered a key that
424
+ # we don't know what it means, so we don't know whether it's meant
425
+ # to be a list or not.
426
+ #
427
+ # Since we can't really tell one way or another, we'll just leave it
428
+ # as a list, even though it may be a single item list, because that's
429
+ # what makes the most sense for email headers.
430
+ unparsed[name] = value
431
+ continue
432
+
433
+ # If this is one of our string fields, then we'll check to see if our
434
+ # value is a list of a single item. If it is then we'll assume that
435
+ # it was emitted as a single string, and unwrap the str from inside
436
+ # the list.
437
+ #
438
+ # If it's any other kind of data, then we haven't the faintest clue
439
+ # what we should parse it as, and we have to just add it to our list
440
+ # of unparsed stuff.
441
+ if raw_name in _STRING_FIELDS and len(value) == 1:
442
+ raw[raw_name] = value[0]
443
+ # If this is import_names, we need to special case the empty field
444
+ # case, which converts to an empty list instead of None. We can't let
445
+ # the empty case slip through, as it will fail validation.
446
+ elif raw_name == "import_names" and value == [""]:
447
+ raw[raw_name] = []
448
+ # If this is one of our list of string fields, then we can just assign
449
+ # the value, since email *only* has strings, and our get_all() call
450
+ # above ensures that this is a list.
451
+ elif raw_name in _LIST_FIELDS:
452
+ raw[raw_name] = value
453
+ # Special Case: Keywords
454
+ # The keywords field is implemented in the metadata spec as a str,
455
+ # but it conceptually is a list of strings, and is serialized using
456
+ # ", ".join(keywords), so we'll do some light data massaging to turn
457
+ # this into what it logically is.
458
+ elif raw_name == "keywords" and len(value) == 1:
459
+ raw[raw_name] = _parse_keywords(value[0])
460
+ # Special Case: Project-URL
461
+ # The project urls is implemented in the metadata spec as a list of
462
+ # specially-formatted strings that represent a key and a value, which
463
+ # is fundamentally a mapping, however the email format doesn't support
464
+ # mappings in a sane way, so it was crammed into a list of strings
465
+ # instead.
466
+ #
467
+ # We will do a little light data massaging to turn this into a map as
468
+ # it logically should be.
469
+ elif raw_name == "project_urls":
470
+ try:
471
+ raw[raw_name] = _parse_project_urls(value)
472
+ except KeyError:
473
+ unparsed[name] = value
474
+ # Nothing that we've done has managed to parse this, so it'll just
475
+ # throw it in our unparsable data and move on.
476
+ else:
477
+ unparsed[name] = value
478
+
479
+ # We need to support getting the Description from the message payload in
480
+ # addition to getting it from the the headers. This does mean, though, there
481
+ # is the possibility of it being set both ways, in which case we put both
482
+ # in 'unparsed' since we don't know which is right.
483
+ try:
484
+ payload = _get_payload(parsed, data)
485
+ except ValueError:
486
+ unparsed.setdefault("description", []).append(
487
+ parsed.get_payload(decode=isinstance(data, bytes)) # type: ignore[call-overload]
488
+ )
489
+ else:
490
+ if payload:
491
+ # Check to see if we've already got a description, if so then both
492
+ # it, and this body move to unparsable.
493
+ if "description" in raw:
494
+ description_header = cast("str", raw.pop("description"))
495
+ unparsed.setdefault("description", []).extend(
496
+ [description_header, payload]
497
+ )
498
+ elif "description" in unparsed:
499
+ unparsed["description"].append(payload)
500
+ else:
501
+ raw["description"] = payload
502
+
503
+ # We need to cast our `raw` to a metadata, because a TypedDict only support
504
+ # literal key names, but we're computing our key names on purpose, but the
505
+ # way this function is implemented, our `TypedDict` can only have valid key
506
+ # names.
507
+ return cast("RawMetadata", raw), unparsed
508
+
509
+
510
+ _NOT_FOUND = object()
511
+
512
+
513
+ # Keep the two values in sync.
514
+ _VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"]
515
+ _MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"]
516
+
517
+ _REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"])
518
+
519
+
520
+ class _Validator(Generic[T]):
521
+ """Validate a metadata field.
522
+
523
+ All _process_*() methods correspond to a core metadata field. The method is
524
+ called with the field's raw value. If the raw value is valid it is returned
525
+ in its "enriched" form (e.g. ``version.Version`` for the ``Version`` field).
526
+ If the raw value is invalid, :exc:`InvalidMetadata` is raised (with a cause
527
+ as appropriate).
528
+ """
529
+
530
+ name: str
531
+ raw_name: str
532
+ added: _MetadataVersion
533
+
534
+ def __init__(
535
+ self,
536
+ *,
537
+ added: _MetadataVersion = "1.0",
538
+ ) -> None:
539
+ self.added = added
540
+
541
+ def __set_name__(self, _owner: Metadata, name: str) -> None:
542
+ self.name = name
543
+ self.raw_name = _RAW_TO_EMAIL_MAPPING[name]
544
+
545
+ def __get__(self, instance: Metadata, _owner: type[Metadata]) -> T:
546
+ # With Python 3.8, the caching can be replaced with functools.cached_property().
547
+ # No need to check the cache as attribute lookup will resolve into the
548
+ # instance's __dict__ before __get__ is called.
549
+ cache = instance.__dict__
550
+ value = instance._raw.get(self.name)
551
+
552
+ # To make the _process_* methods easier, we'll check if the value is None
553
+ # and if this field is NOT a required attribute, and if both of those
554
+ # things are true, we'll skip the the converter. This will mean that the
555
+ # converters never have to deal with the None union.
556
+ if self.name in _REQUIRED_ATTRS or value is not None:
557
+ try:
558
+ converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}")
559
+ except AttributeError:
560
+ pass
561
+ else:
562
+ value = converter(value)
563
+
564
+ cache[self.name] = value
565
+ try:
566
+ del instance._raw[self.name] # type: ignore[misc]
567
+ except KeyError:
568
+ pass
569
+
570
+ return cast("T", value)
571
+
572
+ def _invalid_metadata(
573
+ self, msg: str, cause: Exception | None = None
574
+ ) -> InvalidMetadata:
575
+ exc = InvalidMetadata(
576
+ self.raw_name, msg.format_map({"field": repr(self.raw_name)})
577
+ )
578
+ exc.__cause__ = cause
579
+ return exc
580
+
581
+ def _process_metadata_version(self, value: str) -> _MetadataVersion:
582
+ # Implicitly makes Metadata-Version required.
583
+ if value not in _VALID_METADATA_VERSIONS:
584
+ raise self._invalid_metadata(f"{value!r} is not a valid metadata version")
585
+ return cast("_MetadataVersion", value)
586
+
587
+ def _process_name(self, value: str) -> str:
588
+ if not value:
589
+ raise self._invalid_metadata("{field} is a required field")
590
+ # Validate the name as a side-effect.
591
+ try:
592
+ utils.canonicalize_name(value, validate=True)
593
+ except utils.InvalidName as exc:
594
+ raise self._invalid_metadata(
595
+ f"{value!r} is invalid for {{field}}", cause=exc
596
+ ) from exc
597
+ else:
598
+ return value
599
+
600
+ def _process_version(self, value: str) -> version_module.Version:
601
+ if not value:
602
+ raise self._invalid_metadata("{field} is a required field")
603
+ try:
604
+ return version_module.parse(value)
605
+ except version_module.InvalidVersion as exc:
606
+ raise self._invalid_metadata(
607
+ f"{value!r} is invalid for {{field}}", cause=exc
608
+ ) from exc
609
+
610
+ def _process_summary(self, value: str) -> str:
611
+ """Check the field contains no newlines."""
612
+ if "\n" in value:
613
+ raise self._invalid_metadata("{field} must be a single line")
614
+ return value
615
+
616
+ def _process_description_content_type(self, value: str) -> str:
617
+ content_types = {"text/plain", "text/x-rst", "text/markdown"}
618
+ message = email.message.EmailMessage()
619
+ message["content-type"] = value
620
+
621
+ content_type, parameters = (
622
+ # Defaults to `text/plain` if parsing failed.
623
+ message.get_content_type().lower(),
624
+ message["content-type"].params,
625
+ )
626
+ # Check if content-type is valid or defaulted to `text/plain` and thus was
627
+ # not parseable.
628
+ if content_type not in content_types or content_type not in value.lower():
629
+ raise self._invalid_metadata(
630
+ f"{{field}} must be one of {list(content_types)}, not {value!r}"
631
+ )
632
+
633
+ charset = parameters.get("charset", "UTF-8")
634
+ if charset != "UTF-8":
635
+ raise self._invalid_metadata(
636
+ f"{{field}} can only specify the UTF-8 charset, not {charset!r}"
637
+ )
638
+
639
+ markdown_variants = {"GFM", "CommonMark"}
640
+ variant = parameters.get("variant", "GFM") # Use an acceptable default.
641
+ if content_type == "text/markdown" and variant not in markdown_variants:
642
+ raise self._invalid_metadata(
643
+ f"valid Markdown variants for {{field}} are {list(markdown_variants)}, "
644
+ f"not {variant!r}",
645
+ )
646
+ return value
647
+
648
+ def _process_dynamic(self, value: list[str]) -> list[str]:
649
+ for dynamic_field in map(str.lower, value):
650
+ if dynamic_field in {"name", "version", "metadata-version"}:
651
+ raise self._invalid_metadata(
652
+ f"{dynamic_field!r} is not allowed as a dynamic field"
653
+ )
654
+ elif dynamic_field not in _EMAIL_TO_RAW_MAPPING:
655
+ raise self._invalid_metadata(
656
+ f"{dynamic_field!r} is not a valid dynamic field"
657
+ )
658
+ return list(map(str.lower, value))
659
+
660
+ def _process_provides_extra(
661
+ self,
662
+ value: list[str],
663
+ ) -> list[utils.NormalizedName]:
664
+ normalized_names = []
665
+ try:
666
+ for name in value:
667
+ normalized_names.append(utils.canonicalize_name(name, validate=True))
668
+ except utils.InvalidName as exc:
669
+ raise self._invalid_metadata(
670
+ f"{name!r} is invalid for {{field}}", cause=exc
671
+ ) from exc
672
+ else:
673
+ return normalized_names
674
+
675
+ def _process_requires_python(self, value: str) -> specifiers.SpecifierSet:
676
+ try:
677
+ return specifiers.SpecifierSet(value)
678
+ except specifiers.InvalidSpecifier as exc:
679
+ raise self._invalid_metadata(
680
+ f"{value!r} is invalid for {{field}}", cause=exc
681
+ ) from exc
682
+
683
+ def _process_requires_dist(
684
+ self,
685
+ value: list[str],
686
+ ) -> list[requirements.Requirement]:
687
+ reqs = []
688
+ try:
689
+ for req in value:
690
+ reqs.append(requirements.Requirement(req))
691
+ except requirements.InvalidRequirement as exc:
692
+ raise self._invalid_metadata(
693
+ f"{req!r} is invalid for {{field}}", cause=exc
694
+ ) from exc
695
+ else:
696
+ return reqs
697
+
698
+ def _process_license_expression(self, value: str) -> NormalizedLicenseExpression:
699
+ try:
700
+ return licenses.canonicalize_license_expression(value)
701
+ except ValueError as exc:
702
+ raise self._invalid_metadata(
703
+ f"{value!r} is invalid for {{field}}", cause=exc
704
+ ) from exc
705
+
706
+ def _process_license_files(self, value: list[str]) -> list[str]:
707
+ paths = []
708
+ for path in value:
709
+ if ".." in path:
710
+ raise self._invalid_metadata(
711
+ f"{path!r} is invalid for {{field}}, "
712
+ "parent directory indicators are not allowed"
713
+ )
714
+ if "*" in path:
715
+ raise self._invalid_metadata(
716
+ f"{path!r} is invalid for {{field}}, paths must be resolved"
717
+ )
718
+ if (
719
+ pathlib.PurePosixPath(path).is_absolute()
720
+ or pathlib.PureWindowsPath(path).is_absolute()
721
+ ):
722
+ raise self._invalid_metadata(
723
+ f"{path!r} is invalid for {{field}}, paths must be relative"
724
+ )
725
+ if pathlib.PureWindowsPath(path).as_posix() != path:
726
+ raise self._invalid_metadata(
727
+ f"{path!r} is invalid for {{field}}, paths must use '/' delimiter"
728
+ )
729
+ paths.append(path)
730
+ return paths
731
+
732
+ def _process_import_names(self, value: list[str]) -> list[str]:
733
+ for import_name in value:
734
+ name, semicolon, private = import_name.partition(";")
735
+ name = name.rstrip()
736
+ for identifier in name.split("."):
737
+ if not identifier.isidentifier():
738
+ raise self._invalid_metadata(
739
+ f"{name!r} is invalid for {{field}}; "
740
+ f"{identifier!r} is not a valid identifier"
741
+ )
742
+ elif keyword.iskeyword(identifier):
743
+ raise self._invalid_metadata(
744
+ f"{name!r} is invalid for {{field}}; "
745
+ f"{identifier!r} is a keyword"
746
+ )
747
+ if semicolon and private.lstrip() != "private":
748
+ raise self._invalid_metadata(
749
+ f"{import_name!r} is invalid for {{field}}; "
750
+ "the only valid option is 'private'"
751
+ )
752
+ return value
753
+
754
+ _process_import_namespaces = _process_import_names
755
+
756
+
757
+ class Metadata:
758
+ """Representation of distribution metadata.
759
+
760
+ Compared to :class:`RawMetadata`, this class provides objects representing
761
+ metadata fields instead of only using built-in types. Any invalid metadata
762
+ will cause :exc:`InvalidMetadata` to be raised (with a
763
+ :py:attr:`~BaseException.__cause__` attribute as appropriate).
764
+ """
765
+
766
+ _raw: RawMetadata
767
+
768
+ @classmethod
769
+ def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata:
770
+ """Create an instance from :class:`RawMetadata`.
771
+
772
+ If *validate* is true, all metadata will be validated. All exceptions
773
+ related to validation will be gathered and raised as an :class:`ExceptionGroup`.
774
+ """
775
+ ins = cls()
776
+ ins._raw = data.copy() # Mutations occur due to caching enriched values.
777
+
778
+ if validate:
779
+ collector = _ErrorCollector()
780
+ metadata_version = None
781
+ with collector.collect(InvalidMetadata):
782
+ metadata_version = ins.metadata_version
783
+ metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version)
784
+
785
+ # Make sure to check for the fields that are present, the required
786
+ # fields (so their absence can be reported).
787
+ fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS
788
+ # Remove fields that have already been checked.
789
+ fields_to_check -= {"metadata_version"}
790
+
791
+ for key in fields_to_check:
792
+ try:
793
+ if metadata_version:
794
+ # Can't use getattr() as that triggers descriptor protocol which
795
+ # will fail due to no value for the instance argument.
796
+ try:
797
+ field_metadata_version = cls.__dict__[key].added
798
+ except KeyError:
799
+ exc = InvalidMetadata(key, f"unrecognized field: {key!r}")
800
+ collector.error(exc)
801
+ continue
802
+ field_age = _VALID_METADATA_VERSIONS.index(
803
+ field_metadata_version
804
+ )
805
+ if field_age > metadata_age:
806
+ field = _RAW_TO_EMAIL_MAPPING[key]
807
+ exc = InvalidMetadata(
808
+ field,
809
+ f"{field} introduced in metadata version "
810
+ f"{field_metadata_version}, not {metadata_version}",
811
+ )
812
+ collector.error(exc)
813
+ continue
814
+ getattr(ins, key)
815
+ except InvalidMetadata as exc:
816
+ collector.error(exc)
817
+
818
+ collector.finalize("invalid metadata")
819
+
820
+ return ins
821
+
822
+ @classmethod
823
+ def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata:
824
+ """Parse metadata from email headers.
825
+
826
+ If *validate* is true, the metadata will be validated. All exceptions
827
+ related to validation will be gathered and raised as an :class:`ExceptionGroup`.
828
+ """
829
+ raw, unparsed = parse_email(data)
830
+
831
+ if validate:
832
+ with _ErrorCollector().on_exit("unparsed") as collector:
833
+ for unparsed_key in unparsed:
834
+ if unparsed_key in _EMAIL_TO_RAW_MAPPING:
835
+ message = f"{unparsed_key!r} has invalid data"
836
+ else:
837
+ message = f"unrecognized field: {unparsed_key!r}"
838
+ collector.error(InvalidMetadata(unparsed_key, message))
839
+
840
+ try:
841
+ return cls.from_raw(raw, validate=validate)
842
+ except ExceptionGroup as exc_group:
843
+ raise ExceptionGroup(
844
+ "invalid or unparsed metadata", exc_group.exceptions
845
+ ) from None
846
+
847
+ metadata_version: _Validator[_MetadataVersion] = _Validator()
848
+ """:external:ref:`core-metadata-metadata-version`
849
+ (required; validated to be a valid metadata version)"""
850
+ # `name` is not normalized/typed to NormalizedName so as to provide access to
851
+ # the original/raw name.
852
+ name: _Validator[str] = _Validator()
853
+ """:external:ref:`core-metadata-name`
854
+ (required; validated using :func:`~packaging.utils.canonicalize_name` and its
855
+ *validate* parameter)"""
856
+ version: _Validator[version_module.Version] = _Validator()
857
+ """:external:ref:`core-metadata-version` (required)"""
858
+ dynamic: _Validator[list[str] | None] = _Validator(
859
+ added="2.2",
860
+ )
861
+ """:external:ref:`core-metadata-dynamic`
862
+ (validated against core metadata field names and lowercased)"""
863
+ platforms: _Validator[list[str] | None] = _Validator()
864
+ """:external:ref:`core-metadata-platform`"""
865
+ supported_platforms: _Validator[list[str] | None] = _Validator(added="1.1")
866
+ """:external:ref:`core-metadata-supported-platform`"""
867
+ summary: _Validator[str | None] = _Validator()
868
+ """:external:ref:`core-metadata-summary` (validated to contain no newlines)"""
869
+ description: _Validator[str | None] = _Validator() # TODO 2.1: can be in body
870
+ """:external:ref:`core-metadata-description`"""
871
+ description_content_type: _Validator[str | None] = _Validator(added="2.1")
872
+ """:external:ref:`core-metadata-description-content-type` (validated)"""
873
+ keywords: _Validator[list[str] | None] = _Validator()
874
+ """:external:ref:`core-metadata-keywords`"""
875
+ home_page: _Validator[str | None] = _Validator()
876
+ """:external:ref:`core-metadata-home-page`"""
877
+ download_url: _Validator[str | None] = _Validator(added="1.1")
878
+ """:external:ref:`core-metadata-download-url`"""
879
+ author: _Validator[str | None] = _Validator()
880
+ """:external:ref:`core-metadata-author`"""
881
+ author_email: _Validator[str | None] = _Validator()
882
+ """:external:ref:`core-metadata-author-email`"""
883
+ maintainer: _Validator[str | None] = _Validator(added="1.2")
884
+ """:external:ref:`core-metadata-maintainer`"""
885
+ maintainer_email: _Validator[str | None] = _Validator(added="1.2")
886
+ """:external:ref:`core-metadata-maintainer-email`"""
887
+ license: _Validator[str | None] = _Validator()
888
+ """:external:ref:`core-metadata-license`"""
889
+ license_expression: _Validator[NormalizedLicenseExpression | None] = _Validator(
890
+ added="2.4"
891
+ )
892
+ """:external:ref:`core-metadata-license-expression`"""
893
+ license_files: _Validator[list[str] | None] = _Validator(added="2.4")
894
+ """:external:ref:`core-metadata-license-file`"""
895
+ classifiers: _Validator[list[str] | None] = _Validator(added="1.1")
896
+ """:external:ref:`core-metadata-classifier`"""
897
+ requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator(
898
+ added="1.2"
899
+ )
900
+ """:external:ref:`core-metadata-requires-dist`"""
901
+ requires_python: _Validator[specifiers.SpecifierSet | None] = _Validator(
902
+ added="1.2"
903
+ )
904
+ """:external:ref:`core-metadata-requires-python`"""
905
+ # Because `Requires-External` allows for non-PEP 440 version specifiers, we
906
+ # don't do any processing on the values.
907
+ requires_external: _Validator[list[str] | None] = _Validator(added="1.2")
908
+ """:external:ref:`core-metadata-requires-external`"""
909
+ project_urls: _Validator[dict[str, str] | None] = _Validator(added="1.2")
910
+ """:external:ref:`core-metadata-project-url`"""
911
+ # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation
912
+ # regardless of metadata version.
913
+ provides_extra: _Validator[list[utils.NormalizedName] | None] = _Validator(
914
+ added="2.1",
915
+ )
916
+ """:external:ref:`core-metadata-provides-extra`"""
917
+ provides_dist: _Validator[list[str] | None] = _Validator(added="1.2")
918
+ """:external:ref:`core-metadata-provides-dist`"""
919
+ obsoletes_dist: _Validator[list[str] | None] = _Validator(added="1.2")
920
+ """:external:ref:`core-metadata-obsoletes-dist`"""
921
+ import_names: _Validator[list[str] | None] = _Validator(added="2.5")
922
+ """:external:ref:`core-metadata-import-name`"""
923
+ import_namespaces: _Validator[list[str] | None] = _Validator(added="2.5")
924
+ """:external:ref:`core-metadata-import-namespace`"""
925
+ requires: _Validator[list[str] | None] = _Validator(added="1.1")
926
+ """``Requires`` (deprecated)"""
927
+ provides: _Validator[list[str] | None] = _Validator(added="1.1")
928
+ """``Provides`` (deprecated)"""
929
+ obsoletes: _Validator[list[str] | None] = _Validator(added="1.1")
930
+ """``Obsoletes`` (deprecated)"""
931
+
932
+ def as_rfc822(self) -> RFC822Message:
933
+ """
934
+ Return an RFC822 message with the metadata.
935
+ """
936
+ message = RFC822Message()
937
+ self._write_metadata(message)
938
+ return message
939
+
940
+ def _write_metadata(self, message: RFC822Message) -> None:
941
+ """
942
+ Return an RFC822 message with the metadata.
943
+ """
944
+ for name, validator in self.__class__.__dict__.items():
945
+ if isinstance(validator, _Validator) and name != "description":
946
+ value = getattr(self, name)
947
+ email_name = _RAW_TO_EMAIL_MAPPING[name]
948
+ if value is not None:
949
+ if email_name == "project-url":
950
+ for label, url in value.items():
951
+ message[email_name] = f"{label}, {url}"
952
+ elif email_name == "keywords":
953
+ message[email_name] = ",".join(value)
954
+ elif email_name == "import-name" and value == []:
955
+ message[email_name] = ""
956
+ elif isinstance(value, list):
957
+ for item in value:
958
+ message[email_name] = str(item)
959
+ else:
960
+ message[email_name] = str(value)
961
+
962
+ # The description is a special case because it is in the body of the message.
963
+ if self.description is not None:
964
+ message.set_payload(self.description)
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/py.typed ADDED
File without changes
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/pylock.py ADDED
@@ -0,0 +1,905 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import logging
5
+ import re
6
+ from collections.abc import Mapping, Sequence
7
+ from dataclasses import dataclass
8
+ from datetime import datetime
9
+ from typing import (
10
+ TYPE_CHECKING,
11
+ Any,
12
+ Callable,
13
+ Protocol,
14
+ TypeVar,
15
+ cast,
16
+ )
17
+ from urllib.parse import urlparse
18
+
19
+ from .markers import Environment, Marker, default_environment
20
+ from .specifiers import SpecifierSet
21
+ from .tags import create_compatible_tags_selector, sys_tags
22
+ from .utils import (
23
+ NormalizedName,
24
+ is_normalized_name,
25
+ parse_sdist_filename,
26
+ parse_wheel_filename,
27
+ )
28
+ from .version import Version
29
+
30
+ if TYPE_CHECKING: # pragma: no cover
31
+ from collections.abc import Collection, Iterator
32
+ from pathlib import Path
33
+
34
+ from typing_extensions import Self
35
+
36
+ from .tags import Tag
37
+
38
+ _logger = logging.getLogger(__name__)
39
+
40
+ __all__ = [
41
+ "Package",
42
+ "PackageArchive",
43
+ "PackageDirectory",
44
+ "PackageSdist",
45
+ "PackageVcs",
46
+ "PackageWheel",
47
+ "Pylock",
48
+ "PylockUnsupportedVersionError",
49
+ "PylockValidationError",
50
+ "is_valid_pylock_path",
51
+ ]
52
+
53
+
54
+ def __dir__() -> list[str]:
55
+ return __all__
56
+
57
+
58
+ _T = TypeVar("_T")
59
+ _T2 = TypeVar("_T2")
60
+
61
+
62
+ class _FromMappingProtocol(Protocol): # pragma: no cover
63
+ @classmethod
64
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self: ...
65
+
66
+
67
+ _FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol)
68
+
69
+
70
+ _PYLOCK_FILE_NAME_RE = re.compile(r"^pylock\.([^.]+)\.toml$")
71
+
72
+
73
+ def is_valid_pylock_path(path: Path) -> bool:
74
+ """Check if the given path is a valid pylock file path."""
75
+ return path.name == "pylock.toml" or bool(_PYLOCK_FILE_NAME_RE.match(path.name))
76
+
77
+
78
+ def _toml_key(key: str) -> str:
79
+ return key.replace("_", "-")
80
+
81
+
82
+ def _toml_value(key: str, value: Any) -> Any: # noqa: ANN401
83
+ if isinstance(value, (Version, Marker, SpecifierSet)):
84
+ return str(value)
85
+ if isinstance(value, Sequence) and key == "environments":
86
+ return [str(v) for v in value]
87
+ return value
88
+
89
+
90
+ def _toml_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]:
91
+ return {
92
+ _toml_key(key): _toml_value(key, value)
93
+ for key, value in data
94
+ if value is not None
95
+ }
96
+
97
+
98
+ def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None:
99
+ """Get a value from the dictionary and verify it's the expected type."""
100
+ if (value := d.get(key)) is None:
101
+ return None
102
+ if not isinstance(value, expected_type):
103
+ raise PylockValidationError(
104
+ f"Unexpected type {type(value).__name__} "
105
+ f"(expected {expected_type.__name__})",
106
+ context=key,
107
+ )
108
+ return value
109
+
110
+
111
+ def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T:
112
+ """Get a required value from the dictionary and verify it's the expected type."""
113
+ if (value := _get(d, expected_type, key)) is None:
114
+ raise _PylockRequiredKeyError(key)
115
+ return value
116
+
117
+
118
+ def _get_sequence(
119
+ d: Mapping[str, Any], expected_item_type: type[_T], key: str
120
+ ) -> Sequence[_T] | None:
121
+ """Get a list value from the dictionary and verify it's the expected items type."""
122
+ if (value := _get(d, Sequence, key)) is None: # type: ignore[type-abstract]
123
+ return None
124
+ if isinstance(value, (str, bytes)):
125
+ # special case: str and bytes are Sequences, but we want to reject it
126
+ raise PylockValidationError(
127
+ f"Unexpected type {type(value).__name__} (expected Sequence)",
128
+ context=key,
129
+ )
130
+ for i, item in enumerate(value):
131
+ if not isinstance(item, expected_item_type):
132
+ raise PylockValidationError(
133
+ f"Unexpected type {type(item).__name__} "
134
+ f"(expected {expected_item_type.__name__})",
135
+ context=f"{key}[{i}]",
136
+ )
137
+ return value
138
+
139
+
140
+ def _get_as(
141
+ d: Mapping[str, Any],
142
+ expected_type: type[_T],
143
+ target_type: Callable[[_T], _T2],
144
+ key: str,
145
+ ) -> _T2 | None:
146
+ """Get a value from the dictionary, verify it's the expected type,
147
+ and convert to the target type.
148
+
149
+ This assumes the target_type constructor accepts the value.
150
+ """
151
+ if (value := _get(d, expected_type, key)) is None:
152
+ return None
153
+ try:
154
+ return target_type(value)
155
+ except Exception as e:
156
+ raise PylockValidationError(e, context=key) from e
157
+
158
+
159
+ def _get_required_as(
160
+ d: Mapping[str, Any],
161
+ expected_type: type[_T],
162
+ target_type: Callable[[_T], _T2],
163
+ key: str,
164
+ ) -> _T2:
165
+ """Get a required value from the dict, verify it's the expected type,
166
+ and convert to the target type."""
167
+ if (value := _get_as(d, expected_type, target_type, key)) is None:
168
+ raise _PylockRequiredKeyError(key)
169
+ return value
170
+
171
+
172
+ def _get_sequence_as(
173
+ d: Mapping[str, Any],
174
+ expected_item_type: type[_T],
175
+ target_item_type: Callable[[_T], _T2],
176
+ key: str,
177
+ ) -> list[_T2] | None:
178
+ """Get list value from dictionary and verify expected items type."""
179
+ if (value := _get_sequence(d, expected_item_type, key)) is None:
180
+ return None
181
+ result = []
182
+ try:
183
+ for item in value:
184
+ typed_item = target_item_type(item)
185
+ result.append(typed_item)
186
+ except Exception as e:
187
+ raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e
188
+ return result
189
+
190
+
191
+ def _get_object(
192
+ d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str
193
+ ) -> _FromMappingProtocolT | None:
194
+ """Get a dictionary value from the dictionary and convert it to a dataclass."""
195
+ if (value := _get(d, Mapping, key)) is None: # type: ignore[type-abstract]
196
+ return None
197
+ try:
198
+ return target_type._from_dict(value)
199
+ except Exception as e:
200
+ raise PylockValidationError(e, context=key) from e
201
+
202
+
203
+ def _get_sequence_of_objects(
204
+ d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str
205
+ ) -> list[_FromMappingProtocolT] | None:
206
+ """Get a list value from the dictionary and convert its items to a dataclass."""
207
+ if (value := _get_sequence(d, Mapping, key)) is None: # type: ignore[type-abstract]
208
+ return None
209
+ result: list[_FromMappingProtocolT] = []
210
+ try:
211
+ for item in value:
212
+ typed_item = target_item_type._from_dict(item)
213
+ result.append(typed_item)
214
+ except Exception as e:
215
+ raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e
216
+ return result
217
+
218
+
219
+ def _get_required_sequence_of_objects(
220
+ d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str
221
+ ) -> Sequence[_FromMappingProtocolT]:
222
+ """Get a required list value from the dictionary and convert its items to a
223
+ dataclass."""
224
+ if (result := _get_sequence_of_objects(d, target_item_type, key)) is None:
225
+ raise _PylockRequiredKeyError(key)
226
+ return result
227
+
228
+
229
+ def _validate_normalized_name(name: str) -> NormalizedName:
230
+ """Validate that a string is a NormalizedName."""
231
+ if not is_normalized_name(name):
232
+ raise PylockValidationError(f"Name {name!r} is not normalized")
233
+ return NormalizedName(name)
234
+
235
+
236
+ def _validate_path_url(path: str | None, url: str | None) -> None:
237
+ if not path and not url:
238
+ raise PylockValidationError("path or url must be provided")
239
+
240
+
241
+ def _path_name(path: str | None) -> str | None:
242
+ if not path:
243
+ return None
244
+ # If the path is relative it MAY use POSIX-style path separators explicitly
245
+ # for portability
246
+ if "/" in path:
247
+ return path.rsplit("/", 1)[-1]
248
+ elif "\\" in path:
249
+ return path.rsplit("\\", 1)[-1]
250
+ else:
251
+ return path
252
+
253
+
254
+ def _url_name(url: str | None) -> str | None:
255
+ if not url:
256
+ return None
257
+ url_path = urlparse(url).path
258
+ return url_path.rsplit("/", 1)[-1]
259
+
260
+
261
+ def _validate_hashes(hashes: Mapping[str, Any]) -> Mapping[str, Any]:
262
+ if not hashes:
263
+ raise PylockValidationError("At least one hash must be provided")
264
+ if not all(isinstance(hash_val, str) for hash_val in hashes.values()):
265
+ raise PylockValidationError("Hash values must be strings")
266
+ return hashes
267
+
268
+
269
+ class PylockValidationError(Exception):
270
+ """Raised when when input data is not spec-compliant."""
271
+
272
+ context: str | None = None
273
+ message: str
274
+
275
+ def __init__(
276
+ self,
277
+ cause: str | Exception,
278
+ *,
279
+ context: str | None = None,
280
+ ) -> None:
281
+ if isinstance(cause, PylockValidationError):
282
+ if cause.context:
283
+ self.context = (
284
+ f"{context}.{cause.context}" if context else cause.context
285
+ )
286
+ else:
287
+ self.context = context
288
+ self.message = cause.message
289
+ else:
290
+ self.context = context
291
+ self.message = str(cause)
292
+
293
+ def __str__(self) -> str:
294
+ if self.context:
295
+ return f"{self.message} in {self.context!r}"
296
+ return self.message
297
+
298
+
299
+ class _PylockRequiredKeyError(PylockValidationError):
300
+ def __init__(self, key: str) -> None:
301
+ super().__init__("Missing required value", context=key)
302
+
303
+
304
+ class PylockUnsupportedVersionError(PylockValidationError):
305
+ """Raised when encountering an unsupported `lock_version`."""
306
+
307
+
308
+ class PylockSelectError(Exception):
309
+ """Base exception for errors raised by :meth:`Pylock.select`."""
310
+
311
+
312
+ @dataclass(frozen=True, init=False)
313
+ class PackageVcs:
314
+ type: str
315
+ url: str | None = None
316
+ path: str | None = None
317
+ requested_revision: str | None = None
318
+ commit_id: str # type: ignore[misc]
319
+ subdirectory: str | None = None
320
+
321
+ def __init__(
322
+ self,
323
+ *,
324
+ type: str,
325
+ url: str | None = None,
326
+ path: str | None = None,
327
+ requested_revision: str | None = None,
328
+ commit_id: str,
329
+ subdirectory: str | None = None,
330
+ ) -> None:
331
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
332
+ object.__setattr__(self, "type", type)
333
+ object.__setattr__(self, "url", url)
334
+ object.__setattr__(self, "path", path)
335
+ object.__setattr__(self, "requested_revision", requested_revision)
336
+ object.__setattr__(self, "commit_id", commit_id)
337
+ object.__setattr__(self, "subdirectory", subdirectory)
338
+
339
+ @classmethod
340
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
341
+ package_vcs = cls(
342
+ type=_get_required(d, str, "type"),
343
+ url=_get(d, str, "url"),
344
+ path=_get(d, str, "path"),
345
+ requested_revision=_get(d, str, "requested-revision"),
346
+ commit_id=_get_required(d, str, "commit-id"),
347
+ subdirectory=_get(d, str, "subdirectory"),
348
+ )
349
+ _validate_path_url(package_vcs.path, package_vcs.url)
350
+ return package_vcs
351
+
352
+
353
+ @dataclass(frozen=True, init=False)
354
+ class PackageDirectory:
355
+ path: str
356
+ editable: bool | None = None
357
+ subdirectory: str | None = None
358
+
359
+ def __init__(
360
+ self,
361
+ *,
362
+ path: str,
363
+ editable: bool | None = None,
364
+ subdirectory: str | None = None,
365
+ ) -> None:
366
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
367
+ object.__setattr__(self, "path", path)
368
+ object.__setattr__(self, "editable", editable)
369
+ object.__setattr__(self, "subdirectory", subdirectory)
370
+
371
+ @classmethod
372
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
373
+ return cls(
374
+ path=_get_required(d, str, "path"),
375
+ editable=_get(d, bool, "editable"),
376
+ subdirectory=_get(d, str, "subdirectory"),
377
+ )
378
+
379
+
380
+ @dataclass(frozen=True, init=False)
381
+ class PackageArchive:
382
+ url: str | None = None
383
+ path: str | None = None
384
+ size: int | None = None
385
+ upload_time: datetime | None = None
386
+ hashes: Mapping[str, str] # type: ignore[misc]
387
+ subdirectory: str | None = None
388
+
389
+ def __init__(
390
+ self,
391
+ *,
392
+ url: str | None = None,
393
+ path: str | None = None,
394
+ size: int | None = None,
395
+ upload_time: datetime | None = None,
396
+ hashes: Mapping[str, str],
397
+ subdirectory: str | None = None,
398
+ ) -> None:
399
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
400
+ object.__setattr__(self, "url", url)
401
+ object.__setattr__(self, "path", path)
402
+ object.__setattr__(self, "size", size)
403
+ object.__setattr__(self, "upload_time", upload_time)
404
+ object.__setattr__(self, "hashes", hashes)
405
+ object.__setattr__(self, "subdirectory", subdirectory)
406
+
407
+ @classmethod
408
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
409
+ package_archive = cls(
410
+ url=_get(d, str, "url"),
411
+ path=_get(d, str, "path"),
412
+ size=_get(d, int, "size"),
413
+ upload_time=_get(d, datetime, "upload-time"),
414
+ hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract]
415
+ subdirectory=_get(d, str, "subdirectory"),
416
+ )
417
+ _validate_path_url(package_archive.path, package_archive.url)
418
+ return package_archive
419
+
420
+
421
+ @dataclass(frozen=True, init=False)
422
+ class PackageSdist:
423
+ name: str | None = None
424
+ upload_time: datetime | None = None
425
+ url: str | None = None
426
+ path: str | None = None
427
+ size: int | None = None
428
+ hashes: Mapping[str, str] # type: ignore[misc]
429
+
430
+ def __init__(
431
+ self,
432
+ *,
433
+ name: str | None = None,
434
+ upload_time: datetime | None = None,
435
+ url: str | None = None,
436
+ path: str | None = None,
437
+ size: int | None = None,
438
+ hashes: Mapping[str, str],
439
+ ) -> None:
440
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
441
+ object.__setattr__(self, "name", name)
442
+ object.__setattr__(self, "upload_time", upload_time)
443
+ object.__setattr__(self, "url", url)
444
+ object.__setattr__(self, "path", path)
445
+ object.__setattr__(self, "size", size)
446
+ object.__setattr__(self, "hashes", hashes)
447
+
448
+ @classmethod
449
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
450
+ package_sdist = cls(
451
+ name=_get(d, str, "name"),
452
+ upload_time=_get(d, datetime, "upload-time"),
453
+ url=_get(d, str, "url"),
454
+ path=_get(d, str, "path"),
455
+ size=_get(d, int, "size"),
456
+ hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract]
457
+ )
458
+ _validate_path_url(package_sdist.path, package_sdist.url)
459
+ return package_sdist
460
+
461
+ @property
462
+ def filename(self) -> str:
463
+ """Get the filename of the sdist."""
464
+ filename = self.name or _path_name(self.path) or _url_name(self.url)
465
+ if not filename:
466
+ raise PylockValidationError("Cannot determine sdist filename")
467
+ return filename
468
+
469
+
470
+ @dataclass(frozen=True, init=False)
471
+ class PackageWheel:
472
+ name: str | None = None
473
+ upload_time: datetime | None = None
474
+ url: str | None = None
475
+ path: str | None = None
476
+ size: int | None = None
477
+ hashes: Mapping[str, str] # type: ignore[misc]
478
+
479
+ def __init__(
480
+ self,
481
+ *,
482
+ name: str | None = None,
483
+ upload_time: datetime | None = None,
484
+ url: str | None = None,
485
+ path: str | None = None,
486
+ size: int | None = None,
487
+ hashes: Mapping[str, str],
488
+ ) -> None:
489
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
490
+ object.__setattr__(self, "name", name)
491
+ object.__setattr__(self, "upload_time", upload_time)
492
+ object.__setattr__(self, "url", url)
493
+ object.__setattr__(self, "path", path)
494
+ object.__setattr__(self, "size", size)
495
+ object.__setattr__(self, "hashes", hashes)
496
+
497
+ @classmethod
498
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
499
+ package_wheel = cls(
500
+ name=_get(d, str, "name"),
501
+ upload_time=_get(d, datetime, "upload-time"),
502
+ url=_get(d, str, "url"),
503
+ path=_get(d, str, "path"),
504
+ size=_get(d, int, "size"),
505
+ hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract]
506
+ )
507
+ _validate_path_url(package_wheel.path, package_wheel.url)
508
+ return package_wheel
509
+
510
+ @property
511
+ def filename(self) -> str:
512
+ """Get the filename of the wheel."""
513
+ filename = self.name or _path_name(self.path) or _url_name(self.url)
514
+ if not filename:
515
+ raise PylockValidationError("Cannot determine wheel filename")
516
+ return filename
517
+
518
+
519
+ @dataclass(frozen=True, init=False)
520
+ class Package:
521
+ name: NormalizedName
522
+ version: Version | None = None
523
+ marker: Marker | None = None
524
+ requires_python: SpecifierSet | None = None
525
+ dependencies: Sequence[Mapping[str, Any]] | None = None
526
+ vcs: PackageVcs | None = None
527
+ directory: PackageDirectory | None = None
528
+ archive: PackageArchive | None = None
529
+ index: str | None = None
530
+ sdist: PackageSdist | None = None
531
+ wheels: Sequence[PackageWheel] | None = None
532
+ attestation_identities: Sequence[Mapping[str, Any]] | None = None
533
+ tool: Mapping[str, Any] | None = None
534
+
535
+ def __init__(
536
+ self,
537
+ *,
538
+ name: NormalizedName,
539
+ version: Version | None = None,
540
+ marker: Marker | None = None,
541
+ requires_python: SpecifierSet | None = None,
542
+ dependencies: Sequence[Mapping[str, Any]] | None = None,
543
+ vcs: PackageVcs | None = None,
544
+ directory: PackageDirectory | None = None,
545
+ archive: PackageArchive | None = None,
546
+ index: str | None = None,
547
+ sdist: PackageSdist | None = None,
548
+ wheels: Sequence[PackageWheel] | None = None,
549
+ attestation_identities: Sequence[Mapping[str, Any]] | None = None,
550
+ tool: Mapping[str, Any] | None = None,
551
+ ) -> None:
552
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
553
+ object.__setattr__(self, "name", name)
554
+ object.__setattr__(self, "version", version)
555
+ object.__setattr__(self, "marker", marker)
556
+ object.__setattr__(self, "requires_python", requires_python)
557
+ object.__setattr__(self, "dependencies", dependencies)
558
+ object.__setattr__(self, "vcs", vcs)
559
+ object.__setattr__(self, "directory", directory)
560
+ object.__setattr__(self, "archive", archive)
561
+ object.__setattr__(self, "index", index)
562
+ object.__setattr__(self, "sdist", sdist)
563
+ object.__setattr__(self, "wheels", wheels)
564
+ object.__setattr__(self, "attestation_identities", attestation_identities)
565
+ object.__setattr__(self, "tool", tool)
566
+
567
+ @classmethod
568
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
569
+ package = cls(
570
+ name=_get_required_as(d, str, _validate_normalized_name, "name"),
571
+ version=_get_as(d, str, Version, "version"),
572
+ requires_python=_get_as(d, str, SpecifierSet, "requires-python"),
573
+ dependencies=_get_sequence(d, Mapping, "dependencies"), # type: ignore[type-abstract]
574
+ marker=_get_as(d, str, Marker, "marker"),
575
+ vcs=_get_object(d, PackageVcs, "vcs"),
576
+ directory=_get_object(d, PackageDirectory, "directory"),
577
+ archive=_get_object(d, PackageArchive, "archive"),
578
+ index=_get(d, str, "index"),
579
+ sdist=_get_object(d, PackageSdist, "sdist"),
580
+ wheels=_get_sequence_of_objects(d, PackageWheel, "wheels"),
581
+ attestation_identities=_get_sequence(d, Mapping, "attestation-identities"), # type: ignore[type-abstract]
582
+ tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract]
583
+ )
584
+ distributions = bool(package.sdist) + len(package.wheels or [])
585
+ direct_urls = (
586
+ bool(package.vcs) + bool(package.directory) + bool(package.archive)
587
+ )
588
+ if distributions > 0 and direct_urls > 0:
589
+ raise PylockValidationError(
590
+ "None of vcs, directory, archive must be set if sdist or wheels are set"
591
+ )
592
+ if distributions == 0 and direct_urls != 1:
593
+ raise PylockValidationError(
594
+ "Exactly one of vcs, directory, archive must be set "
595
+ "if sdist and wheels are not set"
596
+ )
597
+ for i, wheel in enumerate(package.wheels or []):
598
+ try:
599
+ (name, version, _, _) = parse_wheel_filename(wheel.filename)
600
+ except Exception as e:
601
+ raise PylockValidationError(
602
+ f"Invalid wheel filename {wheel.filename!r}",
603
+ context=f"wheels[{i}]",
604
+ ) from e
605
+ if name != package.name:
606
+ raise PylockValidationError(
607
+ f"Name in {wheel.filename!r} is not consistent with "
608
+ f"package name {package.name!r}",
609
+ context=f"wheels[{i}]",
610
+ )
611
+ if package.version and version != package.version:
612
+ raise PylockValidationError(
613
+ f"Version in {wheel.filename!r} is not consistent with "
614
+ f"package version {str(package.version)!r}",
615
+ context=f"wheels[{i}]",
616
+ )
617
+ if package.sdist:
618
+ try:
619
+ name, version = parse_sdist_filename(package.sdist.filename)
620
+ except Exception as e:
621
+ raise PylockValidationError(
622
+ f"Invalid sdist filename {package.sdist.filename!r}",
623
+ context="sdist",
624
+ ) from e
625
+ if name != package.name:
626
+ raise PylockValidationError(
627
+ f"Name in {package.sdist.filename!r} is not consistent with "
628
+ f"package name {package.name!r}",
629
+ context="sdist",
630
+ )
631
+ if package.version and version != package.version:
632
+ raise PylockValidationError(
633
+ f"Version in {package.sdist.filename!r} is not consistent with "
634
+ f"package version {str(package.version)!r}",
635
+ context="sdist",
636
+ )
637
+ try:
638
+ for i, attestation_identity in enumerate( # noqa: B007
639
+ package.attestation_identities or []
640
+ ):
641
+ _get_required(attestation_identity, str, "kind")
642
+ except Exception as e:
643
+ raise PylockValidationError(
644
+ e, context=f"attestation-identities[{i}]"
645
+ ) from e
646
+ return package
647
+
648
+ @property
649
+ def is_direct(self) -> bool:
650
+ return not (self.sdist or self.wheels)
651
+
652
+
653
+ @dataclass(frozen=True, init=False)
654
+ class Pylock:
655
+ """A class representing a pylock file."""
656
+
657
+ lock_version: Version
658
+ environments: Sequence[Marker] | None = None
659
+ requires_python: SpecifierSet | None = None
660
+ extras: Sequence[NormalizedName] | None = None
661
+ dependency_groups: Sequence[str] | None = None
662
+ default_groups: Sequence[str] | None = None
663
+ created_by: str # type: ignore[misc]
664
+ packages: Sequence[Package] # type: ignore[misc]
665
+ tool: Mapping[str, Any] | None = None
666
+
667
+ def __init__(
668
+ self,
669
+ *,
670
+ lock_version: Version,
671
+ environments: Sequence[Marker] | None = None,
672
+ requires_python: SpecifierSet | None = None,
673
+ extras: Sequence[NormalizedName] | None = None,
674
+ dependency_groups: Sequence[str] | None = None,
675
+ default_groups: Sequence[str] | None = None,
676
+ created_by: str,
677
+ packages: Sequence[Package],
678
+ tool: Mapping[str, Any] | None = None,
679
+ ) -> None:
680
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
681
+ object.__setattr__(self, "lock_version", lock_version)
682
+ object.__setattr__(self, "environments", environments)
683
+ object.__setattr__(self, "requires_python", requires_python)
684
+ object.__setattr__(self, "extras", extras)
685
+ object.__setattr__(self, "dependency_groups", dependency_groups)
686
+ object.__setattr__(self, "default_groups", default_groups)
687
+ object.__setattr__(self, "created_by", created_by)
688
+ object.__setattr__(self, "packages", packages)
689
+ object.__setattr__(self, "tool", tool)
690
+
691
+ @classmethod
692
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
693
+ pylock = cls(
694
+ lock_version=_get_required_as(d, str, Version, "lock-version"),
695
+ environments=_get_sequence_as(d, str, Marker, "environments"),
696
+ extras=_get_sequence_as(d, str, _validate_normalized_name, "extras"),
697
+ dependency_groups=_get_sequence(d, str, "dependency-groups"),
698
+ default_groups=_get_sequence(d, str, "default-groups"),
699
+ created_by=_get_required(d, str, "created-by"),
700
+ requires_python=_get_as(d, str, SpecifierSet, "requires-python"),
701
+ packages=_get_required_sequence_of_objects(d, Package, "packages"),
702
+ tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract]
703
+ )
704
+ if not Version("1") <= pylock.lock_version < Version("2"):
705
+ raise PylockUnsupportedVersionError(
706
+ f"pylock version {pylock.lock_version} is not supported"
707
+ )
708
+ if pylock.lock_version > Version("1.0"):
709
+ _logger.warning(
710
+ "pylock minor version %s is not supported", pylock.lock_version
711
+ )
712
+ return pylock
713
+
714
+ @classmethod
715
+ def from_dict(cls, d: Mapping[str, Any], /) -> Self:
716
+ """Create and validate a Pylock instance from a TOML dictionary.
717
+
718
+ Raises :class:`PylockValidationError` if the input data is not
719
+ spec-compliant.
720
+ """
721
+ return cls._from_dict(d)
722
+
723
+ def to_dict(self) -> Mapping[str, Any]:
724
+ """Convert the Pylock instance to a TOML dictionary."""
725
+ return dataclasses.asdict(self, dict_factory=_toml_dict_factory)
726
+
727
+ def validate(self) -> None:
728
+ """Validate the Pylock instance against the specification.
729
+
730
+ Raises :class:`PylockValidationError` otherwise."""
731
+ self.from_dict(self.to_dict())
732
+
733
+ def select(
734
+ self,
735
+ *,
736
+ environment: Environment | None = None,
737
+ tags: Sequence[Tag] | None = None,
738
+ extras: Collection[str] | None = None,
739
+ dependency_groups: Collection[str] | None = None,
740
+ ) -> Iterator[
741
+ tuple[
742
+ Package,
743
+ PackageVcs
744
+ | PackageDirectory
745
+ | PackageArchive
746
+ | PackageWheel
747
+ | PackageSdist,
748
+ ]
749
+ ]:
750
+ """Select what to install from the lock file.
751
+
752
+ The *environment* and *tags* parameters represent the environment being
753
+ selected for. If unspecified, ``packaging.markers.default_environment()`` and
754
+ ``packaging.tags.sys_tags()`` are used.
755
+
756
+ The *extras* parameter represents the extras to install.
757
+
758
+ The *dependency_groups* parameter represents the groups to install. If
759
+ unspecified, the default groups are used.
760
+
761
+ This method must be used on valid Pylock instances (i.e. one obtained
762
+ from :meth:`Pylock.from_dict` or if constructed manually, after calling
763
+ :meth:`Pylock.validate`).
764
+ """
765
+ compatible_tags_selector = create_compatible_tags_selector(tags or sys_tags())
766
+
767
+ # #. Gather the extras and dependency groups to install and set ``extras`` and
768
+ # ``dependency_groups`` for marker evaluation, respectively.
769
+ #
770
+ # #. ``extras`` SHOULD be set to the empty set by default.
771
+ # #. ``dependency_groups`` SHOULD be the set created from
772
+ # :ref:`pylock-default-groups` by default.
773
+ env = cast(
774
+ "dict[str, str | frozenset[str]]",
775
+ dict(
776
+ environment or {}, # Marker.evaluate will fill-up
777
+ extras=frozenset(extras or []),
778
+ dependency_groups=frozenset(
779
+ (self.default_groups or [])
780
+ if dependency_groups is None # to allow selecting no group
781
+ else dependency_groups
782
+ ),
783
+ ),
784
+ )
785
+ env_python_full_version = (
786
+ environment["python_full_version"]
787
+ if environment
788
+ else default_environment()["python_full_version"]
789
+ )
790
+
791
+ # #. Check if the metadata version specified by :ref:`pylock-lock-version` is
792
+ # supported; an error or warning MUST be raised as appropriate.
793
+ # Covered by lock.validate() which is a precondition for this method.
794
+
795
+ # #. If :ref:`pylock-requires-python` is specified, check that the environment
796
+ # being installed for meets the requirement; an error MUST be raised if it is
797
+ # not met.
798
+ if self.requires_python and not self.requires_python.contains(
799
+ env_python_full_version,
800
+ ):
801
+ raise PylockSelectError(
802
+ f"python_full_version {env_python_full_version!r} "
803
+ f"in provided environment does not satisfy the Python version "
804
+ f"requirement {str(self.requires_python)!r}"
805
+ )
806
+
807
+ # #. If :ref:`pylock-environments` is specified, check that at least one of the
808
+ # environment marker expressions is satisfied; an error MUST be raised if no
809
+ # expression is satisfied.
810
+ if self.environments:
811
+ for env_marker in self.environments:
812
+ if env_marker.evaluate(
813
+ cast("dict[str, str]", environment or {}), context="requirement"
814
+ ):
815
+ break
816
+ else:
817
+ raise PylockSelectError(
818
+ "Provided environment does not satisfy any of the "
819
+ "environments specified in the lock file"
820
+ )
821
+
822
+ # #. For each package listed in :ref:`pylock-packages`:
823
+ selected_packages_by_name: dict[str, tuple[int, Package]] = {}
824
+ for package_index, package in enumerate(self.packages):
825
+ # #. If :ref:`pylock-packages-marker` is specified, check if it is
826
+ # satisfied;if it isn't, skip to the next package.
827
+ if package.marker and not package.marker.evaluate(env, context="lock_file"):
828
+ continue
829
+
830
+ # #. If :ref:`pylock-packages-requires-python` is specified, check if it is
831
+ # satisfied; an error MUST be raised if it isn't.
832
+ if package.requires_python and not package.requires_python.contains(
833
+ env_python_full_version,
834
+ ):
835
+ raise PylockSelectError(
836
+ f"python_full_version {env_python_full_version!r} "
837
+ f"in provided environment does not satisfy the Python version "
838
+ f"requirement {str(package.requires_python)!r} for package "
839
+ f"{package.name!r} at packages[{package_index}]"
840
+ )
841
+
842
+ # #. Check that no other conflicting instance of the package has been slated
843
+ # to be installed; an error about the ambiguity MUST be raised otherwise.
844
+ if package.name in selected_packages_by_name:
845
+ raise PylockSelectError(
846
+ f"Multiple packages with the name {package.name!r} are "
847
+ f"selected at packages[{package_index}] and "
848
+ f"packages[{selected_packages_by_name[package.name][0]}]"
849
+ )
850
+
851
+ # #. Check that the source of the package is specified appropriately (i.e.
852
+ # there are no conflicting sources in the package entry);
853
+ # an error MUST be raised if any issues are found.
854
+ # Covered by lock.validate() which is a precondition for this method.
855
+
856
+ # #. Add the package to the set of packages to install.
857
+ selected_packages_by_name[package.name] = (package_index, package)
858
+
859
+ # #. For each package to be installed:
860
+ for package_index, package in selected_packages_by_name.values():
861
+ # - If :ref:`pylock-packages-vcs` is set:
862
+ if package.vcs is not None:
863
+ yield package, package.vcs
864
+
865
+ # - Else if :ref:`pylock-packages-directory` is set:
866
+ elif package.directory is not None:
867
+ yield package, package.directory
868
+
869
+ # - Else if :ref:`pylock-packages-archive` is set:
870
+ elif package.archive is not None:
871
+ yield package, package.archive
872
+
873
+ # - Else if there are entries for :ref:`pylock-packages-wheels`:
874
+ elif package.wheels:
875
+ # #. Look for the appropriate wheel file based on
876
+ # :ref:`pylock-packages-wheels-name`; if one is not found then move
877
+ # on to :ref:`pylock-packages-sdist` or an error MUST be raised about
878
+ # a lack of source for the project.
879
+ best_wheel = next(
880
+ compatible_tags_selector(
881
+ (wheel, parse_wheel_filename(wheel.filename)[-1])
882
+ for wheel in package.wheels
883
+ ),
884
+ None,
885
+ )
886
+ if best_wheel:
887
+ yield package, best_wheel
888
+ elif package.sdist is not None:
889
+ yield package, package.sdist
890
+ else:
891
+ raise PylockSelectError(
892
+ f"No wheel found matching the provided tags "
893
+ f"for package {package.name!r} "
894
+ f"at packages[{package_index}], "
895
+ f"and no sdist available as a fallback"
896
+ )
897
+
898
+ # - Else if no :ref:`pylock-packages-wheels` file is found or
899
+ # :ref:`pylock-packages-sdist` is solely set:
900
+ elif package.sdist is not None:
901
+ yield package, package.sdist
902
+
903
+ else:
904
+ # Covered by lock.validate() which is a precondition for this method.
905
+ raise NotImplementedError # pragma: no cover
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/requirements.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file is dual licensed under the terms of the Apache License, Version
2
+ # 2.0, and the BSD License. See the LICENSE file in the root of this repository
3
+ # for complete details.
4
+ from __future__ import annotations
5
+
6
+ from typing import Iterator
7
+
8
+ from ._parser import parse_requirement as _parse_requirement
9
+ from ._tokenizer import ParserSyntaxError
10
+ from .markers import Marker, _normalize_extra_values
11
+ from .specifiers import SpecifierSet
12
+ from .utils import canonicalize_name
13
+
14
+ __all__ = [
15
+ "InvalidRequirement",
16
+ "Requirement",
17
+ ]
18
+
19
+
20
+ def __dir__() -> list[str]:
21
+ return __all__
22
+
23
+
24
+ class InvalidRequirement(ValueError):
25
+ """
26
+ An invalid requirement was found, users should refer to PEP 508.
27
+ """
28
+
29
+
30
+ class Requirement:
31
+ """Parse a requirement.
32
+
33
+ Parse a given requirement string into its parts, such as name, specifier,
34
+ URL, and extras. Raises InvalidRequirement on a badly-formed requirement
35
+ string.
36
+
37
+ Instances are safe to serialize with :mod:`pickle`. They use a stable
38
+ format so the same pickle can be loaded in future packaging releases.
39
+
40
+ .. versionchanged:: 26.2
41
+
42
+ Added a stable pickle format. Pickles created with packaging 26.2+ can
43
+ be unpickled with future releases. Backward compatibility with pickles
44
+ from pip._vendor.packaging < 26.2 is supported but may be removed in a future
45
+ release.
46
+ """
47
+
48
+ # TODO: Can we test whether something is contained within a requirement?
49
+ # If so how do we do that? Do we need to test against the _name_ of
50
+ # the thing as well as the version? What about the markers?
51
+ # TODO: Can we normalize the name and extra name?
52
+
53
+ def __init__(self, requirement_string: str) -> None:
54
+ try:
55
+ parsed = _parse_requirement(requirement_string)
56
+ except ParserSyntaxError as e:
57
+ raise InvalidRequirement(str(e)) from e
58
+
59
+ self.name: str = parsed.name
60
+ self.url: str | None = parsed.url or None
61
+ self.extras: set[str] = set(parsed.extras or [])
62
+ self.specifier: SpecifierSet = SpecifierSet(parsed.specifier)
63
+ self.marker: Marker | None = None
64
+ if parsed.marker is not None:
65
+ self.marker = Marker.__new__(Marker)
66
+ self.marker._markers = _normalize_extra_values(parsed.marker)
67
+
68
+ def _iter_parts(self, name: str) -> Iterator[str]:
69
+ yield name
70
+
71
+ if self.extras:
72
+ formatted_extras = ",".join(sorted(self.extras))
73
+ yield f"[{formatted_extras}]"
74
+
75
+ if self.specifier:
76
+ yield str(self.specifier)
77
+
78
+ if self.url:
79
+ yield f" @ {self.url}"
80
+ if self.marker:
81
+ yield " "
82
+
83
+ if self.marker:
84
+ yield f"; {self.marker}"
85
+
86
+ def __getstate__(self) -> str:
87
+ # Return the requirement string for compactness and stability.
88
+ # Re-parsed on load to reconstruct all fields.
89
+ return str(self)
90
+
91
+ def __setstate__(self, state: object) -> None:
92
+ if isinstance(state, str):
93
+ # New format (26.2+): just the requirement string.
94
+ try:
95
+ tmp = Requirement(state)
96
+ except InvalidRequirement as exc:
97
+ raise TypeError(f"Cannot restore Requirement from {state!r}") from exc
98
+ self.name = tmp.name
99
+ self.url = tmp.url
100
+ self.extras = tmp.extras
101
+ self.specifier = tmp.specifier
102
+ self.marker = tmp.marker
103
+ return
104
+ if isinstance(state, dict):
105
+ # Old format (packaging <= 26.1, no __slots__): plain __dict__.
106
+ self.__dict__.update(state)
107
+ return
108
+ raise TypeError(f"Cannot restore Requirement from {state!r}")
109
+
110
+ def __str__(self) -> str:
111
+ return "".join(self._iter_parts(self.name))
112
+
113
+ def __repr__(self) -> str:
114
+ return f"<{self.__class__.__name__}({str(self)!r})>"
115
+
116
+ def __hash__(self) -> int:
117
+ return hash(tuple(self._iter_parts(canonicalize_name(self.name))))
118
+
119
+ def __eq__(self, other: object) -> bool:
120
+ if not isinstance(other, Requirement):
121
+ return NotImplemented
122
+
123
+ return (
124
+ canonicalize_name(self.name) == canonicalize_name(other.name)
125
+ and self.extras == other.extras
126
+ and self.specifier == other.specifier
127
+ and self.url == other.url
128
+ and self.marker == other.marker
129
+ )
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/specifiers.py ADDED
@@ -0,0 +1,1943 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file is dual licensed under the terms of the Apache License, Version
2
+ # 2.0, and the BSD License. See the LICENSE file in the root of this repository
3
+ # for complete details.
4
+ """
5
+ .. testsetup::
6
+
7
+ from pip._vendor.packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier
8
+ from pip._vendor.packaging.version import Version
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import abc
14
+ import enum
15
+ import functools
16
+ import itertools
17
+ import re
18
+ import sys
19
+ import typing
20
+ from typing import (
21
+ TYPE_CHECKING,
22
+ Any,
23
+ Callable,
24
+ Final,
25
+ Iterable,
26
+ Iterator,
27
+ Sequence,
28
+ TypeVar,
29
+ Union,
30
+ )
31
+
32
+ from .utils import canonicalize_version
33
+ from .version import InvalidVersion, Version
34
+
35
+ if sys.version_info >= (3, 10):
36
+ from typing import TypeGuard # pragma: no cover
37
+ elif TYPE_CHECKING:
38
+ from typing_extensions import TypeGuard
39
+
40
+ __all__ = [
41
+ "BaseSpecifier",
42
+ "InvalidSpecifier",
43
+ "Specifier",
44
+ "SpecifierSet",
45
+ ]
46
+
47
+
48
+ def __dir__() -> list[str]:
49
+ return __all__
50
+
51
+
52
+ def _validate_spec(spec: object, /) -> TypeGuard[tuple[str, str]]:
53
+ return (
54
+ isinstance(spec, tuple)
55
+ and len(spec) == 2
56
+ and isinstance(spec[0], str)
57
+ and isinstance(spec[1], str)
58
+ )
59
+
60
+
61
+ def _validate_pre(pre: object, /) -> TypeGuard[bool | None]:
62
+ return pre is None or isinstance(pre, bool)
63
+
64
+
65
+ T = TypeVar("T")
66
+ UnparsedVersion = Union[Version, str]
67
+ UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion)
68
+ CallableOperator = Callable[[Version, str], bool]
69
+
70
+ # The smallest possible PEP 440 version. No valid version is less than this.
71
+ _MIN_VERSION: Final[Version] = Version("0.dev0")
72
+
73
+
74
+ def _trim_release(release: tuple[int, ...]) -> tuple[int, ...]:
75
+ """Strip trailing zeros from a release tuple for normalized comparison."""
76
+ end = len(release)
77
+ while end > 1 and release[end - 1] == 0:
78
+ end -= 1
79
+ return release if end == len(release) else release[:end]
80
+
81
+
82
+ class _BoundaryKind(enum.Enum):
83
+ """Where a boundary marker sits in the version ordering."""
84
+
85
+ AFTER_LOCALS = enum.auto() # after V+local, before V.post0
86
+ AFTER_POSTS = enum.auto() # after V.postN, before next release
87
+
88
+
89
+ @functools.total_ordering
90
+ class _BoundaryVersion:
91
+ """A point on the version line between two real PEP 440 versions.
92
+
93
+ Some specifier semantics imply boundaries between real versions:
94
+ ``<=1.0`` includes ``1.0+local`` and ``>1.0`` excludes
95
+ ``1.0.post0``. No real :class:`Version` falls on those boundaries,
96
+ so this class creates values that sort between the real versions
97
+ on either side.
98
+
99
+ Two kinds exist, shown relative to a base version V::
100
+
101
+ V < V+local < AFTER_LOCALS(V) < V.post0 < AFTER_POSTS(V)
102
+
103
+ ``AFTER_LOCALS`` sits after V and every V+local, but before
104
+ V.post0. Upper bound of ``<=V``, ``==V``, ``!=V``.
105
+
106
+ ``AFTER_POSTS`` sits after every V.postN, but before the next
107
+ release segment. Lower bound of ``>V`` (final or pre-release V)
108
+ to exclude post-releases per PEP 440.
109
+ """
110
+
111
+ __slots__ = ("_kind", "_trimmed_release", "version")
112
+
113
+ def __init__(self, version: Version, kind: _BoundaryKind) -> None:
114
+ self.version = version
115
+ self._kind = kind
116
+ self._trimmed_release = _trim_release(version.release)
117
+
118
+ def _is_family(self, other: Version) -> bool:
119
+ """Is ``other`` a version that this boundary sorts above?"""
120
+ v = self.version
121
+ if not (
122
+ other.epoch == v.epoch
123
+ and _trim_release(other.release) == self._trimmed_release
124
+ and other.pre == v.pre
125
+ ):
126
+ return False
127
+ if self._kind == _BoundaryKind.AFTER_LOCALS:
128
+ # Local family: exact same public version (any local label).
129
+ return other.post == v.post and other.dev == v.dev
130
+ # Post family: same base + any post-release (or identical).
131
+ return other.dev == v.dev or other.post is not None
132
+
133
+ def __eq__(self, other: object) -> bool:
134
+ if isinstance(other, _BoundaryVersion):
135
+ return self.version == other.version and self._kind == other._kind
136
+ return NotImplemented
137
+
138
+ def __lt__(self, other: _BoundaryVersion | Version) -> bool:
139
+ if isinstance(other, _BoundaryVersion):
140
+ if self.version != other.version:
141
+ return self.version < other.version
142
+ return self._kind.value < other._kind.value
143
+ return not self._is_family(other) and self.version < other
144
+
145
+ def __hash__(self) -> int:
146
+ return hash((self.version, self._kind))
147
+
148
+ def __repr__(self) -> str:
149
+ return f"{self.__class__.__name__}({self.version!r}, {self._kind.name})"
150
+
151
+
152
+ @functools.total_ordering
153
+ class _LowerBound:
154
+ """Lower bound of a version range.
155
+
156
+ A version *v* of ``None`` means unbounded below (-inf).
157
+ At equal versions, ``[v`` sorts before ``(v`` because an inclusive
158
+ bound starts earlier.
159
+ """
160
+
161
+ __slots__ = ("inclusive", "version")
162
+
163
+ def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None:
164
+ self.version = version
165
+ self.inclusive = inclusive
166
+
167
+ def __eq__(self, other: object) -> bool:
168
+ if not isinstance(other, _LowerBound):
169
+ return NotImplemented # pragma: no cover
170
+ return self.version == other.version and self.inclusive == other.inclusive
171
+
172
+ def __lt__(self, other: _LowerBound) -> bool:
173
+ if not isinstance(other, _LowerBound): # pragma: no cover
174
+ return NotImplemented
175
+ # -inf < anything (except -inf).
176
+ if self.version is None:
177
+ return other.version is not None
178
+ if other.version is None:
179
+ return False
180
+ if self.version != other.version:
181
+ return self.version < other.version
182
+ # [v < (v: inclusive starts earlier.
183
+ return self.inclusive and not other.inclusive
184
+
185
+ def __hash__(self) -> int:
186
+ return hash((self.version, self.inclusive))
187
+
188
+ def __repr__(self) -> str:
189
+ bracket = "[" if self.inclusive else "("
190
+ return f"<{self.__class__.__name__} {bracket}{self.version!r}>"
191
+
192
+
193
+ @functools.total_ordering
194
+ class _UpperBound:
195
+ """Upper bound of a version range.
196
+
197
+ A version *v* of ``None`` means unbounded above (+inf).
198
+ At equal versions, ``v)`` sorts before ``v]`` because an exclusive
199
+ bound ends earlier.
200
+ """
201
+
202
+ __slots__ = ("inclusive", "version")
203
+
204
+ def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None:
205
+ self.version = version
206
+ self.inclusive = inclusive
207
+
208
+ def __eq__(self, other: object) -> bool:
209
+ if not isinstance(other, _UpperBound):
210
+ return NotImplemented # pragma: no cover
211
+ return self.version == other.version and self.inclusive == other.inclusive
212
+
213
+ def __lt__(self, other: _UpperBound) -> bool:
214
+ if not isinstance(other, _UpperBound): # pragma: no cover
215
+ return NotImplemented
216
+ # Nothing < +inf (except +inf itself).
217
+ if self.version is None:
218
+ return False
219
+ if other.version is None:
220
+ return True
221
+ if self.version != other.version:
222
+ return self.version < other.version
223
+ # v) < v]: exclusive ends earlier.
224
+ return not self.inclusive and other.inclusive
225
+
226
+ def __hash__(self) -> int:
227
+ return hash((self.version, self.inclusive))
228
+
229
+ def __repr__(self) -> str:
230
+ bracket = "]" if self.inclusive else ")"
231
+ return f"<{self.__class__.__name__} {self.version!r}{bracket}>"
232
+
233
+
234
+ if typing.TYPE_CHECKING:
235
+ _VersionOrBoundary = Union[Version, _BoundaryVersion, None]
236
+
237
+ #: A single contiguous version range, represented as a
238
+ #: (lower bound, upper bound) pair.
239
+ _VersionRange = tuple[_LowerBound, _UpperBound]
240
+
241
+ _NEG_INF = _LowerBound(None, False)
242
+ _POS_INF = _UpperBound(None, False)
243
+ _FULL_RANGE: tuple[_VersionRange] = ((_NEG_INF, _POS_INF),)
244
+
245
+
246
+ def _range_is_empty(lower: _LowerBound, upper: _UpperBound) -> bool:
247
+ """True when the range defined by *lower* and *upper* contains no versions."""
248
+ if lower.version is None or upper.version is None:
249
+ return False
250
+ if lower.version == upper.version:
251
+ return not (lower.inclusive and upper.inclusive)
252
+ return lower.version > upper.version
253
+
254
+
255
+ def _intersect_ranges(
256
+ left: Sequence[_VersionRange],
257
+ right: Sequence[_VersionRange],
258
+ ) -> list[_VersionRange]:
259
+ """Intersect two sorted, non-overlapping range lists (two-pointer merge)."""
260
+ result: list[_VersionRange] = []
261
+ left_index = right_index = 0
262
+ while left_index < len(left) and right_index < len(right):
263
+ left_lower, left_upper = left[left_index]
264
+ right_lower, right_upper = right[right_index]
265
+
266
+ lower = max(left_lower, right_lower)
267
+ upper = min(left_upper, right_upper)
268
+
269
+ if not _range_is_empty(lower, upper):
270
+ result.append((lower, upper))
271
+
272
+ # Advance whichever side has the smaller upper bound.
273
+ if left_upper < right_upper:
274
+ left_index += 1
275
+ else:
276
+ right_index += 1
277
+
278
+ return result
279
+
280
+
281
+ def _next_prefix_dev0(version: Version) -> Version:
282
+ """Smallest version in the next prefix: 1.2 -> 1.3.dev0."""
283
+ release = (*version.release[:-1], version.release[-1] + 1)
284
+ return Version.from_parts(epoch=version.epoch, release=release, dev=0)
285
+
286
+
287
+ def _base_dev0(version: Version) -> Version:
288
+ """The .dev0 of a version's base release: 1.2 -> 1.2.dev0."""
289
+ return Version.from_parts(epoch=version.epoch, release=version.release, dev=0)
290
+
291
+
292
+ def _coerce_version(version: UnparsedVersion) -> Version | None:
293
+ if not isinstance(version, Version):
294
+ try:
295
+ version = Version(version)
296
+ except InvalidVersion:
297
+ return None
298
+ return version
299
+
300
+
301
+ def _public_version(version: Version) -> Version:
302
+ if version.local is None:
303
+ return version
304
+ return version.__replace__(local=None)
305
+
306
+
307
+ def _post_base(version: Version) -> Version:
308
+ """The version that *version* is a post-release of.
309
+
310
+ 1.0.post1 -> 1.0, 1.0a1.post0 -> 1.0a1, 1.0.post0.dev1 -> 1.0.
311
+ """
312
+ return version.__replace__(post=None, dev=None, local=None)
313
+
314
+
315
+ def _earliest_prerelease(version: Version) -> Version:
316
+ """Earliest pre-release of *version*.
317
+
318
+ 1.2 -> 1.2.dev0, 1.2.post1 -> 1.2.post1.dev0.
319
+ """
320
+ return version.__replace__(dev=0, local=None)
321
+
322
+
323
+ def _nearest_non_prerelease(
324
+ v: _VersionOrBoundary,
325
+ ) -> Version | None:
326
+ """Smallest non-pre-release version at or above *v*, or None."""
327
+ if v is None:
328
+ return None
329
+ if isinstance(v, _BoundaryVersion):
330
+ inner = v.version
331
+ if inner.is_prerelease:
332
+ # AFTER_LOCALS(1.0a1) -> nearest non-pre is 1.0
333
+ return inner.__replace__(pre=None, dev=None, local=None)
334
+ # AFTER_LOCALS(1.0) -> nearest non-pre is 1.0.post0
335
+ # AFTER_LOCALS(1.0.post0) -> nearest non-pre is 1.0.post1
336
+ k = (inner.post + 1) if inner.post is not None else 0
337
+ return inner.__replace__(post=k, local=None)
338
+ if not v.is_prerelease:
339
+ return v
340
+ # Strip pre/dev to get the final or post-release form.
341
+ return v.__replace__(pre=None, dev=None, local=None)
342
+
343
+
344
+ class InvalidSpecifier(ValueError):
345
+ """
346
+ Raised when attempting to create a :class:`Specifier` with a specifier
347
+ string that is invalid.
348
+
349
+ >>> Specifier("lolwat")
350
+ Traceback (most recent call last):
351
+ ...
352
+ packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat'
353
+ """
354
+
355
+
356
+ class BaseSpecifier(metaclass=abc.ABCMeta):
357
+ __slots__ = ()
358
+ __match_args__ = ("_str",)
359
+
360
+ @property
361
+ def _str(self) -> str:
362
+ """Internal property for match_args"""
363
+ return str(self)
364
+
365
+ @abc.abstractmethod
366
+ def __str__(self) -> str:
367
+ """
368
+ Returns the str representation of this Specifier-like object. This
369
+ should be representative of the Specifier itself.
370
+ """
371
+
372
+ @abc.abstractmethod
373
+ def __hash__(self) -> int:
374
+ """
375
+ Returns a hash value for this Specifier-like object.
376
+ """
377
+
378
+ @abc.abstractmethod
379
+ def __eq__(self, other: object) -> bool:
380
+ """
381
+ Returns a boolean representing whether or not the two Specifier-like
382
+ objects are equal.
383
+
384
+ :param other: The other object to check against.
385
+ """
386
+
387
+ @property
388
+ @abc.abstractmethod
389
+ def prereleases(self) -> bool | None:
390
+ """Whether or not pre-releases as a whole are allowed.
391
+
392
+ This can be set to either ``True`` or ``False`` to explicitly enable or disable
393
+ prereleases or it can be set to ``None`` (the default) to use default semantics.
394
+ """
395
+
396
+ @prereleases.setter # noqa: B027
397
+ def prereleases(self, value: bool) -> None:
398
+ """Setter for :attr:`prereleases`.
399
+
400
+ :param value: The value to set.
401
+ """
402
+
403
+ @abc.abstractmethod
404
+ def contains(self, item: str, prereleases: bool | None = None) -> bool:
405
+ """
406
+ Determines if the given item is contained within this specifier.
407
+ """
408
+
409
+ @typing.overload
410
+ def filter(
411
+ self,
412
+ iterable: Iterable[UnparsedVersionVar],
413
+ prereleases: bool | None = None,
414
+ key: None = ...,
415
+ ) -> Iterator[UnparsedVersionVar]: ...
416
+
417
+ @typing.overload
418
+ def filter(
419
+ self,
420
+ iterable: Iterable[T],
421
+ prereleases: bool | None = None,
422
+ key: Callable[[T], UnparsedVersion] = ...,
423
+ ) -> Iterator[T]: ...
424
+
425
+ @abc.abstractmethod
426
+ def filter(
427
+ self,
428
+ iterable: Iterable[Any],
429
+ prereleases: bool | None = None,
430
+ key: Callable[[Any], UnparsedVersion] | None = None,
431
+ ) -> Iterator[Any]:
432
+ """
433
+ Takes an iterable of items and filters them so that only items which
434
+ are contained within this specifier are allowed in it.
435
+ """
436
+
437
+
438
+ class Specifier(BaseSpecifier):
439
+ """This class abstracts handling of version specifiers.
440
+
441
+ .. tip::
442
+
443
+ It is generally not required to instantiate this manually. You should instead
444
+ prefer to work with :class:`SpecifierSet` instead, which can parse
445
+ comma-separated version specifiers (which is what package metadata contains).
446
+
447
+ Instances are safe to serialize with :mod:`pickle`. They use a stable
448
+ format so the same pickle can be loaded in future packaging releases.
449
+
450
+ .. versionchanged:: 26.2
451
+
452
+ Added a stable pickle format. Pickles created with packaging 26.2+ can
453
+ be unpickled with future releases. Backward compatibility with pickles
454
+ from pip._vendor.packaging < 26.2 is supported but may be removed in a future
455
+ release.
456
+ """
457
+
458
+ __slots__ = (
459
+ "_prereleases",
460
+ "_ranges",
461
+ "_spec",
462
+ "_spec_version",
463
+ "_wildcard_split",
464
+ )
465
+
466
+ _specifier_regex_str = r"""
467
+ (?:
468
+ (?:
469
+ # The identity operators allow for an escape hatch that will
470
+ # do an exact string match of the version you wish to install.
471
+ # This will not be parsed by PEP 440 and we cannot determine
472
+ # any semantic meaning from it. This operator is discouraged
473
+ # but included entirely as an escape hatch.
474
+ === # Only match for the identity operator
475
+ \s*
476
+ [^\s;)]* # The arbitrary version can be just about anything,
477
+ # we match everything except for whitespace, a
478
+ # semi-colon for marker support, and a closing paren
479
+ # since versions can be enclosed in them.
480
+ )
481
+ |
482
+ (?:
483
+ # The (non)equality operators allow for wild card and local
484
+ # versions to be specified so we have to define these two
485
+ # operators separately to enable that.
486
+ (?:==|!=) # Only match for equals and not equals
487
+
488
+ \s*
489
+ v?
490
+ (?:[0-9]+!)? # epoch
491
+ [0-9]+(?:\.[0-9]+)* # release
492
+
493
+ # You cannot use a wild card and a pre-release, post-release, a dev or
494
+ # local version together so group them with a | and make them optional.
495
+ (?:
496
+ \.\* # Wild card syntax of .*
497
+ |
498
+ (?a: # pre release
499
+ [-_\.]?
500
+ (alpha|beta|preview|pre|a|b|c|rc)
501
+ [-_\.]?
502
+ [0-9]*
503
+ )?
504
+ (?a: # post release
505
+ (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
506
+ )?
507
+ (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
508
+ (?a:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local
509
+ )?
510
+ )
511
+ |
512
+ (?:
513
+ # The compatible operator requires at least two digits in the
514
+ # release segment.
515
+ (?:~=) # Only match for the compatible operator
516
+
517
+ \s*
518
+ v?
519
+ (?:[0-9]+!)? # epoch
520
+ [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *)
521
+ (?: # pre release
522
+ [-_\.]?
523
+ (alpha|beta|preview|pre|a|b|c|rc)
524
+ [-_\.]?
525
+ [0-9]*
526
+ )?
527
+ (?: # post release
528
+ (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
529
+ )?
530
+ (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
531
+ )
532
+ |
533
+ (?:
534
+ # All other operators only allow a sub set of what the
535
+ # (non)equality operators do. Specifically they do not allow
536
+ # local versions to be specified nor do they allow the prefix
537
+ # matching wild cards.
538
+ (?:<=|>=|<|>)
539
+
540
+ \s*
541
+ v?
542
+ (?:[0-9]+!)? # epoch
543
+ [0-9]+(?:\.[0-9]+)* # release
544
+ (?a: # pre release
545
+ [-_\.]?
546
+ (alpha|beta|preview|pre|a|b|c|rc)
547
+ [-_\.]?
548
+ [0-9]*
549
+ )?
550
+ (?a: # post release
551
+ (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
552
+ )?
553
+ (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
554
+ )
555
+ )
556
+ """
557
+
558
+ _regex = re.compile(
559
+ r"\s*" + _specifier_regex_str + r"\s*", re.VERBOSE | re.IGNORECASE
560
+ )
561
+
562
+ _operators: Final = {
563
+ "~=": "compatible",
564
+ "==": "equal",
565
+ "!=": "not_equal",
566
+ "<=": "less_than_equal",
567
+ ">=": "greater_than_equal",
568
+ "<": "less_than",
569
+ ">": "greater_than",
570
+ "===": "arbitrary",
571
+ }
572
+
573
+ def __init__(self, spec: str = "", prereleases: bool | None = None) -> None:
574
+ """Initialize a Specifier instance.
575
+
576
+ :param spec:
577
+ The string representation of a specifier which will be parsed and
578
+ normalized before use.
579
+ :param prereleases:
580
+ This tells the specifier if it should accept prerelease versions if
581
+ applicable or not. The default of ``None`` will autodetect it from the
582
+ given specifiers.
583
+ :raises InvalidSpecifier:
584
+ If the given specifier is invalid (i.e. bad syntax).
585
+ """
586
+ if not self._regex.fullmatch(spec):
587
+ raise InvalidSpecifier(f"Invalid specifier: {spec!r}")
588
+
589
+ spec = spec.strip()
590
+ if spec.startswith("==="):
591
+ operator, version = spec[:3], spec[3:].strip()
592
+ elif spec.startswith(("~=", "==", "!=", "<=", ">=")):
593
+ operator, version = spec[:2], spec[2:].strip()
594
+ else:
595
+ operator, version = spec[:1], spec[1:].strip()
596
+
597
+ self._spec: tuple[str, str] = (operator, version)
598
+
599
+ # Store whether or not this Specifier should accept prereleases
600
+ self._prereleases = prereleases
601
+
602
+ # Specifier version cache
603
+ self._spec_version: tuple[str, Version] | None = None
604
+
605
+ # Populated on first wildcard (==X.*) comparison
606
+ self._wildcard_split: tuple[list[str], int] | None = None
607
+
608
+ # Version range cache (populated by _to_ranges)
609
+ self._ranges: Sequence[_VersionRange] | None = None
610
+
611
+ def _get_spec_version(self, version: str) -> Version | None:
612
+ """One element cache, as only one spec Version is needed per Specifier."""
613
+ if self._spec_version is not None and self._spec_version[0] == version:
614
+ return self._spec_version[1]
615
+
616
+ version_specifier = _coerce_version(version)
617
+ if version_specifier is None:
618
+ return None
619
+
620
+ self._spec_version = (version, version_specifier)
621
+ return version_specifier
622
+
623
+ def _require_spec_version(self, version: str) -> Version:
624
+ """Get spec version, asserting it's valid (not for === operator).
625
+
626
+ This method should only be called for operators where version
627
+ strings are guaranteed to be valid PEP 440 versions (not ===).
628
+ """
629
+ spec_version = self._get_spec_version(version)
630
+ assert spec_version is not None
631
+ return spec_version
632
+
633
+ def _to_ranges(self) -> Sequence[_VersionRange]:
634
+ """Convert this specifier to sorted, non-overlapping version ranges.
635
+
636
+ Each standard operator maps to one or two ranges. ``===`` is
637
+ modeled as full range (actual check done separately). Cached.
638
+ """
639
+ if self._ranges is not None:
640
+ return self._ranges
641
+
642
+ op = self.operator
643
+ ver_str = self.version
644
+
645
+ if op == "===":
646
+ self._ranges = _FULL_RANGE
647
+ return _FULL_RANGE
648
+
649
+ if ver_str.endswith(".*"):
650
+ result = self._wildcard_ranges(op, ver_str)
651
+ else:
652
+ result = self._standard_ranges(op, ver_str)
653
+
654
+ self._ranges = result
655
+ return result
656
+
657
+ def _wildcard_ranges(self, op: str, ver_str: str) -> list[_VersionRange]:
658
+ # ==1.2.* -> [1.2.dev0, 1.3.dev0); !=1.2.* -> complement.
659
+ base = self._require_spec_version(ver_str[:-2])
660
+ lower = _base_dev0(base)
661
+ upper = _next_prefix_dev0(base)
662
+ if op == "==":
663
+ return [(_LowerBound(lower, True), _UpperBound(upper, False))]
664
+ # !=
665
+ return [
666
+ (_NEG_INF, _UpperBound(lower, False)),
667
+ (_LowerBound(upper, True), _POS_INF),
668
+ ]
669
+
670
+ def _standard_ranges(self, op: str, ver_str: str) -> list[_VersionRange]:
671
+ v = self._require_spec_version(ver_str)
672
+
673
+ if op == ">=":
674
+ return [(_LowerBound(v, True), _POS_INF)]
675
+
676
+ if op == "<=":
677
+ return [
678
+ (
679
+ _NEG_INF,
680
+ _UpperBound(_BoundaryVersion(v, _BoundaryKind.AFTER_LOCALS), True),
681
+ )
682
+ ]
683
+
684
+ if op == ">":
685
+ if v.dev is not None:
686
+ # >V.devN: dev versions have no post-releases, so the
687
+ # next real version is V.dev(N+1).
688
+ lower_ver = v.__replace__(dev=v.dev + 1, local=None)
689
+ return [(_LowerBound(lower_ver, True), _POS_INF)]
690
+ if v.post is not None:
691
+ # >V.postN: next real version is V.post(N+1).dev0.
692
+ lower_ver = v.__replace__(post=v.post + 1, dev=0, local=None)
693
+ return [(_LowerBound(lower_ver, True), _POS_INF)]
694
+ # >V (final or pre-release): skip V+local and all V.postN.
695
+ return [
696
+ (
697
+ _LowerBound(_BoundaryVersion(v, _BoundaryKind.AFTER_POSTS), False),
698
+ _POS_INF,
699
+ )
700
+ ]
701
+
702
+ if op == "<":
703
+ # <V excludes prereleases of V when V is not a prerelease.
704
+ # V.dev0 is the earliest prerelease of V (final, post, etc.).
705
+ bound = v if v.is_prerelease else v.__replace__(dev=0, local=None)
706
+ if bound <= _MIN_VERSION:
707
+ return []
708
+ return [(_NEG_INF, _UpperBound(bound, False))]
709
+
710
+ # ==, !=: local versions of V match when spec has no local segment.
711
+ has_local = "+" in ver_str
712
+ after_locals = _BoundaryVersion(v, _BoundaryKind.AFTER_LOCALS)
713
+ upper = v if has_local else after_locals
714
+
715
+ if op == "==":
716
+ return [(_LowerBound(v, True), _UpperBound(upper, True))]
717
+
718
+ if op == "!=":
719
+ return [
720
+ (_NEG_INF, _UpperBound(v, False)),
721
+ (_LowerBound(upper, False), _POS_INF),
722
+ ]
723
+
724
+ if op == "~=":
725
+ prefix = v.__replace__(release=v.release[:-1])
726
+ return [
727
+ (_LowerBound(v, True), _UpperBound(_next_prefix_dev0(prefix), False))
728
+ ]
729
+
730
+ raise ValueError(f"Unknown operator: {op!r}") # pragma: no cover
731
+
732
+ @property
733
+ def prereleases(self) -> bool | None:
734
+ # If there is an explicit prereleases set for this, then we'll just
735
+ # blindly use that.
736
+ if self._prereleases is not None:
737
+ return self._prereleases
738
+
739
+ # Only the "!=" operator does not imply prereleases when
740
+ # the version in the specifier is a prerelease.
741
+ operator, version_str = self._spec
742
+ if operator == "!=":
743
+ return False
744
+
745
+ # The == specifier with trailing .* cannot include prereleases
746
+ # e.g. "==1.0a1.*" is not valid.
747
+ if operator == "==" and version_str.endswith(".*"):
748
+ return False
749
+
750
+ # "===" can have arbitrary string versions, so we cannot parse
751
+ # those, we take prereleases as unknown (None) for those.
752
+ version = self._get_spec_version(version_str)
753
+ if version is None:
754
+ return None
755
+
756
+ # For all other operators, use the check if spec Version
757
+ # object implies pre-releases.
758
+ return version.is_prerelease
759
+
760
+ @prereleases.setter
761
+ def prereleases(self, value: bool | None) -> None:
762
+ self._prereleases = value
763
+
764
+ def __getstate__(self) -> tuple[tuple[str, str], bool | None]:
765
+ # Return state as a 2-item tuple for compactness:
766
+ # ((operator, version), prereleases)
767
+ # Cache members are excluded and will be recomputed on demand.
768
+ return (self._spec, self._prereleases)
769
+
770
+ def __setstate__(self, state: object) -> None:
771
+ # Always discard cached values - they will be recomputed on demand.
772
+ self._spec_version = None
773
+ self._wildcard_split = None
774
+ self._ranges = None
775
+
776
+ if isinstance(state, tuple):
777
+ if len(state) == 2:
778
+ # New format (26.2+): ((operator, version), prereleases)
779
+ spec, prereleases = state
780
+ if _validate_spec(spec) and _validate_pre(prereleases):
781
+ self._spec = spec
782
+ self._prereleases = prereleases
783
+ return
784
+ if len(state) == 2 and isinstance(state[1], dict):
785
+ # Format (packaging 26.0-26.1): (None, {slot: value}).
786
+ _, slot_dict = state
787
+ spec = slot_dict.get("_spec")
788
+ prereleases = slot_dict.get("_prereleases", "invalid")
789
+ if _validate_spec(spec) and _validate_pre(prereleases):
790
+ self._spec = spec
791
+ self._prereleases = prereleases
792
+ return
793
+ if isinstance(state, dict):
794
+ # Old format (packaging <= 25.x, no __slots__): state is a plain dict.
795
+ spec = state.get("_spec")
796
+ prereleases = state.get("_prereleases", "invalid")
797
+ if _validate_spec(spec) and _validate_pre(prereleases):
798
+ self._spec = spec
799
+ self._prereleases = prereleases
800
+ return
801
+
802
+ raise TypeError(f"Cannot restore Specifier from {state!r}")
803
+
804
+ @property
805
+ def operator(self) -> str:
806
+ """The operator of this specifier.
807
+
808
+ >>> Specifier("==1.2.3").operator
809
+ '=='
810
+ """
811
+ return self._spec[0]
812
+
813
+ @property
814
+ def version(self) -> str:
815
+ """The version of this specifier.
816
+
817
+ >>> Specifier("==1.2.3").version
818
+ '1.2.3'
819
+ """
820
+ return self._spec[1]
821
+
822
+ def __repr__(self) -> str:
823
+ """A representation of the Specifier that shows all internal state.
824
+
825
+ >>> Specifier('>=1.0.0')
826
+ <Specifier('>=1.0.0')>
827
+ >>> Specifier('>=1.0.0', prereleases=False)
828
+ <Specifier('>=1.0.0', prereleases=False)>
829
+ >>> Specifier('>=1.0.0', prereleases=True)
830
+ <Specifier('>=1.0.0', prereleases=True)>
831
+ """
832
+ pre = (
833
+ f", prereleases={self.prereleases!r}"
834
+ if self._prereleases is not None
835
+ else ""
836
+ )
837
+
838
+ return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
839
+
840
+ def __str__(self) -> str:
841
+ """A string representation of the Specifier that can be round-tripped.
842
+
843
+ >>> str(Specifier('>=1.0.0'))
844
+ '>=1.0.0'
845
+ >>> str(Specifier('>=1.0.0', prereleases=False))
846
+ '>=1.0.0'
847
+ """
848
+ return "{}{}".format(*self._spec)
849
+
850
+ @property
851
+ def _canonical_spec(self) -> tuple[str, str]:
852
+ operator, version = self._spec
853
+ if operator == "===" or version.endswith(".*"):
854
+ return operator, version
855
+
856
+ spec_version = self._require_spec_version(version)
857
+
858
+ canonical_version = canonicalize_version(
859
+ spec_version, strip_trailing_zero=(operator != "~=")
860
+ )
861
+
862
+ return operator, canonical_version
863
+
864
+ def __hash__(self) -> int:
865
+ return hash(self._canonical_spec)
866
+
867
+ def __eq__(self, other: object) -> bool:
868
+ """Whether or not the two Specifier-like objects are equal.
869
+
870
+ :param other: The other object to check against.
871
+
872
+ The value of :attr:`prereleases` is ignored.
873
+
874
+ >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0")
875
+ True
876
+ >>> (Specifier("==1.2.3", prereleases=False) ==
877
+ ... Specifier("==1.2.3", prereleases=True))
878
+ True
879
+ >>> Specifier("==1.2.3") == "==1.2.3"
880
+ True
881
+ >>> Specifier("==1.2.3") == Specifier("==1.2.4")
882
+ False
883
+ >>> Specifier("==1.2.3") == Specifier("~=1.2.3")
884
+ False
885
+ """
886
+ if isinstance(other, str):
887
+ try:
888
+ other = self.__class__(str(other))
889
+ except InvalidSpecifier:
890
+ return NotImplemented
891
+ elif not isinstance(other, self.__class__):
892
+ return NotImplemented
893
+
894
+ return self._canonical_spec == other._canonical_spec
895
+
896
+ def _get_operator(self, op: str) -> CallableOperator:
897
+ operator_callable: CallableOperator = getattr(
898
+ self, f"_compare_{self._operators[op]}"
899
+ )
900
+ return operator_callable
901
+
902
+ def _compare_compatible(self, prospective: Version, spec: str) -> bool:
903
+ # Compatible releases have an equivalent combination of >= and ==. That
904
+ # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
905
+ # implement this in terms of the other specifiers instead of
906
+ # implementing it ourselves. The only thing we need to do is construct
907
+ # the other specifiers.
908
+
909
+ # We want everything but the last item in the version, but we want to
910
+ # ignore suffix segments.
911
+ prefix = _version_join(
912
+ list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1]
913
+ )
914
+
915
+ # Add the prefix notation to the end of our string
916
+ prefix += ".*"
917
+
918
+ return (self._compare_greater_than_equal(prospective, spec)) and (
919
+ self._compare_equal(prospective, prefix)
920
+ )
921
+
922
+ def _get_wildcard_split(self, spec: str) -> tuple[list[str], int]:
923
+ """Cached split of a wildcard spec into components and numeric length.
924
+
925
+ >>> Specifier("==1.*")._get_wildcard_split("1.*")
926
+ (['0', '1'], 2)
927
+ >>> Specifier("==3.10.*")._get_wildcard_split("3.10.*")
928
+ (['0', '3', '10'], 3)
929
+ """
930
+ wildcard_split = self._wildcard_split
931
+ if wildcard_split is None:
932
+ normalized = canonicalize_version(spec[:-2], strip_trailing_zero=False)
933
+ split_spec = _version_split(normalized)
934
+ wildcard_split = (split_spec, _numeric_prefix_len(split_spec))
935
+ self._wildcard_split = wildcard_split
936
+ return wildcard_split
937
+
938
+ def _compare_equal(self, prospective: Version, spec: str) -> bool:
939
+ # We need special logic to handle prefix matching
940
+ if spec.endswith(".*"):
941
+ split_spec, spec_numeric_len = self._get_wildcard_split(spec)
942
+
943
+ # In the case of prefix matching we want to ignore local segment.
944
+ normalized_prospective = canonicalize_version(
945
+ _public_version(prospective), strip_trailing_zero=False
946
+ )
947
+ # Split the prospective version out by bangs and dots, and pretend
948
+ # that there is an implicit dot in between a release segment and
949
+ # a pre-release segment.
950
+ split_prospective = _version_split(normalized_prospective)
951
+
952
+ # 0-pad the prospective version before shortening it to get the correct
953
+ # shortened version.
954
+ padded_prospective = _left_pad(split_prospective, spec_numeric_len)
955
+
956
+ # Shorten the prospective version to be the same length as the spec
957
+ # so that we can determine if the specifier is a prefix of the
958
+ # prospective version or not.
959
+ shortened_prospective = padded_prospective[: len(split_spec)]
960
+
961
+ return shortened_prospective == split_spec
962
+ else:
963
+ # Convert our spec string into a Version
964
+ spec_version = self._require_spec_version(spec)
965
+
966
+ # If the specifier does not have a local segment, then we want to
967
+ # act as if the prospective version also does not have a local
968
+ # segment.
969
+ if not spec_version.local:
970
+ prospective = _public_version(prospective)
971
+
972
+ return prospective == spec_version
973
+
974
+ def _compare_not_equal(self, prospective: Version, spec: str) -> bool:
975
+ return not self._compare_equal(prospective, spec)
976
+
977
+ def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool:
978
+ # NB: Local version identifiers are NOT permitted in the version
979
+ # specifier, so local version labels can be universally removed from
980
+ # the prospective version.
981
+ return _public_version(prospective) <= self._require_spec_version(spec)
982
+
983
+ def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool:
984
+ # NB: Local version identifiers are NOT permitted in the version
985
+ # specifier, so local version labels can be universally removed from
986
+ # the prospective version.
987
+ return _public_version(prospective) >= self._require_spec_version(spec)
988
+
989
+ def _compare_less_than(self, prospective: Version, spec_str: str) -> bool:
990
+ # Convert our spec to a Version instance, since we'll want to work with
991
+ # it as a version.
992
+ spec = self._require_spec_version(spec_str)
993
+
994
+ # Check to see if the prospective version is less than the spec
995
+ # version. If it's not we can short circuit and just return False now
996
+ # instead of doing extra unneeded work.
997
+ if not prospective < spec:
998
+ return False
999
+
1000
+ # The spec says: "<V MUST NOT allow a pre-release of the specified
1001
+ # version unless the specified version is itself a pre-release."
1002
+ if (
1003
+ not spec.is_prerelease
1004
+ and prospective.is_prerelease
1005
+ and prospective >= _earliest_prerelease(spec)
1006
+ ):
1007
+ return False
1008
+
1009
+ # If we've gotten to here, it means that prospective version is both
1010
+ # less than the spec version *and* it's not a pre-release of the same
1011
+ # version in the spec.
1012
+ return True
1013
+
1014
+ def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool:
1015
+ # Convert our spec to a Version instance, since we'll want to work with
1016
+ # it as a version.
1017
+ spec = self._require_spec_version(spec_str)
1018
+
1019
+ # Check to see if the prospective version is greater than the spec
1020
+ # version. If it's not we can short circuit and just return False now
1021
+ # instead of doing extra unneeded work.
1022
+ if not prospective > spec:
1023
+ return False
1024
+
1025
+ # The spec says: ">V MUST NOT allow a post-release of the specified
1026
+ # version unless the specified version is itself a post-release."
1027
+ if (
1028
+ not spec.is_postrelease
1029
+ and prospective.is_postrelease
1030
+ and _post_base(prospective) == spec
1031
+ ):
1032
+ return False
1033
+
1034
+ # Per the spec: ">V MUST NOT match a local version of the specified
1035
+ # version". A "local version of V" is any version whose public part
1036
+ # equals V. So >1.0a1 must not match 1.0a1+local, but must still
1037
+ # match 1.0a2+local.
1038
+ if prospective.local is not None and _public_version(prospective) == spec:
1039
+ return False
1040
+
1041
+ # If we've gotten to here, it means that prospective version is both
1042
+ # greater than the spec version *and* it's not a pre-release of the
1043
+ # same version in the spec.
1044
+ return True
1045
+
1046
+ def _compare_arbitrary(self, prospective: Version | str, spec: str) -> bool:
1047
+ return str(prospective).lower() == str(spec).lower()
1048
+
1049
+ def __contains__(self, item: str | Version) -> bool:
1050
+ """Return whether or not the item is contained in this specifier.
1051
+
1052
+ :param item: The item to check for.
1053
+
1054
+ This is used for the ``in`` operator and behaves the same as
1055
+ :meth:`contains` with no ``prereleases`` argument passed.
1056
+
1057
+ >>> "1.2.3" in Specifier(">=1.2.3")
1058
+ True
1059
+ >>> Version("1.2.3") in Specifier(">=1.2.3")
1060
+ True
1061
+ >>> "1.0.0" in Specifier(">=1.2.3")
1062
+ False
1063
+ >>> "1.3.0a1" in Specifier(">=1.2.3")
1064
+ True
1065
+ >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True)
1066
+ True
1067
+ """
1068
+ return self.contains(item)
1069
+
1070
+ def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool:
1071
+ """Return whether or not the item is contained in this specifier.
1072
+
1073
+ :param item:
1074
+ The item to check for, which can be a version string or a
1075
+ :class:`Version` instance.
1076
+ :param prereleases:
1077
+ Whether or not to match prereleases with this Specifier. If set to
1078
+ ``None`` (the default), it will follow the recommendation from
1079
+ :pep:`440` and match prereleases, as there are no other versions.
1080
+
1081
+ >>> Specifier(">=1.2.3").contains("1.2.3")
1082
+ True
1083
+ >>> Specifier(">=1.2.3").contains(Version("1.2.3"))
1084
+ True
1085
+ >>> Specifier(">=1.2.3").contains("1.0.0")
1086
+ False
1087
+ >>> Specifier(">=1.2.3").contains("1.3.0a1")
1088
+ True
1089
+ >>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1")
1090
+ False
1091
+ >>> Specifier(">=1.2.3").contains("1.3.0a1")
1092
+ True
1093
+ """
1094
+
1095
+ return bool(list(self.filter([item], prereleases=prereleases)))
1096
+
1097
+ @typing.overload
1098
+ def filter(
1099
+ self,
1100
+ iterable: Iterable[UnparsedVersionVar],
1101
+ prereleases: bool | None = None,
1102
+ key: None = ...,
1103
+ ) -> Iterator[UnparsedVersionVar]: ...
1104
+
1105
+ @typing.overload
1106
+ def filter(
1107
+ self,
1108
+ iterable: Iterable[T],
1109
+ prereleases: bool | None = None,
1110
+ key: Callable[[T], UnparsedVersion] = ...,
1111
+ ) -> Iterator[T]: ...
1112
+
1113
+ def filter(
1114
+ self,
1115
+ iterable: Iterable[Any],
1116
+ prereleases: bool | None = None,
1117
+ key: Callable[[Any], UnparsedVersion] | None = None,
1118
+ ) -> Iterator[Any]:
1119
+ """Filter items in the given iterable, that match the specifier.
1120
+
1121
+ :param iterable:
1122
+ An iterable that can contain version strings and :class:`Version` instances.
1123
+ The items in the iterable will be filtered according to the specifier.
1124
+ :param prereleases:
1125
+ Whether or not to allow prereleases in the returned iterator. If set to
1126
+ ``None`` (the default), it will follow the recommendation from :pep:`440`
1127
+ and match prereleases if there are no other versions.
1128
+ :param key:
1129
+ A callable that takes a single argument (an item from the iterable) and
1130
+ returns a version string or :class:`Version` instance to be used for
1131
+ filtering.
1132
+
1133
+ >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
1134
+ ['1.3']
1135
+ >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")]))
1136
+ ['1.2.3', '1.3', <Version('1.4')>]
1137
+ >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"]))
1138
+ ['1.5a1']
1139
+ >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
1140
+ ['1.3', '1.5a1']
1141
+ >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
1142
+ ['1.3', '1.5a1']
1143
+ >>> list(Specifier(">=1.2.3").filter(
1144
+ ... [{"ver": "1.2"}, {"ver": "1.3"}],
1145
+ ... key=lambda x: x["ver"]))
1146
+ [{'ver': '1.3'}]
1147
+ """
1148
+ prereleases_versions = []
1149
+ found_non_prereleases = False
1150
+
1151
+ # Determine if to include prereleases by default
1152
+ include_prereleases = (
1153
+ prereleases if prereleases is not None else self.prereleases
1154
+ )
1155
+
1156
+ # Get the matching operator
1157
+ operator_callable = self._get_operator(self.operator)
1158
+
1159
+ # Filter versions
1160
+ for version in iterable:
1161
+ parsed_version = _coerce_version(version if key is None else key(version))
1162
+ match = False
1163
+ if parsed_version is None:
1164
+ # === operator can match arbitrary (non-version) strings
1165
+ if self.operator == "===" and self._compare_arbitrary(
1166
+ version, self.version
1167
+ ):
1168
+ yield version
1169
+ elif self.operator == "===":
1170
+ match = self._compare_arbitrary(
1171
+ version if key is None else key(version), self.version
1172
+ )
1173
+ else:
1174
+ match = operator_callable(parsed_version, self.version)
1175
+
1176
+ if match and parsed_version is not None:
1177
+ # If it's not a prerelease or prereleases are allowed, yield it directly
1178
+ if not parsed_version.is_prerelease or include_prereleases:
1179
+ found_non_prereleases = True
1180
+ yield version
1181
+ # Otherwise collect prereleases for potential later use
1182
+ elif prereleases is None and self._prereleases is not False:
1183
+ prereleases_versions.append(version)
1184
+
1185
+ # If no non-prereleases were found and prereleases weren't
1186
+ # explicitly forbidden, yield the collected prereleases
1187
+ if (
1188
+ not found_non_prereleases
1189
+ and prereleases is None
1190
+ and self._prereleases is not False
1191
+ ):
1192
+ yield from prereleases_versions
1193
+
1194
+
1195
+ _prefix_regex = re.compile(r"([0-9]+)((?:a|b|c|rc)[0-9]+)")
1196
+
1197
+
1198
+ def _pep440_filter_prereleases(
1199
+ iterable: Iterable[Any], key: Callable[[Any], UnparsedVersion] | None
1200
+ ) -> Iterator[Any]:
1201
+ """Filter per PEP 440: exclude prereleases unless no finals exist."""
1202
+ # Two lists used:
1203
+ # * all_nonfinal to preserve order if no finals exist
1204
+ # * arbitrary_strings for streaming when first final found
1205
+ all_nonfinal: list[Any] = []
1206
+ arbitrary_strings: list[Any] = []
1207
+
1208
+ found_final = False
1209
+ for item in iterable:
1210
+ parsed = _coerce_version(item if key is None else key(item))
1211
+
1212
+ if parsed is None:
1213
+ # Arbitrary strings are always included as it is not
1214
+ # possible to determine if they are prereleases,
1215
+ # and they have already passed all specifiers.
1216
+ if found_final:
1217
+ yield item
1218
+ else:
1219
+ arbitrary_strings.append(item)
1220
+ all_nonfinal.append(item)
1221
+ continue
1222
+
1223
+ if not parsed.is_prerelease:
1224
+ # Final release found - flush arbitrary strings, then yield
1225
+ if not found_final:
1226
+ yield from arbitrary_strings
1227
+ found_final = True
1228
+ yield item
1229
+ continue
1230
+
1231
+ # Prerelease - buffer if no finals yet, otherwise skip
1232
+ if not found_final:
1233
+ all_nonfinal.append(item)
1234
+
1235
+ # No finals found - yield all buffered items
1236
+ if not found_final:
1237
+ yield from all_nonfinal
1238
+
1239
+
1240
+ def _version_split(version: str) -> list[str]:
1241
+ """Split version into components.
1242
+
1243
+ The split components are intended for version comparison. The logic does
1244
+ not attempt to retain the original version string, so joining the
1245
+ components back with :func:`_version_join` may not produce the original
1246
+ version string.
1247
+ """
1248
+ result: list[str] = []
1249
+
1250
+ epoch, _, rest = version.rpartition("!")
1251
+ result.append(epoch or "0")
1252
+
1253
+ for item in rest.split("."):
1254
+ match = _prefix_regex.fullmatch(item)
1255
+ if match:
1256
+ result.extend(match.groups())
1257
+ else:
1258
+ result.append(item)
1259
+ return result
1260
+
1261
+
1262
+ def _version_join(components: list[str]) -> str:
1263
+ """Join split version components into a version string.
1264
+
1265
+ This function assumes the input came from :func:`_version_split`, where the
1266
+ first component must be the epoch (either empty or numeric), and all other
1267
+ components numeric.
1268
+ """
1269
+ epoch, *rest = components
1270
+ return f"{epoch}!{'.'.join(rest)}"
1271
+
1272
+
1273
+ def _is_not_suffix(segment: str) -> bool:
1274
+ return not any(
1275
+ segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post")
1276
+ )
1277
+
1278
+
1279
+ def _numeric_prefix_len(split: list[str]) -> int:
1280
+ """Count leading numeric components in a :func:`_version_split` result.
1281
+
1282
+ >>> _numeric_prefix_len(["0", "1", "2", "a1"])
1283
+ 3
1284
+ """
1285
+ count = 0
1286
+ for segment in split:
1287
+ if not segment.isdigit():
1288
+ break
1289
+ count += 1
1290
+ return count
1291
+
1292
+
1293
+ def _left_pad(split: list[str], target_numeric_len: int) -> list[str]:
1294
+ """Pad a :func:`_version_split` result with ``"0"`` segments to reach
1295
+ ``target_numeric_len`` numeric components. Suffix segments are preserved.
1296
+
1297
+ >>> _left_pad(["0", "1", "a1"], 4)
1298
+ ['0', '1', '0', '0', 'a1']
1299
+ """
1300
+ numeric_len = _numeric_prefix_len(split)
1301
+ pad_needed = target_numeric_len - numeric_len
1302
+ if pad_needed <= 0:
1303
+ return split
1304
+ return [*split[:numeric_len], *(["0"] * pad_needed), *split[numeric_len:]]
1305
+
1306
+
1307
+ def _operator_cost(op_entry: tuple[CallableOperator, str, str]) -> int:
1308
+ """Sort key for Cost Based Ordering of specifier operators in _filter_versions.
1309
+
1310
+ Operators run sequentially on a shrinking candidate set, so operators that
1311
+ reject the most versions should run first to minimize work for later ones.
1312
+
1313
+ Tier 0: Exact equality (==, ===), likely to narrow candidates to one version
1314
+ Tier 1: Range checks (>=, <=, >, <), cheap and usually reject a large portion
1315
+ Tier 2: Wildcard equality (==.*) and compatible release (~=), more expensive
1316
+ Tier 3: Exact !=, cheap but rarely rejects
1317
+ Tier 4: Wildcard !=.*, expensive and rarely rejects
1318
+ """
1319
+ _, ver, op = op_entry
1320
+ if op == "==":
1321
+ return 0 if not ver.endswith(".*") else 2
1322
+ if op in (">=", "<=", ">", "<"):
1323
+ return 1
1324
+ if op == "~=":
1325
+ return 2
1326
+ if op == "!=":
1327
+ return 3 if not ver.endswith(".*") else 4
1328
+ if op == "===":
1329
+ return 0
1330
+
1331
+ raise ValueError(f"Unknown operator: {op!r}") # pragma: no cover
1332
+
1333
+
1334
+ class SpecifierSet(BaseSpecifier):
1335
+ """This class abstracts handling of a set of version specifiers.
1336
+
1337
+ It can be passed a single specifier (``>=3.0``), a comma-separated list of
1338
+ specifiers (``>=3.0,!=3.1``), or no specifier at all.
1339
+
1340
+ Instances are safe to serialize with :mod:`pickle`. They use a stable
1341
+ format so the same pickle can be loaded in future packaging
1342
+ releases.
1343
+
1344
+ .. versionchanged:: 26.2
1345
+
1346
+ Added a stable pickle format. Pickles created with
1347
+ packaging 26.2+ can be unpickled with future releases.
1348
+ Backward compatibility with pickles from
1349
+ packaging < 26.2 is supported but may be removed in a future
1350
+ release.
1351
+ """
1352
+
1353
+ __slots__ = (
1354
+ "_canonicalized",
1355
+ "_has_arbitrary",
1356
+ "_is_unsatisfiable",
1357
+ "_prereleases",
1358
+ "_resolved_ops",
1359
+ "_specs",
1360
+ )
1361
+
1362
+ def __init__(
1363
+ self,
1364
+ specifiers: str | Iterable[Specifier] = "",
1365
+ prereleases: bool | None = None,
1366
+ ) -> None:
1367
+ """Initialize a SpecifierSet instance.
1368
+
1369
+ :param specifiers:
1370
+ The string representation of a specifier or a comma-separated list of
1371
+ specifiers which will be parsed and normalized before use.
1372
+ May also be an iterable of ``Specifier`` instances, which will be used
1373
+ as is.
1374
+ :param prereleases:
1375
+ This tells the SpecifierSet if it should accept prerelease versions if
1376
+ applicable or not. The default of ``None`` will autodetect it from the
1377
+ given specifiers.
1378
+
1379
+ :raises InvalidSpecifier:
1380
+ If the given ``specifiers`` are not parseable than this exception will be
1381
+ raised.
1382
+ """
1383
+
1384
+ if isinstance(specifiers, str):
1385
+ # Split on `,` to break each individual specifier into its own item, and
1386
+ # strip each item to remove leading/trailing whitespace.
1387
+ split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
1388
+
1389
+ self._specs: tuple[Specifier, ...] = tuple(map(Specifier, split_specifiers))
1390
+ # Fast substring check; avoids iterating parsed specs.
1391
+ self._has_arbitrary = "===" in specifiers
1392
+ else:
1393
+ self._specs = tuple(specifiers)
1394
+ # Substring check works for both Specifier objects and plain
1395
+ # strings (setuptools passes lists of strings).
1396
+ self._has_arbitrary = any("===" in str(s) for s in self._specs)
1397
+
1398
+ self._canonicalized = len(self._specs) <= 1
1399
+ self._resolved_ops: list[tuple[CallableOperator, str, str]] | None = None
1400
+
1401
+ # Store our prereleases value so we can use it later to determine if
1402
+ # we accept prereleases or not.
1403
+ self._prereleases = prereleases
1404
+
1405
+ self._is_unsatisfiable: bool | None = None
1406
+
1407
+ def _canonical_specs(self) -> tuple[Specifier, ...]:
1408
+ """Deduplicate, sort, and cache specs for order-sensitive operations."""
1409
+ if not self._canonicalized:
1410
+ self._specs = tuple(dict.fromkeys(sorted(self._specs, key=str)))
1411
+ self._canonicalized = True
1412
+ self._resolved_ops = None
1413
+ self._is_unsatisfiable = None
1414
+ return self._specs
1415
+
1416
+ @property
1417
+ def prereleases(self) -> bool | None:
1418
+ # If we have been given an explicit prerelease modifier, then we'll
1419
+ # pass that through here.
1420
+ if self._prereleases is not None:
1421
+ return self._prereleases
1422
+
1423
+ # If we don't have any specifiers, and we don't have a forced value,
1424
+ # then we'll just return None since we don't know if this should have
1425
+ # pre-releases or not.
1426
+ if not self._specs:
1427
+ return None
1428
+
1429
+ # Otherwise we'll see if any of the given specifiers accept
1430
+ # prereleases, if any of them do we'll return True, otherwise False.
1431
+ if any(s.prereleases for s in self._specs):
1432
+ return True
1433
+
1434
+ return None
1435
+
1436
+ @prereleases.setter
1437
+ def prereleases(self, value: bool | None) -> None:
1438
+ self._prereleases = value
1439
+ self._is_unsatisfiable = None
1440
+
1441
+ def __getstate__(self) -> tuple[tuple[Specifier, ...], bool | None]:
1442
+ # Return state as a 2-item tuple for compactness:
1443
+ # (specs, prereleases)
1444
+ # Cache members are excluded and will be recomputed on demand.
1445
+ return (self._specs, self._prereleases)
1446
+
1447
+ def __setstate__(self, state: object) -> None:
1448
+ # Always discard cached values - they will be recomputed on demand.
1449
+ self._resolved_ops = None
1450
+ self._is_unsatisfiable = None
1451
+
1452
+ if isinstance(state, tuple):
1453
+ if len(state) == 2:
1454
+ # New format (26.2+): (specs, prereleases)
1455
+ specs, prereleases = state
1456
+ if (
1457
+ isinstance(specs, tuple)
1458
+ and all(isinstance(s, Specifier) for s in specs)
1459
+ and _validate_pre(prereleases)
1460
+ ):
1461
+ self._specs = specs
1462
+ self._prereleases = prereleases
1463
+ self._canonicalized = len(specs) <= 1
1464
+ self._has_arbitrary = any("===" in str(s) for s in specs)
1465
+ return
1466
+ if len(state) == 2 and isinstance(state[1], dict):
1467
+ # Format (packaging 26.0-26.1): (None, {slot: value}).
1468
+ _, slot_dict = state
1469
+ specs = slot_dict.get("_specs", ())
1470
+ prereleases = slot_dict.get("_prereleases")
1471
+ # Convert frozenset to tuple (26.0 stored as frozenset)
1472
+ if isinstance(specs, frozenset):
1473
+ specs = tuple(sorted(specs, key=str))
1474
+ if (
1475
+ isinstance(specs, tuple)
1476
+ and all(isinstance(s, Specifier) for s in specs)
1477
+ and _validate_pre(prereleases)
1478
+ ):
1479
+ self._specs = specs
1480
+ self._prereleases = prereleases
1481
+ self._canonicalized = len(self._specs) <= 1
1482
+ self._has_arbitrary = any("===" in str(s) for s in self._specs)
1483
+ return
1484
+ if isinstance(state, dict):
1485
+ # Old format (packaging <= 25.x, no __slots__): state is a plain dict.
1486
+ specs = state.get("_specs", ())
1487
+ prereleases = state.get("_prereleases")
1488
+ # Convert frozenset to tuple (26.0 stored as frozenset)
1489
+ if isinstance(specs, frozenset):
1490
+ specs = tuple(sorted(specs, key=str))
1491
+ if (
1492
+ isinstance(specs, tuple)
1493
+ and all(isinstance(s, Specifier) for s in specs)
1494
+ and _validate_pre(prereleases)
1495
+ ):
1496
+ self._specs = specs
1497
+ self._prereleases = prereleases
1498
+ self._canonicalized = len(self._specs) <= 1
1499
+ self._has_arbitrary = any("===" in str(s) for s in self._specs)
1500
+ return
1501
+
1502
+ raise TypeError(f"Cannot restore SpecifierSet from {state!r}")
1503
+
1504
+ def __repr__(self) -> str:
1505
+ """A representation of the specifier set that shows all internal state.
1506
+
1507
+ Note that the ordering of the individual specifiers within the set may not
1508
+ match the input string.
1509
+
1510
+ >>> SpecifierSet('>=1.0.0,!=2.0.0')
1511
+ <SpecifierSet('!=2.0.0,>=1.0.0')>
1512
+ >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False)
1513
+ <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=False)>
1514
+ >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True)
1515
+ <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=True)>
1516
+ """
1517
+ pre = (
1518
+ f", prereleases={self.prereleases!r}"
1519
+ if self._prereleases is not None
1520
+ else ""
1521
+ )
1522
+
1523
+ return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
1524
+
1525
+ def __str__(self) -> str:
1526
+ """A string representation of the specifier set that can be round-tripped.
1527
+
1528
+ Note that the ordering of the individual specifiers within the set may not
1529
+ match the input string.
1530
+
1531
+ >>> str(SpecifierSet(">=1.0.0,!=1.0.1"))
1532
+ '!=1.0.1,>=1.0.0'
1533
+ >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False))
1534
+ '!=1.0.1,>=1.0.0'
1535
+ """
1536
+ return ",".join(str(s) for s in self._canonical_specs())
1537
+
1538
+ def __hash__(self) -> int:
1539
+ return hash(self._canonical_specs())
1540
+
1541
+ def __and__(self, other: SpecifierSet | str) -> SpecifierSet:
1542
+ """Return a SpecifierSet which is a combination of the two sets.
1543
+
1544
+ :param other: The other object to combine with.
1545
+
1546
+ >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1'
1547
+ <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>
1548
+ >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1')
1549
+ <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>
1550
+ """
1551
+ if isinstance(other, str):
1552
+ other = SpecifierSet(other)
1553
+ elif not isinstance(other, SpecifierSet):
1554
+ return NotImplemented
1555
+
1556
+ specifier = SpecifierSet()
1557
+ specifier._specs = self._specs + other._specs
1558
+ specifier._canonicalized = len(specifier._specs) <= 1
1559
+ specifier._has_arbitrary = self._has_arbitrary or other._has_arbitrary
1560
+ specifier._resolved_ops = None
1561
+
1562
+ # Combine prerelease settings: use common or non-None value
1563
+ if self._prereleases is None or self._prereleases == other._prereleases:
1564
+ specifier._prereleases = other._prereleases
1565
+ elif other._prereleases is None:
1566
+ specifier._prereleases = self._prereleases
1567
+ else:
1568
+ raise ValueError(
1569
+ "Cannot combine SpecifierSets with True and False prerelease overrides."
1570
+ )
1571
+
1572
+ return specifier
1573
+
1574
+ def __eq__(self, other: object) -> bool:
1575
+ """Whether or not the two SpecifierSet-like objects are equal.
1576
+
1577
+ :param other: The other object to check against.
1578
+
1579
+ The value of :attr:`prereleases` is ignored.
1580
+
1581
+ >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1")
1582
+ True
1583
+ >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) ==
1584
+ ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True))
1585
+ True
1586
+ >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1"
1587
+ True
1588
+ >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0")
1589
+ False
1590
+ >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2")
1591
+ False
1592
+ """
1593
+ if isinstance(other, (str, Specifier)):
1594
+ other = SpecifierSet(str(other))
1595
+ elif not isinstance(other, SpecifierSet):
1596
+ return NotImplemented
1597
+
1598
+ return self._canonical_specs() == other._canonical_specs()
1599
+
1600
+ def __len__(self) -> int:
1601
+ """Returns the number of specifiers in this specifier set."""
1602
+ return len(self._specs)
1603
+
1604
+ def __iter__(self) -> Iterator[Specifier]:
1605
+ """
1606
+ Returns an iterator over all the underlying :class:`Specifier` instances
1607
+ in this specifier set.
1608
+
1609
+ >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str)
1610
+ [<Specifier('!=1.0.1')>, <Specifier('>=1.0.0')>]
1611
+ """
1612
+ return iter(self._specs)
1613
+
1614
+ def _get_ranges(self) -> Sequence[_VersionRange]:
1615
+ """Intersect all specifiers into a single list of version ranges.
1616
+
1617
+ Returns an empty list when unsatisfiable. ``===`` specs are
1618
+ modeled as full range; string matching is checked separately
1619
+ by :meth:`_check_arbitrary_unsatisfiable`.
1620
+ """
1621
+ specs = self._specs
1622
+
1623
+ result: Sequence[_VersionRange] | None = None
1624
+ for s in specs:
1625
+ if result is None:
1626
+ result = s._to_ranges()
1627
+ else:
1628
+ result = _intersect_ranges(result, s._to_ranges())
1629
+ if not result:
1630
+ break
1631
+
1632
+ if result is None: # pragma: no cover
1633
+ raise RuntimeError("_get_ranges called with no specs")
1634
+ return result
1635
+
1636
+ def is_unsatisfiable(self) -> bool:
1637
+ """Check whether this specifier set can never be satisfied.
1638
+
1639
+ Returns True if no version can satisfy all specifiers simultaneously.
1640
+
1641
+ >>> SpecifierSet(">=2.0,<1.0").is_unsatisfiable()
1642
+ True
1643
+ >>> SpecifierSet(">=1.0,<2.0").is_unsatisfiable()
1644
+ False
1645
+ >>> SpecifierSet("").is_unsatisfiable()
1646
+ False
1647
+ >>> SpecifierSet("==1.0,!=1.0").is_unsatisfiable()
1648
+ True
1649
+ """
1650
+ cached = self._is_unsatisfiable
1651
+ if cached is not None:
1652
+ return cached
1653
+
1654
+ if not self._specs:
1655
+ self._is_unsatisfiable = False
1656
+ return False
1657
+
1658
+ result = not self._get_ranges()
1659
+
1660
+ if not result:
1661
+ result = self._check_arbitrary_unsatisfiable()
1662
+
1663
+ if not result and self.prereleases is False:
1664
+ result = self._check_prerelease_only_ranges()
1665
+
1666
+ self._is_unsatisfiable = result
1667
+ return result
1668
+
1669
+ def _check_prerelease_only_ranges(self) -> bool:
1670
+ """With prereleases=False, check if every range contains only
1671
+ pre-release versions (which would be excluded from matching)."""
1672
+ for lower, upper in self._get_ranges():
1673
+ nearest = _nearest_non_prerelease(lower.version)
1674
+ if nearest is None:
1675
+ return False
1676
+ if upper.version is None or nearest < upper.version:
1677
+ return False
1678
+ if nearest == upper.version and upper.inclusive:
1679
+ return False
1680
+ return True
1681
+
1682
+ def _check_arbitrary_unsatisfiable(self) -> bool:
1683
+ """Check === (arbitrary equality) specs for unsatisfiability.
1684
+
1685
+ === uses case-insensitive string comparison, so the only candidate
1686
+ that can match ``===V`` is the literal string V. This method
1687
+ checks whether that candidate is excluded by other specifiers.
1688
+ """
1689
+ arbitrary = [s for s in self._specs if s.operator == "==="]
1690
+ if not arbitrary:
1691
+ return False
1692
+
1693
+ # Multiple === must agree on the same string (case-insensitive).
1694
+ first = arbitrary[0].version.lower()
1695
+ if any(s.version.lower() != first for s in arbitrary[1:]):
1696
+ return True
1697
+
1698
+ # The sole candidate is the === version string. Check whether
1699
+ # it can satisfy every standard spec.
1700
+ candidate = _coerce_version(arbitrary[0].version)
1701
+
1702
+ # With prereleases=False, a prerelease candidate is excluded
1703
+ # by contains() before the === string check even runs.
1704
+ if (
1705
+ self.prereleases is False
1706
+ and candidate is not None
1707
+ and candidate.is_prerelease
1708
+ ):
1709
+ return True
1710
+
1711
+ standard = [s for s in self._specs if s.operator != "==="]
1712
+ if not standard:
1713
+ return False
1714
+
1715
+ if candidate is None:
1716
+ # Unparsable string cannot satisfy any standard spec.
1717
+ return True
1718
+
1719
+ return not all(s.contains(candidate) for s in standard)
1720
+
1721
+ def __contains__(self, item: UnparsedVersion) -> bool:
1722
+ """Return whether or not the item is contained in this specifier.
1723
+
1724
+ :param item: The item to check for.
1725
+
1726
+ This is used for the ``in`` operator and behaves the same as
1727
+ :meth:`contains` with no ``prereleases`` argument passed.
1728
+
1729
+ >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1")
1730
+ True
1731
+ >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1")
1732
+ True
1733
+ >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1")
1734
+ False
1735
+ >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1")
1736
+ True
1737
+ >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)
1738
+ True
1739
+ """
1740
+ return self.contains(item)
1741
+
1742
+ def contains(
1743
+ self,
1744
+ item: UnparsedVersion,
1745
+ prereleases: bool | None = None,
1746
+ installed: bool | None = None,
1747
+ ) -> bool:
1748
+ """Return whether or not the item is contained in this SpecifierSet.
1749
+
1750
+ :param item:
1751
+ The item to check for, which can be a version string or a
1752
+ :class:`Version` instance.
1753
+ :param prereleases:
1754
+ Whether or not to match prereleases with this SpecifierSet. If set to
1755
+ ``None`` (the default), it will follow the recommendation from :pep:`440`
1756
+ and match prereleases, as there are no other versions.
1757
+ :param installed:
1758
+ Whether or not the item is installed. If set to ``True``, it will
1759
+ accept prerelease versions even if the specifier does not allow them.
1760
+
1761
+ >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3")
1762
+ True
1763
+ >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3"))
1764
+ True
1765
+ >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1")
1766
+ False
1767
+ >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1")
1768
+ True
1769
+ >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False).contains("1.3.0a1")
1770
+ False
1771
+ >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True)
1772
+ True
1773
+ """
1774
+ version = _coerce_version(item)
1775
+
1776
+ if version is not None and installed and version.is_prerelease:
1777
+ prereleases = True
1778
+
1779
+ # When item is a string and === is involved, keep it as-is
1780
+ # so the comparison isn't done against the normalized form.
1781
+ if version is None or (self._has_arbitrary and not isinstance(item, Version)):
1782
+ check_item = item
1783
+ else:
1784
+ check_item = version
1785
+ return bool(list(self.filter([check_item], prereleases=prereleases)))
1786
+
1787
+ @typing.overload
1788
+ def filter(
1789
+ self,
1790
+ iterable: Iterable[UnparsedVersionVar],
1791
+ prereleases: bool | None = None,
1792
+ key: None = ...,
1793
+ ) -> Iterator[UnparsedVersionVar]: ...
1794
+
1795
+ @typing.overload
1796
+ def filter(
1797
+ self,
1798
+ iterable: Iterable[T],
1799
+ prereleases: bool | None = None,
1800
+ key: Callable[[T], UnparsedVersion] = ...,
1801
+ ) -> Iterator[T]: ...
1802
+
1803
+ def filter(
1804
+ self,
1805
+ iterable: Iterable[Any],
1806
+ prereleases: bool | None = None,
1807
+ key: Callable[[Any], UnparsedVersion] | None = None,
1808
+ ) -> Iterator[Any]:
1809
+ """Filter items in the given iterable, that match the specifiers in this set.
1810
+
1811
+ :param iterable:
1812
+ An iterable that can contain version strings and :class:`Version` instances.
1813
+ The items in the iterable will be filtered according to the specifier.
1814
+ :param prereleases:
1815
+ Whether or not to allow prereleases in the returned iterator. If set to
1816
+ ``None`` (the default), it will follow the recommendation from :pep:`440`
1817
+ and match prereleases if there are no other versions.
1818
+ :param key:
1819
+ A callable that takes a single argument (an item from the iterable) and
1820
+ returns a version string or :class:`Version` instance to be used for
1821
+ filtering.
1822
+
1823
+ >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
1824
+ ['1.3']
1825
+ >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")]))
1826
+ ['1.3', <Version('1.4')>]
1827
+ >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"]))
1828
+ ['1.5a1']
1829
+ >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
1830
+ ['1.3', '1.5a1']
1831
+ >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
1832
+ ['1.3', '1.5a1']
1833
+ >>> list(SpecifierSet(">=1.2.3").filter(
1834
+ ... [{"ver": "1.2"}, {"ver": "1.3"}],
1835
+ ... key=lambda x: x["ver"]))
1836
+ [{'ver': '1.3'}]
1837
+
1838
+ An "empty" SpecifierSet will filter items based on the presence of prerelease
1839
+ versions in the set.
1840
+
1841
+ >>> list(SpecifierSet("").filter(["1.3", "1.5a1"]))
1842
+ ['1.3']
1843
+ >>> list(SpecifierSet("").filter(["1.5a1"]))
1844
+ ['1.5a1']
1845
+ >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"]))
1846
+ ['1.3', '1.5a1']
1847
+ >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True))
1848
+ ['1.3', '1.5a1']
1849
+ """
1850
+ # Determine if we're forcing a prerelease or not, if we're not forcing
1851
+ # one for this particular filter call, then we'll use whatever the
1852
+ # SpecifierSet thinks for whether or not we should support prereleases.
1853
+ if prereleases is None and self.prereleases is not None:
1854
+ prereleases = self.prereleases
1855
+
1856
+ # Filter versions that match all specifiers using Cost Based Ordering.
1857
+ if self._specs:
1858
+ # When prereleases is None, we need to let all versions through
1859
+ # the individual filters, then decide about prereleases at the end
1860
+ # based on whether any non-prereleases matched ALL specs.
1861
+
1862
+ # Fast path: single specifier, delegate directly.
1863
+ if len(self._specs) == 1:
1864
+ filtered = self._specs[0].filter(
1865
+ iterable,
1866
+ prereleases=True if prereleases is None else prereleases,
1867
+ key=key,
1868
+ )
1869
+ else:
1870
+ filtered = self._filter_versions(
1871
+ iterable,
1872
+ key,
1873
+ prereleases=True if prereleases is None else prereleases,
1874
+ )
1875
+
1876
+ if prereleases is not None:
1877
+ return filtered
1878
+
1879
+ return _pep440_filter_prereleases(filtered, key)
1880
+
1881
+ # Handle Empty SpecifierSet.
1882
+ if prereleases is True:
1883
+ return iter(iterable)
1884
+
1885
+ if prereleases is False:
1886
+ return (
1887
+ item
1888
+ for item in iterable
1889
+ if (
1890
+ (version := _coerce_version(item if key is None else key(item)))
1891
+ is None
1892
+ or not version.is_prerelease
1893
+ )
1894
+ )
1895
+
1896
+ # PEP 440: exclude prereleases unless no final releases matched
1897
+ return _pep440_filter_prereleases(iterable, key)
1898
+
1899
+ def _filter_versions(
1900
+ self,
1901
+ iterable: Iterable[Any],
1902
+ key: Callable[[Any], UnparsedVersion] | None,
1903
+ prereleases: bool | None = None,
1904
+ ) -> Iterator[Any]:
1905
+ """Filter versions against all specifiers in a single pass.
1906
+
1907
+ Uses Cost Based Ordering: specifiers are sorted by _operator_cost so
1908
+ that cheap range operators reject versions early, avoiding expensive
1909
+ wildcard or compatible operators on versions that would have been
1910
+ rejected anyway.
1911
+ """
1912
+ # Pre-resolve operators and sort (cached after first call).
1913
+ if self._resolved_ops is None:
1914
+ self._resolved_ops = sorted(
1915
+ (
1916
+ (spec._get_operator(spec.operator), spec.version, spec.operator)
1917
+ for spec in self._specs
1918
+ ),
1919
+ key=_operator_cost,
1920
+ )
1921
+ ops = self._resolved_ops
1922
+ exclude_prereleases = prereleases is False
1923
+
1924
+ for item in iterable:
1925
+ parsed = _coerce_version(item if key is None else key(item))
1926
+
1927
+ if parsed is None:
1928
+ # Only === can match non-parseable versions.
1929
+ if all(
1930
+ op == "===" and str(item).lower() == ver.lower()
1931
+ for _, ver, op in ops
1932
+ ):
1933
+ yield item
1934
+ elif exclude_prereleases and parsed.is_prerelease:
1935
+ pass
1936
+ elif all(
1937
+ str(item if key is None else key(item)).lower() == ver.lower()
1938
+ if op == "==="
1939
+ else op_fn(parsed, ver)
1940
+ for op_fn, ver, op in ops
1941
+ ):
1942
+ # Short-circuits on the first failing operator.
1943
+ yield item
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/tags.py ADDED
@@ -0,0 +1,932 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file is dual licensed under the terms of the Apache License, Version
2
+ # 2.0, and the BSD License. See the LICENSE file in the root of this repository
3
+ # for complete details.
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ import operator
9
+ import platform
10
+ import re
11
+ import struct
12
+ import subprocess
13
+ import sys
14
+ import sysconfig
15
+ from importlib.machinery import EXTENSION_SUFFIXES
16
+ from typing import (
17
+ TYPE_CHECKING,
18
+ Iterable,
19
+ Iterator,
20
+ Sequence,
21
+ Tuple,
22
+ TypeVar,
23
+ cast,
24
+ )
25
+
26
+ from . import _manylinux, _musllinux
27
+
28
+ if TYPE_CHECKING:
29
+ from collections.abc import Callable, Iterable
30
+ from typing import AbstractSet
31
+
32
+
33
+ __all__ = [
34
+ "INTERPRETER_SHORT_NAMES",
35
+ "AppleVersion",
36
+ "PythonVersion",
37
+ "Tag",
38
+ "UnsortedTagsError",
39
+ "android_platforms",
40
+ "compatible_tags",
41
+ "cpython_tags",
42
+ "create_compatible_tags_selector",
43
+ "generic_tags",
44
+ "interpreter_name",
45
+ "interpreter_version",
46
+ "ios_platforms",
47
+ "mac_platforms",
48
+ "parse_tag",
49
+ "platform_tags",
50
+ "sys_tags",
51
+ ]
52
+
53
+
54
+ def __dir__() -> list[str]:
55
+ return __all__
56
+
57
+
58
+ logger = logging.getLogger(__name__)
59
+
60
+ PythonVersion = Sequence[int]
61
+ AppleVersion = Tuple[int, int]
62
+ _T = TypeVar("_T")
63
+
64
+ INTERPRETER_SHORT_NAMES: dict[str, str] = {
65
+ "python": "py", # Generic.
66
+ "cpython": "cp",
67
+ "pypy": "pp",
68
+ "ironpython": "ip",
69
+ "jython": "jy",
70
+ }
71
+
72
+
73
+ # This function can be unit tested without reloading the module
74
+ # (Unlike _32_BIT_INTERPRETER)
75
+ def _compute_32_bit_interpreter() -> bool:
76
+ return struct.calcsize("P") == 4
77
+
78
+
79
+ _32_BIT_INTERPRETER = _compute_32_bit_interpreter()
80
+
81
+
82
+ class UnsortedTagsError(ValueError):
83
+ """
84
+ Raised when a tag component is not in sorted order per PEP 425.
85
+ """
86
+
87
+
88
+ class Tag:
89
+ """
90
+ A representation of the tag triple for a wheel.
91
+
92
+ Instances are considered immutable and thus are hashable. Equality checking
93
+ is also supported.
94
+
95
+ Instances are safe to serialize with :mod:`pickle`. They use a stable
96
+ format so the same pickle can be loaded in future packaging releases.
97
+
98
+ .. versionchanged:: 26.2
99
+
100
+ Added a stable pickle format. Pickles created with packaging 26.2+ can
101
+ be unpickled with future releases. Backward compatibility with pickles
102
+ from pip._vendor.packaging < 26.2 is supported but may be removed in a future
103
+ release.
104
+ """
105
+
106
+ __slots__ = ["_abi", "_hash", "_interpreter", "_platform"]
107
+
108
+ def __init__(self, interpreter: str, abi: str, platform: str) -> None:
109
+ """
110
+ :param str interpreter: The interpreter name, e.g. ``"py"``
111
+ (see :attr:`INTERPRETER_SHORT_NAMES` for mapping
112
+ well-known interpreter names to their short names).
113
+ :param str abi: The ABI that a wheel supports, e.g. ``"cp37m"``.
114
+ :param str platform: The OS/platform the wheel supports,
115
+ e.g. ``"win_amd64"``.
116
+ """
117
+ self._interpreter = interpreter.lower()
118
+ self._abi = abi.lower()
119
+ self._platform = platform.lower()
120
+ # The __hash__ of every single element in a Set[Tag] will be evaluated each time
121
+ # that a set calls its `.disjoint()` method, which may be called hundreds of
122
+ # times when scanning a page of links for packages with tags matching that
123
+ # Set[Tag]. Pre-computing the value here produces significant speedups for
124
+ # downstream consumers.
125
+ self._hash = hash((self._interpreter, self._abi, self._platform))
126
+
127
+ @property
128
+ def interpreter(self) -> str:
129
+ """
130
+ The interpreter name, e.g. ``"py"`` (see
131
+ :attr:`INTERPRETER_SHORT_NAMES` for mapping well-known interpreter
132
+ names to their short names).
133
+ """
134
+ return self._interpreter
135
+
136
+ @property
137
+ def abi(self) -> str:
138
+ """
139
+ The supported ABI.
140
+ """
141
+ return self._abi
142
+
143
+ @property
144
+ def platform(self) -> str:
145
+ """
146
+ The OS/platform.
147
+ """
148
+ return self._platform
149
+
150
+ def __eq__(self, other: object) -> bool:
151
+ if not isinstance(other, Tag):
152
+ return NotImplemented
153
+
154
+ return (
155
+ (self._hash == other._hash) # Short-circuit ASAP for perf reasons.
156
+ and (self._platform == other._platform)
157
+ and (self._abi == other._abi)
158
+ and (self._interpreter == other._interpreter)
159
+ )
160
+
161
+ def __hash__(self) -> int:
162
+ return self._hash
163
+
164
+ def __str__(self) -> str:
165
+ return f"{self._interpreter}-{self._abi}-{self._platform}"
166
+
167
+ def __repr__(self) -> str:
168
+ return f"<{self} @ {id(self)}>"
169
+
170
+ def __getstate__(self) -> tuple[str, str, str]:
171
+ # Return state as a 3-item tuple: (interpreter, abi, platform).
172
+ # Cache member _hash is excluded and will be recomputed.
173
+ return (self._interpreter, self._abi, self._platform)
174
+
175
+ def __setstate__(self, state: object) -> None:
176
+ if isinstance(state, tuple):
177
+ if len(state) == 3 and all(isinstance(s, str) for s in state):
178
+ # New format (26.2+): (interpreter, abi, platform)
179
+ self._interpreter, self._abi, self._platform = state
180
+ self._hash = hash((self._interpreter, self._abi, self._platform))
181
+ return
182
+ if len(state) == 2 and isinstance(state[1], dict):
183
+ # Old format (packaging <= 26.1, __slots__): (None, {slot: value}).
184
+ _, slots = state
185
+ try:
186
+ interpreter = slots["_interpreter"]
187
+ abi = slots["_abi"]
188
+ platform = slots["_platform"]
189
+ except KeyError:
190
+ raise TypeError(f"Cannot restore Tag from {state!r}") from None
191
+ if not all(
192
+ isinstance(value, str) for value in (interpreter, abi, platform)
193
+ ):
194
+ raise TypeError(f"Cannot restore Tag from {state!r}")
195
+ self._interpreter = interpreter.lower()
196
+ self._abi = abi.lower()
197
+ self._platform = platform.lower()
198
+ self._hash = hash((self._interpreter, self._abi, self._platform))
199
+ return
200
+ raise TypeError(f"Cannot restore Tag from {state!r}")
201
+
202
+
203
+ def parse_tag(tag: str, *, validate_order: bool = False) -> frozenset[Tag]:
204
+ """
205
+ Parses the provided tag (e.g. `py3-none-any`) into a frozenset of
206
+ :class:`Tag` instances.
207
+
208
+ Returning a set is required due to the possibility that the tag is a
209
+ `compressed tag set`_, e.g. ``"py2.py3-none-any"`` which supports both
210
+ Python 2 and Python 3.
211
+
212
+ If **validate_order** is true, compressed tag set components are checked
213
+ to be in sorted order as required by PEP 425.
214
+
215
+ :param str tag: The tag to parse, e.g. ``"py3-none-any"``.
216
+ :param bool validate_order: Check whether compressed tag set components
217
+ are in sorted order.
218
+ :raises UnsortedTagsError: If **validate_order** is true and any compressed tag
219
+ set component is not in sorted order.
220
+
221
+ .. versionadded:: 26.1
222
+ The *validate_order* parameter.
223
+ """
224
+ tags = set()
225
+ interpreters, abis, platforms = tag.split("-")
226
+ if validate_order:
227
+ for component in (interpreters, abis, platforms):
228
+ parts = component.split(".")
229
+ if parts != sorted(parts):
230
+ raise UnsortedTagsError(
231
+ f"Tag component {component!r} is not in sorted order per PEP 425"
232
+ )
233
+ for interpreter in interpreters.split("."):
234
+ for abi in abis.split("."):
235
+ for platform_ in platforms.split("."):
236
+ tags.add(Tag(interpreter, abi, platform_))
237
+ return frozenset(tags)
238
+
239
+
240
+ def _get_config_var(name: str, warn: bool = False) -> int | str | None:
241
+ value: int | str | None = sysconfig.get_config_var(name)
242
+ if value is None and warn:
243
+ logger.debug(
244
+ "Config variable '%s' is unset, Python ABI tag may be incorrect", name
245
+ )
246
+ return value
247
+
248
+
249
+ def _normalize_string(string: str) -> str:
250
+ return string.replace(".", "_").replace("-", "_").replace(" ", "_")
251
+
252
+
253
+ def _is_threaded_cpython(abis: list[str]) -> bool:
254
+ """
255
+ Determine if the ABI corresponds to a threaded (`--disable-gil`) build.
256
+
257
+ The threaded builds are indicated by a "t" in the abiflags.
258
+ """
259
+ if len(abis) == 0:
260
+ return False
261
+ # expect e.g., cp313
262
+ m = re.match(r"cp\d+(.*)", abis[0])
263
+ if not m:
264
+ return False
265
+ abiflags = m.group(1)
266
+ return "t" in abiflags
267
+
268
+
269
+ def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool:
270
+ """
271
+ Determine if the Python version supports abi3.
272
+
273
+ PEP 384 was first implemented in Python 3.2. The free-threaded
274
+ builds do not support abi3.
275
+ """
276
+ return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading
277
+
278
+
279
+ def _abi3t_applies(python_version: PythonVersion, threading: bool) -> bool:
280
+ """
281
+ Determine if the Python version supports abi3t.
282
+
283
+ PEP 803 was first implemented in Python 3.15 but, per PEP 803, this
284
+ returns tags going back to Python 3.2 to mirror the abi3
285
+ implementation and leave open the possibility of abi3t wheels
286
+ supporting older Python versions.
287
+
288
+ """
289
+ return len(python_version) > 1 and tuple(python_version) >= (3, 2) and threading
290
+
291
+
292
+ def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]:
293
+ py_version = tuple(py_version) # To allow for version comparison.
294
+ abis = []
295
+ version = _version_nodot(py_version[:2])
296
+ threading = debug = pymalloc = ucs4 = ""
297
+ with_debug = _get_config_var("Py_DEBUG", warn)
298
+ has_refcount = hasattr(sys, "gettotalrefcount")
299
+ # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled
300
+ # extension modules is the best option.
301
+ # https://github.com/pypa/pip/issues/3383#issuecomment-173267692
302
+ has_ext = "_d.pyd" in EXTENSION_SUFFIXES
303
+ if with_debug or (with_debug is None and (has_refcount or has_ext)):
304
+ debug = "d"
305
+ if py_version >= (3, 13) and _get_config_var("Py_GIL_DISABLED", warn):
306
+ threading = "t"
307
+ if py_version < (3, 8):
308
+ with_pymalloc = _get_config_var("WITH_PYMALLOC", warn)
309
+ if with_pymalloc or with_pymalloc is None:
310
+ pymalloc = "m"
311
+ if py_version < (3, 3):
312
+ unicode_size = _get_config_var("Py_UNICODE_SIZE", warn)
313
+ if unicode_size == 4 or (
314
+ unicode_size is None and sys.maxunicode == 0x10FFFF
315
+ ):
316
+ ucs4 = "u"
317
+ elif debug:
318
+ # Debug builds can also load "normal" extension modules.
319
+ # We can also assume no UCS-4 or pymalloc requirement.
320
+ abis.append(f"cp{version}{threading}")
321
+ abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}")
322
+ return abis
323
+
324
+
325
+ def cpython_tags(
326
+ python_version: PythonVersion | None = None,
327
+ abis: Iterable[str] | None = None,
328
+ platforms: Iterable[str] | None = None,
329
+ *,
330
+ warn: bool = False,
331
+ ) -> Iterator[Tag]:
332
+ """
333
+ Yields the tags for the CPython interpreter.
334
+
335
+ The specific tags generated are:
336
+
337
+ - ``cp<python_version>-<abi>-<platform>``
338
+ - ``cp<python_version>-<stable_abi>-<platform>``
339
+ - ``cp<python_version>-none-<platform>``
340
+ - ``cp<older version>-<stable_abi>-<platform>`` where "older version" is all older
341
+ minor versions down to Python 3.2 (when ``abi3`` was introduced)
342
+
343
+ If ``python_version`` only provides a major-only version then only
344
+ user-provided ABIs via ``abis`` and the ``none`` ABI will be used.
345
+
346
+ The ``stable_abi`` will be either ``abi3`` or ``abi3t`` if `abi` is a
347
+ GIL-enabled ABI like `"cp315"` or a free-threaded ABI like `"cp315t"`,
348
+ respectively.
349
+
350
+ :param Sequence python_version: A one- or two-item sequence representing the
351
+ targeted Python version. Defaults to
352
+ ``sys.version_info[:2]``.
353
+ :param Iterable abis: Iterable of compatible ABIs. Defaults to the ABIs
354
+ compatible with the current system.
355
+ :param Iterable platforms: Iterable of compatible platforms. Defaults to the
356
+ platforms compatible with the current system.
357
+ :param bool warn: Whether warnings should be logged. Defaults to ``False``.
358
+ """
359
+ if not python_version:
360
+ python_version = sys.version_info[:2]
361
+
362
+ interpreter = f"cp{_version_nodot(python_version[:2])}"
363
+
364
+ if abis is None:
365
+ abis = _cpython_abis(python_version, warn) if len(python_version) > 1 else []
366
+ abis = list(abis)
367
+ # 'abi3' and 'none' are explicitly handled later.
368
+ for explicit_abi in ("abi3", "none"):
369
+ try:
370
+ abis.remove(explicit_abi)
371
+ except ValueError: # noqa: PERF203
372
+ pass
373
+
374
+ platforms = list(platforms or platform_tags())
375
+ for abi in abis:
376
+ for platform_ in platforms:
377
+ yield Tag(interpreter, abi, platform_)
378
+
379
+ threading = _is_threaded_cpython(abis)
380
+ use_abi3 = _abi3_applies(python_version, threading)
381
+ use_abi3t = _abi3t_applies(python_version, threading)
382
+
383
+ if use_abi3:
384
+ yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms)
385
+ if use_abi3t:
386
+ yield from (Tag(interpreter, "abi3t", platform_) for platform_ in platforms)
387
+
388
+ yield from (Tag(interpreter, "none", platform_) for platform_ in platforms)
389
+
390
+ if use_abi3 or use_abi3t:
391
+ for minor_version in range(python_version[1] - 1, 1, -1):
392
+ for platform_ in platforms:
393
+ version = _version_nodot((python_version[0], minor_version))
394
+ interpreter = f"cp{version}"
395
+ if use_abi3:
396
+ yield Tag(interpreter, "abi3", platform_)
397
+ if use_abi3t:
398
+ # Support for abi3t was introduced in Python 3.15, but in
399
+ # principle abi3t wheels are possible for older limited API
400
+ # versions, so allow things like ("cp37", "abi3t", "platform")
401
+ yield Tag(interpreter, "abi3t", platform_)
402
+
403
+
404
+ def _generic_abi() -> list[str]:
405
+ """
406
+ Return the ABI tag based on EXT_SUFFIX.
407
+ """
408
+ # The following are examples of `EXT_SUFFIX`.
409
+ # We want to keep the parts which are related to the ABI and remove the
410
+ # parts which are related to the platform:
411
+ # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310
412
+ # - mac: '.cpython-310-darwin.so' => cp310
413
+ # - win: '.cp310-win_amd64.pyd' => cp310
414
+ # - win: '.pyd' => cp37 (uses _cpython_abis())
415
+ # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73
416
+ # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib'
417
+ # => graalpy_38_native
418
+
419
+ ext_suffix = _get_config_var("EXT_SUFFIX", warn=True)
420
+ if not isinstance(ext_suffix, str) or ext_suffix[0] != ".":
421
+ raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')")
422
+ parts = ext_suffix.split(".")
423
+ if len(parts) < 3:
424
+ # CPython3.7 and earlier uses ".pyd" on Windows.
425
+ return _cpython_abis(sys.version_info[:2])
426
+ soabi = parts[1]
427
+ if soabi.startswith("cpython"):
428
+ # non-windows
429
+ abi = "cp" + soabi.split("-")[1]
430
+ elif soabi.startswith("cp"):
431
+ # windows
432
+ abi = soabi.split("-")[0]
433
+ elif soabi.startswith("pypy"):
434
+ abi = "-".join(soabi.split("-")[:2])
435
+ elif soabi.startswith("graalpy"):
436
+ abi = "-".join(soabi.split("-")[:3])
437
+ elif soabi:
438
+ # pyston, ironpython, others?
439
+ abi = soabi
440
+ else:
441
+ return []
442
+ return [_normalize_string(abi)]
443
+
444
+
445
+ def generic_tags(
446
+ interpreter: str | None = None,
447
+ abis: Iterable[str] | None = None,
448
+ platforms: Iterable[str] | None = None,
449
+ *,
450
+ warn: bool = False,
451
+ ) -> Iterator[Tag]:
452
+ """
453
+ Yields the tags for an interpreter which requires no specialization.
454
+
455
+ This function should be used if one of the other interpreter-specific
456
+ functions provided by this module is not appropriate (i.e. not calculating
457
+ tags for a CPython interpreter).
458
+
459
+ The specific tags generated are:
460
+
461
+ - ``<interpreter>-<abi>-<platform>``
462
+
463
+ The ``"none"`` ABI will be added if it was not explicitly provided.
464
+
465
+ :param str interpreter: The name of the interpreter. Defaults to being
466
+ calculated.
467
+ :param Iterable abis: Iterable of compatible ABIs. Defaults to the ABIs
468
+ compatible with the current system.
469
+ :param Iterable platforms: Iterable of compatible platforms. Defaults to the
470
+ platforms compatible with the current system.
471
+ :param bool warn: Whether warnings should be logged. Defaults to ``False``.
472
+ """
473
+ if not interpreter:
474
+ interp_name = interpreter_name()
475
+ interp_version = interpreter_version(warn=warn)
476
+ interpreter = f"{interp_name}{interp_version}"
477
+ abis = _generic_abi() if abis is None else list(abis)
478
+ platforms = list(platforms or platform_tags())
479
+ if "none" not in abis:
480
+ abis.append("none")
481
+ for abi in abis:
482
+ for platform_ in platforms:
483
+ yield Tag(interpreter, abi, platform_)
484
+
485
+
486
+ def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]:
487
+ """
488
+ Yields Python versions in descending order.
489
+
490
+ After the latest version, the major-only version will be yielded, and then
491
+ all previous versions of that major version.
492
+ """
493
+ if len(py_version) > 1:
494
+ yield f"py{_version_nodot(py_version[:2])}"
495
+ yield f"py{py_version[0]}"
496
+ if len(py_version) > 1:
497
+ for minor in range(py_version[1] - 1, -1, -1):
498
+ yield f"py{_version_nodot((py_version[0], minor))}"
499
+
500
+
501
+ def compatible_tags(
502
+ python_version: PythonVersion | None = None,
503
+ interpreter: str | None = None,
504
+ platforms: Iterable[str] | None = None,
505
+ ) -> Iterator[Tag]:
506
+ """
507
+ Yields the tags for an interpreter compatible with the Python version
508
+ specified by ``python_version``.
509
+
510
+ The specific tags generated are:
511
+
512
+ - ``py*-none-<platform>``
513
+ - ``<interpreter>-none-any`` if ``interpreter`` is provided
514
+ - ``py*-none-any``
515
+
516
+ :param Sequence python_version: A one- or two-item sequence representing the
517
+ compatible version of Python. Defaults to
518
+ ``sys.version_info[:2]``.
519
+ :param str interpreter: The name of the interpreter (if known), e.g.
520
+ ``"cp38"``. Defaults to the current interpreter.
521
+ :param Iterable platforms: Iterable of compatible platforms. Defaults to the
522
+ platforms compatible with the current system.
523
+ """
524
+ if not python_version:
525
+ python_version = sys.version_info[:2]
526
+ platforms = list(platforms or platform_tags())
527
+ for version in _py_interpreter_range(python_version):
528
+ for platform_ in platforms:
529
+ yield Tag(version, "none", platform_)
530
+ if interpreter:
531
+ yield Tag(interpreter, "none", "any")
532
+ for version in _py_interpreter_range(python_version):
533
+ yield Tag(version, "none", "any")
534
+
535
+
536
+ def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str:
537
+ if not is_32bit:
538
+ return arch
539
+
540
+ if arch.startswith("ppc"):
541
+ return "ppc"
542
+
543
+ return "i386"
544
+
545
+
546
+ def _mac_binary_formats(version: AppleVersion, cpu_arch: str) -> list[str]:
547
+ formats = [cpu_arch]
548
+ if cpu_arch == "x86_64":
549
+ if version < (10, 4):
550
+ return []
551
+ formats.extend(["intel", "fat64", "fat32"])
552
+
553
+ elif cpu_arch == "i386":
554
+ if version < (10, 4):
555
+ return []
556
+ formats.extend(["intel", "fat32", "fat"])
557
+
558
+ elif cpu_arch == "ppc64":
559
+ # TODO: Need to care about 32-bit PPC for ppc64 through 10.2?
560
+ if version > (10, 5) or version < (10, 4):
561
+ return []
562
+ formats.append("fat64")
563
+
564
+ elif cpu_arch == "ppc":
565
+ if version > (10, 6):
566
+ return []
567
+ formats.extend(["fat32", "fat"])
568
+
569
+ if cpu_arch in {"arm64", "x86_64"}:
570
+ formats.append("universal2")
571
+
572
+ if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}:
573
+ formats.append("universal")
574
+
575
+ return formats
576
+
577
+
578
+ def mac_platforms(
579
+ version: AppleVersion | None = None, arch: str | None = None
580
+ ) -> Iterator[str]:
581
+ """
582
+ Yields the :attr:`~Tag.platform` tags for macOS.
583
+
584
+ The `version` parameter is a two-item tuple specifying the macOS version to
585
+ generate platform tags for. The `arch` parameter is the CPU architecture to
586
+ generate platform tags for. Both parameters default to the appropriate value
587
+ for the current system.
588
+
589
+ :param tuple version: A two-item tuple representing the version of macOS.
590
+ Defaults to the current system's version.
591
+ :param str arch: The CPU architecture. Defaults to the architecture of the
592
+ current system, e.g. ``"x86_64"``.
593
+
594
+ .. note::
595
+ Equivalent support for the other major platforms is purposefully not
596
+ provided:
597
+
598
+ - On Windows, platform compatibility is statically specified
599
+ - On Linux, code must be run on the system itself to determine
600
+ compatibility
601
+ """
602
+ version_str, _, cpu_arch = platform.mac_ver()
603
+ if version is None:
604
+ version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2])))
605
+ if version == (10, 16):
606
+ # When built against an older macOS SDK, Python will report macOS 10.16
607
+ # instead of the real version.
608
+ version_str = subprocess.run(
609
+ [
610
+ sys.executable,
611
+ "-sS",
612
+ "-c",
613
+ "import platform; print(platform.mac_ver()[0])",
614
+ ],
615
+ check=True,
616
+ env={"SYSTEM_VERSION_COMPAT": "0"},
617
+ stdout=subprocess.PIPE,
618
+ text=True,
619
+ ).stdout
620
+ version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2])))
621
+
622
+ if arch is None:
623
+ arch = _mac_arch(cpu_arch)
624
+
625
+ if (10, 0) <= version < (11, 0):
626
+ # Prior to Mac OS 11, each yearly release of Mac OS bumped the
627
+ # "minor" version number. The major version was always 10.
628
+ major_version = 10
629
+ for minor_version in range(version[1], -1, -1):
630
+ compat_version = major_version, minor_version
631
+ binary_formats = _mac_binary_formats(compat_version, arch)
632
+ for binary_format in binary_formats:
633
+ yield f"macosx_{major_version}_{minor_version}_{binary_format}"
634
+
635
+ if version >= (11, 0):
636
+ # Starting with Mac OS 11, each yearly release bumps the major version
637
+ # number. The minor versions are now the midyear updates.
638
+ minor_version = 0
639
+ for major_version in range(version[0], 10, -1):
640
+ compat_version = major_version, minor_version
641
+ binary_formats = _mac_binary_formats(compat_version, arch)
642
+ for binary_format in binary_formats:
643
+ yield f"macosx_{major_version}_{minor_version}_{binary_format}"
644
+
645
+ if version >= (11, 0):
646
+ # Mac OS 11 on x86_64 is compatible with binaries from previous releases.
647
+ # Arm64 support was introduced in 11.0, so no Arm binaries from previous
648
+ # releases exist.
649
+ #
650
+ # However, the "universal2" binary format can have a
651
+ # macOS version earlier than 11.0 when the x86_64 part of the binary supports
652
+ # that version of macOS.
653
+ major_version = 10
654
+ if arch == "x86_64":
655
+ for minor_version in range(16, 3, -1):
656
+ compat_version = major_version, minor_version
657
+ binary_formats = _mac_binary_formats(compat_version, arch)
658
+ for binary_format in binary_formats:
659
+ yield f"macosx_{major_version}_{minor_version}_{binary_format}"
660
+ else:
661
+ for minor_version in range(16, 3, -1):
662
+ compat_version = major_version, minor_version
663
+ binary_format = "universal2"
664
+ yield f"macosx_{major_version}_{minor_version}_{binary_format}"
665
+
666
+
667
+ def ios_platforms(
668
+ version: AppleVersion | None = None, multiarch: str | None = None
669
+ ) -> Iterator[str]:
670
+ """
671
+
672
+ Yields the :attr:`~Tag.platform` tags for iOS.
673
+
674
+ :param tuple version: A two-item tuple representing the version of iOS.
675
+ Defaults to the current system's version.
676
+ :param str multiarch: The CPU architecture+ABI to be used. This should be in
677
+ the format by ``sys.implementation._multiarch`` (e.g.,
678
+ ``arm64_iphoneos`` or ``x86_64_iphonesimulator``).
679
+ Defaults to the current system's multiarch value.
680
+
681
+ .. note::
682
+ Behavior of this method is undefined if invoked on non-iOS platforms
683
+ without providing explicit version and multiarch arguments.
684
+ """
685
+ if version is None:
686
+ # if iOS is the current platform, ios_ver *must* be defined. However,
687
+ # it won't exist for CPython versions before 3.13, which causes a mypy
688
+ # error.
689
+ _, release, _, _ = platform.ios_ver() # type: ignore[attr-defined, unused-ignore]
690
+ version = cast("AppleVersion", tuple(map(int, release.split(".")[:2])))
691
+
692
+ if multiarch is None:
693
+ multiarch = sys.implementation._multiarch
694
+ multiarch = multiarch.replace("-", "_")
695
+
696
+ ios_platform_template = "ios_{major}_{minor}_{multiarch}"
697
+
698
+ # Consider any iOS major.minor version from the version requested, down to
699
+ # 12.0. 12.0 is the first iOS version that is known to have enough features
700
+ # to support CPython. Consider every possible minor release up to X.9. There
701
+ # highest the minor has ever gone is 8 (14.8 and 15.8) but having some extra
702
+ # candidates that won't ever match doesn't really hurt, and it saves us from
703
+ # having to keep an explicit list of known iOS versions in the code. Return
704
+ # the results descending order of version number.
705
+
706
+ # If the requested major version is less than 12, there won't be any matches.
707
+ if version[0] < 12:
708
+ return
709
+
710
+ # Consider the actual X.Y version that was requested.
711
+ yield ios_platform_template.format(
712
+ major=version[0], minor=version[1], multiarch=multiarch
713
+ )
714
+
715
+ # Consider every minor version from X.0 to the minor version prior to the
716
+ # version requested by the platform.
717
+ for minor in range(version[1] - 1, -1, -1):
718
+ yield ios_platform_template.format(
719
+ major=version[0], minor=minor, multiarch=multiarch
720
+ )
721
+
722
+ for major in range(version[0] - 1, 11, -1):
723
+ for minor in range(9, -1, -1):
724
+ yield ios_platform_template.format(
725
+ major=major, minor=minor, multiarch=multiarch
726
+ )
727
+
728
+
729
+ def android_platforms(
730
+ api_level: int | None = None, abi: str | None = None
731
+ ) -> Iterator[str]:
732
+ """
733
+ Yields the :attr:`~Tag.platform` tags for Android. If this function is invoked on
734
+ non-Android platforms, the ``api_level`` and ``abi`` arguments are required.
735
+
736
+ :param int api_level: The maximum `API level
737
+ <https://developer.android.com/tools/releases/platforms>`__ to return. Defaults
738
+ to the current system's version, as returned by ``platform.android_ver``.
739
+ :param str abi: The `Android ABI <https://developer.android.com/ndk/guides/abis>`__,
740
+ e.g. ``arm64_v8a``. Defaults to the current system's ABI , as returned by
741
+ ``sysconfig.get_platform``. Hyphens and periods will be replaced with
742
+ underscores.
743
+ """
744
+ if platform.system() != "Android" and (api_level is None or abi is None):
745
+ raise TypeError(
746
+ "on non-Android platforms, the api_level and abi arguments are required"
747
+ )
748
+
749
+ if api_level is None:
750
+ # Python 3.13 was the first version to return platform.system() == "Android",
751
+ # and also the first version to define platform.android_ver().
752
+ api_level = platform.android_ver().api_level # type: ignore[attr-defined]
753
+
754
+ if abi is None:
755
+ abi = sysconfig.get_platform().split("-")[-1]
756
+ abi = _normalize_string(abi)
757
+
758
+ # 16 is the minimum API level known to have enough features to support CPython
759
+ # without major patching. Yield every API level from the maximum down to the
760
+ # minimum, inclusive.
761
+ min_api_level = 16
762
+ for ver in range(api_level, min_api_level - 1, -1):
763
+ yield f"android_{ver}_{abi}"
764
+
765
+
766
+ def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]:
767
+ linux = _normalize_string(sysconfig.get_platform())
768
+ if not linux.startswith("linux_"):
769
+ # we should never be here, just yield the sysconfig one and return
770
+ yield linux
771
+ return
772
+ if is_32bit:
773
+ if linux == "linux_x86_64":
774
+ linux = "linux_i686"
775
+ elif linux == "linux_aarch64":
776
+ linux = "linux_armv8l"
777
+ _, arch = linux.split("_", 1)
778
+ archs = {"armv8l": ["armv8l", "armv7l"]}.get(arch, [arch])
779
+ yield from _manylinux.platform_tags(archs)
780
+ yield from _musllinux.platform_tags(archs)
781
+ for arch in archs:
782
+ yield f"linux_{arch}"
783
+
784
+
785
+ def _emscripten_platforms() -> Iterator[str]:
786
+ pyemscripten_platform_version = sysconfig.get_config_var(
787
+ "PYEMSCRIPTEN_PLATFORM_VERSION"
788
+ )
789
+ if pyemscripten_platform_version:
790
+ yield f"pyemscripten_{pyemscripten_platform_version}_wasm32"
791
+ yield from _generic_platforms()
792
+
793
+
794
+ def _generic_platforms() -> Iterator[str]:
795
+ yield _normalize_string(sysconfig.get_platform())
796
+
797
+
798
+ def platform_tags() -> Iterator[str]:
799
+ """
800
+ Yields the :attr:`~Tag.platform` tags for the running interpreter.
801
+ """
802
+ if platform.system() == "Darwin":
803
+ return mac_platforms()
804
+ elif platform.system() == "iOS":
805
+ return ios_platforms()
806
+ elif platform.system() == "Android":
807
+ return android_platforms()
808
+ elif platform.system() == "Linux":
809
+ return _linux_platforms()
810
+ elif platform.system() == "Emscripten":
811
+ return _emscripten_platforms()
812
+ else:
813
+ return _generic_platforms()
814
+
815
+
816
+ def interpreter_name() -> str:
817
+ """
818
+ Returns the name of the running interpreter.
819
+
820
+ Some implementations have a reserved, two-letter abbreviation which will
821
+ be returned when appropriate.
822
+
823
+ This typically acts as the prefix to the :attr:`~Tag.interpreter` tag.
824
+ """
825
+ name = sys.implementation.name
826
+ return INTERPRETER_SHORT_NAMES.get(name) or name
827
+
828
+
829
+ def interpreter_version(*, warn: bool = False) -> str:
830
+ """
831
+ Returns the running interpreter's version.
832
+
833
+ This typically acts as the suffix to the :attr:`~Tag.interpreter` tag.
834
+
835
+ :param bool warn: Whether warnings should be logged. Defaults to ``False``.
836
+ """
837
+ version = _get_config_var("py_version_nodot", warn=warn)
838
+ return str(version) if version else _version_nodot(sys.version_info[:2])
839
+
840
+
841
+ def _version_nodot(version: PythonVersion) -> str:
842
+ return "".join(map(str, version))
843
+
844
+
845
+ def sys_tags(*, warn: bool = False) -> Iterator[Tag]:
846
+ """
847
+ Yields the sequence of tag triples that the running interpreter supports.
848
+
849
+ The iterable is ordered so that the best-matching tag is first in the
850
+ sequence. The exact preferential order to tags is interpreter-specific, but
851
+ in general the tag importance is in the order of:
852
+
853
+ 1. Interpreter
854
+ 2. Platform
855
+ 3. ABI
856
+
857
+ This order is due to the fact that an ABI is inherently tied to the
858
+ platform, but platform-specific code is not necessarily tied to the ABI. The
859
+ interpreter is the most important tag as it dictates basic support for any
860
+ wheel.
861
+
862
+ The function returns an iterable in order to allow for the possible
863
+ short-circuiting of tag generation if the entire sequence is not necessary
864
+ and tag calculation happens to be expensive.
865
+
866
+ :param bool warn: Whether warnings should be logged. Defaults to ``False``.
867
+
868
+ .. versionchanged:: 21.3
869
+ Added the `pp3-none-any` tag (:issue:`311`).
870
+ .. versionchanged:: 27.0
871
+ Added the `abi3t` tag (:issue:`1099`).
872
+ """
873
+
874
+ interp_name = interpreter_name()
875
+ if interp_name == "cp":
876
+ yield from cpython_tags(warn=warn)
877
+ else:
878
+ yield from generic_tags()
879
+
880
+ if interp_name == "pp":
881
+ interp = "pp3"
882
+ elif interp_name == "cp":
883
+ interp = "cp" + interpreter_version(warn=warn)
884
+ else:
885
+ interp = None
886
+ yield from compatible_tags(interpreter=interp)
887
+
888
+
889
+ def create_compatible_tags_selector(
890
+ tags: Iterable[Tag],
891
+ ) -> Callable[[Iterable[tuple[_T, AbstractSet[Tag]]]], Iterator[_T]]:
892
+ """Create a callable to select things compatible with supported tags.
893
+
894
+ This function accepts an ordered sequence of tags, with the preferred
895
+ tags first.
896
+
897
+ The returned callable accepts an iterable of tuples (thing, set[Tag]),
898
+ and returns an iterator of things, with the things with the best
899
+ matching tags first.
900
+
901
+ Example to select compatible wheel filenames:
902
+
903
+ >>> from packaging import tags
904
+ >>> from packaging.utils import parse_wheel_filename
905
+ >>> selector = tags.create_compatible_tags_selector(tags.sys_tags())
906
+ >>> filenames = ["foo-1.0-py3-none-any.whl", "foo-1.0-py2-none-any.whl"]
907
+ >>> list(selector([
908
+ ... (filename, parse_wheel_filename(filename)[-1]) for filename in filenames
909
+ ... ]))
910
+ ['foo-1.0-py3-none-any.whl']
911
+
912
+ .. versionadded:: 26.1
913
+ """
914
+ tag_ranks: dict[Tag, int] = {}
915
+ for rank, tag in enumerate(tags):
916
+ tag_ranks.setdefault(tag, rank) # ignore duplicate tags, keep first
917
+ supported_tags = tag_ranks.keys()
918
+
919
+ def selector(
920
+ tagged_things: Iterable[tuple[_T, AbstractSet[Tag]]],
921
+ ) -> Iterator[_T]:
922
+ ranked_things: list[tuple[_T, int]] = []
923
+ for thing, thing_tags in tagged_things:
924
+ supported_thing_tags = thing_tags & supported_tags
925
+ if supported_thing_tags:
926
+ thing_rank = min(tag_ranks[t] for t in supported_thing_tags)
927
+ ranked_things.append((thing, thing_rank))
928
+ return iter(
929
+ thing for thing, _ in sorted(ranked_things, key=operator.itemgetter(1))
930
+ )
931
+
932
+ return selector
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/utils.py ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file is dual licensed under the terms of the Apache License, Version
2
+ # 2.0, and the BSD License. See the LICENSE file in the root of this repository
3
+ # for complete details.
4
+
5
+ from __future__ import annotations
6
+
7
+ import re
8
+ from typing import NewType, Tuple, Union, cast
9
+
10
+ from .tags import Tag, UnsortedTagsError, parse_tag
11
+ from .version import InvalidVersion, Version, _TrimmedRelease
12
+
13
+ __all__ = [
14
+ "BuildTag",
15
+ "InvalidName",
16
+ "InvalidSdistFilename",
17
+ "InvalidWheelFilename",
18
+ "NormalizedName",
19
+ "canonicalize_name",
20
+ "canonicalize_version",
21
+ "is_normalized_name",
22
+ "parse_sdist_filename",
23
+ "parse_wheel_filename",
24
+ ]
25
+
26
+
27
+ def __dir__() -> list[str]:
28
+ return __all__
29
+
30
+
31
+ BuildTag = Union[Tuple[()], Tuple[int, str]]
32
+
33
+ NormalizedName = NewType("NormalizedName", str)
34
+ """
35
+ A :class:`typing.NewType` of :class:`str`, representing a normalized name.
36
+ """
37
+
38
+
39
+ class InvalidName(ValueError):
40
+ """
41
+ An invalid distribution name; users should refer to the packaging user guide.
42
+ """
43
+
44
+
45
+ class InvalidWheelFilename(ValueError):
46
+ """
47
+ An invalid wheel filename was found, users should refer to PEP 427.
48
+ """
49
+
50
+
51
+ class InvalidSdistFilename(ValueError):
52
+ """
53
+ An invalid sdist filename was found, users should refer to the packaging user guide.
54
+ """
55
+
56
+
57
+ # Core metadata spec for `Name`
58
+ _validate_regex = re.compile(
59
+ r"[a-z0-9]|[a-z0-9][a-z0-9._-]*[a-z0-9]", re.IGNORECASE | re.ASCII
60
+ )
61
+ _normalized_regex = re.compile(r"[a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9]", re.ASCII)
62
+ # PEP 427: The build number must start with a digit.
63
+ _build_tag_regex = re.compile(r"(\d+)(.*)", re.ASCII)
64
+
65
+
66
+ def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName:
67
+ """
68
+ This function takes a valid Python package or extra name, and returns the
69
+ normalized form of it.
70
+
71
+ The return type is typed as :class:`NormalizedName`. This allows type
72
+ checkers to help require that a string has passed through this function
73
+ before use.
74
+
75
+ If **validate** is true, then the function will check if **name** is a valid
76
+ distribution name before normalizing.
77
+
78
+ :param str name: The name to normalize.
79
+ :param bool validate: Check whether the name is a valid distribution name.
80
+ :raises InvalidName: If **validate** is true and the name is not an
81
+ acceptable distribution name.
82
+
83
+ >>> from packaging.utils import canonicalize_name
84
+ >>> canonicalize_name("Django")
85
+ 'django'
86
+ >>> canonicalize_name("oslo.concurrency")
87
+ 'oslo-concurrency'
88
+ >>> canonicalize_name("requests")
89
+ 'requests'
90
+ """
91
+ if validate and not _validate_regex.fullmatch(name):
92
+ raise InvalidName(f"name is invalid: {name!r}")
93
+ # Ensure all ``.`` and ``_`` are ``-``
94
+ # Emulates ``re.sub(r"[-_.]+", "-", name).lower()`` from PEP 503
95
+ # Much faster than re, and even faster than str.translate
96
+ value = name.lower().replace("_", "-").replace(".", "-")
97
+ # Condense repeats (faster than regex)
98
+ while "--" in value:
99
+ value = value.replace("--", "-")
100
+ return cast("NormalizedName", value)
101
+
102
+
103
+ def is_normalized_name(name: str) -> bool:
104
+ """
105
+ Check if a name is already normalized (i.e. :func:`canonicalize_name` would
106
+ roundtrip to the same value).
107
+
108
+ :param str name: The name to check.
109
+
110
+ >>> from packaging.utils import is_normalized_name
111
+ >>> is_normalized_name("requests")
112
+ True
113
+ >>> is_normalized_name("Django")
114
+ False
115
+ """
116
+ return _normalized_regex.fullmatch(name) is not None
117
+
118
+
119
+ def canonicalize_version(
120
+ version: Version | str, *, strip_trailing_zero: bool = True
121
+ ) -> str:
122
+ """Return a canonical form of a version as a string.
123
+
124
+ This function takes a string representing a package version (or a
125
+ :class:`~packaging.version.Version` instance), and returns the
126
+ normalized form of it. By default, it strips trailing zeros from
127
+ the release segment.
128
+
129
+ >>> from packaging.utils import canonicalize_version
130
+ >>> canonicalize_version('1.0.1')
131
+ '1.0.1'
132
+
133
+ Per PEP 625, versions may have multiple canonical forms, differing
134
+ only by trailing zeros.
135
+
136
+ >>> canonicalize_version('1.0.0')
137
+ '1'
138
+ >>> canonicalize_version('1.0.0', strip_trailing_zero=False)
139
+ '1.0.0'
140
+
141
+ Invalid versions are returned unaltered.
142
+
143
+ >>> canonicalize_version('foo bar baz')
144
+ 'foo bar baz'
145
+
146
+ >>> canonicalize_version('1.4.0.0.0')
147
+ '1.4'
148
+ """
149
+ if isinstance(version, str):
150
+ try:
151
+ version = Version(version)
152
+ except InvalidVersion:
153
+ return str(version)
154
+ return str(_TrimmedRelease(version) if strip_trailing_zero else version)
155
+
156
+
157
+ def parse_wheel_filename(
158
+ filename: str,
159
+ *,
160
+ validate_order: bool = False,
161
+ ) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]:
162
+ """
163
+ This function takes the filename of a wheel file, and parses it,
164
+ returning a tuple of name, version, build number, and tags.
165
+
166
+ The name part of the tuple is normalized and typed as
167
+ :class:`NormalizedName`. The version portion is an instance of
168
+ :class:`~packaging.version.Version`. The build number is ``()`` if
169
+ there is no build number in the wheel filename, otherwise a
170
+ two-item tuple of an integer for the leading digits and
171
+ a string for the rest of the build number. The tags portion is a
172
+ frozen set of :class:`~packaging.tags.Tag` instances (as the tag
173
+ string format allows multiple tags to be combined into a single
174
+ string).
175
+
176
+ If **validate_order** is true, compressed tag set components are
177
+ checked to be in sorted order as required by PEP 425.
178
+
179
+ :param str filename: The name of the wheel file.
180
+ :param bool validate_order: Check whether compressed tag set components
181
+ are in sorted order.
182
+ :raises InvalidWheelFilename: If the filename in question
183
+ does not follow the :ref:`wheel specification
184
+ <pypug:binary-distribution-format>`.
185
+
186
+ >>> from packaging.utils import parse_wheel_filename
187
+ >>> from packaging.tags import Tag
188
+ >>> from packaging.version import Version
189
+ >>> name, ver, build, tags = parse_wheel_filename("foo-1.0-py3-none-any.whl")
190
+ >>> name
191
+ 'foo'
192
+ >>> ver == Version('1.0')
193
+ True
194
+ >>> tags == {Tag("py3", "none", "any")}
195
+ True
196
+ >>> not build
197
+ True
198
+
199
+ .. versionadded:: 26.1
200
+ The *validate_order* parameter.
201
+ """
202
+ if not filename.endswith(".whl"):
203
+ raise InvalidWheelFilename(
204
+ f"Invalid wheel filename (extension must be '.whl'): {filename!r}"
205
+ )
206
+
207
+ filename = filename[:-4]
208
+ dashes = filename.count("-")
209
+ if dashes not in (4, 5):
210
+ raise InvalidWheelFilename(
211
+ f"Invalid wheel filename (wrong number of parts): {filename!r}"
212
+ )
213
+
214
+ parts = filename.split("-", dashes - 2)
215
+ name_part = parts[0]
216
+ # See PEP 427 for the rules on escaping the project name.
217
+ if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None:
218
+ raise InvalidWheelFilename(f"Invalid project name: {filename!r}")
219
+ name = canonicalize_name(name_part)
220
+
221
+ try:
222
+ version = Version(parts[1])
223
+ except InvalidVersion as e:
224
+ raise InvalidWheelFilename(
225
+ f"Invalid wheel filename (invalid version): {filename!r}"
226
+ ) from e
227
+
228
+ if dashes == 5:
229
+ build_part = parts[2]
230
+ build_match = _build_tag_regex.match(build_part)
231
+ if build_match is None:
232
+ raise InvalidWheelFilename(
233
+ f"Invalid build number: {build_part} in {filename!r}"
234
+ )
235
+ build = cast("BuildTag", (int(build_match.group(1)), build_match.group(2)))
236
+ else:
237
+ build = ()
238
+ tag_str = parts[-1]
239
+ try:
240
+ tags = parse_tag(tag_str, validate_order=validate_order)
241
+ except UnsortedTagsError:
242
+ raise InvalidWheelFilename(
243
+ f"Invalid wheel filename (compressed tag set components must be in "
244
+ f"sorted order per PEP 425): {filename!r}"
245
+ ) from None
246
+ return (name, version, build, tags)
247
+
248
+
249
+ def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:
250
+ """
251
+ This function takes the filename of a sdist file (as specified
252
+ in the `Source distribution format`_ documentation), and parses
253
+ it, returning a tuple of the normalized name and version as
254
+ represented by an instance of :class:`~packaging.version.Version`.
255
+
256
+ :param str filename: The name of the sdist file.
257
+ :raises InvalidSdistFilename: If the filename does not end
258
+ with an sdist extension (``.zip`` or ``.tar.gz``), or if it does not
259
+ contain a dash separating the name and the version of the distribution.
260
+
261
+ >>> from packaging.utils import parse_sdist_filename
262
+ >>> from packaging.version import Version
263
+ >>> name, ver = parse_sdist_filename("foo-1.0.tar.gz")
264
+ >>> name
265
+ 'foo'
266
+ >>> ver == Version('1.0')
267
+ True
268
+
269
+ .. _Source distribution format: https://packaging.python.org/specifications/source-distribution-format/#source-distribution-file-name
270
+ """
271
+ if filename.endswith(".tar.gz"):
272
+ file_stem = filename[: -len(".tar.gz")]
273
+ elif filename.endswith(".zip"):
274
+ file_stem = filename[: -len(".zip")]
275
+ else:
276
+ raise InvalidSdistFilename(
277
+ f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):"
278
+ f" {filename!r}"
279
+ )
280
+
281
+ # We are requiring a PEP 440 version, which cannot contain dashes,
282
+ # so we split on the last dash.
283
+ name_part, sep, version_part = file_stem.rpartition("-")
284
+ if not sep:
285
+ raise InvalidSdistFilename(f"Invalid sdist filename: {filename!r}")
286
+
287
+ name = canonicalize_name(name_part)
288
+
289
+ try:
290
+ version = Version(version_part)
291
+ except InvalidVersion as e:
292
+ raise InvalidSdistFilename(
293
+ f"Invalid sdist filename (invalid version): {filename!r}"
294
+ ) from e
295
+
296
+ return (name, version)
.venv/lib/python3.14/site-packages/pip/_vendor/packaging/version.py ADDED
@@ -0,0 +1,1231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file is dual licensed under the terms of the Apache License, Version
2
+ # 2.0, and the BSD License. See the LICENSE file in the root of this repository
3
+ # for complete details.
4
+ """
5
+ .. testsetup::
6
+
7
+ from pip._vendor.packaging.version import parse, normalize_pre, Version, _cmpkey
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ import sys
14
+ import typing
15
+ from typing import (
16
+ Any,
17
+ Callable,
18
+ Literal,
19
+ NamedTuple,
20
+ SupportsInt,
21
+ Tuple,
22
+ TypedDict,
23
+ Union,
24
+ )
25
+
26
+ if typing.TYPE_CHECKING:
27
+ from typing_extensions import Self, Unpack
28
+
29
+ if sys.version_info >= (3, 13): # pragma: no cover
30
+ from warnings import deprecated as _deprecated
31
+ elif typing.TYPE_CHECKING:
32
+ from typing_extensions import deprecated as _deprecated
33
+ else: # pragma: no cover
34
+ import functools
35
+ import warnings
36
+
37
+ def _deprecated(message: str) -> object:
38
+ def decorator(func: Callable[[...], object]) -> object:
39
+ @functools.wraps(func)
40
+ def wrapper(*args: object, **kwargs: object) -> object:
41
+ warnings.warn(
42
+ message,
43
+ category=DeprecationWarning,
44
+ stacklevel=2,
45
+ )
46
+ return func(*args, **kwargs)
47
+
48
+ return wrapper
49
+
50
+ return decorator
51
+
52
+
53
+ _LETTER_NORMALIZATION = {
54
+ "alpha": "a",
55
+ "beta": "b",
56
+ "c": "rc",
57
+ "pre": "rc",
58
+ "preview": "rc",
59
+ "rev": "post",
60
+ "r": "post",
61
+ }
62
+
63
+ __all__ = ["VERSION_PATTERN", "InvalidVersion", "Version", "normalize_pre", "parse"]
64
+
65
+
66
+ def __dir__() -> list[str]:
67
+ return __all__
68
+
69
+
70
+ LocalType = Tuple[Union[int, str], ...]
71
+
72
+ CmpLocalType = Tuple[Tuple[int, str], ...]
73
+ CmpSuffix = Tuple[int, int, int, int, int, int]
74
+ CmpKey = Union[
75
+ Tuple[int, Tuple[int, ...], CmpSuffix],
76
+ Tuple[int, Tuple[int, ...], CmpSuffix, CmpLocalType],
77
+ ]
78
+ VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool]
79
+
80
+
81
+ class _VersionReplace(TypedDict, total=False):
82
+ epoch: int | None
83
+ release: tuple[int, ...] | None
84
+ pre: tuple[str, int] | None
85
+ post: int | None
86
+ dev: int | None
87
+ local: str | None
88
+
89
+
90
+ def normalize_pre(letter: str, /) -> str:
91
+ """Normalize the pre-release segment of a version string.
92
+
93
+ Returns a lowercase version of the string if not a known pre-release
94
+ identifier.
95
+
96
+ >>> normalize_pre('alpha')
97
+ 'a'
98
+ >>> normalize_pre('BETA')
99
+ 'b'
100
+ >>> normalize_pre('rc')
101
+ 'rc'
102
+
103
+ :param letter:
104
+
105
+ .. versionadded:: 26.1
106
+ """
107
+ letter = letter.lower()
108
+ return _LETTER_NORMALIZATION.get(letter, letter)
109
+
110
+
111
+ def parse(version: str) -> Version:
112
+ """Parse the given version string.
113
+
114
+ This is identical to the :class:`Version` constructor.
115
+
116
+ >>> parse('1.0.dev1')
117
+ <Version('1.0.dev1')>
118
+
119
+ :param version: The version string to parse.
120
+ :raises InvalidVersion: When the version string is not a valid version.
121
+ """
122
+ return Version(version)
123
+
124
+
125
+ class InvalidVersion(ValueError):
126
+ """Raised when a version string is not a valid version.
127
+
128
+ >>> Version("invalid")
129
+ Traceback (most recent call last):
130
+ ...
131
+ packaging.version.InvalidVersion: Invalid version: 'invalid'
132
+ """
133
+
134
+
135
+ class _BaseVersion:
136
+ __slots__ = ()
137
+
138
+ # This can also be a normal member (see the packaging_legacy package);
139
+ # we are just requiring it to be readable. Actually defining a property
140
+ # has runtime effect on subclasses, so it's typing only.
141
+ if typing.TYPE_CHECKING:
142
+
143
+ @property
144
+ def _key(self) -> tuple[Any, ...]: ...
145
+
146
+ def __hash__(self) -> int:
147
+ return hash(self._key)
148
+
149
+ # Please keep the duplicated `isinstance` check
150
+ # in the six comparisons hereunder
151
+ # unless you find a way to avoid adding overhead function calls.
152
+ def __lt__(self, other: _BaseVersion) -> bool:
153
+ if not isinstance(other, _BaseVersion):
154
+ return NotImplemented
155
+
156
+ return self._key < other._key
157
+
158
+ def __le__(self, other: _BaseVersion) -> bool:
159
+ if not isinstance(other, _BaseVersion):
160
+ return NotImplemented
161
+
162
+ return self._key <= other._key
163
+
164
+ def __eq__(self, other: object) -> bool:
165
+ if not isinstance(other, _BaseVersion):
166
+ return NotImplemented
167
+
168
+ return self._key == other._key
169
+
170
+ def __ge__(self, other: _BaseVersion) -> bool:
171
+ if not isinstance(other, _BaseVersion):
172
+ return NotImplemented
173
+
174
+ return self._key >= other._key
175
+
176
+ def __gt__(self, other: _BaseVersion) -> bool:
177
+ if not isinstance(other, _BaseVersion):
178
+ return NotImplemented
179
+
180
+ return self._key > other._key
181
+
182
+ def __ne__(self, other: object) -> bool:
183
+ if not isinstance(other, _BaseVersion):
184
+ return NotImplemented
185
+
186
+ return self._key != other._key
187
+
188
+
189
+ # Deliberately not anchored to the start and end of the string, to make it
190
+ # easier for 3rd party code to reuse
191
+
192
+ # Note that ++ doesn't behave identically on CPython and PyPy, so not using it here
193
+ _VERSION_PATTERN = r"""
194
+ v?+ # optional leading v
195
+ (?a:
196
+ (?:(?P<epoch>[0-9]+)!)?+ # epoch
197
+ (?P<release>[0-9]+(?:\.[0-9]+)*+) # release segment
198
+ (?P<pre> # pre-release
199
+ [._-]?+
200
+ (?P<pre_l>alpha|a|beta|b|preview|pre|c|rc)
201
+ [._-]?+
202
+ (?P<pre_n>[0-9]+)?
203
+ )?+
204
+ (?P<post> # post release
205
+ (?:-(?P<post_n1>[0-9]+))
206
+ |
207
+ (?:
208
+ [._-]?
209
+ (?P<post_l>post|rev|r)
210
+ [._-]?
211
+ (?P<post_n2>[0-9]+)?
212
+ )
213
+ )?+
214
+ (?P<dev> # dev release
215
+ [._-]?+
216
+ (?P<dev_l>dev)
217
+ [._-]?+
218
+ (?P<dev_n>[0-9]+)?
219
+ )?+
220
+ )
221
+ (?a:\+
222
+ (?P<local> # local version
223
+ [a-z0-9]+
224
+ (?:[._-][a-z0-9]+)*+
225
+ )
226
+ )?+
227
+ """
228
+
229
+ _VERSION_PATTERN_OLD = _VERSION_PATTERN.replace("*+", "*").replace("?+", "?")
230
+
231
+ # Possessive qualifiers were added in Python 3.11.
232
+ # CPython 3.11.0-3.11.4 had a bug: https://github.com/python/cpython/pull/107795
233
+ # Older PyPy also had a bug.
234
+ VERSION_PATTERN = (
235
+ _VERSION_PATTERN_OLD
236
+ if (sys.implementation.name == "cpython" and sys.version_info < (3, 11, 5))
237
+ or (sys.implementation.name == "pypy" and sys.version_info < (3, 11, 13))
238
+ or sys.version_info < (3, 11)
239
+ else _VERSION_PATTERN
240
+ )
241
+ """
242
+ A string containing the regular expression used to match a valid version.
243
+
244
+ The pattern is not anchored at either end, and is intended for embedding in larger
245
+ expressions (for example, matching a version number as part of a file name). The
246
+ regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE``
247
+ flags set.
248
+
249
+ .. versionchanged:: 26.0
250
+
251
+ The regex now uses possessive qualifiers on Python 3.11 if they are
252
+ supported (CPython 3.11.5+, PyPy 3.11.13+).
253
+
254
+ :meta hide-value:
255
+ """
256
+
257
+
258
+ # Validation pattern for local version in replace()
259
+ _LOCAL_PATTERN = re.compile(r"[a-z0-9]+(?:[._-][a-z0-9]+)*", re.IGNORECASE | re.ASCII)
260
+
261
+ # Fast path: If a version has only digits and dots then we
262
+ # can skip the regex and parse it as a release segment
263
+ _SIMPLE_VERSION_INDICATORS = frozenset(".0123456789")
264
+
265
+
266
+ def _validate_epoch(value: object, /) -> int:
267
+ epoch = value or 0
268
+ if isinstance(epoch, int) and epoch >= 0:
269
+ return epoch
270
+ msg = f"epoch must be non-negative integer, got {epoch}"
271
+ raise InvalidVersion(msg)
272
+
273
+
274
+ def _validate_release(value: object, /) -> tuple[int, ...]:
275
+ release = (0,) if value is None else value
276
+ if (
277
+ isinstance(release, tuple)
278
+ and len(release) > 0
279
+ and all(isinstance(i, int) and i >= 0 for i in release)
280
+ ):
281
+ return release
282
+ msg = f"release must be a non-empty tuple of non-negative integers, got {release}"
283
+ raise InvalidVersion(msg)
284
+
285
+
286
+ def _validate_pre(value: object, /) -> tuple[Literal["a", "b", "rc"], int] | None:
287
+ if value is None:
288
+ return value
289
+ if isinstance(value, tuple) and len(value) == 2:
290
+ letter, number = value
291
+ letter = normalize_pre(letter)
292
+ if letter in {"a", "b", "rc"} and isinstance(number, int) and number >= 0:
293
+ # type checkers can't infer the Literal type here on letter
294
+ return (letter, number) # type: ignore[return-value]
295
+ msg = f"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got {value}"
296
+ raise InvalidVersion(msg)
297
+
298
+
299
+ def _validate_post(value: object, /) -> tuple[Literal["post"], int] | None:
300
+ if value is None:
301
+ return value
302
+ if isinstance(value, int) and value >= 0:
303
+ return ("post", value)
304
+ msg = f"post must be non-negative integer, got {value}"
305
+ raise InvalidVersion(msg)
306
+
307
+
308
+ def _validate_dev(value: object, /) -> tuple[Literal["dev"], int] | None:
309
+ if value is None:
310
+ return value
311
+ if isinstance(value, int) and value >= 0:
312
+ return ("dev", value)
313
+ msg = f"dev must be non-negative integer, got {value}"
314
+ raise InvalidVersion(msg)
315
+
316
+
317
+ def _validate_local(value: object, /) -> LocalType | None:
318
+ if value is None:
319
+ return value
320
+ if isinstance(value, str) and _LOCAL_PATTERN.fullmatch(value):
321
+ return _parse_local_version(value)
322
+ msg = f"local must be a valid version string, got {value!r}"
323
+ raise InvalidVersion(msg)
324
+
325
+
326
+ # Backward compatibility for internals before 26.0. Do not use.
327
+ class _Version(NamedTuple):
328
+ epoch: int
329
+ release: tuple[int, ...]
330
+ dev: tuple[Literal["dev"], int] | None
331
+ pre: tuple[Literal["a", "b", "rc"], int] | None
332
+ post: tuple[Literal["post"], int] | None
333
+ local: LocalType | None
334
+
335
+
336
+ class Version(_BaseVersion):
337
+ """This class abstracts handling of a project's versions.
338
+
339
+ A :class:`Version` instance is comparison aware and can be compared and
340
+ sorted using the standard Python interfaces.
341
+
342
+ >>> v1 = Version("1.0a5")
343
+ >>> v2 = Version("1.0")
344
+ >>> v1
345
+ <Version('1.0a5')>
346
+ >>> v2
347
+ <Version('1.0')>
348
+ >>> v1 < v2
349
+ True
350
+ >>> v1 == v2
351
+ False
352
+ >>> v1 > v2
353
+ False
354
+ >>> v1 >= v2
355
+ False
356
+ >>> v1 <= v2
357
+ True
358
+
359
+ :class:`Version` is immutable; use :meth:`__replace__` to change
360
+ part of a version.
361
+
362
+ Instances are safe to serialize with :mod:`pickle`. They use a stable
363
+ format so the same pickle can be loaded in future packaging releases.
364
+
365
+ .. versionchanged:: 26.2
366
+
367
+ Added a stable pickle format. Pickles created with packaging 26.2+ can
368
+ be unpickled with future releases. Backward compatibility with pickles
369
+ from pip._vendor.packaging < 26.2 is supported but may be removed in a future
370
+ release.
371
+ """
372
+
373
+ __slots__ = (
374
+ "_dev",
375
+ "_epoch",
376
+ "_hash_cache",
377
+ "_key_cache",
378
+ "_local",
379
+ "_post",
380
+ "_pre",
381
+ "_release",
382
+ )
383
+ __match_args__ = ("_str",)
384
+ """
385
+ Pattern matching is supported on Python 3.10+.
386
+
387
+ .. versionadded:: 26.0
388
+
389
+ :meta hide-value:
390
+ """
391
+
392
+ _regex = re.compile(r"\s*" + VERSION_PATTERN + r"\s*", re.VERBOSE | re.IGNORECASE)
393
+
394
+ _epoch: int
395
+ _release: tuple[int, ...]
396
+ _dev: tuple[Literal["dev"], int] | None
397
+ _pre: tuple[Literal["a", "b", "rc"], int] | None
398
+ _post: tuple[Literal["post"], int] | None
399
+ _local: LocalType | None
400
+
401
+ _hash_cache: int | None
402
+ _key_cache: CmpKey | None
403
+
404
+ def __init__(self, version: str) -> None:
405
+ """Initialize a Version object.
406
+
407
+ :param version:
408
+ The string representation of a version which will be parsed and normalized
409
+ before use.
410
+ :raises InvalidVersion:
411
+ If the ``version`` does not conform to PEP 440 in any way then this
412
+ exception will be raised.
413
+ """
414
+ if _SIMPLE_VERSION_INDICATORS.issuperset(version):
415
+ try:
416
+ self._release = tuple(map(int, version.split(".")))
417
+ except ValueError:
418
+ # Empty parts (from "1..2", ".1", etc.) are invalid versions.
419
+ # Any other ValueError (e.g. int str-digits limit) should
420
+ # propagate to the caller.
421
+ if "" in version.split("."):
422
+ raise InvalidVersion(f"Invalid version: {version!r}") from None
423
+ # TODO: remove "no cover" when Python 3.9 is dropped.
424
+ raise # pragma: no cover
425
+
426
+ self._epoch = 0
427
+ self._pre = None
428
+ self._post = None
429
+ self._dev = None
430
+ self._local = None
431
+ self._key_cache = None
432
+ self._hash_cache = None
433
+ return
434
+
435
+ # Validate the version and parse it into pieces
436
+ match = self._regex.fullmatch(version)
437
+ if not match:
438
+ raise InvalidVersion(f"Invalid version: {version!r}")
439
+ self._epoch = int(match.group("epoch")) if match.group("epoch") else 0
440
+ self._release = tuple(map(int, match.group("release").split(".")))
441
+ # We can type ignore the assignments below because the regex guarantees
442
+ # the correct strings
443
+ self._pre = _parse_letter_version(match.group("pre_l"), match.group("pre_n")) # type: ignore[assignment]
444
+ self._post = _parse_letter_version( # type: ignore[assignment]
445
+ match.group("post_l"), match.group("post_n1") or match.group("post_n2")
446
+ )
447
+ self._dev = _parse_letter_version(match.group("dev_l"), match.group("dev_n")) # type: ignore[assignment]
448
+ self._local = _parse_local_version(match.group("local"))
449
+
450
+ # Key which will be used for sorting
451
+ self._key_cache = None
452
+ self._hash_cache = None
453
+
454
+ @classmethod
455
+ def from_parts(
456
+ cls,
457
+ *,
458
+ epoch: int = 0,
459
+ release: tuple[int, ...],
460
+ pre: tuple[str, int] | None = None,
461
+ post: int | None = None,
462
+ dev: int | None = None,
463
+ local: str | None = None,
464
+ ) -> Self:
465
+ """
466
+ Return a new version composed of the various parts.
467
+
468
+ This allows you to build a version without going though a string and
469
+ running a regular expression. It normalizes pre-release strings. The
470
+ ``release=`` keyword argument is required.
471
+
472
+ >>> Version.from_parts(release=(1,2,3))
473
+ <Version('1.2.3')>
474
+ >>> Version.from_parts(release=(0,1,0), pre=("b", 1))
475
+ <Version('0.1.0b1')>
476
+
477
+ :param epoch:
478
+ :param release: This version tuple is required
479
+
480
+ .. versionadded:: 26.1
481
+ """
482
+ _epoch = _validate_epoch(epoch)
483
+ _release = _validate_release(release)
484
+ _pre = _validate_pre(pre) if pre is not None else None
485
+ _post = _validate_post(post) if post is not None else None
486
+ _dev = _validate_dev(dev) if dev is not None else None
487
+ _local = _validate_local(local) if local is not None else None
488
+
489
+ new_version = cls.__new__(cls)
490
+ new_version._key_cache = None
491
+ new_version._hash_cache = None
492
+ new_version._epoch = _epoch
493
+ new_version._release = _release
494
+ new_version._pre = _pre
495
+ new_version._post = _post
496
+ new_version._dev = _dev
497
+ new_version._local = _local
498
+
499
+ return new_version
500
+
501
+ def __replace__(self, **kwargs: Unpack[_VersionReplace]) -> Self:
502
+ """
503
+ __replace__(*, epoch=..., release=..., pre=..., post=..., dev=..., local=...)
504
+
505
+ Return a new version with parts replaced.
506
+
507
+ This returns a new version (unless no parts were changed). The
508
+ pre-release is normalized. Setting a value to ``None`` clears it.
509
+
510
+ >>> v = Version("1.2.3")
511
+ >>> v.__replace__(pre=("a", 1))
512
+ <Version('1.2.3a1')>
513
+
514
+ :param int | None epoch:
515
+ :param tuple[int, ...] | None release:
516
+ :param tuple[str, int] | None pre:
517
+ :param int | None post:
518
+ :param int | None dev:
519
+ :param str | None local:
520
+
521
+ .. versionadded:: 26.0
522
+ .. versionchanged:: 26.1
523
+
524
+ The pre-release portion is now normalized.
525
+ """
526
+ epoch = _validate_epoch(kwargs["epoch"]) if "epoch" in kwargs else self._epoch
527
+ release = (
528
+ _validate_release(kwargs["release"])
529
+ if "release" in kwargs
530
+ else self._release
531
+ )
532
+ pre = _validate_pre(kwargs["pre"]) if "pre" in kwargs else self._pre
533
+ post = _validate_post(kwargs["post"]) if "post" in kwargs else self._post
534
+ dev = _validate_dev(kwargs["dev"]) if "dev" in kwargs else self._dev
535
+ local = _validate_local(kwargs["local"]) if "local" in kwargs else self._local
536
+
537
+ if (
538
+ epoch == self._epoch
539
+ and release == self._release
540
+ and pre == self._pre
541
+ and post == self._post
542
+ and dev == self._dev
543
+ and local == self._local
544
+ ):
545
+ return self
546
+
547
+ new_version = self.__class__.__new__(self.__class__)
548
+ new_version._key_cache = None
549
+ new_version._hash_cache = None
550
+ new_version._epoch = epoch
551
+ new_version._release = release
552
+ new_version._pre = pre
553
+ new_version._post = post
554
+ new_version._dev = dev
555
+ new_version._local = local
556
+
557
+ return new_version
558
+
559
+ @property
560
+ def _key(self) -> CmpKey:
561
+ if self._key_cache is None:
562
+ self._key_cache = _cmpkey(
563
+ self._epoch,
564
+ self._release,
565
+ self._pre,
566
+ self._post,
567
+ self._dev,
568
+ self._local,
569
+ )
570
+ return self._key_cache
571
+
572
+ # __hash__ must be defined when __eq__ is overridden,
573
+ # otherwise Python sets __hash__ to None.
574
+ def __hash__(self) -> int:
575
+ if (cached_hash := self._hash_cache) is not None:
576
+ return cached_hash
577
+
578
+ if (key := self._key_cache) is None:
579
+ self._key_cache = key = _cmpkey(
580
+ self._epoch,
581
+ self._release,
582
+ self._pre,
583
+ self._post,
584
+ self._dev,
585
+ self._local,
586
+ )
587
+ self._hash_cache = cached_hash = hash(key)
588
+ return cached_hash
589
+
590
+ # Override comparison methods to use direct _key_cache access
591
+ # This is faster than property access, especially before Python 3.12
592
+ def __lt__(self, other: _BaseVersion) -> bool:
593
+ if isinstance(other, Version):
594
+ if self._key_cache is None:
595
+ self._key_cache = _cmpkey(
596
+ self._epoch,
597
+ self._release,
598
+ self._pre,
599
+ self._post,
600
+ self._dev,
601
+ self._local,
602
+ )
603
+ if other._key_cache is None:
604
+ other._key_cache = _cmpkey(
605
+ other._epoch,
606
+ other._release,
607
+ other._pre,
608
+ other._post,
609
+ other._dev,
610
+ other._local,
611
+ )
612
+ return self._key_cache < other._key_cache
613
+
614
+ if not isinstance(other, _BaseVersion):
615
+ return NotImplemented
616
+
617
+ return super().__lt__(other)
618
+
619
+ def __le__(self, other: _BaseVersion) -> bool:
620
+ if isinstance(other, Version):
621
+ if self._key_cache is None:
622
+ self._key_cache = _cmpkey(
623
+ self._epoch,
624
+ self._release,
625
+ self._pre,
626
+ self._post,
627
+ self._dev,
628
+ self._local,
629
+ )
630
+ if other._key_cache is None:
631
+ other._key_cache = _cmpkey(
632
+ other._epoch,
633
+ other._release,
634
+ other._pre,
635
+ other._post,
636
+ other._dev,
637
+ other._local,
638
+ )
639
+ return self._key_cache <= other._key_cache
640
+
641
+ if not isinstance(other, _BaseVersion):
642
+ return NotImplemented
643
+
644
+ return super().__le__(other)
645
+
646
+ def __eq__(self, other: object) -> bool:
647
+ if isinstance(other, Version):
648
+ if self._key_cache is None:
649
+ self._key_cache = _cmpkey(
650
+ self._epoch,
651
+ self._release,
652
+ self._pre,
653
+ self._post,
654
+ self._dev,
655
+ self._local,
656
+ )
657
+ if other._key_cache is None:
658
+ other._key_cache = _cmpkey(
659
+ other._epoch,
660
+ other._release,
661
+ other._pre,
662
+ other._post,
663
+ other._dev,
664
+ other._local,
665
+ )
666
+ return self._key_cache == other._key_cache
667
+
668
+ if not isinstance(other, _BaseVersion):
669
+ return NotImplemented
670
+
671
+ return super().__eq__(other)
672
+
673
+ def __ge__(self, other: _BaseVersion) -> bool:
674
+ if isinstance(other, Version):
675
+ if self._key_cache is None:
676
+ self._key_cache = _cmpkey(
677
+ self._epoch,
678
+ self._release,
679
+ self._pre,
680
+ self._post,
681
+ self._dev,
682
+ self._local,
683
+ )
684
+ if other._key_cache is None:
685
+ other._key_cache = _cmpkey(
686
+ other._epoch,
687
+ other._release,
688
+ other._pre,
689
+ other._post,
690
+ other._dev,
691
+ other._local,
692
+ )
693
+ return self._key_cache >= other._key_cache
694
+
695
+ if not isinstance(other, _BaseVersion):
696
+ return NotImplemented
697
+
698
+ return super().__ge__(other)
699
+
700
+ def __gt__(self, other: _BaseVersion) -> bool:
701
+ if isinstance(other, Version):
702
+ if self._key_cache is None:
703
+ self._key_cache = _cmpkey(
704
+ self._epoch,
705
+ self._release,
706
+ self._pre,
707
+ self._post,
708
+ self._dev,
709
+ self._local,
710
+ )
711
+ if other._key_cache is None:
712
+ other._key_cache = _cmpkey(
713
+ other._epoch,
714
+ other._release,
715
+ other._pre,
716
+ other._post,
717
+ other._dev,
718
+ other._local,
719
+ )
720
+ return self._key_cache > other._key_cache
721
+
722
+ if not isinstance(other, _BaseVersion):
723
+ return NotImplemented
724
+
725
+ return super().__gt__(other)
726
+
727
+ def __ne__(self, other: object) -> bool:
728
+ if isinstance(other, Version):
729
+ if self._key_cache is None:
730
+ self._key_cache = _cmpkey(
731
+ self._epoch,
732
+ self._release,
733
+ self._pre,
734
+ self._post,
735
+ self._dev,
736
+ self._local,
737
+ )
738
+ if other._key_cache is None:
739
+ other._key_cache = _cmpkey(
740
+ other._epoch,
741
+ other._release,
742
+ other._pre,
743
+ other._post,
744
+ other._dev,
745
+ other._local,
746
+ )
747
+ return self._key_cache != other._key_cache
748
+
749
+ if not isinstance(other, _BaseVersion):
750
+ return NotImplemented
751
+
752
+ return super().__ne__(other)
753
+
754
+ def __getstate__(
755
+ self,
756
+ ) -> tuple[
757
+ int,
758
+ tuple[int, ...],
759
+ tuple[str, int] | None,
760
+ tuple[str, int] | None,
761
+ tuple[str, int] | None,
762
+ LocalType | None,
763
+ ]:
764
+ # Return state as a 6-item tuple for compactness:
765
+ # (epoch, release, pre, post, dev, local)
766
+ # Cache members are excluded and will be recomputed on demand
767
+ return (
768
+ self._epoch,
769
+ self._release,
770
+ self._pre,
771
+ self._post,
772
+ self._dev,
773
+ self._local,
774
+ )
775
+
776
+ def __setstate__(self, state: object) -> None:
777
+ # Always discard cached values — they may contain stale references
778
+ # (e.g. packaging._structures.InfinityType from pre-26.1 pickles)
779
+ # and will be recomputed on demand from the core fields above.
780
+ self._key_cache = None
781
+ self._hash_cache = None
782
+
783
+ if isinstance(state, tuple):
784
+ if len(state) == 6:
785
+ # New format (26.2+): (epoch, release, pre, post, dev, local)
786
+ (
787
+ self._epoch,
788
+ self._release,
789
+ self._pre,
790
+ self._post,
791
+ self._dev,
792
+ self._local,
793
+ ) = state
794
+ return
795
+ if len(state) == 2:
796
+ # Format (packaging 26.0-26.1): (None, {slot: value}).
797
+ _, slot_dict = state
798
+ if isinstance(slot_dict, dict):
799
+ self._epoch = slot_dict["_epoch"]
800
+ self._release = slot_dict["_release"]
801
+ self._pre = slot_dict.get("_pre")
802
+ self._post = slot_dict.get("_post")
803
+ self._dev = slot_dict.get("_dev")
804
+ self._local = slot_dict.get("_local")
805
+ return
806
+ if isinstance(state, dict):
807
+ # Old format (packaging <= 25.x, no __slots__): state is a plain
808
+ # dict with "_version" (_Version NamedTuple) and "_key" entries.
809
+ version_nt = state.get("_version")
810
+ if version_nt is not None:
811
+ self._epoch = version_nt.epoch
812
+ self._release = version_nt.release
813
+ self._pre = version_nt.pre
814
+ self._post = version_nt.post
815
+ self._dev = version_nt.dev
816
+ self._local = version_nt.local
817
+ return
818
+
819
+ raise TypeError(f"Cannot restore Version from {state!r}")
820
+
821
+ @property
822
+ @_deprecated("Version._version is private and will be removed soon")
823
+ def _version(self) -> _Version:
824
+ return _Version(
825
+ self._epoch, self._release, self._dev, self._pre, self._post, self._local
826
+ )
827
+
828
+ @_version.setter
829
+ @_deprecated("Version._version is private and will be removed soon")
830
+ def _version(self, value: _Version) -> None:
831
+ self._epoch = value.epoch
832
+ self._release = value.release
833
+ self._dev = value.dev
834
+ self._pre = value.pre
835
+ self._post = value.post
836
+ self._local = value.local
837
+ self._key_cache = None
838
+ self._hash_cache = None
839
+
840
+ def __repr__(self) -> str:
841
+ """A representation of the Version that shows all internal state.
842
+
843
+ >>> Version('1.0.0')
844
+ <Version('1.0.0')>
845
+ """
846
+ return f"<{self.__class__.__name__}({str(self)!r})>"
847
+
848
+ def __str__(self) -> str:
849
+ """A string representation of the version that can be round-tripped.
850
+
851
+ >>> str(Version("1.0a5"))
852
+ '1.0a5'
853
+ """
854
+ # This is a hot function, so not calling self.base_version
855
+ version = ".".join(map(str, self.release))
856
+
857
+ # Epoch
858
+ if self.epoch:
859
+ version = f"{self.epoch}!{version}"
860
+
861
+ # Pre-release
862
+ if self.pre is not None:
863
+ version += "".join(map(str, self.pre))
864
+
865
+ # Post-release
866
+ if self.post is not None:
867
+ version += f".post{self.post}"
868
+
869
+ # Development release
870
+ if self.dev is not None:
871
+ version += f".dev{self.dev}"
872
+
873
+ # Local version segment
874
+ if self.local is not None:
875
+ version += f"+{self.local}"
876
+
877
+ return version
878
+
879
+ @property
880
+ def _str(self) -> str:
881
+ """Internal property for match_args"""
882
+ return str(self)
883
+
884
+ @property
885
+ def epoch(self) -> int:
886
+ """The epoch of the version.
887
+
888
+ >>> Version("2.0.0").epoch
889
+ 0
890
+ >>> Version("1!2.0.0").epoch
891
+ 1
892
+ """
893
+ return self._epoch
894
+
895
+ @property
896
+ def release(self) -> tuple[int, ...]:
897
+ """The components of the "release" segment of the version.
898
+
899
+ >>> Version("1.2.3").release
900
+ (1, 2, 3)
901
+ >>> Version("2.0.0").release
902
+ (2, 0, 0)
903
+ >>> Version("1!2.0.0.post0").release
904
+ (2, 0, 0)
905
+
906
+ Includes trailing zeroes but not the epoch or any pre-release / development /
907
+ post-release suffixes.
908
+ """
909
+ return self._release
910
+
911
+ @property
912
+ def pre(self) -> tuple[Literal["a", "b", "rc"], int] | None:
913
+ """The pre-release segment of the version.
914
+
915
+ >>> print(Version("1.2.3").pre)
916
+ None
917
+ >>> Version("1.2.3a1").pre
918
+ ('a', 1)
919
+ >>> Version("1.2.3b1").pre
920
+ ('b', 1)
921
+ >>> Version("1.2.3rc1").pre
922
+ ('rc', 1)
923
+ """
924
+ return self._pre
925
+
926
+ @property
927
+ def post(self) -> int | None:
928
+ """The post-release number of the version.
929
+
930
+ >>> print(Version("1.2.3").post)
931
+ None
932
+ >>> Version("1.2.3.post1").post
933
+ 1
934
+ """
935
+ return self._post[1] if self._post else None
936
+
937
+ @property
938
+ def dev(self) -> int | None:
939
+ """The development number of the version.
940
+
941
+ >>> print(Version("1.2.3").dev)
942
+ None
943
+ >>> Version("1.2.3.dev1").dev
944
+ 1
945
+ """
946
+ return self._dev[1] if self._dev else None
947
+
948
+ @property
949
+ def local(self) -> str | None:
950
+ """The local version segment of the version.
951
+
952
+ >>> print(Version("1.2.3").local)
953
+ None
954
+ >>> Version("1.2.3+abc").local
955
+ 'abc'
956
+ """
957
+ if self._local:
958
+ return ".".join(str(x) for x in self._local)
959
+ else:
960
+ return None
961
+
962
+ @property
963
+ def public(self) -> str:
964
+ """The public portion of the version.
965
+
966
+ This returns a string. If you want a :class:`Version` again and care
967
+ about performance, use ``v.__replace__(local=None)`` instead.
968
+
969
+ >>> Version("1.2.3").public
970
+ '1.2.3'
971
+ >>> Version("1.2.3+abc").public
972
+ '1.2.3'
973
+ >>> Version("1!1.2.3dev1+abc").public
974
+ '1!1.2.3.dev1'
975
+ """
976
+ return str(self).split("+", 1)[0]
977
+
978
+ @property
979
+ def base_version(self) -> str:
980
+ """The "base version" of the version.
981
+
982
+ This returns a string. If you want a :class:`Version` again and care
983
+ about performance, use
984
+ ``v.__replace__(pre=None, post=None, dev=None, local=None)`` instead.
985
+
986
+ >>> Version("1.2.3").base_version
987
+ '1.2.3'
988
+ >>> Version("1.2.3+abc").base_version
989
+ '1.2.3'
990
+ >>> Version("1!1.2.3dev1+abc").base_version
991
+ '1!1.2.3'
992
+
993
+ The "base version" is the public version of the project without any pre or post
994
+ release markers.
995
+ """
996
+ release_segment = ".".join(map(str, self.release))
997
+ return f"{self.epoch}!{release_segment}" if self.epoch else release_segment
998
+
999
+ @property
1000
+ def is_prerelease(self) -> bool:
1001
+ """Whether this version is a pre-release.
1002
+
1003
+ >>> Version("1.2.3").is_prerelease
1004
+ False
1005
+ >>> Version("1.2.3a1").is_prerelease
1006
+ True
1007
+ >>> Version("1.2.3b1").is_prerelease
1008
+ True
1009
+ >>> Version("1.2.3rc1").is_prerelease
1010
+ True
1011
+ >>> Version("1.2.3dev1").is_prerelease
1012
+ True
1013
+ """
1014
+ return self.dev is not None or self.pre is not None
1015
+
1016
+ @property
1017
+ def is_postrelease(self) -> bool:
1018
+ """Whether this version is a post-release.
1019
+
1020
+ >>> Version("1.2.3").is_postrelease
1021
+ False
1022
+ >>> Version("1.2.3.post1").is_postrelease
1023
+ True
1024
+ """
1025
+ return self.post is not None
1026
+
1027
+ @property
1028
+ def is_devrelease(self) -> bool:
1029
+ """Whether this version is a development release.
1030
+
1031
+ >>> Version("1.2.3").is_devrelease
1032
+ False
1033
+ >>> Version("1.2.3.dev1").is_devrelease
1034
+ True
1035
+ """
1036
+ return self.dev is not None
1037
+
1038
+ @property
1039
+ def major(self) -> int:
1040
+ """The first item of :attr:`release` or ``0`` if unavailable.
1041
+
1042
+ >>> Version("1.2.3").major
1043
+ 1
1044
+ """
1045
+ return self.release[0] if len(self.release) >= 1 else 0
1046
+
1047
+ @property
1048
+ def minor(self) -> int:
1049
+ """The second item of :attr:`release` or ``0`` if unavailable.
1050
+
1051
+ >>> Version("1.2.3").minor
1052
+ 2
1053
+ >>> Version("1").minor
1054
+ 0
1055
+ """
1056
+ return self.release[1] if len(self.release) >= 2 else 0
1057
+
1058
+ @property
1059
+ def micro(self) -> int:
1060
+ """The third item of :attr:`release` or ``0`` if unavailable.
1061
+
1062
+ >>> Version("1.2.3").micro
1063
+ 3
1064
+ >>> Version("1").micro
1065
+ 0
1066
+ """
1067
+ return self.release[2] if len(self.release) >= 3 else 0
1068
+
1069
+
1070
+ class _TrimmedRelease(Version):
1071
+ __slots__ = ()
1072
+
1073
+ def __init__(self, version: str | Version) -> None:
1074
+ if isinstance(version, Version):
1075
+ self._epoch = version._epoch
1076
+ self._release = version._release
1077
+ self._dev = version._dev
1078
+ self._pre = version._pre
1079
+ self._post = version._post
1080
+ self._local = version._local
1081
+ self._key_cache = version._key_cache
1082
+ return
1083
+ super().__init__(version) # pragma: no cover
1084
+
1085
+ @property
1086
+ def release(self) -> tuple[int, ...]:
1087
+ """
1088
+ Release segment without any trailing zeros.
1089
+
1090
+ >>> _TrimmedRelease('1.0.0').release
1091
+ (1,)
1092
+ >>> _TrimmedRelease('0.0').release
1093
+ (0,)
1094
+ """
1095
+ # This leaves one 0.
1096
+ rel = super().release
1097
+ len_release = len(rel)
1098
+ i = len_release
1099
+ while i > 1 and rel[i - 1] == 0:
1100
+ i -= 1
1101
+ return rel if i == len_release else rel[:i]
1102
+
1103
+
1104
+ def _parse_letter_version(
1105
+ letter: str | None, number: str | bytes | SupportsInt | None
1106
+ ) -> tuple[str, int] | None:
1107
+ if letter:
1108
+ # We normalize any letters to their lower case form
1109
+ letter = letter.lower()
1110
+
1111
+ # We consider some words to be alternate spellings of other words and
1112
+ # in those cases we want to normalize the spellings to our preferred
1113
+ # spelling.
1114
+ letter = _LETTER_NORMALIZATION.get(letter, letter)
1115
+
1116
+ # We consider there to be an implicit 0 in a pre-release if there is
1117
+ # not a numeral associated with it.
1118
+ return letter, int(number or 0)
1119
+
1120
+ if number:
1121
+ # We assume if we are given a number, but we are not given a letter
1122
+ # then this is using the implicit post release syntax (e.g. 1.0-1)
1123
+ return "post", int(number)
1124
+
1125
+ return None
1126
+
1127
+
1128
+ _local_version_separators = re.compile(r"[\._-]")
1129
+
1130
+
1131
+ def _parse_local_version(local: str | None) -> LocalType | None:
1132
+ """
1133
+ Takes a string like ``"abc.1.twelve"`` and turns it into
1134
+ ``("abc", 1, "twelve")``.
1135
+ """
1136
+ if local is not None:
1137
+ return tuple(
1138
+ part.lower() if not part.isdigit() else int(part)
1139
+ for part in _local_version_separators.split(local)
1140
+ )
1141
+ return None
1142
+
1143
+
1144
+ # Sort ranks for pre-release: dev-only < a < b < rc < stable (no pre-release).
1145
+ _PRE_RANK = {"a": 0, "b": 1, "rc": 2}
1146
+ _PRE_RANK_DEV_ONLY = -1 # sorts before a(0)
1147
+ _PRE_RANK_STABLE = 3 # sorts after rc(2)
1148
+
1149
+ # In local version segments, strings sort before ints per PEP 440.
1150
+ _LOCAL_STR_RANK = -1 # sorts before all non-negative ints
1151
+
1152
+ # Pre-computed suffix for stable releases (no pre, post, or dev segments).
1153
+ # See _cmpkey() for the suffix layout.
1154
+ _STABLE_SUFFIX = (_PRE_RANK_STABLE, 0, 0, 0, 1, 0)
1155
+
1156
+
1157
+ def _cmpkey(
1158
+ epoch: int,
1159
+ release: tuple[int, ...],
1160
+ pre: tuple[str, int] | None,
1161
+ post: tuple[str, int] | None,
1162
+ dev: tuple[str, int] | None,
1163
+ local: LocalType | None,
1164
+ ) -> CmpKey:
1165
+ """Build a comparison key for PEP 440 ordering.
1166
+
1167
+ Returns ``(epoch, release, suffix)`` or
1168
+ ``(epoch, release, suffix, local)`` so that plain tuple
1169
+ comparison gives the correct order.
1170
+
1171
+ Trailing zeros are stripped from the release so that ``1.0.0 == 1``.
1172
+
1173
+ The suffix is a flat 6-int tuple that encodes pre/post/dev:
1174
+ ``(pre_rank, pre_n, post_rank, post_n, dev_rank, dev_n)``
1175
+
1176
+ pre_rank: dev-only=-1, a=0, b=1, rc=2, no-pre=3
1177
+ Dev-only releases (no pre or post) get -1 so they sort before
1178
+ any alpha/beta/rc. Releases without a pre-release tag get 3
1179
+ so they sort after rc.
1180
+ post_rank: no-post=0, post=1
1181
+ Releases without a post segment sort before those with one.
1182
+ dev_rank: dev=0, no-dev=1
1183
+ Releases without a dev segment sort after those with one.
1184
+
1185
+ Local segments use ``(n, "")`` for ints and ``(-1, s)`` for strings,
1186
+ following PEP 440: strings sort before ints, strings compare
1187
+ lexicographically, ints compare numerically, and shorter segments
1188
+ sort before longer when prefixes match. Versions without a local
1189
+ segment sort before those with one (3-tuple < 4-tuple).
1190
+
1191
+ >>> _cmpkey(0, (1, 0, 0), None, None, None, None)
1192
+ (0, (1,), (3, 0, 0, 0, 1, 0))
1193
+ >>> _cmpkey(0, (1,), ("a", 1), None, None, None)
1194
+ (0, (1,), (0, 1, 0, 0, 1, 0))
1195
+ >>> _cmpkey(0, (1,), None, None, None, ("ubuntu", 1))
1196
+ (0, (1,), (3, 0, 0, 0, 1, 0), ((-1, 'ubuntu'), (1, '')))
1197
+ """
1198
+ # Strip trailing zeros: 1.0.0 compares equal to 1.
1199
+ len_release = len(release)
1200
+ i = len_release
1201
+ while i and release[i - 1] == 0:
1202
+ i -= 1
1203
+ trimmed = release if i == len_release else release[:i]
1204
+
1205
+ # Fast path: stable release with no local segment.
1206
+ if pre is None and post is None and dev is None and local is None:
1207
+ return epoch, trimmed, _STABLE_SUFFIX
1208
+
1209
+ if pre is None and post is None and dev is not None:
1210
+ # dev-only (e.g. 1.0.dev1) sorts before all pre-releases.
1211
+ pre_rank, pre_n = _PRE_RANK_DEV_ONLY, 0
1212
+ elif pre is None:
1213
+ pre_rank, pre_n = _PRE_RANK_STABLE, 0
1214
+ else:
1215
+ pre_rank, pre_n = _PRE_RANK[pre[0]], pre[1]
1216
+
1217
+ post_rank = 0 if post is None else 1
1218
+ post_n = 0 if post is None else post[1]
1219
+
1220
+ dev_rank = 1 if dev is None else 0
1221
+ dev_n = 0 if dev is None else dev[1]
1222
+
1223
+ suffix = (pre_rank, pre_n, post_rank, post_n, dev_rank, dev_n)
1224
+
1225
+ if local is None:
1226
+ return epoch, trimmed, suffix
1227
+
1228
+ cmp_local: CmpLocalType = tuple(
1229
+ (seg, "") if isinstance(seg, int) else (_LOCAL_STR_RANK, seg) for seg in local
1230
+ )
1231
+ return epoch, trimmed, suffix, cmp_local
.venv/lib/python3.14/site-packages/pip/_vendor/pkg_resources/LICENSE ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Permission is hereby granted, free of charge, to any person obtaining a copy
2
+ of this software and associated documentation files (the "Software"), to
3
+ deal in the Software without restriction, including without limitation the
4
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
5
+ sell copies of the Software, and to permit persons to whom the Software is
6
+ furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in
9
+ all copies or substantial portions of the Software.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
16
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
17
+ IN THE SOFTWARE.
.venv/lib/python3.14/site-packages/pip/_vendor/pkg_resources/__init__.py ADDED
The diff for this file is too large to render. See raw diff
 
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2010-202x The platformdirs developers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/__init__.py ADDED
@@ -0,0 +1,917 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utilities for determining application-specific dirs.
2
+
3
+ Provides convenience functions (e.g. :func:`user_data_dir`, :func:`user_config_path`), a :data:`PlatformDirs` class that
4
+ auto-detects the current platform, and the :class:`~platformdirs.api.PlatformDirsABC` base class.
5
+
6
+ See <https://github.com/platformdirs/platformdirs> for details and usage.
7
+
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ import sys
14
+ from typing import TYPE_CHECKING
15
+
16
+ from .api import PlatformDirsABC
17
+ from .version import __version__
18
+ from .version import __version_tuple__ as __version_info__
19
+
20
+ if TYPE_CHECKING:
21
+ from pathlib import Path
22
+ from typing import Literal
23
+
24
+ if sys.platform == "win32":
25
+ from pip._vendor.platformdirs.windows import Windows as _Result
26
+ elif sys.platform == "darwin":
27
+ from pip._vendor.platformdirs.macos import MacOS as _Result
28
+ else:
29
+ from pip._vendor.platformdirs.unix import Unix as _Result
30
+
31
+
32
+ def _set_platform_dir_class() -> type[PlatformDirsABC]:
33
+ if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system":
34
+ if os.getenv("SHELL") or os.getenv("PREFIX"):
35
+ return _Result
36
+
37
+ from pip._vendor.platformdirs.android import _android_folder # noqa: PLC0415
38
+
39
+ if _android_folder() is not None:
40
+ from pip._vendor.platformdirs.android import Android # noqa: PLC0415
41
+
42
+ return Android # return to avoid redefinition of a result
43
+
44
+ return _Result
45
+
46
+
47
+ if TYPE_CHECKING:
48
+ # Work around mypy issue: https://github.com/python/mypy/issues/10962
49
+ PlatformDirs = _Result
50
+ else:
51
+ PlatformDirs = _set_platform_dir_class() #: Currently active platform
52
+ AppDirs = PlatformDirs #: Backwards compatibility with appdirs
53
+
54
+
55
+ def user_data_dir( # noqa: PLR0913, PLR0917
56
+ appname: str | None = None,
57
+ appauthor: str | Literal[False] | None = None,
58
+ version: str | None = None,
59
+ roaming: bool = False, # noqa: FBT001, FBT002
60
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
61
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
62
+ ) -> str:
63
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
64
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
65
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
66
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
67
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
68
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
69
+
70
+ :returns: data directory tied to the user
71
+
72
+ """
73
+ return PlatformDirs(
74
+ appname=appname,
75
+ appauthor=appauthor,
76
+ version=version,
77
+ roaming=roaming,
78
+ ensure_exists=ensure_exists,
79
+ use_site_for_root=use_site_for_root,
80
+ ).user_data_dir
81
+
82
+
83
+ def site_data_dir(
84
+ appname: str | None = None,
85
+ appauthor: str | Literal[False] | None = None,
86
+ version: str | None = None,
87
+ multipath: bool = False, # noqa: FBT001, FBT002
88
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
89
+ ) -> str:
90
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
91
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
92
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
93
+ :param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`.
94
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
95
+
96
+ :returns: data directory shared by users
97
+
98
+ """
99
+ return PlatformDirs(
100
+ appname=appname,
101
+ appauthor=appauthor,
102
+ version=version,
103
+ multipath=multipath,
104
+ ensure_exists=ensure_exists,
105
+ ).site_data_dir
106
+
107
+
108
+ def user_config_dir( # noqa: PLR0913, PLR0917
109
+ appname: str | None = None,
110
+ appauthor: str | Literal[False] | None = None,
111
+ version: str | None = None,
112
+ roaming: bool = False, # noqa: FBT001, FBT002
113
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
114
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
115
+ ) -> str:
116
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
117
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
118
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
119
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
120
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
121
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
122
+
123
+ :returns: config directory tied to the user
124
+
125
+ """
126
+ return PlatformDirs(
127
+ appname=appname,
128
+ appauthor=appauthor,
129
+ version=version,
130
+ roaming=roaming,
131
+ ensure_exists=ensure_exists,
132
+ use_site_for_root=use_site_for_root,
133
+ ).user_config_dir
134
+
135
+
136
+ def site_config_dir(
137
+ appname: str | None = None,
138
+ appauthor: str | Literal[False] | None = None,
139
+ version: str | None = None,
140
+ multipath: bool = False, # noqa: FBT001, FBT002
141
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
142
+ ) -> str:
143
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
144
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
145
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
146
+ :param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`.
147
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
148
+
149
+ :returns: config directory shared by users
150
+
151
+ """
152
+ return PlatformDirs(
153
+ appname=appname,
154
+ appauthor=appauthor,
155
+ version=version,
156
+ multipath=multipath,
157
+ ensure_exists=ensure_exists,
158
+ ).site_config_dir
159
+
160
+
161
+ def user_cache_dir( # noqa: PLR0913, PLR0917
162
+ appname: str | None = None,
163
+ appauthor: str | Literal[False] | None = None,
164
+ version: str | None = None,
165
+ opinion: bool = True, # noqa: FBT001, FBT002
166
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
167
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
168
+ ) -> str:
169
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
170
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
171
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
172
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
173
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
174
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
175
+
176
+ :returns: cache directory tied to the user
177
+
178
+ """
179
+ return PlatformDirs(
180
+ appname=appname,
181
+ appauthor=appauthor,
182
+ version=version,
183
+ opinion=opinion,
184
+ ensure_exists=ensure_exists,
185
+ use_site_for_root=use_site_for_root,
186
+ ).user_cache_dir
187
+
188
+
189
+ def site_cache_dir(
190
+ appname: str | None = None,
191
+ appauthor: str | Literal[False] | None = None,
192
+ version: str | None = None,
193
+ opinion: bool = True, # noqa: FBT001, FBT002
194
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
195
+ ) -> str:
196
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
197
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
198
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
199
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
200
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
201
+
202
+ :returns: cache directory shared by users
203
+
204
+ """
205
+ return PlatformDirs(
206
+ appname=appname,
207
+ appauthor=appauthor,
208
+ version=version,
209
+ opinion=opinion,
210
+ ensure_exists=ensure_exists,
211
+ ).site_cache_dir
212
+
213
+
214
+ def user_state_dir( # noqa: PLR0913, PLR0917
215
+ appname: str | None = None,
216
+ appauthor: str | Literal[False] | None = None,
217
+ version: str | None = None,
218
+ roaming: bool = False, # noqa: FBT001, FBT002
219
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
220
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
221
+ ) -> str:
222
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
223
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
224
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
225
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
226
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
227
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
228
+
229
+ :returns: state directory tied to the user
230
+
231
+ """
232
+ return PlatformDirs(
233
+ appname=appname,
234
+ appauthor=appauthor,
235
+ version=version,
236
+ roaming=roaming,
237
+ ensure_exists=ensure_exists,
238
+ use_site_for_root=use_site_for_root,
239
+ ).user_state_dir
240
+
241
+
242
+ def site_state_dir(
243
+ appname: str | None = None,
244
+ appauthor: str | Literal[False] | None = None,
245
+ version: str | None = None,
246
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
247
+ ) -> str:
248
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
249
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
250
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
251
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
252
+
253
+ :returns: state directory shared by users
254
+
255
+ """
256
+ return PlatformDirs(
257
+ appname=appname,
258
+ appauthor=appauthor,
259
+ version=version,
260
+ ensure_exists=ensure_exists,
261
+ ).site_state_dir
262
+
263
+
264
+ def user_log_dir( # noqa: PLR0913, PLR0917
265
+ appname: str | None = None,
266
+ appauthor: str | Literal[False] | None = None,
267
+ version: str | None = None,
268
+ opinion: bool = True, # noqa: FBT001, FBT002
269
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
270
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
271
+ ) -> str:
272
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
273
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
274
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
275
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
276
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
277
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
278
+
279
+ :returns: log directory tied to the user
280
+
281
+ """
282
+ return PlatformDirs(
283
+ appname=appname,
284
+ appauthor=appauthor,
285
+ version=version,
286
+ opinion=opinion,
287
+ ensure_exists=ensure_exists,
288
+ use_site_for_root=use_site_for_root,
289
+ ).user_log_dir
290
+
291
+
292
+ def site_log_dir(
293
+ appname: str | None = None,
294
+ appauthor: str | Literal[False] | None = None,
295
+ version: str | None = None,
296
+ opinion: bool = True, # noqa: FBT001, FBT002
297
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
298
+ ) -> str:
299
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
300
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
301
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
302
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
303
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
304
+
305
+ :returns: log directory shared by users
306
+
307
+ """
308
+ return PlatformDirs(
309
+ appname=appname,
310
+ appauthor=appauthor,
311
+ version=version,
312
+ opinion=opinion,
313
+ ensure_exists=ensure_exists,
314
+ ).site_log_dir
315
+
316
+
317
+ def user_documents_dir() -> str:
318
+ """:returns: documents directory tied to the user"""
319
+ return PlatformDirs().user_documents_dir
320
+
321
+
322
+ def user_downloads_dir() -> str:
323
+ """:returns: downloads directory tied to the user"""
324
+ return PlatformDirs().user_downloads_dir
325
+
326
+
327
+ def user_pictures_dir() -> str:
328
+ """:returns: pictures directory tied to the user"""
329
+ return PlatformDirs().user_pictures_dir
330
+
331
+
332
+ def user_videos_dir() -> str:
333
+ """:returns: videos directory tied to the user"""
334
+ return PlatformDirs().user_videos_dir
335
+
336
+
337
+ def user_music_dir() -> str:
338
+ """:returns: music directory tied to the user"""
339
+ return PlatformDirs().user_music_dir
340
+
341
+
342
+ def user_desktop_dir() -> str:
343
+ """:returns: desktop directory tied to the user"""
344
+ return PlatformDirs().user_desktop_dir
345
+
346
+
347
+ def user_projects_dir() -> str:
348
+ """:returns: projects directory tied to the user"""
349
+ return PlatformDirs().user_projects_dir
350
+
351
+
352
+ def user_publicshare_dir() -> str:
353
+ """:returns: public share directory tied to the user"""
354
+ return PlatformDirs().user_publicshare_dir
355
+
356
+
357
+ def user_templates_dir() -> str:
358
+ """:returns: templates directory tied to the user"""
359
+ return PlatformDirs().user_templates_dir
360
+
361
+
362
+ def user_fonts_dir() -> str:
363
+ """:returns: fonts directory tied to the user"""
364
+ return PlatformDirs().user_fonts_dir
365
+
366
+
367
+ def user_preference_dir() -> str:
368
+ """:returns: preference directory tied to the user"""
369
+ return PlatformDirs().user_preference_dir
370
+
371
+
372
+ def user_bin_dir() -> str:
373
+ """:returns: bin directory tied to the user"""
374
+ return PlatformDirs().user_bin_dir
375
+
376
+
377
+ def site_bin_dir() -> str:
378
+ """:returns: bin directory shared by users"""
379
+ return PlatformDirs().site_bin_dir
380
+
381
+
382
+ def user_applications_dir() -> str:
383
+ """:returns: applications directory tied to the user"""
384
+ return PlatformDirs().user_applications_dir
385
+
386
+
387
+ def site_applications_dir(
388
+ multipath: bool = False, # noqa: FBT001, FBT002
389
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
390
+ ) -> str:
391
+ """:param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`.
392
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
393
+
394
+ :returns: applications directory shared by users
395
+
396
+ """
397
+ return PlatformDirs(
398
+ multipath=multipath,
399
+ ensure_exists=ensure_exists,
400
+ ).site_applications_dir
401
+
402
+
403
+ def user_runtime_dir( # noqa: PLR0913, PLR0917
404
+ appname: str | None = None,
405
+ appauthor: str | Literal[False] | None = None,
406
+ version: str | None = None,
407
+ opinion: bool = True, # noqa: FBT001, FBT002
408
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
409
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
410
+ ) -> str:
411
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
412
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
413
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
414
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
415
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
416
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
417
+
418
+ :returns: runtime directory tied to the user
419
+
420
+ """
421
+ return PlatformDirs(
422
+ appname=appname,
423
+ appauthor=appauthor,
424
+ version=version,
425
+ opinion=opinion,
426
+ ensure_exists=ensure_exists,
427
+ use_site_for_root=use_site_for_root,
428
+ ).user_runtime_dir
429
+
430
+
431
+ def site_runtime_dir(
432
+ appname: str | None = None,
433
+ appauthor: str | Literal[False] | None = None,
434
+ version: str | None = None,
435
+ opinion: bool = True, # noqa: FBT001, FBT002
436
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
437
+ ) -> str:
438
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
439
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
440
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
441
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
442
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
443
+
444
+ :returns: runtime directory shared by users
445
+
446
+ """
447
+ return PlatformDirs(
448
+ appname=appname,
449
+ appauthor=appauthor,
450
+ version=version,
451
+ opinion=opinion,
452
+ ensure_exists=ensure_exists,
453
+ ).site_runtime_dir
454
+
455
+
456
+ def user_data_path( # noqa: PLR0913, PLR0917
457
+ appname: str | None = None,
458
+ appauthor: str | Literal[False] | None = None,
459
+ version: str | None = None,
460
+ roaming: bool = False, # noqa: FBT001, FBT002
461
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
462
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
463
+ ) -> Path:
464
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
465
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
466
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
467
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
468
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
469
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
470
+
471
+ :returns: data path tied to the user
472
+
473
+ """
474
+ return PlatformDirs(
475
+ appname=appname,
476
+ appauthor=appauthor,
477
+ version=version,
478
+ roaming=roaming,
479
+ ensure_exists=ensure_exists,
480
+ use_site_for_root=use_site_for_root,
481
+ ).user_data_path
482
+
483
+
484
+ def site_data_path(
485
+ appname: str | None = None,
486
+ appauthor: str | Literal[False] | None = None,
487
+ version: str | None = None,
488
+ multipath: bool = False, # noqa: FBT001, FBT002
489
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
490
+ ) -> Path:
491
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
492
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
493
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
494
+ :param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`.
495
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
496
+
497
+ :returns: data path shared by users
498
+
499
+ """
500
+ return PlatformDirs(
501
+ appname=appname,
502
+ appauthor=appauthor,
503
+ version=version,
504
+ multipath=multipath,
505
+ ensure_exists=ensure_exists,
506
+ ).site_data_path
507
+
508
+
509
+ def user_config_path( # noqa: PLR0913, PLR0917
510
+ appname: str | None = None,
511
+ appauthor: str | Literal[False] | None = None,
512
+ version: str | None = None,
513
+ roaming: bool = False, # noqa: FBT001, FBT002
514
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
515
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
516
+ ) -> Path:
517
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
518
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
519
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
520
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
521
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
522
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
523
+
524
+ :returns: config path tied to the user
525
+
526
+ """
527
+ return PlatformDirs(
528
+ appname=appname,
529
+ appauthor=appauthor,
530
+ version=version,
531
+ roaming=roaming,
532
+ ensure_exists=ensure_exists,
533
+ use_site_for_root=use_site_for_root,
534
+ ).user_config_path
535
+
536
+
537
+ def site_config_path(
538
+ appname: str | None = None,
539
+ appauthor: str | Literal[False] | None = None,
540
+ version: str | None = None,
541
+ multipath: bool = False, # noqa: FBT001, FBT002
542
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
543
+ ) -> Path:
544
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
545
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
546
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
547
+ :param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`.
548
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
549
+
550
+ :returns: config path shared by users
551
+
552
+ """
553
+ return PlatformDirs(
554
+ appname=appname,
555
+ appauthor=appauthor,
556
+ version=version,
557
+ multipath=multipath,
558
+ ensure_exists=ensure_exists,
559
+ ).site_config_path
560
+
561
+
562
+ def site_cache_path(
563
+ appname: str | None = None,
564
+ appauthor: str | Literal[False] | None = None,
565
+ version: str | None = None,
566
+ opinion: bool = True, # noqa: FBT001, FBT002
567
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
568
+ ) -> Path:
569
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
570
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
571
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
572
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
573
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
574
+
575
+ :returns: cache path shared by users
576
+
577
+ """
578
+ return PlatformDirs(
579
+ appname=appname,
580
+ appauthor=appauthor,
581
+ version=version,
582
+ opinion=opinion,
583
+ ensure_exists=ensure_exists,
584
+ ).site_cache_path
585
+
586
+
587
+ def user_cache_path( # noqa: PLR0913, PLR0917
588
+ appname: str | None = None,
589
+ appauthor: str | Literal[False] | None = None,
590
+ version: str | None = None,
591
+ opinion: bool = True, # noqa: FBT001, FBT002
592
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
593
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
594
+ ) -> Path:
595
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
596
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
597
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
598
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
599
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
600
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
601
+
602
+ :returns: cache path tied to the user
603
+
604
+ """
605
+ return PlatformDirs(
606
+ appname=appname,
607
+ appauthor=appauthor,
608
+ version=version,
609
+ opinion=opinion,
610
+ ensure_exists=ensure_exists,
611
+ use_site_for_root=use_site_for_root,
612
+ ).user_cache_path
613
+
614
+
615
+ def user_state_path( # noqa: PLR0913, PLR0917
616
+ appname: str | None = None,
617
+ appauthor: str | Literal[False] | None = None,
618
+ version: str | None = None,
619
+ roaming: bool = False, # noqa: FBT001, FBT002
620
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
621
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
622
+ ) -> Path:
623
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
624
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
625
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
626
+ :param roaming: See `roaming <platformdirs.api.PlatformDirsABC.roaming>`.
627
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
628
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
629
+
630
+ :returns: state path tied to the user
631
+
632
+ """
633
+ return PlatformDirs(
634
+ appname=appname,
635
+ appauthor=appauthor,
636
+ version=version,
637
+ roaming=roaming,
638
+ ensure_exists=ensure_exists,
639
+ use_site_for_root=use_site_for_root,
640
+ ).user_state_path
641
+
642
+
643
+ def site_state_path(
644
+ appname: str | None = None,
645
+ appauthor: str | Literal[False] | None = None,
646
+ version: str | None = None,
647
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
648
+ ) -> Path:
649
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
650
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
651
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
652
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
653
+
654
+ :returns: state path shared by users
655
+
656
+ """
657
+ return PlatformDirs(
658
+ appname=appname,
659
+ appauthor=appauthor,
660
+ version=version,
661
+ ensure_exists=ensure_exists,
662
+ ).site_state_path
663
+
664
+
665
+ def user_log_path( # noqa: PLR0913, PLR0917
666
+ appname: str | None = None,
667
+ appauthor: str | Literal[False] | None = None,
668
+ version: str | None = None,
669
+ opinion: bool = True, # noqa: FBT001, FBT002
670
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
671
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
672
+ ) -> Path:
673
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
674
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
675
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
676
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
677
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
678
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
679
+
680
+ :returns: log path tied to the user
681
+
682
+ """
683
+ return PlatformDirs(
684
+ appname=appname,
685
+ appauthor=appauthor,
686
+ version=version,
687
+ opinion=opinion,
688
+ ensure_exists=ensure_exists,
689
+ use_site_for_root=use_site_for_root,
690
+ ).user_log_path
691
+
692
+
693
+ def site_log_path(
694
+ appname: str | None = None,
695
+ appauthor: str | Literal[False] | None = None,
696
+ version: str | None = None,
697
+ opinion: bool = True, # noqa: FBT001, FBT002
698
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
699
+ ) -> Path:
700
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
701
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
702
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
703
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
704
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
705
+
706
+ :returns: log path shared by users
707
+
708
+ """
709
+ return PlatformDirs(
710
+ appname=appname,
711
+ appauthor=appauthor,
712
+ version=version,
713
+ opinion=opinion,
714
+ ensure_exists=ensure_exists,
715
+ ).site_log_path
716
+
717
+
718
+ def user_documents_path() -> Path:
719
+ """:returns: documents path tied to the user"""
720
+ return PlatformDirs().user_documents_path
721
+
722
+
723
+ def user_downloads_path() -> Path:
724
+ """:returns: downloads path tied to the user"""
725
+ return PlatformDirs().user_downloads_path
726
+
727
+
728
+ def user_pictures_path() -> Path:
729
+ """:returns: pictures path tied to the user"""
730
+ return PlatformDirs().user_pictures_path
731
+
732
+
733
+ def user_videos_path() -> Path:
734
+ """:returns: videos path tied to the user"""
735
+ return PlatformDirs().user_videos_path
736
+
737
+
738
+ def user_music_path() -> Path:
739
+ """:returns: music path tied to the user"""
740
+ return PlatformDirs().user_music_path
741
+
742
+
743
+ def user_desktop_path() -> Path:
744
+ """:returns: desktop path tied to the user"""
745
+ return PlatformDirs().user_desktop_path
746
+
747
+
748
+ def user_projects_path() -> Path:
749
+ """:returns: projects path tied to the user"""
750
+ return PlatformDirs().user_projects_path
751
+
752
+
753
+ def user_publicshare_path() -> Path:
754
+ """:returns: public share path tied to the user"""
755
+ return PlatformDirs().user_publicshare_path
756
+
757
+
758
+ def user_templates_path() -> Path:
759
+ """:returns: templates path tied to the user"""
760
+ return PlatformDirs().user_templates_path
761
+
762
+
763
+ def user_fonts_path() -> Path:
764
+ """:returns: fonts path tied to the user"""
765
+ return PlatformDirs().user_fonts_path
766
+
767
+
768
+ def user_preference_path() -> Path:
769
+ """:returns: preference path tied to the user"""
770
+ return PlatformDirs().user_preference_path
771
+
772
+
773
+ def user_bin_path() -> Path:
774
+ """:returns: bin path tied to the user"""
775
+ return PlatformDirs().user_bin_path
776
+
777
+
778
+ def site_bin_path() -> Path:
779
+ """:returns: bin path shared by users"""
780
+ return PlatformDirs().site_bin_path
781
+
782
+
783
+ def user_applications_path() -> Path:
784
+ """:returns: applications path tied to the user"""
785
+ return PlatformDirs().user_applications_path
786
+
787
+
788
+ def site_applications_path(
789
+ multipath: bool = False, # noqa: FBT001, FBT002
790
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
791
+ ) -> Path:
792
+ """:param multipath: See `multipath <platformdirs.api.PlatformDirsABC.multipath>`.
793
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
794
+
795
+ :returns: applications path shared by users
796
+
797
+ """
798
+ return PlatformDirs(
799
+ multipath=multipath,
800
+ ensure_exists=ensure_exists,
801
+ ).site_applications_path
802
+
803
+
804
+ def user_runtime_path( # noqa: PLR0913, PLR0917
805
+ appname: str | None = None,
806
+ appauthor: str | Literal[False] | None = None,
807
+ version: str | None = None,
808
+ opinion: bool = True, # noqa: FBT001, FBT002
809
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
810
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
811
+ ) -> Path:
812
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
813
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
814
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
815
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
816
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
817
+ :param use_site_for_root: See `use_site_for_root <platformdirs.api.PlatformDirsABC.use_site_for_root>`.
818
+
819
+ :returns: runtime path tied to the user
820
+
821
+ """
822
+ return PlatformDirs(
823
+ appname=appname,
824
+ appauthor=appauthor,
825
+ version=version,
826
+ opinion=opinion,
827
+ ensure_exists=ensure_exists,
828
+ use_site_for_root=use_site_for_root,
829
+ ).user_runtime_path
830
+
831
+
832
+ def site_runtime_path(
833
+ appname: str | None = None,
834
+ appauthor: str | Literal[False] | None = None,
835
+ version: str | None = None,
836
+ opinion: bool = True, # noqa: FBT001, FBT002
837
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
838
+ ) -> Path:
839
+ """:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
840
+ :param appauthor: See `appauthor <platformdirs.api.PlatformDirsABC.appauthor>`.
841
+ :param version: See `version <platformdirs.api.PlatformDirsABC.version>`.
842
+ :param opinion: See `opinion <platformdirs.api.PlatformDirsABC.opinion>`.
843
+ :param ensure_exists: See `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
844
+
845
+ :returns: runtime path shared by users
846
+
847
+ """
848
+ return PlatformDirs(
849
+ appname=appname,
850
+ appauthor=appauthor,
851
+ version=version,
852
+ opinion=opinion,
853
+ ensure_exists=ensure_exists,
854
+ ).site_runtime_path
855
+
856
+
857
+ __all__ = [
858
+ "AppDirs",
859
+ "PlatformDirs",
860
+ "PlatformDirsABC",
861
+ "__version__",
862
+ "__version_info__",
863
+ "site_applications_dir",
864
+ "site_applications_path",
865
+ "site_bin_dir",
866
+ "site_bin_path",
867
+ "site_cache_dir",
868
+ "site_cache_path",
869
+ "site_config_dir",
870
+ "site_config_path",
871
+ "site_data_dir",
872
+ "site_data_path",
873
+ "site_log_dir",
874
+ "site_log_path",
875
+ "site_runtime_dir",
876
+ "site_runtime_path",
877
+ "site_state_dir",
878
+ "site_state_path",
879
+ "user_applications_dir",
880
+ "user_applications_path",
881
+ "user_bin_dir",
882
+ "user_bin_path",
883
+ "user_cache_dir",
884
+ "user_cache_path",
885
+ "user_config_dir",
886
+ "user_config_path",
887
+ "user_data_dir",
888
+ "user_data_path",
889
+ "user_desktop_dir",
890
+ "user_desktop_path",
891
+ "user_documents_dir",
892
+ "user_documents_path",
893
+ "user_downloads_dir",
894
+ "user_downloads_path",
895
+ "user_fonts_dir",
896
+ "user_fonts_path",
897
+ "user_log_dir",
898
+ "user_log_path",
899
+ "user_music_dir",
900
+ "user_music_path",
901
+ "user_pictures_dir",
902
+ "user_pictures_path",
903
+ "user_preference_dir",
904
+ "user_preference_path",
905
+ "user_projects_dir",
906
+ "user_projects_path",
907
+ "user_publicshare_dir",
908
+ "user_publicshare_path",
909
+ "user_runtime_dir",
910
+ "user_runtime_path",
911
+ "user_state_dir",
912
+ "user_state_path",
913
+ "user_templates_dir",
914
+ "user_templates_path",
915
+ "user_videos_dir",
916
+ "user_videos_path",
917
+ ]
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/__main__.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Main entry point."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pip._vendor.platformdirs import PlatformDirs, __version__
6
+
7
+ PROPS = (
8
+ "user_data_dir",
9
+ "user_config_dir",
10
+ "user_cache_dir",
11
+ "user_state_dir",
12
+ "user_log_dir",
13
+ "user_documents_dir",
14
+ "user_downloads_dir",
15
+ "user_pictures_dir",
16
+ "user_videos_dir",
17
+ "user_music_dir",
18
+ "user_projects_dir",
19
+ "user_publicshare_dir",
20
+ "user_templates_dir",
21
+ "user_fonts_dir",
22
+ "user_preference_dir",
23
+ "user_bin_dir",
24
+ "site_bin_dir",
25
+ "user_applications_dir",
26
+ "user_runtime_dir",
27
+ "site_data_dir",
28
+ "site_config_dir",
29
+ "site_cache_dir",
30
+ "site_state_dir",
31
+ "site_log_dir",
32
+ "site_applications_dir",
33
+ "site_runtime_dir",
34
+ )
35
+
36
+
37
+ def main() -> None:
38
+ """Run the main entry point."""
39
+ app_name = "MyApp"
40
+ app_author = "MyCompany"
41
+
42
+ print(f"-- platformdirs {__version__} --") # noqa: T201
43
+
44
+ print("-- app dirs (with optional 'version')") # noqa: T201
45
+ dirs = PlatformDirs(app_name, app_author, version="1.0")
46
+ for prop in PROPS:
47
+ print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
48
+
49
+ print("\n-- app dirs (without optional 'version')") # noqa: T201
50
+ dirs = PlatformDirs(app_name, app_author)
51
+ for prop in PROPS:
52
+ print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
53
+
54
+ print("\n-- app dirs (without optional 'appauthor')") # noqa: T201
55
+ dirs = PlatformDirs(app_name)
56
+ for prop in PROPS:
57
+ print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
58
+
59
+ print("\n-- app dirs (with disabled 'appauthor')") # noqa: T201
60
+ dirs = PlatformDirs(app_name, appauthor=False)
61
+ for prop in PROPS:
62
+ print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
63
+
64
+
65
+ if __name__ == "__main__":
66
+ main()
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/_xdg.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """XDG environment variable mixin for Unix and macOS."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+ from .api import PlatformDirsABC
8
+
9
+
10
+ class XDGMixin(PlatformDirsABC):
11
+ """Mixin that checks XDG environment variables, falling back to platform-specific defaults via ``super()``."""
12
+
13
+ @property
14
+ def user_data_dir(self) -> str:
15
+ """:returns: data directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default"""
16
+ if path := os.environ.get("XDG_DATA_HOME", "").strip():
17
+ return self._append_app_name_and_version(path)
18
+ return super().user_data_dir
19
+
20
+ @property
21
+ def _site_data_dirs(self) -> list[str]:
22
+ if xdg_dirs := os.environ.get("XDG_DATA_DIRS", "").strip():
23
+ return [self._append_app_name_and_version(p) for p in xdg_dirs.split(os.pathsep) if p.strip()]
24
+ return super()._site_data_dirs
25
+
26
+ @property
27
+ def site_data_dir(self) -> str:
28
+ """:returns: data directories shared by users, from ``$XDG_DATA_DIRS`` if set, else platform default"""
29
+ dirs = self._site_data_dirs
30
+ return os.pathsep.join(dirs) if self.multipath else dirs[0]
31
+
32
+ @property
33
+ def user_config_dir(self) -> str:
34
+ """:returns: config directory tied to the user, from ``$XDG_CONFIG_HOME`` if set, else platform default"""
35
+ if path := os.environ.get("XDG_CONFIG_HOME", "").strip():
36
+ return self._append_app_name_and_version(path)
37
+ return super().user_config_dir
38
+
39
+ @property
40
+ def _site_config_dirs(self) -> list[str]:
41
+ if xdg_dirs := os.environ.get("XDG_CONFIG_DIRS", "").strip():
42
+ return [self._append_app_name_and_version(p) for p in xdg_dirs.split(os.pathsep) if p.strip()]
43
+ return super()._site_config_dirs
44
+
45
+ @property
46
+ def site_config_dir(self) -> str:
47
+ """:returns: config directories shared by users, from ``$XDG_CONFIG_DIRS`` if set, else platform default"""
48
+ dirs = self._site_config_dirs
49
+ return os.pathsep.join(dirs) if self.multipath else dirs[0]
50
+
51
+ @property
52
+ def user_cache_dir(self) -> str:
53
+ """:returns: cache directory tied to the user, from ``$XDG_CACHE_HOME`` if set, else platform default"""
54
+ if path := os.environ.get("XDG_CACHE_HOME", "").strip():
55
+ return self._append_app_name_and_version(path)
56
+ return super().user_cache_dir
57
+
58
+ @property
59
+ def user_state_dir(self) -> str:
60
+ """:returns: state directory tied to the user, from ``$XDG_STATE_HOME`` if set, else platform default"""
61
+ if path := os.environ.get("XDG_STATE_HOME", "").strip():
62
+ return self._append_app_name_and_version(path)
63
+ return super().user_state_dir
64
+
65
+ @property
66
+ def user_runtime_dir(self) -> str:
67
+ """:returns: runtime directory tied to the user, from ``$XDG_RUNTIME_DIR`` if set, else platform default"""
68
+ if path := os.environ.get("XDG_RUNTIME_DIR", "").strip():
69
+ return self._append_app_name_and_version(path)
70
+ return super().user_runtime_dir
71
+
72
+ @property
73
+ def site_runtime_dir(self) -> str:
74
+ """:returns: runtime directory shared by users, from ``$XDG_RUNTIME_DIR`` if set, else platform default"""
75
+ if path := os.environ.get("XDG_RUNTIME_DIR", "").strip():
76
+ return self._append_app_name_and_version(path)
77
+ return super().site_runtime_dir
78
+
79
+ @property
80
+ def user_documents_dir(self) -> str:
81
+ """:returns: documents directory tied to the user, from ``$XDG_DOCUMENTS_DIR`` if set, else platform default"""
82
+ if path := os.environ.get("XDG_DOCUMENTS_DIR", "").strip():
83
+ return os.path.expanduser(path) # noqa: PTH111
84
+ return super().user_documents_dir
85
+
86
+ @property
87
+ def user_downloads_dir(self) -> str:
88
+ """:returns: downloads directory tied to the user, from ``$XDG_DOWNLOAD_DIR`` if set, else platform default"""
89
+ if path := os.environ.get("XDG_DOWNLOAD_DIR", "").strip():
90
+ return os.path.expanduser(path) # noqa: PTH111
91
+ return super().user_downloads_dir
92
+
93
+ @property
94
+ def user_pictures_dir(self) -> str:
95
+ """:returns: pictures directory tied to the user, from ``$XDG_PICTURES_DIR`` if set, else platform default"""
96
+ if path := os.environ.get("XDG_PICTURES_DIR", "").strip():
97
+ return os.path.expanduser(path) # noqa: PTH111
98
+ return super().user_pictures_dir
99
+
100
+ @property
101
+ def user_videos_dir(self) -> str:
102
+ """:returns: videos directory tied to the user, from ``$XDG_VIDEOS_DIR`` if set, else platform default"""
103
+ if path := os.environ.get("XDG_VIDEOS_DIR", "").strip():
104
+ return os.path.expanduser(path) # noqa: PTH111
105
+ return super().user_videos_dir
106
+
107
+ @property
108
+ def user_music_dir(self) -> str:
109
+ """:returns: music directory tied to the user, from ``$XDG_MUSIC_DIR`` if set, else platform default"""
110
+ if path := os.environ.get("XDG_MUSIC_DIR", "").strip():
111
+ return os.path.expanduser(path) # noqa: PTH111
112
+ return super().user_music_dir
113
+
114
+ @property
115
+ def user_desktop_dir(self) -> str:
116
+ """:returns: desktop directory tied to the user, from ``$XDG_DESKTOP_DIR`` if set, else platform default"""
117
+ if path := os.environ.get("XDG_DESKTOP_DIR", "").strip():
118
+ return os.path.expanduser(path) # noqa: PTH111
119
+ return super().user_desktop_dir
120
+
121
+ @property
122
+ def user_projects_dir(self) -> str:
123
+ """:returns: projects directory tied to the user, from ``$XDG_PROJECTS_DIR`` if set, else platform default"""
124
+ if path := os.environ.get("XDG_PROJECTS_DIR", "").strip():
125
+ return os.path.expanduser(path) # noqa: PTH111 # API returns str, not Path
126
+ return super().user_projects_dir
127
+
128
+ @property
129
+ def user_publicshare_dir(self) -> str:
130
+ """:returns: public share directory tied to the user, from ``$XDG_PUBLICSHARE_DIR`` if set, else platform default"""
131
+ if path := os.environ.get("XDG_PUBLICSHARE_DIR", "").strip():
132
+ return os.path.expanduser(path) # noqa: PTH111 # API returns str, not Path
133
+ return super().user_publicshare_dir
134
+
135
+ @property
136
+ def user_templates_dir(self) -> str:
137
+ """:returns: templates directory tied to the user, from ``$XDG_TEMPLATES_DIR`` if set, else platform default"""
138
+ if path := os.environ.get("XDG_TEMPLATES_DIR", "").strip():
139
+ return os.path.expanduser(path) # noqa: PTH111 # API returns str, not Path
140
+ return super().user_templates_dir
141
+
142
+ @property
143
+ def user_fonts_dir(self) -> str:
144
+ """:returns: fonts directory tied to the user, from ``$XDG_DATA_HOME/fonts`` if set, else platform default"""
145
+ if path := os.environ.get("XDG_DATA_HOME", "").strip():
146
+ return f"{os.path.expanduser(path)}/fonts" # noqa: PTH111 # API returns str, not Path
147
+ return super().user_fonts_dir
148
+
149
+ @property
150
+ def user_applications_dir(self) -> str:
151
+ """:returns: applications directory tied to the user, from ``$XDG_DATA_HOME`` if set, else platform default"""
152
+ if path := os.environ.get("XDG_DATA_HOME", "").strip():
153
+ return os.path.join(os.path.expanduser(path), "applications") # noqa: PTH111, PTH118
154
+ return super().user_applications_dir
155
+
156
+ @property
157
+ def _site_applications_dirs(self) -> list[str]:
158
+ if xdg_dirs := os.environ.get("XDG_DATA_DIRS", "").strip():
159
+ return [os.path.join(p, "applications") for p in xdg_dirs.split(os.pathsep) if p.strip()] # noqa: PTH118
160
+ return super()._site_applications_dirs
161
+
162
+ @property
163
+ def site_applications_dir(self) -> str:
164
+ """:returns: applications directories shared by users, from ``$XDG_DATA_DIRS`` if set, else platform default"""
165
+ dirs = self._site_applications_dirs
166
+ return os.pathsep.join(dirs) if self.multipath else dirs[0]
167
+
168
+
169
+ __all__ = [
170
+ "XDGMixin",
171
+ ]
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/android.py ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Android."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import re
7
+ import sys
8
+ from functools import lru_cache
9
+ from typing import TYPE_CHECKING, cast
10
+
11
+ from .api import PlatformDirsABC
12
+
13
+
14
+ class Android(PlatformDirsABC): # noqa: PLR0904
15
+ """Platform directories for Android.
16
+
17
+ Follows the guidance `from here <https://android.stackexchange.com/a/216132>`_. Directories are typically located
18
+ under the app's private storage (``/data/user/<userid>/<packagename>/``).
19
+
20
+ Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>`, `version
21
+ <platformdirs.api.PlatformDirsABC.version>`, `opinion <platformdirs.api.PlatformDirsABC.opinion>`, `ensure_exists
22
+ <platformdirs.api.PlatformDirsABC.ensure_exists>`.
23
+
24
+ """
25
+
26
+ @property
27
+ def user_data_dir(self) -> str:
28
+ """:returns: data directory tied to the user, e.g. ``/data/user/<userid>/<packagename>/files/<AppName>``"""
29
+ return self._append_app_name_and_version(cast("str", _android_folder()), "files")
30
+
31
+ @property
32
+ def site_data_dir(self) -> str:
33
+ """:returns: data directory shared by users, same as `user_data_dir`"""
34
+ return self.user_data_dir
35
+
36
+ @property
37
+ def user_config_dir(self) -> str:
38
+ """:returns: config directory tied to the user, e.g. ``/data/user/<userid>/<packagename>/shared_prefs/<AppName>``"""
39
+ return self._append_app_name_and_version(cast("str", _android_folder()), "shared_prefs")
40
+
41
+ @property
42
+ def site_config_dir(self) -> str:
43
+ """:returns: config directory shared by users, same as `user_config_dir`"""
44
+ return self.user_config_dir
45
+
46
+ @property
47
+ def user_cache_dir(self) -> str:
48
+ """:returns: cache directory tied to the user, e.g.,``/data/user/<userid>/<packagename>/cache/<AppName>``"""
49
+ return self._append_app_name_and_version(cast("str", _android_folder()), "cache")
50
+
51
+ @property
52
+ def site_cache_dir(self) -> str:
53
+ """:returns: cache directory shared by users, same as `user_cache_dir`"""
54
+ return self.user_cache_dir
55
+
56
+ @property
57
+ def user_state_dir(self) -> str:
58
+ """:returns: state directory tied to the user, same as `user_data_dir`"""
59
+ return self.user_data_dir
60
+
61
+ @property
62
+ def site_state_dir(self) -> str:
63
+ """:returns: state directory shared by users, same as `user_state_dir`"""
64
+ return self.user_state_dir
65
+
66
+ @property
67
+ def user_log_dir(self) -> str:
68
+ """:returns: log directory tied to the user, same as `user_cache_dir` if not opinionated else ``log`` in it, e.g. ``/data/user/<userid>/<packagename>/cache/<AppName>/log``"""
69
+ path = self.user_cache_dir
70
+ if self.opinion:
71
+ path = os.path.join(path, "log") # noqa: PTH118
72
+ self._optionally_create_directory(path)
73
+ return path
74
+
75
+ @property
76
+ def site_log_dir(self) -> str:
77
+ """:returns: log directory shared by users, same as `user_log_dir`"""
78
+ return self.user_log_dir
79
+
80
+ @property
81
+ def user_documents_dir(self) -> str:
82
+ """:returns: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``"""
83
+ return _android_documents_folder()
84
+
85
+ @property
86
+ def user_downloads_dir(self) -> str:
87
+ """:returns: downloads directory tied to the user e.g. ``/storage/emulated/0/Downloads``"""
88
+ return _android_downloads_folder()
89
+
90
+ @property
91
+ def user_pictures_dir(self) -> str:
92
+ """:returns: pictures directory tied to the user e.g. ``/storage/emulated/0/Pictures``"""
93
+ return _android_pictures_folder()
94
+
95
+ @property
96
+ def user_videos_dir(self) -> str:
97
+ """:returns: videos directory tied to the user e.g. ``/storage/emulated/0/DCIM/Camera``"""
98
+ return _android_videos_folder()
99
+
100
+ @property
101
+ def user_music_dir(self) -> str:
102
+ """:returns: music directory tied to the user e.g. ``/storage/emulated/0/Music``"""
103
+ return _android_music_folder()
104
+
105
+ @property
106
+ def user_desktop_dir(self) -> str:
107
+ """:returns: desktop directory tied to the user e.g. ``/storage/emulated/0/Desktop``"""
108
+ return "/storage/emulated/0/Desktop"
109
+
110
+ @property
111
+ def user_projects_dir(self) -> str:
112
+ """:returns: projects directory tied to the user e.g. ``/storage/emulated/0/Projects``"""
113
+ return "/storage/emulated/0/Projects"
114
+
115
+ @property
116
+ def user_publicshare_dir(self) -> str:
117
+ """:returns: public share directory tied to the user e.g. ``/storage/emulated/0/Public``"""
118
+ return "/storage/emulated/0/Public"
119
+
120
+ @property
121
+ def user_templates_dir(self) -> str:
122
+ """:returns: templates directory tied to the user e.g. ``/storage/emulated/0/Templates``"""
123
+ return "/storage/emulated/0/Templates"
124
+
125
+ @property
126
+ def user_fonts_dir(self) -> str:
127
+ """:returns: fonts directory tied to the user e.g. ``/storage/emulated/0/fonts``"""
128
+ return "/storage/emulated/0/fonts"
129
+
130
+ @property
131
+ def user_preference_dir(self) -> str:
132
+ """:returns: preference directory tied to the user, same as ``user_config_dir``"""
133
+ return self.user_config_dir
134
+
135
+ @property
136
+ def user_bin_dir(self) -> str:
137
+ """:returns: bin directory tied to the user, e.g. ``/data/user/<userid>/<packagename>/files/bin``"""
138
+ return os.path.join(cast("str", _android_folder()), "files", "bin") # noqa: PTH118
139
+
140
+ @property
141
+ def site_bin_dir(self) -> str:
142
+ """:returns: bin directory shared by users, same as `user_bin_dir`"""
143
+ return self.user_bin_dir
144
+
145
+ @property
146
+ def user_applications_dir(self) -> str:
147
+ """:returns: applications directory tied to the user, same as `user_data_dir`"""
148
+ return self.user_data_dir
149
+
150
+ @property
151
+ def site_applications_dir(self) -> str:
152
+ """:returns: applications directory shared by users, same as `user_applications_dir`"""
153
+ return self.user_applications_dir
154
+
155
+ @property
156
+ def user_runtime_dir(self) -> str:
157
+ """:returns: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it, e.g. ``/data/user/<userid>/<packagename>/cache/<AppName>/tmp``"""
158
+ path = self.user_cache_dir
159
+ if self.opinion:
160
+ path = os.path.join(path, "tmp") # noqa: PTH118
161
+ self._optionally_create_directory(path)
162
+ return path
163
+
164
+ @property
165
+ def site_runtime_dir(self) -> str:
166
+ """:returns: runtime directory shared by users, same as `user_runtime_dir`"""
167
+ return self.user_runtime_dir
168
+
169
+
170
+ @lru_cache(maxsize=1)
171
+ def _android_folder() -> str | None: # noqa: C901
172
+ """:returns: base folder for the Android OS or None if it cannot be found"""
173
+ result: str | None = None
174
+ # type checker isn't happy with our "import android", just don't do this when type checking see
175
+ # https://stackoverflow.com/a/61394121
176
+ if not TYPE_CHECKING:
177
+ try:
178
+ # First try to get a path to android app using python4android (if available)...
179
+ from android import mActivity # noqa: PLC0415
180
+
181
+ context = cast("android.content.Context", mActivity.getApplicationContext()) # noqa: F821
182
+ result = context.getFilesDir().getParentFile().getAbsolutePath()
183
+ except Exception: # noqa: BLE001
184
+ result = None
185
+ if result is None:
186
+ try:
187
+ # ...and fall back to using plain pyjnius, if python4android isn't available or doesn't deliver any useful
188
+ # result...
189
+ from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import]
190
+
191
+ context = autoclass("android.content.Context")
192
+ result = context.getFilesDir().getParentFile().getAbsolutePath()
193
+ except Exception: # noqa: BLE001
194
+ result = None
195
+ if result is None:
196
+ # and if that fails, too, find an android folder looking at path on the sys.path
197
+ # warning: only works for apps installed under /data, not adopted storage etc.
198
+ pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files")
199
+ for path in sys.path:
200
+ if pattern.match(path):
201
+ result = path.split("/files")[0]
202
+ break
203
+ else:
204
+ result = None
205
+ if result is None:
206
+ # one last try: find an android folder looking at path on the sys.path taking adopted storage paths into
207
+ # account
208
+ pattern = re.compile(r"/mnt/expand/[a-fA-F0-9-]{36}/(data|user/\d+)/(.+)/files")
209
+ for path in sys.path:
210
+ if pattern.match(path):
211
+ result = path.split("/files")[0]
212
+ break
213
+ else:
214
+ result = None
215
+ return result
216
+
217
+
218
+ @lru_cache(maxsize=1)
219
+ def _android_documents_folder() -> str:
220
+ """:returns: documents folder for the Android OS"""
221
+ # Get directories with pyjnius
222
+ try:
223
+ from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import]
224
+
225
+ context = autoclass("android.content.Context")
226
+ environment = autoclass("android.os.Environment")
227
+ documents_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOCUMENTS).getAbsolutePath()
228
+ except Exception: # noqa: BLE001
229
+ documents_dir = "/storage/emulated/0/Documents"
230
+
231
+ return documents_dir
232
+
233
+
234
+ @lru_cache(maxsize=1)
235
+ def _android_downloads_folder() -> str:
236
+ """:returns: downloads folder for the Android OS"""
237
+ # Get directories with pyjnius
238
+ try:
239
+ from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import]
240
+
241
+ context = autoclass("android.content.Context")
242
+ environment = autoclass("android.os.Environment")
243
+ downloads_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOWNLOADS).getAbsolutePath()
244
+ except Exception: # noqa: BLE001
245
+ downloads_dir = "/storage/emulated/0/Downloads"
246
+
247
+ return downloads_dir
248
+
249
+
250
+ @lru_cache(maxsize=1)
251
+ def _android_pictures_folder() -> str:
252
+ """:returns: pictures folder for the Android OS"""
253
+ # Get directories with pyjnius
254
+ try:
255
+ from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import]
256
+
257
+ context = autoclass("android.content.Context")
258
+ environment = autoclass("android.os.Environment")
259
+ pictures_dir: str = context.getExternalFilesDir(environment.DIRECTORY_PICTURES).getAbsolutePath()
260
+ except Exception: # noqa: BLE001
261
+ pictures_dir = "/storage/emulated/0/Pictures"
262
+
263
+ return pictures_dir
264
+
265
+
266
+ @lru_cache(maxsize=1)
267
+ def _android_videos_folder() -> str:
268
+ """:returns: videos folder for the Android OS"""
269
+ # Get directories with pyjnius
270
+ try:
271
+ from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import]
272
+
273
+ context = autoclass("android.content.Context")
274
+ environment = autoclass("android.os.Environment")
275
+ videos_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DCIM).getAbsolutePath()
276
+ except Exception: # noqa: BLE001
277
+ videos_dir = "/storage/emulated/0/DCIM/Camera"
278
+
279
+ return videos_dir
280
+
281
+
282
+ @lru_cache(maxsize=1)
283
+ def _android_music_folder() -> str:
284
+ """:returns: music folder for the Android OS"""
285
+ # Get directories with pyjnius
286
+ try:
287
+ from jnius import autoclass # noqa: PLC0415 # ty: ignore[unresolved-import]
288
+
289
+ context = autoclass("android.content.Context")
290
+ environment = autoclass("android.os.Environment")
291
+ music_dir: str = context.getExternalFilesDir(environment.DIRECTORY_MUSIC).getAbsolutePath()
292
+ except Exception: # noqa: BLE001
293
+ music_dir = "/storage/emulated/0/Music"
294
+
295
+ return music_dir
296
+
297
+
298
+ __all__ = [
299
+ "Android",
300
+ ]
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/api.py ADDED
@@ -0,0 +1,463 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Base API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from abc import ABC, abstractmethod
7
+ from pathlib import Path
8
+ from typing import TYPE_CHECKING
9
+
10
+ if TYPE_CHECKING:
11
+ from collections.abc import Iterator
12
+ from typing import Literal
13
+
14
+
15
+ class PlatformDirsABC(ABC): # noqa: PLR0904
16
+ """Abstract base class defining all platform directory properties, their :class:`~pathlib.Path` variants, and iterators.
17
+
18
+ Platform-specific subclasses (e.g. :class:`~platformdirs.windows.Windows`, :class:`~platformdirs.macos.MacOS`,
19
+ :class:`~platformdirs.unix.Unix`) implement the abstract properties to return the appropriate paths for each
20
+ operating system.
21
+
22
+ """
23
+
24
+ def __init__( # noqa: PLR0913, PLR0917
25
+ self,
26
+ appname: str | None = None,
27
+ appauthor: str | Literal[False] | None = None,
28
+ version: str | None = None,
29
+ roaming: bool = False, # noqa: FBT001, FBT002
30
+ multipath: bool = False, # noqa: FBT001, FBT002
31
+ opinion: bool = True, # noqa: FBT001, FBT002
32
+ ensure_exists: bool = False, # noqa: FBT001, FBT002
33
+ use_site_for_root: bool = False, # noqa: FBT001, FBT002
34
+ ) -> None:
35
+ """Create a new platform directory.
36
+
37
+ :param appname: See `appname`.
38
+ :param appauthor: See `appauthor`.
39
+ :param version: See `version`.
40
+ :param roaming: See `roaming`.
41
+ :param multipath: See `multipath`.
42
+ :param opinion: See `opinion`.
43
+ :param ensure_exists: See `ensure_exists`.
44
+ :param use_site_for_root: See `use_site_for_root`.
45
+
46
+ """
47
+ self.appname = appname #: The name of the application.
48
+ self.appauthor = appauthor
49
+ """The name of the app author or distributing body for this application.
50
+
51
+ Typically, it is the owning company name. Defaults to `appname`. You may pass ``False`` to disable it.
52
+
53
+ .. note::
54
+
55
+ On Windows, the directory structure is ``<base>/<appauthor>/<appname>``. When ``appauthor`` is ``None`` (the
56
+ default), it falls back to ``appname``, resulting in ``<base>/<appname>/<appname>`` (e.g.
57
+ ``AppData/Local/myapp/myapp``). Pass ``appauthor=False`` to omit the author directory entirely and get
58
+ ``<base>/<appname>``.
59
+
60
+ """
61
+ self.version = version
62
+ """An optional version path element to append to the path.
63
+
64
+ You might want to use this if you want multiple versions of your app to be able to run independently. If used,
65
+ this would typically be ``<major>.<minor>``.
66
+
67
+ """
68
+ self.roaming = roaming
69
+ """Whether to use the roaming appdata directory on Windows.
70
+
71
+ That means that for users on a Windows network setup for roaming profiles, this user data will be synced on
72
+ login (see `here <https://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>`_).
73
+
74
+ """
75
+ self.multipath = multipath
76
+ """An optional parameter which indicates that the entire list of data dirs should be returned.
77
+
78
+ By default, the first item would only be returned. Only affects ``site_data_dir`` and ``site_config_dir`` on
79
+ Unix and macOS.
80
+
81
+ """
82
+ self.opinion = opinion
83
+ """Whether to use opinionated values.
84
+
85
+ When enabled, appends an additional subdirectory for certain directories: e.g. ``Cache`` for cache and ``Logs``
86
+ for logs on Windows, ``log`` for logs on Unix.
87
+
88
+ """
89
+ self.ensure_exists = ensure_exists
90
+ """Optionally create the directory (and any missing parents) upon access if it does not exist.
91
+
92
+ By default, no directories are created.
93
+
94
+ """
95
+ self.use_site_for_root = use_site_for_root
96
+ """Whether to redirect ``user_*_dir`` calls to their ``site_*_dir`` equivalents when running as root (uid 0).
97
+
98
+ Only has an effect on Unix. Disabled by default for backwards compatibility. When enabled, XDG user environment
99
+ variables (e.g. ``XDG_DATA_HOME``) are bypassed for the redirected directories.
100
+
101
+ """
102
+
103
+ def _append_app_name_and_version(self, *base: str) -> str:
104
+ params = list(base[1:])
105
+ if self.appname:
106
+ params.append(self.appname)
107
+ if self.version:
108
+ params.append(self.version)
109
+ path = os.path.join(base[0], *params) # noqa: PTH118
110
+ self._optionally_create_directory(path)
111
+ return path
112
+
113
+ def _optionally_create_directory(self, path: str) -> None:
114
+ if self.ensure_exists:
115
+ Path(path).mkdir(parents=True, exist_ok=True)
116
+
117
+ def _first_item_as_path_if_multipath(self, directory: str) -> Path:
118
+ if self.multipath:
119
+ # If multipath is True, the first path is returned.
120
+ directory = directory.partition(os.pathsep)[0]
121
+ return Path(directory)
122
+
123
+ @property
124
+ @abstractmethod
125
+ def user_data_dir(self) -> str:
126
+ """:returns: data directory tied to the user"""
127
+
128
+ @property
129
+ @abstractmethod
130
+ def site_data_dir(self) -> str:
131
+ """:returns: data directory shared by users"""
132
+
133
+ @property
134
+ def _site_data_dirs(self) -> list[str]:
135
+ raise NotImplementedError
136
+
137
+ @property
138
+ @abstractmethod
139
+ def user_config_dir(self) -> str:
140
+ """:returns: config directory tied to the user"""
141
+
142
+ @property
143
+ @abstractmethod
144
+ def site_config_dir(self) -> str:
145
+ """:returns: config directory shared by users"""
146
+
147
+ @property
148
+ def _site_config_dirs(self) -> list[str]:
149
+ raise NotImplementedError
150
+
151
+ @property
152
+ @abstractmethod
153
+ def user_cache_dir(self) -> str:
154
+ """:returns: cache directory tied to the user"""
155
+
156
+ @property
157
+ @abstractmethod
158
+ def site_cache_dir(self) -> str:
159
+ """:returns: cache directory shared by users"""
160
+
161
+ @property
162
+ @abstractmethod
163
+ def user_state_dir(self) -> str:
164
+ """:returns: state directory tied to the user"""
165
+
166
+ @property
167
+ @abstractmethod
168
+ def site_state_dir(self) -> str:
169
+ """:returns: state directory shared by users"""
170
+
171
+ @property
172
+ @abstractmethod
173
+ def user_log_dir(self) -> str:
174
+ """:returns: log directory tied to the user"""
175
+
176
+ @property
177
+ @abstractmethod
178
+ def site_log_dir(self) -> str:
179
+ """:returns: log directory shared by users"""
180
+
181
+ @property
182
+ @abstractmethod
183
+ def user_documents_dir(self) -> str:
184
+ """:returns: documents directory tied to the user"""
185
+
186
+ @property
187
+ @abstractmethod
188
+ def user_downloads_dir(self) -> str:
189
+ """:returns: downloads directory tied to the user"""
190
+
191
+ @property
192
+ @abstractmethod
193
+ def user_pictures_dir(self) -> str:
194
+ """:returns: pictures directory tied to the user"""
195
+
196
+ @property
197
+ @abstractmethod
198
+ def user_videos_dir(self) -> str:
199
+ """:returns: videos directory tied to the user"""
200
+
201
+ @property
202
+ @abstractmethod
203
+ def user_music_dir(self) -> str:
204
+ """:returns: music directory tied to the user"""
205
+
206
+ @property
207
+ @abstractmethod
208
+ def user_desktop_dir(self) -> str:
209
+ """:returns: desktop directory tied to the user"""
210
+
211
+ @property
212
+ @abstractmethod
213
+ def user_projects_dir(self) -> str:
214
+ """:returns: projects directory tied to the user"""
215
+
216
+ @property
217
+ @abstractmethod
218
+ def user_publicshare_dir(self) -> str:
219
+ """:returns: public share directory tied to the user"""
220
+
221
+ @property
222
+ @abstractmethod
223
+ def user_templates_dir(self) -> str:
224
+ """:returns: templates directory tied to the user"""
225
+
226
+ @property
227
+ @abstractmethod
228
+ def user_fonts_dir(self) -> str:
229
+ """:returns: fonts directory tied to the user"""
230
+
231
+ @property
232
+ @abstractmethod
233
+ def user_preference_dir(self) -> str:
234
+ """:returns: preference directory tied to the user"""
235
+
236
+ @property
237
+ @abstractmethod
238
+ def user_bin_dir(self) -> str:
239
+ """:returns: bin directory tied to the user"""
240
+
241
+ @property
242
+ @abstractmethod
243
+ def site_bin_dir(self) -> str:
244
+ """:returns: bin directory shared by users"""
245
+
246
+ @property
247
+ @abstractmethod
248
+ def user_applications_dir(self) -> str:
249
+ """:returns: applications directory tied to the user"""
250
+
251
+ @property
252
+ @abstractmethod
253
+ def site_applications_dir(self) -> str:
254
+ """:returns: applications directory shared by users"""
255
+
256
+ @property
257
+ def _site_applications_dirs(self) -> list[str]:
258
+ raise NotImplementedError
259
+
260
+ @property
261
+ @abstractmethod
262
+ def user_runtime_dir(self) -> str:
263
+ """:returns: runtime directory tied to the user"""
264
+
265
+ @property
266
+ @abstractmethod
267
+ def site_runtime_dir(self) -> str:
268
+ """:returns: runtime directory shared by users"""
269
+
270
+ @property
271
+ def user_data_path(self) -> Path:
272
+ """:returns: data path tied to the user"""
273
+ return Path(self.user_data_dir)
274
+
275
+ @property
276
+ def site_data_path(self) -> Path:
277
+ """:returns: data path shared by users"""
278
+ return Path(self.site_data_dir)
279
+
280
+ @property
281
+ def user_config_path(self) -> Path:
282
+ """:returns: config path tied to the user"""
283
+ return Path(self.user_config_dir)
284
+
285
+ @property
286
+ def site_config_path(self) -> Path:
287
+ """:returns: config path shared by users"""
288
+ return Path(self.site_config_dir)
289
+
290
+ @property
291
+ def user_cache_path(self) -> Path:
292
+ """:returns: cache path tied to the user"""
293
+ return Path(self.user_cache_dir)
294
+
295
+ @property
296
+ def site_cache_path(self) -> Path:
297
+ """:returns: cache path shared by users"""
298
+ return Path(self.site_cache_dir)
299
+
300
+ @property
301
+ def user_state_path(self) -> Path:
302
+ """:returns: state path tied to the user"""
303
+ return Path(self.user_state_dir)
304
+
305
+ @property
306
+ def site_state_path(self) -> Path:
307
+ """:returns: state path shared by users"""
308
+ return Path(self.site_state_dir)
309
+
310
+ @property
311
+ def user_log_path(self) -> Path:
312
+ """:returns: log path tied to the user"""
313
+ return Path(self.user_log_dir)
314
+
315
+ @property
316
+ def site_log_path(self) -> Path:
317
+ """:returns: log path shared by users"""
318
+ return Path(self.site_log_dir)
319
+
320
+ @property
321
+ def user_documents_path(self) -> Path:
322
+ """:returns: documents path tied to the user"""
323
+ return Path(self.user_documents_dir)
324
+
325
+ @property
326
+ def user_downloads_path(self) -> Path:
327
+ """:returns: downloads path tied to the user"""
328
+ return Path(self.user_downloads_dir)
329
+
330
+ @property
331
+ def user_pictures_path(self) -> Path:
332
+ """:returns: pictures path tied to the user"""
333
+ return Path(self.user_pictures_dir)
334
+
335
+ @property
336
+ def user_videos_path(self) -> Path:
337
+ """:returns: videos path tied to the user"""
338
+ return Path(self.user_videos_dir)
339
+
340
+ @property
341
+ def user_music_path(self) -> Path:
342
+ """:returns: music path tied to the user"""
343
+ return Path(self.user_music_dir)
344
+
345
+ @property
346
+ def user_desktop_path(self) -> Path:
347
+ """:returns: desktop path tied to the user"""
348
+ return Path(self.user_desktop_dir)
349
+
350
+ @property
351
+ def user_projects_path(self) -> Path:
352
+ """:returns: projects path tied to the user"""
353
+ return Path(self.user_projects_dir)
354
+
355
+ @property
356
+ def user_publicshare_path(self) -> Path:
357
+ """:returns: public share path tied to the user"""
358
+ return Path(self.user_publicshare_dir)
359
+
360
+ @property
361
+ def user_templates_path(self) -> Path:
362
+ """:returns: templates path tied to the user"""
363
+ return Path(self.user_templates_dir)
364
+
365
+ @property
366
+ def user_fonts_path(self) -> Path:
367
+ """:returns: fonts path tied to the user"""
368
+ return Path(self.user_fonts_dir)
369
+
370
+ @property
371
+ def user_preference_path(self) -> Path:
372
+ """:returns: preference path tied to the user"""
373
+ return Path(self.user_preference_dir)
374
+
375
+ @property
376
+ def user_bin_path(self) -> Path:
377
+ """:returns: bin path tied to the user"""
378
+ return Path(self.user_bin_dir)
379
+
380
+ @property
381
+ def site_bin_path(self) -> Path:
382
+ """:returns: bin path shared by users"""
383
+ return Path(self.site_bin_dir)
384
+
385
+ @property
386
+ def user_applications_path(self) -> Path:
387
+ """:returns: applications path tied to the user"""
388
+ return Path(self.user_applications_dir)
389
+
390
+ @property
391
+ def site_applications_path(self) -> Path:
392
+ """:returns: applications path shared by users"""
393
+ return Path(self.site_applications_dir)
394
+
395
+ @property
396
+ def user_runtime_path(self) -> Path:
397
+ """:returns: runtime path tied to the user"""
398
+ return Path(self.user_runtime_dir)
399
+
400
+ @property
401
+ def site_runtime_path(self) -> Path:
402
+ """:returns: runtime path shared by users"""
403
+ return Path(self.site_runtime_dir)
404
+
405
+ def iter_config_dirs(self) -> Iterator[str]:
406
+ """:yield: all user and site configuration directories."""
407
+ yield self.user_config_dir
408
+ yield self.site_config_dir
409
+
410
+ def iter_data_dirs(self) -> Iterator[str]:
411
+ """:yield: all user and site data directories."""
412
+ yield self.user_data_dir
413
+ yield self.site_data_dir
414
+
415
+ def iter_cache_dirs(self) -> Iterator[str]:
416
+ """:yield: all user and site cache directories."""
417
+ yield self.user_cache_dir
418
+ yield self.site_cache_dir
419
+
420
+ def iter_state_dirs(self) -> Iterator[str]:
421
+ """:yield: all user and site state directories."""
422
+ yield self.user_state_dir
423
+ yield self.site_state_dir
424
+
425
+ def iter_log_dirs(self) -> Iterator[str]:
426
+ """:yield: all user and site log directories."""
427
+ yield self.user_log_dir
428
+ yield self.site_log_dir
429
+
430
+ def iter_runtime_dirs(self) -> Iterator[str]:
431
+ """:yield: all user and site runtime directories."""
432
+ yield self.user_runtime_dir
433
+ yield self.site_runtime_dir
434
+
435
+ def iter_config_paths(self) -> Iterator[Path]:
436
+ """:yield: all user and site configuration paths."""
437
+ for path in self.iter_config_dirs():
438
+ yield Path(path)
439
+
440
+ def iter_data_paths(self) -> Iterator[Path]:
441
+ """:yield: all user and site data paths."""
442
+ for path in self.iter_data_dirs():
443
+ yield Path(path)
444
+
445
+ def iter_cache_paths(self) -> Iterator[Path]:
446
+ """:yield: all user and site cache paths."""
447
+ for path in self.iter_cache_dirs():
448
+ yield Path(path)
449
+
450
+ def iter_state_paths(self) -> Iterator[Path]:
451
+ """:yield: all user and site state paths."""
452
+ for path in self.iter_state_dirs():
453
+ yield Path(path)
454
+
455
+ def iter_log_paths(self) -> Iterator[Path]:
456
+ """:yield: all user and site log paths."""
457
+ for path in self.iter_log_dirs():
458
+ yield Path(path)
459
+
460
+ def iter_runtime_paths(self) -> Iterator[Path]:
461
+ """:yield: all user and site runtime paths."""
462
+ for path in self.iter_runtime_dirs():
463
+ yield Path(path)
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/macos.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """macOS."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os.path
6
+ import sys
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from collections.abc import Iterator
11
+
12
+ from ._xdg import XDGMixin
13
+ from .api import PlatformDirsABC
14
+
15
+ if TYPE_CHECKING:
16
+ from pathlib import Path
17
+
18
+
19
+ class _MacOSDefaults(PlatformDirsABC): # noqa: PLR0904
20
+ """Default platform directories for macOS without XDG environment variable overrides.
21
+
22
+ Follows the guidance from `Apple's File System Programming Guide
23
+ <https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html>`_.
24
+ The XDG env var handling is in :class:`~platformdirs._xdg.XDGMixin`.
25
+
26
+ """
27
+
28
+ def _base_user_app_support_dir(self) -> str:
29
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support")) # noqa: PTH111
30
+
31
+ def _base_site_dirs(self) -> list[str]:
32
+ is_homebrew = "/opt/python" in sys.prefix
33
+ homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else ""
34
+ path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/share")] if is_homebrew else []
35
+ path_list.append(self._append_app_name_and_version("/Library/Application Support"))
36
+ return path_list
37
+
38
+ @property
39
+ def user_data_dir(self) -> str:
40
+ """:returns: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``"""
41
+ return self._base_user_app_support_dir()
42
+
43
+ @property
44
+ def _site_data_dirs(self) -> list[str]:
45
+ return self._base_site_dirs()
46
+
47
+ @property
48
+ def site_data_path(self) -> Path:
49
+ """:returns: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
50
+ return self._first_item_as_path_if_multipath(self.site_data_dir)
51
+
52
+ @property
53
+ def site_config_path(self) -> Path:
54
+ """:returns: config path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
55
+ return self._first_item_as_path_if_multipath(self.site_config_dir)
56
+
57
+ @property
58
+ def user_config_dir(self) -> str:
59
+ """:returns: config directory tied to the user, same as `user_data_dir`"""
60
+ return self._base_user_app_support_dir()
61
+
62
+ @property
63
+ def _site_config_dirs(self) -> list[str]:
64
+ return self._base_site_dirs()
65
+
66
+ @property
67
+ def user_cache_dir(self) -> str:
68
+ """:returns: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``"""
69
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches")) # noqa: PTH111
70
+
71
+ @property
72
+ def site_cache_dir(self) -> str:
73
+ """:returns: cache directory shared by users, e.g. ``/Library/Caches/$appname/$version``. If we're using a Python binary managed by `Homebrew <https://brew.sh>`_, the directory will be under the Homebrew prefix, e.g. ``$homebrew_prefix/var/cache/$appname/$version``. If `multipath <platformdirs.api.PlatformDirsABC.multipath>` is enabled, and we're in Homebrew, the response is a multi-path string separated by ":", e.g. ``$homebrew_prefix/var/cache/$appname/$version:/Library/Caches/$appname/$version``"""
74
+ is_homebrew = "/opt/python" in sys.prefix
75
+ homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else ""
76
+ path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/var/cache")] if is_homebrew else []
77
+ path_list.append(self._append_app_name_and_version("/Library/Caches"))
78
+ if self.multipath:
79
+ return os.pathsep.join(path_list)
80
+ return path_list[0]
81
+
82
+ @property
83
+ def site_cache_path(self) -> Path:
84
+ """:returns: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
85
+ return self._first_item_as_path_if_multipath(self.site_cache_dir)
86
+
87
+ @property
88
+ def user_state_dir(self) -> str:
89
+ """:returns: state directory tied to the user, same as `user_data_dir`"""
90
+ return self._base_user_app_support_dir()
91
+
92
+ @property
93
+ def site_state_dir(self) -> str:
94
+ """:returns: state directory shared by users, same as `site_data_dir`"""
95
+ return self._base_site_dirs()[0]
96
+
97
+ @property
98
+ def user_log_dir(self) -> str:
99
+ """:returns: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``"""
100
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs")) # noqa: PTH111
101
+
102
+ @property
103
+ def site_log_dir(self) -> str:
104
+ """:returns: log directory shared by users, e.g. ``/Library/Logs/$appname/$version``"""
105
+ return self._append_app_name_and_version("/Library/Logs")
106
+
107
+ @property
108
+ def user_documents_dir(self) -> str:
109
+ """:returns: documents directory tied to the user, e.g. ``~/Documents``"""
110
+ return os.path.expanduser("~/Documents") # noqa: PTH111
111
+
112
+ @property
113
+ def user_downloads_dir(self) -> str:
114
+ """:returns: downloads directory tied to the user, e.g. ``~/Downloads``"""
115
+ return os.path.expanduser("~/Downloads") # noqa: PTH111
116
+
117
+ @property
118
+ def user_pictures_dir(self) -> str:
119
+ """:returns: pictures directory tied to the user, e.g. ``~/Pictures``"""
120
+ return os.path.expanduser("~/Pictures") # noqa: PTH111
121
+
122
+ @property
123
+ def user_videos_dir(self) -> str:
124
+ """:returns: videos directory tied to the user, e.g. ``~/Movies``"""
125
+ return os.path.expanduser("~/Movies") # noqa: PTH111
126
+
127
+ @property
128
+ def user_music_dir(self) -> str:
129
+ """:returns: music directory tied to the user, e.g. ``~/Music``"""
130
+ return os.path.expanduser("~/Music") # noqa: PTH111
131
+
132
+ @property
133
+ def user_desktop_dir(self) -> str:
134
+ """:returns: desktop directory tied to the user, e.g. ``~/Desktop``"""
135
+ return os.path.expanduser("~/Desktop") # noqa: PTH111
136
+
137
+ @property
138
+ def user_projects_dir(self) -> str:
139
+ """:returns: projects directory tied to the user, e.g. ``~/Projects``"""
140
+ return os.path.expanduser("~/Projects") # noqa: PTH111
141
+
142
+ @property
143
+ def user_publicshare_dir(self) -> str:
144
+ """:returns: public share directory tied to the user, e.g. ``~/Public``"""
145
+ return os.path.expanduser("~/Public") # noqa: PTH111 # API returns str, not Path
146
+
147
+ @property
148
+ def user_templates_dir(self) -> str:
149
+ """:returns: templates directory tied to the user, e.g. ``~/Templates``"""
150
+ return os.path.expanduser("~/Templates") # noqa: PTH111 # API returns str, not Path
151
+
152
+ @property
153
+ def user_fonts_dir(self) -> str:
154
+ """:returns: fonts directory tied to the user, e.g. ``~/Library/Fonts``"""
155
+ return os.path.expanduser("~/Library/Fonts") # noqa: PTH111 # API returns str, not Path
156
+
157
+ @property
158
+ def user_preference_dir(self) -> str:
159
+ """:returns: preference directory tied to the user, e.g. ``~/Library/Preferences/AppName``"""
160
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Preferences")) # noqa: PTH111 # API returns str, not Path
161
+
162
+ @property
163
+ def user_bin_dir(self) -> str:
164
+ """:returns: bin directory tied to the user, e.g. ``~/.local/bin``"""
165
+ return os.path.expanduser("~/.local/bin") # noqa: PTH111
166
+
167
+ @property
168
+ def site_bin_dir(self) -> str:
169
+ """:returns: bin directory shared by users, e.g. ``/usr/local/bin``"""
170
+ return "/usr/local/bin"
171
+
172
+ @property
173
+ def user_applications_dir(self) -> str:
174
+ """:returns: applications directory tied to the user, e.g. ``~/Applications``"""
175
+ return os.path.expanduser("~/Applications") # noqa: PTH111
176
+
177
+ @property
178
+ def _site_applications_dirs(self) -> list[str]:
179
+ return ["/Applications"]
180
+
181
+ @property
182
+ def site_applications_dir(self) -> str:
183
+ """:returns: applications directory shared by users, e.g. ``/Applications``"""
184
+ dirs = self._site_applications_dirs
185
+ return os.pathsep.join(dirs) if self.multipath else dirs[0]
186
+
187
+ @property
188
+ def user_runtime_dir(self) -> str:
189
+ """:returns: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``"""
190
+ return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems")) # noqa: PTH111
191
+
192
+ @property
193
+ def site_runtime_dir(self) -> str:
194
+ """:returns: runtime directory shared by users, same as `user_runtime_dir`"""
195
+ return self.user_runtime_dir
196
+
197
+ def iter_config_dirs(self) -> Iterator[str]:
198
+ """:yield: all user and site configuration directories."""
199
+ yield self.user_config_dir
200
+ yield from self._site_config_dirs
201
+
202
+ def iter_data_dirs(self) -> Iterator[str]:
203
+ """:yield: all user and site data directories."""
204
+ yield self.user_data_dir
205
+ yield from self._site_data_dirs
206
+
207
+
208
+ class MacOS(XDGMixin, _MacOSDefaults):
209
+ """Platform directories for the macOS operating system.
210
+
211
+ Follows the guidance from `Apple documentation
212
+ <https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html>`_.
213
+ Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>`, `version
214
+ <platformdirs.api.PlatformDirsABC.version>`, `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
215
+
216
+ XDG environment variables (e.g. ``$XDG_DATA_HOME``) are supported and take precedence over macOS defaults.
217
+
218
+ """
219
+
220
+
221
+ __all__ = [
222
+ "MacOS",
223
+ ]
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/py.typed ADDED
File without changes
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/unix.py ADDED
@@ -0,0 +1,321 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Unix."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sys
7
+ from configparser import ConfigParser
8
+ from functools import cached_property
9
+ from pathlib import Path
10
+ from tempfile import gettempdir
11
+ from typing import TYPE_CHECKING, NoReturn
12
+
13
+ from ._xdg import XDGMixin
14
+ from .api import PlatformDirsABC
15
+
16
+ if TYPE_CHECKING:
17
+ from collections.abc import Iterator
18
+
19
+ if sys.platform == "win32":
20
+
21
+ def getuid() -> NoReturn:
22
+ msg = "should only be used on Unix"
23
+ raise RuntimeError(msg)
24
+
25
+ else:
26
+ from os import getuid
27
+
28
+
29
+ class _UnixDefaults(PlatformDirsABC): # noqa: PLR0904
30
+ """Default directories for Unix/Linux without XDG environment variable overrides.
31
+
32
+ The XDG env var handling is in :class:`~platformdirs._xdg.XDGMixin`.
33
+
34
+ """
35
+
36
+ @cached_property
37
+ def _use_site(self) -> bool:
38
+ return self.use_site_for_root and getuid() == 0
39
+
40
+ @property
41
+ def user_data_dir(self) -> str:
42
+ """:returns: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or ``$XDG_DATA_HOME/$appname/$version``"""
43
+ return self._append_app_name_and_version(os.path.expanduser("~/.local/share")) # noqa: PTH111
44
+
45
+ @property
46
+ def _site_data_dirs(self) -> list[str]:
47
+ return [self._append_app_name_and_version("/usr/local/share"), self._append_app_name_and_version("/usr/share")]
48
+
49
+ @property
50
+ def user_config_dir(self) -> str:
51
+ """:returns: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or ``$XDG_CONFIG_HOME/$appname/$version``"""
52
+ return self._append_app_name_and_version(os.path.expanduser("~/.config")) # noqa: PTH111
53
+
54
+ @property
55
+ def _site_config_dirs(self) -> list[str]:
56
+ return [self._append_app_name_and_version("/etc/xdg")]
57
+
58
+ @property
59
+ def user_cache_dir(self) -> str:
60
+ """:returns: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or ``$XDG_CACHE_HOME/$appname/$version``"""
61
+ return self._append_app_name_and_version(os.path.expanduser("~/.cache")) # noqa: PTH111
62
+
63
+ @property
64
+ def site_cache_dir(self) -> str:
65
+ """:returns: cache directory shared by users, e.g. ``/var/cache/$appname/$version``"""
66
+ return self._append_app_name_and_version("/var/cache")
67
+
68
+ @property
69
+ def user_state_dir(self) -> str:
70
+ """:returns: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or ``$XDG_STATE_HOME/$appname/$version``"""
71
+ return self._append_app_name_and_version(os.path.expanduser("~/.local/state")) # noqa: PTH111
72
+
73
+ @property
74
+ def site_state_dir(self) -> str:
75
+ """:returns: state directory shared by users, e.g. ``/var/lib/$appname/$version``"""
76
+ return self._append_app_name_and_version("/var/lib")
77
+
78
+ @property
79
+ def user_log_dir(self) -> str:
80
+ """:returns: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it"""
81
+ path = self.user_state_dir
82
+ if self.opinion:
83
+ path = os.path.join(path, "log") # noqa: PTH118
84
+ self._optionally_create_directory(path)
85
+ return path
86
+
87
+ @property
88
+ def site_log_dir(self) -> str:
89
+ """:returns: log directory shared by users, e.g. ``/var/log/$appname/$version``
90
+
91
+ Unlike `user_log_dir`, ``opinion`` has no effect since ``/var/log`` is inherently a log directory.
92
+
93
+ """
94
+ return self._append_app_name_and_version("/var/log")
95
+
96
+ @property
97
+ def user_documents_dir(self) -> str:
98
+ """:returns: documents directory tied to the user, e.g. ``~/Documents``"""
99
+ return _get_user_media_dir("XDG_DOCUMENTS_DIR", "~/Documents")
100
+
101
+ @property
102
+ def user_downloads_dir(self) -> str:
103
+ """:returns: downloads directory tied to the user, e.g. ``~/Downloads``"""
104
+ return _get_user_media_dir("XDG_DOWNLOAD_DIR", "~/Downloads")
105
+
106
+ @property
107
+ def user_pictures_dir(self) -> str:
108
+ """:returns: pictures directory tied to the user, e.g. ``~/Pictures``"""
109
+ return _get_user_media_dir("XDG_PICTURES_DIR", "~/Pictures")
110
+
111
+ @property
112
+ def user_videos_dir(self) -> str:
113
+ """:returns: videos directory tied to the user, e.g. ``~/Videos``"""
114
+ return _get_user_media_dir("XDG_VIDEOS_DIR", "~/Videos")
115
+
116
+ @property
117
+ def user_music_dir(self) -> str:
118
+ """:returns: music directory tied to the user, e.g. ``~/Music``"""
119
+ return _get_user_media_dir("XDG_MUSIC_DIR", "~/Music")
120
+
121
+ @property
122
+ def user_desktop_dir(self) -> str:
123
+ """:returns: desktop directory tied to the user, e.g. ``~/Desktop``"""
124
+ return _get_user_media_dir("XDG_DESKTOP_DIR", "~/Desktop")
125
+
126
+ @property
127
+ def user_projects_dir(self) -> str:
128
+ """:returns: projects directory tied to the user, e.g. ``~/Projects``"""
129
+ return _get_user_media_dir("XDG_PROJECTS_DIR", "~/Projects")
130
+
131
+ @property
132
+ def user_publicshare_dir(self) -> str:
133
+ """:returns: public share directory tied to the user, e.g. ``~/Public``"""
134
+ return _get_user_media_dir("XDG_PUBLICSHARE_DIR", "~/Public")
135
+
136
+ @property
137
+ def user_templates_dir(self) -> str:
138
+ """:returns: templates directory tied to the user, e.g. ``~/Templates``"""
139
+ return _get_user_media_dir("XDG_TEMPLATES_DIR", "~/Templates")
140
+
141
+ @property
142
+ def user_fonts_dir(self) -> str:
143
+ """:returns: fonts directory tied to the user, e.g. ``~/.local/share/fonts``"""
144
+ return f"{os.path.expanduser('~/.local/share')}/fonts" # noqa: PTH111 # API returns str, not Path
145
+
146
+ @property
147
+ def user_preference_dir(self) -> str:
148
+ """:returns: preference directory tied to the user, same as ``user_config_dir``"""
149
+ return self.user_config_dir
150
+
151
+ @property
152
+ def user_bin_dir(self) -> str:
153
+ """:returns: bin directory tied to the user, e.g. ``~/.local/bin``"""
154
+ return os.path.expanduser("~/.local/bin") # noqa: PTH111
155
+
156
+ @property
157
+ def site_bin_dir(self) -> str:
158
+ """:returns: bin directory shared by users, e.g. ``/usr/local/bin``"""
159
+ return "/usr/local/bin"
160
+
161
+ @property
162
+ def user_applications_dir(self) -> str:
163
+ """:returns: applications directory tied to the user, e.g. ``~/.local/share/applications``"""
164
+ return os.path.join(os.path.expanduser("~/.local/share"), "applications") # noqa: PTH111, PTH118
165
+
166
+ @property
167
+ def _site_applications_dirs(self) -> list[str]:
168
+ return [os.path.join(p, "applications") for p in ["/usr/local/share", "/usr/share"]] # noqa: PTH118
169
+
170
+ @property
171
+ def site_applications_dir(self) -> str:
172
+ """:returns: applications directory shared by users, e.g. ``/usr/share/applications``"""
173
+ dirs = self._site_applications_dirs
174
+ return os.pathsep.join(dirs) if self.multipath else dirs[0]
175
+
176
+ @property
177
+ def user_runtime_dir(self) -> str:
178
+ """:returns: runtime directory tied to the user, e.g. ``$XDG_RUNTIME_DIR/$appname/$version``.
179
+
180
+ If ``$XDG_RUNTIME_DIR`` is unset, tries the platform default (``/tmp/run/user/$(id -u)`` on OpenBSD, ``/var/run/user/$(id -u)`` on FreeBSD/NetBSD, ``/run/user/$(id -u)`` otherwise). If the default is not writable, falls back to a temporary directory.
181
+
182
+ """
183
+ if sys.platform.startswith("openbsd"):
184
+ path = f"/tmp/run/user/{getuid()}" # noqa: S108
185
+ elif sys.platform.startswith(("freebsd", "netbsd")):
186
+ path = f"/var/run/user/{getuid()}"
187
+ else:
188
+ path = f"/run/user/{getuid()}"
189
+ if not os.access(path, os.W_OK):
190
+ path = f"{gettempdir()}/runtime-{getuid()}"
191
+ return self._append_app_name_and_version(path)
192
+
193
+ @property
194
+ def site_runtime_dir(self) -> str:
195
+ """:returns: runtime directory shared by users, e.g. ``/run/$appname/$version`` or ``$XDG_RUNTIME_DIR/$appname/$version``.
196
+
197
+ Note that this behaves almost exactly like `user_runtime_dir` if ``$XDG_RUNTIME_DIR`` is set, but will fall back to paths associated to the root user instead of a regular logged-in user if it's not set.
198
+
199
+ If you wish to ensure that a logged-in root user path is returned e.g. ``/run/user/0``, use `user_runtime_dir` instead.
200
+
201
+ For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/$appname/$version`` if ``$XDG_RUNTIME_DIR`` is not set.
202
+
203
+ """
204
+ if sys.platform.startswith(("freebsd", "openbsd", "netbsd")):
205
+ path = "/var/run"
206
+ else:
207
+ path = "/run"
208
+ return self._append_app_name_and_version(path)
209
+
210
+ @property
211
+ def site_data_path(self) -> Path:
212
+ """:returns: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
213
+ return self._first_item_as_path_if_multipath(self.site_data_dir)
214
+
215
+ @property
216
+ def site_config_path(self) -> Path:
217
+ """:returns: config path shared by users, returns the first item, even if ``multipath`` is set to ``True``"""
218
+ return self._first_item_as_path_if_multipath(self.site_config_dir)
219
+
220
+ @property
221
+ def site_cache_path(self) -> Path:
222
+ """:returns: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
223
+ return self._first_item_as_path_if_multipath(self.site_cache_dir)
224
+
225
+ def iter_config_dirs(self) -> Iterator[str]:
226
+ """:yield: all user and site configuration directories."""
227
+ if not self._use_site:
228
+ yield self.user_config_dir
229
+ yield from self._site_config_dirs
230
+
231
+ def iter_data_dirs(self) -> Iterator[str]:
232
+ """:yield: all user and site data directories."""
233
+ if not self._use_site:
234
+ yield self.user_data_dir
235
+ yield from self._site_data_dirs
236
+
237
+
238
+ class Unix(XDGMixin, _UnixDefaults):
239
+ """On Unix/Linux, we follow the `XDG Basedir Spec <https://specifications.freedesktop.org/basedir/latest/>`_.
240
+
241
+ The spec allows overriding directories with environment variables. The examples shown are the default values,
242
+ alongside the name of the environment variable that overrides them. Makes use of the `appname
243
+ <platformdirs.api.PlatformDirsABC.appname>`, `version <platformdirs.api.PlatformDirsABC.version>`, `multipath
244
+ <platformdirs.api.PlatformDirsABC.multipath>`, `opinion <platformdirs.api.PlatformDirsABC.opinion>`, `ensure_exists
245
+ <platformdirs.api.PlatformDirsABC.ensure_exists>`.
246
+
247
+ """
248
+
249
+ @property
250
+ def user_data_dir(self) -> str:
251
+ """:returns: data directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
252
+ return self.site_data_dir if self._use_site else super().user_data_dir
253
+
254
+ @property
255
+ def user_config_dir(self) -> str:
256
+ """:returns: config directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
257
+ return self.site_config_dir if self._use_site else super().user_config_dir
258
+
259
+ @property
260
+ def user_cache_dir(self) -> str:
261
+ """:returns: cache directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
262
+ return self.site_cache_dir if self._use_site else super().user_cache_dir
263
+
264
+ @property
265
+ def user_state_dir(self) -> str:
266
+ """:returns: state directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
267
+ return self.site_state_dir if self._use_site else super().user_state_dir
268
+
269
+ @property
270
+ def user_log_dir(self) -> str:
271
+ """:returns: log directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
272
+ return self.site_log_dir if self._use_site else super().user_log_dir
273
+
274
+ @property
275
+ def user_applications_dir(self) -> str:
276
+ """:returns: applications directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
277
+ return self.site_applications_dir if self._use_site else super().user_applications_dir
278
+
279
+ @property
280
+ def user_runtime_dir(self) -> str:
281
+ """:returns: runtime directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
282
+ return self.site_runtime_dir if self._use_site else super().user_runtime_dir
283
+
284
+ @property
285
+ def user_bin_dir(self) -> str:
286
+ """:returns: bin directory tied to the user, or site equivalent when root with ``use_site_for_root``"""
287
+ return self.site_bin_dir if self._use_site else super().user_bin_dir
288
+
289
+
290
+ def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str:
291
+ if media_dir := _get_user_dirs_folder(env_var):
292
+ return media_dir
293
+ return os.path.expanduser(fallback_tilde_path) # noqa: PTH111
294
+
295
+
296
+ def _get_user_dirs_folder(key: str) -> str | None:
297
+ """Return directory from user-dirs.dirs config file.
298
+
299
+ See https://freedesktop.org/wiki/Software/xdg-user-dirs/.
300
+
301
+ """
302
+ config_home = os.environ.get("XDG_CONFIG_HOME", "").strip() or os.path.expanduser("~/.config") # noqa: PTH111
303
+ user_dirs_config_path = Path(config_home) / "user-dirs.dirs"
304
+ if user_dirs_config_path.exists():
305
+ parser = ConfigParser()
306
+
307
+ with user_dirs_config_path.open() as stream:
308
+ parser.read_string(f"[top]\n{stream.read()}")
309
+
310
+ if key not in parser["top"]:
311
+ return None
312
+
313
+ path = parser["top"][key].strip('"')
314
+ return path.replace("$HOME", os.path.expanduser("~")) # noqa: PTH111
315
+
316
+ return None
317
+
318
+
319
+ __all__ = [
320
+ "Unix",
321
+ ]
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/version.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '4.10.0'
22
+ __version_tuple__ = version_tuple = (4, 10, 0)
23
+
24
+ __commit_id__ = commit_id = None
.venv/lib/python3.14/site-packages/pip/_vendor/platformdirs/windows.py ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Windows."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import TYPE_CHECKING, Final
9
+
10
+ from .api import PlatformDirsABC
11
+
12
+ if TYPE_CHECKING:
13
+ from collections.abc import Callable
14
+
15
+ # Not exposed by CPython; defined in the Windows SDK (shlobj_core.h)
16
+ _KF_FLAG_DONT_VERIFY: Final[int] = 0x00004000
17
+
18
+
19
+ class Windows(PlatformDirsABC): # noqa: PLR0904
20
+ """`MSDN on where to store app data files <https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid>`_.
21
+
22
+ Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>`, `appauthor
23
+ <platformdirs.api.PlatformDirsABC.appauthor>`, `version <platformdirs.api.PlatformDirsABC.version>`, `roaming
24
+ <platformdirs.api.PlatformDirsABC.roaming>`, `opinion <platformdirs.api.PlatformDirsABC.opinion>`, `ensure_exists
25
+ <platformdirs.api.PlatformDirsABC.ensure_exists>`.
26
+
27
+ """
28
+
29
+ @property
30
+ def user_data_dir(self) -> str:
31
+ r""":returns: data directory tied to the user, e.g. ``%USERPROFILE%\AppData\Local\$appauthor\$appname`` (not roaming) or ``%USERPROFILE%\AppData\Roaming\$appauthor\$appname`` (roaming)"""
32
+ const = "CSIDL_APPDATA" if self.roaming else "CSIDL_LOCAL_APPDATA"
33
+ path = os.path.normpath(get_win_folder(const))
34
+ return self._append_parts(path)
35
+
36
+ def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str:
37
+ params = []
38
+ if self.appname:
39
+ if self.appauthor is not False:
40
+ author = self.appauthor or self.appname
41
+ params.append(author)
42
+ params.append(self.appname)
43
+ if opinion_value is not None and self.opinion:
44
+ params.append(opinion_value)
45
+ if self.version:
46
+ params.append(self.version)
47
+ path = os.path.join(path, *params) # noqa: PTH118
48
+ self._optionally_create_directory(path)
49
+ return path
50
+
51
+ @property
52
+ def site_data_dir(self) -> str:
53
+ r""":returns: data directory shared by users, e.g. ``C:\ProgramData\$appauthor\$appname``"""
54
+ path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA"))
55
+ return self._append_parts(path)
56
+
57
+ @property
58
+ def user_config_dir(self) -> str:
59
+ """:returns: config directory tied to the user, same as `user_data_dir`"""
60
+ return self.user_data_dir
61
+
62
+ @property
63
+ def site_config_dir(self) -> str:
64
+ """:returns: config directory shared by users, same as `site_data_dir`"""
65
+ return self.site_data_dir
66
+
67
+ @property
68
+ def user_cache_dir(self) -> str:
69
+ r""":returns: cache directory tied to the user (if opinionated with ``Cache`` folder within ``$appname``) e.g. ``%USERPROFILE%\AppData\Local\$appauthor\$appname\Cache\$version``"""
70
+ path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA"))
71
+ return self._append_parts(path, opinion_value="Cache")
72
+
73
+ @property
74
+ def site_cache_dir(self) -> str:
75
+ r""":returns: cache directory shared by users, e.g. ``C:\ProgramData\$appauthor\$appname\Cache\$version``"""
76
+ path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA"))
77
+ return self._append_parts(path, opinion_value="Cache")
78
+
79
+ @property
80
+ def user_state_dir(self) -> str:
81
+ """:returns: state directory tied to the user, same as `user_data_dir`"""
82
+ return self.user_data_dir
83
+
84
+ @property
85
+ def site_state_dir(self) -> str:
86
+ """:returns: state directory shared by users, same as `site_data_dir`"""
87
+ return self.site_data_dir
88
+
89
+ @property
90
+ def user_log_dir(self) -> str:
91
+ """:returns: log directory tied to the user, same as `user_data_dir` if not opinionated else ``Logs`` in it"""
92
+ path = self.user_data_dir
93
+ if self.opinion:
94
+ path = os.path.join(path, "Logs") # noqa: PTH118
95
+ self._optionally_create_directory(path)
96
+ return path
97
+
98
+ @property
99
+ def site_log_dir(self) -> str:
100
+ """:returns: log directory shared by users, same as `site_data_dir` if not opinionated else ``Logs`` in it"""
101
+ path = self.site_data_dir
102
+ if self.opinion:
103
+ path = os.path.join(path, "Logs") # noqa: PTH118
104
+ self._optionally_create_directory(path)
105
+ return path
106
+
107
+ @property
108
+ def user_documents_dir(self) -> str:
109
+ r""":returns: documents directory tied to the user e.g. ``%USERPROFILE%\Documents``"""
110
+ return os.path.normpath(get_win_folder("CSIDL_PERSONAL"))
111
+
112
+ @property
113
+ def user_downloads_dir(self) -> str:
114
+ r""":returns: downloads directory tied to the user e.g. ``%USERPROFILE%\Downloads``"""
115
+ return os.path.normpath(get_win_folder("CSIDL_DOWNLOADS"))
116
+
117
+ @property
118
+ def user_pictures_dir(self) -> str:
119
+ r""":returns: pictures directory tied to the user e.g. ``%USERPROFILE%\Pictures``"""
120
+ return os.path.normpath(get_win_folder("CSIDL_MYPICTURES"))
121
+
122
+ @property
123
+ def user_videos_dir(self) -> str:
124
+ r""":returns: videos directory tied to the user e.g. ``%USERPROFILE%\Videos``"""
125
+ return os.path.normpath(get_win_folder("CSIDL_MYVIDEO"))
126
+
127
+ @property
128
+ def user_music_dir(self) -> str:
129
+ r""":returns: music directory tied to the user e.g. ``%USERPROFILE%\Music``"""
130
+ return os.path.normpath(get_win_folder("CSIDL_MYMUSIC"))
131
+
132
+ @property
133
+ def user_desktop_dir(self) -> str:
134
+ r""":returns: desktop directory tied to the user, e.g. ``%USERPROFILE%\Desktop``"""
135
+ return os.path.normpath(get_win_folder("CSIDL_DESKTOPDIRECTORY"))
136
+
137
+ @property
138
+ def user_projects_dir(self) -> str:
139
+ r""":returns: projects directory tied to the user, e.g. ``%USERPROFILE%\Projects``"""
140
+ return os.path.normpath(os.path.expanduser("~/Projects")) # noqa: PTH111
141
+
142
+ @property
143
+ def user_publicshare_dir(self) -> str:
144
+ r""":returns: public share directory e.g. ``C:\Users\Public``"""
145
+ return os.path.normpath(os.environ.get("PUBLIC", str(Path("~").expanduser().parent / "Public")))
146
+
147
+ @property
148
+ def user_templates_dir(self) -> str:
149
+ r""":returns: templates directory tied to the user e.g. ``%APPDATA%\Microsoft\Windows\Templates``"""
150
+ return os.path.normpath(str(Path(get_win_folder("CSIDL_APPDATA")) / "Microsoft" / "Windows" / "Templates"))
151
+
152
+ @property
153
+ def user_fonts_dir(self) -> str:
154
+ r""":returns: fonts directory tied to the user e.g. ``%LOCALAPPDATA%\Microsoft\Windows\Fonts``"""
155
+ return os.path.normpath(str(Path(get_win_folder("CSIDL_LOCAL_APPDATA")) / "Microsoft" / "Windows" / "Fonts"))
156
+
157
+ @property
158
+ def user_preference_dir(self) -> str:
159
+ r""":returns: preference directory tied to the user, same as ``user_config_dir``"""
160
+ return self.user_config_dir
161
+
162
+ @property
163
+ def user_bin_dir(self) -> str:
164
+ r""":returns: bin directory tied to the user, e.g. ``%LOCALAPPDATA%\Programs``"""
165
+ return os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Programs")) # noqa: PTH118
166
+
167
+ @property
168
+ def site_bin_dir(self) -> str:
169
+ """:returns: bin directory shared by users, e.g. ``C:\\ProgramData\bin``"""
170
+ return os.path.normpath(os.path.join(get_win_folder("CSIDL_COMMON_APPDATA"), "bin")) # noqa: PTH118
171
+
172
+ @property
173
+ def user_applications_dir(self) -> str:
174
+ r""":returns: applications directory tied to the user, e.g. ``Start Menu\Programs``"""
175
+ return os.path.normpath(get_win_folder("CSIDL_PROGRAMS"))
176
+
177
+ @property
178
+ def site_applications_dir(self) -> str:
179
+ r""":returns: applications directory shared by users, e.g. ``C:\ProgramData\Microsoft\Windows\Start Menu\Programs``"""
180
+ return os.path.normpath(get_win_folder("CSIDL_COMMON_PROGRAMS"))
181
+
182
+ @property
183
+ def user_runtime_dir(self) -> str:
184
+ r""":returns: runtime directory tied to the user, e.g. ``%USERPROFILE%\AppData\Local\Temp\$appauthor\$appname``"""
185
+ path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp")) # noqa: PTH118
186
+ return self._append_parts(path)
187
+
188
+ @property
189
+ def site_runtime_dir(self) -> str:
190
+ """:returns: runtime directory shared by users, same as `user_runtime_dir`"""
191
+ return self.user_runtime_dir
192
+
193
+
194
+ def get_win_folder_from_env_vars(csidl_name: str) -> str:
195
+ """Get folder from environment variables."""
196
+ result = get_win_folder_if_csidl_name_not_env_var(csidl_name)
197
+ if result is not None:
198
+ return result
199
+
200
+ env_var_name = {
201
+ "CSIDL_APPDATA": "APPDATA",
202
+ "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE",
203
+ "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA",
204
+ }.get(csidl_name)
205
+ if env_var_name is None:
206
+ msg = f"Unknown CSIDL name: {csidl_name}"
207
+ raise ValueError(msg)
208
+ result = os.environ.get(env_var_name)
209
+ if result is None:
210
+ msg = f"Unset environment variable: {env_var_name}"
211
+ raise ValueError(msg)
212
+ return result
213
+
214
+
215
+ def get_win_folder_if_csidl_name_not_env_var(csidl_name: str) -> str | None: # noqa: PLR0911
216
+ """Get a folder for a CSIDL name that does not exist as an environment variable."""
217
+ if csidl_name == "CSIDL_PERSONAL":
218
+ return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents") # noqa: PTH118
219
+
220
+ if csidl_name == "CSIDL_DOWNLOADS":
221
+ return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Downloads") # noqa: PTH118
222
+
223
+ if csidl_name == "CSIDL_MYPICTURES":
224
+ return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Pictures") # noqa: PTH118
225
+
226
+ if csidl_name == "CSIDL_MYVIDEO":
227
+ return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Videos") # noqa: PTH118
228
+
229
+ if csidl_name == "CSIDL_MYMUSIC":
230
+ return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Music") # noqa: PTH118
231
+
232
+ if csidl_name == "CSIDL_PROGRAMS":
233
+ return os.path.join( # noqa: PTH118
234
+ os.path.normpath(os.environ["APPDATA"]),
235
+ "Microsoft",
236
+ "Windows",
237
+ "Start Menu",
238
+ "Programs",
239
+ )
240
+
241
+ if csidl_name == "CSIDL_COMMON_PROGRAMS":
242
+ return os.path.join( # noqa: PTH118
243
+ os.path.normpath(os.environ.get("PROGRAMDATA", os.environ.get("ALLUSERSPROFILE", "C:\\ProgramData"))),
244
+ "Microsoft",
245
+ "Windows",
246
+ "Start Menu",
247
+ "Programs",
248
+ )
249
+ return None
250
+
251
+
252
+ def get_win_folder_from_registry(csidl_name: str) -> str:
253
+ """Get folder from the registry.
254
+
255
+ This is a fallback technique at best. I'm not sure if using the registry for these guarantees us the correct answer
256
+ for all CSIDL_* names.
257
+
258
+ """
259
+ machine_names = {
260
+ "CSIDL_COMMON_APPDATA",
261
+ "CSIDL_COMMON_PROGRAMS",
262
+ }
263
+ shell_folder_name = {
264
+ "CSIDL_APPDATA": "AppData",
265
+ "CSIDL_COMMON_APPDATA": "Common AppData",
266
+ "CSIDL_LOCAL_APPDATA": "Local AppData",
267
+ "CSIDL_PERSONAL": "Personal",
268
+ "CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}",
269
+ "CSIDL_MYPICTURES": "My Pictures",
270
+ "CSIDL_MYVIDEO": "My Video",
271
+ "CSIDL_MYMUSIC": "My Music",
272
+ "CSIDL_PROGRAMS": "Programs",
273
+ "CSIDL_COMMON_PROGRAMS": "Common Programs",
274
+ }.get(csidl_name)
275
+ if shell_folder_name is None:
276
+ msg = f"Unknown CSIDL name: {csidl_name}"
277
+ raise ValueError(msg)
278
+ if sys.platform != "win32": # only needed for mypy type checker to know that this code runs only on Windows
279
+ raise NotImplementedError
280
+ import winreg # noqa: PLC0415
281
+
282
+ # Use HKEY_LOCAL_MACHINE for system-wide folders, HKEY_CURRENT_USER for user-specific folders
283
+ hkey = winreg.HKEY_LOCAL_MACHINE if csidl_name in machine_names else winreg.HKEY_CURRENT_USER
284
+
285
+ key = winreg.OpenKey(hkey, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
286
+ directory, _ = winreg.QueryValueEx(key, shell_folder_name)
287
+ return str(directory)
288
+
289
+
290
+ _KNOWN_FOLDER_GUIDS: dict[str, str] = {
291
+ "CSIDL_APPDATA": "{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}",
292
+ "CSIDL_COMMON_APPDATA": "{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}",
293
+ "CSIDL_LOCAL_APPDATA": "{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}",
294
+ "CSIDL_PERSONAL": "{FDD39AD0-238F-46AF-ADB4-6C85480369C7}",
295
+ "CSIDL_MYPICTURES": "{33E28130-4E1E-4676-835A-98395C3BC3BB}",
296
+ "CSIDL_MYVIDEO": "{18989B1D-99B5-455B-841C-AB7C74E4DDFC}",
297
+ "CSIDL_MYMUSIC": "{4BD8D571-6D19-48D3-BE97-422220080E43}",
298
+ "CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}",
299
+ "CSIDL_DESKTOPDIRECTORY": "{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}",
300
+ "CSIDL_PROGRAMS": "{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}",
301
+ "CSIDL_COMMON_PROGRAMS": "{0139D44E-6AFE-49F2-8690-3DAFCAE6FFB8}",
302
+ }
303
+
304
+
305
+ def get_win_folder_via_ctypes(csidl_name: str) -> str:
306
+ """Get folder via :func:`SHGetKnownFolderPath`.
307
+
308
+ See https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath.
309
+
310
+ """
311
+ if sys.platform != "win32": # only needed for type checker to know that this code runs only on Windows
312
+ raise NotImplementedError
313
+ from ctypes import HRESULT, POINTER, Structure, WinDLL, byref, create_unicode_buffer, wintypes # noqa: PLC0415
314
+
315
+ class _GUID(Structure):
316
+ _fields_ = [
317
+ ("Data1", wintypes.DWORD),
318
+ ("Data2", wintypes.WORD),
319
+ ("Data3", wintypes.WORD),
320
+ ("Data4", wintypes.BYTE * 8),
321
+ ]
322
+
323
+ ole32 = WinDLL("ole32")
324
+ ole32.CLSIDFromString.restype = HRESULT
325
+ ole32.CLSIDFromString.argtypes = [wintypes.LPCOLESTR, POINTER(_GUID)]
326
+ ole32.CoTaskMemFree.restype = None
327
+ ole32.CoTaskMemFree.argtypes = [wintypes.LPVOID]
328
+
329
+ shell32 = WinDLL("shell32")
330
+ shell32.SHGetKnownFolderPath.restype = HRESULT
331
+ shell32.SHGetKnownFolderPath.argtypes = [POINTER(_GUID), wintypes.DWORD, wintypes.HANDLE, POINTER(wintypes.LPWSTR)]
332
+
333
+ kernel32 = WinDLL("kernel32")
334
+ kernel32.GetShortPathNameW.restype = wintypes.DWORD
335
+ kernel32.GetShortPathNameW.argtypes = [wintypes.LPWSTR, wintypes.LPWSTR, wintypes.DWORD]
336
+
337
+ folder_guid = _KNOWN_FOLDER_GUIDS.get(csidl_name)
338
+ if folder_guid is None:
339
+ msg = f"Unknown CSIDL name: {csidl_name}"
340
+ raise ValueError(msg)
341
+
342
+ guid = _GUID()
343
+ ole32.CLSIDFromString(folder_guid, byref(guid))
344
+
345
+ path_ptr = wintypes.LPWSTR()
346
+ shell32.SHGetKnownFolderPath(byref(guid), _KF_FLAG_DONT_VERIFY, None, byref(path_ptr))
347
+ result = path_ptr.value
348
+ ole32.CoTaskMemFree(path_ptr)
349
+
350
+ if result is None:
351
+ msg = f"SHGetKnownFolderPath returned NULL for {csidl_name}"
352
+ raise ValueError(msg)
353
+
354
+ if any(ord(c) > 255 for c in result): # noqa: PLR2004
355
+ buf = create_unicode_buffer(1024)
356
+ if kernel32.GetShortPathNameW(result, buf, 1024):
357
+ result = buf.value
358
+
359
+ return result
360
+
361
+
362
+ def _pick_get_win_folder() -> Callable[[str], str]:
363
+ """Select the best method to resolve Windows folder paths: ctypes, then registry, then environment variables."""
364
+ try:
365
+ import ctypes # noqa: PLC0415, F401
366
+ except ImportError:
367
+ pass
368
+ else:
369
+ return get_win_folder_via_ctypes
370
+ try:
371
+ import winreg # noqa: PLC0415, F401
372
+ except ImportError:
373
+ return get_win_folder_from_env_vars
374
+ else:
375
+ return get_win_folder_from_registry
376
+
377
+
378
+ _resolve_win_folder = _pick_get_win_folder()
379
+
380
+
381
+ def get_win_folder(csidl_name: str) -> str:
382
+ """Get a Windows folder path, checking for ``WIN_PD_OVERRIDE_*`` environment variable overrides first.
383
+
384
+ For example, ``CSIDL_LOCAL_APPDATA`` can be overridden by setting ``WIN_PD_OVERRIDE_LOCAL_APPDATA``.
385
+
386
+ """
387
+ env_var = f"WIN_PD_OVERRIDE_{csidl_name.removeprefix('CSIDL_')}"
388
+ if override := os.environ.get(env_var, "").strip():
389
+ return override
390
+ return _resolve_win_folder(csidl_name)
391
+
392
+
393
+ __all__ = [
394
+ "Windows",
395
+ ]
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/LICENSE ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2006-2022 by the respective authors (see AUTHORS file).
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are
6
+ met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ * Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/__init__.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Pygments
3
+ ~~~~~~~~
4
+
5
+ Pygments is a syntax highlighting package written in Python.
6
+
7
+ It is a generic syntax highlighter for general use in all kinds of software
8
+ such as forum systems, wikis or other applications that need to prettify
9
+ source code. Highlights are:
10
+
11
+ * a wide range of common languages and markup formats is supported
12
+ * special attention is paid to details, increasing quality by a fair amount
13
+ * support for new languages and formats are added easily
14
+ * a number of output formats, presently HTML, LaTeX, RTF, SVG, all image
15
+ formats that PIL supports, and ANSI sequences
16
+ * it is usable as a command-line tool and as a library
17
+ * ... and it highlights even Brainfuck!
18
+
19
+ The `Pygments master branch`_ is installable with ``easy_install Pygments==dev``.
20
+
21
+ .. _Pygments master branch:
22
+ https://github.com/pygments/pygments/archive/master.zip#egg=Pygments-dev
23
+
24
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
25
+ :license: BSD, see LICENSE for details.
26
+ """
27
+ from io import StringIO, BytesIO
28
+
29
+ __version__ = '2.20.0'
30
+ __docformat__ = 'restructuredtext'
31
+
32
+ __all__ = ['lex', 'format', 'highlight']
33
+
34
+
35
+ def lex(code, lexer):
36
+ """
37
+ Lex `code` with the `lexer` (must be a `Lexer` instance)
38
+ and return an iterable of tokens. Currently, this only calls
39
+ `lexer.get_tokens()`.
40
+ """
41
+ try:
42
+ return lexer.get_tokens(code)
43
+ except TypeError:
44
+ # Heuristic to catch a common mistake.
45
+ from pip._vendor.pygments.lexer import RegexLexer
46
+ if isinstance(lexer, type) and issubclass(lexer, RegexLexer):
47
+ raise TypeError('lex() argument must be a lexer instance, '
48
+ 'not a class')
49
+ raise
50
+
51
+
52
+ def format(tokens, formatter, outfile=None): # pylint: disable=redefined-builtin
53
+ """
54
+ Format ``tokens`` (an iterable of tokens) with the formatter ``formatter``
55
+ (a `Formatter` instance).
56
+
57
+ If ``outfile`` is given and a valid file object (an object with a
58
+ ``write`` method), the result will be written to it, otherwise it
59
+ is returned as a string.
60
+ """
61
+ try:
62
+ if not outfile:
63
+ realoutfile = getattr(formatter, 'encoding', None) and BytesIO() or StringIO()
64
+ formatter.format(tokens, realoutfile)
65
+ return realoutfile.getvalue()
66
+ else:
67
+ formatter.format(tokens, outfile)
68
+ except TypeError:
69
+ # Heuristic to catch a common mistake.
70
+ from pip._vendor.pygments.formatter import Formatter
71
+ if isinstance(formatter, type) and issubclass(formatter, Formatter):
72
+ raise TypeError('format() argument must be a formatter instance, '
73
+ 'not a class')
74
+ raise
75
+
76
+
77
+ def highlight(code, lexer, formatter, outfile=None):
78
+ """
79
+ This is the most high-level highlighting function. It combines `lex` and
80
+ `format` in one function.
81
+ """
82
+ return format(lex(code, lexer), formatter, outfile)
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/__main__.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.__main__
3
+ ~~~~~~~~~~~~~~~~~
4
+
5
+ Main entry point for ``python -m pygments``.
6
+
7
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import sys
12
+ from pip._vendor.pygments.cmdline import main
13
+
14
+ try:
15
+ sys.exit(main(sys.argv))
16
+ except KeyboardInterrupt:
17
+ sys.exit(1)
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/console.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.console
3
+ ~~~~~~~~~~~~~~~~
4
+
5
+ Format colored console output.
6
+
7
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ esc = "\x1b["
12
+
13
+ codes = {}
14
+ codes[""] = ""
15
+ codes["reset"] = esc + "39;49;00m"
16
+
17
+ codes["bold"] = esc + "01m"
18
+ codes["faint"] = esc + "02m"
19
+ codes["standout"] = esc + "03m"
20
+ codes["underline"] = esc + "04m"
21
+ codes["blink"] = esc + "05m"
22
+ codes["overline"] = esc + "06m"
23
+
24
+ dark_colors = ["black", "red", "green", "yellow", "blue",
25
+ "magenta", "cyan", "gray"]
26
+ light_colors = ["brightblack", "brightred", "brightgreen", "brightyellow", "brightblue",
27
+ "brightmagenta", "brightcyan", "white"]
28
+
29
+ x = 30
30
+ for dark, light in zip(dark_colors, light_colors):
31
+ codes[dark] = esc + "%im" % x
32
+ codes[light] = esc + "%im" % (60 + x)
33
+ x += 1
34
+
35
+ del dark, light, x
36
+
37
+ codes["white"] = codes["bold"]
38
+
39
+
40
+ def reset_color():
41
+ return codes["reset"]
42
+
43
+
44
+ def colorize(color_key, text):
45
+ return codes[color_key] + text + codes["reset"]
46
+
47
+
48
+ def ansiformat(attr, text):
49
+ """
50
+ Format ``text`` with a color and/or some attributes::
51
+
52
+ color normal color
53
+ *color* bold color
54
+ _color_ underlined color
55
+ +color+ blinking color
56
+ """
57
+ result = []
58
+ if attr[:1] == attr[-1:] == '+':
59
+ result.append(codes['blink'])
60
+ attr = attr[1:-1]
61
+ if attr[:1] == attr[-1:] == '*':
62
+ result.append(codes['bold'])
63
+ attr = attr[1:-1]
64
+ if attr[:1] == attr[-1:] == '_':
65
+ result.append(codes['underline'])
66
+ attr = attr[1:-1]
67
+ result.append(codes[attr])
68
+ result.append(text)
69
+ result.append(codes['reset'])
70
+ return ''.join(result)
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/filter.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.filter
3
+ ~~~~~~~~~~~~~~~
4
+
5
+ Module that implements the default filter.
6
+
7
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+
12
+ def apply_filters(stream, filters, lexer=None):
13
+ """
14
+ Use this method to apply an iterable of filters to
15
+ a stream. If lexer is given it's forwarded to the
16
+ filter, otherwise the filter receives `None`.
17
+ """
18
+ def _apply(filter_, stream):
19
+ yield from filter_.filter(lexer, stream)
20
+ for filter_ in filters:
21
+ stream = _apply(filter_, stream)
22
+ return stream
23
+
24
+
25
+ def simplefilter(f):
26
+ """
27
+ Decorator that converts a function into a filter::
28
+
29
+ @simplefilter
30
+ def lowercase(self, lexer, stream, options):
31
+ for ttype, value in stream:
32
+ yield ttype, value.lower()
33
+ """
34
+ return type(f.__name__, (FunctionFilter,), {
35
+ '__module__': getattr(f, '__module__'),
36
+ '__doc__': f.__doc__,
37
+ 'function': f,
38
+ })
39
+
40
+
41
+ class Filter:
42
+ """
43
+ Default filter. Subclass this class or use the `simplefilter`
44
+ decorator to create own filters.
45
+ """
46
+
47
+ def __init__(self, **options):
48
+ self.options = options
49
+
50
+ def filter(self, lexer, stream):
51
+ raise NotImplementedError()
52
+
53
+
54
+ class FunctionFilter(Filter):
55
+ """
56
+ Abstract class used by `simplefilter` to create simple
57
+ function filters on the fly. The `simplefilter` decorator
58
+ automatically creates subclasses of this class for
59
+ functions passed to it.
60
+ """
61
+ function = None
62
+
63
+ def __init__(self, **options):
64
+ if not hasattr(self, 'function'):
65
+ raise TypeError(f'{self.__class__.__name__!r} used without bound function')
66
+ Filter.__init__(self, **options)
67
+
68
+ def filter(self, lexer, stream):
69
+ # pylint: disable=not-callable
70
+ yield from self.function(lexer, stream, self.options)
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/filters/__init__.py ADDED
@@ -0,0 +1,942 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.filters
3
+ ~~~~~~~~~~~~~~~~
4
+
5
+ Module containing filter lookup functions and default
6
+ filters.
7
+
8
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
9
+ :license: BSD, see LICENSE for details.
10
+ """
11
+
12
+ import re
13
+
14
+ from pip._vendor.pygments.token import String, Comment, Keyword, Name, Error, Whitespace, \
15
+ string_to_tokentype
16
+ from pip._vendor.pygments.filter import Filter
17
+ from pip._vendor.pygments.util import get_list_opt, get_int_opt, get_bool_opt, \
18
+ get_choice_opt, ClassNotFound, OptionError
19
+ from pip._vendor.pygments.plugin import find_plugin_filters
20
+
21
+
22
+ def find_filter_class(filtername):
23
+ """Lookup a filter by name. Return None if not found."""
24
+ if filtername in FILTERS:
25
+ return FILTERS[filtername]
26
+ for name, cls in find_plugin_filters():
27
+ if name == filtername:
28
+ return cls
29
+ return None
30
+
31
+
32
+ def get_filter_by_name(filtername, **options):
33
+ """Return an instantiated filter.
34
+
35
+ Options are passed to the filter initializer if wanted.
36
+ Raise a ClassNotFound if not found.
37
+ """
38
+ cls = find_filter_class(filtername)
39
+ if cls:
40
+ return cls(**options)
41
+ else:
42
+ raise ClassNotFound(f'filter {filtername!r} not found')
43
+
44
+
45
+ def get_all_filters():
46
+ """Return a generator of all filter names."""
47
+ yield from FILTERS
48
+ for name, _ in find_plugin_filters():
49
+ yield name
50
+
51
+
52
+ def _replace_special(ttype, value, regex, specialttype,
53
+ replacefunc=lambda x: x):
54
+ last = 0
55
+ for match in regex.finditer(value):
56
+ start, end = match.start(), match.end()
57
+ if start != last:
58
+ yield ttype, value[last:start]
59
+ yield specialttype, replacefunc(value[start:end])
60
+ last = end
61
+ if last != len(value):
62
+ yield ttype, value[last:]
63
+
64
+
65
+ class CodeTagFilter(Filter):
66
+ """Highlight special code tags in comments and docstrings.
67
+
68
+ Options accepted:
69
+
70
+ `codetags` : list of strings
71
+ A list of strings that are flagged as code tags. The default is to
72
+ highlight ``XXX``, ``TODO``, ``FIXME``, ``BUG`` and ``NOTE``.
73
+
74
+ .. versionchanged:: 2.13
75
+ Now recognizes ``FIXME`` by default.
76
+ """
77
+
78
+ def __init__(self, **options):
79
+ Filter.__init__(self, **options)
80
+ tags = get_list_opt(options, 'codetags',
81
+ ['XXX', 'TODO', 'FIXME', 'BUG', 'NOTE'])
82
+ self.tag_re = re.compile(r'\b({})\b'.format('|'.join([
83
+ re.escape(tag) for tag in tags if tag
84
+ ])))
85
+
86
+ def filter(self, lexer, stream):
87
+ regex = self.tag_re
88
+ for ttype, value in stream:
89
+ if ttype in String.Doc or \
90
+ ttype in Comment and \
91
+ ttype not in Comment.Preproc:
92
+ yield from _replace_special(ttype, value, regex, Comment.Special)
93
+ else:
94
+ yield ttype, value
95
+
96
+
97
+ class SymbolFilter(Filter):
98
+ """Convert mathematical symbols into Unicode characters.
99
+
100
+ Examples are ``\\<longrightarrow>`` in Isabelle or
101
+ ``\\longrightarrow`` in LaTeX.
102
+
103
+ This is mostly useful for HTML or console output when you want to
104
+ approximate the source rendering you'd see in an IDE.
105
+
106
+ Options accepted:
107
+
108
+ `lang` : string
109
+ The symbol language. Must be one of ``'isabelle'`` or
110
+ ``'latex'``. The default is ``'isabelle'``.
111
+ """
112
+
113
+ latex_symbols = {
114
+ '\\alpha' : '\U000003b1',
115
+ '\\beta' : '\U000003b2',
116
+ '\\gamma' : '\U000003b3',
117
+ '\\delta' : '\U000003b4',
118
+ '\\varepsilon' : '\U000003b5',
119
+ '\\zeta' : '\U000003b6',
120
+ '\\eta' : '\U000003b7',
121
+ '\\vartheta' : '\U000003b8',
122
+ '\\iota' : '\U000003b9',
123
+ '\\kappa' : '\U000003ba',
124
+ '\\lambda' : '\U000003bb',
125
+ '\\mu' : '\U000003bc',
126
+ '\\nu' : '\U000003bd',
127
+ '\\xi' : '\U000003be',
128
+ '\\pi' : '\U000003c0',
129
+ '\\varrho' : '\U000003c1',
130
+ '\\sigma' : '\U000003c3',
131
+ '\\tau' : '\U000003c4',
132
+ '\\upsilon' : '\U000003c5',
133
+ '\\varphi' : '\U000003c6',
134
+ '\\chi' : '\U000003c7',
135
+ '\\psi' : '\U000003c8',
136
+ '\\omega' : '\U000003c9',
137
+ '\\Gamma' : '\U00000393',
138
+ '\\Delta' : '\U00000394',
139
+ '\\Theta' : '\U00000398',
140
+ '\\Lambda' : '\U0000039b',
141
+ '\\Xi' : '\U0000039e',
142
+ '\\Pi' : '\U000003a0',
143
+ '\\Sigma' : '\U000003a3',
144
+ '\\Upsilon' : '\U000003a5',
145
+ '\\Phi' : '\U000003a6',
146
+ '\\Psi' : '\U000003a8',
147
+ '\\Omega' : '\U000003a9',
148
+ '\\leftarrow' : '\U00002190',
149
+ '\\longleftarrow' : '\U000027f5',
150
+ '\\rightarrow' : '\U00002192',
151
+ '\\longrightarrow' : '\U000027f6',
152
+ '\\Leftarrow' : '\U000021d0',
153
+ '\\Longleftarrow' : '\U000027f8',
154
+ '\\Rightarrow' : '\U000021d2',
155
+ '\\Longrightarrow' : '\U000027f9',
156
+ '\\leftrightarrow' : '\U00002194',
157
+ '\\longleftrightarrow' : '\U000027f7',
158
+ '\\Leftrightarrow' : '\U000021d4',
159
+ '\\Longleftrightarrow' : '\U000027fa',
160
+ '\\mapsto' : '\U000021a6',
161
+ '\\longmapsto' : '\U000027fc',
162
+ '\\relbar' : '\U00002500',
163
+ '\\Relbar' : '\U00002550',
164
+ '\\hookleftarrow' : '\U000021a9',
165
+ '\\hookrightarrow' : '\U000021aa',
166
+ '\\leftharpoondown' : '\U000021bd',
167
+ '\\rightharpoondown' : '\U000021c1',
168
+ '\\leftharpoonup' : '\U000021bc',
169
+ '\\rightharpoonup' : '\U000021c0',
170
+ '\\rightleftharpoons' : '\U000021cc',
171
+ '\\leadsto' : '\U0000219d',
172
+ '\\downharpoonleft' : '\U000021c3',
173
+ '\\downharpoonright' : '\U000021c2',
174
+ '\\upharpoonleft' : '\U000021bf',
175
+ '\\upharpoonright' : '\U000021be',
176
+ '\\restriction' : '\U000021be',
177
+ '\\uparrow' : '\U00002191',
178
+ '\\Uparrow' : '\U000021d1',
179
+ '\\downarrow' : '\U00002193',
180
+ '\\Downarrow' : '\U000021d3',
181
+ '\\updownarrow' : '\U00002195',
182
+ '\\Updownarrow' : '\U000021d5',
183
+ '\\langle' : '\U000027e8',
184
+ '\\rangle' : '\U000027e9',
185
+ '\\lceil' : '\U00002308',
186
+ '\\rceil' : '\U00002309',
187
+ '\\lfloor' : '\U0000230a',
188
+ '\\rfloor' : '\U0000230b',
189
+ '\\flqq' : '\U000000ab',
190
+ '\\frqq' : '\U000000bb',
191
+ '\\bot' : '\U000022a5',
192
+ '\\top' : '\U000022a4',
193
+ '\\wedge' : '\U00002227',
194
+ '\\bigwedge' : '\U000022c0',
195
+ '\\vee' : '\U00002228',
196
+ '\\bigvee' : '\U000022c1',
197
+ '\\forall' : '\U00002200',
198
+ '\\exists' : '\U00002203',
199
+ '\\nexists' : '\U00002204',
200
+ '\\neg' : '\U000000ac',
201
+ '\\Box' : '\U000025a1',
202
+ '\\Diamond' : '\U000025c7',
203
+ '\\vdash' : '\U000022a2',
204
+ '\\models' : '\U000022a8',
205
+ '\\dashv' : '\U000022a3',
206
+ '\\surd' : '\U0000221a',
207
+ '\\le' : '\U00002264',
208
+ '\\ge' : '\U00002265',
209
+ '\\ll' : '\U0000226a',
210
+ '\\gg' : '\U0000226b',
211
+ '\\lesssim' : '\U00002272',
212
+ '\\gtrsim' : '\U00002273',
213
+ '\\lessapprox' : '\U00002a85',
214
+ '\\gtrapprox' : '\U00002a86',
215
+ '\\in' : '\U00002208',
216
+ '\\notin' : '\U00002209',
217
+ '\\subset' : '\U00002282',
218
+ '\\supset' : '\U00002283',
219
+ '\\subseteq' : '\U00002286',
220
+ '\\supseteq' : '\U00002287',
221
+ '\\sqsubset' : '\U0000228f',
222
+ '\\sqsupset' : '\U00002290',
223
+ '\\sqsubseteq' : '\U00002291',
224
+ '\\sqsupseteq' : '\U00002292',
225
+ '\\cap' : '\U00002229',
226
+ '\\bigcap' : '\U000022c2',
227
+ '\\cup' : '\U0000222a',
228
+ '\\bigcup' : '\U000022c3',
229
+ '\\sqcup' : '\U00002294',
230
+ '\\bigsqcup' : '\U00002a06',
231
+ '\\sqcap' : '\U00002293',
232
+ '\\Bigsqcap' : '\U00002a05',
233
+ '\\setminus' : '\U00002216',
234
+ '\\propto' : '\U0000221d',
235
+ '\\uplus' : '\U0000228e',
236
+ '\\bigplus' : '\U00002a04',
237
+ '\\sim' : '\U0000223c',
238
+ '\\doteq' : '\U00002250',
239
+ '\\simeq' : '\U00002243',
240
+ '\\approx' : '\U00002248',
241
+ '\\asymp' : '\U0000224d',
242
+ '\\cong' : '\U00002245',
243
+ '\\equiv' : '\U00002261',
244
+ '\\Join' : '\U000022c8',
245
+ '\\bowtie' : '\U00002a1d',
246
+ '\\prec' : '\U0000227a',
247
+ '\\succ' : '\U0000227b',
248
+ '\\preceq' : '\U0000227c',
249
+ '\\succeq' : '\U0000227d',
250
+ '\\parallel' : '\U00002225',
251
+ '\\mid' : '\U000000a6',
252
+ '\\pm' : '\U000000b1',
253
+ '\\mp' : '\U00002213',
254
+ '\\times' : '\U000000d7',
255
+ '\\div' : '\U000000f7',
256
+ '\\cdot' : '\U000022c5',
257
+ '\\star' : '\U000022c6',
258
+ '\\circ' : '\U00002218',
259
+ '\\dagger' : '\U00002020',
260
+ '\\ddagger' : '\U00002021',
261
+ '\\lhd' : '\U000022b2',
262
+ '\\rhd' : '\U000022b3',
263
+ '\\unlhd' : '\U000022b4',
264
+ '\\unrhd' : '\U000022b5',
265
+ '\\triangleleft' : '\U000025c3',
266
+ '\\triangleright' : '\U000025b9',
267
+ '\\triangle' : '\U000025b3',
268
+ '\\triangleq' : '\U0000225c',
269
+ '\\oplus' : '\U00002295',
270
+ '\\bigoplus' : '\U00002a01',
271
+ '\\otimes' : '\U00002297',
272
+ '\\bigotimes' : '\U00002a02',
273
+ '\\odot' : '\U00002299',
274
+ '\\bigodot' : '\U00002a00',
275
+ '\\ominus' : '\U00002296',
276
+ '\\oslash' : '\U00002298',
277
+ '\\dots' : '\U00002026',
278
+ '\\cdots' : '\U000022ef',
279
+ '\\sum' : '\U00002211',
280
+ '\\prod' : '\U0000220f',
281
+ '\\coprod' : '\U00002210',
282
+ '\\infty' : '\U0000221e',
283
+ '\\int' : '\U0000222b',
284
+ '\\oint' : '\U0000222e',
285
+ '\\clubsuit' : '\U00002663',
286
+ '\\diamondsuit' : '\U00002662',
287
+ '\\heartsuit' : '\U00002661',
288
+ '\\spadesuit' : '\U00002660',
289
+ '\\aleph' : '\U00002135',
290
+ '\\emptyset' : '\U00002205',
291
+ '\\nabla' : '\U00002207',
292
+ '\\partial' : '\U00002202',
293
+ '\\flat' : '\U0000266d',
294
+ '\\natural' : '\U0000266e',
295
+ '\\sharp' : '\U0000266f',
296
+ '\\angle' : '\U00002220',
297
+ '\\copyright' : '\U000000a9',
298
+ '\\textregistered' : '\U000000ae',
299
+ '\\textonequarter' : '\U000000bc',
300
+ '\\textonehalf' : '\U000000bd',
301
+ '\\textthreequarters' : '\U000000be',
302
+ '\\textordfeminine' : '\U000000aa',
303
+ '\\textordmasculine' : '\U000000ba',
304
+ '\\euro' : '\U000020ac',
305
+ '\\pounds' : '\U000000a3',
306
+ '\\yen' : '\U000000a5',
307
+ '\\textcent' : '\U000000a2',
308
+ '\\textcurrency' : '\U000000a4',
309
+ '\\textdegree' : '\U000000b0',
310
+ }
311
+
312
+ isabelle_symbols = {
313
+ '\\<zero>' : '\U0001d7ec',
314
+ '\\<one>' : '\U0001d7ed',
315
+ '\\<two>' : '\U0001d7ee',
316
+ '\\<three>' : '\U0001d7ef',
317
+ '\\<four>' : '\U0001d7f0',
318
+ '\\<five>' : '\U0001d7f1',
319
+ '\\<six>' : '\U0001d7f2',
320
+ '\\<seven>' : '\U0001d7f3',
321
+ '\\<eight>' : '\U0001d7f4',
322
+ '\\<nine>' : '\U0001d7f5',
323
+ '\\<A>' : '\U0001d49c',
324
+ '\\<B>' : '\U0000212c',
325
+ '\\<C>' : '\U0001d49e',
326
+ '\\<D>' : '\U0001d49f',
327
+ '\\<E>' : '\U00002130',
328
+ '\\<F>' : '\U00002131',
329
+ '\\<G>' : '\U0001d4a2',
330
+ '\\<H>' : '\U0000210b',
331
+ '\\<I>' : '\U00002110',
332
+ '\\<J>' : '\U0001d4a5',
333
+ '\\<K>' : '\U0001d4a6',
334
+ '\\<L>' : '\U00002112',
335
+ '\\<M>' : '\U00002133',
336
+ '\\<N>' : '\U0001d4a9',
337
+ '\\<O>' : '\U0001d4aa',
338
+ '\\<P>' : '\U0001d4ab',
339
+ '\\<Q>' : '\U0001d4ac',
340
+ '\\<R>' : '\U0000211b',
341
+ '\\<S>' : '\U0001d4ae',
342
+ '\\<T>' : '\U0001d4af',
343
+ '\\<U>' : '\U0001d4b0',
344
+ '\\<V>' : '\U0001d4b1',
345
+ '\\<W>' : '\U0001d4b2',
346
+ '\\<X>' : '\U0001d4b3',
347
+ '\\<Y>' : '\U0001d4b4',
348
+ '\\<Z>' : '\U0001d4b5',
349
+ '\\<a>' : '\U0001d5ba',
350
+ '\\<b>' : '\U0001d5bb',
351
+ '\\<c>' : '\U0001d5bc',
352
+ '\\<d>' : '\U0001d5bd',
353
+ '\\<e>' : '\U0001d5be',
354
+ '\\<f>' : '\U0001d5bf',
355
+ '\\<g>' : '\U0001d5c0',
356
+ '\\<h>' : '\U0001d5c1',
357
+ '\\<i>' : '\U0001d5c2',
358
+ '\\<j>' : '\U0001d5c3',
359
+ '\\<k>' : '\U0001d5c4',
360
+ '\\<l>' : '\U0001d5c5',
361
+ '\\<m>' : '\U0001d5c6',
362
+ '\\<n>' : '\U0001d5c7',
363
+ '\\<o>' : '\U0001d5c8',
364
+ '\\<p>' : '\U0001d5c9',
365
+ '\\<q>' : '\U0001d5ca',
366
+ '\\<r>' : '\U0001d5cb',
367
+ '\\<s>' : '\U0001d5cc',
368
+ '\\<t>' : '\U0001d5cd',
369
+ '\\<u>' : '\U0001d5ce',
370
+ '\\<v>' : '\U0001d5cf',
371
+ '\\<w>' : '\U0001d5d0',
372
+ '\\<x>' : '\U0001d5d1',
373
+ '\\<y>' : '\U0001d5d2',
374
+ '\\<z>' : '\U0001d5d3',
375
+ '\\<AA>' : '\U0001d504',
376
+ '\\<BB>' : '\U0001d505',
377
+ '\\<CC>' : '\U0000212d',
378
+ '\\<DD>' : '\U0001d507',
379
+ '\\<EE>' : '\U0001d508',
380
+ '\\<FF>' : '\U0001d509',
381
+ '\\<GG>' : '\U0001d50a',
382
+ '\\<HH>' : '\U0000210c',
383
+ '\\<II>' : '\U00002111',
384
+ '\\<JJ>' : '\U0001d50d',
385
+ '\\<KK>' : '\U0001d50e',
386
+ '\\<LL>' : '\U0001d50f',
387
+ '\\<MM>' : '\U0001d510',
388
+ '\\<NN>' : '\U0001d511',
389
+ '\\<OO>' : '\U0001d512',
390
+ '\\<PP>' : '\U0001d513',
391
+ '\\<QQ>' : '\U0001d514',
392
+ '\\<RR>' : '\U0000211c',
393
+ '\\<SS>' : '\U0001d516',
394
+ '\\<TT>' : '\U0001d517',
395
+ '\\<UU>' : '\U0001d518',
396
+ '\\<VV>' : '\U0001d519',
397
+ '\\<WW>' : '\U0001d51a',
398
+ '\\<XX>' : '\U0001d51b',
399
+ '\\<YY>' : '\U0001d51c',
400
+ '\\<ZZ>' : '\U00002128',
401
+ '\\<aa>' : '\U0001d51e',
402
+ '\\<bb>' : '\U0001d51f',
403
+ '\\<cc>' : '\U0001d520',
404
+ '\\<dd>' : '\U0001d521',
405
+ '\\<ee>' : '\U0001d522',
406
+ '\\<ff>' : '\U0001d523',
407
+ '\\<gg>' : '\U0001d524',
408
+ '\\<hh>' : '\U0001d525',
409
+ '\\<ii>' : '\U0001d526',
410
+ '\\<jj>' : '\U0001d527',
411
+ '\\<kk>' : '\U0001d528',
412
+ '\\<ll>' : '\U0001d529',
413
+ '\\<mm>' : '\U0001d52a',
414
+ '\\<nn>' : '\U0001d52b',
415
+ '\\<oo>' : '\U0001d52c',
416
+ '\\<pp>' : '\U0001d52d',
417
+ '\\<qq>' : '\U0001d52e',
418
+ '\\<rr>' : '\U0001d52f',
419
+ '\\<ss>' : '\U0001d530',
420
+ '\\<tt>' : '\U0001d531',
421
+ '\\<uu>' : '\U0001d532',
422
+ '\\<vv>' : '\U0001d533',
423
+ '\\<ww>' : '\U0001d534',
424
+ '\\<xx>' : '\U0001d535',
425
+ '\\<yy>' : '\U0001d536',
426
+ '\\<zz>' : '\U0001d537',
427
+ '\\<alpha>' : '\U000003b1',
428
+ '\\<beta>' : '\U000003b2',
429
+ '\\<gamma>' : '\U000003b3',
430
+ '\\<delta>' : '\U000003b4',
431
+ '\\<epsilon>' : '\U000003b5',
432
+ '\\<zeta>' : '\U000003b6',
433
+ '\\<eta>' : '\U000003b7',
434
+ '\\<theta>' : '\U000003b8',
435
+ '\\<iota>' : '\U000003b9',
436
+ '\\<kappa>' : '\U000003ba',
437
+ '\\<lambda>' : '\U000003bb',
438
+ '\\<mu>' : '\U000003bc',
439
+ '\\<nu>' : '\U000003bd',
440
+ '\\<xi>' : '\U000003be',
441
+ '\\<pi>' : '\U000003c0',
442
+ '\\<rho>' : '\U000003c1',
443
+ '\\<sigma>' : '\U000003c3',
444
+ '\\<tau>' : '\U000003c4',
445
+ '\\<upsilon>' : '\U000003c5',
446
+ '\\<phi>' : '\U000003c6',
447
+ '\\<chi>' : '\U000003c7',
448
+ '\\<psi>' : '\U000003c8',
449
+ '\\<omega>' : '\U000003c9',
450
+ '\\<Gamma>' : '\U00000393',
451
+ '\\<Delta>' : '\U00000394',
452
+ '\\<Theta>' : '\U00000398',
453
+ '\\<Lambda>' : '\U0000039b',
454
+ '\\<Xi>' : '\U0000039e',
455
+ '\\<Pi>' : '\U000003a0',
456
+ '\\<Sigma>' : '\U000003a3',
457
+ '\\<Upsilon>' : '\U000003a5',
458
+ '\\<Phi>' : '\U000003a6',
459
+ '\\<Psi>' : '\U000003a8',
460
+ '\\<Omega>' : '\U000003a9',
461
+ '\\<bool>' : '\U0001d539',
462
+ '\\<complex>' : '\U00002102',
463
+ '\\<nat>' : '\U00002115',
464
+ '\\<rat>' : '\U0000211a',
465
+ '\\<real>' : '\U0000211d',
466
+ '\\<int>' : '\U00002124',
467
+ '\\<leftarrow>' : '\U00002190',
468
+ '\\<longleftarrow>' : '\U000027f5',
469
+ '\\<rightarrow>' : '\U00002192',
470
+ '\\<longrightarrow>' : '\U000027f6',
471
+ '\\<Leftarrow>' : '\U000021d0',
472
+ '\\<Longleftarrow>' : '\U000027f8',
473
+ '\\<Rightarrow>' : '\U000021d2',
474
+ '\\<Longrightarrow>' : '\U000027f9',
475
+ '\\<leftrightarrow>' : '\U00002194',
476
+ '\\<longleftrightarrow>' : '\U000027f7',
477
+ '\\<Leftrightarrow>' : '\U000021d4',
478
+ '\\<Longleftrightarrow>' : '\U000027fa',
479
+ '\\<mapsto>' : '\U000021a6',
480
+ '\\<longmapsto>' : '\U000027fc',
481
+ '\\<midarrow>' : '\U00002500',
482
+ '\\<Midarrow>' : '\U00002550',
483
+ '\\<hookleftarrow>' : '\U000021a9',
484
+ '\\<hookrightarrow>' : '\U000021aa',
485
+ '\\<leftharpoondown>' : '\U000021bd',
486
+ '\\<rightharpoondown>' : '\U000021c1',
487
+ '\\<leftharpoonup>' : '\U000021bc',
488
+ '\\<rightharpoonup>' : '\U000021c0',
489
+ '\\<rightleftharpoons>' : '\U000021cc',
490
+ '\\<leadsto>' : '\U0000219d',
491
+ '\\<downharpoonleft>' : '\U000021c3',
492
+ '\\<downharpoonright>' : '\U000021c2',
493
+ '\\<upharpoonleft>' : '\U000021bf',
494
+ '\\<upharpoonright>' : '\U000021be',
495
+ '\\<restriction>' : '\U000021be',
496
+ '\\<Colon>' : '\U00002237',
497
+ '\\<up>' : '\U00002191',
498
+ '\\<Up>' : '\U000021d1',
499
+ '\\<down>' : '\U00002193',
500
+ '\\<Down>' : '\U000021d3',
501
+ '\\<updown>' : '\U00002195',
502
+ '\\<Updown>' : '\U000021d5',
503
+ '\\<langle>' : '\U000027e8',
504
+ '\\<rangle>' : '\U000027e9',
505
+ '\\<lceil>' : '\U00002308',
506
+ '\\<rceil>' : '\U00002309',
507
+ '\\<lfloor>' : '\U0000230a',
508
+ '\\<rfloor>' : '\U0000230b',
509
+ '\\<lparr>' : '\U00002987',
510
+ '\\<rparr>' : '\U00002988',
511
+ '\\<lbrakk>' : '\U000027e6',
512
+ '\\<rbrakk>' : '\U000027e7',
513
+ '\\<lbrace>' : '\U00002983',
514
+ '\\<rbrace>' : '\U00002984',
515
+ '\\<guillemotleft>' : '\U000000ab',
516
+ '\\<guillemotright>' : '\U000000bb',
517
+ '\\<bottom>' : '\U000022a5',
518
+ '\\<top>' : '\U000022a4',
519
+ '\\<and>' : '\U00002227',
520
+ '\\<And>' : '\U000022c0',
521
+ '\\<or>' : '\U00002228',
522
+ '\\<Or>' : '\U000022c1',
523
+ '\\<forall>' : '\U00002200',
524
+ '\\<exists>' : '\U00002203',
525
+ '\\<nexists>' : '\U00002204',
526
+ '\\<not>' : '\U000000ac',
527
+ '\\<box>' : '\U000025a1',
528
+ '\\<diamond>' : '\U000025c7',
529
+ '\\<turnstile>' : '\U000022a2',
530
+ '\\<Turnstile>' : '\U000022a8',
531
+ '\\<tturnstile>' : '\U000022a9',
532
+ '\\<TTurnstile>' : '\U000022ab',
533
+ '\\<stileturn>' : '\U000022a3',
534
+ '\\<surd>' : '\U0000221a',
535
+ '\\<le>' : '\U00002264',
536
+ '\\<ge>' : '\U00002265',
537
+ '\\<lless>' : '\U0000226a',
538
+ '\\<ggreater>' : '\U0000226b',
539
+ '\\<lesssim>' : '\U00002272',
540
+ '\\<greatersim>' : '\U00002273',
541
+ '\\<lessapprox>' : '\U00002a85',
542
+ '\\<greaterapprox>' : '\U00002a86',
543
+ '\\<in>' : '\U00002208',
544
+ '\\<notin>' : '\U00002209',
545
+ '\\<subset>' : '\U00002282',
546
+ '\\<supset>' : '\U00002283',
547
+ '\\<subseteq>' : '\U00002286',
548
+ '\\<supseteq>' : '\U00002287',
549
+ '\\<sqsubset>' : '\U0000228f',
550
+ '\\<sqsupset>' : '\U00002290',
551
+ '\\<sqsubseteq>' : '\U00002291',
552
+ '\\<sqsupseteq>' : '\U00002292',
553
+ '\\<inter>' : '\U00002229',
554
+ '\\<Inter>' : '\U000022c2',
555
+ '\\<union>' : '\U0000222a',
556
+ '\\<Union>' : '\U000022c3',
557
+ '\\<squnion>' : '\U00002294',
558
+ '\\<Squnion>' : '\U00002a06',
559
+ '\\<sqinter>' : '\U00002293',
560
+ '\\<Sqinter>' : '\U00002a05',
561
+ '\\<setminus>' : '\U00002216',
562
+ '\\<propto>' : '\U0000221d',
563
+ '\\<uplus>' : '\U0000228e',
564
+ '\\<Uplus>' : '\U00002a04',
565
+ '\\<noteq>' : '\U00002260',
566
+ '\\<sim>' : '\U0000223c',
567
+ '\\<doteq>' : '\U00002250',
568
+ '\\<simeq>' : '\U00002243',
569
+ '\\<approx>' : '\U00002248',
570
+ '\\<asymp>' : '\U0000224d',
571
+ '\\<cong>' : '\U00002245',
572
+ '\\<smile>' : '\U00002323',
573
+ '\\<equiv>' : '\U00002261',
574
+ '\\<frown>' : '\U00002322',
575
+ '\\<Join>' : '\U000022c8',
576
+ '\\<bowtie>' : '\U00002a1d',
577
+ '\\<prec>' : '\U0000227a',
578
+ '\\<succ>' : '\U0000227b',
579
+ '\\<preceq>' : '\U0000227c',
580
+ '\\<succeq>' : '\U0000227d',
581
+ '\\<parallel>' : '\U00002225',
582
+ '\\<bar>' : '\U000000a6',
583
+ '\\<plusminus>' : '\U000000b1',
584
+ '\\<minusplus>' : '\U00002213',
585
+ '\\<times>' : '\U000000d7',
586
+ '\\<div>' : '\U000000f7',
587
+ '\\<cdot>' : '\U000022c5',
588
+ '\\<star>' : '\U000022c6',
589
+ '\\<bullet>' : '\U00002219',
590
+ '\\<circ>' : '\U00002218',
591
+ '\\<dagger>' : '\U00002020',
592
+ '\\<ddagger>' : '\U00002021',
593
+ '\\<lhd>' : '\U000022b2',
594
+ '\\<rhd>' : '\U000022b3',
595
+ '\\<unlhd>' : '\U000022b4',
596
+ '\\<unrhd>' : '\U000022b5',
597
+ '\\<triangleleft>' : '\U000025c3',
598
+ '\\<triangleright>' : '\U000025b9',
599
+ '\\<triangle>' : '\U000025b3',
600
+ '\\<triangleq>' : '\U0000225c',
601
+ '\\<oplus>' : '\U00002295',
602
+ '\\<Oplus>' : '\U00002a01',
603
+ '\\<otimes>' : '\U00002297',
604
+ '\\<Otimes>' : '\U00002a02',
605
+ '\\<odot>' : '\U00002299',
606
+ '\\<Odot>' : '\U00002a00',
607
+ '\\<ominus>' : '\U00002296',
608
+ '\\<oslash>' : '\U00002298',
609
+ '\\<dots>' : '\U00002026',
610
+ '\\<cdots>' : '\U000022ef',
611
+ '\\<Sum>' : '\U00002211',
612
+ '\\<Prod>' : '\U0000220f',
613
+ '\\<Coprod>' : '\U00002210',
614
+ '\\<infinity>' : '\U0000221e',
615
+ '\\<integral>' : '\U0000222b',
616
+ '\\<ointegral>' : '\U0000222e',
617
+ '\\<clubsuit>' : '\U00002663',
618
+ '\\<diamondsuit>' : '\U00002662',
619
+ '\\<heartsuit>' : '\U00002661',
620
+ '\\<spadesuit>' : '\U00002660',
621
+ '\\<aleph>' : '\U00002135',
622
+ '\\<emptyset>' : '\U00002205',
623
+ '\\<nabla>' : '\U00002207',
624
+ '\\<partial>' : '\U00002202',
625
+ '\\<flat>' : '\U0000266d',
626
+ '\\<natural>' : '\U0000266e',
627
+ '\\<sharp>' : '\U0000266f',
628
+ '\\<angle>' : '\U00002220',
629
+ '\\<copyright>' : '\U000000a9',
630
+ '\\<registered>' : '\U000000ae',
631
+ '\\<hyphen>' : '\U000000ad',
632
+ '\\<inverse>' : '\U000000af',
633
+ '\\<onequarter>' : '\U000000bc',
634
+ '\\<onehalf>' : '\U000000bd',
635
+ '\\<threequarters>' : '\U000000be',
636
+ '\\<ordfeminine>' : '\U000000aa',
637
+ '\\<ordmasculine>' : '\U000000ba',
638
+ '\\<section>' : '\U000000a7',
639
+ '\\<paragraph>' : '\U000000b6',
640
+ '\\<exclamdown>' : '\U000000a1',
641
+ '\\<questiondown>' : '\U000000bf',
642
+ '\\<euro>' : '\U000020ac',
643
+ '\\<pounds>' : '\U000000a3',
644
+ '\\<yen>' : '\U000000a5',
645
+ '\\<cent>' : '\U000000a2',
646
+ '\\<currency>' : '\U000000a4',
647
+ '\\<degree>' : '\U000000b0',
648
+ '\\<amalg>' : '\U00002a3f',
649
+ '\\<mho>' : '\U00002127',
650
+ '\\<lozenge>' : '\U000025ca',
651
+ '\\<wp>' : '\U00002118',
652
+ '\\<wrong>' : '\U00002240',
653
+ '\\<struct>' : '\U000022c4',
654
+ '\\<acute>' : '\U000000b4',
655
+ '\\<index>' : '\U00000131',
656
+ '\\<dieresis>' : '\U000000a8',
657
+ '\\<cedilla>' : '\U000000b8',
658
+ '\\<hungarumlaut>' : '\U000002dd',
659
+ '\\<some>' : '\U000003f5',
660
+ '\\<newline>' : '\U000023ce',
661
+ '\\<open>' : '\U00002039',
662
+ '\\<close>' : '\U0000203a',
663
+ '\\<here>' : '\U00002302',
664
+ '\\<^sub>' : '\U000021e9',
665
+ '\\<^sup>' : '\U000021e7',
666
+ '\\<^bold>' : '\U00002759',
667
+ '\\<^bsub>' : '\U000021d8',
668
+ '\\<^esub>' : '\U000021d9',
669
+ '\\<^bsup>' : '\U000021d7',
670
+ '\\<^esup>' : '\U000021d6',
671
+ }
672
+
673
+ lang_map = {'isabelle' : isabelle_symbols, 'latex' : latex_symbols}
674
+
675
+ def __init__(self, **options):
676
+ Filter.__init__(self, **options)
677
+ lang = get_choice_opt(options, 'lang',
678
+ ['isabelle', 'latex'], 'isabelle')
679
+ self.symbols = self.lang_map[lang]
680
+
681
+ def filter(self, lexer, stream):
682
+ for ttype, value in stream:
683
+ if value in self.symbols:
684
+ yield ttype, self.symbols[value]
685
+ else:
686
+ yield ttype, value
687
+
688
+
689
+ class KeywordCaseFilter(Filter):
690
+ """Convert keywords to lowercase or uppercase or capitalize them.
691
+
692
+ This means first letter uppercase, rest lowercase.
693
+
694
+ This can be useful e.g. if you highlight Pascal code and want to adapt the
695
+ code to your styleguide.
696
+
697
+ Options accepted:
698
+
699
+ `case` : string
700
+ The casing to convert keywords to. Must be one of ``'lower'``,
701
+ ``'upper'`` or ``'capitalize'``. The default is ``'lower'``.
702
+ """
703
+
704
+ def __init__(self, **options):
705
+ Filter.__init__(self, **options)
706
+ case = get_choice_opt(options, 'case',
707
+ ['lower', 'upper', 'capitalize'], 'lower')
708
+ self.convert = getattr(str, case)
709
+
710
+ def filter(self, lexer, stream):
711
+ for ttype, value in stream:
712
+ if ttype in Keyword:
713
+ yield ttype, self.convert(value)
714
+ else:
715
+ yield ttype, value
716
+
717
+
718
+ class NameHighlightFilter(Filter):
719
+ """Highlight a normal Name (and Name.*) token with a different token type.
720
+
721
+ Example::
722
+
723
+ filter = NameHighlightFilter(
724
+ names=['foo', 'bar', 'baz'],
725
+ tokentype=Name.Function,
726
+ )
727
+
728
+ This would highlight the names "foo", "bar" and "baz"
729
+ as functions. `Name.Function` is the default token type.
730
+
731
+ Options accepted:
732
+
733
+ `names` : list of strings
734
+ A list of names that should be given the different token type.
735
+ There is no default.
736
+ `tokentype` : TokenType or string
737
+ A token type or a string containing a token type name that is
738
+ used for highlighting the strings in `names`. The default is
739
+ `Name.Function`.
740
+ """
741
+
742
+ def __init__(self, **options):
743
+ Filter.__init__(self, **options)
744
+ self.names = set(get_list_opt(options, 'names', []))
745
+ tokentype = options.get('tokentype')
746
+ if tokentype:
747
+ self.tokentype = string_to_tokentype(tokentype)
748
+ else:
749
+ self.tokentype = Name.Function
750
+
751
+ def filter(self, lexer, stream):
752
+ for ttype, value in stream:
753
+ if ttype in Name and value in self.names:
754
+ yield self.tokentype, value
755
+ else:
756
+ yield ttype, value
757
+
758
+
759
+ class ErrorToken(Exception):
760
+ pass
761
+
762
+
763
+ class RaiseOnErrorTokenFilter(Filter):
764
+ """Raise an exception when the lexer generates an error token.
765
+
766
+ Options accepted:
767
+
768
+ `excclass` : Exception class
769
+ The exception class to raise.
770
+ The default is `pygments.filters.ErrorToken`.
771
+
772
+ .. versionadded:: 0.8
773
+ """
774
+
775
+ def __init__(self, **options):
776
+ Filter.__init__(self, **options)
777
+ self.exception = options.get('excclass', ErrorToken)
778
+ try:
779
+ # issubclass() will raise TypeError if first argument is not a class
780
+ if not issubclass(self.exception, Exception):
781
+ raise TypeError
782
+ except TypeError:
783
+ raise OptionError('excclass option is not an exception class')
784
+
785
+ def filter(self, lexer, stream):
786
+ for ttype, value in stream:
787
+ if ttype is Error:
788
+ raise self.exception(value)
789
+ yield ttype, value
790
+
791
+
792
+ class VisibleWhitespaceFilter(Filter):
793
+ """Convert tabs, newlines and/or spaces to visible characters.
794
+
795
+ Options accepted:
796
+
797
+ `spaces` : string or bool
798
+ If this is a one-character string, spaces will be replaces by this string.
799
+ If it is another true value, spaces will be replaced by ``·`` (unicode
800
+ MIDDLE DOT). If it is a false value, spaces will not be replaced. The
801
+ default is ``False``.
802
+ `tabs` : string or bool
803
+ The same as for `spaces`, but the default replacement character is ``»``
804
+ (unicode RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK). The default value
805
+ is ``False``. Note: this will not work if the `tabsize` option for the
806
+ lexer is nonzero, as tabs will already have been expanded then.
807
+ `tabsize` : int
808
+ If tabs are to be replaced by this filter (see the `tabs` option), this
809
+ is the total number of characters that a tab should be expanded to.
810
+ The default is ``8``.
811
+ `newlines` : string or bool
812
+ The same as for `spaces`, but the default replacement character is ``¶``
813
+ (unicode PILCROW SIGN). The default value is ``False``.
814
+ `wstokentype` : bool
815
+ If true, give whitespace the special `Whitespace` token type. This allows
816
+ styling the visible whitespace differently (e.g. greyed out), but it can
817
+ disrupt background colors. The default is ``True``.
818
+
819
+ .. versionadded:: 0.8
820
+ """
821
+
822
+ def __init__(self, **options):
823
+ Filter.__init__(self, **options)
824
+ for name, default in [('spaces', '·'),
825
+ ('tabs', '»'),
826
+ ('newlines', '¶')]:
827
+ opt = options.get(name, False)
828
+ if isinstance(opt, str) and len(opt) == 1:
829
+ setattr(self, name, opt)
830
+ else:
831
+ setattr(self, name, (opt and default or ''))
832
+ tabsize = get_int_opt(options, 'tabsize', 8)
833
+ if self.tabs:
834
+ self.tabs += ' ' * (tabsize - 1)
835
+ if self.newlines:
836
+ self.newlines += '\n'
837
+ self.wstt = get_bool_opt(options, 'wstokentype', True)
838
+
839
+ def filter(self, lexer, stream):
840
+ if self.wstt:
841
+ spaces = self.spaces or ' '
842
+ tabs = self.tabs or '\t'
843
+ newlines = self.newlines or '\n'
844
+ regex = re.compile(r'\s')
845
+
846
+ def replacefunc(wschar):
847
+ if wschar == ' ':
848
+ return spaces
849
+ elif wschar == '\t':
850
+ return tabs
851
+ elif wschar == '\n':
852
+ return newlines
853
+ return wschar
854
+
855
+ for ttype, value in stream:
856
+ yield from _replace_special(ttype, value, regex, Whitespace,
857
+ replacefunc)
858
+ else:
859
+ spaces, tabs, newlines = self.spaces, self.tabs, self.newlines
860
+ # simpler processing
861
+ for ttype, value in stream:
862
+ if spaces:
863
+ value = value.replace(' ', spaces)
864
+ if tabs:
865
+ value = value.replace('\t', tabs)
866
+ if newlines:
867
+ value = value.replace('\n', newlines)
868
+ yield ttype, value
869
+
870
+
871
+ class GobbleFilter(Filter):
872
+ """Gobble source code lines (eats initial characters).
873
+
874
+ This filter drops the first ``n`` characters off every line of code. This
875
+ may be useful when the source code fed to the lexer is indented by a fixed
876
+ amount of space that isn't desired in the output.
877
+
878
+ Options accepted:
879
+
880
+ `n` : int
881
+ The number of characters to gobble.
882
+
883
+ .. versionadded:: 1.2
884
+ """
885
+ def __init__(self, **options):
886
+ Filter.__init__(self, **options)
887
+ self.n = get_int_opt(options, 'n', 0)
888
+
889
+ def gobble(self, value, left):
890
+ if left < len(value):
891
+ return value[left:], 0
892
+ else:
893
+ return '', left - len(value)
894
+
895
+ def filter(self, lexer, stream):
896
+ n = self.n
897
+ left = n # How many characters left to gobble.
898
+ for ttype, value in stream:
899
+ # Remove ``left`` tokens from first line, ``n`` from all others.
900
+ parts = value.split('\n')
901
+ (parts[0], left) = self.gobble(parts[0], left)
902
+ for i in range(1, len(parts)):
903
+ (parts[i], left) = self.gobble(parts[i], n)
904
+ value = '\n'.join(parts)
905
+
906
+ if value != '':
907
+ yield ttype, value
908
+
909
+
910
+ class TokenMergeFilter(Filter):
911
+ """Merge consecutive tokens with the same token type in the output stream.
912
+
913
+ .. versionadded:: 1.2
914
+ """
915
+ def __init__(self, **options):
916
+ Filter.__init__(self, **options)
917
+
918
+ def filter(self, lexer, stream):
919
+ current_type = None
920
+ current_value = None
921
+ for ttype, value in stream:
922
+ if ttype is current_type:
923
+ current_value += value
924
+ else:
925
+ if current_type is not None:
926
+ yield current_type, current_value
927
+ current_type = ttype
928
+ current_value = value
929
+ if current_type is not None:
930
+ yield current_type, current_value
931
+
932
+
933
+ FILTERS = {
934
+ 'codetagify': CodeTagFilter,
935
+ 'keywordcase': KeywordCaseFilter,
936
+ 'highlight': NameHighlightFilter,
937
+ 'raiseonerror': RaiseOnErrorTokenFilter,
938
+ 'whitespace': VisibleWhitespaceFilter,
939
+ 'gobble': GobbleFilter,
940
+ 'tokenmerge': TokenMergeFilter,
941
+ 'symbols': SymbolFilter,
942
+ }
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/formatter.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.formatter
3
+ ~~~~~~~~~~~~~~~~~~
4
+
5
+ Base formatter class.
6
+
7
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import codecs
12
+
13
+ from pip._vendor.pygments.util import get_bool_opt
14
+ from pip._vendor.pygments.styles import get_style_by_name
15
+
16
+ __all__ = ['Formatter']
17
+
18
+
19
+ def _lookup_style(style):
20
+ if isinstance(style, str):
21
+ return get_style_by_name(style)
22
+ return style
23
+
24
+
25
+ class Formatter:
26
+ """
27
+ Converts a token stream to text.
28
+
29
+ Formatters should have attributes to help selecting them. These
30
+ are similar to the corresponding :class:`~pygments.lexer.Lexer`
31
+ attributes.
32
+
33
+ .. autoattribute:: name
34
+ :no-value:
35
+
36
+ .. autoattribute:: aliases
37
+ :no-value:
38
+
39
+ .. autoattribute:: filenames
40
+ :no-value:
41
+
42
+ You can pass options as keyword arguments to the constructor.
43
+ All formatters accept these basic options:
44
+
45
+ ``style``
46
+ The style to use, can be a string or a Style subclass
47
+ (default: "default"). Not used by e.g. the
48
+ TerminalFormatter.
49
+ ``full``
50
+ Tells the formatter to output a "full" document, i.e.
51
+ a complete self-contained document. This doesn't have
52
+ any effect for some formatters (default: false).
53
+ ``title``
54
+ If ``full`` is true, the title that should be used to
55
+ caption the document (default: '').
56
+ ``encoding``
57
+ If given, must be an encoding name. This will be used to
58
+ convert the Unicode token strings to byte strings in the
59
+ output. If it is "" or None, Unicode strings will be written
60
+ to the output file, which most file-like objects do not
61
+ support (default: None).
62
+ ``outencoding``
63
+ Overrides ``encoding`` if given.
64
+
65
+ """
66
+
67
+ #: Full name for the formatter, in human-readable form.
68
+ name = None
69
+
70
+ #: A list of short, unique identifiers that can be used to lookup
71
+ #: the formatter from a list, e.g. using :func:`.get_formatter_by_name()`.
72
+ aliases = []
73
+
74
+ #: A list of fnmatch patterns that match filenames for which this
75
+ #: formatter can produce output. The patterns in this list should be unique
76
+ #: among all formatters.
77
+ filenames = []
78
+
79
+ #: If True, this formatter outputs Unicode strings when no encoding
80
+ #: option is given.
81
+ unicodeoutput = True
82
+
83
+ def __init__(self, **options):
84
+ """
85
+ As with lexers, this constructor takes arbitrary optional arguments,
86
+ and if you override it, you should first process your own options, then
87
+ call the base class implementation.
88
+ """
89
+ self.style = _lookup_style(options.get('style', 'default'))
90
+ self.full = get_bool_opt(options, 'full', False)
91
+ self.title = options.get('title', '')
92
+ self.encoding = options.get('encoding', None) or None
93
+ if self.encoding in ('guess', 'chardet'):
94
+ # can happen for e.g. pygmentize -O encoding=guess
95
+ self.encoding = 'utf-8'
96
+ self.encoding = options.get('outencoding') or self.encoding
97
+ self.options = options
98
+
99
+ def get_style_defs(self, arg=''):
100
+ """
101
+ This method must return statements or declarations suitable to define
102
+ the current style for subsequent highlighted text (e.g. CSS classes
103
+ in the `HTMLFormatter`).
104
+
105
+ The optional argument `arg` can be used to modify the generation and
106
+ is formatter dependent (it is standardized because it can be given on
107
+ the command line).
108
+
109
+ This method is called by the ``-S`` :doc:`command-line option <cmdline>`,
110
+ the `arg` is then given by the ``-a`` option.
111
+ """
112
+ return ''
113
+
114
+ def format(self, tokensource, outfile):
115
+ """
116
+ This method must format the tokens from the `tokensource` iterable and
117
+ write the formatted version to the file object `outfile`.
118
+
119
+ Formatter options can control how exactly the tokens are converted.
120
+ """
121
+ if self.encoding:
122
+ # wrap the outfile in a StreamWriter
123
+ outfile = codecs.lookup(self.encoding)[3](outfile)
124
+ return self.format_unencoded(tokensource, outfile)
125
+
126
+ # Allow writing Formatter[str] or Formatter[bytes]. That's equivalent to
127
+ # Formatter. This helps when using third-party type stubs from typeshed.
128
+ def __class_getitem__(cls, name):
129
+ return cls
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/formatters/__init__.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.formatters
3
+ ~~~~~~~~~~~~~~~~~~~
4
+
5
+ Pygments formatters.
6
+
7
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import re
12
+ import sys
13
+ import types
14
+ import fnmatch
15
+ from os.path import basename
16
+
17
+ from pip._vendor.pygments.formatters._mapping import FORMATTERS
18
+ from pip._vendor.pygments.plugin import find_plugin_formatters
19
+ from pip._vendor.pygments.util import ClassNotFound
20
+
21
+ __all__ = ['get_formatter_by_name', 'get_formatter_for_filename',
22
+ 'get_all_formatters', 'load_formatter_from_file'] + list(FORMATTERS)
23
+
24
+ _formatter_cache = {} # classes by name
25
+ _pattern_cache = {}
26
+
27
+
28
+ def _fn_matches(fn, glob):
29
+ """Return whether the supplied file name fn matches pattern filename."""
30
+ if glob not in _pattern_cache:
31
+ pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob))
32
+ return pattern.match(fn)
33
+ return _pattern_cache[glob].match(fn)
34
+
35
+
36
+ def _load_formatters(module_name):
37
+ """Load a formatter (and all others in the module too)."""
38
+ mod = __import__(module_name, None, None, ['__all__'])
39
+ for formatter_name in mod.__all__:
40
+ cls = getattr(mod, formatter_name)
41
+ _formatter_cache[cls.name] = cls
42
+
43
+
44
+ def get_all_formatters():
45
+ """Return a generator for all formatter classes."""
46
+ # NB: this returns formatter classes, not info like get_all_lexers().
47
+ for info in FORMATTERS.values():
48
+ if info[1] not in _formatter_cache:
49
+ _load_formatters(info[0])
50
+ yield _formatter_cache[info[1]]
51
+ for _, formatter in find_plugin_formatters():
52
+ yield formatter
53
+
54
+
55
+ def find_formatter_class(alias):
56
+ """Lookup a formatter by alias.
57
+
58
+ Returns None if not found.
59
+ """
60
+ for module_name, name, aliases, _, _ in FORMATTERS.values():
61
+ if alias in aliases:
62
+ if name not in _formatter_cache:
63
+ _load_formatters(module_name)
64
+ return _formatter_cache[name]
65
+ for _, cls in find_plugin_formatters():
66
+ if alias in cls.aliases:
67
+ return cls
68
+
69
+
70
+ def get_formatter_by_name(_alias, **options):
71
+ """
72
+ Return an instance of a :class:`.Formatter` subclass that has `alias` in its
73
+ aliases list. The formatter is given the `options` at its instantiation.
74
+
75
+ Will raise :exc:`pygments.util.ClassNotFound` if no formatter with that
76
+ alias is found.
77
+ """
78
+ cls = find_formatter_class(_alias)
79
+ if cls is None:
80
+ raise ClassNotFound(f"no formatter found for name {_alias!r}")
81
+ return cls(**options)
82
+
83
+
84
+ def load_formatter_from_file(filename, formattername="CustomFormatter", **options):
85
+ """
86
+ Return a `Formatter` subclass instance loaded from the provided file, relative
87
+ to the current directory.
88
+
89
+ The file is expected to contain a Formatter class named ``formattername``
90
+ (by default, CustomFormatter). Users should be very careful with the input, because
91
+ this method is equivalent to running ``eval()`` on the input file. The formatter is
92
+ given the `options` at its instantiation.
93
+
94
+ :exc:`pygments.util.ClassNotFound` is raised if there are any errors loading
95
+ the formatter.
96
+
97
+ .. versionadded:: 2.2
98
+ """
99
+ try:
100
+ # This empty dict will contain the namespace for the exec'd file
101
+ custom_namespace = {}
102
+ with open(filename, 'rb') as f:
103
+ exec(f.read(), custom_namespace)
104
+ # Retrieve the class `formattername` from that namespace
105
+ if formattername not in custom_namespace:
106
+ raise ClassNotFound(f'no valid {formattername} class found in {filename}')
107
+ formatter_class = custom_namespace[formattername]
108
+ # And finally instantiate it with the options
109
+ return formatter_class(**options)
110
+ except OSError as err:
111
+ raise ClassNotFound(f'cannot read {filename}: {err}')
112
+ except ClassNotFound:
113
+ raise
114
+ except Exception as err:
115
+ raise ClassNotFound(f'error when loading custom formatter: {err}')
116
+
117
+
118
+ def get_formatter_for_filename(fn, **options):
119
+ """
120
+ Return a :class:`.Formatter` subclass instance that has a filename pattern
121
+ matching `fn`. The formatter is given the `options` at its instantiation.
122
+
123
+ Will raise :exc:`pygments.util.ClassNotFound` if no formatter for that filename
124
+ is found.
125
+ """
126
+ fn = basename(fn)
127
+ for modname, name, _, filenames, _ in FORMATTERS.values():
128
+ for filename in filenames:
129
+ if _fn_matches(fn, filename):
130
+ if name not in _formatter_cache:
131
+ _load_formatters(modname)
132
+ return _formatter_cache[name](**options)
133
+ for _name, cls in find_plugin_formatters():
134
+ for filename in cls.filenames:
135
+ if _fn_matches(fn, filename):
136
+ return cls(**options)
137
+ raise ClassNotFound(f"no formatter found for file name {fn!r}")
138
+
139
+
140
+ class _automodule(types.ModuleType):
141
+ """Automatically import formatters."""
142
+
143
+ def __getattr__(self, name):
144
+ info = FORMATTERS.get(name)
145
+ if info:
146
+ _load_formatters(info[0])
147
+ cls = _formatter_cache[info[1]]
148
+ setattr(self, name, cls)
149
+ return cls
150
+ raise AttributeError(name)
151
+
152
+
153
+ oldmod = sys.modules[__name__]
154
+ newmod = _automodule(__name__)
155
+ newmod.__dict__.update(oldmod.__dict__)
156
+ sys.modules[__name__] = newmod
157
+ del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/formatters/_mapping.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Automatically generated by scripts/gen_mapfiles.py.
2
+ # DO NOT EDIT BY HAND; run `tox -e mapfiles` instead.
3
+
4
+ FORMATTERS = {
5
+ 'BBCodeFormatter': ('pygments.formatters.bbcode', 'BBCode', ('bbcode', 'bb'), (), 'Format tokens with BBcodes. These formatting codes are used by many bulletin boards, so you can highlight your sourcecode with pygments before posting it there.'),
6
+ 'BmpImageFormatter': ('pygments.formatters.img', 'img_bmp', ('bmp', 'bitmap'), ('*.bmp',), 'Create a bitmap image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'),
7
+ 'GifImageFormatter': ('pygments.formatters.img', 'img_gif', ('gif',), ('*.gif',), 'Create a GIF image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'),
8
+ 'GroffFormatter': ('pygments.formatters.groff', 'groff', ('groff', 'troff', 'roff'), (), 'Format tokens with groff escapes to change their color and font style.'),
9
+ 'HtmlFormatter': ('pygments.formatters.html', 'HTML', ('html',), ('*.html', '*.htm'), "Format tokens as HTML 4 ``<span>`` tags. By default, the content is enclosed in a ``<pre>`` tag, itself wrapped in a ``<div>`` tag (but see the `nowrap` option). The ``<div>``'s CSS class can be set by the `cssclass` option."),
10
+ 'IRCFormatter': ('pygments.formatters.irc', 'IRC', ('irc', 'IRC'), (), 'Format tokens with IRC color sequences'),
11
+ 'ImageFormatter': ('pygments.formatters.img', 'img', ('img', 'IMG', 'png'), ('*.png',), 'Create a PNG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'),
12
+ 'JpgImageFormatter': ('pygments.formatters.img', 'img_jpg', ('jpg', 'jpeg'), ('*.jpg',), 'Create a JPEG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'),
13
+ 'LatexFormatter': ('pygments.formatters.latex', 'LaTeX', ('latex', 'tex'), ('*.tex',), 'Format tokens as LaTeX code. This needs the `fancyvrb` and `color` standard packages.'),
14
+ 'NullFormatter': ('pygments.formatters.other', 'Text only', ('text', 'null'), ('*.txt',), 'Output the text unchanged without any formatting.'),
15
+ 'PangoMarkupFormatter': ('pygments.formatters.pangomarkup', 'Pango Markup', ('pango', 'pangomarkup'), (), 'Format tokens as Pango Markup code. It can then be rendered to an SVG.'),
16
+ 'RawTokenFormatter': ('pygments.formatters.other', 'Raw tokens', ('raw', 'tokens'), ('*.raw',), 'Format tokens as a raw representation for storing token streams.'),
17
+ 'RtfFormatter': ('pygments.formatters.rtf', 'RTF', ('rtf',), ('*.rtf',), 'Format tokens as RTF markup. This formatter automatically outputs full RTF documents with color information and other useful stuff. Perfect for Copy and Paste into Microsoft(R) Word(R) documents.'),
18
+ 'SvgFormatter': ('pygments.formatters.svg', 'SVG', ('svg',), ('*.svg',), 'Format tokens as an SVG graphics file. This formatter is still experimental. Each line of code is a ``<text>`` element with explicit ``x`` and ``y`` coordinates containing ``<tspan>`` elements with the individual token styles.'),
19
+ 'Terminal256Formatter': ('pygments.formatters.terminal256', 'Terminal256', ('terminal256', 'console256', '256'), (), 'Format tokens with ANSI color sequences, for output in a 256-color terminal or console. Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'),
20
+ 'TerminalFormatter': ('pygments.formatters.terminal', 'Terminal', ('terminal', 'console'), (), 'Format tokens with ANSI color sequences, for output in a text console. Color sequences are terminated at newlines, so that paging the output works correctly.'),
21
+ 'TerminalTrueColorFormatter': ('pygments.formatters.terminal256', 'TerminalTrueColor', ('terminal16m', 'console16m', '16m'), (), 'Format tokens with ANSI color sequences, for output in a true-color terminal or console. Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'),
22
+ 'TestcaseFormatter': ('pygments.formatters.other', 'Testcase', ('testcase',), (), 'Format tokens as appropriate for a new testcase.'),
23
+ }
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/lexer.py ADDED
@@ -0,0 +1,965 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.lexer
3
+ ~~~~~~~~~~~~~~
4
+
5
+ Base lexer classes.
6
+
7
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import re
12
+ import sys
13
+ import time
14
+
15
+ from pip._vendor.pygments.filter import apply_filters, Filter
16
+ from pip._vendor.pygments.filters import get_filter_by_name
17
+ from pip._vendor.pygments.token import Error, Text, Other, Whitespace, _TokenType
18
+ from pip._vendor.pygments.util import get_bool_opt, get_int_opt, get_list_opt, \
19
+ make_analysator, Future, guess_decode
20
+ from pip._vendor.pygments.regexopt import regex_opt
21
+
22
+ __all__ = ['Lexer', 'RegexLexer', 'ExtendedRegexLexer', 'DelegatingLexer',
23
+ 'LexerContext', 'include', 'inherit', 'bygroups', 'using', 'this',
24
+ 'default', 'words', 'line_re']
25
+
26
+ line_re = re.compile('.*?\n')
27
+
28
+ _encoding_map = [(b'\xef\xbb\xbf', 'utf-8'),
29
+ (b'\xff\xfe\0\0', 'utf-32'),
30
+ (b'\0\0\xfe\xff', 'utf-32be'),
31
+ (b'\xff\xfe', 'utf-16'),
32
+ (b'\xfe\xff', 'utf-16be')]
33
+
34
+ _default_analyse = staticmethod(lambda x: 0.0)
35
+
36
+
37
+ class LexerMeta(type):
38
+ """
39
+ This metaclass automagically converts ``analyse_text`` methods into
40
+ static methods which always return float values.
41
+ """
42
+
43
+ def __new__(mcs, name, bases, d):
44
+ if 'analyse_text' in d:
45
+ d['analyse_text'] = make_analysator(d['analyse_text'])
46
+ return type.__new__(mcs, name, bases, d)
47
+
48
+
49
+ class Lexer(metaclass=LexerMeta):
50
+ """
51
+ Lexer for a specific language.
52
+
53
+ See also :doc:`lexerdevelopment`, a high-level guide to writing
54
+ lexers.
55
+
56
+ Lexer classes have attributes used for choosing the most appropriate
57
+ lexer based on various criteria.
58
+
59
+ .. autoattribute:: name
60
+ :no-value:
61
+ .. autoattribute:: aliases
62
+ :no-value:
63
+ .. autoattribute:: filenames
64
+ :no-value:
65
+ .. autoattribute:: alias_filenames
66
+ .. autoattribute:: mimetypes
67
+ :no-value:
68
+ .. autoattribute:: priority
69
+
70
+ Lexers included in Pygments should have two additional attributes:
71
+
72
+ .. autoattribute:: url
73
+ :no-value:
74
+ .. autoattribute:: version_added
75
+ :no-value:
76
+
77
+ Lexers included in Pygments may have additional attributes:
78
+
79
+ .. autoattribute:: _example
80
+ :no-value:
81
+
82
+ You can pass options to the constructor. The basic options recognized
83
+ by all lexers and processed by the base `Lexer` class are:
84
+
85
+ ``stripnl``
86
+ Strip leading and trailing newlines from the input (default: True).
87
+ ``stripall``
88
+ Strip all leading and trailing whitespace from the input
89
+ (default: False).
90
+ ``ensurenl``
91
+ Make sure that the input ends with a newline (default: True). This
92
+ is required for some lexers that consume input linewise.
93
+
94
+ .. versionadded:: 1.3
95
+
96
+ ``tabsize``
97
+ If given and greater than 0, expand tabs in the input (default: 0).
98
+ ``encoding``
99
+ If given, must be an encoding name. This encoding will be used to
100
+ convert the input string to Unicode, if it is not already a Unicode
101
+ string (default: ``'guess'``, which uses a simple UTF-8 / Locale /
102
+ Latin1 detection. Can also be ``'chardet'`` to use the chardet
103
+ library, if it is installed.
104
+ ``inencoding``
105
+ Overrides the ``encoding`` if given.
106
+ """
107
+
108
+ #: Full name of the lexer, in human-readable form
109
+ name = None
110
+
111
+ #: A list of short, unique identifiers that can be used to look
112
+ #: up the lexer from a list, e.g., using `get_lexer_by_name()`.
113
+ aliases = []
114
+
115
+ #: A list of `fnmatch` patterns that match filenames which contain
116
+ #: content for this lexer. The patterns in this list should be unique among
117
+ #: all lexers.
118
+ filenames = []
119
+
120
+ #: A list of `fnmatch` patterns that match filenames which may or may not
121
+ #: contain content for this lexer. This list is used by the
122
+ #: :func:`.guess_lexer_for_filename()` function, to determine which lexers
123
+ #: are then included in guessing the correct one. That means that
124
+ #: e.g. every lexer for HTML and a template language should include
125
+ #: ``\*.html`` in this list.
126
+ alias_filenames = []
127
+
128
+ #: A list of MIME types for content that can be lexed with this lexer.
129
+ mimetypes = []
130
+
131
+ #: Priority, should multiple lexers match and no content is provided
132
+ priority = 0
133
+
134
+ #: URL of the language specification/definition. Used in the Pygments
135
+ #: documentation. Set to an empty string to disable.
136
+ url = None
137
+
138
+ #: Version of Pygments in which the lexer was added.
139
+ version_added = None
140
+
141
+ #: Example file name. Relative to the ``tests/examplefiles`` directory.
142
+ #: This is used by the documentation generator to show an example.
143
+ _example = None
144
+
145
+ def __init__(self, **options):
146
+ """
147
+ This constructor takes arbitrary options as keyword arguments.
148
+ Every subclass must first process its own options and then call
149
+ the `Lexer` constructor, since it processes the basic
150
+ options like `stripnl`.
151
+
152
+ An example looks like this:
153
+
154
+ .. sourcecode:: python
155
+
156
+ def __init__(self, **options):
157
+ self.compress = options.get('compress', '')
158
+ Lexer.__init__(self, **options)
159
+
160
+ As these options must all be specifiable as strings (due to the
161
+ command line usage), there are various utility functions
162
+ available to help with that, see `Utilities`_.
163
+ """
164
+ self.options = options
165
+ self.stripnl = get_bool_opt(options, 'stripnl', True)
166
+ self.stripall = get_bool_opt(options, 'stripall', False)
167
+ self.ensurenl = get_bool_opt(options, 'ensurenl', True)
168
+ self.tabsize = get_int_opt(options, 'tabsize', 0)
169
+ self.encoding = options.get('encoding', 'guess')
170
+ self.encoding = options.get('inencoding') or self.encoding
171
+ self.filters = []
172
+ for filter_ in get_list_opt(options, 'filters', ()):
173
+ self.add_filter(filter_)
174
+
175
+ def __repr__(self):
176
+ if self.options:
177
+ return f'<pygments.lexers.{self.__class__.__name__} with {self.options!r}>'
178
+ else:
179
+ return f'<pygments.lexers.{self.__class__.__name__}>'
180
+
181
+ def add_filter(self, filter_, **options):
182
+ """
183
+ Add a new stream filter to this lexer.
184
+ """
185
+ if not isinstance(filter_, Filter):
186
+ filter_ = get_filter_by_name(filter_, **options)
187
+ self.filters.append(filter_)
188
+
189
+ def analyse_text(text):
190
+ """
191
+ A static method which is called for lexer guessing.
192
+
193
+ It should analyse the text and return a float in the range
194
+ from ``0.0`` to ``1.0``. If it returns ``0.0``, the lexer
195
+ will not be selected as the most probable one, if it returns
196
+ ``1.0``, it will be selected immediately. This is used by
197
+ `guess_lexer`.
198
+
199
+ The `LexerMeta` metaclass automatically wraps this function so
200
+ that it works like a static method (no ``self`` or ``cls``
201
+ parameter) and the return value is automatically converted to
202
+ `float`. If the return value is an object that is boolean `False`
203
+ it's the same as if the return values was ``0.0``.
204
+ """
205
+
206
+ def _preprocess_lexer_input(self, text):
207
+ """Apply preprocessing such as decoding the input, removing BOM and normalizing newlines."""
208
+
209
+ if not isinstance(text, str):
210
+ if self.encoding == 'guess':
211
+ text, _ = guess_decode(text)
212
+ elif self.encoding == 'chardet':
213
+ try:
214
+ # pip vendoring note: this code is not reachable by pip,
215
+ # removed import of chardet to make it clear.
216
+ raise ImportError('chardet is not vendored by pip')
217
+ except ImportError as e:
218
+ raise ImportError('To enable chardet encoding guessing, '
219
+ 'please install the chardet library '
220
+ 'from http://chardet.feedparser.org/') from e
221
+ # check for BOM first
222
+ decoded = None
223
+ for bom, encoding in _encoding_map:
224
+ if text.startswith(bom):
225
+ decoded = text[len(bom):].decode(encoding, 'replace')
226
+ break
227
+ # no BOM found, so use chardet
228
+ if decoded is None:
229
+ enc = chardet.detect(text[:1024]) # Guess using first 1KB
230
+ decoded = text.decode(enc.get('encoding') or 'utf-8',
231
+ 'replace')
232
+ text = decoded
233
+ else:
234
+ text = text.decode(self.encoding)
235
+ if text.startswith('\ufeff'):
236
+ text = text[len('\ufeff'):]
237
+ else:
238
+ if text.startswith('\ufeff'):
239
+ text = text[len('\ufeff'):]
240
+
241
+ # text now *is* a unicode string
242
+ text = text.replace('\r\n', '\n')
243
+ text = text.replace('\r', '\n')
244
+ if self.stripall:
245
+ text = text.strip()
246
+ elif self.stripnl:
247
+ text = text.strip('\n')
248
+ if self.tabsize > 0:
249
+ text = text.expandtabs(self.tabsize)
250
+ if self.ensurenl and not text.endswith('\n'):
251
+ text += '\n'
252
+
253
+ return text
254
+
255
+ def get_tokens(self, text, unfiltered=False):
256
+ """
257
+ This method is the basic interface of a lexer. It is called by
258
+ the `highlight()` function. It must process the text and return an
259
+ iterable of ``(tokentype, value)`` pairs from `text`.
260
+
261
+ Normally, you don't need to override this method. The default
262
+ implementation processes the options recognized by all lexers
263
+ (`stripnl`, `stripall` and so on), and then yields all tokens
264
+ from `get_tokens_unprocessed()`, with the ``index`` dropped.
265
+
266
+ If `unfiltered` is set to `True`, the filtering mechanism is
267
+ bypassed even if filters are defined.
268
+ """
269
+ text = self._preprocess_lexer_input(text)
270
+
271
+ def streamer():
272
+ for _, t, v in self.get_tokens_unprocessed(text):
273
+ yield t, v
274
+ stream = streamer()
275
+ if not unfiltered:
276
+ stream = apply_filters(stream, self.filters, self)
277
+ return stream
278
+
279
+ def get_tokens_unprocessed(self, text):
280
+ """
281
+ This method should process the text and return an iterable of
282
+ ``(index, tokentype, value)`` tuples where ``index`` is the starting
283
+ position of the token within the input text.
284
+
285
+ It must be overridden by subclasses. It is recommended to
286
+ implement it as a generator to maximize effectiveness.
287
+ """
288
+ raise NotImplementedError
289
+
290
+
291
+ class DelegatingLexer(Lexer):
292
+ """
293
+ This lexer takes two lexer as arguments. A root lexer and
294
+ a language lexer. First everything is scanned using the language
295
+ lexer, afterwards all ``Other`` tokens are lexed using the root
296
+ lexer.
297
+
298
+ The lexers from the ``template`` lexer package use this base lexer.
299
+ """
300
+
301
+ def __init__(self, _root_lexer, _language_lexer, _needle=Other, **options):
302
+ self.root_lexer = _root_lexer(**options)
303
+ self.language_lexer = _language_lexer(**options)
304
+ self.needle = _needle
305
+ Lexer.__init__(self, **options)
306
+
307
+ def get_tokens_unprocessed(self, text):
308
+ buffered = ''
309
+ insertions = []
310
+ lng_buffer = []
311
+ for i, t, v in self.language_lexer.get_tokens_unprocessed(text):
312
+ if t is self.needle:
313
+ if lng_buffer:
314
+ insertions.append((len(buffered), lng_buffer))
315
+ lng_buffer = []
316
+ buffered += v
317
+ else:
318
+ lng_buffer.append((i, t, v))
319
+ if lng_buffer:
320
+ insertions.append((len(buffered), lng_buffer))
321
+ return do_insertions(insertions,
322
+ self.root_lexer.get_tokens_unprocessed(buffered))
323
+
324
+
325
+ # ------------------------------------------------------------------------------
326
+ # RegexLexer and ExtendedRegexLexer
327
+ #
328
+
329
+
330
+ class include(str): # pylint: disable=invalid-name
331
+ """
332
+ Indicates that a state should include rules from another state.
333
+ """
334
+ pass
335
+
336
+
337
+ class _inherit:
338
+ """
339
+ Indicates the a state should inherit from its superclass.
340
+ """
341
+ def __repr__(self):
342
+ return 'inherit'
343
+
344
+ inherit = _inherit() # pylint: disable=invalid-name
345
+
346
+
347
+ class combined(tuple): # pylint: disable=invalid-name
348
+ """
349
+ Indicates a state combined from multiple states.
350
+ """
351
+
352
+ def __new__(cls, *args):
353
+ return tuple.__new__(cls, args)
354
+
355
+ def __init__(self, *args):
356
+ # tuple.__init__ doesn't do anything
357
+ pass
358
+
359
+
360
+ class _PseudoMatch:
361
+ """
362
+ A pseudo match object constructed from a string.
363
+ """
364
+
365
+ def __init__(self, start, text):
366
+ self._text = text
367
+ self._start = start
368
+
369
+ def start(self, arg=None):
370
+ return self._start
371
+
372
+ def end(self, arg=None):
373
+ return self._start + len(self._text)
374
+
375
+ def group(self, arg=None):
376
+ if arg:
377
+ raise IndexError('No such group')
378
+ return self._text
379
+
380
+ def groups(self):
381
+ return (self._text,)
382
+
383
+ def groupdict(self):
384
+ return {}
385
+
386
+
387
+ def bygroups(*args):
388
+ """
389
+ Callback that yields multiple actions for each group in the match.
390
+ """
391
+ def callback(lexer, match, ctx=None):
392
+ for i, action in enumerate(args):
393
+ if action is None:
394
+ continue
395
+ elif type(action) is _TokenType:
396
+ data = match.group(i + 1)
397
+ if data:
398
+ yield match.start(i + 1), action, data
399
+ else:
400
+ data = match.group(i + 1)
401
+ if data is not None:
402
+ if ctx:
403
+ ctx.pos = match.start(i + 1)
404
+ for item in action(lexer,
405
+ _PseudoMatch(match.start(i + 1), data), ctx):
406
+ if item:
407
+ yield item
408
+ if ctx:
409
+ ctx.pos = match.end()
410
+ return callback
411
+
412
+
413
+ class _This:
414
+ """
415
+ Special singleton used for indicating the caller class.
416
+ Used by ``using``.
417
+ """
418
+
419
+ this = _This()
420
+
421
+
422
+ def using(_other, **kwargs):
423
+ """
424
+ Callback that processes the match with a different lexer.
425
+
426
+ The keyword arguments are forwarded to the lexer, except `state` which
427
+ is handled separately.
428
+
429
+ `state` specifies the state that the new lexer will start in, and can
430
+ be an enumerable such as ('root', 'inline', 'string') or a simple
431
+ string which is assumed to be on top of the root state.
432
+
433
+ Note: For that to work, `_other` must not be an `ExtendedRegexLexer`.
434
+ """
435
+ gt_kwargs = {}
436
+ if 'state' in kwargs:
437
+ s = kwargs.pop('state')
438
+ if isinstance(s, (list, tuple)):
439
+ gt_kwargs['stack'] = s
440
+ else:
441
+ gt_kwargs['stack'] = ('root', s)
442
+
443
+ if _other is this:
444
+ def callback(lexer, match, ctx=None):
445
+ # if keyword arguments are given the callback
446
+ # function has to create a new lexer instance
447
+ if kwargs:
448
+ # XXX: cache that somehow
449
+ d = dict(lexer.options)
450
+ d.update(kwargs)
451
+ lx = lexer.__class__(**d)
452
+ else:
453
+ lx = lexer
454
+ s = match.start()
455
+ for i, t, v in lx.get_tokens_unprocessed(match.group(), **gt_kwargs):
456
+ yield i + s, t, v
457
+ if ctx:
458
+ ctx.pos = match.end()
459
+ else:
460
+ def callback(lexer, match, ctx=None):
461
+ # XXX: cache that somehow
462
+ d = dict(lexer.options)
463
+ d.update(kwargs)
464
+ lx = _other(**d)
465
+
466
+ s = match.start()
467
+ for i, t, v in lx.get_tokens_unprocessed(match.group(), **gt_kwargs):
468
+ yield i + s, t, v
469
+ if ctx:
470
+ ctx.pos = match.end()
471
+ return callback
472
+
473
+
474
+ class default:
475
+ """
476
+ Indicates a state or state action (e.g. #pop) to apply.
477
+ For example default('#pop') is equivalent to ('', Token, '#pop')
478
+ Note that state tuples may be used as well.
479
+
480
+ .. versionadded:: 2.0
481
+ """
482
+ def __init__(self, state):
483
+ self.state = state
484
+
485
+
486
+ class words(Future):
487
+ """
488
+ Indicates a list of literal words that is transformed into an optimized
489
+ regex that matches any of the words.
490
+
491
+ .. versionadded:: 2.0
492
+ """
493
+ def __init__(self, words, prefix='', suffix=''):
494
+ self.words = words
495
+ self.prefix = prefix
496
+ self.suffix = suffix
497
+
498
+ def get(self):
499
+ return regex_opt(self.words, prefix=self.prefix, suffix=self.suffix)
500
+
501
+
502
+ class RegexLexerMeta(LexerMeta):
503
+ """
504
+ Metaclass for RegexLexer, creates the self._tokens attribute from
505
+ self.tokens on the first instantiation.
506
+ """
507
+
508
+ def _process_regex(cls, regex, rflags, state):
509
+ """Preprocess the regular expression component of a token definition."""
510
+ if isinstance(regex, Future):
511
+ regex = regex.get()
512
+ return re.compile(regex, rflags).match
513
+
514
+ def _process_token(cls, token):
515
+ """Preprocess the token component of a token definition."""
516
+ assert type(token) is _TokenType or callable(token), \
517
+ f'token type must be simple type or callable, not {token!r}'
518
+ return token
519
+
520
+ def _process_new_state(cls, new_state, unprocessed, processed):
521
+ """Preprocess the state transition action of a token definition."""
522
+ if isinstance(new_state, str):
523
+ # an existing state
524
+ if new_state == '#pop':
525
+ return -1
526
+ elif new_state in unprocessed:
527
+ return (new_state,)
528
+ elif new_state == '#push':
529
+ return new_state
530
+ elif new_state[:5] == '#pop:':
531
+ return -int(new_state[5:])
532
+ else:
533
+ assert False, f'unknown new state {new_state!r}'
534
+ elif isinstance(new_state, combined):
535
+ # combine a new state from existing ones
536
+ tmp_state = '_tmp_%d' % cls._tmpname
537
+ cls._tmpname += 1
538
+ itokens = []
539
+ for istate in new_state:
540
+ assert istate != new_state, f'circular state ref {istate!r}'
541
+ itokens.extend(cls._process_state(unprocessed,
542
+ processed, istate))
543
+ processed[tmp_state] = itokens
544
+ return (tmp_state,)
545
+ elif isinstance(new_state, tuple):
546
+ # push more than one state
547
+ for istate in new_state:
548
+ assert (istate in unprocessed or
549
+ istate in ('#pop', '#push')), \
550
+ 'unknown new state ' + istate
551
+ return new_state
552
+ else:
553
+ assert False, f'unknown new state def {new_state!r}'
554
+
555
+ def _process_state(cls, unprocessed, processed, state):
556
+ """Preprocess a single state definition."""
557
+ assert isinstance(state, str), f"wrong state name {state!r}"
558
+ assert state[0] != '#', f"invalid state name {state!r}"
559
+ if state in processed:
560
+ return processed[state]
561
+ tokens = processed[state] = []
562
+ rflags = cls.flags
563
+ for tdef in unprocessed[state]:
564
+ if isinstance(tdef, include):
565
+ # it's a state reference
566
+ assert tdef != state, f"circular state reference {state!r}"
567
+ tokens.extend(cls._process_state(unprocessed, processed,
568
+ str(tdef)))
569
+ continue
570
+ if isinstance(tdef, _inherit):
571
+ # should be processed already, but may not in the case of:
572
+ # 1. the state has no counterpart in any parent
573
+ # 2. the state includes more than one 'inherit'
574
+ continue
575
+ if isinstance(tdef, default):
576
+ new_state = cls._process_new_state(tdef.state, unprocessed, processed)
577
+ tokens.append((re.compile('').match, None, new_state))
578
+ continue
579
+
580
+ assert type(tdef) is tuple, f"wrong rule def {tdef!r}"
581
+
582
+ try:
583
+ rex = cls._process_regex(tdef[0], rflags, state)
584
+ except Exception as err:
585
+ raise ValueError(f"uncompilable regex {tdef[0]!r} in state {state!r} of {cls!r}: {err}") from err
586
+
587
+ token = cls._process_token(tdef[1])
588
+
589
+ if len(tdef) == 2:
590
+ new_state = None
591
+ else:
592
+ new_state = cls._process_new_state(tdef[2],
593
+ unprocessed, processed)
594
+
595
+ tokens.append((rex, token, new_state))
596
+ return tokens
597
+
598
+ def process_tokendef(cls, name, tokendefs=None):
599
+ """Preprocess a dictionary of token definitions."""
600
+ processed = cls._all_tokens[name] = {}
601
+ tokendefs = tokendefs or cls.tokens[name]
602
+ for state in list(tokendefs):
603
+ cls._process_state(tokendefs, processed, state)
604
+ return processed
605
+
606
+ def get_tokendefs(cls):
607
+ """
608
+ Merge tokens from superclasses in MRO order, returning a single tokendef
609
+ dictionary.
610
+
611
+ Any state that is not defined by a subclass will be inherited
612
+ automatically. States that *are* defined by subclasses will, by
613
+ default, override that state in the superclass. If a subclass wishes to
614
+ inherit definitions from a superclass, it can use the special value
615
+ "inherit", which will cause the superclass' state definition to be
616
+ included at that point in the state.
617
+ """
618
+ tokens = {}
619
+ inheritable = {}
620
+ for c in cls.__mro__:
621
+ toks = c.__dict__.get('tokens', {})
622
+
623
+ for state, items in toks.items():
624
+ curitems = tokens.get(state)
625
+ if curitems is None:
626
+ # N.b. because this is assigned by reference, sufficiently
627
+ # deep hierarchies are processed incrementally (e.g. for
628
+ # A(B), B(C), C(RegexLexer), B will be premodified so X(B)
629
+ # will not see any inherits in B).
630
+ tokens[state] = items
631
+ try:
632
+ inherit_ndx = items.index(inherit)
633
+ except ValueError:
634
+ continue
635
+ inheritable[state] = inherit_ndx
636
+ continue
637
+
638
+ inherit_ndx = inheritable.pop(state, None)
639
+ if inherit_ndx is None:
640
+ continue
641
+
642
+ # Replace the "inherit" value with the items
643
+ curitems[inherit_ndx:inherit_ndx+1] = items
644
+ try:
645
+ # N.b. this is the index in items (that is, the superclass
646
+ # copy), so offset required when storing below.
647
+ new_inh_ndx = items.index(inherit)
648
+ except ValueError:
649
+ pass
650
+ else:
651
+ inheritable[state] = inherit_ndx + new_inh_ndx
652
+
653
+ return tokens
654
+
655
+ def __call__(cls, *args, **kwds):
656
+ """Instantiate cls after preprocessing its token definitions."""
657
+ if '_tokens' not in cls.__dict__:
658
+ cls._all_tokens = {}
659
+ cls._tmpname = 0
660
+ if hasattr(cls, 'token_variants') and cls.token_variants:
661
+ # don't process yet
662
+ pass
663
+ else:
664
+ cls._tokens = cls.process_tokendef('', cls.get_tokendefs())
665
+
666
+ return type.__call__(cls, *args, **kwds)
667
+
668
+
669
+ class RegexLexer(Lexer, metaclass=RegexLexerMeta):
670
+ """
671
+ Base for simple stateful regular expression-based lexers.
672
+ Simplifies the lexing process so that you need only
673
+ provide a list of states and regular expressions.
674
+ """
675
+
676
+ #: Flags for compiling the regular expressions.
677
+ #: Defaults to MULTILINE.
678
+ flags = re.MULTILINE
679
+
680
+ #: At all time there is a stack of states. Initially, the stack contains
681
+ #: a single state 'root'. The top of the stack is called "the current state".
682
+ #:
683
+ #: Dict of ``{'state': [(regex, tokentype, new_state), ...], ...}``
684
+ #:
685
+ #: ``new_state`` can be omitted to signify no state transition.
686
+ #: If ``new_state`` is a string, it is pushed on the stack. This ensure
687
+ #: the new current state is ``new_state``.
688
+ #: If ``new_state`` is a tuple of strings, all of those strings are pushed
689
+ #: on the stack and the current state will be the last element of the list.
690
+ #: ``new_state`` can also be ``combined('state1', 'state2', ...)``
691
+ #: to signify a new, anonymous state combined from the rules of two
692
+ #: or more existing ones.
693
+ #: Furthermore, it can be '#pop' to signify going back one step in
694
+ #: the state stack, or '#push' to push the current state on the stack
695
+ #: again. Note that if you push while in a combined state, the combined
696
+ #: state itself is pushed, and not only the state in which the rule is
697
+ #: defined.
698
+ #:
699
+ #: The tuple can also be replaced with ``include('state')``, in which
700
+ #: case the rules from the state named by the string are included in the
701
+ #: current one.
702
+ tokens = {}
703
+
704
+ def get_tokens_unprocessed(self, text, stack=('root',)):
705
+ """
706
+ Split ``text`` into (tokentype, text) pairs.
707
+
708
+ ``stack`` is the initial stack (default: ``['root']``)
709
+ """
710
+ pos = 0
711
+ tokendefs = self._tokens
712
+ statestack = list(stack)
713
+ statetokens = tokendefs[statestack[-1]]
714
+ while 1:
715
+ for rexmatch, action, new_state in statetokens:
716
+ m = rexmatch(text, pos)
717
+ if m:
718
+ if action is not None:
719
+ if type(action) is _TokenType:
720
+ yield pos, action, m.group()
721
+ else:
722
+ yield from action(self, m)
723
+ pos = m.end()
724
+ if new_state is not None:
725
+ # state transition
726
+ if isinstance(new_state, tuple):
727
+ for state in new_state:
728
+ if state == '#pop':
729
+ if len(statestack) > 1:
730
+ statestack.pop()
731
+ elif state == '#push':
732
+ statestack.append(statestack[-1])
733
+ else:
734
+ statestack.append(state)
735
+ elif isinstance(new_state, int):
736
+ # pop, but keep at least one state on the stack
737
+ # (random code leading to unexpected pops should
738
+ # not allow exceptions)
739
+ if abs(new_state) >= len(statestack):
740
+ del statestack[1:]
741
+ else:
742
+ del statestack[new_state:]
743
+ elif new_state == '#push':
744
+ statestack.append(statestack[-1])
745
+ else:
746
+ assert False, f"wrong state def: {new_state!r}"
747
+ statetokens = tokendefs[statestack[-1]]
748
+ break
749
+ else:
750
+ # We are here only if all state tokens have been considered
751
+ # and there was not a match on any of them.
752
+ try:
753
+ if text[pos] == '\n':
754
+ # at EOL, reset state to "root"
755
+ statestack = ['root']
756
+ statetokens = tokendefs['root']
757
+ yield pos, Whitespace, '\n'
758
+ pos += 1
759
+ continue
760
+ yield pos, Error, text[pos]
761
+ pos += 1
762
+ except IndexError:
763
+ break
764
+
765
+
766
+ class LexerContext:
767
+ """
768
+ A helper object that holds lexer position data.
769
+ """
770
+
771
+ def __init__(self, text, pos, stack=None, end=None):
772
+ self.text = text
773
+ self.pos = pos
774
+ self.end = end or len(text) # end=0 not supported ;-)
775
+ self.stack = stack or ['root']
776
+
777
+ def __repr__(self):
778
+ return f'LexerContext({self.text!r}, {self.pos!r}, {self.stack!r})'
779
+
780
+
781
+ class ExtendedRegexLexer(RegexLexer):
782
+ """
783
+ A RegexLexer that uses a context object to store its state.
784
+ """
785
+
786
+ def get_tokens_unprocessed(self, text=None, context=None):
787
+ """
788
+ Split ``text`` into (tokentype, text) pairs.
789
+ If ``context`` is given, use this lexer context instead.
790
+ """
791
+ tokendefs = self._tokens
792
+ if not context:
793
+ ctx = LexerContext(text, 0)
794
+ statetokens = tokendefs['root']
795
+ else:
796
+ ctx = context
797
+ statetokens = tokendefs[ctx.stack[-1]]
798
+ text = ctx.text
799
+ while 1:
800
+ for rexmatch, action, new_state in statetokens:
801
+ m = rexmatch(text, ctx.pos, ctx.end)
802
+ if m:
803
+ if action is not None:
804
+ if type(action) is _TokenType:
805
+ yield ctx.pos, action, m.group()
806
+ ctx.pos = m.end()
807
+ else:
808
+ yield from action(self, m, ctx)
809
+ if not new_state:
810
+ # altered the state stack?
811
+ statetokens = tokendefs[ctx.stack[-1]]
812
+ # CAUTION: callback must set ctx.pos!
813
+ if new_state is not None:
814
+ # state transition
815
+ if isinstance(new_state, tuple):
816
+ for state in new_state:
817
+ if state == '#pop':
818
+ if len(ctx.stack) > 1:
819
+ ctx.stack.pop()
820
+ elif state == '#push':
821
+ ctx.stack.append(ctx.stack[-1])
822
+ else:
823
+ ctx.stack.append(state)
824
+ elif isinstance(new_state, int):
825
+ # see RegexLexer for why this check is made
826
+ if abs(new_state) >= len(ctx.stack):
827
+ del ctx.stack[1:]
828
+ else:
829
+ del ctx.stack[new_state:]
830
+ elif new_state == '#push':
831
+ ctx.stack.append(ctx.stack[-1])
832
+ else:
833
+ assert False, f"wrong state def: {new_state!r}"
834
+ statetokens = tokendefs[ctx.stack[-1]]
835
+ break
836
+ else:
837
+ try:
838
+ if ctx.pos >= ctx.end:
839
+ break
840
+ if text[ctx.pos] == '\n':
841
+ # at EOL, reset state to "root"
842
+ ctx.stack = ['root']
843
+ statetokens = tokendefs['root']
844
+ yield ctx.pos, Text, '\n'
845
+ ctx.pos += 1
846
+ continue
847
+ yield ctx.pos, Error, text[ctx.pos]
848
+ ctx.pos += 1
849
+ except IndexError:
850
+ break
851
+
852
+
853
+ def do_insertions(insertions, tokens):
854
+ """
855
+ Helper for lexers which must combine the results of several
856
+ sublexers.
857
+
858
+ ``insertions`` is a list of ``(index, itokens)`` pairs.
859
+ Each ``itokens`` iterable should be inserted at position
860
+ ``index`` into the token stream given by the ``tokens``
861
+ argument.
862
+
863
+ The result is a combined token stream.
864
+
865
+ TODO: clean up the code here.
866
+ """
867
+ insertions = iter(insertions)
868
+ try:
869
+ index, itokens = next(insertions)
870
+ except StopIteration:
871
+ # no insertions
872
+ yield from tokens
873
+ return
874
+
875
+ realpos = None
876
+ insleft = True
877
+
878
+ # iterate over the token stream where we want to insert
879
+ # the tokens from the insertion list.
880
+ for i, t, v in tokens:
881
+ # first iteration. store the position of first item
882
+ if realpos is None:
883
+ realpos = i
884
+ oldi = 0
885
+ while insleft and i + len(v) >= index:
886
+ tmpval = v[oldi:index - i]
887
+ if tmpval:
888
+ yield realpos, t, tmpval
889
+ realpos += len(tmpval)
890
+ for it_index, it_token, it_value in itokens:
891
+ yield realpos, it_token, it_value
892
+ realpos += len(it_value)
893
+ oldi = index - i
894
+ try:
895
+ index, itokens = next(insertions)
896
+ except StopIteration:
897
+ insleft = False
898
+ break # not strictly necessary
899
+ if oldi < len(v):
900
+ yield realpos, t, v[oldi:]
901
+ realpos += len(v) - oldi
902
+
903
+ # leftover tokens
904
+ while insleft:
905
+ # no normal tokens, set realpos to zero
906
+ realpos = realpos or 0
907
+ for p, t, v in itokens:
908
+ yield realpos, t, v
909
+ realpos += len(v)
910
+ try:
911
+ index, itokens = next(insertions)
912
+ except StopIteration:
913
+ insleft = False
914
+ break # not strictly necessary
915
+
916
+
917
+ class ProfilingRegexLexerMeta(RegexLexerMeta):
918
+ """Metaclass for ProfilingRegexLexer, collects regex timing info."""
919
+
920
+ def _process_regex(cls, regex, rflags, state):
921
+ if isinstance(regex, words):
922
+ rex = regex_opt(regex.words, prefix=regex.prefix,
923
+ suffix=regex.suffix)
924
+ else:
925
+ rex = regex
926
+ compiled = re.compile(rex, rflags)
927
+
928
+ def match_func(text, pos, endpos=sys.maxsize):
929
+ info = cls._prof_data[-1].setdefault((state, rex), [0, 0.0])
930
+ t0 = time.time()
931
+ res = compiled.match(text, pos, endpos)
932
+ t1 = time.time()
933
+ info[0] += 1
934
+ info[1] += t1 - t0
935
+ return res
936
+ return match_func
937
+
938
+
939
+ class ProfilingRegexLexer(RegexLexer, metaclass=ProfilingRegexLexerMeta):
940
+ """Drop-in replacement for RegexLexer that does profiling of its regexes."""
941
+
942
+ _prof_data = []
943
+ _prof_sort_index = 4 # defaults to time per call
944
+
945
+ def get_tokens_unprocessed(self, text, stack=('root',)):
946
+ # this needs to be a stack, since using(this) will produce nested calls
947
+ self.__class__._prof_data.append({})
948
+ yield from RegexLexer.get_tokens_unprocessed(self, text, stack)
949
+ rawdata = self.__class__._prof_data.pop()
950
+ data = sorted(((s, repr(r).strip('u\'').replace('\\\\', '\\')[:65],
951
+ n, 1000 * t, 1000 * t / n)
952
+ for ((s, r), (n, t)) in rawdata.items()),
953
+ key=lambda x: x[self._prof_sort_index],
954
+ reverse=True)
955
+ sum_total = sum(x[3] for x in data)
956
+
957
+ print()
958
+ print('Profiling result for %s lexing %d chars in %.3f ms' %
959
+ (self.__class__.__name__, len(text), sum_total))
960
+ print('=' * 110)
961
+ print('%-20s %-64s ncalls tottime percall' % ('state', 'regex'))
962
+ print('-' * 110)
963
+ for d in data:
964
+ print('%-20s %-65s %5d %8.4f %8.4f' % d)
965
+ print('=' * 110)
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/lexers/__init__.py ADDED
@@ -0,0 +1,362 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.lexers
3
+ ~~~~~~~~~~~~~~~
4
+
5
+ Pygments lexers.
6
+
7
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import re
12
+ import sys
13
+ import types
14
+ import fnmatch
15
+ from os.path import basename
16
+
17
+ from pip._vendor.pygments.lexers._mapping import LEXERS
18
+ from pip._vendor.pygments.modeline import get_filetype_from_buffer
19
+ from pip._vendor.pygments.plugin import find_plugin_lexers
20
+ from pip._vendor.pygments.util import ClassNotFound, guess_decode
21
+
22
+ COMPAT = {
23
+ 'Python3Lexer': 'PythonLexer',
24
+ 'Python3TracebackLexer': 'PythonTracebackLexer',
25
+ 'LeanLexer': 'Lean3Lexer',
26
+ }
27
+
28
+ __all__ = ['get_lexer_by_name', 'get_lexer_for_filename', 'find_lexer_class',
29
+ 'guess_lexer', 'load_lexer_from_file'] + list(LEXERS) + list(COMPAT)
30
+
31
+ _lexer_cache = {}
32
+ _pattern_cache = {}
33
+
34
+
35
+ def _fn_matches(fn, glob):
36
+ """Return whether the supplied file name fn matches pattern filename."""
37
+ if glob not in _pattern_cache:
38
+ pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob))
39
+ return pattern.match(fn)
40
+ return _pattern_cache[glob].match(fn)
41
+
42
+
43
+ def _load_lexers(module_name):
44
+ """Load a lexer (and all others in the module too)."""
45
+ mod = __import__(module_name, None, None, ['__all__'])
46
+ for lexer_name in mod.__all__:
47
+ cls = getattr(mod, lexer_name)
48
+ _lexer_cache[cls.name] = cls
49
+
50
+
51
+ def get_all_lexers(plugins=True):
52
+ """Return a generator of tuples in the form ``(name, aliases,
53
+ filenames, mimetypes)`` of all know lexers.
54
+
55
+ If *plugins* is true (the default), plugin lexers supplied by entrypoints
56
+ are also returned. Otherwise, only builtin ones are considered.
57
+ """
58
+ for item in LEXERS.values():
59
+ yield item[1:]
60
+ if plugins:
61
+ for lexer in find_plugin_lexers():
62
+ yield lexer.name, lexer.aliases, lexer.filenames, lexer.mimetypes
63
+
64
+
65
+ def find_lexer_class(name):
66
+ """
67
+ Return the `Lexer` subclass that with the *name* attribute as given by
68
+ the *name* argument.
69
+ """
70
+ if name in _lexer_cache:
71
+ return _lexer_cache[name]
72
+ # lookup builtin lexers
73
+ for module_name, lname, aliases, _, _ in LEXERS.values():
74
+ if name == lname:
75
+ _load_lexers(module_name)
76
+ return _lexer_cache[name]
77
+ # continue with lexers from setuptools entrypoints
78
+ for cls in find_plugin_lexers():
79
+ if cls.name == name:
80
+ return cls
81
+
82
+
83
+ def find_lexer_class_by_name(_alias):
84
+ """
85
+ Return the `Lexer` subclass that has `alias` in its aliases list, without
86
+ instantiating it.
87
+
88
+ Like `get_lexer_by_name`, but does not instantiate the class.
89
+
90
+ Will raise :exc:`pygments.util.ClassNotFound` if no lexer with that alias is
91
+ found.
92
+
93
+ .. versionadded:: 2.2
94
+ """
95
+ if not _alias:
96
+ raise ClassNotFound(f'no lexer for alias {_alias!r} found')
97
+ # lookup builtin lexers
98
+ for module_name, name, aliases, _, _ in LEXERS.values():
99
+ if _alias.lower() in aliases:
100
+ if name not in _lexer_cache:
101
+ _load_lexers(module_name)
102
+ return _lexer_cache[name]
103
+ # continue with lexers from setuptools entrypoints
104
+ for cls in find_plugin_lexers():
105
+ if _alias.lower() in cls.aliases:
106
+ return cls
107
+ raise ClassNotFound(f'no lexer for alias {_alias!r} found')
108
+
109
+
110
+ def get_lexer_by_name(_alias, **options):
111
+ """
112
+ Return an instance of a `Lexer` subclass that has `alias` in its
113
+ aliases list. The lexer is given the `options` at its
114
+ instantiation.
115
+
116
+ Will raise :exc:`pygments.util.ClassNotFound` if no lexer with that alias is
117
+ found.
118
+ """
119
+ if not _alias:
120
+ raise ClassNotFound(f'no lexer for alias {_alias!r} found')
121
+
122
+ # lookup builtin lexers
123
+ for module_name, name, aliases, _, _ in LEXERS.values():
124
+ if _alias.lower() in aliases:
125
+ if name not in _lexer_cache:
126
+ _load_lexers(module_name)
127
+ return _lexer_cache[name](**options)
128
+ # continue with lexers from setuptools entrypoints
129
+ for cls in find_plugin_lexers():
130
+ if _alias.lower() in cls.aliases:
131
+ return cls(**options)
132
+ raise ClassNotFound(f'no lexer for alias {_alias!r} found')
133
+
134
+
135
+ def load_lexer_from_file(filename, lexername="CustomLexer", **options):
136
+ """Load a lexer from a file.
137
+
138
+ This method expects a file located relative to the current working
139
+ directory, which contains a Lexer class. By default, it expects the
140
+ Lexer to be name CustomLexer; you can specify your own class name
141
+ as the second argument to this function.
142
+
143
+ Users should be very careful with the input, because this method
144
+ is equivalent to running eval on the input file.
145
+
146
+ Raises ClassNotFound if there are any problems importing the Lexer.
147
+
148
+ .. versionadded:: 2.2
149
+ """
150
+ try:
151
+ # This empty dict will contain the namespace for the exec'd file
152
+ custom_namespace = {}
153
+ with open(filename, 'rb') as f:
154
+ exec(f.read(), custom_namespace)
155
+ # Retrieve the class `lexername` from that namespace
156
+ if lexername not in custom_namespace:
157
+ raise ClassNotFound(f'no valid {lexername} class found in {filename}')
158
+ lexer_class = custom_namespace[lexername]
159
+ # And finally instantiate it with the options
160
+ return lexer_class(**options)
161
+ except OSError as err:
162
+ raise ClassNotFound(f'cannot read {filename}: {err}')
163
+ except ClassNotFound:
164
+ raise
165
+ except Exception as err:
166
+ raise ClassNotFound(f'error when loading custom lexer: {err}')
167
+
168
+
169
+ def find_lexer_class_for_filename(_fn, code=None):
170
+ """Get a lexer for a filename.
171
+
172
+ If multiple lexers match the filename pattern, use ``analyse_text()`` to
173
+ figure out which one is more appropriate.
174
+
175
+ Returns None if not found.
176
+ """
177
+ matches = []
178
+ fn = basename(_fn)
179
+ for modname, name, _, filenames, _ in LEXERS.values():
180
+ for filename in filenames:
181
+ if _fn_matches(fn, filename):
182
+ if name not in _lexer_cache:
183
+ _load_lexers(modname)
184
+ matches.append((_lexer_cache[name], filename))
185
+ for cls in find_plugin_lexers():
186
+ for filename in cls.filenames:
187
+ if _fn_matches(fn, filename):
188
+ matches.append((cls, filename))
189
+
190
+ if isinstance(code, bytes):
191
+ # decode it, since all analyse_text functions expect unicode
192
+ code = guess_decode(code)
193
+
194
+ def get_rating(info):
195
+ cls, filename = info
196
+ # explicit patterns get a bonus
197
+ bonus = '*' not in filename and 0.5 or 0
198
+ # The class _always_ defines analyse_text because it's included in
199
+ # the Lexer class. The default implementation returns None which
200
+ # gets turned into 0.0. Run scripts/detect_missing_analyse_text.py
201
+ # to find lexers which need it overridden.
202
+ if code:
203
+ return cls.analyse_text(code) + bonus, cls.__name__
204
+ return cls.priority + bonus, cls.__name__
205
+
206
+ if matches:
207
+ matches.sort(key=get_rating)
208
+ # print "Possible lexers, after sort:", matches
209
+ return matches[-1][0]
210
+
211
+
212
+ def get_lexer_for_filename(_fn, code=None, **options):
213
+ """Get a lexer for a filename.
214
+
215
+ Return a `Lexer` subclass instance that has a filename pattern
216
+ matching `fn`. The lexer is given the `options` at its
217
+ instantiation.
218
+
219
+ Raise :exc:`pygments.util.ClassNotFound` if no lexer for that filename
220
+ is found.
221
+
222
+ If multiple lexers match the filename pattern, use their ``analyse_text()``
223
+ methods to figure out which one is more appropriate.
224
+ """
225
+ res = find_lexer_class_for_filename(_fn, code)
226
+ if not res:
227
+ raise ClassNotFound(f'no lexer for filename {_fn!r} found')
228
+ return res(**options)
229
+
230
+
231
+ def get_lexer_for_mimetype(_mime, **options):
232
+ """
233
+ Return a `Lexer` subclass instance that has `mime` in its mimetype
234
+ list. The lexer is given the `options` at its instantiation.
235
+
236
+ Will raise :exc:`pygments.util.ClassNotFound` if not lexer for that mimetype
237
+ is found.
238
+ """
239
+ for modname, name, _, _, mimetypes in LEXERS.values():
240
+ if _mime in mimetypes:
241
+ if name not in _lexer_cache:
242
+ _load_lexers(modname)
243
+ return _lexer_cache[name](**options)
244
+ for cls in find_plugin_lexers():
245
+ if _mime in cls.mimetypes:
246
+ return cls(**options)
247
+ raise ClassNotFound(f'no lexer for mimetype {_mime!r} found')
248
+
249
+
250
+ def _iter_lexerclasses(plugins=True):
251
+ """Return an iterator over all lexer classes."""
252
+ for key in sorted(LEXERS):
253
+ module_name, name = LEXERS[key][:2]
254
+ if name not in _lexer_cache:
255
+ _load_lexers(module_name)
256
+ yield _lexer_cache[name]
257
+ if plugins:
258
+ yield from find_plugin_lexers()
259
+
260
+
261
+ def guess_lexer_for_filename(_fn, _text, **options):
262
+ """
263
+ As :func:`guess_lexer()`, but only lexers which have a pattern in `filenames`
264
+ or `alias_filenames` that matches `filename` are taken into consideration.
265
+
266
+ :exc:`pygments.util.ClassNotFound` is raised if no lexer thinks it can
267
+ handle the content.
268
+ """
269
+ fn = basename(_fn)
270
+ primary = {}
271
+ matching_lexers = set()
272
+ for lexer in _iter_lexerclasses():
273
+ for filename in lexer.filenames:
274
+ if _fn_matches(fn, filename):
275
+ matching_lexers.add(lexer)
276
+ primary[lexer] = True
277
+ for filename in lexer.alias_filenames:
278
+ if _fn_matches(fn, filename):
279
+ matching_lexers.add(lexer)
280
+ primary[lexer] = False
281
+ if not matching_lexers:
282
+ raise ClassNotFound(f'no lexer for filename {fn!r} found')
283
+ if len(matching_lexers) == 1:
284
+ return matching_lexers.pop()(**options)
285
+ result = []
286
+ for lexer in matching_lexers:
287
+ rv = lexer.analyse_text(_text)
288
+ if rv == 1.0:
289
+ return lexer(**options)
290
+ result.append((rv, lexer))
291
+
292
+ def type_sort(t):
293
+ # sort by:
294
+ # - analyse score
295
+ # - is primary filename pattern?
296
+ # - priority
297
+ # - last resort: class name
298
+ return (t[0], primary[t[1]], t[1].priority, t[1].__name__)
299
+ result.sort(key=type_sort)
300
+
301
+ return result[-1][1](**options)
302
+
303
+
304
+ def guess_lexer(_text, **options):
305
+ """
306
+ Return a `Lexer` subclass instance that's guessed from the text in
307
+ `text`. For that, the :meth:`.analyse_text()` method of every known lexer
308
+ class is called with the text as argument, and the lexer which returned the
309
+ highest value will be instantiated and returned.
310
+
311
+ :exc:`pygments.util.ClassNotFound` is raised if no lexer thinks it can
312
+ handle the content.
313
+ """
314
+
315
+ if not isinstance(_text, str):
316
+ inencoding = options.get('inencoding', options.get('encoding'))
317
+ if inencoding:
318
+ _text = _text.decode(inencoding or 'utf8')
319
+ else:
320
+ _text, _ = guess_decode(_text)
321
+
322
+ # try to get a vim modeline first
323
+ ft = get_filetype_from_buffer(_text)
324
+
325
+ if ft is not None:
326
+ try:
327
+ return get_lexer_by_name(ft, **options)
328
+ except ClassNotFound:
329
+ pass
330
+
331
+ best_lexer = [0.0, None]
332
+ for lexer in _iter_lexerclasses():
333
+ rv = lexer.analyse_text(_text)
334
+ if rv == 1.0:
335
+ return lexer(**options)
336
+ if rv > best_lexer[0]:
337
+ best_lexer[:] = (rv, lexer)
338
+ if not best_lexer[0] or best_lexer[1] is None:
339
+ raise ClassNotFound('no lexer matching the text found')
340
+ return best_lexer[1](**options)
341
+
342
+
343
+ class _automodule(types.ModuleType):
344
+ """Automatically import lexers."""
345
+
346
+ def __getattr__(self, name):
347
+ info = LEXERS.get(name)
348
+ if info:
349
+ _load_lexers(info[0])
350
+ cls = _lexer_cache[info[1]]
351
+ setattr(self, name, cls)
352
+ return cls
353
+ if name in COMPAT:
354
+ return getattr(self, COMPAT[name])
355
+ raise AttributeError(name)
356
+
357
+
358
+ oldmod = sys.modules[__name__]
359
+ newmod = _automodule(__name__)
360
+ newmod.__dict__.update(oldmod.__dict__)
361
+ sys.modules[__name__] = newmod
362
+ del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/lexers/_mapping.py ADDED
@@ -0,0 +1,603 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Automatically generated by scripts/gen_mapfiles.py.
2
+ # DO NOT EDIT BY HAND; run `tox -e mapfiles` instead.
3
+
4
+ LEXERS = {
5
+ 'ABAPLexer': ('pip._vendor.pygments.lexers.business', 'ABAP', ('abap',), ('*.abap', '*.ABAP'), ('text/x-abap',)),
6
+ 'AMDGPULexer': ('pip._vendor.pygments.lexers.amdgpu', 'AMDGPU', ('amdgpu',), ('*.isa',), ()),
7
+ 'APLLexer': ('pip._vendor.pygments.lexers.apl', 'APL', ('apl',), ('*.apl', '*.aplf', '*.aplo', '*.apln', '*.aplc', '*.apli', '*.dyalog'), ()),
8
+ 'AbnfLexer': ('pip._vendor.pygments.lexers.grammar_notation', 'ABNF', ('abnf',), ('*.abnf',), ('text/x-abnf',)),
9
+ 'ActionScript3Lexer': ('pip._vendor.pygments.lexers.actionscript', 'ActionScript 3', ('actionscript3', 'as3'), ('*.as',), ('application/x-actionscript3', 'text/x-actionscript3', 'text/actionscript3')),
10
+ 'ActionScriptLexer': ('pip._vendor.pygments.lexers.actionscript', 'ActionScript', ('actionscript', 'as'), ('*.as',), ('application/x-actionscript', 'text/x-actionscript', 'text/actionscript')),
11
+ 'AdaLexer': ('pip._vendor.pygments.lexers.ada', 'Ada', ('ada', 'ada95', 'ada2005'), ('*.adb', '*.ads', '*.ada'), ('text/x-ada',)),
12
+ 'AdlLexer': ('pip._vendor.pygments.lexers.archetype', 'ADL', ('adl',), ('*.adl', '*.adls', '*.adlf', '*.adlx'), ()),
13
+ 'AgdaLexer': ('pip._vendor.pygments.lexers.haskell', 'Agda', ('agda',), ('*.agda',), ('text/x-agda',)),
14
+ 'AheuiLexer': ('pip._vendor.pygments.lexers.esoteric', 'Aheui', ('aheui',), ('*.aheui',), ()),
15
+ 'AlloyLexer': ('pip._vendor.pygments.lexers.dsls', 'Alloy', ('alloy',), ('*.als',), ('text/x-alloy',)),
16
+ 'AmbientTalkLexer': ('pip._vendor.pygments.lexers.ambient', 'AmbientTalk', ('ambienttalk', 'ambienttalk/2', 'at'), ('*.at',), ('text/x-ambienttalk',)),
17
+ 'AmplLexer': ('pip._vendor.pygments.lexers.ampl', 'Ampl', ('ampl',), ('*.run',), ()),
18
+ 'Angular2HtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML + Angular2', ('html+ng2',), ('*.ng2',), ()),
19
+ 'Angular2Lexer': ('pip._vendor.pygments.lexers.templates', 'Angular2', ('ng2',), (), ()),
20
+ 'AntlrActionScriptLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With ActionScript Target', ('antlr-actionscript', 'antlr-as'), ('*.G', '*.g'), ()),
21
+ 'AntlrCSharpLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With C# Target', ('antlr-csharp', 'antlr-c#'), ('*.G', '*.g'), ()),
22
+ 'AntlrCppLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With CPP Target', ('antlr-cpp',), ('*.G', '*.g'), ()),
23
+ 'AntlrJavaLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With Java Target', ('antlr-java',), ('*.G', '*.g'), ()),
24
+ 'AntlrLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR', ('antlr',), (), ()),
25
+ 'AntlrObjectiveCLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With ObjectiveC Target', ('antlr-objc',), ('*.G', '*.g'), ()),
26
+ 'AntlrPerlLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With Perl Target', ('antlr-perl',), ('*.G', '*.g'), ()),
27
+ 'AntlrPythonLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With Python Target', ('antlr-python',), ('*.G', '*.g'), ()),
28
+ 'AntlrRubyLexer': ('pip._vendor.pygments.lexers.parsers', 'ANTLR With Ruby Target', ('antlr-ruby', 'antlr-rb'), ('*.G', '*.g'), ()),
29
+ 'ApacheConfLexer': ('pip._vendor.pygments.lexers.configs', 'ApacheConf', ('apacheconf', 'aconf', 'apache'), ('.htaccess', 'apache.conf', 'apache2.conf'), ('text/x-apacheconf',)),
30
+ 'AppleScriptLexer': ('pip._vendor.pygments.lexers.scripting', 'AppleScript', ('applescript',), ('*.applescript',), ()),
31
+ 'ArduinoLexer': ('pip._vendor.pygments.lexers.c_like', 'Arduino', ('arduino',), ('*.ino',), ('text/x-arduino',)),
32
+ 'ArrowLexer': ('pip._vendor.pygments.lexers.arrow', 'Arrow', ('arrow',), ('*.arw',), ()),
33
+ 'ArturoLexer': ('pip._vendor.pygments.lexers.arturo', 'Arturo', ('arturo', 'art'), ('*.art',), ()),
34
+ 'AscLexer': ('pip._vendor.pygments.lexers.asc', 'ASCII armored', ('asc', 'pem'), ('*.asc', '*.pem', 'id_dsa', 'id_ecdsa', 'id_ecdsa_sk', 'id_ed25519', 'id_ed25519_sk', 'id_rsa'), ('application/pgp-keys', 'application/pgp-encrypted', 'application/pgp-signature', 'application/pem-certificate-chain')),
35
+ 'Asn1Lexer': ('pip._vendor.pygments.lexers.asn1', 'ASN.1', ('asn1',), ('*.asn1',), ()),
36
+ 'AspectJLexer': ('pip._vendor.pygments.lexers.jvm', 'AspectJ', ('aspectj',), ('*.aj',), ('text/x-aspectj',)),
37
+ 'AsymptoteLexer': ('pip._vendor.pygments.lexers.graphics', 'Asymptote', ('asymptote', 'asy'), ('*.asy',), ('text/x-asymptote',)),
38
+ 'AugeasLexer': ('pip._vendor.pygments.lexers.configs', 'Augeas', ('augeas',), ('*.aug',), ()),
39
+ 'AutoItLexer': ('pip._vendor.pygments.lexers.automation', 'AutoIt', ('autoit',), ('*.au3',), ('text/x-autoit',)),
40
+ 'AutohotkeyLexer': ('pip._vendor.pygments.lexers.automation', 'autohotkey', ('autohotkey', 'ahk'), ('*.ahk', '*.ahkl'), ('text/x-autohotkey',)),
41
+ 'AwkLexer': ('pip._vendor.pygments.lexers.textedit', 'Awk', ('awk', 'gawk', 'mawk', 'nawk'), ('*.awk',), ('application/x-awk',)),
42
+ 'BBCBasicLexer': ('pip._vendor.pygments.lexers.basic', 'BBC Basic', ('bbcbasic',), ('*.bbc',), ()),
43
+ 'BBCodeLexer': ('pip._vendor.pygments.lexers.markup', 'BBCode', ('bbcode',), (), ('text/x-bbcode',)),
44
+ 'BCLexer': ('pip._vendor.pygments.lexers.algebra', 'BC', ('bc',), ('*.bc',), ()),
45
+ 'BQNLexer': ('pip._vendor.pygments.lexers.bqn', 'BQN', ('bqn',), ('*.bqn',), ()),
46
+ 'BSTLexer': ('pip._vendor.pygments.lexers.bibtex', 'BST', ('bst', 'bst-pybtex'), ('*.bst',), ()),
47
+ 'BareLexer': ('pip._vendor.pygments.lexers.bare', 'BARE', ('bare',), ('*.bare',), ()),
48
+ 'BaseMakefileLexer': ('pip._vendor.pygments.lexers.make', 'Base Makefile', ('basemake',), (), ()),
49
+ 'BashLexer': ('pip._vendor.pygments.lexers.shell', 'Bash', ('bash', 'sh', 'ksh', 'zsh', 'shell', 'openrc'), ('*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass', '*.exheres-0', '*.exlib', '*.zsh', '.bashrc', 'bashrc', '.bash_*', 'bash_*', 'zshrc', '.zshrc', '.kshrc', 'kshrc', 'PKGBUILD'), ('application/x-sh', 'application/x-shellscript', 'text/x-shellscript')),
50
+ 'BashSessionLexer': ('pip._vendor.pygments.lexers.shell', 'Bash Session', ('console', 'shell-session'), ('*.sh-session', '*.shell-session'), ('application/x-shell-session', 'application/x-sh-session')),
51
+ 'BatchLexer': ('pip._vendor.pygments.lexers.shell', 'Batchfile', ('batch', 'bat', 'dosbatch', 'winbatch'), ('*.bat', '*.cmd'), ('application/x-dos-batch',)),
52
+ 'BddLexer': ('pip._vendor.pygments.lexers.bdd', 'Bdd', ('bdd',), ('*.feature',), ('text/x-bdd',)),
53
+ 'BefungeLexer': ('pip._vendor.pygments.lexers.esoteric', 'Befunge', ('befunge',), ('*.befunge',), ('application/x-befunge',)),
54
+ 'BerryLexer': ('pip._vendor.pygments.lexers.berry', 'Berry', ('berry', 'be'), ('*.be',), ('text/x-berry', 'application/x-berry')),
55
+ 'BibTeXLexer': ('pip._vendor.pygments.lexers.bibtex', 'BibTeX', ('bibtex', 'bib'), ('*.bib',), ('text/x-bibtex',)),
56
+ 'BlitzBasicLexer': ('pip._vendor.pygments.lexers.basic', 'BlitzBasic', ('blitzbasic', 'b3d', 'bplus'), ('*.bb', '*.decls'), ('text/x-bb',)),
57
+ 'BlitzMaxLexer': ('pip._vendor.pygments.lexers.basic', 'BlitzMax', ('blitzmax', 'bmax'), ('*.bmx',), ('text/x-bmx',)),
58
+ 'BlueprintLexer': ('pip._vendor.pygments.lexers.blueprint', 'Blueprint', ('blueprint',), ('*.blp',), ('text/x-blueprint',)),
59
+ 'BnfLexer': ('pip._vendor.pygments.lexers.grammar_notation', 'BNF', ('bnf',), ('*.bnf',), ('text/x-bnf',)),
60
+ 'BoaLexer': ('pip._vendor.pygments.lexers.boa', 'Boa', ('boa',), ('*.boa',), ()),
61
+ 'BooLexer': ('pip._vendor.pygments.lexers.dotnet', 'Boo', ('boo',), ('*.boo',), ('text/x-boo',)),
62
+ 'BoogieLexer': ('pip._vendor.pygments.lexers.verification', 'Boogie', ('boogie',), ('*.bpl',), ()),
63
+ 'BrainfuckLexer': ('pip._vendor.pygments.lexers.esoteric', 'Brainfuck', ('brainfuck', 'bf'), ('*.bf', '*.b'), ('application/x-brainfuck',)),
64
+ 'BugsLexer': ('pip._vendor.pygments.lexers.modeling', 'BUGS', ('bugs', 'winbugs', 'openbugs'), ('*.bug',), ()),
65
+ 'CAmkESLexer': ('pip._vendor.pygments.lexers.esoteric', 'CAmkES', ('camkes', 'idl4'), ('*.camkes', '*.idl4'), ()),
66
+ 'CLexer': ('pip._vendor.pygments.lexers.c_cpp', 'C', ('c',), ('*.c', '*.h', '*.idc', '*.x[bp]m'), ('text/x-chdr', 'text/x-csrc', 'image/x-xbitmap', 'image/x-xpixmap')),
67
+ 'CMakeLexer': ('pip._vendor.pygments.lexers.make', 'CMake', ('cmake',), ('*.cmake', 'CMakeLists.txt'), ('text/x-cmake',)),
68
+ 'CObjdumpLexer': ('pip._vendor.pygments.lexers.asm', 'c-objdump', ('c-objdump',), ('*.c-objdump',), ('text/x-c-objdump',)),
69
+ 'CPSALexer': ('pip._vendor.pygments.lexers.lisp', 'CPSA', ('cpsa',), ('*.cpsa',), ()),
70
+ 'CSSUL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'CSS+UL4', ('css+ul4',), ('*.cssul4',), ()),
71
+ 'CSharpAspxLexer': ('pip._vendor.pygments.lexers.dotnet', 'aspx-cs', ('aspx-cs',), ('*.aspx', '*.asax', '*.ascx', '*.ashx', '*.asmx', '*.axd'), ()),
72
+ 'CSharpLexer': ('pip._vendor.pygments.lexers.dotnet', 'C#', ('csharp', 'c#', 'cs'), ('*.cs',), ('text/x-csharp',)),
73
+ 'Ca65Lexer': ('pip._vendor.pygments.lexers.asm', 'ca65 assembler', ('ca65',), ('*.s',), ()),
74
+ 'CadlLexer': ('pip._vendor.pygments.lexers.archetype', 'cADL', ('cadl',), ('*.cadl',), ()),
75
+ 'CapDLLexer': ('pip._vendor.pygments.lexers.esoteric', 'CapDL', ('capdl',), ('*.cdl',), ()),
76
+ 'CapnProtoLexer': ('pip._vendor.pygments.lexers.capnproto', "Cap'n Proto", ('capnp',), ('*.capnp',), ()),
77
+ 'CarbonLexer': ('pip._vendor.pygments.lexers.carbon', 'Carbon', ('carbon',), ('*.carbon',), ('text/x-carbon',)),
78
+ 'CbmBasicV2Lexer': ('pip._vendor.pygments.lexers.basic', 'CBM BASIC V2', ('cbmbas',), ('*.bas',), ()),
79
+ 'CddlLexer': ('pip._vendor.pygments.lexers.cddl', 'CDDL', ('cddl',), ('*.cddl',), ('text/x-cddl',)),
80
+ 'CeylonLexer': ('pip._vendor.pygments.lexers.jvm', 'Ceylon', ('ceylon',), ('*.ceylon',), ('text/x-ceylon',)),
81
+ 'Cfengine3Lexer': ('pip._vendor.pygments.lexers.configs', 'CFEngine3', ('cfengine3', 'cf3'), ('*.cf',), ()),
82
+ 'ChaiscriptLexer': ('pip._vendor.pygments.lexers.scripting', 'ChaiScript', ('chaiscript', 'chai'), ('*.chai',), ('text/x-chaiscript', 'application/x-chaiscript')),
83
+ 'ChapelLexer': ('pip._vendor.pygments.lexers.chapel', 'Chapel', ('chapel', 'chpl'), ('*.chpl',), ()),
84
+ 'CharmciLexer': ('pip._vendor.pygments.lexers.c_like', 'Charmci', ('charmci',), ('*.ci',), ()),
85
+ 'CheetahHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Cheetah', ('html+cheetah', 'html+spitfire', 'htmlcheetah'), (), ('text/html+cheetah', 'text/html+spitfire')),
86
+ 'CheetahJavascriptLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Cheetah', ('javascript+cheetah', 'js+cheetah', 'javascript+spitfire', 'js+spitfire'), (), ('application/x-javascript+cheetah', 'text/x-javascript+cheetah', 'text/javascript+cheetah', 'application/x-javascript+spitfire', 'text/x-javascript+spitfire', 'text/javascript+spitfire')),
87
+ 'CheetahLexer': ('pip._vendor.pygments.lexers.templates', 'Cheetah', ('cheetah', 'spitfire'), ('*.tmpl', '*.spt'), ('application/x-cheetah', 'application/x-spitfire')),
88
+ 'CheetahXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Cheetah', ('xml+cheetah', 'xml+spitfire'), (), ('application/xml+cheetah', 'application/xml+spitfire')),
89
+ 'CirruLexer': ('pip._vendor.pygments.lexers.webmisc', 'Cirru', ('cirru',), ('*.cirru',), ('text/x-cirru',)),
90
+ 'ClayLexer': ('pip._vendor.pygments.lexers.c_like', 'Clay', ('clay',), ('*.clay',), ('text/x-clay',)),
91
+ 'CleanLexer': ('pip._vendor.pygments.lexers.clean', 'Clean', ('clean',), ('*.icl', '*.dcl'), ()),
92
+ 'ClojureLexer': ('pip._vendor.pygments.lexers.jvm', 'Clojure', ('clojure', 'clj'), ('*.clj', '*.cljc'), ('text/x-clojure', 'application/x-clojure')),
93
+ 'ClojureScriptLexer': ('pip._vendor.pygments.lexers.jvm', 'ClojureScript', ('clojurescript', 'cljs'), ('*.cljs',), ('text/x-clojurescript', 'application/x-clojurescript')),
94
+ 'CobolFreeformatLexer': ('pip._vendor.pygments.lexers.business', 'COBOLFree', ('cobolfree',), ('*.cbl', '*.CBL'), ()),
95
+ 'CobolLexer': ('pip._vendor.pygments.lexers.business', 'COBOL', ('cobol',), ('*.cob', '*.COB', '*.cpy', '*.CPY'), ('text/x-cobol',)),
96
+ 'CodeQLLexer': ('pip._vendor.pygments.lexers.codeql', 'CodeQL', ('codeql', 'ql'), ('*.ql', '*.qll'), ()),
97
+ 'CoffeeScriptLexer': ('pip._vendor.pygments.lexers.javascript', 'CoffeeScript', ('coffeescript', 'coffee-script', 'coffee'), ('*.coffee',), ('text/coffeescript',)),
98
+ 'ColdfusionCFCLexer': ('pip._vendor.pygments.lexers.templates', 'Coldfusion CFC', ('cfc',), ('*.cfc',), ()),
99
+ 'ColdfusionHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'Coldfusion HTML', ('cfm',), ('*.cfm', '*.cfml'), ('application/x-coldfusion',)),
100
+ 'ColdfusionLexer': ('pip._vendor.pygments.lexers.templates', 'cfstatement', ('cfs',), (), ()),
101
+ 'Comal80Lexer': ('pip._vendor.pygments.lexers.comal', 'COMAL-80', ('comal', 'comal80'), ('*.cml', '*.comal'), ()),
102
+ 'CommonLispLexer': ('pip._vendor.pygments.lexers.lisp', 'Common Lisp', ('common-lisp', 'cl', 'lisp'), ('*.cl', '*.lisp'), ('text/x-common-lisp',)),
103
+ 'ComponentPascalLexer': ('pip._vendor.pygments.lexers.oberon', 'Component Pascal', ('componentpascal', 'cp'), ('*.cp', '*.cps'), ('text/x-component-pascal',)),
104
+ 'CplintLexer': ('pip._vendor.pygments.lexers.cplint', 'cplint', ('cplint',), ('*.ecl', '*.prolog', '*.pro', '*.pl', '*.P', '*.lpad', '*.cpl'), ('text/x-cplint',)),
105
+ 'CppLexer': ('pip._vendor.pygments.lexers.c_cpp', 'C++', ('cpp', 'c++'), ('*.cpp', '*.hpp', '*.c++', '*.h++', '*.cc', '*.hh', '*.cxx', '*.hxx', '*.C', '*.H', '*.cp', '*.CPP', '*.tpp', '*.cppm', '*.ixx', '*.mxx'), ('text/x-c++hdr', 'text/x-c++src')),
106
+ 'CppObjdumpLexer': ('pip._vendor.pygments.lexers.asm', 'cpp-objdump', ('cpp-objdump', 'c++-objdumb', 'cxx-objdump'), ('*.cpp-objdump', '*.c++-objdump', '*.cxx-objdump'), ('text/x-cpp-objdump',)),
107
+ 'CrmshLexer': ('pip._vendor.pygments.lexers.dsls', 'Crmsh', ('crmsh', 'pcmk'), ('*.crmsh', '*.pcmk'), ()),
108
+ 'CrocLexer': ('pip._vendor.pygments.lexers.d', 'Croc', ('croc',), ('*.croc',), ('text/x-crocsrc',)),
109
+ 'CryptolLexer': ('pip._vendor.pygments.lexers.haskell', 'Cryptol', ('cryptol', 'cry'), ('*.cry',), ('text/x-cryptol',)),
110
+ 'CrystalLexer': ('pip._vendor.pygments.lexers.crystal', 'Crystal', ('cr', 'crystal'), ('*.cr',), ('text/x-crystal',)),
111
+ 'CsoundDocumentLexer': ('pip._vendor.pygments.lexers.csound', 'Csound Document', ('csound-document', 'csound-csd'), ('*.csd',), ()),
112
+ 'CsoundOrchestraLexer': ('pip._vendor.pygments.lexers.csound', 'Csound Orchestra', ('csound', 'csound-orc'), ('*.orc', '*.udo'), ()),
113
+ 'CsoundScoreLexer': ('pip._vendor.pygments.lexers.csound', 'Csound Score', ('csound-score', 'csound-sco'), ('*.sco',), ()),
114
+ 'CssDjangoLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Django/Jinja', ('css+django', 'css+jinja'), ('*.css.j2', '*.css.jinja2'), ('text/css+django', 'text/css+jinja')),
115
+ 'CssErbLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Ruby', ('css+ruby', 'css+erb'), (), ('text/css+ruby',)),
116
+ 'CssGenshiLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Genshi Text', ('css+genshitext', 'css+genshi'), (), ('text/css+genshi',)),
117
+ 'CssLexer': ('pip._vendor.pygments.lexers.css', 'CSS', ('css',), ('*.css',), ('text/css',)),
118
+ 'CssPhpLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+PHP', ('css+php',), (), ('text/css+php',)),
119
+ 'CssSmartyLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Smarty', ('css+smarty',), (), ('text/css+smarty',)),
120
+ 'CudaLexer': ('pip._vendor.pygments.lexers.c_like', 'CUDA', ('cuda', 'cu'), ('*.cu', '*.cuh'), ('text/x-cuda',)),
121
+ 'CypherLexer': ('pip._vendor.pygments.lexers.graph', 'Cypher', ('cypher',), ('*.cyp', '*.cypher'), ()),
122
+ 'CythonLexer': ('pip._vendor.pygments.lexers.python', 'Cython', ('cython', 'pyx', 'pyrex'), ('*.pyx', '*.pxd', '*.pxi'), ('text/x-cython', 'application/x-cython')),
123
+ 'DLexer': ('pip._vendor.pygments.lexers.d', 'D', ('d',), ('*.d', '*.di'), ('text/x-dsrc',)),
124
+ 'DObjdumpLexer': ('pip._vendor.pygments.lexers.asm', 'd-objdump', ('d-objdump',), ('*.d-objdump',), ('text/x-d-objdump',)),
125
+ 'DarcsPatchLexer': ('pip._vendor.pygments.lexers.diff', 'Darcs Patch', ('dpatch',), ('*.dpatch', '*.darcspatch'), ()),
126
+ 'DartLexer': ('pip._vendor.pygments.lexers.javascript', 'Dart', ('dart',), ('*.dart',), ('text/x-dart',)),
127
+ 'Dasm16Lexer': ('pip._vendor.pygments.lexers.asm', 'DASM16', ('dasm16',), ('*.dasm16', '*.dasm'), ('text/x-dasm16',)),
128
+ 'DaxLexer': ('pip._vendor.pygments.lexers.dax', 'Dax', ('dax',), ('*.dax',), ()),
129
+ 'DebianControlLexer': ('pip._vendor.pygments.lexers.installers', 'Debian Control file', ('debcontrol', 'control'), ('control',), ()),
130
+ 'DebianSourcesLexer': ('pip._vendor.pygments.lexers.installers', 'Debian Sources file', ('debian.sources',), ('*.sources',), ()),
131
+ 'DelphiLexer': ('pip._vendor.pygments.lexers.pascal', 'Delphi', ('delphi', 'pas', 'pascal', 'objectpascal'), ('*.pas', '*.dpr'), ('text/x-pascal',)),
132
+ 'DesktopLexer': ('pip._vendor.pygments.lexers.configs', 'Desktop file', ('desktop',), ('*.desktop',), ('application/x-desktop',)),
133
+ 'DevicetreeLexer': ('pip._vendor.pygments.lexers.devicetree', 'Devicetree', ('devicetree', 'dts'), ('*.dts', '*.dtsi'), ('text/x-c',)),
134
+ 'DgLexer': ('pip._vendor.pygments.lexers.python', 'dg', ('dg',), ('*.dg',), ('text/x-dg',)),
135
+ 'DiffLexer': ('pip._vendor.pygments.lexers.diff', 'Diff', ('diff', 'udiff'), ('*.diff', '*.patch'), ('text/x-diff', 'text/x-patch')),
136
+ 'DjangoLexer': ('pip._vendor.pygments.lexers.templates', 'Django/Jinja', ('django', 'jinja'), (), ('application/x-django-templating', 'application/x-jinja')),
137
+ 'DnsZoneLexer': ('pip._vendor.pygments.lexers.dns', 'Zone', ('zone',), ('*.zone',), ('text/dns',)),
138
+ 'DockerLexer': ('pip._vendor.pygments.lexers.configs', 'Docker', ('docker', 'dockerfile'), ('Dockerfile', '*.docker'), ('text/x-dockerfile-config',)),
139
+ 'DtdLexer': ('pip._vendor.pygments.lexers.html', 'DTD', ('dtd',), ('*.dtd',), ('application/xml-dtd',)),
140
+ 'DuelLexer': ('pip._vendor.pygments.lexers.webmisc', 'Duel', ('duel', 'jbst', 'jsonml+bst'), ('*.duel', '*.jbst'), ('text/x-duel', 'text/x-jbst')),
141
+ 'DylanConsoleLexer': ('pip._vendor.pygments.lexers.dylan', 'Dylan session', ('dylan-console', 'dylan-repl'), ('*.dylan-console',), ('text/x-dylan-console',)),
142
+ 'DylanLexer': ('pip._vendor.pygments.lexers.dylan', 'Dylan', ('dylan',), ('*.dylan', '*.dyl', '*.intr'), ('text/x-dylan',)),
143
+ 'DylanLidLexer': ('pip._vendor.pygments.lexers.dylan', 'DylanLID', ('dylan-lid', 'lid'), ('*.lid', '*.hdp'), ('text/x-dylan-lid',)),
144
+ 'ECLLexer': ('pip._vendor.pygments.lexers.ecl', 'ECL', ('ecl',), ('*.ecl',), ('application/x-ecl',)),
145
+ 'ECLexer': ('pip._vendor.pygments.lexers.c_like', 'eC', ('ec',), ('*.ec', '*.eh'), ('text/x-echdr', 'text/x-ecsrc')),
146
+ 'EarlGreyLexer': ('pip._vendor.pygments.lexers.javascript', 'Earl Grey', ('earl-grey', 'earlgrey', 'eg'), ('*.eg',), ('text/x-earl-grey',)),
147
+ 'EasytrieveLexer': ('pip._vendor.pygments.lexers.scripting', 'Easytrieve', ('easytrieve',), ('*.ezt', '*.mac'), ('text/x-easytrieve',)),
148
+ 'EbnfLexer': ('pip._vendor.pygments.lexers.parsers', 'EBNF', ('ebnf',), ('*.ebnf',), ('text/x-ebnf',)),
149
+ 'EiffelLexer': ('pip._vendor.pygments.lexers.eiffel', 'Eiffel', ('eiffel',), ('*.e',), ('text/x-eiffel',)),
150
+ 'ElixirConsoleLexer': ('pip._vendor.pygments.lexers.erlang', 'Elixir iex session', ('iex',), (), ('text/x-elixir-shellsession',)),
151
+ 'ElixirLexer': ('pip._vendor.pygments.lexers.erlang', 'Elixir', ('elixir', 'ex', 'exs'), ('*.ex', '*.eex', '*.exs', '*.leex'), ('text/x-elixir',)),
152
+ 'ElmLexer': ('pip._vendor.pygments.lexers.elm', 'Elm', ('elm',), ('*.elm',), ('text/x-elm',)),
153
+ 'ElpiLexer': ('pip._vendor.pygments.lexers.elpi', 'Elpi', ('elpi',), ('*.elpi',), ('text/x-elpi',)),
154
+ 'EmacsLispLexer': ('pip._vendor.pygments.lexers.lisp', 'EmacsLisp', ('emacs-lisp', 'elisp', 'emacs'), ('*.el',), ('text/x-elisp', 'application/x-elisp')),
155
+ 'EmailLexer': ('pip._vendor.pygments.lexers.email', 'E-mail', ('email', 'eml'), ('*.eml',), ('message/rfc822',)),
156
+ 'ErbLexer': ('pip._vendor.pygments.lexers.templates', 'ERB', ('erb',), (), ('application/x-ruby-templating',)),
157
+ 'ErlangLexer': ('pip._vendor.pygments.lexers.erlang', 'Erlang', ('erlang',), ('*.erl', '*.hrl', '*.es', '*.escript'), ('text/x-erlang',)),
158
+ 'ErlangShellLexer': ('pip._vendor.pygments.lexers.erlang', 'Erlang erl session', ('erl',), ('*.erl-sh',), ('text/x-erl-shellsession',)),
159
+ 'EvoqueHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Evoque', ('html+evoque',), (), ('text/html+evoque',)),
160
+ 'EvoqueLexer': ('pip._vendor.pygments.lexers.templates', 'Evoque', ('evoque',), ('*.evoque',), ('application/x-evoque',)),
161
+ 'EvoqueXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Evoque', ('xml+evoque',), (), ('application/xml+evoque',)),
162
+ 'ExeclineLexer': ('pip._vendor.pygments.lexers.shell', 'execline', ('execline',), ('*.exec',), ()),
163
+ 'EzhilLexer': ('pip._vendor.pygments.lexers.ezhil', 'Ezhil', ('ezhil',), ('*.n',), ('text/x-ezhil',)),
164
+ 'FSharpLexer': ('pip._vendor.pygments.lexers.dotnet', 'F#', ('fsharp', 'f#'), ('*.fs', '*.fsi', '*.fsx'), ('text/x-fsharp',)),
165
+ 'FStarLexer': ('pip._vendor.pygments.lexers.ml', 'FStar', ('fstar',), ('*.fst', '*.fsti'), ('text/x-fstar',)),
166
+ 'FactorLexer': ('pip._vendor.pygments.lexers.factor', 'Factor', ('factor',), ('*.factor',), ('text/x-factor',)),
167
+ 'FancyLexer': ('pip._vendor.pygments.lexers.ruby', 'Fancy', ('fancy', 'fy'), ('*.fy', '*.fancypack'), ('text/x-fancysrc',)),
168
+ 'FantomLexer': ('pip._vendor.pygments.lexers.fantom', 'Fantom', ('fan',), ('*.fan',), ('application/x-fantom',)),
169
+ 'FelixLexer': ('pip._vendor.pygments.lexers.felix', 'Felix', ('felix', 'flx'), ('*.flx', '*.flxh'), ('text/x-felix',)),
170
+ 'FennelLexer': ('pip._vendor.pygments.lexers.lisp', 'Fennel', ('fennel', 'fnl'), ('*.fnl', '*.fnlm'), ('text/x-fennel',)),
171
+ 'FiftLexer': ('pip._vendor.pygments.lexers.fift', 'Fift', ('fift', 'fif'), ('*.fif',), ()),
172
+ 'FishShellLexer': ('pip._vendor.pygments.lexers.shell', 'Fish', ('fish', 'fishshell'), ('*.fish', '*.load'), ('application/x-fish',)),
173
+ 'FlatlineLexer': ('pip._vendor.pygments.lexers.dsls', 'Flatline', ('flatline',), (), ('text/x-flatline',)),
174
+ 'FloScriptLexer': ('pip._vendor.pygments.lexers.floscript', 'FloScript', ('floscript', 'flo'), ('*.flo',), ()),
175
+ 'ForthLexer': ('pip._vendor.pygments.lexers.forth', 'Forth', ('forth',), ('*.frt', '*.fs'), ('application/x-forth',)),
176
+ 'FortranFixedLexer': ('pip._vendor.pygments.lexers.fortran', 'FortranFixed', ('fortranfixed',), ('*.f', '*.F'), ()),
177
+ 'FortranLexer': ('pip._vendor.pygments.lexers.fortran', 'Fortran', ('fortran', 'f90'), ('*.f03', '*.f90', '*.F03', '*.F90'), ('text/x-fortran',)),
178
+ 'FoxProLexer': ('pip._vendor.pygments.lexers.foxpro', 'FoxPro', ('foxpro', 'vfp', 'clipper', 'xbase'), ('*.PRG', '*.prg'), ()),
179
+ 'FreeFemLexer': ('pip._vendor.pygments.lexers.freefem', 'Freefem', ('freefem',), ('*.edp',), ('text/x-freefem',)),
180
+ 'FuncLexer': ('pip._vendor.pygments.lexers.func', 'FunC', ('func', 'fc'), ('*.fc', '*.func'), ()),
181
+ 'FutharkLexer': ('pip._vendor.pygments.lexers.futhark', 'Futhark', ('futhark',), ('*.fut',), ('text/x-futhark',)),
182
+ 'GAPConsoleLexer': ('pip._vendor.pygments.lexers.algebra', 'GAP session', ('gap-console', 'gap-repl'), ('*.tst',), ()),
183
+ 'GAPLexer': ('pip._vendor.pygments.lexers.algebra', 'GAP', ('gap',), ('*.g', '*.gd', '*.gi', '*.gap'), ()),
184
+ 'GDScriptLexer': ('pip._vendor.pygments.lexers.gdscript', 'GDScript', ('gdscript', 'gd'), ('*.gd',), ('text/x-gdscript', 'application/x-gdscript')),
185
+ 'GLShaderLexer': ('pip._vendor.pygments.lexers.graphics', 'GLSL', ('glsl',), ('*.vert', '*.frag', '*.geo'), ('text/x-glslsrc',)),
186
+ 'GSQLLexer': ('pip._vendor.pygments.lexers.gsql', 'GSQL', ('gsql',), ('*.gsql',), ()),
187
+ 'GasLexer': ('pip._vendor.pygments.lexers.asm', 'GAS', ('gas', 'asm'), ('*.s', '*.S'), ('text/x-gas',)),
188
+ 'GcodeLexer': ('pip._vendor.pygments.lexers.gcodelexer', 'g-code', ('gcode',), ('*.gcode',), ()),
189
+ 'GenshiLexer': ('pip._vendor.pygments.lexers.templates', 'Genshi', ('genshi', 'kid', 'xml+genshi', 'xml+kid'), ('*.kid',), ('application/x-genshi', 'application/x-kid')),
190
+ 'GenshiTextLexer': ('pip._vendor.pygments.lexers.templates', 'Genshi Text', ('genshitext',), (), ('application/x-genshi-text', 'text/x-genshi')),
191
+ 'GettextLexer': ('pip._vendor.pygments.lexers.textfmts', 'Gettext Catalog', ('pot', 'po'), ('*.pot', '*.po'), ('application/x-gettext', 'text/x-gettext', 'text/gettext')),
192
+ 'GherkinLexer': ('pip._vendor.pygments.lexers.testing', 'Gherkin', ('gherkin', 'cucumber'), ('*.feature',), ('text/x-gherkin',)),
193
+ 'GleamLexer': ('pip._vendor.pygments.lexers.gleam', 'Gleam', ('gleam',), ('*.gleam',), ('text/x-gleam',)),
194
+ 'GnuplotLexer': ('pip._vendor.pygments.lexers.graphics', 'Gnuplot', ('gnuplot',), ('*.plot', '*.plt'), ('text/x-gnuplot',)),
195
+ 'GoLexer': ('pip._vendor.pygments.lexers.go', 'Go', ('go', 'golang'), ('*.go',), ('text/x-gosrc',)),
196
+ 'GoloLexer': ('pip._vendor.pygments.lexers.jvm', 'Golo', ('golo',), ('*.golo',), ()),
197
+ 'GoodDataCLLexer': ('pip._vendor.pygments.lexers.business', 'GoodData-CL', ('gooddata-cl',), ('*.gdc',), ('text/x-gooddata-cl',)),
198
+ 'GoogleSqlLexer': ('pip._vendor.pygments.lexers.sql', 'GoogleSQL', ('googlesql', 'zetasql'), ('*.googlesql', '*.googlesql.sql'), ('text/x-google-sql', 'text/x-google-sql-aux')),
199
+ 'GosuLexer': ('pip._vendor.pygments.lexers.jvm', 'Gosu', ('gosu',), ('*.gs', '*.gsx', '*.gsp', '*.vark'), ('text/x-gosu',)),
200
+ 'GosuTemplateLexer': ('pip._vendor.pygments.lexers.jvm', 'Gosu Template', ('gst',), ('*.gst',), ('text/x-gosu-template',)),
201
+ 'GraphQLLexer': ('pip._vendor.pygments.lexers.graphql', 'GraphQL', ('graphql',), ('*.graphql',), ()),
202
+ 'GraphvizLexer': ('pip._vendor.pygments.lexers.graphviz', 'Graphviz', ('graphviz', 'dot'), ('*.gv', '*.dot'), ('text/x-graphviz', 'text/vnd.graphviz')),
203
+ 'GroffLexer': ('pip._vendor.pygments.lexers.markup', 'Groff', ('groff', 'nroff', 'man'), ('*.[1-9]', '*.man', '*.1p', '*.3pm'), ('application/x-troff', 'text/troff')),
204
+ 'GroovyLexer': ('pip._vendor.pygments.lexers.jvm', 'Groovy', ('groovy',), ('*.groovy', '*.gradle'), ('text/x-groovy',)),
205
+ 'HLSLShaderLexer': ('pip._vendor.pygments.lexers.graphics', 'HLSL', ('hlsl',), ('*.hlsl', '*.hlsli'), ('text/x-hlsl',)),
206
+ 'HTMLUL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'HTML+UL4', ('html+ul4',), ('*.htmlul4',), ()),
207
+ 'HamlLexer': ('pip._vendor.pygments.lexers.html', 'Haml', ('haml',), ('*.haml',), ('text/x-haml',)),
208
+ 'HandlebarsHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Handlebars', ('html+handlebars',), ('*.handlebars', '*.hbs'), ('text/html+handlebars', 'text/x-handlebars-template')),
209
+ 'HandlebarsLexer': ('pip._vendor.pygments.lexers.templates', 'Handlebars', ('handlebars',), (), ()),
210
+ 'HareLexer': ('pip._vendor.pygments.lexers.hare', 'Hare', ('hare',), ('*.ha',), ('text/x-hare',)),
211
+ 'HaskellLexer': ('pip._vendor.pygments.lexers.haskell', 'Haskell', ('haskell', 'hs'), ('*.hs',), ('text/x-haskell',)),
212
+ 'HaxeLexer': ('pip._vendor.pygments.lexers.haxe', 'Haxe', ('haxe', 'hxsl', 'hx'), ('*.hx', '*.hxsl'), ('text/haxe', 'text/x-haxe', 'text/x-hx')),
213
+ 'HexdumpLexer': ('pip._vendor.pygments.lexers.hexdump', 'Hexdump', ('hexdump',), (), ()),
214
+ 'HsailLexer': ('pip._vendor.pygments.lexers.asm', 'HSAIL', ('hsail', 'hsa'), ('*.hsail',), ('text/x-hsail',)),
215
+ 'HspecLexer': ('pip._vendor.pygments.lexers.haskell', 'Hspec', ('hspec',), ('*Spec.hs',), ()),
216
+ 'HtmlDjangoLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Django/Jinja', ('html+django', 'html+jinja', 'htmldjango'), ('*.html.j2', '*.htm.j2', '*.xhtml.j2', '*.html.jinja2', '*.htm.jinja2', '*.xhtml.jinja2'), ('text/html+django', 'text/html+jinja')),
217
+ 'HtmlGenshiLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Genshi', ('html+genshi', 'html+kid'), (), ('text/html+genshi',)),
218
+ 'HtmlLexer': ('pip._vendor.pygments.lexers.html', 'HTML', ('html',), ('*.html', '*.htm', '*.xhtml', '*.xslt'), ('text/html', 'application/xhtml+xml')),
219
+ 'HtmlPhpLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+PHP', ('html+php',), ('*.phtml',), ('application/x-php', 'application/x-httpd-php', 'application/x-httpd-php3', 'application/x-httpd-php4', 'application/x-httpd-php5')),
220
+ 'HtmlSmartyLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Smarty', ('html+smarty',), (), ('text/html+smarty',)),
221
+ 'HttpLexer': ('pip._vendor.pygments.lexers.textfmts', 'HTTP', ('http',), (), ()),
222
+ 'HxmlLexer': ('pip._vendor.pygments.lexers.haxe', 'Hxml', ('haxeml', 'hxml'), ('*.hxml',), ()),
223
+ 'HyLexer': ('pip._vendor.pygments.lexers.lisp', 'Hy', ('hylang', 'hy'), ('*.hy',), ('text/x-hy', 'application/x-hy')),
224
+ 'HybrisLexer': ('pip._vendor.pygments.lexers.scripting', 'Hybris', ('hybris',), ('*.hyb',), ('text/x-hybris', 'application/x-hybris')),
225
+ 'IDLLexer': ('pip._vendor.pygments.lexers.idl', 'IDL', ('idl',), ('*.pro',), ('text/idl',)),
226
+ 'IconLexer': ('pip._vendor.pygments.lexers.unicon', 'Icon', ('icon',), ('*.icon', '*.ICON'), ()),
227
+ 'IdrisLexer': ('pip._vendor.pygments.lexers.haskell', 'Idris', ('idris', 'idr'), ('*.idr',), ('text/x-idris',)),
228
+ 'IgorLexer': ('pip._vendor.pygments.lexers.igor', 'Igor', ('igor', 'igorpro'), ('*.ipf',), ('text/ipf',)),
229
+ 'Inform6Lexer': ('pip._vendor.pygments.lexers.int_fiction', 'Inform 6', ('inform6', 'i6'), ('*.inf',), ()),
230
+ 'Inform6TemplateLexer': ('pip._vendor.pygments.lexers.int_fiction', 'Inform 6 template', ('i6t',), ('*.i6t',), ()),
231
+ 'Inform7Lexer': ('pip._vendor.pygments.lexers.int_fiction', 'Inform 7', ('inform7', 'i7'), ('*.ni', '*.i7x'), ()),
232
+ 'IniLexer': ('pip._vendor.pygments.lexers.configs', 'INI', ('ini', 'cfg', 'dosini'), ('*.ini', '*.cfg', '*.inf', '.editorconfig'), ('text/x-ini', 'text/inf')),
233
+ 'IoLexer': ('pip._vendor.pygments.lexers.iolang', 'Io', ('io',), ('*.io',), ('text/x-iosrc',)),
234
+ 'IokeLexer': ('pip._vendor.pygments.lexers.jvm', 'Ioke', ('ioke', 'ik'), ('*.ik',), ('text/x-iokesrc',)),
235
+ 'IrcLogsLexer': ('pip._vendor.pygments.lexers.textfmts', 'IRC logs', ('irc',), ('*.weechatlog',), ('text/x-irclog',)),
236
+ 'IsabelleLexer': ('pip._vendor.pygments.lexers.theorem', 'Isabelle', ('isabelle',), ('*.thy',), ('text/x-isabelle',)),
237
+ 'JLexer': ('pip._vendor.pygments.lexers.j', 'J', ('j',), ('*.ijs',), ('text/x-j',)),
238
+ 'JMESPathLexer': ('pip._vendor.pygments.lexers.jmespath', 'JMESPath', ('jmespath', 'jp'), ('*.jp',), ()),
239
+ 'JSLTLexer': ('pip._vendor.pygments.lexers.jslt', 'JSLT', ('jslt',), ('*.jslt',), ('text/x-jslt',)),
240
+ 'JagsLexer': ('pip._vendor.pygments.lexers.modeling', 'JAGS', ('jags',), ('*.jag', '*.bug'), ()),
241
+ 'JanetLexer': ('pip._vendor.pygments.lexers.lisp', 'Janet', ('janet',), ('*.janet', '*.jdn'), ('text/x-janet', 'application/x-janet')),
242
+ 'JasminLexer': ('pip._vendor.pygments.lexers.jvm', 'Jasmin', ('jasmin', 'jasminxt'), ('*.j',), ()),
243
+ 'JavaLexer': ('pip._vendor.pygments.lexers.jvm', 'Java', ('java',), ('*.java',), ('text/x-java',)),
244
+ 'JavascriptDjangoLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Django/Jinja', ('javascript+django', 'js+django', 'javascript+jinja', 'js+jinja'), ('*.js.j2', '*.js.jinja2'), ('application/x-javascript+django', 'application/x-javascript+jinja', 'text/x-javascript+django', 'text/x-javascript+jinja', 'text/javascript+django', 'text/javascript+jinja')),
245
+ 'JavascriptErbLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Ruby', ('javascript+ruby', 'js+ruby', 'javascript+erb', 'js+erb'), (), ('application/x-javascript+ruby', 'text/x-javascript+ruby', 'text/javascript+ruby')),
246
+ 'JavascriptGenshiLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Genshi Text', ('js+genshitext', 'js+genshi', 'javascript+genshitext', 'javascript+genshi'), (), ('application/x-javascript+genshi', 'text/x-javascript+genshi', 'text/javascript+genshi')),
247
+ 'JavascriptLexer': ('pip._vendor.pygments.lexers.javascript', 'JavaScript', ('javascript', 'js'), ('*.js', '*.jsm', '*.mjs', '*.cjs'), ('application/javascript', 'application/x-javascript', 'text/x-javascript', 'text/javascript')),
248
+ 'JavascriptPhpLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+PHP', ('javascript+php', 'js+php'), (), ('application/x-javascript+php', 'text/x-javascript+php', 'text/javascript+php')),
249
+ 'JavascriptSmartyLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Smarty', ('javascript+smarty', 'js+smarty'), (), ('application/x-javascript+smarty', 'text/x-javascript+smarty', 'text/javascript+smarty')),
250
+ 'JavascriptUL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'Javascript+UL4', ('js+ul4',), ('*.jsul4',), ()),
251
+ 'JclLexer': ('pip._vendor.pygments.lexers.scripting', 'JCL', ('jcl',), ('*.jcl',), ('text/x-jcl',)),
252
+ 'JsgfLexer': ('pip._vendor.pygments.lexers.grammar_notation', 'JSGF', ('jsgf',), ('*.jsgf',), ('application/jsgf', 'application/x-jsgf', 'text/jsgf')),
253
+ 'Json5Lexer': ('pip._vendor.pygments.lexers.json5', 'JSON5', ('json5',), ('*.json5',), ()),
254
+ 'JsonBareObjectLexer': ('pip._vendor.pygments.lexers.data', 'JSONBareObject', (), (), ()),
255
+ 'JsonLdLexer': ('pip._vendor.pygments.lexers.data', 'JSON-LD', ('jsonld', 'json-ld'), ('*.jsonld',), ('application/ld+json',)),
256
+ 'JsonLexer': ('pip._vendor.pygments.lexers.data', 'JSON', ('json', 'json-object'), ('*.json', '*.jsonl', '*.ndjson', 'Pipfile.lock', '*.module', '*.xc'), ('application/json', 'application/json-object', 'application/x-ndjson', 'application/jsonl', 'application/json-seq')),
257
+ 'JsonnetLexer': ('pip._vendor.pygments.lexers.jsonnet', 'Jsonnet', ('jsonnet',), ('*.jsonnet', '*.libsonnet'), ()),
258
+ 'JspLexer': ('pip._vendor.pygments.lexers.templates', 'Java Server Page', ('jsp',), ('*.jsp',), ('application/x-jsp',)),
259
+ 'JsxLexer': ('pip._vendor.pygments.lexers.jsx', 'JSX', ('jsx', 'react'), ('*.jsx', '*.react'), ('text/jsx', 'text/typescript-jsx')),
260
+ 'JuliaConsoleLexer': ('pip._vendor.pygments.lexers.julia', 'Julia console', ('jlcon', 'julia-repl'), (), ()),
261
+ 'JuliaLexer': ('pip._vendor.pygments.lexers.julia', 'Julia', ('julia', 'jl'), ('*.jl',), ('text/x-julia', 'application/x-julia')),
262
+ 'JuttleLexer': ('pip._vendor.pygments.lexers.javascript', 'Juttle', ('juttle',), ('*.juttle',), ('application/juttle', 'application/x-juttle', 'text/x-juttle', 'text/juttle')),
263
+ 'KLexer': ('pip._vendor.pygments.lexers.q', 'K', ('k',), ('*.k',), ()),
264
+ 'KalLexer': ('pip._vendor.pygments.lexers.javascript', 'Kal', ('kal',), ('*.kal',), ('text/kal', 'application/kal')),
265
+ 'KconfigLexer': ('pip._vendor.pygments.lexers.configs', 'Kconfig', ('kconfig', 'menuconfig', 'linux-config', 'kernel-config'), ('Kconfig*', '*Config.in*', 'external.in*', 'standard-modules.in'), ('text/x-kconfig',)),
266
+ 'KernelLogLexer': ('pip._vendor.pygments.lexers.textfmts', 'Kernel log', ('kmsg', 'dmesg'), ('*.kmsg', '*.dmesg'), ()),
267
+ 'KokaLexer': ('pip._vendor.pygments.lexers.haskell', 'Koka', ('koka',), ('*.kk', '*.kki'), ('text/x-koka',)),
268
+ 'KotlinLexer': ('pip._vendor.pygments.lexers.jvm', 'Kotlin', ('kotlin',), ('*.kt', '*.kts'), ('text/x-kotlin',)),
269
+ 'KuinLexer': ('pip._vendor.pygments.lexers.kuin', 'Kuin', ('kuin',), ('*.kn',), ()),
270
+ 'KustoLexer': ('pip._vendor.pygments.lexers.kusto', 'Kusto', ('kql', 'kusto'), ('*.kql', '*.kusto', '.csl'), ()),
271
+ 'LSLLexer': ('pip._vendor.pygments.lexers.scripting', 'LSL', ('lsl',), ('*.lsl',), ('text/x-lsl',)),
272
+ 'LassoCssLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Lasso', ('css+lasso',), (), ('text/css+lasso',)),
273
+ 'LassoHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Lasso', ('html+lasso',), (), ('text/html+lasso', 'application/x-httpd-lasso', 'application/x-httpd-lasso[89]')),
274
+ 'LassoJavascriptLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Lasso', ('javascript+lasso', 'js+lasso'), (), ('application/x-javascript+lasso', 'text/x-javascript+lasso', 'text/javascript+lasso')),
275
+ 'LassoLexer': ('pip._vendor.pygments.lexers.javascript', 'Lasso', ('lasso', 'lassoscript'), ('*.lasso', '*.lasso[89]'), ('text/x-lasso',)),
276
+ 'LassoXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Lasso', ('xml+lasso',), (), ('application/xml+lasso',)),
277
+ 'LdaprcLexer': ('pip._vendor.pygments.lexers.ldap', 'LDAP configuration file', ('ldapconf', 'ldaprc'), ('.ldaprc', 'ldaprc', 'ldap.conf'), ('text/x-ldapconf',)),
278
+ 'LdifLexer': ('pip._vendor.pygments.lexers.ldap', 'LDIF', ('ldif',), ('*.ldif',), ('text/x-ldif',)),
279
+ 'Lean3Lexer': ('pip._vendor.pygments.lexers.lean', 'Lean', ('lean', 'lean3'), ('*.lean',), ('text/x-lean', 'text/x-lean3')),
280
+ 'Lean4Lexer': ('pip._vendor.pygments.lexers.lean', 'Lean4', ('lean4',), ('*.lean',), ('text/x-lean4',)),
281
+ 'LessCssLexer': ('pip._vendor.pygments.lexers.css', 'LessCss', ('less',), ('*.less',), ('text/x-less-css',)),
282
+ 'LighttpdConfLexer': ('pip._vendor.pygments.lexers.configs', 'Lighttpd configuration file', ('lighttpd', 'lighty'), ('lighttpd.conf',), ('text/x-lighttpd-conf',)),
283
+ 'LilyPondLexer': ('pip._vendor.pygments.lexers.lilypond', 'LilyPond', ('lilypond',), ('*.ly',), ()),
284
+ 'LimboLexer': ('pip._vendor.pygments.lexers.inferno', 'Limbo', ('limbo',), ('*.b',), ('text/limbo',)),
285
+ 'LiquidLexer': ('pip._vendor.pygments.lexers.templates', 'liquid', ('liquid',), ('*.liquid',), ()),
286
+ 'LiterateAgdaLexer': ('pip._vendor.pygments.lexers.haskell', 'Literate Agda', ('literate-agda', 'lagda'), ('*.lagda',), ('text/x-literate-agda',)),
287
+ 'LiterateCryptolLexer': ('pip._vendor.pygments.lexers.haskell', 'Literate Cryptol', ('literate-cryptol', 'lcryptol', 'lcry'), ('*.lcry',), ('text/x-literate-cryptol',)),
288
+ 'LiterateHaskellLexer': ('pip._vendor.pygments.lexers.haskell', 'Literate Haskell', ('literate-haskell', 'lhaskell', 'lhs'), ('*.lhs',), ('text/x-literate-haskell',)),
289
+ 'LiterateIdrisLexer': ('pip._vendor.pygments.lexers.haskell', 'Literate Idris', ('literate-idris', 'lidris', 'lidr'), ('*.lidr',), ('text/x-literate-idris',)),
290
+ 'LiveScriptLexer': ('pip._vendor.pygments.lexers.javascript', 'LiveScript', ('livescript', 'live-script'), ('*.ls',), ('text/livescript',)),
291
+ 'LlvmLexer': ('pip._vendor.pygments.lexers.asm', 'LLVM', ('llvm',), ('*.ll',), ('text/x-llvm',)),
292
+ 'LlvmMirBodyLexer': ('pip._vendor.pygments.lexers.asm', 'LLVM-MIR Body', ('llvm-mir-body',), (), ()),
293
+ 'LlvmMirLexer': ('pip._vendor.pygments.lexers.asm', 'LLVM-MIR', ('llvm-mir',), ('*.mir',), ()),
294
+ 'LogosLexer': ('pip._vendor.pygments.lexers.objective', 'Logos', ('logos',), ('*.x', '*.xi', '*.xm', '*.xmi'), ('text/x-logos',)),
295
+ 'LogtalkLexer': ('pip._vendor.pygments.lexers.prolog', 'Logtalk', ('logtalk',), ('*.lgt', '*.logtalk'), ('text/x-logtalk',)),
296
+ 'LuaLexer': ('pip._vendor.pygments.lexers.scripting', 'Lua', ('lua',), ('*.lua', '*.wlua'), ('text/x-lua', 'application/x-lua')),
297
+ 'LuauLexer': ('pip._vendor.pygments.lexers.scripting', 'Luau', ('luau',), ('*.luau',), ()),
298
+ 'MCFunctionLexer': ('pip._vendor.pygments.lexers.minecraft', 'MCFunction', ('mcfunction', 'mcf'), ('*.mcfunction',), ('text/mcfunction',)),
299
+ 'MCSchemaLexer': ('pip._vendor.pygments.lexers.minecraft', 'MCSchema', ('mcschema',), ('*.mcschema',), ('text/mcschema',)),
300
+ 'MIMELexer': ('pip._vendor.pygments.lexers.mime', 'MIME', ('mime',), (), ('multipart/mixed', 'multipart/related', 'multipart/alternative')),
301
+ 'MIPSLexer': ('pip._vendor.pygments.lexers.mips', 'MIPS', ('mips',), ('*.mips', '*.MIPS'), ()),
302
+ 'MOOCodeLexer': ('pip._vendor.pygments.lexers.scripting', 'MOOCode', ('moocode', 'moo'), ('*.moo',), ('text/x-moocode',)),
303
+ 'MSDOSSessionLexer': ('pip._vendor.pygments.lexers.shell', 'MSDOS Session', ('doscon',), (), ()),
304
+ 'Macaulay2Lexer': ('pip._vendor.pygments.lexers.macaulay2', 'Macaulay2', ('macaulay2',), ('*.m2',), ()),
305
+ 'MakefileLexer': ('pip._vendor.pygments.lexers.make', 'Makefile', ('make', 'makefile', 'mf', 'bsdmake'), ('*.mak', '*.mk', 'Makefile', 'makefile', 'Makefile.*', 'GNUmakefile'), ('text/x-makefile',)),
306
+ 'MakoCssLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Mako', ('css+mako',), (), ('text/css+mako',)),
307
+ 'MakoHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Mako', ('html+mako',), (), ('text/html+mako',)),
308
+ 'MakoJavascriptLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Mako', ('javascript+mako', 'js+mako'), (), ('application/x-javascript+mako', 'text/x-javascript+mako', 'text/javascript+mako')),
309
+ 'MakoLexer': ('pip._vendor.pygments.lexers.templates', 'Mako', ('mako',), ('*.mao',), ('application/x-mako',)),
310
+ 'MakoXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Mako', ('xml+mako',), (), ('application/xml+mako',)),
311
+ 'MapleLexer': ('pip._vendor.pygments.lexers.maple', 'Maple', ('maple',), ('*.mpl', '*.mi', '*.mm'), ('text/x-maple',)),
312
+ 'MaqlLexer': ('pip._vendor.pygments.lexers.business', 'MAQL', ('maql',), ('*.maql',), ('text/x-gooddata-maql', 'application/x-gooddata-maql')),
313
+ 'MarkdownLexer': ('pip._vendor.pygments.lexers.markup', 'Markdown', ('markdown', 'md'), ('*.md', '*.markdown'), ('text/x-markdown',)),
314
+ 'MaskLexer': ('pip._vendor.pygments.lexers.javascript', 'Mask', ('mask',), ('*.mask',), ('text/x-mask',)),
315
+ 'MasonLexer': ('pip._vendor.pygments.lexers.templates', 'Mason', ('mason',), ('*.m', '*.mhtml', '*.mc', '*.mi', 'autohandler', 'dhandler'), ('application/x-mason',)),
316
+ 'MathematicaLexer': ('pip._vendor.pygments.lexers.algebra', 'Mathematica', ('mathematica', 'mma', 'nb', 'wl', 'wolfram'), ('*.nb', '*.cdf', '*.nbp', '*.ma', '*.wl', '*.wls'), ('application/mathematica', 'application/vnd.wolfram.mathematica', 'application/vnd.wolfram.mathematica.package', 'application/vnd.wolfram.cdf', 'application/vnd.wolfram.wl')),
317
+ 'MatlabLexer': ('pip._vendor.pygments.lexers.matlab', 'Matlab', ('matlab',), ('*.m',), ('text/matlab',)),
318
+ 'MatlabSessionLexer': ('pip._vendor.pygments.lexers.matlab', 'Matlab session', ('matlabsession',), (), ()),
319
+ 'MaximaLexer': ('pip._vendor.pygments.lexers.maxima', 'Maxima', ('maxima', 'macsyma'), ('*.mac', '*.max'), ()),
320
+ 'MesonLexer': ('pip._vendor.pygments.lexers.meson', 'Meson', ('meson', 'meson.build'), ('meson.build', 'meson.options', 'meson_options.txt'), ('text/x-meson',)),
321
+ 'MiniDLexer': ('pip._vendor.pygments.lexers.d', 'MiniD', ('minid',), (), ('text/x-minidsrc',)),
322
+ 'MiniScriptLexer': ('pip._vendor.pygments.lexers.scripting', 'MiniScript', ('miniscript', 'ms'), ('*.ms',), ('text/x-minicript', 'application/x-miniscript')),
323
+ 'ModelicaLexer': ('pip._vendor.pygments.lexers.modeling', 'Modelica', ('modelica',), ('*.mo',), ('text/x-modelica',)),
324
+ 'Modula2Lexer': ('pip._vendor.pygments.lexers.modula2', 'Modula-2', ('modula2', 'm2'), ('*.def', '*.mod'), ('text/x-modula2',)),
325
+ 'MoinWikiLexer': ('pip._vendor.pygments.lexers.markup', 'MoinMoin/Trac Wiki markup', ('trac-wiki', 'moin'), (), ('text/x-trac-wiki',)),
326
+ 'MojoLexer': ('pip._vendor.pygments.lexers.mojo', 'Mojo', ('mojo', '🔥'), ('*.mojo', '*.🔥'), ('text/x-mojo', 'application/x-mojo')),
327
+ 'MonkeyLexer': ('pip._vendor.pygments.lexers.basic', 'Monkey', ('monkey',), ('*.monkey',), ('text/x-monkey',)),
328
+ 'MonteLexer': ('pip._vendor.pygments.lexers.monte', 'Monte', ('monte',), ('*.mt',), ()),
329
+ 'MoonScriptLexer': ('pip._vendor.pygments.lexers.scripting', 'MoonScript', ('moonscript', 'moon'), ('*.moon',), ('text/x-moonscript', 'application/x-moonscript')),
330
+ 'MoselLexer': ('pip._vendor.pygments.lexers.mosel', 'Mosel', ('mosel',), ('*.mos',), ()),
331
+ 'MozPreprocCssLexer': ('pip._vendor.pygments.lexers.markup', 'CSS+mozpreproc', ('css+mozpreproc',), ('*.css.in',), ()),
332
+ 'MozPreprocHashLexer': ('pip._vendor.pygments.lexers.markup', 'mozhashpreproc', ('mozhashpreproc',), (), ()),
333
+ 'MozPreprocJavascriptLexer': ('pip._vendor.pygments.lexers.markup', 'Javascript+mozpreproc', ('javascript+mozpreproc',), ('*.js.in',), ()),
334
+ 'MozPreprocPercentLexer': ('pip._vendor.pygments.lexers.markup', 'mozpercentpreproc', ('mozpercentpreproc',), (), ()),
335
+ 'MozPreprocXulLexer': ('pip._vendor.pygments.lexers.markup', 'XUL+mozpreproc', ('xul+mozpreproc',), ('*.xul.in',), ()),
336
+ 'MqlLexer': ('pip._vendor.pygments.lexers.c_like', 'MQL', ('mql', 'mq4', 'mq5', 'mql4', 'mql5'), ('*.mq4', '*.mq5', '*.mqh'), ('text/x-mql',)),
337
+ 'MscgenLexer': ('pip._vendor.pygments.lexers.dsls', 'Mscgen', ('mscgen', 'msc'), ('*.msc',), ()),
338
+ 'MuPADLexer': ('pip._vendor.pygments.lexers.algebra', 'MuPAD', ('mupad',), ('*.mu',), ()),
339
+ 'MxmlLexer': ('pip._vendor.pygments.lexers.actionscript', 'MXML', ('mxml',), ('*.mxml',), ()),
340
+ 'MySqlLexer': ('pip._vendor.pygments.lexers.sql', 'MySQL', ('mysql',), (), ('text/x-mysql',)),
341
+ 'MyghtyCssLexer': ('pip._vendor.pygments.lexers.templates', 'CSS+Myghty', ('css+myghty',), (), ('text/css+myghty',)),
342
+ 'MyghtyHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Myghty', ('html+myghty',), (), ('text/html+myghty',)),
343
+ 'MyghtyJavascriptLexer': ('pip._vendor.pygments.lexers.templates', 'JavaScript+Myghty', ('javascript+myghty', 'js+myghty'), (), ('application/x-javascript+myghty', 'text/x-javascript+myghty', 'text/javascript+mygthy')),
344
+ 'MyghtyLexer': ('pip._vendor.pygments.lexers.templates', 'Myghty', ('myghty',), ('*.myt', 'autodelegate'), ('application/x-myghty',)),
345
+ 'MyghtyXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Myghty', ('xml+myghty',), (), ('application/xml+myghty',)),
346
+ 'NCLLexer': ('pip._vendor.pygments.lexers.ncl', 'NCL', ('ncl',), ('*.ncl',), ('text/ncl',)),
347
+ 'NSISLexer': ('pip._vendor.pygments.lexers.installers', 'NSIS', ('nsis', 'nsi', 'nsh'), ('*.nsi', '*.nsh'), ('text/x-nsis',)),
348
+ 'NasmLexer': ('pip._vendor.pygments.lexers.asm', 'NASM', ('nasm',), ('*.asm', '*.ASM', '*.nasm'), ('text/x-nasm',)),
349
+ 'NasmObjdumpLexer': ('pip._vendor.pygments.lexers.asm', 'objdump-nasm', ('objdump-nasm',), ('*.objdump-intel',), ('text/x-nasm-objdump',)),
350
+ 'NemerleLexer': ('pip._vendor.pygments.lexers.dotnet', 'Nemerle', ('nemerle',), ('*.n',), ('text/x-nemerle',)),
351
+ 'NesCLexer': ('pip._vendor.pygments.lexers.c_like', 'nesC', ('nesc',), ('*.nc',), ('text/x-nescsrc',)),
352
+ 'NestedTextLexer': ('pip._vendor.pygments.lexers.configs', 'NestedText', ('nestedtext', 'nt'), ('*.nt',), ()),
353
+ 'NewLispLexer': ('pip._vendor.pygments.lexers.lisp', 'NewLisp', ('newlisp',), ('*.lsp', '*.nl', '*.kif'), ('text/x-newlisp', 'application/x-newlisp')),
354
+ 'NewspeakLexer': ('pip._vendor.pygments.lexers.smalltalk', 'Newspeak', ('newspeak',), ('*.ns2',), ('text/x-newspeak',)),
355
+ 'NginxConfLexer': ('pip._vendor.pygments.lexers.configs', 'Nginx configuration file', ('nginx',), ('nginx.conf',), ('text/x-nginx-conf',)),
356
+ 'NimrodLexer': ('pip._vendor.pygments.lexers.nimrod', 'Nimrod', ('nimrod', 'nim'), ('*.nim', '*.nimrod'), ('text/x-nim',)),
357
+ 'NitLexer': ('pip._vendor.pygments.lexers.nit', 'Nit', ('nit',), ('*.nit',), ()),
358
+ 'NixLexer': ('pip._vendor.pygments.lexers.nix', 'Nix', ('nixos', 'nix'), ('*.nix',), ('text/x-nix',)),
359
+ 'NodeConsoleLexer': ('pip._vendor.pygments.lexers.javascript', 'Node.js REPL console session', ('nodejsrepl',), (), ('text/x-nodejsrepl',)),
360
+ 'NotmuchLexer': ('pip._vendor.pygments.lexers.textfmts', 'Notmuch', ('notmuch',), (), ()),
361
+ 'NuSMVLexer': ('pip._vendor.pygments.lexers.smv', 'NuSMV', ('nusmv',), ('*.smv',), ()),
362
+ 'NumPyLexer': ('pip._vendor.pygments.lexers.python', 'NumPy', ('numpy',), (), ()),
363
+ 'NumbaIRLexer': ('pip._vendor.pygments.lexers.numbair', 'Numba_IR', ('numba_ir', 'numbair'), ('*.numba_ir',), ('text/x-numba_ir', 'text/x-numbair')),
364
+ 'ObjdumpLexer': ('pip._vendor.pygments.lexers.asm', 'objdump', ('objdump',), ('*.objdump',), ('text/x-objdump',)),
365
+ 'ObjectiveCLexer': ('pip._vendor.pygments.lexers.objective', 'Objective-C', ('objective-c', 'objectivec', 'obj-c', 'objc'), ('*.m', '*.h'), ('text/x-objective-c',)),
366
+ 'ObjectiveCppLexer': ('pip._vendor.pygments.lexers.objective', 'Objective-C++', ('objective-c++', 'objectivec++', 'obj-c++', 'objc++'), ('*.mm', '*.hh'), ('text/x-objective-c++',)),
367
+ 'ObjectiveJLexer': ('pip._vendor.pygments.lexers.javascript', 'Objective-J', ('objective-j', 'objectivej', 'obj-j', 'objj'), ('*.j',), ('text/x-objective-j',)),
368
+ 'OcamlLexer': ('pip._vendor.pygments.lexers.ml', 'OCaml', ('ocaml',), ('*.ml', '*.mli', '*.mll', '*.mly'), ('text/x-ocaml',)),
369
+ 'OctaveLexer': ('pip._vendor.pygments.lexers.matlab', 'Octave', ('octave',), ('*.m',), ('text/octave',)),
370
+ 'OdinLexer': ('pip._vendor.pygments.lexers.archetype', 'ODIN', ('odin',), ('*.odin',), ('text/odin',)),
371
+ 'OmgIdlLexer': ('pip._vendor.pygments.lexers.c_like', 'OMG Interface Definition Language', ('omg-idl',), ('*.idl', '*.pidl'), ()),
372
+ 'OocLexer': ('pip._vendor.pygments.lexers.ooc', 'Ooc', ('ooc',), ('*.ooc',), ('text/x-ooc',)),
373
+ 'OpaLexer': ('pip._vendor.pygments.lexers.ml', 'Opa', ('opa',), ('*.opa',), ('text/x-opa',)),
374
+ 'OpenEdgeLexer': ('pip._vendor.pygments.lexers.business', 'OpenEdge ABL', ('openedge', 'abl', 'progress'), ('*.p', '*.cls'), ('text/x-openedge', 'application/x-openedge')),
375
+ 'OpenScadLexer': ('pip._vendor.pygments.lexers.openscad', 'OpenSCAD', ('openscad',), ('*.scad',), ('application/x-openscad',)),
376
+ 'OrgLexer': ('pip._vendor.pygments.lexers.markup', 'Org Mode', ('org', 'orgmode', 'org-mode'), ('*.org',), ('text/org',)),
377
+ 'OutputLexer': ('pip._vendor.pygments.lexers.special', 'Text output', ('output',), (), ()),
378
+ 'PacmanConfLexer': ('pip._vendor.pygments.lexers.configs', 'PacmanConf', ('pacmanconf',), ('pacman.conf',), ()),
379
+ 'PanLexer': ('pip._vendor.pygments.lexers.dsls', 'Pan', ('pan',), ('*.pan',), ()),
380
+ 'ParaSailLexer': ('pip._vendor.pygments.lexers.parasail', 'ParaSail', ('parasail',), ('*.psi', '*.psl'), ('text/x-parasail',)),
381
+ 'PawnLexer': ('pip._vendor.pygments.lexers.pawn', 'Pawn', ('pawn',), ('*.p', '*.pwn', '*.inc'), ('text/x-pawn',)),
382
+ 'PddlLexer': ('pip._vendor.pygments.lexers.pddl', 'PDDL', ('pddl',), ('*.pddl',), ()),
383
+ 'PegLexer': ('pip._vendor.pygments.lexers.grammar_notation', 'PEG', ('peg',), ('*.peg',), ('text/x-peg',)),
384
+ 'Perl6Lexer': ('pip._vendor.pygments.lexers.perl', 'Perl6', ('perl6', 'pl6', 'raku'), ('*.pl', '*.pm', '*.nqp', '*.p6', '*.6pl', '*.p6l', '*.pl6', '*.6pm', '*.p6m', '*.pm6', '*.t', '*.raku', '*.rakumod', '*.rakutest', '*.rakudoc'), ('text/x-perl6', 'application/x-perl6')),
385
+ 'PerlLexer': ('pip._vendor.pygments.lexers.perl', 'Perl', ('perl', 'pl'), ('*.pl', '*.pm', '*.t', '*.perl'), ('text/x-perl', 'application/x-perl')),
386
+ 'PhixLexer': ('pip._vendor.pygments.lexers.phix', 'Phix', ('phix',), ('*.exw',), ('text/x-phix',)),
387
+ 'PhpLexer': ('pip._vendor.pygments.lexers.php', 'PHP', ('php', 'php3', 'php4', 'php5'), ('*.php', '*.php[345]', '*.inc'), ('text/x-php',)),
388
+ 'PigLexer': ('pip._vendor.pygments.lexers.jvm', 'Pig', ('pig',), ('*.pig',), ('text/x-pig',)),
389
+ 'PikeLexer': ('pip._vendor.pygments.lexers.c_like', 'Pike', ('pike',), ('*.pike', '*.pmod'), ('text/x-pike',)),
390
+ 'PkgConfigLexer': ('pip._vendor.pygments.lexers.configs', 'PkgConfig', ('pkgconfig',), ('*.pc',), ()),
391
+ 'PlPgsqlLexer': ('pip._vendor.pygments.lexers.sql', 'PL/pgSQL', ('plpgsql',), (), ('text/x-plpgsql',)),
392
+ 'PointlessLexer': ('pip._vendor.pygments.lexers.pointless', 'Pointless', ('pointless',), ('*.ptls',), ()),
393
+ 'PonyLexer': ('pip._vendor.pygments.lexers.pony', 'Pony', ('pony',), ('*.pony',), ()),
394
+ 'PortugolLexer': ('pip._vendor.pygments.lexers.pascal', 'Portugol', ('portugol',), ('*.alg', '*.portugol'), ()),
395
+ 'PostScriptLexer': ('pip._vendor.pygments.lexers.graphics', 'PostScript', ('postscript', 'postscr'), ('*.ps', '*.eps'), ('application/postscript',)),
396
+ 'PostgresConsoleLexer': ('pip._vendor.pygments.lexers.sql', 'PostgreSQL console (psql)', ('psql', 'postgresql-console', 'postgres-console'), (), ('text/x-postgresql-psql',)),
397
+ 'PostgresExplainLexer': ('pip._vendor.pygments.lexers.sql', 'PostgreSQL EXPLAIN dialect', ('postgres-explain',), ('*.explain',), ('text/x-postgresql-explain',)),
398
+ 'PostgresLexer': ('pip._vendor.pygments.lexers.sql', 'PostgreSQL SQL dialect', ('postgresql', 'postgres'), (), ('text/x-postgresql',)),
399
+ 'PovrayLexer': ('pip._vendor.pygments.lexers.graphics', 'POVRay', ('pov',), ('*.pov', '*.inc'), ('text/x-povray',)),
400
+ 'PowerShellLexer': ('pip._vendor.pygments.lexers.shell', 'PowerShell', ('powershell', 'pwsh', 'posh', 'ps1', 'psm1'), ('*.ps1', '*.psm1'), ('text/x-powershell',)),
401
+ 'PowerShellSessionLexer': ('pip._vendor.pygments.lexers.shell', 'PowerShell Session', ('pwsh-session', 'ps1con'), (), ()),
402
+ 'PraatLexer': ('pip._vendor.pygments.lexers.praat', 'Praat', ('praat',), ('*.praat', '*.proc', '*.psc'), ()),
403
+ 'ProcfileLexer': ('pip._vendor.pygments.lexers.procfile', 'Procfile', ('procfile',), ('Procfile',), ()),
404
+ 'PrologLexer': ('pip._vendor.pygments.lexers.prolog', 'Prolog', ('prolog',), ('*.ecl', '*.prolog', '*.pro', '*.pl'), ('text/x-prolog',)),
405
+ 'PromQLLexer': ('pip._vendor.pygments.lexers.promql', 'PromQL', ('promql',), ('*.promql',), ()),
406
+ 'PromelaLexer': ('pip._vendor.pygments.lexers.c_like', 'Promela', ('promela',), ('*.pml', '*.prom', '*.prm', '*.promela', '*.pr', '*.pm'), ('text/x-promela',)),
407
+ 'PropertiesLexer': ('pip._vendor.pygments.lexers.configs', 'Properties', ('properties', 'jproperties'), ('*.properties',), ('text/x-java-properties',)),
408
+ 'ProtoBufLexer': ('pip._vendor.pygments.lexers.dsls', 'Protocol Buffer', ('protobuf', 'proto'), ('*.proto',), ()),
409
+ 'PrqlLexer': ('pip._vendor.pygments.lexers.prql', 'PRQL', ('prql',), ('*.prql',), ('application/prql', 'application/x-prql')),
410
+ 'PsyshConsoleLexer': ('pip._vendor.pygments.lexers.php', 'PsySH console session for PHP', ('psysh',), (), ()),
411
+ 'PtxLexer': ('pip._vendor.pygments.lexers.ptx', 'PTX', ('ptx',), ('*.ptx',), ('text/x-ptx',)),
412
+ 'PugLexer': ('pip._vendor.pygments.lexers.html', 'Pug', ('pug', 'jade'), ('*.pug', '*.jade'), ('text/x-pug', 'text/x-jade')),
413
+ 'PuppetLexer': ('pip._vendor.pygments.lexers.dsls', 'Puppet', ('puppet',), ('*.pp',), ()),
414
+ 'PyPyLogLexer': ('pip._vendor.pygments.lexers.console', 'PyPy Log', ('pypylog', 'pypy'), ('*.pypylog',), ('application/x-pypylog',)),
415
+ 'Python2Lexer': ('pip._vendor.pygments.lexers.python', 'Python 2.x', ('python2', 'py2'), (), ('text/x-python2', 'application/x-python2')),
416
+ 'Python2TracebackLexer': ('pip._vendor.pygments.lexers.python', 'Python 2.x Traceback', ('py2tb',), ('*.py2tb',), ('text/x-python2-traceback',)),
417
+ 'PythonConsoleLexer': ('pip._vendor.pygments.lexers.python', 'Python console session', ('pycon', 'python-console'), (), ('text/x-python-doctest',)),
418
+ 'PythonLexer': ('pip._vendor.pygments.lexers.python', 'Python', ('python', 'py', 'sage', 'python3', 'py3', 'bazel', 'starlark', 'pyi'), ('*.py', '*.pyw', '*.pyi', '*.jy', '*.sage', '*.sc', 'SConstruct', 'SConscript', '*.bzl', 'BUCK', 'BUILD', 'BUILD.bazel', 'WORKSPACE', '*.tac', '*.pye'), ('text/x-python', 'application/x-python', 'text/x-python3', 'application/x-python3')),
419
+ 'PythonTracebackLexer': ('pip._vendor.pygments.lexers.python', 'Python Traceback', ('pytb', 'py3tb'), ('*.pytb', '*.py3tb'), ('text/x-python-traceback', 'text/x-python3-traceback')),
420
+ 'PythonUL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'Python+UL4', ('py+ul4',), ('*.pyul4',), ()),
421
+ 'QBasicLexer': ('pip._vendor.pygments.lexers.basic', 'QBasic', ('qbasic', 'basic'), ('*.BAS', '*.bas'), ('text/basic',)),
422
+ 'QLexer': ('pip._vendor.pygments.lexers.q', 'Q', ('q',), ('*.q',), ()),
423
+ 'QVToLexer': ('pip._vendor.pygments.lexers.qvt', 'QVTO', ('qvto', 'qvt'), ('*.qvto',), ()),
424
+ 'QlikLexer': ('pip._vendor.pygments.lexers.qlik', 'Qlik', ('qlik', 'qlikview', 'qliksense', 'qlikscript'), ('*.qvs', '*.qvw'), ()),
425
+ 'QmlLexer': ('pip._vendor.pygments.lexers.webmisc', 'QML', ('qml', 'qbs'), ('*.qml', '*.qbs'), ('application/x-qml', 'application/x-qt.qbs+qml')),
426
+ 'RConsoleLexer': ('pip._vendor.pygments.lexers.r', 'RConsole', ('rconsole', 'rout'), ('*.Rout',), ()),
427
+ 'RNCCompactLexer': ('pip._vendor.pygments.lexers.rnc', 'Relax-NG Compact', ('rng-compact', 'rnc'), ('*.rnc',), ()),
428
+ 'RPMSpecLexer': ('pip._vendor.pygments.lexers.installers', 'RPMSpec', ('spec',), ('*.spec',), ('text/x-rpm-spec',)),
429
+ 'RacketLexer': ('pip._vendor.pygments.lexers.lisp', 'Racket', ('racket', 'rkt'), ('*.rkt', '*.rktd', '*.rktl'), ('text/x-racket', 'application/x-racket')),
430
+ 'RagelCLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in C Host', ('ragel-c',), ('*.rl',), ()),
431
+ 'RagelCppLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in CPP Host', ('ragel-cpp',), ('*.rl',), ()),
432
+ 'RagelDLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in D Host', ('ragel-d',), ('*.rl',), ()),
433
+ 'RagelEmbeddedLexer': ('pip._vendor.pygments.lexers.parsers', 'Embedded Ragel', ('ragel-em',), ('*.rl',), ()),
434
+ 'RagelJavaLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in Java Host', ('ragel-java',), ('*.rl',), ()),
435
+ 'RagelLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel', ('ragel',), (), ()),
436
+ 'RagelObjectiveCLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in Objective C Host', ('ragel-objc',), ('*.rl',), ()),
437
+ 'RagelRubyLexer': ('pip._vendor.pygments.lexers.parsers', 'Ragel in Ruby Host', ('ragel-ruby', 'ragel-rb'), ('*.rl',), ()),
438
+ 'RawTokenLexer': ('pip._vendor.pygments.lexers.special', 'Raw token data', (), (), ('application/x-pygments-tokens',)),
439
+ 'RdLexer': ('pip._vendor.pygments.lexers.r', 'Rd', ('rd',), ('*.Rd',), ('text/x-r-doc',)),
440
+ 'ReasonLexer': ('pip._vendor.pygments.lexers.ml', 'ReasonML', ('reasonml', 'reason'), ('*.re', '*.rei'), ('text/x-reasonml',)),
441
+ 'RebolLexer': ('pip._vendor.pygments.lexers.rebol', 'REBOL', ('rebol',), ('*.r', '*.r3', '*.reb'), ('text/x-rebol',)),
442
+ 'RedLexer': ('pip._vendor.pygments.lexers.rebol', 'Red', ('red', 'red/system'), ('*.red', '*.reds'), ('text/x-red', 'text/x-red-system')),
443
+ 'RedcodeLexer': ('pip._vendor.pygments.lexers.esoteric', 'Redcode', ('redcode',), ('*.cw',), ()),
444
+ 'RegeditLexer': ('pip._vendor.pygments.lexers.configs', 'reg', ('registry',), ('*.reg',), ('text/x-windows-registry',)),
445
+ 'RegoLexer': ('pip._vendor.pygments.lexers.rego', 'Rego', ('rego',), ('*.rego',), ('text/x-rego',)),
446
+ 'RellLexer': ('pip._vendor.pygments.lexers.rell', 'Rell', ('rell',), ('*.rell',), ('text/x-rell',)),
447
+ 'ResourceLexer': ('pip._vendor.pygments.lexers.resource', 'ResourceBundle', ('resourcebundle', 'resource'), (), ()),
448
+ 'RexxLexer': ('pip._vendor.pygments.lexers.scripting', 'Rexx', ('rexx', 'arexx'), ('*.rexx', '*.rex', '*.rx', '*.arexx'), ('text/x-rexx',)),
449
+ 'RhtmlLexer': ('pip._vendor.pygments.lexers.templates', 'RHTML', ('rhtml', 'html+erb', 'html+ruby'), ('*.rhtml',), ('text/html+ruby',)),
450
+ 'RideLexer': ('pip._vendor.pygments.lexers.ride', 'Ride', ('ride',), ('*.ride',), ('text/x-ride',)),
451
+ 'RitaLexer': ('pip._vendor.pygments.lexers.rita', 'Rita', ('rita',), ('*.rita',), ('text/rita',)),
452
+ 'RoboconfGraphLexer': ('pip._vendor.pygments.lexers.roboconf', 'Roboconf Graph', ('roboconf-graph',), ('*.graph',), ()),
453
+ 'RoboconfInstancesLexer': ('pip._vendor.pygments.lexers.roboconf', 'Roboconf Instances', ('roboconf-instances',), ('*.instances',), ()),
454
+ 'RobotFrameworkLexer': ('pip._vendor.pygments.lexers.robotframework', 'RobotFramework', ('robotframework',), ('*.robot', '*.resource'), ('text/x-robotframework',)),
455
+ 'RocqLexer': ('pip._vendor.pygments.lexers.theorem', 'Rocq Prover', ('coq', 'rocq', 'rocq-prover'), ('*.v',), ('text/x-coq', 'text/x-rocq')),
456
+ 'RqlLexer': ('pip._vendor.pygments.lexers.sql', 'RQL', ('rql',), ('*.rql',), ('text/x-rql',)),
457
+ 'RslLexer': ('pip._vendor.pygments.lexers.dsls', 'RSL', ('rsl',), ('*.rsl',), ('text/rsl',)),
458
+ 'RstLexer': ('pip._vendor.pygments.lexers.markup', 'reStructuredText', ('restructuredtext', 'rst', 'rest'), ('*.rst', '*.rest'), ('text/x-rst', 'text/prs.fallenstein.rst')),
459
+ 'RtsLexer': ('pip._vendor.pygments.lexers.trafficscript', 'TrafficScript', ('trafficscript', 'rts'), ('*.rts',), ()),
460
+ 'RubyConsoleLexer': ('pip._vendor.pygments.lexers.ruby', 'Ruby irb session', ('rbcon', 'irb'), (), ('text/x-ruby-shellsession',)),
461
+ 'RubyLexer': ('pip._vendor.pygments.lexers.ruby', 'Ruby', ('ruby', 'rb', 'duby'), ('*.rb', '*.rbw', 'Rakefile', '*.rake', '*.gemspec', '*.rbx', '*.duby', 'Gemfile', 'Vagrantfile'), ('text/x-ruby', 'application/x-ruby')),
462
+ 'RustLexer': ('pip._vendor.pygments.lexers.rust', 'Rust', ('rust', 'rs'), ('*.rs', '*.rs.in'), ('text/rust', 'text/x-rust')),
463
+ 'SASLexer': ('pip._vendor.pygments.lexers.sas', 'SAS', ('sas',), ('*.SAS', '*.sas'), ('text/x-sas', 'text/sas', 'application/x-sas')),
464
+ 'SLexer': ('pip._vendor.pygments.lexers.r', 'S', ('splus', 's', 'r'), ('*.S', '*.R', '.Rhistory', '.Rprofile', '.Renviron'), ('text/S-plus', 'text/S', 'text/x-r-source', 'text/x-r', 'text/x-R', 'text/x-r-history', 'text/x-r-profile')),
465
+ 'SMLLexer': ('pip._vendor.pygments.lexers.ml', 'Standard ML', ('sml',), ('*.sml', '*.sig', '*.fun'), ('text/x-standardml', 'application/x-standardml')),
466
+ 'SNBTLexer': ('pip._vendor.pygments.lexers.minecraft', 'SNBT', ('snbt',), ('*.snbt',), ('text/snbt',)),
467
+ 'SarlLexer': ('pip._vendor.pygments.lexers.jvm', 'SARL', ('sarl',), ('*.sarl',), ('text/x-sarl',)),
468
+ 'SassLexer': ('pip._vendor.pygments.lexers.css', 'Sass', ('sass',), ('*.sass',), ('text/x-sass',)),
469
+ 'SaviLexer': ('pip._vendor.pygments.lexers.savi', 'Savi', ('savi',), ('*.savi',), ()),
470
+ 'ScalaLexer': ('pip._vendor.pygments.lexers.jvm', 'Scala', ('scala',), ('*.scala',), ('text/x-scala',)),
471
+ 'ScamlLexer': ('pip._vendor.pygments.lexers.html', 'Scaml', ('scaml',), ('*.scaml',), ('text/x-scaml',)),
472
+ 'ScdocLexer': ('pip._vendor.pygments.lexers.scdoc', 'scdoc', ('scdoc', 'scd'), ('*.scd', '*.scdoc'), ()),
473
+ 'SchemeLexer': ('pip._vendor.pygments.lexers.lisp', 'Scheme', ('scheme', 'scm'), ('*.scm', '*.ss'), ('text/x-scheme', 'application/x-scheme')),
474
+ 'ScilabLexer': ('pip._vendor.pygments.lexers.matlab', 'Scilab', ('scilab',), ('*.sci', '*.sce', '*.tst'), ('text/scilab',)),
475
+ 'ScssLexer': ('pip._vendor.pygments.lexers.css', 'SCSS', ('scss',), ('*.scss',), ('text/x-scss',)),
476
+ 'SedLexer': ('pip._vendor.pygments.lexers.textedit', 'Sed', ('sed', 'gsed', 'ssed'), ('*.sed', '*.[gs]sed'), ('text/x-sed',)),
477
+ 'ShExCLexer': ('pip._vendor.pygments.lexers.rdf', 'ShExC', ('shexc', 'shex'), ('*.shex',), ('text/shex',)),
478
+ 'ShenLexer': ('pip._vendor.pygments.lexers.lisp', 'Shen', ('shen',), ('*.shen',), ('text/x-shen', 'application/x-shen')),
479
+ 'SieveLexer': ('pip._vendor.pygments.lexers.sieve', 'Sieve', ('sieve',), ('*.siv', '*.sieve'), ()),
480
+ 'SilverLexer': ('pip._vendor.pygments.lexers.verification', 'Silver', ('silver',), ('*.sil', '*.vpr'), ()),
481
+ 'SingularityLexer': ('pip._vendor.pygments.lexers.configs', 'Singularity', ('singularity',), ('*.def', 'Singularity'), ()),
482
+ 'SlashLexer': ('pip._vendor.pygments.lexers.slash', 'Slash', ('slash',), ('*.sla',), ()),
483
+ 'SlimLexer': ('pip._vendor.pygments.lexers.webmisc', 'Slim', ('slim',), ('*.slim',), ('text/x-slim',)),
484
+ 'SlurmBashLexer': ('pip._vendor.pygments.lexers.shell', 'Slurm', ('slurm', 'sbatch'), ('*.sl',), ()),
485
+ 'SmaliLexer': ('pip._vendor.pygments.lexers.dalvik', 'Smali', ('smali',), ('*.smali',), ('text/smali',)),
486
+ 'SmalltalkLexer': ('pip._vendor.pygments.lexers.smalltalk', 'Smalltalk', ('smalltalk', 'squeak', 'st'), ('*.st',), ('text/x-smalltalk',)),
487
+ 'SmartGameFormatLexer': ('pip._vendor.pygments.lexers.sgf', 'SmartGameFormat', ('sgf',), ('*.sgf',), ()),
488
+ 'SmartyLexer': ('pip._vendor.pygments.lexers.templates', 'Smarty', ('smarty',), ('*.tpl',), ('application/x-smarty',)),
489
+ 'SmithyLexer': ('pip._vendor.pygments.lexers.smithy', 'Smithy', ('smithy',), ('*.smithy',), ()),
490
+ 'SnobolLexer': ('pip._vendor.pygments.lexers.snobol', 'Snobol', ('snobol',), ('*.snobol',), ('text/x-snobol',)),
491
+ 'SnowballLexer': ('pip._vendor.pygments.lexers.dsls', 'Snowball', ('snowball',), ('*.sbl',), ()),
492
+ 'SolidityLexer': ('pip._vendor.pygments.lexers.solidity', 'Solidity', ('solidity',), ('*.sol',), ()),
493
+ 'SoongLexer': ('pip._vendor.pygments.lexers.soong', 'Soong', ('androidbp', 'bp', 'soong'), ('Android.bp',), ()),
494
+ 'SophiaLexer': ('pip._vendor.pygments.lexers.sophia', 'Sophia', ('sophia',), ('*.aes',), ()),
495
+ 'SourcePawnLexer': ('pip._vendor.pygments.lexers.pawn', 'SourcePawn', ('sp',), ('*.sp',), ('text/x-sourcepawn',)),
496
+ 'SourcesListLexer': ('pip._vendor.pygments.lexers.installers', 'Debian Sourcelist', ('debsources', 'sourceslist', 'sources.list'), ('sources.list',), ()),
497
+ 'SparqlLexer': ('pip._vendor.pygments.lexers.rdf', 'SPARQL', ('sparql',), ('*.rq', '*.sparql'), ('application/sparql-query',)),
498
+ 'SpiceLexer': ('pip._vendor.pygments.lexers.spice', 'Spice', ('spice', 'spicelang'), ('*.spice',), ('text/x-spice',)),
499
+ 'SqlJinjaLexer': ('pip._vendor.pygments.lexers.templates', 'SQL+Jinja', ('sql+jinja',), ('*.sql', '*.sql.j2', '*.sql.jinja2'), ()),
500
+ 'SqlLexer': ('pip._vendor.pygments.lexers.sql', 'SQL', ('sql',), ('*.sql',), ('text/x-sql',)),
501
+ 'SqliteConsoleLexer': ('pip._vendor.pygments.lexers.sql', 'sqlite3con', ('sqlite3',), ('*.sqlite3-console',), ('text/x-sqlite3-console',)),
502
+ 'SquidConfLexer': ('pip._vendor.pygments.lexers.configs', 'SquidConf', ('squidconf', 'squid.conf', 'squid'), ('squid.conf',), ('text/x-squidconf',)),
503
+ 'SrcinfoLexer': ('pip._vendor.pygments.lexers.srcinfo', 'Srcinfo', ('srcinfo',), ('.SRCINFO',), ()),
504
+ 'SspLexer': ('pip._vendor.pygments.lexers.templates', 'Scalate Server Page', ('ssp',), ('*.ssp',), ('application/x-ssp',)),
505
+ 'StanLexer': ('pip._vendor.pygments.lexers.modeling', 'Stan', ('stan',), ('*.stan',), ()),
506
+ 'StataLexer': ('pip._vendor.pygments.lexers.stata', 'Stata', ('stata', 'do'), ('*.do', '*.ado'), ('text/x-stata', 'text/stata', 'application/x-stata')),
507
+ 'SuperColliderLexer': ('pip._vendor.pygments.lexers.supercollider', 'SuperCollider', ('supercollider', 'sc'), ('*.sc', '*.scd'), ('application/supercollider', 'text/supercollider')),
508
+ 'SwiftLexer': ('pip._vendor.pygments.lexers.objective', 'Swift', ('swift',), ('*.swift',), ('text/x-swift',)),
509
+ 'SwigLexer': ('pip._vendor.pygments.lexers.c_like', 'SWIG', ('swig',), ('*.swg', '*.i'), ('text/swig',)),
510
+ 'SystemVerilogLexer': ('pip._vendor.pygments.lexers.hdl', 'systemverilog', ('systemverilog', 'sv'), ('*.sv', '*.svh'), ('text/x-systemverilog',)),
511
+ 'SystemdLexer': ('pip._vendor.pygments.lexers.configs', 'Systemd', ('systemd',), ('*.service', '*.socket', '*.device', '*.mount', '*.automount', '*.swap', '*.target', '*.path', '*.timer', '*.slice', '*.scope'), ()),
512
+ 'TAPLexer': ('pip._vendor.pygments.lexers.testing', 'TAP', ('tap',), ('*.tap',), ()),
513
+ 'TNTLexer': ('pip._vendor.pygments.lexers.tnt', 'Typographic Number Theory', ('tnt',), ('*.tnt',), ()),
514
+ 'TOMLLexer': ('pip._vendor.pygments.lexers.configs', 'TOML', ('toml',), ('*.toml', 'Pipfile', 'poetry.lock'), ('application/toml',)),
515
+ 'TableGenLexer': ('pip._vendor.pygments.lexers.tablegen', 'TableGen', ('tablegen', 'td'), ('*.td',), ()),
516
+ 'TactLexer': ('pip._vendor.pygments.lexers.tact', 'Tact', ('tact',), ('*.tact',), ()),
517
+ 'Tads3Lexer': ('pip._vendor.pygments.lexers.int_fiction', 'TADS 3', ('tads3',), ('*.t',), ()),
518
+ 'TalLexer': ('pip._vendor.pygments.lexers.tal', 'Tal', ('tal', 'uxntal'), ('*.tal',), ('text/x-uxntal',)),
519
+ 'TasmLexer': ('pip._vendor.pygments.lexers.asm', 'TASM', ('tasm',), ('*.asm', '*.ASM', '*.tasm'), ('text/x-tasm',)),
520
+ 'TclLexer': ('pip._vendor.pygments.lexers.tcl', 'Tcl', ('tcl',), ('*.tcl', '*.rvt'), ('text/x-tcl', 'text/x-script.tcl', 'application/x-tcl')),
521
+ 'TcshLexer': ('pip._vendor.pygments.lexers.shell', 'Tcsh', ('tcsh', 'csh'), ('*.tcsh', '*.csh'), ('application/x-csh',)),
522
+ 'TcshSessionLexer': ('pip._vendor.pygments.lexers.shell', 'Tcsh Session', ('tcshcon',), (), ()),
523
+ 'TeaTemplateLexer': ('pip._vendor.pygments.lexers.templates', 'Tea', ('tea',), ('*.tea',), ('text/x-tea',)),
524
+ 'TealLexer': ('pip._vendor.pygments.lexers.teal', 'teal', ('teal',), ('*.teal',), ()),
525
+ 'TeraTermLexer': ('pip._vendor.pygments.lexers.teraterm', 'Tera Term macro', ('teratermmacro', 'teraterm', 'ttl'), ('*.ttl',), ('text/x-teratermmacro',)),
526
+ 'TermcapLexer': ('pip._vendor.pygments.lexers.configs', 'Termcap', ('termcap',), ('termcap', 'termcap.src'), ()),
527
+ 'TerminfoLexer': ('pip._vendor.pygments.lexers.configs', 'Terminfo', ('terminfo',), ('terminfo', 'terminfo.src'), ()),
528
+ 'TerraformLexer': ('pip._vendor.pygments.lexers.configs', 'Terraform', ('terraform', 'tf', 'hcl'), ('*.tf', '*.hcl'), ('application/x-tf', 'application/x-terraform')),
529
+ 'TexLexer': ('pip._vendor.pygments.lexers.markup', 'TeX', ('tex', 'latex'), ('*.tex', '*.aux', '*.toc'), ('text/x-tex', 'text/x-latex')),
530
+ 'TextLexer': ('pip._vendor.pygments.lexers.special', 'Text only', ('text',), ('*.txt',), ('text/plain',)),
531
+ 'ThingsDBLexer': ('pip._vendor.pygments.lexers.thingsdb', 'ThingsDB', ('ti', 'thingsdb'), ('*.ti',), ()),
532
+ 'ThriftLexer': ('pip._vendor.pygments.lexers.dsls', 'Thrift', ('thrift',), ('*.thrift',), ('application/x-thrift',)),
533
+ 'TiddlyWiki5Lexer': ('pip._vendor.pygments.lexers.markup', 'tiddler', ('tid',), ('*.tid',), ('text/vnd.tiddlywiki',)),
534
+ 'TlbLexer': ('pip._vendor.pygments.lexers.tlb', 'Tl-b', ('tlb',), ('*.tlb',), ()),
535
+ 'TlsLexer': ('pip._vendor.pygments.lexers.tls', 'TLS Presentation Language', ('tls',), (), ()),
536
+ 'TodotxtLexer': ('pip._vendor.pygments.lexers.textfmts', 'Todotxt', ('todotxt',), ('todo.txt', '*.todotxt'), ('text/x-todo',)),
537
+ 'TransactSqlLexer': ('pip._vendor.pygments.lexers.sql', 'Transact-SQL', ('tsql', 't-sql'), ('*.sql',), ('text/x-tsql',)),
538
+ 'TreetopLexer': ('pip._vendor.pygments.lexers.parsers', 'Treetop', ('treetop',), ('*.treetop', '*.tt'), ()),
539
+ 'TsxLexer': ('pip._vendor.pygments.lexers.jsx', 'TSX', ('tsx',), ('*.tsx',), ('text/typescript-tsx',)),
540
+ 'TurtleLexer': ('pip._vendor.pygments.lexers.rdf', 'Turtle', ('turtle',), ('*.ttl',), ('text/turtle', 'application/x-turtle')),
541
+ 'TwigHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Twig', ('html+twig',), ('*.twig',), ('text/html+twig',)),
542
+ 'TwigLexer': ('pip._vendor.pygments.lexers.templates', 'Twig', ('twig',), (), ('application/x-twig',)),
543
+ 'TypeScriptLexer': ('pip._vendor.pygments.lexers.javascript', 'TypeScript', ('typescript', 'ts'), ('*.ts',), ('application/x-typescript', 'text/x-typescript')),
544
+ 'TypoScriptCssDataLexer': ('pip._vendor.pygments.lexers.typoscript', 'TypoScriptCssData', ('typoscriptcssdata',), (), ()),
545
+ 'TypoScriptHtmlDataLexer': ('pip._vendor.pygments.lexers.typoscript', 'TypoScriptHtmlData', ('typoscripthtmldata',), (), ()),
546
+ 'TypoScriptLexer': ('pip._vendor.pygments.lexers.typoscript', 'TypoScript', ('typoscript',), ('*.typoscript',), ('text/x-typoscript',)),
547
+ 'TypstLexer': ('pip._vendor.pygments.lexers.typst', 'Typst', ('typst',), ('*.typ',), ('text/x-typst',)),
548
+ 'UL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'UL4', ('ul4',), ('*.ul4',), ()),
549
+ 'UcodeLexer': ('pip._vendor.pygments.lexers.unicon', 'ucode', ('ucode',), ('*.u', '*.u1', '*.u2'), ()),
550
+ 'UniconLexer': ('pip._vendor.pygments.lexers.unicon', 'Unicon', ('unicon',), ('*.icn',), ('text/unicon',)),
551
+ 'UnixConfigLexer': ('pip._vendor.pygments.lexers.configs', 'Unix/Linux config files', ('unixconfig', 'linuxconfig'), (), ()),
552
+ 'UrbiscriptLexer': ('pip._vendor.pygments.lexers.urbi', 'UrbiScript', ('urbiscript',), ('*.u',), ('application/x-urbiscript',)),
553
+ 'UrlEncodedLexer': ('pip._vendor.pygments.lexers.html', 'urlencoded', ('urlencoded',), (), ('application/x-www-form-urlencoded',)),
554
+ 'UsdLexer': ('pip._vendor.pygments.lexers.usd', 'USD', ('usd', 'usda'), ('*.usd', '*.usda'), ()),
555
+ 'VBScriptLexer': ('pip._vendor.pygments.lexers.basic', 'VBScript', ('vbscript',), ('*.vbs', '*.VBS'), ()),
556
+ 'VCLLexer': ('pip._vendor.pygments.lexers.varnish', 'VCL', ('vcl',), ('*.vcl',), ('text/x-vclsrc',)),
557
+ 'VCLSnippetLexer': ('pip._vendor.pygments.lexers.varnish', 'VCLSnippets', ('vclsnippets', 'vclsnippet'), (), ('text/x-vclsnippet',)),
558
+ 'VCTreeStatusLexer': ('pip._vendor.pygments.lexers.console', 'VCTreeStatus', ('vctreestatus',), (), ()),
559
+ 'VGLLexer': ('pip._vendor.pygments.lexers.dsls', 'VGL', ('vgl',), ('*.rpf',), ()),
560
+ 'ValaLexer': ('pip._vendor.pygments.lexers.c_like', 'Vala', ('vala', 'vapi'), ('*.vala', '*.vapi'), ('text/x-vala',)),
561
+ 'VbNetAspxLexer': ('pip._vendor.pygments.lexers.dotnet', 'aspx-vb', ('aspx-vb',), ('*.aspx', '*.asax', '*.ascx', '*.ashx', '*.asmx', '*.axd'), ()),
562
+ 'VbNetLexer': ('pip._vendor.pygments.lexers.dotnet', 'VB.net', ('vb.net', 'vbnet', 'lobas', 'oobas', 'sobas', 'visual-basic', 'visualbasic'), ('*.vb', '*.bas'), ('text/x-vbnet', 'text/x-vba')),
563
+ 'VelocityHtmlLexer': ('pip._vendor.pygments.lexers.templates', 'HTML+Velocity', ('html+velocity',), (), ('text/html+velocity',)),
564
+ 'VelocityLexer': ('pip._vendor.pygments.lexers.templates', 'Velocity', ('velocity',), ('*.vm', '*.fhtml'), ()),
565
+ 'VelocityXmlLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Velocity', ('xml+velocity',), (), ('application/xml+velocity',)),
566
+ 'VerifpalLexer': ('pip._vendor.pygments.lexers.verifpal', 'Verifpal', ('verifpal',), ('*.vp',), ('text/x-verifpal',)),
567
+ 'VerilogLexer': ('pip._vendor.pygments.lexers.hdl', 'verilog', ('verilog', 'v'), ('*.v',), ('text/x-verilog',)),
568
+ 'VhdlLexer': ('pip._vendor.pygments.lexers.hdl', 'vhdl', ('vhdl',), ('*.vhdl', '*.vhd'), ('text/x-vhdl',)),
569
+ 'VimLexer': ('pip._vendor.pygments.lexers.textedit', 'VimL', ('vim',), ('*.vim', '.vimrc', '.exrc', '.gvimrc', '_vimrc', '_exrc', '_gvimrc', 'vimrc', 'gvimrc'), ('text/x-vim',)),
570
+ 'VisualPrologGrammarLexer': ('pip._vendor.pygments.lexers.vip', 'Visual Prolog Grammar', ('visualprologgrammar',), ('*.vipgrm',), ()),
571
+ 'VisualPrologLexer': ('pip._vendor.pygments.lexers.vip', 'Visual Prolog', ('visualprolog',), ('*.pro', '*.cl', '*.i', '*.pack', '*.ph'), ()),
572
+ 'VueLexer': ('pip._vendor.pygments.lexers.html', 'Vue', ('vue',), ('*.vue',), ()),
573
+ 'VyperLexer': ('pip._vendor.pygments.lexers.vyper', 'Vyper', ('vyper',), ('*.vy',), ()),
574
+ 'WDiffLexer': ('pip._vendor.pygments.lexers.diff', 'WDiff', ('wdiff',), ('*.wdiff',), ()),
575
+ 'WatLexer': ('pip._vendor.pygments.lexers.webassembly', 'WebAssembly', ('wast', 'wat'), ('*.wat', '*.wast'), ()),
576
+ 'WebIDLLexer': ('pip._vendor.pygments.lexers.webidl', 'Web IDL', ('webidl',), ('*.webidl',), ()),
577
+ 'WgslLexer': ('pip._vendor.pygments.lexers.wgsl', 'WebGPU Shading Language', ('wgsl',), ('*.wgsl',), ('text/wgsl',)),
578
+ 'WhileyLexer': ('pip._vendor.pygments.lexers.whiley', 'Whiley', ('whiley',), ('*.whiley',), ('text/x-whiley',)),
579
+ 'WikitextLexer': ('pip._vendor.pygments.lexers.markup', 'Wikitext', ('wikitext', 'mediawiki'), (), ('text/x-wiki',)),
580
+ 'WoWTocLexer': ('pip._vendor.pygments.lexers.wowtoc', 'World of Warcraft TOC', ('wowtoc',), ('*.toc',), ()),
581
+ 'WrenLexer': ('pip._vendor.pygments.lexers.wren', 'Wren', ('wren',), ('*.wren',), ()),
582
+ 'X10Lexer': ('pip._vendor.pygments.lexers.x10', 'X10', ('x10', 'xten'), ('*.x10',), ('text/x-x10',)),
583
+ 'XMLUL4Lexer': ('pip._vendor.pygments.lexers.ul4', 'XML+UL4', ('xml+ul4',), ('*.xmlul4',), ()),
584
+ 'XQueryLexer': ('pip._vendor.pygments.lexers.webmisc', 'XQuery', ('xquery', 'xqy', 'xq', 'xql', 'xqm'), ('*.xqy', '*.xquery', '*.xq', '*.xql', '*.xqm'), ('text/xquery', 'application/xquery')),
585
+ 'XmlDjangoLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Django/Jinja', ('xml+django', 'xml+jinja'), ('*.xml.j2', '*.xml.jinja2'), ('application/xml+django', 'application/xml+jinja')),
586
+ 'XmlErbLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Ruby', ('xml+ruby', 'xml+erb'), (), ('application/xml+ruby',)),
587
+ 'XmlLexer': ('pip._vendor.pygments.lexers.html', 'XML', ('xml',), ('*.xml', '*.xsl', '*.rss', '*.xslt', '*.xsd', '*.wsdl', '*.wsf', '*.xbrl', '*.pom'), ('text/xml', 'application/xml', 'image/svg+xml', 'application/rss+xml', 'application/atom+xml')),
588
+ 'XmlPhpLexer': ('pip._vendor.pygments.lexers.templates', 'XML+PHP', ('xml+php',), (), ('application/xml+php',)),
589
+ 'XmlSmartyLexer': ('pip._vendor.pygments.lexers.templates', 'XML+Smarty', ('xml+smarty',), (), ('application/xml+smarty',)),
590
+ 'XorgLexer': ('pip._vendor.pygments.lexers.xorg', 'Xorg', ('xorg.conf',), ('xorg.conf',), ()),
591
+ 'XppLexer': ('pip._vendor.pygments.lexers.dotnet', 'X++', ('xpp', 'x++'), ('*.xpp',), ()),
592
+ 'XsltLexer': ('pip._vendor.pygments.lexers.html', 'XSLT', ('xslt',), ('*.xsl', '*.xslt', '*.xpl'), ('application/xsl+xml', 'application/xslt+xml')),
593
+ 'XtendLexer': ('pip._vendor.pygments.lexers.jvm', 'Xtend', ('xtend',), ('*.xtend',), ('text/x-xtend',)),
594
+ 'XtlangLexer': ('pip._vendor.pygments.lexers.lisp', 'xtlang', ('extempore',), ('*.xtm',), ()),
595
+ 'YamlJinjaLexer': ('pip._vendor.pygments.lexers.templates', 'YAML+Jinja', ('yaml+jinja', 'salt', 'sls'), ('*.sls', '*.yaml.j2', '*.yml.j2', '*.yaml.jinja2', '*.yml.jinja2'), ('text/x-yaml+jinja', 'text/x-sls')),
596
+ 'YamlLexer': ('pip._vendor.pygments.lexers.data', 'YAML', ('yaml',), ('*.yaml', '*.yml'), ('text/x-yaml',)),
597
+ 'YangLexer': ('pip._vendor.pygments.lexers.yang', 'YANG', ('yang',), ('*.yang',), ('application/yang',)),
598
+ 'YaraLexer': ('pip._vendor.pygments.lexers.yara', 'YARA', ('yara', 'yar'), ('*.yar',), ('text/x-yara',)),
599
+ 'ZeekLexer': ('pip._vendor.pygments.lexers.dsls', 'Zeek', ('zeek', 'bro'), ('*.zeek', '*.bro'), ()),
600
+ 'ZephirLexer': ('pip._vendor.pygments.lexers.php', 'Zephir', ('zephir',), ('*.zep',), ()),
601
+ 'ZigLexer': ('pip._vendor.pygments.lexers.zig', 'Zig', ('zig',), ('*.zig',), ('text/zig',)),
602
+ 'apdlexer': ('pip._vendor.pygments.lexers.apdlexer', 'ANSYS parametric design language', ('ansys', 'apdl'), ('*.ans',), ()),
603
+ }
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/lexers/python.py ADDED
@@ -0,0 +1,1204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.lexers.python
3
+ ~~~~~~~~~~~~~~~~~~~~~~
4
+
5
+ Lexers for Python and related languages.
6
+
7
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import keyword
12
+
13
+ from pip._vendor.pygments.lexer import DelegatingLexer, RegexLexer, include, \
14
+ bygroups, using, default, words, combined, this
15
+ from pip._vendor.pygments.util import get_bool_opt, shebang_matches
16
+ from pip._vendor.pygments.token import Text, Comment, Operator, Keyword, Name, String, \
17
+ Number, Punctuation, Generic, Other, Error, Whitespace
18
+ from pip._vendor.pygments import unistring as uni
19
+
20
+ __all__ = ['PythonLexer', 'PythonConsoleLexer', 'PythonTracebackLexer',
21
+ 'Python2Lexer', 'Python2TracebackLexer',
22
+ 'CythonLexer', 'DgLexer', 'NumPyLexer']
23
+
24
+
25
+ class PythonLexer(RegexLexer):
26
+ """
27
+ For Python source code (version 3.x).
28
+
29
+ .. versionchanged:: 2.5
30
+ This is now the default ``PythonLexer``. It is still available as the
31
+ alias ``Python3Lexer``.
32
+ """
33
+
34
+ name = 'Python'
35
+ url = 'https://www.python.org'
36
+ aliases = ['python', 'py', 'sage', 'python3', 'py3', 'bazel', 'starlark', 'pyi']
37
+ filenames = [
38
+ '*.py',
39
+ '*.pyw',
40
+ # Type stubs
41
+ '*.pyi',
42
+ # Jython
43
+ '*.jy',
44
+ # Sage
45
+ '*.sage',
46
+ # SCons
47
+ '*.sc',
48
+ 'SConstruct',
49
+ 'SConscript',
50
+ # Skylark/Starlark (used by Bazel, Buck, and Pants)
51
+ '*.bzl',
52
+ 'BUCK',
53
+ 'BUILD',
54
+ 'BUILD.bazel',
55
+ 'WORKSPACE',
56
+ # Twisted Application infrastructure
57
+ '*.tac',
58
+ # Execubot level format
59
+ '*.pye',
60
+ ]
61
+ mimetypes = ['text/x-python', 'application/x-python',
62
+ 'text/x-python3', 'application/x-python3']
63
+ version_added = '0.10'
64
+
65
+ uni_name = f"[{uni.xid_start}][{uni.xid_continue}]*"
66
+
67
+ def innerstring_rules(ttype):
68
+ return [
69
+ # the old style '%s' % (...) string formatting (still valid in Py3)
70
+ (r'%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?'
71
+ '[hlL]?[E-GXc-giorsaux%]', String.Interpol),
72
+ # the new style '{}'.format(...) string formatting
73
+ (r'\{'
74
+ r'((\w+)((\.\w+)|(\[[^\]]+\]))*)?' # field name
75
+ r'(\![sra])?' # conversion
76
+ r'(\:(.?[<>=\^])?[-+ ]?#?0?(\d+)?,?(\.\d+)?[E-GXb-gnosx%]?)?'
77
+ r'\}', String.Interpol),
78
+
79
+ # backslashes, quotes and formatting signs must be parsed one at a time
80
+ (r'[^\\\'"%{\n]+', ttype),
81
+ (r'[\'"\\]', ttype),
82
+ # unhandled string formatting sign
83
+ (r'%|(\{{1,2})', ttype)
84
+ # newlines are an error (use "nl" state)
85
+ ]
86
+
87
+ def fstring_rules(ttype):
88
+ return [
89
+ # Assuming that a '}' is the closing brace after format specifier.
90
+ # Sadly, this means that we won't detect syntax error. But it's
91
+ # more important to parse correct syntax correctly, than to
92
+ # highlight invalid syntax.
93
+ (r'\}', String.Interpol),
94
+ (r'\{', String.Interpol, 'expr-inside-fstring'),
95
+ # backslashes, quotes and formatting signs must be parsed one at a time
96
+ (r'[^\\\'"{}\n]+', ttype),
97
+ (r'[\'"\\]', ttype),
98
+ # newlines are an error (use "nl" state)
99
+ ]
100
+
101
+ tokens = {
102
+ 'root': [
103
+ (r'\n', Whitespace),
104
+ (r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")',
105
+ bygroups(Whitespace, String.Affix, String.Doc)),
106
+ (r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')",
107
+ bygroups(Whitespace, String.Affix, String.Doc)),
108
+ (r'\A#!.+$', Comment.Hashbang),
109
+ (r'#.*$', Comment.Single),
110
+ (r'\\\n', Text),
111
+ (r'\\', Text),
112
+ include('keywords'),
113
+ include('soft-keywords'),
114
+ (r'(def)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'funcname'),
115
+ (r'(class)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'classname'),
116
+ (r'(from)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace),
117
+ 'fromimport'),
118
+ (r'(import)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace),
119
+ 'import'),
120
+ include('expr'),
121
+ ],
122
+ 'expr': [
123
+ # raw f-strings and t-strings
124
+ ('(?i)(r[ft]|[ft]r)(""")',
125
+ bygroups(String.Affix, String.Double),
126
+ combined('rfstringescape', 'tdqf')),
127
+ ("(?i)(r[ft]|[ft]r)(''')",
128
+ bygroups(String.Affix, String.Single),
129
+ combined('rfstringescape', 'tsqf')),
130
+ ('(?i)(r[ft]|[ft]r)(")',
131
+ bygroups(String.Affix, String.Double),
132
+ combined('rfstringescape', 'dqf')),
133
+ ("(?i)(r[ft]|[ft]r)(')",
134
+ bygroups(String.Affix, String.Single),
135
+ combined('rfstringescape', 'sqf')),
136
+ # non-raw f-strings and t-strings
137
+ ('([fFtT])(""")', bygroups(String.Affix, String.Double),
138
+ combined('fstringescape', 'tdqf')),
139
+ ("([fFtT])(''')", bygroups(String.Affix, String.Single),
140
+ combined('fstringescape', 'tsqf')),
141
+ ('([fFtT])(")', bygroups(String.Affix, String.Double),
142
+ combined('fstringescape', 'dqf')),
143
+ ("([fFtT])(')", bygroups(String.Affix, String.Single),
144
+ combined('fstringescape', 'sqf')),
145
+ # raw bytes and strings
146
+ ('(?i)(rb|br|r)(""")',
147
+ bygroups(String.Affix, String.Double), 'tdqs'),
148
+ ("(?i)(rb|br|r)(''')",
149
+ bygroups(String.Affix, String.Single), 'tsqs'),
150
+ ('(?i)(rb|br|r)(")',
151
+ bygroups(String.Affix, String.Double), 'dqs'),
152
+ ("(?i)(rb|br|r)(')",
153
+ bygroups(String.Affix, String.Single), 'sqs'),
154
+ # non-raw strings
155
+ ('([uU]?)(""")', bygroups(String.Affix, String.Double),
156
+ combined('stringescape', 'tdqs')),
157
+ ("([uU]?)(''')", bygroups(String.Affix, String.Single),
158
+ combined('stringescape', 'tsqs')),
159
+ ('([uU]?)(")', bygroups(String.Affix, String.Double),
160
+ combined('stringescape', 'dqs')),
161
+ ("([uU]?)(')", bygroups(String.Affix, String.Single),
162
+ combined('stringescape', 'sqs')),
163
+ # non-raw bytes
164
+ ('([bB])(""")', bygroups(String.Affix, String.Double),
165
+ combined('bytesescape', 'tdqs')),
166
+ ("([bB])(''')", bygroups(String.Affix, String.Single),
167
+ combined('bytesescape', 'tsqs')),
168
+ ('([bB])(")', bygroups(String.Affix, String.Double),
169
+ combined('bytesescape', 'dqs')),
170
+ ("([bB])(')", bygroups(String.Affix, String.Single),
171
+ combined('bytesescape', 'sqs')),
172
+
173
+ (r'[^\S\n]+', Text),
174
+ include('numbers'),
175
+ (r'!=|==|<<|>>|:=|[-~+/*%=<>&^|.]', Operator),
176
+ (r'[]{}:(),;[]', Punctuation),
177
+ (r'(in|is|and|or|not)\b', Operator.Word),
178
+ include('expr-keywords'),
179
+ include('builtins'),
180
+ include('magicfuncs'),
181
+ include('magicvars'),
182
+ include('name'),
183
+ ],
184
+ 'expr-inside-fstring': [
185
+ (r'[{([]', Punctuation, 'expr-inside-fstring-inner'),
186
+ # without format specifier
187
+ (r'(=\s*)?' # debug (https://bugs.python.org/issue36817)
188
+ r'(\![sraf])?' # conversion
189
+ r'\}', String.Interpol, '#pop'),
190
+ # with format specifier
191
+ # we'll catch the remaining '}' in the outer scope
192
+ (r'(=\s*)?' # debug (https://bugs.python.org/issue36817)
193
+ r'(\![sraf])?' # conversion
194
+ r':', String.Interpol, '#pop'),
195
+ (r'\s+', Whitespace), # allow new lines
196
+ include('expr'),
197
+ ],
198
+ 'expr-inside-fstring-inner': [
199
+ (r'[{([]', Punctuation, 'expr-inside-fstring-inner'),
200
+ (r'[])}]', Punctuation, '#pop'),
201
+ (r'\s+', Whitespace), # allow new lines
202
+ include('expr'),
203
+ ],
204
+ 'expr-keywords': [
205
+ # Based on https://docs.python.org/3/reference/expressions.html
206
+ (words((
207
+ 'async for', 'await', 'else', 'for', 'if', 'lambda',
208
+ 'yield', 'yield from'), suffix=r'\b'),
209
+ Keyword),
210
+ (words(('True', 'False', 'None'), suffix=r'\b'), Keyword.Constant),
211
+ ],
212
+ 'keywords': [
213
+ (words((
214
+ 'assert', 'async', 'await', 'break', 'continue', 'del', 'elif',
215
+ 'else', 'except', 'finally', 'for', 'global', 'if', 'lambda',
216
+ 'pass', 'raise', 'nonlocal', 'return', 'try', 'while', 'yield',
217
+ 'yield from', 'as', 'with'), suffix=r'\b'),
218
+ Keyword),
219
+ (words(('True', 'False', 'None'), suffix=r'\b'), Keyword.Constant),
220
+ ],
221
+ 'soft-keywords': [
222
+ # `match`, `case` and `_` soft keywords
223
+ (r'(^[ \t]*)' # at beginning of line + possible indentation
224
+ r'(match|case)\b' # a possible keyword
225
+ r'(?![ \t]*(?:' # not followed by...
226
+ r'[:,;=^&|@~)\]}]|(?:' + # characters and keywords that mean this isn't
227
+ # pattern matching (but None/True/False is ok)
228
+ r'|'.join(k for k in keyword.kwlist if k[0].islower()) + r')\b))',
229
+ bygroups(Text, Keyword), 'soft-keywords-inner'),
230
+ ],
231
+ 'soft-keywords-inner': [
232
+ # optional `_` keyword
233
+ (r'(\s+)([^\n_]*)(_\b)', bygroups(Whitespace, using(this), Keyword)),
234
+ default('#pop')
235
+ ],
236
+ 'builtins': [
237
+ (words((
238
+ '__import__', 'abs', 'aiter', 'all', 'any', 'bin', 'bool', 'bytearray',
239
+ 'breakpoint', 'bytes', 'callable', 'chr', 'classmethod', 'compile',
240
+ 'complex', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval',
241
+ 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals',
242
+ 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'isinstance',
243
+ 'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max',
244
+ 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow',
245
+ 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set',
246
+ 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super',
247
+ 'tuple', 'type', 'vars', 'zip'), prefix=r'(?<!\.)', suffix=r'\b'),
248
+ Name.Builtin),
249
+ (r'(?<!\.)(self|Ellipsis|NotImplemented|cls)\b', Name.Builtin.Pseudo),
250
+ (words((
251
+ 'ArithmeticError', 'AssertionError', 'AttributeError',
252
+ 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning',
253
+ 'EOFError', 'EnvironmentError', 'Exception', 'FloatingPointError',
254
+ 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
255
+ 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError',
256
+ 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError',
257
+ 'NotImplementedError', 'OSError', 'OverflowError',
258
+ 'PendingDeprecationWarning', 'ReferenceError', 'ResourceWarning',
259
+ 'RuntimeError', 'RuntimeWarning', 'StopIteration',
260
+ 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit',
261
+ 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError',
262
+ 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError',
263
+ 'UnicodeWarning', 'UserWarning', 'ValueError', 'VMSError',
264
+ 'Warning', 'WindowsError', 'ZeroDivisionError',
265
+ # new builtin exceptions from PEP 3151
266
+ 'BlockingIOError', 'ChildProcessError', 'ConnectionError',
267
+ 'BrokenPipeError', 'ConnectionAbortedError', 'ConnectionRefusedError',
268
+ 'ConnectionResetError', 'FileExistsError', 'FileNotFoundError',
269
+ 'InterruptedError', 'IsADirectoryError', 'NotADirectoryError',
270
+ 'PermissionError', 'ProcessLookupError', 'TimeoutError',
271
+ # others new in Python 3
272
+ 'StopAsyncIteration', 'ModuleNotFoundError', 'RecursionError',
273
+ 'EncodingWarning'),
274
+ prefix=r'(?<!\.)', suffix=r'\b'),
275
+ Name.Exception),
276
+ ],
277
+ 'magicfuncs': [
278
+ (words((
279
+ '__abs__', '__add__', '__aenter__', '__aexit__', '__aiter__',
280
+ '__and__', '__anext__', '__await__', '__bool__', '__bytes__',
281
+ '__call__', '__complex__', '__contains__', '__del__', '__delattr__',
282
+ '__delete__', '__delitem__', '__dir__', '__divmod__', '__enter__',
283
+ '__eq__', '__exit__', '__float__', '__floordiv__', '__format__',
284
+ '__ge__', '__get__', '__getattr__', '__getattribute__',
285
+ '__getitem__', '__gt__', '__hash__', '__iadd__', '__iand__',
286
+ '__ifloordiv__', '__ilshift__', '__imatmul__', '__imod__',
287
+ '__imul__', '__index__', '__init__', '__instancecheck__',
288
+ '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__',
289
+ '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__',
290
+ '__len__', '__length_hint__', '__lshift__', '__lt__', '__matmul__',
291
+ '__missing__', '__mod__', '__mul__', '__ne__', '__neg__',
292
+ '__new__', '__next__', '__or__', '__pos__', '__pow__',
293
+ '__prepare__', '__radd__', '__rand__', '__rdivmod__', '__repr__',
294
+ '__reversed__', '__rfloordiv__', '__rlshift__', '__rmatmul__',
295
+ '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__',
296
+ '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__',
297
+ '__rxor__', '__set__', '__setattr__', '__setitem__', '__str__',
298
+ '__sub__', '__subclasscheck__', '__truediv__',
299
+ '__xor__'), suffix=r'\b'),
300
+ Name.Function.Magic),
301
+ ],
302
+ 'magicvars': [
303
+ (words((
304
+ '__annotations__', '__bases__', '__class__', '__closure__',
305
+ '__code__', '__defaults__', '__dict__', '__doc__', '__file__',
306
+ '__func__', '__globals__', '__kwdefaults__', '__module__',
307
+ '__mro__', '__name__', '__objclass__', '__qualname__',
308
+ '__self__', '__slots__', '__weakref__'), suffix=r'\b'),
309
+ Name.Variable.Magic),
310
+ ],
311
+ 'numbers': [
312
+ (r'(\d(?:_?\d)*\.(?:\d(?:_?\d)*)?|(?:\d(?:_?\d)*)?\.\d(?:_?\d)*)'
313
+ r'([eE][+-]?\d(?:_?\d)*)?', Number.Float),
314
+ (r'\d(?:_?\d)*[eE][+-]?\d(?:_?\d)*j?', Number.Float),
315
+ (r'0[oO](?:_?[0-7])+', Number.Oct),
316
+ (r'0[bB](?:_?[01])+', Number.Bin),
317
+ (r'0[xX](?:_?[a-fA-F0-9])+', Number.Hex),
318
+ (r'\d(?:_?\d)*', Number.Integer),
319
+ ],
320
+ 'name': [
321
+ (r'@' + uni_name, Name.Decorator),
322
+ (r'@', Operator), # new matrix multiplication operator
323
+ (uni_name, Name),
324
+ ],
325
+ 'funcname': [
326
+ include('magicfuncs'),
327
+ (uni_name, Name.Function, '#pop'),
328
+ default('#pop'),
329
+ ],
330
+ 'classname': [
331
+ (uni_name, Name.Class, '#pop'),
332
+ ],
333
+ 'import': [
334
+ (r'(\s+)(as)(\s+)', bygroups(Whitespace, Keyword, Whitespace)),
335
+ (r'\.', Name.Namespace),
336
+ (uni_name, Name.Namespace),
337
+ (r'(\s*)(,)(\s*)', bygroups(Whitespace, Operator, Whitespace)),
338
+ default('#pop') # all else: go back
339
+ ],
340
+ 'fromimport': [
341
+ (r'(\s+)(import)\b', bygroups(Whitespace, Keyword.Namespace), '#pop'),
342
+ (r'\.', Name.Namespace),
343
+ # if None occurs here, it's "raise x from None", since None can
344
+ # never be a module name
345
+ (r'None\b', Keyword.Constant, '#pop'),
346
+ (uni_name, Name.Namespace),
347
+ default('#pop'),
348
+ ],
349
+ 'rfstringescape': [
350
+ (r'\{\{', String.Escape),
351
+ (r'\}\}', String.Escape),
352
+ ],
353
+ 'fstringescape': [
354
+ include('rfstringescape'),
355
+ include('stringescape'),
356
+ ],
357
+ 'bytesescape': [
358
+ (r'\\([\\abfnrtv"\']|\n|x[a-fA-F0-9]{2}|[0-7]{1,3})', String.Escape)
359
+ ],
360
+ 'stringescape': [
361
+ (r'\\(N\{.*?\}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8})', String.Escape),
362
+ include('bytesescape')
363
+ ],
364
+ 'fstrings-single': fstring_rules(String.Single),
365
+ 'fstrings-double': fstring_rules(String.Double),
366
+ 'strings-single': innerstring_rules(String.Single),
367
+ 'strings-double': innerstring_rules(String.Double),
368
+ 'dqf': [
369
+ (r'"', String.Double, '#pop'),
370
+ (r'\\\\|\\"|\\\n', String.Escape), # included here for raw strings
371
+ include('fstrings-double')
372
+ ],
373
+ 'sqf': [
374
+ (r"'", String.Single, '#pop'),
375
+ (r"\\\\|\\'|\\\n", String.Escape), # included here for raw strings
376
+ include('fstrings-single')
377
+ ],
378
+ 'dqs': [
379
+ (r'"', String.Double, '#pop'),
380
+ (r'\\\\|\\"|\\\n', String.Escape), # included here for raw strings
381
+ include('strings-double')
382
+ ],
383
+ 'sqs': [
384
+ (r"'", String.Single, '#pop'),
385
+ (r"\\\\|\\'|\\\n", String.Escape), # included here for raw strings
386
+ include('strings-single')
387
+ ],
388
+ 'tdqf': [
389
+ (r'"""', String.Double, '#pop'),
390
+ include('fstrings-double'),
391
+ (r'\n', String.Double)
392
+ ],
393
+ 'tsqf': [
394
+ (r"'''", String.Single, '#pop'),
395
+ include('fstrings-single'),
396
+ (r'\n', String.Single)
397
+ ],
398
+ 'tdqs': [
399
+ (r'"""', String.Double, '#pop'),
400
+ include('strings-double'),
401
+ (r'\n', String.Double)
402
+ ],
403
+ 'tsqs': [
404
+ (r"'''", String.Single, '#pop'),
405
+ include('strings-single'),
406
+ (r'\n', String.Single)
407
+ ],
408
+ }
409
+
410
+ def analyse_text(text):
411
+ return shebang_matches(text, r'pythonw?(3(\.\d)?)?') or \
412
+ 'import ' in text[:1000]
413
+
414
+
415
+ Python3Lexer = PythonLexer
416
+
417
+
418
+ class Python2Lexer(RegexLexer):
419
+ """
420
+ For Python 2.x source code.
421
+
422
+ .. versionchanged:: 2.5
423
+ This class has been renamed from ``PythonLexer``. ``PythonLexer`` now
424
+ refers to the Python 3 variant. File name patterns like ``*.py`` have
425
+ been moved to Python 3 as well.
426
+ """
427
+
428
+ name = 'Python 2.x'
429
+ url = 'https://www.python.org'
430
+ aliases = ['python2', 'py2']
431
+ filenames = [] # now taken over by PythonLexer (3.x)
432
+ mimetypes = ['text/x-python2', 'application/x-python2']
433
+ version_added = ''
434
+
435
+ def innerstring_rules(ttype):
436
+ return [
437
+ # the old style '%s' % (...) string formatting
438
+ (r'%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?'
439
+ '[hlL]?[E-GXc-giorsux%]', String.Interpol),
440
+ # backslashes, quotes and formatting signs must be parsed one at a time
441
+ (r'[^\\\'"%\n]+', ttype),
442
+ (r'[\'"\\]', ttype),
443
+ # unhandled string formatting sign
444
+ (r'%', ttype),
445
+ # newlines are an error (use "nl" state)
446
+ ]
447
+
448
+ tokens = {
449
+ 'root': [
450
+ (r'\n', Whitespace),
451
+ (r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")',
452
+ bygroups(Whitespace, String.Affix, String.Doc)),
453
+ (r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')",
454
+ bygroups(Whitespace, String.Affix, String.Doc)),
455
+ (r'[^\S\n]+', Text),
456
+ (r'\A#!.+$', Comment.Hashbang),
457
+ (r'#.*$', Comment.Single),
458
+ (r'[]{}:(),;[]', Punctuation),
459
+ (r'\\\n', Text),
460
+ (r'\\', Text),
461
+ (r'(in|is|and|or|not)\b', Operator.Word),
462
+ (r'!=|==|<<|>>|[-~+/*%=<>&^|.]', Operator),
463
+ include('keywords'),
464
+ (r'(def)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'funcname'),
465
+ (r'(class)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'classname'),
466
+ (r'(from)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace),
467
+ 'fromimport'),
468
+ (r'(import)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace),
469
+ 'import'),
470
+ include('builtins'),
471
+ include('magicfuncs'),
472
+ include('magicvars'),
473
+ include('backtick'),
474
+ ('([rR]|[uUbB][rR]|[rR][uUbB])(""")',
475
+ bygroups(String.Affix, String.Double), 'tdqs'),
476
+ ("([rR]|[uUbB][rR]|[rR][uUbB])(''')",
477
+ bygroups(String.Affix, String.Single), 'tsqs'),
478
+ ('([rR]|[uUbB][rR]|[rR][uUbB])(")',
479
+ bygroups(String.Affix, String.Double), 'dqs'),
480
+ ("([rR]|[uUbB][rR]|[rR][uUbB])(')",
481
+ bygroups(String.Affix, String.Single), 'sqs'),
482
+ ('([uUbB]?)(""")', bygroups(String.Affix, String.Double),
483
+ combined('stringescape', 'tdqs')),
484
+ ("([uUbB]?)(''')", bygroups(String.Affix, String.Single),
485
+ combined('stringescape', 'tsqs')),
486
+ ('([uUbB]?)(")', bygroups(String.Affix, String.Double),
487
+ combined('stringescape', 'dqs')),
488
+ ("([uUbB]?)(')", bygroups(String.Affix, String.Single),
489
+ combined('stringescape', 'sqs')),
490
+ include('name'),
491
+ include('numbers'),
492
+ ],
493
+ 'keywords': [
494
+ (words((
495
+ 'assert', 'break', 'continue', 'del', 'elif', 'else', 'except',
496
+ 'exec', 'finally', 'for', 'global', 'if', 'lambda', 'pass',
497
+ 'print', 'raise', 'return', 'try', 'while', 'yield',
498
+ 'yield from', 'as', 'with'), suffix=r'\b'),
499
+ Keyword),
500
+ ],
501
+ 'builtins': [
502
+ (words((
503
+ '__import__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin',
504
+ 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod',
505
+ 'cmp', 'coerce', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
506
+ 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float',
507
+ 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id',
508
+ 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len',
509
+ 'list', 'locals', 'long', 'map', 'max', 'min', 'next', 'object',
510
+ 'oct', 'open', 'ord', 'pow', 'property', 'range', 'raw_input', 'reduce',
511
+ 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
512
+ 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type',
513
+ 'unichr', 'unicode', 'vars', 'xrange', 'zip'),
514
+ prefix=r'(?<!\.)', suffix=r'\b'),
515
+ Name.Builtin),
516
+ (r'(?<!\.)(self|None|Ellipsis|NotImplemented|False|True|cls'
517
+ r')\b', Name.Builtin.Pseudo),
518
+ (words((
519
+ 'ArithmeticError', 'AssertionError', 'AttributeError',
520
+ 'BaseException', 'DeprecationWarning', 'EOFError', 'EnvironmentError',
521
+ 'Exception', 'FloatingPointError', 'FutureWarning', 'GeneratorExit',
522
+ 'IOError', 'ImportError', 'ImportWarning', 'IndentationError',
523
+ 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
524
+ 'MemoryError', 'NameError',
525
+ 'NotImplementedError', 'OSError', 'OverflowError', 'OverflowWarning',
526
+ 'PendingDeprecationWarning', 'ReferenceError',
527
+ 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration',
528
+ 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit',
529
+ 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError',
530
+ 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError',
531
+ 'UnicodeWarning', 'UserWarning', 'ValueError', 'VMSError', 'Warning',
532
+ 'WindowsError', 'ZeroDivisionError'), prefix=r'(?<!\.)', suffix=r'\b'),
533
+ Name.Exception),
534
+ ],
535
+ 'magicfuncs': [
536
+ (words((
537
+ '__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__',
538
+ '__complex__', '__contains__', '__del__', '__delattr__', '__delete__',
539
+ '__delitem__', '__delslice__', '__div__', '__divmod__', '__enter__',
540
+ '__eq__', '__exit__', '__float__', '__floordiv__', '__ge__', '__get__',
541
+ '__getattr__', '__getattribute__', '__getitem__', '__getslice__', '__gt__',
542
+ '__hash__', '__hex__', '__iadd__', '__iand__', '__idiv__', '__ifloordiv__',
543
+ '__ilshift__', '__imod__', '__imul__', '__index__', '__init__',
544
+ '__instancecheck__', '__int__', '__invert__', '__iop__', '__ior__',
545
+ '__ipow__', '__irshift__', '__isub__', '__iter__', '__itruediv__',
546
+ '__ixor__', '__le__', '__len__', '__long__', '__lshift__', '__lt__',
547
+ '__missing__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__',
548
+ '__nonzero__', '__oct__', '__op__', '__or__', '__pos__', '__pow__',
549
+ '__radd__', '__rand__', '__rcmp__', '__rdiv__', '__rdivmod__', '__repr__',
550
+ '__reversed__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__',
551
+ '__rop__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__',
552
+ '__rtruediv__', '__rxor__', '__set__', '__setattr__', '__setitem__',
553
+ '__setslice__', '__str__', '__sub__', '__subclasscheck__', '__truediv__',
554
+ '__unicode__', '__xor__'), suffix=r'\b'),
555
+ Name.Function.Magic),
556
+ ],
557
+ 'magicvars': [
558
+ (words((
559
+ '__bases__', '__class__', '__closure__', '__code__', '__defaults__',
560
+ '__dict__', '__doc__', '__file__', '__func__', '__globals__',
561
+ '__metaclass__', '__module__', '__mro__', '__name__', '__self__',
562
+ '__slots__', '__weakref__'),
563
+ suffix=r'\b'),
564
+ Name.Variable.Magic),
565
+ ],
566
+ 'numbers': [
567
+ (r'(\d+\.\d*|\d*\.\d+)([eE][+-]?[0-9]+)?j?', Number.Float),
568
+ (r'\d+[eE][+-]?[0-9]+j?', Number.Float),
569
+ (r'0[0-7]+j?', Number.Oct),
570
+ (r'0[bB][01]+', Number.Bin),
571
+ (r'0[xX][a-fA-F0-9]+', Number.Hex),
572
+ (r'\d+L', Number.Integer.Long),
573
+ (r'\d+j?', Number.Integer)
574
+ ],
575
+ 'backtick': [
576
+ ('`.*?`', String.Backtick),
577
+ ],
578
+ 'name': [
579
+ (r'@[\w.]+', Name.Decorator),
580
+ (r'[a-zA-Z_]\w*', Name),
581
+ ],
582
+ 'funcname': [
583
+ include('magicfuncs'),
584
+ (r'[a-zA-Z_]\w*', Name.Function, '#pop'),
585
+ default('#pop'),
586
+ ],
587
+ 'classname': [
588
+ (r'[a-zA-Z_]\w*', Name.Class, '#pop')
589
+ ],
590
+ 'import': [
591
+ (r'(?:[ \t]|\\\n)+', Text),
592
+ (r'as\b', Keyword.Namespace),
593
+ (r',', Operator),
594
+ (r'[a-zA-Z_][\w.]*', Name.Namespace),
595
+ default('#pop') # all else: go back
596
+ ],
597
+ 'fromimport': [
598
+ (r'(?:[ \t]|\\\n)+', Text),
599
+ (r'import\b', Keyword.Namespace, '#pop'),
600
+ # if None occurs here, it's "raise x from None", since None can
601
+ # never be a module name
602
+ (r'None\b', Name.Builtin.Pseudo, '#pop'),
603
+ # sadly, in "raise x from y" y will be highlighted as namespace too
604
+ (r'[a-zA-Z_.][\w.]*', Name.Namespace),
605
+ # anything else here also means "raise x from y" and is therefore
606
+ # not an error
607
+ default('#pop'),
608
+ ],
609
+ 'stringescape': [
610
+ (r'\\([\\abfnrtv"\']|\n|N\{.*?\}|u[a-fA-F0-9]{4}|'
611
+ r'U[a-fA-F0-9]{8}|x[a-fA-F0-9]{2}|[0-7]{1,3})', String.Escape)
612
+ ],
613
+ 'strings-single': innerstring_rules(String.Single),
614
+ 'strings-double': innerstring_rules(String.Double),
615
+ 'dqs': [
616
+ (r'"', String.Double, '#pop'),
617
+ (r'\\\\|\\"|\\\n', String.Escape), # included here for raw strings
618
+ include('strings-double')
619
+ ],
620
+ 'sqs': [
621
+ (r"'", String.Single, '#pop'),
622
+ (r"\\\\|\\'|\\\n", String.Escape), # included here for raw strings
623
+ include('strings-single')
624
+ ],
625
+ 'tdqs': [
626
+ (r'"""', String.Double, '#pop'),
627
+ include('strings-double'),
628
+ (r'\n', String.Double)
629
+ ],
630
+ 'tsqs': [
631
+ (r"'''", String.Single, '#pop'),
632
+ include('strings-single'),
633
+ (r'\n', String.Single)
634
+ ],
635
+ }
636
+
637
+ def analyse_text(text):
638
+ return shebang_matches(text, r'pythonw?2(\.\d)?')
639
+
640
+
641
+ class _PythonConsoleLexerBase(RegexLexer):
642
+ name = 'Python console session'
643
+ aliases = ['pycon', 'python-console']
644
+ mimetypes = ['text/x-python-doctest']
645
+
646
+ """Auxiliary lexer for `PythonConsoleLexer`.
647
+
648
+ Code tokens are output as ``Token.Other.Code``, traceback tokens as
649
+ ``Token.Other.Traceback``.
650
+ """
651
+ tokens = {
652
+ 'root': [
653
+ (r'(>>> )(.*\n)', bygroups(Generic.Prompt, Other.Code), 'continuations'),
654
+ # This happens, e.g., when tracebacks are embedded in documentation;
655
+ # trailing whitespaces are often stripped in such contexts.
656
+ (r'(>>>)(\n)', bygroups(Generic.Prompt, Whitespace)),
657
+ (r'(\^C)?Traceback \(most recent call last\):\n', Other.Traceback, 'traceback'),
658
+ # SyntaxError starts with this
659
+ (r' File "[^"]+", line \d+', Other.Traceback, 'traceback'),
660
+ (r'.*\n', Generic.Output),
661
+ ],
662
+ 'continuations': [
663
+ (r'(\.\.\. )(.*\n)', bygroups(Generic.Prompt, Other.Code)),
664
+ # See above.
665
+ (r'(\.\.\.)(\n)', bygroups(Generic.Prompt, Whitespace)),
666
+ default('#pop'),
667
+ ],
668
+ 'traceback': [
669
+ # As soon as we see a traceback, consume everything until the next
670
+ # >>> prompt.
671
+ (r'(?=>>>( |$))', Text, '#pop'),
672
+ (r'(KeyboardInterrupt)(\n)', bygroups(Name.Class, Whitespace)),
673
+ (r'.*\n', Other.Traceback),
674
+ ],
675
+ }
676
+
677
+
678
+ class PythonConsoleLexer(DelegatingLexer):
679
+ """
680
+ For Python console output or doctests, such as:
681
+
682
+ .. sourcecode:: pycon
683
+
684
+ >>> a = 'foo'
685
+ >>> print(a)
686
+ foo
687
+ >>> 1 / 0
688
+ Traceback (most recent call last):
689
+ File "<stdin>", line 1, in <module>
690
+ ZeroDivisionError: integer division or modulo by zero
691
+
692
+ Additional options:
693
+
694
+ `python3`
695
+ Use Python 3 lexer for code. Default is ``True``.
696
+
697
+ .. versionadded:: 1.0
698
+ .. versionchanged:: 2.5
699
+ Now defaults to ``True``.
700
+ """
701
+
702
+ name = 'Python console session'
703
+ aliases = ['pycon', 'python-console']
704
+ mimetypes = ['text/x-python-doctest']
705
+ url = 'https://python.org'
706
+ version_added = ''
707
+
708
+ def __init__(self, **options):
709
+ python3 = get_bool_opt(options, 'python3', True)
710
+ if python3:
711
+ pylexer = PythonLexer
712
+ tblexer = PythonTracebackLexer
713
+ else:
714
+ pylexer = Python2Lexer
715
+ tblexer = Python2TracebackLexer
716
+ # We have two auxiliary lexers. Use DelegatingLexer twice with
717
+ # different tokens. TODO: DelegatingLexer should support this
718
+ # directly, by accepting a tuplet of auxiliary lexers and a tuple of
719
+ # distinguishing tokens. Then we wouldn't need this intermediary
720
+ # class.
721
+ class _ReplaceInnerCode(DelegatingLexer):
722
+ def __init__(self, **options):
723
+ super().__init__(pylexer, _PythonConsoleLexerBase, Other.Code, **options)
724
+ super().__init__(tblexer, _ReplaceInnerCode, Other.Traceback, **options)
725
+
726
+
727
+ class PythonTracebackLexer(RegexLexer):
728
+ """
729
+ For Python 3.x tracebacks, with support for chained exceptions.
730
+
731
+ .. versionchanged:: 2.5
732
+ This is now the default ``PythonTracebackLexer``. It is still available
733
+ as the alias ``Python3TracebackLexer``.
734
+ """
735
+
736
+ name = 'Python Traceback'
737
+ aliases = ['pytb', 'py3tb']
738
+ filenames = ['*.pytb', '*.py3tb']
739
+ mimetypes = ['text/x-python-traceback', 'text/x-python3-traceback']
740
+ url = 'https://python.org'
741
+ version_added = '1.0'
742
+
743
+ tokens = {
744
+ 'root': [
745
+ (r'\n', Whitespace),
746
+ (r'^(\^C)?Traceback \(most recent call last\):\n', Generic.Traceback, 'intb'),
747
+ (r'^During handling of the above exception, another '
748
+ r'exception occurred:\n\n', Generic.Traceback),
749
+ (r'^The above exception was the direct cause of the '
750
+ r'following exception:\n\n', Generic.Traceback),
751
+ (r'^(?= File "[^"]+", line \d+)', Generic.Traceback, 'intb'),
752
+ (r'^.*\n', Other),
753
+ ],
754
+ 'intb': [
755
+ (r'^( File )("[^"]+")(, line )(\d+)(, in )(.+)(\n)',
756
+ bygroups(Text, Name.Builtin, Text, Number, Text, Name, Whitespace)),
757
+ (r'^( File )("[^"]+")(, line )(\d+)(\n)',
758
+ bygroups(Text, Name.Builtin, Text, Number, Whitespace)),
759
+ (r'^( )(.+)(\n)',
760
+ bygroups(Whitespace, using(PythonLexer), Whitespace), 'markers'),
761
+ (r'^([ \t]*)(\.\.\.)(\n)',
762
+ bygroups(Whitespace, Comment, Whitespace)), # for doctests...
763
+ (r'^([^:]+)(: )(.+)(\n)',
764
+ bygroups(Generic.Error, Text, Name, Whitespace), '#pop'),
765
+ (r'^([a-zA-Z_][\w.]*)(:?\n)',
766
+ bygroups(Generic.Error, Whitespace), '#pop'),
767
+ default('#pop'),
768
+ ],
769
+ 'markers': [
770
+ # Either `PEP 657 <https://www.python.org/dev/peps/pep-0657/>`
771
+ # error locations in Python 3.11+, or single-caret markers
772
+ # for syntax errors before that.
773
+ (r'^( {4,})([~^]+)(\n)',
774
+ bygroups(Whitespace, Punctuation.Marker, Whitespace),
775
+ '#pop'),
776
+ default('#pop'),
777
+ ],
778
+ }
779
+
780
+
781
+ Python3TracebackLexer = PythonTracebackLexer
782
+
783
+
784
+ class Python2TracebackLexer(RegexLexer):
785
+ """
786
+ For Python tracebacks.
787
+
788
+ .. versionchanged:: 2.5
789
+ This class has been renamed from ``PythonTracebackLexer``.
790
+ ``PythonTracebackLexer`` now refers to the Python 3 variant.
791
+ """
792
+
793
+ name = 'Python 2.x Traceback'
794
+ aliases = ['py2tb']
795
+ filenames = ['*.py2tb']
796
+ mimetypes = ['text/x-python2-traceback']
797
+ url = 'https://python.org'
798
+ version_added = '0.7'
799
+
800
+ tokens = {
801
+ 'root': [
802
+ # Cover both (most recent call last) and (innermost last)
803
+ # The optional ^C allows us to catch keyboard interrupt signals.
804
+ (r'^(\^C)?(Traceback.*\n)',
805
+ bygroups(Text, Generic.Traceback), 'intb'),
806
+ # SyntaxError starts with this.
807
+ (r'^(?= File "[^"]+", line \d+)', Generic.Traceback, 'intb'),
808
+ (r'^.*\n', Other),
809
+ ],
810
+ 'intb': [
811
+ (r'^( File )("[^"]+")(, line )(\d+)(, in )(.+)(\n)',
812
+ bygroups(Text, Name.Builtin, Text, Number, Text, Name, Whitespace)),
813
+ (r'^( File )("[^"]+")(, line )(\d+)(\n)',
814
+ bygroups(Text, Name.Builtin, Text, Number, Whitespace)),
815
+ (r'^( )(.+)(\n)',
816
+ bygroups(Text, using(Python2Lexer), Whitespace), 'marker'),
817
+ (r'^([ \t]*)(\.\.\.)(\n)',
818
+ bygroups(Text, Comment, Whitespace)), # for doctests...
819
+ (r'^([^:]+)(: )(.+)(\n)',
820
+ bygroups(Generic.Error, Text, Name, Whitespace), '#pop'),
821
+ (r'^([a-zA-Z_]\w*)(:?\n)',
822
+ bygroups(Generic.Error, Whitespace), '#pop')
823
+ ],
824
+ 'marker': [
825
+ # For syntax errors.
826
+ (r'( {4,})(\^)', bygroups(Text, Punctuation.Marker), '#pop'),
827
+ default('#pop'),
828
+ ],
829
+ }
830
+
831
+
832
+ class CythonLexer(RegexLexer):
833
+ """
834
+ For Pyrex and Cython source code.
835
+ """
836
+
837
+ name = 'Cython'
838
+ url = 'https://cython.org'
839
+ aliases = ['cython', 'pyx', 'pyrex']
840
+ filenames = ['*.pyx', '*.pxd', '*.pxi']
841
+ mimetypes = ['text/x-cython', 'application/x-cython']
842
+ version_added = '1.1'
843
+
844
+ tokens = {
845
+ 'root': [
846
+ (r'\n', Whitespace),
847
+ (r'^(\s*)("""(?:.|\n)*?""")', bygroups(Whitespace, String.Doc)),
848
+ (r"^(\s*)('''(?:.|\n)*?''')", bygroups(Whitespace, String.Doc)),
849
+ (r'[^\S\n]+', Text),
850
+ (r'#.*$', Comment),
851
+ (r'[]{}:(),;[]', Punctuation),
852
+ (r'\\\n', Whitespace),
853
+ (r'\\', Text),
854
+ (r'(in|is|and|or|not)\b', Operator.Word),
855
+ (r'(<)([a-zA-Z0-9.?]+)(>)',
856
+ bygroups(Punctuation, Keyword.Type, Punctuation)),
857
+ (r'!=|==|<<|>>|[-~+/*%=<>&^|.?]', Operator),
858
+ (r'(from)(\d+)(<=)(\s+)(<)(\d+)(:)',
859
+ bygroups(Keyword, Number.Integer, Operator, Whitespace, Operator,
860
+ Name, Punctuation)),
861
+ include('keywords'),
862
+ (r'(def|property)(\s+)', bygroups(Keyword, Whitespace), 'funcname'),
863
+ (r'(cp?def)(\s+)', bygroups(Keyword, Whitespace), 'cdef'),
864
+ # (should actually start a block with only cdefs)
865
+ (r'(cdef)(:)', bygroups(Keyword, Punctuation)),
866
+ (r'(class|cppclass|struct)(\s+)', bygroups(Keyword, Whitespace), 'classname'),
867
+ (r'(from)(\s+)', bygroups(Keyword, Whitespace), 'fromimport'),
868
+ (r'(c?import)(\s+)', bygroups(Keyword, Whitespace), 'import'),
869
+ include('builtins'),
870
+ include('backtick'),
871
+ ('(?:[rR]|[uU][rR]|[rR][uU])"""', String, 'tdqs'),
872
+ ("(?:[rR]|[uU][rR]|[rR][uU])'''", String, 'tsqs'),
873
+ ('(?:[rR]|[uU][rR]|[rR][uU])"', String, 'dqs'),
874
+ ("(?:[rR]|[uU][rR]|[rR][uU])'", String, 'sqs'),
875
+ ('[uU]?"""', String, combined('stringescape', 'tdqs')),
876
+ ("[uU]?'''", String, combined('stringescape', 'tsqs')),
877
+ ('[uU]?"', String, combined('stringescape', 'dqs')),
878
+ ("[uU]?'", String, combined('stringescape', 'sqs')),
879
+ include('name'),
880
+ include('numbers'),
881
+ ],
882
+ 'keywords': [
883
+ (words((
884
+ 'assert', 'async', 'await', 'break', 'by', 'continue', 'ctypedef', 'del',
885
+ 'elif', 'else', 'except', 'except?', 'exec', 'finally', 'for', 'fused', 'gil',
886
+ 'global', 'if', 'include', 'lambda', 'namespace', 'new', 'noexcept','nogil',
887
+ 'pass', 'print', 'raise', 'return', 'try', 'while', 'yield', 'as', 'with'),
888
+ suffix=r'\b'),
889
+ Keyword),
890
+ (words(('True', 'False', 'None', 'NULL'), suffix=r'\b'), Keyword.Constant),
891
+ (r'(DEF|IF|ELIF|ELSE)\b', Comment.Preproc),
892
+ ],
893
+ 'builtins': [
894
+ (words((
895
+ '__import__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bint',
896
+ 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'char', 'chr',
897
+ 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'delattr',
898
+ 'dict', 'dir', 'divmod', 'double', 'enumerate', 'eval', 'execfile', 'exit',
899
+ 'file', 'filter', 'float', 'frozenset', 'getattr', 'globals',
900
+ 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'intern', 'isinstance',
901
+ 'issubclass', 'iter', 'len', 'list', 'locals', 'long', 'map', 'max',
902
+ 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'property',
903
+ 'Py_ssize_t', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed',
904
+ 'round', 'set', 'setattr', 'size_t', 'slice', 'sorted', 'staticmethod',
905
+ 'ssize_t', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode',
906
+ 'unsigned', 'vars', 'xrange', 'zip'), prefix=r'(?<!\.)', suffix=r'\b'),
907
+ Name.Builtin),
908
+ (r'(?<!\.)(self|cls|Ellipsis|NotImplemented)\b', Name.Builtin.Pseudo),
909
+ (words((
910
+ 'ArithmeticError', 'AssertionError', 'AttributeError',
911
+ 'BaseException', 'DeprecationWarning', 'EOFError', 'EnvironmentError',
912
+ 'Exception', 'FloatingPointError', 'FutureWarning', 'GeneratorExit',
913
+ 'IOError', 'ImportError', 'ImportWarning', 'IndentationError',
914
+ 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
915
+ 'MemoryError', 'NameError', 'NotImplementedError',
916
+ 'OSError', 'OverflowError', 'OverflowWarning',
917
+ 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError',
918
+ 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError',
919
+ 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError',
920
+ 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError',
921
+ 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError',
922
+ 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning',
923
+ 'ZeroDivisionError'), prefix=r'(?<!\.)', suffix=r'\b'),
924
+ Name.Exception),
925
+ ],
926
+ 'numbers': [
927
+ (r'(\d+\.?\d*|\d*\.\d+)([eE][+-]?[0-9]+)?', Number.Float),
928
+ (r'0\d+', Number.Oct),
929
+ (r'0[xX][a-fA-F0-9]+', Number.Hex),
930
+ (r'\d+L', Number.Integer.Long),
931
+ (r'\d+', Number.Integer)
932
+ ],
933
+ 'backtick': [
934
+ ('`.*?`', String.Backtick),
935
+ ],
936
+ 'name': [
937
+ (r'@\w+', Name.Decorator),
938
+ (r'[a-zA-Z_]\w*', Name),
939
+ ],
940
+ 'funcname': [
941
+ (r'[a-zA-Z_]\w*', Name.Function, '#pop')
942
+ ],
943
+ 'cdef': [
944
+ (r"(public|readonly|extern|api|inline|packed)\b", Keyword.Reserved),
945
+ (r"(struct|enum|union|class|cppclass)\b(\s+)([a-zA-Z_]\w*)",
946
+ bygroups(Keyword, Whitespace, Name.Class), "#pop",),
947
+ (r"([a-zA-Z_]\w*)(\s*)(?=\()", bygroups(Name.Function, Whitespace), "#pop"),
948
+ (r"([a-zA-Z_]\w*)(\s*)(?=[:,=#\n]|$)", bygroups(Name.Variable, Whitespace), "#pop"),
949
+ (r"([a-zA-Z_]\w*)(\s*)(,)", bygroups(Name.Variable, Whitespace, Punctuation)),
950
+ (r'from\b', Keyword, '#pop'),
951
+ (r'as\b', Keyword),
952
+ (r':', Punctuation, '#pop'),
953
+ (r'(?=["\'])', Text, '#pop'),
954
+ (r'[a-zA-Z_]\w*', Keyword.Type),
955
+ (r'.', Text),
956
+ ],
957
+ 'classname': [
958
+ (r'[a-zA-Z_]\w*', Name.Class, '#pop')
959
+ ],
960
+ 'import': [
961
+ (r'(\s+)(as)(\s+)', bygroups(Whitespace, Keyword, Whitespace)),
962
+ (r'[a-zA-Z_][\w.]*', Name.Namespace),
963
+ (r'(\s*)(,)(\s*)', bygroups(Whitespace, Operator, Whitespace)),
964
+ default('#pop') # all else: go back
965
+ ],
966
+ 'fromimport': [
967
+ (r'(\s+)(c?import)\b', bygroups(Whitespace, Keyword), '#pop'),
968
+ (r'[a-zA-Z_.][\w.]*', Name.Namespace),
969
+ # ``cdef foo from "header"``, or ``for foo from 0 < i < 10``
970
+ default('#pop'),
971
+ ],
972
+ 'stringescape': [
973
+ (r'\\([\\abfnrtv"\']|\n|N\{.*?\}|u[a-fA-F0-9]{4}|'
974
+ r'U[a-fA-F0-9]{8}|x[a-fA-F0-9]{2}|[0-7]{1,3})', String.Escape)
975
+ ],
976
+ 'strings': [
977
+ (r'%(\([a-zA-Z0-9]+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?'
978
+ '[hlL]?[E-GXc-giorsux%]', String.Interpol),
979
+ (r'[^\\\'"%\n]+', String),
980
+ # quotes, percents and backslashes must be parsed one at a time
981
+ (r'[\'"\\]', String),
982
+ # unhandled string formatting sign
983
+ (r'%', String)
984
+ # newlines are an error (use "nl" state)
985
+ ],
986
+ 'nl': [
987
+ (r'\n', String)
988
+ ],
989
+ 'dqs': [
990
+ (r'"', String, '#pop'),
991
+ (r'\\\\|\\"|\\\n', String.Escape), # included here again for raw strings
992
+ include('strings')
993
+ ],
994
+ 'sqs': [
995
+ (r"'", String, '#pop'),
996
+ (r"\\\\|\\'|\\\n", String.Escape), # included here again for raw strings
997
+ include('strings')
998
+ ],
999
+ 'tdqs': [
1000
+ (r'"""', String, '#pop'),
1001
+ include('strings'),
1002
+ include('nl')
1003
+ ],
1004
+ 'tsqs': [
1005
+ (r"'''", String, '#pop'),
1006
+ include('strings'),
1007
+ include('nl')
1008
+ ],
1009
+ }
1010
+
1011
+
1012
+ class DgLexer(RegexLexer):
1013
+ """
1014
+ Lexer for dg,
1015
+ a functional and object-oriented programming language
1016
+ running on the CPython 3 VM.
1017
+ """
1018
+ name = 'dg'
1019
+ aliases = ['dg']
1020
+ filenames = ['*.dg']
1021
+ mimetypes = ['text/x-dg']
1022
+ url = 'http://pyos.github.io/dg'
1023
+ version_added = '1.6'
1024
+
1025
+ tokens = {
1026
+ 'root': [
1027
+ (r'\s+', Text),
1028
+ (r'#.*?$', Comment.Single),
1029
+
1030
+ (r'(?i)0b[01]+', Number.Bin),
1031
+ (r'(?i)0o[0-7]+', Number.Oct),
1032
+ (r'(?i)0x[0-9a-f]+', Number.Hex),
1033
+ (r'(?i)[+-]?[0-9]+\.[0-9]+(e[+-]?[0-9]+)?j?', Number.Float),
1034
+ (r'(?i)[+-]?[0-9]+e[+-]?\d+j?', Number.Float),
1035
+ (r'(?i)[+-]?[0-9]+j?', Number.Integer),
1036
+
1037
+ (r"(?i)(br|r?b?)'''", String, combined('stringescape', 'tsqs', 'string')),
1038
+ (r'(?i)(br|r?b?)"""', String, combined('stringescape', 'tdqs', 'string')),
1039
+ (r"(?i)(br|r?b?)'", String, combined('stringescape', 'sqs', 'string')),
1040
+ (r'(?i)(br|r?b?)"', String, combined('stringescape', 'dqs', 'string')),
1041
+
1042
+ (r"`\w+'*`", Operator),
1043
+ (r'\b(and|in|is|or|where)\b', Operator.Word),
1044
+ (r'[!$%&*+\-./:<-@\\^|~;,]+', Operator),
1045
+
1046
+ (words((
1047
+ 'bool', 'bytearray', 'bytes', 'classmethod', 'complex', 'dict', 'dict\'',
1048
+ 'float', 'frozenset', 'int', 'list', 'list\'', 'memoryview', 'object',
1049
+ 'property', 'range', 'set', 'set\'', 'slice', 'staticmethod', 'str',
1050
+ 'super', 'tuple', 'tuple\'', 'type'),
1051
+ prefix=r'(?<!\.)', suffix=r'(?![\'\w])'),
1052
+ Name.Builtin),
1053
+ (words((
1054
+ '__import__', 'abs', 'all', 'any', 'bin', 'bind', 'chr', 'cmp', 'compile',
1055
+ 'complex', 'delattr', 'dir', 'divmod', 'drop', 'dropwhile', 'enumerate',
1056
+ 'eval', 'exhaust', 'filter', 'flip', 'foldl1?', 'format', 'fst',
1057
+ 'getattr', 'globals', 'hasattr', 'hash', 'head', 'hex', 'id', 'init',
1058
+ 'input', 'isinstance', 'issubclass', 'iter', 'iterate', 'last', 'len',
1059
+ 'locals', 'map', 'max', 'min', 'next', 'oct', 'open', 'ord', 'pow',
1060
+ 'print', 'repr', 'reversed', 'round', 'setattr', 'scanl1?', 'snd',
1061
+ 'sorted', 'sum', 'tail', 'take', 'takewhile', 'vars', 'zip'),
1062
+ prefix=r'(?<!\.)', suffix=r'(?![\'\w])'),
1063
+ Name.Builtin),
1064
+ (r"(?<!\.)(self|Ellipsis|NotImplemented|None|True|False)(?!['\w])",
1065
+ Name.Builtin.Pseudo),
1066
+
1067
+ (r"(?<!\.)[A-Z]\w*(Error|Exception|Warning)'*(?!['\w])",
1068
+ Name.Exception),
1069
+ (r"(?<!\.)(Exception|GeneratorExit|KeyboardInterrupt|StopIteration|"
1070
+ r"SystemExit)(?!['\w])", Name.Exception),
1071
+
1072
+ (r"(?<![\w.])(except|finally|for|if|import|not|otherwise|raise|"
1073
+ r"subclass|while|with|yield)(?!['\w])", Keyword.Reserved),
1074
+
1075
+ (r"[A-Z_]+'*(?!['\w])", Name),
1076
+ (r"[A-Z]\w+'*(?!['\w])", Keyword.Type),
1077
+ (r"\w+'*", Name),
1078
+
1079
+ (r'[()]', Punctuation),
1080
+ (r'.', Error),
1081
+ ],
1082
+ 'stringescape': [
1083
+ (r'\\([\\abfnrtv"\']|\n|N\{.*?\}|u[a-fA-F0-9]{4}|'
1084
+ r'U[a-fA-F0-9]{8}|x[a-fA-F0-9]{2}|[0-7]{1,3})', String.Escape)
1085
+ ],
1086
+ 'string': [
1087
+ (r'%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?'
1088
+ '[hlL]?[E-GXc-giorsux%]', String.Interpol),
1089
+ (r'[^\\\'"%\n]+', String),
1090
+ # quotes, percents and backslashes must be parsed one at a time
1091
+ (r'[\'"\\]', String),
1092
+ # unhandled string formatting sign
1093
+ (r'%', String),
1094
+ (r'\n', String)
1095
+ ],
1096
+ 'dqs': [
1097
+ (r'"', String, '#pop')
1098
+ ],
1099
+ 'sqs': [
1100
+ (r"'", String, '#pop')
1101
+ ],
1102
+ 'tdqs': [
1103
+ (r'"""', String, '#pop')
1104
+ ],
1105
+ 'tsqs': [
1106
+ (r"'''", String, '#pop')
1107
+ ],
1108
+ }
1109
+
1110
+
1111
+ class NumPyLexer(PythonLexer):
1112
+ """
1113
+ A Python lexer recognizing Numerical Python builtins.
1114
+ """
1115
+
1116
+ name = 'NumPy'
1117
+ url = 'https://numpy.org/'
1118
+ aliases = ['numpy']
1119
+ version_added = '0.10'
1120
+
1121
+ # override the mimetypes to not inherit them from python
1122
+ mimetypes = []
1123
+ filenames = []
1124
+
1125
+ EXTRA_KEYWORDS = {
1126
+ 'abs', 'absolute', 'accumulate', 'add', 'alen', 'all', 'allclose',
1127
+ 'alltrue', 'alterdot', 'amax', 'amin', 'angle', 'any', 'append',
1128
+ 'apply_along_axis', 'apply_over_axes', 'arange', 'arccos', 'arccosh',
1129
+ 'arcsin', 'arcsinh', 'arctan', 'arctan2', 'arctanh', 'argmax', 'argmin',
1130
+ 'argsort', 'argwhere', 'around', 'array', 'array2string', 'array_equal',
1131
+ 'array_equiv', 'array_repr', 'array_split', 'array_str', 'arrayrange',
1132
+ 'asanyarray', 'asarray', 'asarray_chkfinite', 'ascontiguousarray',
1133
+ 'asfarray', 'asfortranarray', 'asmatrix', 'asscalar', 'astype',
1134
+ 'atleast_1d', 'atleast_2d', 'atleast_3d', 'average', 'bartlett',
1135
+ 'base_repr', 'beta', 'binary_repr', 'bincount', 'binomial',
1136
+ 'bitwise_and', 'bitwise_not', 'bitwise_or', 'bitwise_xor', 'blackman',
1137
+ 'bmat', 'broadcast', 'byte_bounds', 'bytes', 'byteswap', 'c_',
1138
+ 'can_cast', 'ceil', 'choose', 'clip', 'column_stack', 'common_type',
1139
+ 'compare_chararrays', 'compress', 'concatenate', 'conj', 'conjugate',
1140
+ 'convolve', 'copy', 'corrcoef', 'correlate', 'cos', 'cosh', 'cov',
1141
+ 'cross', 'cumprod', 'cumproduct', 'cumsum', 'delete', 'deprecate',
1142
+ 'diag', 'diagflat', 'diagonal', 'diff', 'digitize', 'disp', 'divide',
1143
+ 'dot', 'dsplit', 'dstack', 'dtype', 'dump', 'dumps', 'ediff1d', 'empty',
1144
+ 'empty_like', 'equal', 'exp', 'expand_dims', 'expm1', 'extract', 'eye',
1145
+ 'fabs', 'fastCopyAndTranspose', 'fft', 'fftfreq', 'fftshift', 'fill',
1146
+ 'finfo', 'fix', 'flat', 'flatnonzero', 'flatten', 'fliplr', 'flipud',
1147
+ 'floor', 'floor_divide', 'fmod', 'frexp', 'fromarrays', 'frombuffer',
1148
+ 'fromfile', 'fromfunction', 'fromiter', 'frompyfunc', 'fromstring',
1149
+ 'generic', 'get_array_wrap', 'get_include', 'get_numarray_include',
1150
+ 'get_numpy_include', 'get_printoptions', 'getbuffer', 'getbufsize',
1151
+ 'geterr', 'geterrcall', 'geterrobj', 'getfield', 'gradient', 'greater',
1152
+ 'greater_equal', 'gumbel', 'hamming', 'hanning', 'histogram',
1153
+ 'histogram2d', 'histogramdd', 'hsplit', 'hstack', 'hypot', 'i0',
1154
+ 'identity', 'ifft', 'imag', 'index_exp', 'indices', 'inf', 'info',
1155
+ 'inner', 'insert', 'int_asbuffer', 'interp', 'intersect1d',
1156
+ 'intersect1d_nu', 'inv', 'invert', 'iscomplex', 'iscomplexobj',
1157
+ 'isfinite', 'isfortran', 'isinf', 'isnan', 'isneginf', 'isposinf',
1158
+ 'isreal', 'isrealobj', 'isscalar', 'issctype', 'issubclass_',
1159
+ 'issubdtype', 'issubsctype', 'item', 'itemset', 'iterable', 'ix_',
1160
+ 'kaiser', 'kron', 'ldexp', 'left_shift', 'less', 'less_equal', 'lexsort',
1161
+ 'linspace', 'load', 'loads', 'loadtxt', 'log', 'log10', 'log1p', 'log2',
1162
+ 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'logspace',
1163
+ 'lstsq', 'mat', 'matrix', 'max', 'maximum', 'maximum_sctype',
1164
+ 'may_share_memory', 'mean', 'median', 'meshgrid', 'mgrid', 'min',
1165
+ 'minimum', 'mintypecode', 'mod', 'modf', 'msort', 'multiply', 'nan',
1166
+ 'nan_to_num', 'nanargmax', 'nanargmin', 'nanmax', 'nanmin', 'nansum',
1167
+ 'ndenumerate', 'ndim', 'ndindex', 'negative', 'newaxis', 'newbuffer',
1168
+ 'newbyteorder', 'nonzero', 'not_equal', 'obj2sctype', 'ogrid', 'ones',
1169
+ 'ones_like', 'outer', 'permutation', 'piecewise', 'pinv', 'pkgload',
1170
+ 'place', 'poisson', 'poly', 'poly1d', 'polyadd', 'polyder', 'polydiv',
1171
+ 'polyfit', 'polyint', 'polymul', 'polysub', 'polyval', 'power', 'prod',
1172
+ 'product', 'ptp', 'put', 'putmask', 'r_', 'randint', 'random_integers',
1173
+ 'random_sample', 'ranf', 'rank', 'ravel', 'real', 'real_if_close',
1174
+ 'recarray', 'reciprocal', 'reduce', 'remainder', 'repeat', 'require',
1175
+ 'reshape', 'resize', 'restoredot', 'right_shift', 'rint', 'roll',
1176
+ 'rollaxis', 'roots', 'rot90', 'round', 'round_', 'row_stack', 's_',
1177
+ 'sample', 'savetxt', 'sctype2char', 'searchsorted', 'seed', 'select',
1178
+ 'set_numeric_ops', 'set_printoptions', 'set_string_function',
1179
+ 'setbufsize', 'setdiff1d', 'seterr', 'seterrcall', 'seterrobj',
1180
+ 'setfield', 'setflags', 'setmember1d', 'setxor1d', 'shape',
1181
+ 'show_config', 'shuffle', 'sign', 'signbit', 'sin', 'sinc', 'sinh',
1182
+ 'size', 'slice', 'solve', 'sometrue', 'sort', 'sort_complex', 'source',
1183
+ 'split', 'sqrt', 'square', 'squeeze', 'standard_normal', 'std',
1184
+ 'subtract', 'sum', 'svd', 'swapaxes', 'take', 'tan', 'tanh', 'tensordot',
1185
+ 'test', 'tile', 'tofile', 'tolist', 'tostring', 'trace', 'transpose',
1186
+ 'trapz', 'tri', 'tril', 'trim_zeros', 'triu', 'true_divide', 'typeDict',
1187
+ 'typename', 'uniform', 'union1d', 'unique', 'unique1d', 'unravel_index',
1188
+ 'unwrap', 'vander', 'var', 'vdot', 'vectorize', 'view', 'vonmises',
1189
+ 'vsplit', 'vstack', 'weibull', 'where', 'who', 'zeros', 'zeros_like'
1190
+ }
1191
+
1192
+ def get_tokens_unprocessed(self, text):
1193
+ for index, token, value in \
1194
+ PythonLexer.get_tokens_unprocessed(self, text):
1195
+ if token is Name and value in self.EXTRA_KEYWORDS:
1196
+ yield index, Keyword.Pseudo, value
1197
+ else:
1198
+ yield index, token, value
1199
+
1200
+ def analyse_text(text):
1201
+ ltext = text[:1000]
1202
+ return (shebang_matches(text, r'pythonw?(3(\.\d)?)?') or
1203
+ 'import ' in ltext) \
1204
+ and ('import numpy' in ltext or 'from numpy import' in ltext)
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/modeline.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.modeline
3
+ ~~~~~~~~~~~~~~~~~
4
+
5
+ A simple modeline parser (based on pymodeline).
6
+
7
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import re
12
+
13
+ __all__ = ['get_filetype_from_buffer']
14
+
15
+
16
+ modeline_re = re.compile(r'''
17
+ (?: vi | vim | ex ) (?: [<=>]? \d* )? :
18
+ .* (?: ft | filetype | syn | syntax ) = ( [^:\s]+ )
19
+ ''', re.VERBOSE)
20
+
21
+
22
+ def get_filetype_from_line(l): # noqa: E741
23
+ m = modeline_re.search(l)
24
+ if m:
25
+ return m.group(1)
26
+
27
+
28
+ def get_filetype_from_buffer(buf, max_lines=5):
29
+ """
30
+ Scan the buffer for modelines and return filetype if one is found.
31
+ """
32
+ lines = buf.splitlines()
33
+ for line in lines[-1:-max_lines-1:-1]:
34
+ ret = get_filetype_from_line(line)
35
+ if ret:
36
+ return ret
37
+ for i in range(max_lines, -1, -1):
38
+ if i < len(lines):
39
+ ret = get_filetype_from_line(lines[i])
40
+ if ret:
41
+ return ret
42
+
43
+ return None
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/plugin.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.plugin
3
+ ~~~~~~~~~~~~~~~
4
+
5
+ Pygments plugin interface.
6
+
7
+ lexer plugins::
8
+
9
+ [pygments.lexers]
10
+ yourlexer = yourmodule:YourLexer
11
+
12
+ formatter plugins::
13
+
14
+ [pygments.formatters]
15
+ yourformatter = yourformatter:YourFormatter
16
+ /.ext = yourformatter:YourFormatter
17
+
18
+ As you can see, you can define extensions for the formatter
19
+ with a leading slash.
20
+
21
+ syntax plugins::
22
+
23
+ [pygments.styles]
24
+ yourstyle = yourstyle:YourStyle
25
+
26
+ filter plugin::
27
+
28
+ [pygments.filter]
29
+ yourfilter = yourfilter:YourFilter
30
+
31
+
32
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
33
+ :license: BSD, see LICENSE for details.
34
+ """
35
+ import functools
36
+ from importlib.metadata import entry_points
37
+
38
+ LEXER_ENTRY_POINT = 'pygments.lexers'
39
+ FORMATTER_ENTRY_POINT = 'pygments.formatters'
40
+ STYLE_ENTRY_POINT = 'pygments.styles'
41
+ FILTER_ENTRY_POINT = 'pygments.filters'
42
+
43
+
44
+ @functools.cache
45
+ def iter_entry_points(group_name):
46
+ groups = entry_points()
47
+ if hasattr(groups, 'select'):
48
+ # New interface in Python 3.10 and newer versions of the
49
+ # importlib_metadata backport.
50
+ return groups.select(group=group_name)
51
+ else:
52
+ # Older interface, deprecated in Python 3.10 and recent
53
+ # importlib_metadata, but we need it in Python 3.8 and 3.9.
54
+ return groups.get(group_name, [])
55
+
56
+
57
+ def find_plugin_lexers():
58
+ for entrypoint in iter_entry_points(LEXER_ENTRY_POINT):
59
+ yield entrypoint.load()
60
+
61
+
62
+ def find_plugin_formatters():
63
+ for entrypoint in iter_entry_points(FORMATTER_ENTRY_POINT):
64
+ yield entrypoint.name, entrypoint.load()
65
+
66
+
67
+ def find_plugin_styles():
68
+ for entrypoint in iter_entry_points(STYLE_ENTRY_POINT):
69
+ yield entrypoint.name, entrypoint.load()
70
+
71
+
72
+ def find_plugin_filters():
73
+ for entrypoint in iter_entry_points(FILTER_ENTRY_POINT):
74
+ yield entrypoint.name, entrypoint.load()
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/regexopt.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.regexopt
3
+ ~~~~~~~~~~~~~~~~~
4
+
5
+ An algorithm that generates optimized regexes for matching long lists of
6
+ literal strings.
7
+
8
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
9
+ :license: BSD, see LICENSE for details.
10
+ """
11
+
12
+ import re
13
+ from re import escape
14
+ from itertools import groupby
15
+ from operator import itemgetter
16
+
17
+ CS_ESCAPE = re.compile(r'[\[\^\\\-\]]')
18
+ FIRST_ELEMENT = itemgetter(0)
19
+
20
+
21
+ def commonprefix(m):
22
+ """Given an iterable of strings, returns the longest common leading substring"""
23
+ if not m:
24
+ return ""
25
+ s1 = min(m)
26
+ s2 = max(m)
27
+ for i, c in enumerate(s1):
28
+ if c != s2[i]:
29
+ return s1[:i]
30
+ return s1
31
+
32
+
33
+ def make_charset(letters):
34
+ return '[' + CS_ESCAPE.sub(lambda m: '\\' + m.group(), ''.join(letters)) + ']'
35
+
36
+
37
+ def regex_opt_inner(strings, open_paren):
38
+ """Return a regex that matches any string in the sorted list of strings."""
39
+ close_paren = open_paren and ')' or ''
40
+ # print strings, repr(open_paren)
41
+ if not strings:
42
+ # print '-> nothing left'
43
+ return ''
44
+ first = strings[0]
45
+ if len(strings) == 1:
46
+ # print '-> only 1 string'
47
+ return open_paren + escape(first) + close_paren
48
+ if not first:
49
+ # print '-> first string empty'
50
+ return open_paren + regex_opt_inner(strings[1:], '(?:') \
51
+ + '?' + close_paren
52
+ if len(first) == 1:
53
+ # multiple one-char strings? make a charset
54
+ oneletter = []
55
+ rest = []
56
+ for s in strings:
57
+ if len(s) == 1:
58
+ oneletter.append(s)
59
+ else:
60
+ rest.append(s)
61
+ if len(oneletter) > 1: # do we have more than one oneletter string?
62
+ if rest:
63
+ # print '-> 1-character + rest'
64
+ return open_paren + regex_opt_inner(rest, '') + '|' \
65
+ + make_charset(oneletter) + close_paren
66
+ # print '-> only 1-character'
67
+ return open_paren + make_charset(oneletter) + close_paren
68
+ prefix = commonprefix(strings)
69
+ if prefix:
70
+ plen = len(prefix)
71
+ # we have a prefix for all strings
72
+ # print '-> prefix:', prefix
73
+ return open_paren + escape(prefix) \
74
+ + regex_opt_inner([s[plen:] for s in strings], '(?:') \
75
+ + close_paren
76
+ # is there a suffix?
77
+ strings_rev = [s[::-1] for s in strings]
78
+ suffix = commonprefix(strings_rev)
79
+ if suffix:
80
+ slen = len(suffix)
81
+ # print '-> suffix:', suffix[::-1]
82
+ return open_paren \
83
+ + regex_opt_inner(sorted(s[:-slen] for s in strings), '(?:') \
84
+ + escape(suffix[::-1]) + close_paren
85
+ # recurse on common 1-string prefixes
86
+ # print '-> last resort'
87
+ return open_paren + \
88
+ '|'.join(regex_opt_inner(list(group[1]), '')
89
+ for group in groupby(strings, lambda s: s[0] == first[0])) \
90
+ + close_paren
91
+
92
+
93
+ def regex_opt(strings, prefix='', suffix=''):
94
+ """Return a compiled regex that matches any string in the given list.
95
+
96
+ The strings to match must be literal strings, not regexes. They will be
97
+ regex-escaped.
98
+
99
+ *prefix* and *suffix* are pre- and appended to the final regex.
100
+ """
101
+ strings = sorted(strings)
102
+ return prefix + regex_opt_inner(strings, '(') + suffix
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/scanner.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.scanner
3
+ ~~~~~~~~~~~~~~~~
4
+
5
+ This library implements a regex based scanner. Some languages
6
+ like Pascal are easy to parse but have some keywords that
7
+ depend on the context. Because of this it's impossible to lex
8
+ that just by using a regular expression lexer like the
9
+ `RegexLexer`.
10
+
11
+ Have a look at the `DelphiLexer` to get an idea of how to use
12
+ this scanner.
13
+
14
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
15
+ :license: BSD, see LICENSE for details.
16
+ """
17
+ import re
18
+
19
+
20
+ class EndOfText(RuntimeError):
21
+ """
22
+ Raise if end of text is reached and the user
23
+ tried to call a match function.
24
+ """
25
+
26
+
27
+ class Scanner:
28
+ """
29
+ Simple scanner
30
+
31
+ All method patterns are regular expression strings (not
32
+ compiled expressions!)
33
+ """
34
+
35
+ def __init__(self, text, flags=0):
36
+ """
37
+ :param text: The text which should be scanned
38
+ :param flags: default regular expression flags
39
+ """
40
+ self.data = text
41
+ self.data_length = len(text)
42
+ self.start_pos = 0
43
+ self.pos = 0
44
+ self.flags = flags
45
+ self.last = None
46
+ self.match = None
47
+ self._re_cache = {}
48
+
49
+ def eos(self):
50
+ """`True` if the scanner reached the end of text."""
51
+ return self.pos >= self.data_length
52
+ eos = property(eos, eos.__doc__)
53
+
54
+ def check(self, pattern):
55
+ """
56
+ Apply `pattern` on the current position and return
57
+ the match object. (Doesn't touch pos). Use this for
58
+ lookahead.
59
+ """
60
+ if self.eos:
61
+ raise EndOfText()
62
+ if pattern not in self._re_cache:
63
+ self._re_cache[pattern] = re.compile(pattern, self.flags)
64
+ return self._re_cache[pattern].match(self.data, self.pos)
65
+
66
+ def test(self, pattern):
67
+ """Apply a pattern on the current position and check
68
+ if it patches. Doesn't touch pos.
69
+ """
70
+ return self.check(pattern) is not None
71
+
72
+ def scan(self, pattern):
73
+ """
74
+ Scan the text for the given pattern and update pos/match
75
+ and related fields. The return value is a boolean that
76
+ indicates if the pattern matched. The matched value is
77
+ stored on the instance as ``match``, the last value is
78
+ stored as ``last``. ``start_pos`` is the position of the
79
+ pointer before the pattern was matched, ``pos`` is the
80
+ end position.
81
+ """
82
+ if self.eos:
83
+ raise EndOfText()
84
+ if pattern not in self._re_cache:
85
+ self._re_cache[pattern] = re.compile(pattern, self.flags)
86
+ self.last = self.match
87
+ m = self._re_cache[pattern].match(self.data, self.pos)
88
+ if m is None:
89
+ return False
90
+ self.start_pos = m.start()
91
+ self.pos = m.end()
92
+ self.match = m.group()
93
+ return True
94
+
95
+ def get_char(self):
96
+ """Scan exactly one char."""
97
+ self.scan('.')
98
+
99
+ def __repr__(self):
100
+ return '<%s %d/%d>' % (
101
+ self.__class__.__name__,
102
+ self.pos,
103
+ self.data_length
104
+ )
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/sphinxext.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.sphinxext
3
+ ~~~~~~~~~~~~~~~~~~
4
+
5
+ Sphinx extension to generate automatic documentation of lexers,
6
+ formatters and filters.
7
+
8
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
9
+ :license: BSD, see LICENSE for details.
10
+ """
11
+
12
+ import sys
13
+
14
+ from docutils import nodes
15
+ from docutils.statemachine import ViewList
16
+ from docutils.parsers.rst import Directive
17
+ from sphinx.util.nodes import nested_parse_with_titles
18
+
19
+
20
+ MODULEDOC = '''
21
+ .. module:: %s
22
+
23
+ %s
24
+ %s
25
+ '''
26
+
27
+ LEXERDOC = '''
28
+ .. class:: %s
29
+
30
+ :Short names: %s
31
+ :Filenames: %s
32
+ :MIME types: %s
33
+
34
+ %s
35
+
36
+ %s
37
+
38
+ '''
39
+
40
+ FMTERDOC = '''
41
+ .. class:: %s
42
+
43
+ :Short names: %s
44
+ :Filenames: %s
45
+
46
+ %s
47
+
48
+ '''
49
+
50
+ FILTERDOC = '''
51
+ .. class:: %s
52
+
53
+ :Name: %s
54
+
55
+ %s
56
+
57
+ '''
58
+
59
+
60
+ class PygmentsDoc(Directive):
61
+ """
62
+ A directive to collect all lexers/formatters/filters and generate
63
+ autoclass directives for them.
64
+ """
65
+ has_content = False
66
+ required_arguments = 1
67
+ optional_arguments = 0
68
+ final_argument_whitespace = False
69
+ option_spec = {}
70
+
71
+ def run(self):
72
+ self.filenames = set()
73
+ if self.arguments[0] == 'lexers':
74
+ out = self.document_lexers()
75
+ elif self.arguments[0] == 'formatters':
76
+ out = self.document_formatters()
77
+ elif self.arguments[0] == 'filters':
78
+ out = self.document_filters()
79
+ elif self.arguments[0] == 'lexers_overview':
80
+ out = self.document_lexers_overview()
81
+ else:
82
+ raise Exception('invalid argument for "pygmentsdoc" directive')
83
+ node = nodes.compound()
84
+ vl = ViewList(out.split('\n'), source='')
85
+ nested_parse_with_titles(self.state, vl, node)
86
+ for fn in self.filenames:
87
+ self.state.document.settings.record_dependencies.add(fn)
88
+ return node.children
89
+
90
+ def document_lexers_overview(self):
91
+ """Generate a tabular overview of all lexers.
92
+
93
+ The columns are the lexer name, the extensions handled by this lexer
94
+ (or "None"), the aliases and a link to the lexer class."""
95
+ from pip._vendor.pygments.lexers._mapping import LEXERS
96
+ from pip._vendor.pygments.lexers import find_lexer_class
97
+ out = []
98
+
99
+ table = []
100
+
101
+ def format_link(name, url):
102
+ if url:
103
+ return f'`{name} <{url}>`_'
104
+ return name
105
+
106
+ for classname, data in sorted(LEXERS.items(), key=lambda x: x[1][1].lower()):
107
+ lexer_cls = find_lexer_class(data[1])
108
+ extensions = lexer_cls.filenames + lexer_cls.alias_filenames
109
+
110
+ table.append({
111
+ 'name': format_link(data[1], lexer_cls.url),
112
+ 'extensions': ', '.join(extensions).replace('*', '\\*').replace('_', '\\') or 'None',
113
+ 'aliases': ', '.join(data[2]),
114
+ 'class': f'{data[0]}.{classname}'
115
+ })
116
+
117
+ column_names = ['name', 'extensions', 'aliases', 'class']
118
+ column_lengths = [max([len(row[column]) for row in table if row[column]])
119
+ for column in column_names]
120
+
121
+ def write_row(*columns):
122
+ """Format a table row"""
123
+ out = []
124
+ for length, col in zip(column_lengths, columns):
125
+ if col:
126
+ out.append(col.ljust(length))
127
+ else:
128
+ out.append(' '*length)
129
+
130
+ return ' '.join(out)
131
+
132
+ def write_seperator():
133
+ """Write a table separator row"""
134
+ sep = ['='*c for c in column_lengths]
135
+ return write_row(*sep)
136
+
137
+ out.append(write_seperator())
138
+ out.append(write_row('Name', 'Extension(s)', 'Short name(s)', 'Lexer class'))
139
+ out.append(write_seperator())
140
+ for row in table:
141
+ out.append(write_row(
142
+ row['name'],
143
+ row['extensions'],
144
+ row['aliases'],
145
+ f':class:`~{row["class"]}`'))
146
+ out.append(write_seperator())
147
+
148
+ return '\n'.join(out)
149
+
150
+ def document_lexers(self):
151
+ from pip._vendor.pygments.lexers._mapping import LEXERS
152
+ from pip._vendor import pygments
153
+ import inspect
154
+ import pathlib
155
+
156
+ out = []
157
+ modules = {}
158
+ moduledocstrings = {}
159
+ for classname, data in sorted(LEXERS.items(), key=lambda x: x[0]):
160
+ module = data[0]
161
+ mod = __import__(module, None, None, [classname])
162
+ self.filenames.add(mod.__file__)
163
+ cls = getattr(mod, classname)
164
+ if not cls.__doc__:
165
+ print(f"Warning: {classname} does not have a docstring.")
166
+ docstring = cls.__doc__
167
+ if isinstance(docstring, bytes):
168
+ docstring = docstring.decode('utf8')
169
+
170
+ example_file = getattr(cls, '_example', None)
171
+ if example_file:
172
+ p = pathlib.Path(inspect.getabsfile(pygments)).parent.parent /\
173
+ 'tests' / 'examplefiles' / example_file
174
+ content = p.read_text(encoding='utf-8')
175
+ if not content:
176
+ raise Exception(
177
+ f"Empty example file '{example_file}' for lexer "
178
+ f"{classname}")
179
+
180
+ if data[2]:
181
+ lexer_name = data[2][0]
182
+ docstring += '\n\n .. admonition:: Example\n'
183
+ docstring += f'\n .. code-block:: {lexer_name}\n\n'
184
+ for line in content.splitlines():
185
+ docstring += f' {line}\n'
186
+
187
+ if cls.version_added:
188
+ version_line = f'.. versionadded:: {cls.version_added}'
189
+ else:
190
+ version_line = ''
191
+
192
+ modules.setdefault(module, []).append((
193
+ classname,
194
+ ', '.join(data[2]) or 'None',
195
+ ', '.join(data[3]).replace('*', '\\*').replace('_', '\\') or 'None',
196
+ ', '.join(data[4]) or 'None',
197
+ docstring,
198
+ version_line))
199
+ if module not in moduledocstrings:
200
+ moddoc = mod.__doc__
201
+ if isinstance(moddoc, bytes):
202
+ moddoc = moddoc.decode('utf8')
203
+ moduledocstrings[module] = moddoc
204
+
205
+ for module, lexers in sorted(modules.items(), key=lambda x: x[0]):
206
+ if moduledocstrings[module] is None:
207
+ raise Exception(f"Missing docstring for {module}")
208
+ heading = moduledocstrings[module].splitlines()[4].strip().rstrip('.')
209
+ out.append(MODULEDOC % (module, heading, '-'*len(heading)))
210
+ for data in lexers:
211
+ out.append(LEXERDOC % data)
212
+
213
+ return ''.join(out)
214
+
215
+ def document_formatters(self):
216
+ from pip._vendor.pygments.formatters import FORMATTERS
217
+
218
+ out = []
219
+ for classname, data in sorted(FORMATTERS.items(), key=lambda x: x[0]):
220
+ module = data[0]
221
+ mod = __import__(module, None, None, [classname])
222
+ self.filenames.add(mod.__file__)
223
+ cls = getattr(mod, classname)
224
+ docstring = cls.__doc__
225
+ if isinstance(docstring, bytes):
226
+ docstring = docstring.decode('utf8')
227
+ heading = cls.__name__
228
+ out.append(FMTERDOC % (heading, ', '.join(data[2]) or 'None',
229
+ ', '.join(data[3]).replace('*', '\\*') or 'None',
230
+ docstring))
231
+ return ''.join(out)
232
+
233
+ def document_filters(self):
234
+ from pip._vendor.pygments.filters import FILTERS
235
+
236
+ out = []
237
+ for name, cls in FILTERS.items():
238
+ self.filenames.add(sys.modules[cls.__module__].__file__)
239
+ docstring = cls.__doc__
240
+ if isinstance(docstring, bytes):
241
+ docstring = docstring.decode('utf8')
242
+ out.append(FILTERDOC % (cls.__name__, name, docstring))
243
+ return ''.join(out)
244
+
245
+
246
+ def setup(app):
247
+ app.add_directive('pygmentsdoc', PygmentsDoc)
.venv/lib/python3.14/site-packages/pip/_vendor/pygments/style.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.style
3
+ ~~~~~~~~~~~~~~
4
+
5
+ Basic style object.
6
+
7
+ :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ from pip._vendor.pygments.token import Token, STANDARD_TYPES
12
+
13
+ # Default mapping of ansixxx to RGB colors.
14
+ _ansimap = {
15
+ # dark
16
+ 'ansiblack': '000000',
17
+ 'ansired': '7f0000',
18
+ 'ansigreen': '007f00',
19
+ 'ansiyellow': '7f7fe0',
20
+ 'ansiblue': '00007f',
21
+ 'ansimagenta': '7f007f',
22
+ 'ansicyan': '007f7f',
23
+ 'ansigray': 'e5e5e5',
24
+ # normal
25
+ 'ansibrightblack': '555555',
26
+ 'ansibrightred': 'ff0000',
27
+ 'ansibrightgreen': '00ff00',
28
+ 'ansibrightyellow': 'ffff00',
29
+ 'ansibrightblue': '0000ff',
30
+ 'ansibrightmagenta': 'ff00ff',
31
+ 'ansibrightcyan': '00ffff',
32
+ 'ansiwhite': 'ffffff',
33
+ }
34
+ # mapping of deprecated #ansixxx colors to new color names
35
+ _deprecated_ansicolors = {
36
+ # dark
37
+ '#ansiblack': 'ansiblack',
38
+ '#ansidarkred': 'ansired',
39
+ '#ansidarkgreen': 'ansigreen',
40
+ '#ansibrown': 'ansiyellow',
41
+ '#ansidarkblue': 'ansiblue',
42
+ '#ansipurple': 'ansimagenta',
43
+ '#ansiteal': 'ansicyan',
44
+ '#ansilightgray': 'ansigray',
45
+ # normal
46
+ '#ansidarkgray': 'ansibrightblack',
47
+ '#ansired': 'ansibrightred',
48
+ '#ansigreen': 'ansibrightgreen',
49
+ '#ansiyellow': 'ansibrightyellow',
50
+ '#ansiblue': 'ansibrightblue',
51
+ '#ansifuchsia': 'ansibrightmagenta',
52
+ '#ansiturquoise': 'ansibrightcyan',
53
+ '#ansiwhite': 'ansiwhite',
54
+ }
55
+ ansicolors = set(_ansimap)
56
+
57
+
58
+ class StyleMeta(type):
59
+
60
+ def __new__(mcs, name, bases, dct):
61
+ obj = type.__new__(mcs, name, bases, dct)
62
+ for token in STANDARD_TYPES:
63
+ if token not in obj.styles:
64
+ obj.styles[token] = ''
65
+
66
+ def colorformat(text):
67
+ if text in ansicolors:
68
+ return text
69
+ if text[0:1] == '#':
70
+ col = text[1:]
71
+ if len(col) == 6:
72
+ return col
73
+ elif len(col) == 3:
74
+ return col[0] * 2 + col[1] * 2 + col[2] * 2
75
+ elif text == '':
76
+ return ''
77
+ elif text.startswith('var') or text.startswith('calc'):
78
+ return text
79
+ assert False, f"wrong color format {text!r}"
80
+
81
+ _styles = obj._styles = {}
82
+
83
+ for ttype in obj.styles:
84
+ for token in ttype.split():
85
+ if token in _styles:
86
+ continue
87
+ ndef = _styles.get(token.parent, None)
88
+ styledefs = obj.styles.get(token, '').split()
89
+ if not ndef or token is None:
90
+ ndef = ['', 0, 0, 0, '', '', 0, 0, 0]
91
+ elif 'noinherit' in styledefs and token is not Token:
92
+ ndef = _styles[Token][:]
93
+ else:
94
+ ndef = ndef[:]
95
+ _styles[token] = ndef
96
+ for styledef in obj.styles.get(token, '').split():
97
+ if styledef == 'noinherit':
98
+ pass
99
+ elif styledef == 'bold':
100
+ ndef[1] = 1
101
+ elif styledef == 'nobold':
102
+ ndef[1] = 0
103
+ elif styledef == 'italic':
104
+ ndef[2] = 1
105
+ elif styledef == 'noitalic':
106
+ ndef[2] = 0
107
+ elif styledef == 'underline':
108
+ ndef[3] = 1
109
+ elif styledef == 'nounderline':
110
+ ndef[3] = 0
111
+ elif styledef[:3] == 'bg:':
112
+ ndef[4] = colorformat(styledef[3:])
113
+ elif styledef[:7] == 'border:':
114
+ ndef[5] = colorformat(styledef[7:])
115
+ elif styledef == 'roman':
116
+ ndef[6] = 1
117
+ elif styledef == 'sans':
118
+ ndef[7] = 1
119
+ elif styledef == 'mono':
120
+ ndef[8] = 1
121
+ else:
122
+ ndef[0] = colorformat(styledef)
123
+
124
+ return obj
125
+
126
+ def style_for_token(cls, token):
127
+ t = cls._styles[token]
128
+ ansicolor = bgansicolor = None
129
+ color = t[0]
130
+ if color in _deprecated_ansicolors:
131
+ color = _deprecated_ansicolors[color]
132
+ if color in ansicolors:
133
+ ansicolor = color
134
+ color = _ansimap[color]
135
+ bgcolor = t[4]
136
+ if bgcolor in _deprecated_ansicolors:
137
+ bgcolor = _deprecated_ansicolors[bgcolor]
138
+ if bgcolor in ansicolors:
139
+ bgansicolor = bgcolor
140
+ bgcolor = _ansimap[bgcolor]
141
+
142
+ return {
143
+ 'color': color or None,
144
+ 'bold': bool(t[1]),
145
+ 'italic': bool(t[2]),
146
+ 'underline': bool(t[3]),
147
+ 'bgcolor': bgcolor or None,
148
+ 'border': t[5] or None,
149
+ 'roman': bool(t[6]) or None,
150
+ 'sans': bool(t[7]) or None,
151
+ 'mono': bool(t[8]) or None,
152
+ 'ansicolor': ansicolor,
153
+ 'bgansicolor': bgansicolor,
154
+ }
155
+
156
+ def list_styles(cls):
157
+ return list(cls)
158
+
159
+ def styles_token(cls, ttype):
160
+ return ttype in cls._styles
161
+
162
+ def __iter__(cls):
163
+ for token in cls._styles:
164
+ yield token, cls.style_for_token(token)
165
+
166
+ def __len__(cls):
167
+ return len(cls._styles)
168
+
169
+
170
+ class Style(metaclass=StyleMeta):
171
+
172
+ #: overall background color (``None`` means transparent)
173
+ background_color = '#ffffff'
174
+
175
+ #: highlight background color
176
+ highlight_color = '#ffffcc'
177
+
178
+ #: line number font color
179
+ line_number_color = 'inherit'
180
+
181
+ #: line number background color
182
+ line_number_background_color = 'transparent'
183
+
184
+ #: special line number font color
185
+ line_number_special_color = '#000000'
186
+
187
+ #: special line number background color
188
+ line_number_special_background_color = '#ffffc0'
189
+
190
+ #: Style definitions for individual token types.
191
+ styles = {}
192
+
193
+ #: user-friendly style name (used when selecting the style, so this
194
+ # should be all-lowercase, no spaces, hyphens)
195
+ name = 'unnamed'
196
+
197
+ aliases = []
198
+
199
+ # Attribute for lexers defined within Pygments. If set
200
+ # to True, the style is not shown in the style gallery
201
+ # on the website. This is intended for language-specific
202
+ # styles.
203
+ web_style_gallery_exclude = False