diff --git a/lib/python3.12/site-packages/asttokens/__init__.py b/lib/python3.12/site-packages/asttokens/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7b717d9aea96ea5d4cd88bedcc6b108d96331c38 --- /dev/null +++ b/lib/python3.12/site-packages/asttokens/__init__.py @@ -0,0 +1,24 @@ +# Copyright 2016 Grist Labs, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module enhances the Python AST tree with token and source code information, sufficent to +detect the source text of each AST node. This is helpful for tools that make source code +transformations. +""" + +from .line_numbers import LineNumbers +from .asttokens import ASTText, ASTTokens, supports_tokenless + +__all__ = ['ASTText', 'ASTTokens', 'LineNumbers', 'supports_tokenless'] diff --git a/lib/python3.12/site-packages/asttokens/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/asttokens/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..738e5cb3396b2770c180077955a5816d8b4700ef Binary files /dev/null and b/lib/python3.12/site-packages/asttokens/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/asttokens/__pycache__/astroid_compat.cpython-312.pyc b/lib/python3.12/site-packages/asttokens/__pycache__/astroid_compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..40f7ec011315634dc14acb7c4b6ed2325a763453 Binary files /dev/null and b/lib/python3.12/site-packages/asttokens/__pycache__/astroid_compat.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/asttokens/__pycache__/asttokens.cpython-312.pyc b/lib/python3.12/site-packages/asttokens/__pycache__/asttokens.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..567674eac2a961a0c8894d6f4efe61f6ae602041 Binary files /dev/null and b/lib/python3.12/site-packages/asttokens/__pycache__/asttokens.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/asttokens/__pycache__/line_numbers.cpython-312.pyc b/lib/python3.12/site-packages/asttokens/__pycache__/line_numbers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d6c6aa583035ebc9feb8bf8595146b2a4dd6f9f Binary files /dev/null and b/lib/python3.12/site-packages/asttokens/__pycache__/line_numbers.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/asttokens/__pycache__/mark_tokens.cpython-312.pyc b/lib/python3.12/site-packages/asttokens/__pycache__/mark_tokens.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f0b78c773f21f961541077441609e5127ae211b3 Binary files /dev/null and b/lib/python3.12/site-packages/asttokens/__pycache__/mark_tokens.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/asttokens/__pycache__/util.cpython-312.pyc b/lib/python3.12/site-packages/asttokens/__pycache__/util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9208ac90b49353eedfe4a52fd55c2be54a6a0681 Binary files /dev/null and b/lib/python3.12/site-packages/asttokens/__pycache__/util.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/asttokens/__pycache__/version.cpython-312.pyc b/lib/python3.12/site-packages/asttokens/__pycache__/version.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c5021186c834f87af756cb1cdd0b4acf8d8e3c1 Binary files /dev/null and b/lib/python3.12/site-packages/asttokens/__pycache__/version.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/asttokens/astroid_compat.py b/lib/python3.12/site-packages/asttokens/astroid_compat.py new file mode 100644 index 0000000000000000000000000000000000000000..9af3e17e028d5718e9a34da08d4bc3c6757c0596 --- /dev/null +++ b/lib/python3.12/site-packages/asttokens/astroid_compat.py @@ -0,0 +1,18 @@ +try: + from astroid import nodes as astroid_node_classes + + # astroid_node_classes should be whichever module has the NodeNG class + from astroid.nodes import NodeNG + from astroid.nodes import BaseContainer +except Exception: + try: + from astroid import node_classes as astroid_node_classes + from astroid.node_classes import NodeNG + from astroid.node_classes import _BaseContainer as BaseContainer + except Exception: # pragma: no cover + astroid_node_classes = None + NodeNG = None + BaseContainer = None + + +__all__ = ["astroid_node_classes", "NodeNG", "BaseContainer"] diff --git a/lib/python3.12/site-packages/asttokens/asttokens.py b/lib/python3.12/site-packages/asttokens/asttokens.py new file mode 100644 index 0000000000000000000000000000000000000000..6cbc5aad29624b3a4548013010752f365f3ee210 --- /dev/null +++ b/lib/python3.12/site-packages/asttokens/asttokens.py @@ -0,0 +1,450 @@ +# Copyright 2016 Grist Labs, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import abc +import ast +import bisect +import sys +import token +from ast import Module +from typing import Iterable, Iterator, List, Optional, Tuple, Any, cast, TYPE_CHECKING + +from .line_numbers import LineNumbers +from .util import ( + Token, match_token, is_non_coding_token, patched_generate_tokens, last_stmt, + annotate_fstring_nodes, generate_tokens, is_module, is_stmt +) + +if TYPE_CHECKING: # pragma: no cover + from .util import AstNode, TokenInfo + + +class ASTTextBase(metaclass=abc.ABCMeta): + def __init__(self, source_text: str, filename: str) -> None: + self._filename = filename + + # Decode source after parsing to let Python 2 handle coding declarations. + # (If the encoding was not utf-8 compatible, then even if it parses correctly, + # we'll fail with a unicode error here.) + source_text = str(source_text) + + self._text = source_text + self._line_numbers = LineNumbers(source_text) + + @abc.abstractmethod + def get_text_positions(self, node, padded): + # type: (AstNode, bool) -> Tuple[Tuple[int, int], Tuple[int, int]] + """ + Returns two ``(lineno, col_offset)`` tuples for the start and end of the given node. + If the positions can't be determined, or the nodes don't correspond to any particular text, + returns ``(1, 0)`` for both. + + ``padded`` corresponds to the ``padded`` argument to ``ast.get_source_segment()``. + This means that if ``padded`` is True, the start position will be adjusted to include + leading whitespace if ``node`` is a multiline statement. + """ + raise NotImplementedError # pragma: no cover + + def get_text_range(self, node, padded=True): + # type: (AstNode, bool) -> Tuple[int, int] + """ + Returns the (startpos, endpos) positions in source text corresponding to the given node. + Returns (0, 0) for nodes (like `Load`) that don't correspond to any particular text. + + See ``get_text_positions()`` for details on the ``padded`` argument. + """ + start, end = self.get_text_positions(node, padded) + return ( + self._line_numbers.line_to_offset(*start), + self._line_numbers.line_to_offset(*end), + ) + + def get_text(self, node, padded=True): + # type: (AstNode, bool) -> str + """ + Returns the text corresponding to the given node. + Returns '' for nodes (like `Load`) that don't correspond to any particular text. + + See ``get_text_positions()`` for details on the ``padded`` argument. + """ + start, end = self.get_text_range(node, padded) + return self._text[start: end] + + +class ASTTokens(ASTTextBase): + """ + ASTTokens maintains the text of Python code in several forms: as a string, as line numbers, and + as tokens, and is used to mark and access token and position information. + + ``source_text`` must be a unicode or UTF8-encoded string. If you pass in UTF8 bytes, remember + that all offsets you'll get are to the unicode text, which is available as the ``.text`` + property. + + If ``parse`` is set, the ``source_text`` will be parsed with ``ast.parse()``, and the resulting + tree marked with token info and made available as the ``.tree`` property. + + If ``tree`` is given, it will be marked and made available as the ``.tree`` property. In + addition to the trees produced by the ``ast`` module, ASTTokens will also mark trees produced + using ``astroid`` library . + + If only ``source_text`` is given, you may use ``.mark_tokens(tree)`` to mark the nodes of an AST + tree created separately. + """ + + def __init__(self, source_text, parse=False, tree=None, filename='', tokens=None): + # type: (Any, bool, Optional[Module], str, Optional[Iterable[TokenInfo]]) -> None + super(ASTTokens, self).__init__(source_text, filename) + + self._tree = ast.parse(source_text, filename) if parse else tree + + # Tokenize the code. + if tokens is None: + tokens = generate_tokens(self._text) + self._tokens = list(self._translate_tokens(tokens)) + + # Extract the start positions of all tokens, so that we can quickly map positions to tokens. + self._token_offsets = [tok.startpos for tok in self._tokens] + + if self._tree: + self.mark_tokens(self._tree) + + def mark_tokens(self, root_node): + # type: (Module) -> None + """ + Given the root of the AST or Astroid tree produced from source_text, visits all nodes marking + them with token and position information by adding ``.first_token`` and + ``.last_token`` attributes. This is done automatically in the constructor when ``parse`` or + ``tree`` arguments are set, but may be used manually with a separate AST or Astroid tree. + """ + # The hard work of this class is done by MarkTokens + from .mark_tokens import MarkTokens # to avoid import loops + MarkTokens(self).visit_tree(root_node) + + def _translate_tokens(self, original_tokens): + # type: (Iterable[TokenInfo]) -> Iterator[Token] + """ + Translates the given standard library tokens into our own representation. + """ + for index, tok in enumerate(patched_generate_tokens(original_tokens)): + tok_type, tok_str, start, end, line = tok + yield Token(tok_type, tok_str, start, end, line, index, + self._line_numbers.line_to_offset(start[0], start[1]), + self._line_numbers.line_to_offset(end[0], end[1])) + + @property + def text(self): + # type: () -> str + """The source code passed into the constructor.""" + return self._text + + @property + def tokens(self): + # type: () -> List[Token] + """The list of tokens corresponding to the source code from the constructor.""" + return self._tokens + + @property + def tree(self): + # type: () -> Optional[Module] + """The root of the AST tree passed into the constructor or parsed from the source code.""" + return self._tree + + @property + def filename(self): + # type: () -> str + """The filename that was parsed""" + return self._filename + + def get_token_from_offset(self, offset): + # type: (int) -> Token + """ + Returns the token containing the given character offset (0-based position in source text), + or the preceeding token if the position is between tokens. + """ + return self._tokens[bisect.bisect(self._token_offsets, offset) - 1] + + def get_token(self, lineno, col_offset): + # type: (int, int) -> Token + """ + Returns the token containing the given (lineno, col_offset) position, or the preceeding token + if the position is between tokens. + """ + # TODO: add test for multibyte unicode. We need to translate offsets from ast module (which + # are in utf8) to offsets into the unicode text. tokenize module seems to use unicode offsets + # but isn't explicit. + return self.get_token_from_offset(self._line_numbers.line_to_offset(lineno, col_offset)) + + def get_token_from_utf8(self, lineno, col_offset): + # type: (int, int) -> Token + """ + Same as get_token(), but interprets col_offset as a UTF8 offset, which is what `ast` uses. + """ + return self.get_token(lineno, self._line_numbers.from_utf8_col(lineno, col_offset)) + + def next_token(self, tok, include_extra=False): + # type: (Token, bool) -> Token + """ + Returns the next token after the given one. If include_extra is True, includes non-coding + tokens from the tokenize module, such as NL and COMMENT. + """ + i = tok.index + 1 + if not include_extra: + while is_non_coding_token(self._tokens[i].type): + i += 1 + return self._tokens[i] + + def prev_token(self, tok, include_extra=False): + # type: (Token, bool) -> Token + """ + Returns the previous token before the given one. If include_extra is True, includes non-coding + tokens from the tokenize module, such as NL and COMMENT. + """ + i = tok.index - 1 + if not include_extra: + while is_non_coding_token(self._tokens[i].type): + i -= 1 + return self._tokens[i] + + def find_token(self, start_token, tok_type, tok_str=None, reverse=False): + # type: (Token, int, Optional[str], bool) -> Token + """ + Looks for the first token, starting at start_token, that matches tok_type and, if given, the + token string. Searches backwards if reverse is True. Returns ENDMARKER token if not found (you + can check it with `token.ISEOF(t.type)`). + """ + t = start_token + advance = self.prev_token if reverse else self.next_token + while not match_token(t, tok_type, tok_str) and not token.ISEOF(t.type): + t = advance(t, include_extra=True) + return t + + def token_range(self, + first_token, # type: Token + last_token, # type: Token + include_extra=False, # type: bool + ): + # type: (...) -> Iterator[Token] + """ + Yields all tokens in order from first_token through and including last_token. If + include_extra is True, includes non-coding tokens such as tokenize.NL and .COMMENT. + """ + for i in range(first_token.index, last_token.index + 1): + if include_extra or not is_non_coding_token(self._tokens[i].type): + yield self._tokens[i] + + def get_tokens(self, node, include_extra=False): + # type: (AstNode, bool) -> Iterator[Token] + """ + Yields all tokens making up the given node. If include_extra is True, includes non-coding + tokens such as tokenize.NL and .COMMENT. + """ + return self.token_range(node.first_token, node.last_token, include_extra=include_extra) + + def get_text_positions(self, node, padded): + # type: (AstNode, bool) -> Tuple[Tuple[int, int], Tuple[int, int]] + """ + Returns two ``(lineno, col_offset)`` tuples for the start and end of the given node. + If the positions can't be determined, or the nodes don't correspond to any particular text, + returns ``(1, 0)`` for both. + + ``padded`` corresponds to the ``padded`` argument to ``ast.get_source_segment()``. + This means that if ``padded`` is True, the start position will be adjusted to include + leading whitespace if ``node`` is a multiline statement. + """ + if not hasattr(node, 'first_token'): + return (1, 0), (1, 0) + + start = node.first_token.start + end = node.last_token.end + if padded and any(match_token(t, token.NEWLINE) for t in self.get_tokens(node)): + # Set col_offset to 0 to include leading indentation for multiline statements. + start = (start[0], 0) + + return start, end + + +class ASTText(ASTTextBase): + """ + Supports the same ``get_text*`` methods as ``ASTTokens``, + but uses the AST to determine the text positions instead of tokens. + This is faster than ``ASTTokens`` as it requires less setup work. + + It also (sometimes) supports nodes inside f-strings, which ``ASTTokens`` doesn't. + + Some node types and/or Python versions are not supported. + In these cases the ``get_text*`` methods will fall back to using ``ASTTokens`` + which incurs the usual setup cost the first time. + If you want to avoid this, check ``supports_tokenless(node)`` before calling ``get_text*`` methods. + """ + def __init__(self, source_text, tree=None, filename=''): + # type: (Any, Optional[Module], str) -> None + super(ASTText, self).__init__(source_text, filename) + + self._tree = tree + if self._tree is not None: + annotate_fstring_nodes(self._tree) + + self._asttokens = None # type: Optional[ASTTokens] + + @property + def tree(self): + # type: () -> Module + if self._tree is None: + self._tree = ast.parse(self._text, self._filename) + annotate_fstring_nodes(self._tree) + return self._tree + + @property + def asttokens(self): + # type: () -> ASTTokens + if self._asttokens is None: + self._asttokens = ASTTokens( + self._text, + tree=self.tree, + filename=self._filename, + ) + return self._asttokens + + def _get_text_positions_tokenless(self, node, padded): + # type: (AstNode, bool) -> Tuple[Tuple[int, int], Tuple[int, int]] + """ + Version of ``get_text_positions()`` that doesn't use tokens. + """ + if is_module(node): + # Modules don't have position info, so just return the range of the whole text. + # The token-using method does something different, but its behavior seems weird and inconsistent. + # For example, in a file with only comments, it only returns the first line. + # It's hard to imagine a case when this matters. + return (1, 0), self._line_numbers.offset_to_line(len(self._text)) + + if getattr(node, 'lineno', None) is None: + return (1, 0), (1, 0) + + assert node # tell mypy that node is not None, which we allowed up to here for compatibility + + decorators = getattr(node, 'decorator_list', []) + if not decorators: + # Astroid uses node.decorators.nodes instead of node.decorator_list. + decorators_node = getattr(node, 'decorators', None) + decorators = getattr(decorators_node, 'nodes', []) + if decorators: + # Function/Class definition nodes are marked by AST as starting at def/class, + # not the first decorator. This doesn't match the token-using behavior, + # or inspect.getsource(), and just seems weird. + start_node = decorators[0] + else: + start_node = node + + start_lineno = start_node.lineno + end_node = last_stmt(node) + + # Include leading indentation for multiline statements. + # This doesn't mean simple statements that happen to be on multiple lines, + # but compound statements where inner indentation matters. + # So we don't just compare node.lineno and node.end_lineno, + # we check for a contained statement starting on a different line. + if padded and ( + start_lineno != end_node.lineno + or ( + # Astroid docstrings aren't treated as separate statements. + # So to handle function/class definitions with a docstring but no other body, + # we just check that the node is a statement with a docstring + # and spanning multiple lines in the simple, literal sense. + start_lineno != node.end_lineno + and getattr(node, "doc_node", None) + and is_stmt(node) + ) + ): + start_col_offset = 0 + else: + start_col_offset = self._line_numbers.from_utf8_col(start_lineno, start_node.col_offset) + + start = (start_lineno, start_col_offset) + + # To match the token-using behaviour, we exclude trailing semicolons and comments. + # This means that for blocks containing multiple statements, we have to use the last one + # instead of the actual node for end_lineno and end_col_offset. + end_lineno = cast(int, end_node.end_lineno) + end_col_offset = cast(int, end_node.end_col_offset) + end_col_offset = self._line_numbers.from_utf8_col(end_lineno, end_col_offset) + end = (end_lineno, end_col_offset) + + return start, end + + def get_text_positions(self, node, padded): + # type: (AstNode, bool) -> Tuple[Tuple[int, int], Tuple[int, int]] + """ + Returns two ``(lineno, col_offset)`` tuples for the start and end of the given node. + If the positions can't be determined, or the nodes don't correspond to any particular text, + returns ``(1, 0)`` for both. + + ``padded`` corresponds to the ``padded`` argument to ``ast.get_source_segment()``. + This means that if ``padded`` is True, the start position will be adjusted to include + leading whitespace if ``node`` is a multiline statement. + """ + if getattr(node, "_broken_positions", None): + # This node was marked in util.annotate_fstring_nodes as having untrustworthy lineno/col_offset. + return (1, 0), (1, 0) + + if supports_tokenless(node): + return self._get_text_positions_tokenless(node, padded) + + return self.asttokens.get_text_positions(node, padded) + + +# Node types that _get_text_positions_tokenless doesn't support. +# These initial values are missing lineno. +_unsupported_tokenless_types = ("arguments", "Arguments", "withitem") # type: Tuple[str, ...] +if sys.version_info[:2] == (3, 8): + # _get_text_positions_tokenless works incorrectly for these types due to bugs in Python 3.8. + _unsupported_tokenless_types += ("arg", "Starred") + # no lineno in 3.8 + _unsupported_tokenless_types += ("Slice", "ExtSlice", "Index", "keyword") + + +def supports_tokenless(node=None): + # type: (Any) -> bool + """ + Returns True if the Python version and the node (if given) are supported by + the ``get_text*`` methods of ``ASTText`` without falling back to ``ASTTokens``. + See ``ASTText`` for why this matters. + + The following cases are not supported: + + - PyPy + - ``ast.arguments`` / ``astroid.Arguments`` + - ``ast.withitem`` + - ``astroid.Comprehension`` + - ``astroid.AssignName`` inside ``astroid.Arguments`` or ``astroid.ExceptHandler`` + - The following nodes in Python 3.8 only: + - ``ast.arg`` + - ``ast.Starred`` + - ``ast.Slice`` + - ``ast.ExtSlice`` + - ``ast.Index`` + - ``ast.keyword`` + """ + return ( + type(node).__name__ not in _unsupported_tokenless_types + and not ( + # astroid nodes + not isinstance(node, ast.AST) and node is not None and ( + ( + type(node).__name__ == "AssignName" + and type(node.parent).__name__ in ("Arguments", "ExceptHandler") + ) + ) + ) + and 'pypy' not in sys.version.lower() + ) diff --git a/lib/python3.12/site-packages/asttokens/line_numbers.py b/lib/python3.12/site-packages/asttokens/line_numbers.py new file mode 100644 index 0000000000000000000000000000000000000000..1441a53b7872cd2c491a0d39c28c7a4d16294d92 --- /dev/null +++ b/lib/python3.12/site-packages/asttokens/line_numbers.py @@ -0,0 +1,74 @@ +# Copyright 2016 Grist Labs, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import bisect +import re +from typing import Dict, List, Tuple + +_line_start_re = re.compile(r'^', re.M) + +class LineNumbers: + """ + Class to convert between character offsets in a text string, and pairs (line, column) of 1-based + line and 0-based column numbers, as used by tokens and AST nodes. + + This class expects unicode for input and stores positions in unicode. But it supports + translating to and from utf8 offsets, which are used by ast parsing. + """ + def __init__(self, text): + # type: (str) -> None + # A list of character offsets of each line's first character. + self._line_offsets = [m.start(0) for m in _line_start_re.finditer(text)] + self._text = text + self._text_len = len(text) + self._utf8_offset_cache = {} # type: Dict[int, List[int]] # maps line num to list of char offset for each byte in line + + def from_utf8_col(self, line, utf8_column): + # type: (int, int) -> int + """ + Given a 1-based line number and 0-based utf8 column, returns a 0-based unicode column. + """ + offsets = self._utf8_offset_cache.get(line) + if offsets is None: + end_offset = self._line_offsets[line] if line < len(self._line_offsets) else self._text_len + line_text = self._text[self._line_offsets[line - 1] : end_offset] + + offsets = [i for i,c in enumerate(line_text) for byte in c.encode('utf8')] + offsets.append(len(line_text)) + self._utf8_offset_cache[line] = offsets + + return offsets[max(0, min(len(offsets)-1, utf8_column))] + + def line_to_offset(self, line, column): + # type: (int, int) -> int + """ + Converts 1-based line number and 0-based column to 0-based character offset into text. + """ + line -= 1 + if line >= len(self._line_offsets): + return self._text_len + elif line < 0: + return 0 + else: + return min(self._line_offsets[line] + max(0, column), self._text_len) + + def offset_to_line(self, offset): + # type: (int) -> Tuple[int, int] + """ + Converts 0-based character offset to pair (line, col) of 1-based line and 0-based column + numbers. + """ + offset = max(0, min(self._text_len, offset)) + line_index = bisect.bisect_right(self._line_offsets, offset) - 1 + return (line_index + 1, offset - self._line_offsets[line_index]) diff --git a/lib/python3.12/site-packages/asttokens/mark_tokens.py b/lib/python3.12/site-packages/asttokens/mark_tokens.py new file mode 100644 index 0000000000000000000000000000000000000000..62d38f8871b3bdf1a96daf61f15bd91ad52865e5 --- /dev/null +++ b/lib/python3.12/site-packages/asttokens/mark_tokens.py @@ -0,0 +1,469 @@ +# Copyright 2016 Grist Labs, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import ast +import numbers +import sys +import token +from ast import Module +from typing import Callable, List, Union, cast, Optional, Tuple, TYPE_CHECKING + +from . import util +from .asttokens import ASTTokens +from .astroid_compat import astroid_node_classes as nc, BaseContainer as AstroidBaseContainer + +if TYPE_CHECKING: + from .util import AstNode + + +# Mapping of matching braces. To find a token here, look up token[:2]. +_matching_pairs_left = { + (token.OP, '('): (token.OP, ')'), + (token.OP, '['): (token.OP, ']'), + (token.OP, '{'): (token.OP, '}'), +} + +_matching_pairs_right = { + (token.OP, ')'): (token.OP, '('), + (token.OP, ']'): (token.OP, '['), + (token.OP, '}'): (token.OP, '{'), +} + + +class MarkTokens: + """ + Helper that visits all nodes in the AST tree and assigns .first_token and .last_token attributes + to each of them. This is the heart of the token-marking logic. + """ + def __init__(self, code): + # type: (ASTTokens) -> None + self._code = code + self._methods = util.NodeMethods() + self._iter_children = None # type: Optional[Callable] + + def visit_tree(self, node): + # type: (Module) -> None + self._iter_children = util.iter_children_func(node) + util.visit_tree(node, self._visit_before_children, self._visit_after_children) + + def _visit_before_children(self, node, parent_token): + # type: (AstNode, Optional[util.Token]) -> Tuple[Optional[util.Token], Optional[util.Token]] + col = getattr(node, 'col_offset', None) + token = self._code.get_token_from_utf8(node.lineno, col) if col is not None else None + + if not token and util.is_module(node): + # We'll assume that a Module node starts at the start of the source code. + token = self._code.get_token(1, 0) + + # Use our own token, or our parent's if we don't have one, to pass to child calls as + # parent_token argument. The second value becomes the token argument of _visit_after_children. + return (token or parent_token, token) + + def _visit_after_children(self, node, parent_token, token): + # type: (AstNode, Optional[util.Token], Optional[util.Token]) -> None + # This processes the node generically first, after all children have been processed. + + # Get the first and last tokens that belong to children. Note how this doesn't assume that we + # iterate through children in order that corresponds to occurrence in source code. This + # assumption can fail (e.g. with return annotations). + first = token + last = None + for child in cast(Callable, self._iter_children)(node): + # astroid slices have especially wrong positions, we don't want them to corrupt their parents. + if util.is_empty_astroid_slice(child): + continue + if not first or child.first_token.index < first.index: + first = child.first_token + if not last or child.last_token.index > last.index: + last = child.last_token + + # If we don't have a first token from _visit_before_children, and there were no children, then + # use the parent's token as the first token. + first = first or parent_token + + # If no children, set last token to the first one. + last = last or first + + # Statements continue to before NEWLINE. This helps cover a few different cases at once. + if util.is_stmt(node): + last = self._find_last_in_stmt(cast(util.Token, last)) + + # Capture any unmatched brackets. + first, last = self._expand_to_matching_pairs(cast(util.Token, first), cast(util.Token, last), node) + + # Give a chance to node-specific methods to adjust. + nfirst, nlast = self._methods.get(self, node.__class__)(node, first, last) + + if (nfirst, nlast) != (first, last): + # If anything changed, expand again to capture any unmatched brackets. + nfirst, nlast = self._expand_to_matching_pairs(nfirst, nlast, node) + + node.first_token = nfirst + node.last_token = nlast + + def _find_last_in_stmt(self, start_token): + # type: (util.Token) -> util.Token + t = start_token + while (not util.match_token(t, token.NEWLINE) and + not util.match_token(t, token.OP, ';') and + not token.ISEOF(t.type)): + t = self._code.next_token(t, include_extra=True) + return self._code.prev_token(t) + + def _expand_to_matching_pairs(self, first_token, last_token, node): + # type: (util.Token, util.Token, AstNode) -> Tuple[util.Token, util.Token] + """ + Scan tokens in [first_token, last_token] range that are between node's children, and for any + unmatched brackets, adjust first/last tokens to include the closing pair. + """ + # We look for opening parens/braces among non-child tokens (i.e. tokens between our actual + # child nodes). If we find any closing ones, we match them to the opens. + to_match_right = [] # type: List[Tuple[int, str]] + to_match_left = [] + for tok in self._code.token_range(first_token, last_token): + tok_info = tok[:2] + if to_match_right and tok_info == to_match_right[-1]: + to_match_right.pop() + elif tok_info in _matching_pairs_left: + to_match_right.append(_matching_pairs_left[tok_info]) + elif tok_info in _matching_pairs_right: + to_match_left.append(_matching_pairs_right[tok_info]) + + # Once done, extend `last_token` to match any unclosed parens/braces. + for match in reversed(to_match_right): + last = self._code.next_token(last_token) + # Allow for trailing commas or colons (allowed in subscripts) before the closing delimiter + while any(util.match_token(last, token.OP, x) for x in (',', ':')): + last = self._code.next_token(last) + # Now check for the actual closing delimiter. + if util.match_token(last, *match): + last_token = last + + # And extend `first_token` to match any unclosed opening parens/braces. + for match in to_match_left: + first = self._code.prev_token(first_token) + if util.match_token(first, *match): + first_token = first + + return (first_token, last_token) + + #---------------------------------------------------------------------- + # Node visitors. Each takes a preliminary first and last tokens, and returns the adjusted pair + # that will actually be assigned. + + def visit_default(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + # pylint: disable=no-self-use + # By default, we don't need to adjust the token we computed earlier. + return (first_token, last_token) + + def handle_comp(self, open_brace, node, first_token, last_token): + # type: (str, AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + # For list/set/dict comprehensions, we only get the token of the first child, so adjust it to + # include the opening brace (the closing brace will be matched automatically). + before = self._code.prev_token(first_token) + util.expect_token(before, token.OP, open_brace) + return (before, last_token) + + def visit_comprehension(self, + node, # type: AstNode + first_token, # type: util.Token + last_token, # type: util.Token + ): + # type: (...) -> Tuple[util.Token, util.Token] + # The 'comprehension' node starts with 'for' but we only get first child; we search backwards + # to find the 'for' keyword. + first = self._code.find_token(first_token, token.NAME, 'for', reverse=True) + return (first, last_token) + + def visit_if(self, node, first_token, last_token): + # type: (util.Token, util.Token, util.Token) -> Tuple[util.Token, util.Token] + while first_token.string not in ('if', 'elif'): + first_token = self._code.prev_token(first_token) + return first_token, last_token + + def handle_attr(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + # Attribute node has ".attr" (2 tokens) after the last child. + dot = self._code.find_token(last_token, token.OP, '.') + name = self._code.next_token(dot) + util.expect_token(name, token.NAME) + return (first_token, name) + + visit_attribute = handle_attr + visit_assignattr = handle_attr + visit_delattr = handle_attr + + def handle_def(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + # With astroid, nodes that start with a doc-string can have an empty body, in which case we + # need to adjust the last token to include the doc string. + if not node.body and (getattr(node, 'doc_node', None) or getattr(node, 'doc', None)): # type: ignore[union-attr] + last_token = self._code.find_token(last_token, token.STRING) + + # Include @ from decorator + if first_token.index > 0: + prev = self._code.prev_token(first_token) + if util.match_token(prev, token.OP, '@'): + first_token = prev + return (first_token, last_token) + + visit_classdef = handle_def + visit_functiondef = handle_def + + def handle_following_brackets(self, node, last_token, opening_bracket): + # type: (AstNode, util.Token, str) -> util.Token + # This is for calls and subscripts, which have a pair of brackets + # at the end which may contain no nodes, e.g. foo() or bar[:]. + # We look for the opening bracket and then let the matching pair be found automatically + # Remember that last_token is at the end of all children, + # so we are not worried about encountering a bracket that belongs to a child. + first_child = next(cast(Callable, self._iter_children)(node)) + call_start = self._code.find_token(first_child.last_token, token.OP, opening_bracket) + if call_start.index > last_token.index: + last_token = call_start + return last_token + + def visit_call(self, node, first_token, last_token): + # type: (util.Token, util.Token, util.Token) -> Tuple[util.Token, util.Token] + last_token = self.handle_following_brackets(node, last_token, '(') + + # Handling a python bug with decorators with empty parens, e.g. + # @deco() + # def ... + if util.match_token(first_token, token.OP, '@'): + first_token = self._code.next_token(first_token) + return (first_token, last_token) + + def visit_matchclass(self, node, first_token, last_token): + # type: (util.Token, util.Token, util.Token) -> Tuple[util.Token, util.Token] + last_token = self.handle_following_brackets(node, last_token, '(') + return (first_token, last_token) + + def visit_subscript(self, + node, # type: AstNode + first_token, # type: util.Token + last_token, # type: util.Token + ): + # type: (...) -> Tuple[util.Token, util.Token] + last_token = self.handle_following_brackets(node, last_token, '[') + return (first_token, last_token) + + def visit_slice(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + # consume `:` tokens to the left and right. In Python 3.9, Slice nodes are + # given a col_offset, (and end_col_offset), so this will always start inside + # the slice, even if it is the empty slice. However, in 3.8 and below, this + # will only expand to the full slice if the slice contains a node with a + # col_offset. So x[:] will only get the correct tokens in 3.9, but x[1:] and + # x[:1] will even on earlier versions of Python. + while True: + prev = self._code.prev_token(first_token) + if prev.string != ':': + break + first_token = prev + while True: + next_ = self._code.next_token(last_token) + if next_.string != ':': + break + last_token = next_ + return (first_token, last_token) + + def handle_bare_tuple(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + # A bare tuple doesn't include parens; if there is a trailing comma, make it part of the tuple. + maybe_comma = self._code.next_token(last_token) + if util.match_token(maybe_comma, token.OP, ','): + last_token = maybe_comma + return (first_token, last_token) + + # In Python3.8 parsed tuples include parentheses when present. + def handle_tuple_nonempty(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + assert isinstance(node, ast.Tuple) or isinstance(node, AstroidBaseContainer) + # It's a bare tuple if the first token belongs to the first child. The first child may + # include extraneous parentheses (which don't create new nodes), so account for those too. + child = node.elts[0] + if TYPE_CHECKING: + child = cast(AstNode, child) + child_first, child_last = self._gobble_parens(child.first_token, child.last_token, True) + if first_token == child_first: + return self.handle_bare_tuple(node, first_token, last_token) + return (first_token, last_token) + + def visit_tuple(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + assert isinstance(node, ast.Tuple) or isinstance(node, AstroidBaseContainer) + if not node.elts: + # An empty tuple is just "()", and we need no further info. + return (first_token, last_token) + return self.handle_tuple_nonempty(node, first_token, last_token) + + def _gobble_parens(self, first_token, last_token, include_all=False): + # type: (util.Token, util.Token, bool) -> Tuple[util.Token, util.Token] + # Expands a range of tokens to include one or all pairs of surrounding parentheses, and + # returns (first, last) tokens that include these parens. + while first_token.index > 0: + prev = self._code.prev_token(first_token) + next = self._code.next_token(last_token) + if util.match_token(prev, token.OP, '(') and util.match_token(next, token.OP, ')'): + first_token, last_token = prev, next + if include_all: + continue + break + return (first_token, last_token) + + def visit_str(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + return self.handle_str(first_token, last_token) + + def visit_joinedstr(self, + node, # type: AstNode + first_token, # type: util.Token + last_token, # type: util.Token + ): + # type: (...) -> Tuple[util.Token, util.Token] + if sys.version_info < (3, 12): + # Older versions don't tokenize the contents of f-strings + return self.handle_str(first_token, last_token) + + last = first_token + while True: + if util.match_token(last, getattr(token, "FSTRING_START")): + # Python 3.12+ has tokens for the start (e.g. `f"`) and end (`"`) + # of the f-string. We can't just look for the next FSTRING_END + # because f-strings can be nested, e.g. f"{f'{x}'}", so we need + # to treat this like matching balanced parentheses. + count = 1 + while count > 0: + last = self._code.next_token(last) + # mypy complains about token.FSTRING_START and token.FSTRING_END. + if util.match_token(last, getattr(token, "FSTRING_START")): + count += 1 + elif util.match_token(last, getattr(token, "FSTRING_END")): + count -= 1 + last_token = last + last = self._code.next_token(last_token) + elif util.match_token(last, token.STRING): + # Similar to handle_str, we also need to handle adjacent strings. + last_token = last + last = self._code.next_token(last_token) + else: + break + return (first_token, last_token) + + def visit_bytes(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + return self.handle_str(first_token, last_token) + + def handle_str(self, first_token, last_token): + # type: (util.Token, util.Token) -> Tuple[util.Token, util.Token] + # Multiple adjacent STRING tokens form a single string. + last = self._code.next_token(last_token) + while util.match_token(last, token.STRING): + last_token = last + last = self._code.next_token(last_token) + return (first_token, last_token) + + def handle_num(self, + node, # type: AstNode + value, # type: Union[complex, int, numbers.Number] + first_token, # type: util.Token + last_token, # type: util.Token + ): + # type: (...) -> Tuple[util.Token, util.Token] + # A constant like '-1' gets turned into two tokens; this will skip the '-'. + while util.match_token(last_token, token.OP): + last_token = self._code.next_token(last_token) + + if isinstance(value, complex): + # A complex number like -2j cannot be compared directly to 0 + # A complex number like 1-2j is expressed as a binary operation + # so we don't need to worry about it + value = value.imag + + # This makes sure that the - is included + if value < 0 and first_token.type == token.NUMBER: # type: ignore[operator] + first_token = self._code.prev_token(first_token) + return (first_token, last_token) + + def visit_num(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + n = node.n # type: ignore[union-attr] # ast.Num has been removed in python 3.14 + assert isinstance(n, (complex, int, numbers.Number)) + return self.handle_num(node, n, first_token, last_token) + + def visit_const(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + assert isinstance(node, ast.Constant) or isinstance(node, nc.Const) + if isinstance(node.value, numbers.Number): + return self.handle_num(node, node.value, first_token, last_token) + elif isinstance(node.value, (str, bytes)): + return self.visit_str(node, first_token, last_token) + return (first_token, last_token) + + visit_constant = visit_const + + def visit_keyword(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + # Until python 3.9 (https://bugs.python.org/issue40141), + # ast.keyword nodes didn't have line info. Astroid has lineno None. + assert isinstance(node, ast.keyword) or isinstance(node, nc.Keyword) + if node.arg is not None and getattr(node, 'lineno', None) is None: + equals = self._code.find_token(first_token, token.OP, '=', reverse=True) + name = self._code.prev_token(equals) + util.expect_token(name, token.NAME, node.arg) + first_token = name + return (first_token, last_token) + + def visit_starred(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + # Astroid has 'Starred' nodes (for "foo(*bar)" type args), but they need to be adjusted. + if not util.match_token(first_token, token.OP, '*'): + star = self._code.prev_token(first_token) + if util.match_token(star, token.OP, '*'): + first_token = star + return (first_token, last_token) + + def visit_assignname(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + # Astroid may turn 'except' clause into AssignName, but we need to adjust it. + if util.match_token(first_token, token.NAME, 'except'): + colon = self._code.find_token(last_token, token.OP, ':') + first_token = last_token = self._code.prev_token(colon) + return (first_token, last_token) + + # Async nodes should typically start with the word 'async' + # but Python < 3.7 doesn't put the col_offset there + # AsyncFunctionDef is slightly different because it might have + # decorators before that, which visit_functiondef handles + def handle_async(self, node, first_token, last_token): + # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] + if not first_token.string == 'async': + first_token = self._code.prev_token(first_token) + return (first_token, last_token) + + visit_asyncfor = handle_async + visit_asyncwith = handle_async + + def visit_asyncfunctiondef(self, + node, # type: AstNode + first_token, # type: util.Token + last_token, # type: util.Token + ): + # type: (...) -> Tuple[util.Token, util.Token] + if util.match_token(first_token, token.NAME, 'def'): + # Include the 'async' token + first_token = self._code.prev_token(first_token) + return self.visit_functiondef(node, first_token, last_token) diff --git a/lib/python3.12/site-packages/asttokens/py.typed b/lib/python3.12/site-packages/asttokens/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/asttokens/version.py b/lib/python3.12/site-packages/asttokens/version.py new file mode 100644 index 0000000000000000000000000000000000000000..055276878107052a2bd2810e5a0b07182ef1cd58 --- /dev/null +++ b/lib/python3.12/site-packages/asttokens/version.py @@ -0,0 +1 @@ +__version__ = "3.0.1" diff --git a/lib/python3.12/site-packages/h11-0.16.0.dist-info/INSTALLER b/lib/python3.12/site-packages/h11-0.16.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/h11-0.16.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/h11-0.16.0.dist-info/METADATA b/lib/python3.12/site-packages/h11-0.16.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..8a2f639061cc4a203f7109d8335d28076442c61d --- /dev/null +++ b/lib/python3.12/site-packages/h11-0.16.0.dist-info/METADATA @@ -0,0 +1,202 @@ +Metadata-Version: 2.4 +Name: h11 +Version: 0.16.0 +Summary: A pure-Python, bring-your-own-I/O implementation of HTTP/1.1 +Home-page: https://github.com/python-hyper/h11 +Author: Nathaniel J. Smith +Author-email: njs@pobox.com +License: MIT +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: System :: Networking +Requires-Python: >=3.8 +License-File: LICENSE.txt +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: requires-python +Dynamic: summary + +h11 +=== + +.. image:: https://travis-ci.org/python-hyper/h11.svg?branch=master + :target: https://travis-ci.org/python-hyper/h11 + :alt: Automated test status + +.. image:: https://codecov.io/gh/python-hyper/h11/branch/master/graph/badge.svg + :target: https://codecov.io/gh/python-hyper/h11 + :alt: Test coverage + +.. image:: https://readthedocs.org/projects/h11/badge/?version=latest + :target: http://h11.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +This is a little HTTP/1.1 library written from scratch in Python, +heavily inspired by `hyper-h2 `_. + +It's a "bring-your-own-I/O" library; h11 contains no IO code +whatsoever. This means you can hook h11 up to your favorite network +API, and that could be anything you want: synchronous, threaded, +asynchronous, or your own implementation of `RFC 6214 +`_ -- h11 won't judge you. +(Compare this to the current state of the art, where every time a `new +network API `_ comes along then someone +gets to start over reimplementing the entire HTTP protocol from +scratch.) Cory Benfield made an `excellent blog post describing the +benefits of this approach +`_, or if you like video +then here's his `PyCon 2016 talk on the same theme +`_. + +This also means that h11 is not immediately useful out of the box: +it's a toolkit for building programs that speak HTTP, not something +that could directly replace ``requests`` or ``twisted.web`` or +whatever. But h11 makes it much easier to implement something like +``requests`` or ``twisted.web``. + +At a high level, working with h11 goes like this: + +1) First, create an ``h11.Connection`` object to track the state of a + single HTTP/1.1 connection. + +2) When you read data off the network, pass it to + ``conn.receive_data(...)``; you'll get back a list of objects + representing high-level HTTP "events". + +3) When you want to send a high-level HTTP event, create the + corresponding "event" object and pass it to ``conn.send(...)``; + this will give you back some bytes that you can then push out + through the network. + +For example, a client might instantiate and then send a +``h11.Request`` object, then zero or more ``h11.Data`` objects for the +request body (e.g., if this is a POST), and then a +``h11.EndOfMessage`` to indicate the end of the message. Then the +server would then send back a ``h11.Response``, some ``h11.Data``, and +its own ``h11.EndOfMessage``. If either side violates the protocol, +you'll get a ``h11.ProtocolError`` exception. + +h11 is suitable for implementing both servers and clients, and has a +pleasantly symmetric API: the events you send as a client are exactly +the ones that you receive as a server and vice-versa. + +`Here's an example of a tiny HTTP client +`_ + +It also has `a fine manual `_. + +FAQ +--- + +*Whyyyyy?* + +I wanted to play with HTTP in `Curio +`__ and `Trio +`__, which at the time didn't have any +HTTP libraries. So I thought, no big deal, Python has, like, a dozen +different implementations of HTTP, surely I can find one that's +reusable. I didn't find one, but I did find Cory's call-to-arms +blog-post. So I figured, well, fine, if I have to implement HTTP from +scratch, at least I can make sure no-one *else* has to ever again. + +*Should I use it?* + +Maybe. You should be aware that it's a very young project. But, it's +feature complete and has an exhaustive test-suite and complete docs, +so the next step is for people to try using it and see how it goes +:-). If you do then please let us know -- if nothing else we'll want +to talk to you before making any incompatible changes! + +*What are the features/limitations?* + +Roughly speaking, it's trying to be a robust, complete, and non-hacky +implementation of the first "chapter" of the HTTP/1.1 spec: `RFC 7230: +HTTP/1.1 Message Syntax and Routing +`_. That is, it mostly focuses on +implementing HTTP at the level of taking bytes on and off the wire, +and the headers related to that, and tries to be anal about spec +conformance. It doesn't know about higher-level concerns like URL +routing, conditional GETs, cross-origin cookie policies, or content +negotiation. But it does know how to take care of framing, +cross-version differences in keep-alive handling, and the "obsolete +line folding" rule, so you can focus your energies on the hard / +interesting parts for your application, and it tries to support the +full specification in the sense that any useful HTTP/1.1 conformant +application should be able to use h11. + +It's pure Python, and has no dependencies outside of the standard +library. + +It has a test suite with 100.0% coverage for both statements and +branches. + +Currently it supports Python 3 (testing on 3.8-3.12) and PyPy 3. +The last Python 2-compatible version was h11 0.11.x. +(Originally it had a Cython wrapper for `http-parser +`_ and a beautiful nested state +machine implemented with ``yield from`` to postprocess the output. But +I had to take these out -- the new *parser* needs fewer lines-of-code +than the old *parser wrapper*, is written in pure Python, uses no +exotic language syntax, and has more features. It's sad, really; that +old state machine was really slick. I just need a few sentences here +to mourn that.) + +I don't know how fast it is. I haven't benchmarked or profiled it yet, +so it's probably got a few pointless hot spots, and I've been trying +to err on the side of simplicity and robustness instead of +micro-optimization. But at the architectural level I tried hard to +avoid fundamentally bad decisions, e.g., I believe that all the +parsing algorithms remain linear-time even in the face of pathological +input like slowloris, and there are no byte-by-byte loops. (I also +believe that it maintains bounded memory usage in the face of +arbitrary/pathological input.) + +The whole library is ~800 lines-of-code. You can read and understand +the whole thing in less than an hour. Most of the energy invested in +this so far has been spent on trying to keep things simple by +minimizing special-cases and ad hoc state manipulation; even though it +is now quite small and simple, I'm still annoyed that I haven't +figured out how to make it even smaller and simpler. (Unfortunately, +HTTP does not lend itself to simplicity.) + +The API is ~feature complete and I don't expect the general outlines +to change much, but you can't judge an API's ergonomics until you +actually document and use it, so I'd expect some changes in the +details. + +*How do I try it?* + +.. code-block:: sh + + $ pip install h11 + $ git clone git@github.com:python-hyper/h11 + $ cd h11/examples + $ python basic-client.py + +and go from there. + +*License?* + +MIT + +*Code of conduct?* + +Contributors are requested to follow our `code of conduct +`_ in +all project spaces. diff --git a/lib/python3.12/site-packages/h11-0.16.0.dist-info/RECORD b/lib/python3.12/site-packages/h11-0.16.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..a8f8e63f529ce81b7d7c970ea791147d9a732175 --- /dev/null +++ b/lib/python3.12/site-packages/h11-0.16.0.dist-info/RECORD @@ -0,0 +1,29 @@ +h11-0.16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +h11-0.16.0.dist-info/METADATA,sha256=KPMmCYrAn8unm48YD5YIfIQf4kViFct7hyqcfVzRnWQ,8348 +h11-0.16.0.dist-info/RECORD,, +h11-0.16.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91 +h11-0.16.0.dist-info/licenses/LICENSE.txt,sha256=N9tbuFkm2yikJ6JYZ_ELEjIAOuob5pzLhRE4rbjm82E,1124 +h11-0.16.0.dist-info/top_level.txt,sha256=F7dC4jl3zeh8TGHEPaWJrMbeuoWbS379Gwdi-Yvdcis,4 +h11/__init__.py,sha256=iO1KzkSO42yZ6ffg-VMgbx_ZVTWGUY00nRYEWn-s3kY,1507 +h11/__pycache__/__init__.cpython-312.pyc,, +h11/__pycache__/_abnf.cpython-312.pyc,, +h11/__pycache__/_connection.cpython-312.pyc,, +h11/__pycache__/_events.cpython-312.pyc,, +h11/__pycache__/_headers.cpython-312.pyc,, +h11/__pycache__/_readers.cpython-312.pyc,, +h11/__pycache__/_receivebuffer.cpython-312.pyc,, +h11/__pycache__/_state.cpython-312.pyc,, +h11/__pycache__/_util.cpython-312.pyc,, +h11/__pycache__/_version.cpython-312.pyc,, +h11/__pycache__/_writers.cpython-312.pyc,, +h11/_abnf.py,sha256=ybixr0xsupnkA6GFAyMubuXF6Tc1lb_hF890NgCsfNc,4815 +h11/_connection.py,sha256=k9YRVf6koZqbttBW36xSWaJpWdZwa-xQVU9AHEo9DuI,26863 +h11/_events.py,sha256=I97aXoal1Wu7dkL548BANBUCkOIbe-x5CioYA9IBY14,11792 +h11/_headers.py,sha256=P7D-lBNxHwdLZPLimmYwrPG-9ZkjElvvJZJdZAgSP-4,10412 +h11/_readers.py,sha256=a4RypORUCC3d0q_kxPuBIM7jTD8iLt5X91TH0FsduN4,8590 +h11/_receivebuffer.py,sha256=xrspsdsNgWFxRfQcTXxR8RrdjRXXTK0Io5cQYWpJ1Ws,5252 +h11/_state.py,sha256=_5LG_BGR8FCcFQeBPH-TMHgm_-B-EUcWCnQof_9XjFE,13231 +h11/_util.py,sha256=LWkkjXyJaFlAy6Lt39w73UStklFT5ovcvo0TkY7RYuk,4888 +h11/_version.py,sha256=GVSsbPSPDcOuF6ptfIiXnVJoaEm3ygXbMnqlr_Giahw,686 +h11/_writers.py,sha256=oFKm6PtjeHfbj4RLX7VB7KDc1gIY53gXG3_HR9ltmTA,5081 +h11/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 diff --git a/lib/python3.12/site-packages/h11-0.16.0.dist-info/WHEEL b/lib/python3.12/site-packages/h11-0.16.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..1eb3c49d99559863120cfb8433fc8738fba43ba9 --- /dev/null +++ b/lib/python3.12/site-packages/h11-0.16.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (78.1.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt b/lib/python3.12/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..8f080eae848f759c9173bfc0c79506357ebe5090 --- /dev/null +++ b/lib/python3.12/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Nathaniel J. Smith and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/python3.12/site-packages/h11-0.16.0.dist-info/top_level.txt b/lib/python3.12/site-packages/h11-0.16.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..0d24def711344ec6f4da2108f7d5c9261eb35f8b --- /dev/null +++ b/lib/python3.12/site-packages/h11-0.16.0.dist-info/top_level.txt @@ -0,0 +1 @@ +h11 diff --git a/lib/python3.12/site-packages/httpcore/__init__.py b/lib/python3.12/site-packages/httpcore/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9a92dc4a440bdf6f259ec1083c89c817eb7b631b --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/__init__.py @@ -0,0 +1,141 @@ +from ._api import request, stream +from ._async import ( + AsyncConnectionInterface, + AsyncConnectionPool, + AsyncHTTP2Connection, + AsyncHTTP11Connection, + AsyncHTTPConnection, + AsyncHTTPProxy, + AsyncSOCKSProxy, +) +from ._backends.base import ( + SOCKET_OPTION, + AsyncNetworkBackend, + AsyncNetworkStream, + NetworkBackend, + NetworkStream, +) +from ._backends.mock import AsyncMockBackend, AsyncMockStream, MockBackend, MockStream +from ._backends.sync import SyncBackend +from ._exceptions import ( + ConnectError, + ConnectionNotAvailable, + ConnectTimeout, + LocalProtocolError, + NetworkError, + PoolTimeout, + ProtocolError, + ProxyError, + ReadError, + ReadTimeout, + RemoteProtocolError, + TimeoutException, + UnsupportedProtocol, + WriteError, + WriteTimeout, +) +from ._models import URL, Origin, Proxy, Request, Response +from ._ssl import default_ssl_context +from ._sync import ( + ConnectionInterface, + ConnectionPool, + HTTP2Connection, + HTTP11Connection, + HTTPConnection, + HTTPProxy, + SOCKSProxy, +) + +# The 'httpcore.AnyIOBackend' class is conditional on 'anyio' being installed. +try: + from ._backends.anyio import AnyIOBackend +except ImportError: # pragma: nocover + + class AnyIOBackend: # type: ignore + def __init__(self, *args, **kwargs): # type: ignore + msg = ( + "Attempted to use 'httpcore.AnyIOBackend' but 'anyio' is not installed." + ) + raise RuntimeError(msg) + + +# The 'httpcore.TrioBackend' class is conditional on 'trio' being installed. +try: + from ._backends.trio import TrioBackend +except ImportError: # pragma: nocover + + class TrioBackend: # type: ignore + def __init__(self, *args, **kwargs): # type: ignore + msg = "Attempted to use 'httpcore.TrioBackend' but 'trio' is not installed." + raise RuntimeError(msg) + + +__all__ = [ + # top-level requests + "request", + "stream", + # models + "Origin", + "URL", + "Request", + "Response", + "Proxy", + # async + "AsyncHTTPConnection", + "AsyncConnectionPool", + "AsyncHTTPProxy", + "AsyncHTTP11Connection", + "AsyncHTTP2Connection", + "AsyncConnectionInterface", + "AsyncSOCKSProxy", + # sync + "HTTPConnection", + "ConnectionPool", + "HTTPProxy", + "HTTP11Connection", + "HTTP2Connection", + "ConnectionInterface", + "SOCKSProxy", + # network backends, implementations + "SyncBackend", + "AnyIOBackend", + "TrioBackend", + # network backends, mock implementations + "AsyncMockBackend", + "AsyncMockStream", + "MockBackend", + "MockStream", + # network backends, interface + "AsyncNetworkStream", + "AsyncNetworkBackend", + "NetworkStream", + "NetworkBackend", + # util + "default_ssl_context", + "SOCKET_OPTION", + # exceptions + "ConnectionNotAvailable", + "ProxyError", + "ProtocolError", + "LocalProtocolError", + "RemoteProtocolError", + "UnsupportedProtocol", + "TimeoutException", + "PoolTimeout", + "ConnectTimeout", + "ReadTimeout", + "WriteTimeout", + "NetworkError", + "ConnectError", + "ReadError", + "WriteError", +] + +__version__ = "1.0.9" + + +__locals = locals() +for __name in __all__: + # Exclude SOCKET_OPTION, it causes AttributeError on Python 3.14 + if not __name.startswith(("__", "SOCKET_OPTION")): + setattr(__locals[__name], "__module__", "httpcore") # noqa diff --git a/lib/python3.12/site-packages/httpcore/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7e3c4a244b02fe5d57fd97dba9e294700f6ac30 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/__pycache__/_api.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/__pycache__/_api.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..077a42217f8bec55a7f25d3f9340abddb2b7bb7c Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/__pycache__/_api.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/__pycache__/_exceptions.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/__pycache__/_exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dcf74e036c4a3e7d78e40721bd8c87b1334ec0df Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/__pycache__/_exceptions.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/__pycache__/_models.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/__pycache__/_models.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fe01485c8785381fc7c3335000cb35cceb6f66da Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/__pycache__/_models.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/__pycache__/_ssl.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/__pycache__/_ssl.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f05f73573b592b491f4f87b2a5c69784a93ae080 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/__pycache__/_ssl.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/__pycache__/_synchronization.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/__pycache__/_synchronization.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78271a29891856a61462ce5edbfa81e8e79638d0 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/__pycache__/_synchronization.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/__pycache__/_trace.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/__pycache__/_trace.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..377b01002ea54af31e4dfda92f080cf90150bdf0 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/__pycache__/_trace.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/__pycache__/_utils.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ff901f66664d69d4441f1fb1f0c6acf791b94ab Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/__pycache__/_utils.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_api.py b/lib/python3.12/site-packages/httpcore/_api.py new file mode 100644 index 0000000000000000000000000000000000000000..38b961d10de88bebc98c758d0d1f14af1e7c0370 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_api.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +import contextlib +import typing + +from ._models import URL, Extensions, HeaderTypes, Response +from ._sync.connection_pool import ConnectionPool + + +def request( + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, +) -> Response: + """ + Sends an HTTP request, returning the response. + + ``` + response = httpcore.request("GET", "https://www.example.com/") + ``` + + Arguments: + method: The HTTP method for the request. Typically one of `"GET"`, + `"OPTIONS"`, `"HEAD"`, `"POST"`, `"PUT"`, `"PATCH"`, or `"DELETE"`. + url: The URL of the HTTP request. Either as an instance of `httpcore.URL`, + or as str/bytes. + headers: The HTTP request headers. Either as a dictionary of str/bytes, + or as a list of two-tuples of str/bytes. + content: The content of the request body. Either as bytes, + or as a bytes iterator. + extensions: A dictionary of optional extra information included on the request. + Possible keys include `"timeout"`. + + Returns: + An instance of `httpcore.Response`. + """ + with ConnectionPool() as pool: + return pool.request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + + +@contextlib.contextmanager +def stream( + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, +) -> typing.Iterator[Response]: + """ + Sends an HTTP request, returning the response within a content manager. + + ``` + with httpcore.stream("GET", "https://www.example.com/") as response: + ... + ``` + + When using the `stream()` function, the body of the response will not be + automatically read. If you want to access the response body you should + either use `content = response.read()`, or `for chunk in response.iter_content()`. + + Arguments: + method: The HTTP method for the request. Typically one of `"GET"`, + `"OPTIONS"`, `"HEAD"`, `"POST"`, `"PUT"`, `"PATCH"`, or `"DELETE"`. + url: The URL of the HTTP request. Either as an instance of `httpcore.URL`, + or as str/bytes. + headers: The HTTP request headers. Either as a dictionary of str/bytes, + or as a list of two-tuples of str/bytes. + content: The content of the request body. Either as bytes, + or as a bytes iterator. + extensions: A dictionary of optional extra information included on the request. + Possible keys include `"timeout"`. + + Returns: + An instance of `httpcore.Response`. + """ + with ConnectionPool() as pool: + with pool.stream( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) as response: + yield response diff --git a/lib/python3.12/site-packages/httpcore/_async/__init__.py b/lib/python3.12/site-packages/httpcore/_async/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..88dc7f01e132933728cbcf45c88ce82e85ddf65f --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_async/__init__.py @@ -0,0 +1,39 @@ +from .connection import AsyncHTTPConnection +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .http_proxy import AsyncHTTPProxy +from .interfaces import AsyncConnectionInterface + +try: + from .http2 import AsyncHTTP2Connection +except ImportError: # pragma: nocover + + class AsyncHTTP2Connection: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use http2 support, but the `h2` package is not " + "installed. Use 'pip install httpcore[http2]'." + ) + + +try: + from .socks_proxy import AsyncSOCKSProxy +except ImportError: # pragma: nocover + + class AsyncSOCKSProxy: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use SOCKS support, but the `socksio` package is not " + "installed. Use 'pip install httpcore[socks]'." + ) + + +__all__ = [ + "AsyncHTTPConnection", + "AsyncConnectionPool", + "AsyncHTTPProxy", + "AsyncHTTP11Connection", + "AsyncHTTP2Connection", + "AsyncConnectionInterface", + "AsyncSOCKSProxy", +] diff --git a/lib/python3.12/site-packages/httpcore/_async/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_async/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afec1a55405e4411b12af31b3e2cfdc1f2c5fa82 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_async/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9273f1da10f22bbb224ac4d7705bc583111bf8f1 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ad0285888d8a18d2ff7b96becc36e16b930abd3 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_async/__pycache__/http11.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_async/__pycache__/http11.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13a1671613665057a900c207062cbc705e964d00 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_async/__pycache__/http11.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_async/__pycache__/http2.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_async/__pycache__/http2.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d2b01abbbae9dee322fdc1f814250c0d066c388 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_async/__pycache__/http2.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30bf549aaac9f65f3a731ef3be578957cd96d6ef Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_async/__pycache__/interfaces.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_async/__pycache__/interfaces.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32a3320ddf452edc2a9e970e7d17dfc3a8dfb748 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_async/__pycache__/interfaces.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f48869ca38468f0036c69ffdad4bf427bd7f842 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_async/connection.py b/lib/python3.12/site-packages/httpcore/_async/connection.py new file mode 100644 index 0000000000000000000000000000000000000000..b42581dff8aabf4c2ef80ffda26296e1b368d693 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_async/connection.py @@ -0,0 +1,222 @@ +from __future__ import annotations + +import itertools +import logging +import ssl +import types +import typing + +from .._backends.auto import AutoBackend +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream +from .._exceptions import ConnectError, ConnectTimeout +from .._models import Origin, Request, Response +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +RETRIES_BACKOFF_FACTOR = 0.5 # 0s, 0.5s, 1s, 2s, 4s, etc. + + +logger = logging.getLogger("httpcore.connection") + + +def exponential_backoff(factor: float) -> typing.Iterator[float]: + """ + Generate a geometric sequence that has a ratio of 2 and starts with 0. + + For example: + - `factor = 2`: `0, 2, 4, 8, 16, 32, 64, ...` + - `factor = 3`: `0, 3, 6, 12, 24, 48, 96, ...` + """ + yield 0 + for n in itertools.count(): + yield factor * 2**n + + +class AsyncHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + origin: Origin, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._origin = origin + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend: AsyncNetworkBackend = ( + AutoBackend() if network_backend is None else network_backend + ) + self._connection: AsyncConnectionInterface | None = None + self._connect_failed: bool = False + self._request_lock = AsyncLock() + self._socket_options = socket_options + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection to {self._origin}" + ) + + try: + async with self._request_lock: + if self._connection is None: + stream = await self._connect(request) + + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except BaseException as exc: + self._connect_failed = True + raise exc + + return await self._connection.handle_async_request(request) + + async def _connect(self, request: Request) -> AsyncNetworkStream: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + retries_left = self._retries + delays = exponential_backoff(factor=RETRIES_BACKOFF_FACTOR) + + while True: + try: + if self._uds is None: + kwargs = { + "host": self._origin.host.decode("ascii"), + "port": self._origin.port, + "local_address": self._local_address, + "timeout": timeout, + "socket_options": self._socket_options, + } + async with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = await self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + else: + kwargs = { + "path": self._uds, + "timeout": timeout, + "socket_options": self._socket_options, + } + async with Trace( + "connect_unix_socket", logger, request, kwargs + ) as trace: + stream = await self._network_backend.connect_unix_socket( + **kwargs + ) + trace.return_value = stream + + if self._origin.scheme in (b"https", b"wss"): + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + return stream + except (ConnectError, ConnectTimeout): + if retries_left <= 0: + raise + retries_left -= 1 + delay = next(delays) + async with Trace("retry", logger, request, kwargs) as trace: + await self._network_backend.sleep(delay) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + async def aclose(self) -> None: + if self._connection is not None: + async with Trace("close", logger, None, {}): + await self._connection.aclose() + + def is_available(self) -> bool: + if self._connection is None: + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> AsyncHTTPConnection: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() diff --git a/lib/python3.12/site-packages/httpcore/_async/connection_pool.py b/lib/python3.12/site-packages/httpcore/_async/connection_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..96e973d0ce223f6bed9be9e6a6a2f3c01622c611 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_async/connection_pool.py @@ -0,0 +1,420 @@ +from __future__ import annotations + +import ssl +import sys +import types +import typing + +from .._backends.auto import AutoBackend +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend +from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol +from .._models import Origin, Proxy, Request, Response +from .._synchronization import AsyncEvent, AsyncShieldCancellation, AsyncThreadLock +from .connection import AsyncHTTPConnection +from .interfaces import AsyncConnectionInterface, AsyncRequestInterface + + +class AsyncPoolRequest: + def __init__(self, request: Request) -> None: + self.request = request + self.connection: AsyncConnectionInterface | None = None + self._connection_acquired = AsyncEvent() + + def assign_to_connection(self, connection: AsyncConnectionInterface | None) -> None: + self.connection = connection + self._connection_acquired.set() + + def clear_connection(self) -> None: + self.connection = None + self._connection_acquired = AsyncEvent() + + async def wait_for_connection( + self, timeout: float | None = None + ) -> AsyncConnectionInterface: + if self.connection is None: + await self._connection_acquired.wait(timeout=timeout) + assert self.connection is not None + return self.connection + + def is_queued(self) -> bool: + return self.connection is None + + +class AsyncConnectionPool(AsyncRequestInterface): + """ + A connection pool for making HTTP requests. + """ + + def __init__( + self, + ssl_context: ssl.SSLContext | None = None, + proxy: Proxy | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish a + connection. + local_address: Local address to connect from. Can also be used to connect + using a particular address family. Using `local_address="0.0.0.0"` + will connect using an `AF_INET` address (IPv4), while using + `local_address="::"` will connect using an `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + socket_options: Socket options that have to be included + in the TCP socket when the connection was established. + """ + self._ssl_context = ssl_context + self._proxy = proxy + self._max_connections = ( + sys.maxsize if max_connections is None else max_connections + ) + self._max_keepalive_connections = ( + sys.maxsize + if max_keepalive_connections is None + else max_keepalive_connections + ) + self._max_keepalive_connections = min( + self._max_connections, self._max_keepalive_connections + ) + + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend = ( + AutoBackend() if network_backend is None else network_backend + ) + self._socket_options = socket_options + + # The mutable state on a connection pool is the queue of incoming requests, + # and the set of connections that are servicing those requests. + self._connections: list[AsyncConnectionInterface] = [] + self._requests: list[AsyncPoolRequest] = [] + + # We only mutate the state of the connection pool within an 'optional_thread_lock' + # context. This holds a threading lock unless we're running in async mode, + # in which case it is a no-op. + self._optional_thread_lock = AsyncThreadLock() + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + if self._proxy is not None: + if self._proxy.url.scheme in (b"socks5", b"socks5h"): + from .socks_proxy import AsyncSocks5Connection + + return AsyncSocks5Connection( + proxy_origin=self._proxy.url.origin, + proxy_auth=self._proxy.auth, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + elif origin.scheme == b"http": + from .http_proxy import AsyncForwardHTTPConnection + + return AsyncForwardHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + ) + from .http_proxy import AsyncTunnelHTTPConnection + + return AsyncTunnelHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + return AsyncHTTPConnection( + origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + retries=self._retries, + local_address=self._local_address, + uds=self._uds, + network_backend=self._network_backend, + socket_options=self._socket_options, + ) + + @property + def connections(self) -> list[AsyncConnectionInterface]: + """ + Return a list of the connections currently in the pool. + + For example: + + ```python + >>> pool.connections + [ + , + , + , + ] + ``` + """ + return list(self._connections) + + async def handle_async_request(self, request: Request) -> Response: + """ + Send an HTTP request, and return an HTTP response. + + This is the core implementation that is called into by `.request()` or `.stream()`. + """ + scheme = request.url.scheme.decode() + if scheme == "": + raise UnsupportedProtocol( + "Request URL is missing an 'http://' or 'https://' protocol." + ) + if scheme not in ("http", "https", "ws", "wss"): + raise UnsupportedProtocol( + f"Request URL has an unsupported protocol '{scheme}://'." + ) + + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("pool", None) + + with self._optional_thread_lock: + # Add the incoming request to our request queue. + pool_request = AsyncPoolRequest(request) + self._requests.append(pool_request) + + try: + while True: + with self._optional_thread_lock: + # Assign incoming requests to available connections, + # closing or creating new connections as required. + closing = self._assign_requests_to_connections() + await self._close_connections(closing) + + # Wait until this request has an assigned connection. + connection = await pool_request.wait_for_connection(timeout=timeout) + + try: + # Send the request on the assigned connection. + response = await connection.handle_async_request( + pool_request.request + ) + except ConnectionNotAvailable: + # In some cases a connection may initially be available to + # handle a request, but then become unavailable. + # + # In this case we clear the connection and try again. + pool_request.clear_connection() + else: + break # pragma: nocover + + except BaseException as exc: + with self._optional_thread_lock: + # For any exception or cancellation we remove the request from + # the queue, and then re-assign requests to connections. + self._requests.remove(pool_request) + closing = self._assign_requests_to_connections() + + await self._close_connections(closing) + raise exc from None + + # Return the response. Note that in this case we still have to manage + # the point at which the response is closed. + assert isinstance(response.stream, typing.AsyncIterable) + return Response( + status=response.status, + headers=response.headers, + content=PoolByteStream( + stream=response.stream, pool_request=pool_request, pool=self + ), + extensions=response.extensions, + ) + + def _assign_requests_to_connections(self) -> list[AsyncConnectionInterface]: + """ + Manage the state of the connection pool, assigning incoming + requests to connections as available. + + Called whenever a new request is added or removed from the pool. + + Any closing connections are returned, allowing the I/O for closing + those connections to be handled seperately. + """ + closing_connections = [] + + # First we handle cleaning up any connections that are closed, + # have expired their keep-alive, or surplus idle connections. + for connection in list(self._connections): + if connection.is_closed(): + # log: "removing closed connection" + self._connections.remove(connection) + elif connection.has_expired(): + # log: "closing expired connection" + self._connections.remove(connection) + closing_connections.append(connection) + elif ( + connection.is_idle() + and len([connection.is_idle() for connection in self._connections]) + > self._max_keepalive_connections + ): + # log: "closing idle connection" + self._connections.remove(connection) + closing_connections.append(connection) + + # Assign queued requests to connections. + queued_requests = [request for request in self._requests if request.is_queued()] + for pool_request in queued_requests: + origin = pool_request.request.url.origin + available_connections = [ + connection + for connection in self._connections + if connection.can_handle_request(origin) and connection.is_available() + ] + idle_connections = [ + connection for connection in self._connections if connection.is_idle() + ] + + # There are three cases for how we may be able to handle the request: + # + # 1. There is an existing connection that can handle the request. + # 2. We can create a new connection to handle the request. + # 3. We can close an idle connection and then create a new connection + # to handle the request. + if available_connections: + # log: "reusing existing connection" + connection = available_connections[0] + pool_request.assign_to_connection(connection) + elif len(self._connections) < self._max_connections: + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + elif idle_connections: + # log: "closing idle connection" + connection = idle_connections[0] + self._connections.remove(connection) + closing_connections.append(connection) + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + + return closing_connections + + async def _close_connections(self, closing: list[AsyncConnectionInterface]) -> None: + # Close connections which have been removed from the pool. + with AsyncShieldCancellation(): + for connection in closing: + await connection.aclose() + + async def aclose(self) -> None: + # Explicitly close the connection pool. + # Clears all existing requests and connections. + with self._optional_thread_lock: + closing_connections = list(self._connections) + self._connections = [] + await self._close_connections(closing_connections) + + async def __aenter__(self) -> AsyncConnectionPool: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + with self._optional_thread_lock: + request_is_queued = [request.is_queued() for request in self._requests] + connection_is_idle = [ + connection.is_idle() for connection in self._connections + ] + + num_active_requests = request_is_queued.count(False) + num_queued_requests = request_is_queued.count(True) + num_active_connections = connection_is_idle.count(False) + num_idle_connections = connection_is_idle.count(True) + + requests_info = ( + f"Requests: {num_active_requests} active, {num_queued_requests} queued" + ) + connection_info = ( + f"Connections: {num_active_connections} active, {num_idle_connections} idle" + ) + + return f"<{class_name} [{requests_info} | {connection_info}]>" + + +class PoolByteStream: + def __init__( + self, + stream: typing.AsyncIterable[bytes], + pool_request: AsyncPoolRequest, + pool: AsyncConnectionPool, + ) -> None: + self._stream = stream + self._pool_request = pool_request + self._pool = pool + self._closed = False + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + try: + async for part in self._stream: + yield part + except BaseException as exc: + await self.aclose() + raise exc from None + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + with AsyncShieldCancellation(): + if hasattr(self._stream, "aclose"): + await self._stream.aclose() + + with self._pool._optional_thread_lock: + self._pool._requests.remove(self._pool_request) + closing = self._pool._assign_requests_to_connections() + + await self._pool._close_connections(closing) diff --git a/lib/python3.12/site-packages/httpcore/_async/http11.py b/lib/python3.12/site-packages/httpcore/_async/http11.py new file mode 100644 index 0000000000000000000000000000000000000000..e6d6d709852b137a862cfe2b3af42dc790fa705d --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_async/http11.py @@ -0,0 +1,379 @@ +from __future__ import annotations + +import enum +import logging +import ssl +import time +import types +import typing + +import h11 + +from .._backends.base import AsyncNetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, + WriteError, + map_exceptions, +) +from .._models import Origin, Request, Response +from .._synchronization import AsyncLock, AsyncShieldCancellation +from .._trace import Trace +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore.http11") + + +# A subset of `h11.Event` types supported by `_send_event` +H11SendEvent = typing.Union[ + h11.Request, + h11.Data, + h11.EndOfMessage, +] + + +class HTTPConnectionState(enum.IntEnum): + NEW = 0 + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class AsyncHTTP11Connection(AsyncConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + MAX_INCOMPLETE_EVENT_SIZE = 100 * 1024 + + def __init__( + self, + origin: Origin, + stream: AsyncNetworkStream, + keepalive_expiry: float | None = None, + ) -> None: + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._expire_at: float | None = None + self._state = HTTPConnectionState.NEW + self._state_lock = AsyncLock() + self._request_count = 0 + self._h11_state = h11.Connection( + our_role=h11.CLIENT, + max_incomplete_event_size=self.MAX_INCOMPLETE_EVENT_SIZE, + ) + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + async with self._state_lock: + if self._state in (HTTPConnectionState.NEW, HTTPConnectionState.IDLE): + self._request_count += 1 + self._state = HTTPConnectionState.ACTIVE + self._expire_at = None + else: + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request} + try: + async with Trace( + "send_request_headers", logger, request, kwargs + ) as trace: + await self._send_request_headers(**kwargs) + async with Trace("send_request_body", logger, request, kwargs) as trace: + await self._send_request_body(**kwargs) + except WriteError: + # If we get a write error while we're writing the request, + # then we supress this error and move on to attempting to + # read the response. Servers can sometimes close the request + # pre-emptively and then respond with a well formed HTTP + # error response. + pass + + async with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + ( + http_version, + status, + reason_phrase, + headers, + trailing_data, + ) = await self._receive_response_headers(**kwargs) + trace.return_value = ( + http_version, + status, + reason_phrase, + headers, + ) + + network_stream = self._network_stream + + # CONNECT or Upgrade request + if (status == 101) or ( + (request.method == b"CONNECT") and (200 <= status < 300) + ): + network_stream = AsyncHTTP11UpgradeStream(network_stream, trailing_data) + + return Response( + status=status, + headers=headers, + content=HTTP11ConnectionByteStream(self, request), + extensions={ + "http_version": http_version, + "reason_phrase": reason_phrase, + "network_stream": network_stream, + }, + ) + except BaseException as exc: + with AsyncShieldCancellation(): + async with Trace("response_closed", logger, request) as trace: + await self._response_closed() + raise exc + + # Sending the request... + + async def _send_request_headers(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with map_exceptions({h11.LocalProtocolError: LocalProtocolError}): + event = h11.Request( + method=request.method, + target=request.url.target, + headers=request.headers, + ) + await self._send_event(event, timeout=timeout) + + async def _send_request_body(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + assert isinstance(request.stream, typing.AsyncIterable) + async for chunk in request.stream: + event = h11.Data(data=chunk) + await self._send_event(event, timeout=timeout) + + await self._send_event(h11.EndOfMessage(), timeout=timeout) + + async def _send_event(self, event: h11.Event, timeout: float | None = None) -> None: + bytes_to_send = self._h11_state.send(event) + if bytes_to_send is not None: + await self._network_stream.write(bytes_to_send, timeout=timeout) + + # Receiving the response... + + async def _receive_response_headers( + self, request: Request + ) -> tuple[bytes, int, bytes, list[tuple[bytes, bytes]], bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = await self._receive_event(timeout=timeout) + if isinstance(event, h11.Response): + break + if ( + isinstance(event, h11.InformationalResponse) + and event.status_code == 101 + ): + break + + http_version = b"HTTP/" + event.http_version + + # h11 version 0.11+ supports a `raw_items` interface to get the + # raw header casing, rather than the enforced lowercase headers. + headers = event.headers.raw_items() + + trailing_data, _ = self._h11_state.trailing_data + + return http_version, event.status_code, event.reason, headers, trailing_data + + async def _receive_response_body( + self, request: Request + ) -> typing.AsyncIterator[bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = await self._receive_event(timeout=timeout) + if isinstance(event, h11.Data): + yield bytes(event.data) + elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)): + break + + async def _receive_event( + self, timeout: float | None = None + ) -> h11.Event | type[h11.PAUSED]: + while True: + with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}): + event = self._h11_state.next_event() + + if event is h11.NEED_DATA: + data = await self._network_stream.read( + self.READ_NUM_BYTES, timeout=timeout + ) + + # If we feed this case through h11 we'll raise an exception like: + # + # httpcore.RemoteProtocolError: can't handle event type + # ConnectionClosed when role=SERVER and state=SEND_RESPONSE + # + # Which is accurate, but not very informative from an end-user + # perspective. Instead we handle this case distinctly and treat + # it as a ConnectError. + if data == b"" and self._h11_state.their_state == h11.SEND_RESPONSE: + msg = "Server disconnected without sending a response." + raise RemoteProtocolError(msg) + + self._h11_state.receive_data(data) + else: + # mypy fails to narrow the type in the above if statement above + return event # type: ignore[return-value] + + async def _response_closed(self) -> None: + async with self._state_lock: + if ( + self._h11_state.our_state is h11.DONE + and self._h11_state.their_state is h11.DONE + ): + self._state = HTTPConnectionState.IDLE + self._h11_state.start_next_cycle() + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + else: + await self.aclose() + + # Once the connection is no longer required... + + async def aclose(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._state = HTTPConnectionState.CLOSED + await self._network_stream.aclose() + + # The AsyncConnectionInterface methods provide information about the state of + # the connection, allowing for a connection pooling implementation to + # determine when to reuse and when to close the connection... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + # Note that HTTP/1.1 connections in the "NEW" state are not treated as + # being "available". The control flow which created the connection will + # be able to send an outgoing request, but the connection will not be + # acquired from the connection pool for any other request. + return self._state == HTTPConnectionState.IDLE + + def has_expired(self) -> bool: + now = time.monotonic() + keepalive_expired = self._expire_at is not None and now > self._expire_at + + # If the HTTP connection is idle but the socket is readable, then the + # only valid state is that the socket is about to return b"", indicating + # a server-initiated disconnect. + server_disconnected = ( + self._state == HTTPConnectionState.IDLE + and self._network_stream.get_extra_info("is_readable") + ) + + return keepalive_expired or server_disconnected + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/1.1, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> AsyncHTTP11Connection: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() + + +class HTTP11ConnectionByteStream: + def __init__(self, connection: AsyncHTTP11Connection, request: Request) -> None: + self._connection = connection + self._request = request + self._closed = False + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + kwargs = {"request": self._request} + try: + async with Trace("receive_response_body", logger, self._request, kwargs): + async for chunk in self._connection._receive_response_body(**kwargs): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + async with Trace("response_closed", logger, self._request): + await self._connection._response_closed() + + +class AsyncHTTP11UpgradeStream(AsyncNetworkStream): + def __init__(self, stream: AsyncNetworkStream, leading_data: bytes) -> None: + self._stream = stream + self._leading_data = leading_data + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._leading_data: + buffer = self._leading_data[:max_bytes] + self._leading_data = self._leading_data[max_bytes:] + return buffer + else: + return await self._stream.read(max_bytes, timeout) + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + await self._stream.write(buffer, timeout) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + return await self._stream.start_tls(ssl_context, server_hostname, timeout) + + def get_extra_info(self, info: str) -> typing.Any: + return self._stream.get_extra_info(info) diff --git a/lib/python3.12/site-packages/httpcore/_async/http2.py b/lib/python3.12/site-packages/httpcore/_async/http2.py new file mode 100644 index 0000000000000000000000000000000000000000..dbd0beeb4da32d8c0175d412fa442eae8f837723 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_async/http2.py @@ -0,0 +1,592 @@ +from __future__ import annotations + +import enum +import logging +import time +import types +import typing + +import h2.config +import h2.connection +import h2.events +import h2.exceptions +import h2.settings + +from .._backends.base import AsyncNetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, +) +from .._models import Origin, Request, Response +from .._synchronization import AsyncLock, AsyncSemaphore, AsyncShieldCancellation +from .._trace import Trace +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore.http2") + + +def has_body_headers(request: Request) -> bool: + return any( + k.lower() == b"content-length" or k.lower() == b"transfer-encoding" + for k, v in request.headers + ) + + +class HTTPConnectionState(enum.IntEnum): + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class AsyncHTTP2Connection(AsyncConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + CONFIG = h2.config.H2Configuration(validate_inbound_headers=False) + + def __init__( + self, + origin: Origin, + stream: AsyncNetworkStream, + keepalive_expiry: float | None = None, + ): + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._h2_state = h2.connection.H2Connection(config=self.CONFIG) + self._state = HTTPConnectionState.IDLE + self._expire_at: float | None = None + self._request_count = 0 + self._init_lock = AsyncLock() + self._state_lock = AsyncLock() + self._read_lock = AsyncLock() + self._write_lock = AsyncLock() + self._sent_connection_init = False + self._used_all_stream_ids = False + self._connection_error = False + + # Mapping from stream ID to response stream events. + self._events: dict[ + int, + list[ + h2.events.ResponseReceived + | h2.events.DataReceived + | h2.events.StreamEnded + | h2.events.StreamReset, + ], + ] = {} + + # Connection terminated events are stored as state since + # we need to handle them for all streams. + self._connection_terminated: h2.events.ConnectionTerminated | None = None + + self._read_exception: Exception | None = None + self._write_exception: Exception | None = None + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + # This cannot occur in normal operation, since the connection pool + # will only send requests on connections that handle them. + # It's in place simply for resilience as a guard against incorrect + # usage, for anyone working directly with httpcore connections. + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + async with self._state_lock: + if self._state in (HTTPConnectionState.ACTIVE, HTTPConnectionState.IDLE): + self._request_count += 1 + self._expire_at = None + self._state = HTTPConnectionState.ACTIVE + else: + raise ConnectionNotAvailable() + + async with self._init_lock: + if not self._sent_connection_init: + try: + sci_kwargs = {"request": request} + async with Trace( + "send_connection_init", logger, request, sci_kwargs + ): + await self._send_connection_init(**sci_kwargs) + except BaseException as exc: + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + self._sent_connection_init = True + + # Initially start with just 1 until the remote server provides + # its max_concurrent_streams value + self._max_streams = 1 + + local_settings_max_streams = ( + self._h2_state.local_settings.max_concurrent_streams + ) + self._max_streams_semaphore = AsyncSemaphore(local_settings_max_streams) + + for _ in range(local_settings_max_streams - self._max_streams): + await self._max_streams_semaphore.acquire() + + await self._max_streams_semaphore.acquire() + + try: + stream_id = self._h2_state.get_next_available_stream_id() + self._events[stream_id] = [] + except h2.exceptions.NoAvailableStreamIDError: # pragma: nocover + self._used_all_stream_ids = True + self._request_count -= 1 + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request, "stream_id": stream_id} + async with Trace("send_request_headers", logger, request, kwargs): + await self._send_request_headers(request=request, stream_id=stream_id) + async with Trace("send_request_body", logger, request, kwargs): + await self._send_request_body(request=request, stream_id=stream_id) + async with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + status, headers = await self._receive_response( + request=request, stream_id=stream_id + ) + trace.return_value = (status, headers) + + return Response( + status=status, + headers=headers, + content=HTTP2ConnectionByteStream(self, request, stream_id=stream_id), + extensions={ + "http_version": b"HTTP/2", + "network_stream": self._network_stream, + "stream_id": stream_id, + }, + ) + except BaseException as exc: # noqa: PIE786 + with AsyncShieldCancellation(): + kwargs = {"stream_id": stream_id} + async with Trace("response_closed", logger, request, kwargs): + await self._response_closed(stream_id=stream_id) + + if isinstance(exc, h2.exceptions.ProtocolError): + # One case where h2 can raise a protocol error is when a + # closed frame has been seen by the state machine. + # + # This happens when one stream is reading, and encounters + # a GOAWAY event. Other flows of control may then raise + # a protocol error at any point they interact with the 'h2_state'. + # + # In this case we'll have stored the event, and should raise + # it as a RemoteProtocolError. + if self._connection_terminated: # pragma: nocover + raise RemoteProtocolError(self._connection_terminated) + # If h2 raises a protocol error in some other state then we + # must somehow have made a protocol violation. + raise LocalProtocolError(exc) # pragma: nocover + + raise exc + + async def _send_connection_init(self, request: Request) -> None: + """ + The HTTP/2 connection requires some initial setup before we can start + using individual request/response streams on it. + """ + # Need to set these manually here instead of manipulating via + # __setitem__() otherwise the H2Connection will emit SettingsUpdate + # frames in addition to sending the undesired defaults. + self._h2_state.local_settings = h2.settings.Settings( + client=True, + initial_values={ + # Disable PUSH_PROMISE frames from the server since we don't do anything + # with them for now. Maybe when we support caching? + h2.settings.SettingCodes.ENABLE_PUSH: 0, + # These two are taken from h2 for safe defaults + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS: 100, + h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE: 65536, + }, + ) + + # Some websites (*cough* Yahoo *cough*) balk at this setting being + # present in the initial handshake since it's not defined in the original + # RFC despite the RFC mandating ignoring settings you don't know about. + del self._h2_state.local_settings[ + h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL + ] + + self._h2_state.initiate_connection() + self._h2_state.increment_flow_control_window(2**24) + await self._write_outgoing_data(request) + + # Sending the request... + + async def _send_request_headers(self, request: Request, stream_id: int) -> None: + """ + Send the request headers to a given stream ID. + """ + end_stream = not has_body_headers(request) + + # In HTTP/2 the ':authority' pseudo-header is used instead of 'Host'. + # In order to gracefully handle HTTP/1.1 and HTTP/2 we always require + # HTTP/1.1 style headers, and map them appropriately if we end up on + # an HTTP/2 connection. + authority = [v for k, v in request.headers if k.lower() == b"host"][0] + + headers = [ + (b":method", request.method), + (b":authority", authority), + (b":scheme", request.url.scheme), + (b":path", request.url.target), + ] + [ + (k.lower(), v) + for k, v in request.headers + if k.lower() + not in ( + b"host", + b"transfer-encoding", + ) + ] + + self._h2_state.send_headers(stream_id, headers, end_stream=end_stream) + self._h2_state.increment_flow_control_window(2**24, stream_id=stream_id) + await self._write_outgoing_data(request) + + async def _send_request_body(self, request: Request, stream_id: int) -> None: + """ + Iterate over the request body sending it to a given stream ID. + """ + if not has_body_headers(request): + return + + assert isinstance(request.stream, typing.AsyncIterable) + async for data in request.stream: + await self._send_stream_data(request, stream_id, data) + await self._send_end_stream(request, stream_id) + + async def _send_stream_data( + self, request: Request, stream_id: int, data: bytes + ) -> None: + """ + Send a single chunk of data in one or more data frames. + """ + while data: + max_flow = await self._wait_for_outgoing_flow(request, stream_id) + chunk_size = min(len(data), max_flow) + chunk, data = data[:chunk_size], data[chunk_size:] + self._h2_state.send_data(stream_id, chunk) + await self._write_outgoing_data(request) + + async def _send_end_stream(self, request: Request, stream_id: int) -> None: + """ + Send an empty data frame on on a given stream ID with the END_STREAM flag set. + """ + self._h2_state.end_stream(stream_id) + await self._write_outgoing_data(request) + + # Receiving the response... + + async def _receive_response( + self, request: Request, stream_id: int + ) -> tuple[int, list[tuple[bytes, bytes]]]: + """ + Return the response status code and headers for a given stream ID. + """ + while True: + event = await self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.ResponseReceived): + break + + status_code = 200 + headers = [] + assert event.headers is not None + for k, v in event.headers: + if k == b":status": + status_code = int(v.decode("ascii", errors="ignore")) + elif not k.startswith(b":"): + headers.append((k, v)) + + return (status_code, headers) + + async def _receive_response_body( + self, request: Request, stream_id: int + ) -> typing.AsyncIterator[bytes]: + """ + Iterator that returns the bytes of the response body for a given stream ID. + """ + while True: + event = await self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.DataReceived): + assert event.flow_controlled_length is not None + assert event.data is not None + amount = event.flow_controlled_length + self._h2_state.acknowledge_received_data(amount, stream_id) + await self._write_outgoing_data(request) + yield event.data + elif isinstance(event, h2.events.StreamEnded): + break + + async def _receive_stream_event( + self, request: Request, stream_id: int + ) -> h2.events.ResponseReceived | h2.events.DataReceived | h2.events.StreamEnded: + """ + Return the next available event for a given stream ID. + + Will read more data from the network if required. + """ + while not self._events.get(stream_id): + await self._receive_events(request, stream_id) + event = self._events[stream_id].pop(0) + if isinstance(event, h2.events.StreamReset): + raise RemoteProtocolError(event) + return event + + async def _receive_events( + self, request: Request, stream_id: int | None = None + ) -> None: + """ + Read some data from the network until we see one or more events + for a given stream ID. + """ + async with self._read_lock: + if self._connection_terminated is not None: + last_stream_id = self._connection_terminated.last_stream_id + if stream_id and last_stream_id and stream_id > last_stream_id: + self._request_count -= 1 + raise ConnectionNotAvailable() + raise RemoteProtocolError(self._connection_terminated) + + # This conditional is a bit icky. We don't want to block reading if we've + # actually got an event to return for a given stream. We need to do that + # check *within* the atomic read lock. Though it also need to be optional, + # because when we call it from `_wait_for_outgoing_flow` we *do* want to + # block until we've available flow control, event when we have events + # pending for the stream ID we're attempting to send on. + if stream_id is None or not self._events.get(stream_id): + events = await self._read_incoming_data(request) + for event in events: + if isinstance(event, h2.events.RemoteSettingsChanged): + async with Trace( + "receive_remote_settings", logger, request + ) as trace: + await self._receive_remote_settings_change(event) + trace.return_value = event + + elif isinstance( + event, + ( + h2.events.ResponseReceived, + h2.events.DataReceived, + h2.events.StreamEnded, + h2.events.StreamReset, + ), + ): + if event.stream_id in self._events: + self._events[event.stream_id].append(event) + + elif isinstance(event, h2.events.ConnectionTerminated): + self._connection_terminated = event + + await self._write_outgoing_data(request) + + async def _receive_remote_settings_change( + self, event: h2.events.RemoteSettingsChanged + ) -> None: + max_concurrent_streams = event.changed_settings.get( + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS + ) + if max_concurrent_streams: + new_max_streams = min( + max_concurrent_streams.new_value, + self._h2_state.local_settings.max_concurrent_streams, + ) + if new_max_streams and new_max_streams != self._max_streams: + while new_max_streams > self._max_streams: + await self._max_streams_semaphore.release() + self._max_streams += 1 + while new_max_streams < self._max_streams: + await self._max_streams_semaphore.acquire() + self._max_streams -= 1 + + async def _response_closed(self, stream_id: int) -> None: + await self._max_streams_semaphore.release() + del self._events[stream_id] + async with self._state_lock: + if self._connection_terminated and not self._events: + await self.aclose() + + elif self._state == HTTPConnectionState.ACTIVE and not self._events: + self._state = HTTPConnectionState.IDLE + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + if self._used_all_stream_ids: # pragma: nocover + await self.aclose() + + async def aclose(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._h2_state.close_connection() + self._state = HTTPConnectionState.CLOSED + await self._network_stream.aclose() + + # Wrappers around network read/write operations... + + async def _read_incoming_data(self, request: Request) -> list[h2.events.Event]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + if self._read_exception is not None: + raise self._read_exception # pragma: nocover + + try: + data = await self._network_stream.read(self.READ_NUM_BYTES, timeout) + if data == b"": + raise RemoteProtocolError("Server disconnected") + except Exception as exc: + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future reads. + # (For example, this means that a single read timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._read_exception = exc + self._connection_error = True + raise exc + + events: list[h2.events.Event] = self._h2_state.receive_data(data) + + return events + + async def _write_outgoing_data(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + async with self._write_lock: + data_to_send = self._h2_state.data_to_send() + + if self._write_exception is not None: + raise self._write_exception # pragma: nocover + + try: + await self._network_stream.write(data_to_send, timeout) + except Exception as exc: # pragma: nocover + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future write. + # (For example, this means that a single write timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._write_exception = exc + self._connection_error = True + raise exc + + # Flow control... + + async def _wait_for_outgoing_flow(self, request: Request, stream_id: int) -> int: + """ + Returns the maximum allowable outgoing flow for a given stream. + + If the allowable flow is zero, then waits on the network until + WindowUpdated frames have increased the flow rate. + https://tools.ietf.org/html/rfc7540#section-6.9 + """ + local_flow: int = self._h2_state.local_flow_control_window(stream_id) + max_frame_size: int = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + while flow == 0: + await self._receive_events(request) + local_flow = self._h2_state.local_flow_control_window(stream_id) + max_frame_size = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + return flow + + # Interface for connection pooling... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + return ( + self._state != HTTPConnectionState.CLOSED + and not self._connection_error + and not self._used_all_stream_ids + and not ( + self._h2_state.state_machine.state + == h2.connection.ConnectionState.CLOSED + ) + ) + + def has_expired(self) -> bool: + now = time.monotonic() + return self._expire_at is not None and now > self._expire_at + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/2, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> AsyncHTTP2Connection: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + await self.aclose() + + +class HTTP2ConnectionByteStream: + def __init__( + self, connection: AsyncHTTP2Connection, request: Request, stream_id: int + ) -> None: + self._connection = connection + self._request = request + self._stream_id = stream_id + self._closed = False + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + kwargs = {"request": self._request, "stream_id": self._stream_id} + try: + async with Trace("receive_response_body", logger, self._request, kwargs): + async for chunk in self._connection._receive_response_body( + request=self._request, stream_id=self._stream_id + ): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + kwargs = {"stream_id": self._stream_id} + async with Trace("response_closed", logger, self._request, kwargs): + await self._connection._response_closed(stream_id=self._stream_id) diff --git a/lib/python3.12/site-packages/httpcore/_async/http_proxy.py b/lib/python3.12/site-packages/httpcore/_async/http_proxy.py new file mode 100644 index 0000000000000000000000000000000000000000..cc9d92066e1680576846e46ccdf645a2b1dd5718 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_async/http_proxy.py @@ -0,0 +1,367 @@ +from __future__ import annotations + +import base64 +import logging +import ssl +import typing + +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend +from .._exceptions import ProxyError +from .._models import ( + URL, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, +) +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .connection import AsyncHTTPConnection +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +ByteOrStr = typing.Union[bytes, str] +HeadersAsSequence = typing.Sequence[typing.Tuple[ByteOrStr, ByteOrStr]] +HeadersAsMapping = typing.Mapping[ByteOrStr, ByteOrStr] + + +logger = logging.getLogger("httpcore.proxy") + + +def merge_headers( + default_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + override_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, +) -> list[tuple[bytes, bytes]]: + """ + Append default_headers and override_headers, de-duplicating if a key exists + in both cases. + """ + default_headers = [] if default_headers is None else list(default_headers) + override_headers = [] if override_headers is None else list(override_headers) + has_override = set(key.lower() for key, value in override_headers) + default_headers = [ + (key, value) + for key, value in default_headers + if key.lower() not in has_override + ] + return default_headers + override_headers + + +class AsyncHTTPProxy(AsyncConnectionPool): # pragma: nocover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + proxy_auth: Any proxy authentication as a two-tuple of + (username, password). May be either bytes or ascii-only str. + proxy_headers: Any HTTP headers to use for the proxy requests. + For example `{"Proxy-Authorization": "Basic :"}`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + proxy_ssl_context: The same as `ssl_context`, but for a proxy server rather than a remote origin. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + local_address=local_address, + uds=uds, + socket_options=socket_options, + ) + + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if ( + self._proxy_url.scheme == b"http" and proxy_ssl_context is not None + ): # pragma: no cover + raise RuntimeError( + "The `proxy_ssl_context` argument is not allowed for the http scheme" + ) + + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + if proxy_auth is not None: + username = enforce_bytes(proxy_auth[0], name="proxy_auth") + password = enforce_bytes(proxy_auth[1], name="proxy_auth") + userpass = username + b":" + password + authorization = b"Basic " + base64.b64encode(userpass) + self._proxy_headers = [ + (b"Proxy-Authorization", authorization) + ] + self._proxy_headers + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + if origin.scheme == b"http": + return AsyncForwardHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + proxy_ssl_context=self._proxy_ssl_context, + ) + return AsyncTunnelHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + ssl_context=self._ssl_context, + proxy_ssl_context=self._proxy_ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class AsyncForwardHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + keepalive_expiry: float | None = None, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + ) -> None: + self._connection = AsyncHTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._remote_origin = remote_origin + + async def handle_async_request(self, request: Request) -> Response: + headers = merge_headers(self._proxy_headers, request.headers) + url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=bytes(request.url), + ) + proxy_request = Request( + method=request.method, + url=url, + headers=headers, + content=request.stream, + extensions=request.extensions, + ) + return await self._connection.handle_async_request(proxy_request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + await self._connection.aclose() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + +class AsyncTunnelHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + proxy_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: AsyncNetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._connection: AsyncConnectionInterface = AsyncHTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._connect_lock = AsyncLock() + self._connected = False + + async def handle_async_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("connect", None) + + async with self._connect_lock: + if not self._connected: + target = b"%b:%d" % (self._remote_origin.host, self._remote_origin.port) + + connect_url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=target, + ) + connect_headers = merge_headers( + [(b"Host", target), (b"Accept", b"*/*")], self._proxy_headers + ) + connect_request = Request( + method=b"CONNECT", + url=connect_url, + headers=connect_headers, + extensions=request.extensions, + ) + connect_response = await self._connection.handle_async_request( + connect_request + ) + + if connect_response.status < 200 or connect_response.status > 299: + reason_bytes = connect_response.extensions.get("reason_phrase", b"") + reason_str = reason_bytes.decode("ascii", errors="ignore") + msg = "%d %s" % (connect_response.status, reason_str) + await self._connection.aclose() + raise ProxyError(msg) + + stream = connect_response.extensions["network_stream"] + + # Upgrade the stream to SSL + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + + self._connected = True + return await self._connection.handle_async_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + await self._connection.aclose() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/lib/python3.12/site-packages/httpcore/_async/interfaces.py b/lib/python3.12/site-packages/httpcore/_async/interfaces.py new file mode 100644 index 0000000000000000000000000000000000000000..361583bede6b2b84088b38054d5d8116ef9f1597 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_async/interfaces.py @@ -0,0 +1,137 @@ +from __future__ import annotations + +import contextlib +import typing + +from .._models import ( + URL, + Extensions, + HeaderTypes, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, + include_request_headers, +) + + +class AsyncRequestInterface: + async def request( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.AsyncIterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> Response: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = await self.handle_async_request(request) + try: + await response.aread() + finally: + await response.aclose() + return response + + @contextlib.asynccontextmanager + async def stream( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.AsyncIterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> typing.AsyncIterator[Response]: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = await self.handle_async_request(request) + try: + yield response + finally: + await response.aclose() + + async def handle_async_request(self, request: Request) -> Response: + raise NotImplementedError() # pragma: nocover + + +class AsyncConnectionInterface(AsyncRequestInterface): + async def aclose(self) -> None: + raise NotImplementedError() # pragma: nocover + + def info(self) -> str: + raise NotImplementedError() # pragma: nocover + + def can_handle_request(self, origin: Origin) -> bool: + raise NotImplementedError() # pragma: nocover + + def is_available(self) -> bool: + """ + Return `True` if the connection is currently able to accept an + outgoing request. + + An HTTP/1.1 connection will only be available if it is currently idle. + + An HTTP/2 connection will be available so long as the stream ID space is + not yet exhausted, and the connection is not in an error state. + + While the connection is being established we may not yet know if it is going + to result in an HTTP/1.1 or HTTP/2 connection. The connection should be + treated as being available, but might ultimately raise `NewConnectionRequired` + required exceptions if multiple requests are attempted over a connection + that ends up being established as HTTP/1.1. + """ + raise NotImplementedError() # pragma: nocover + + def has_expired(self) -> bool: + """ + Return `True` if the connection is in a state where it should be closed. + + This either means that the connection is idle and it has passed the + expiry time on its keep-alive, or that server has sent an EOF. + """ + raise NotImplementedError() # pragma: nocover + + def is_idle(self) -> bool: + """ + Return `True` if the connection is currently idle. + """ + raise NotImplementedError() # pragma: nocover + + def is_closed(self) -> bool: + """ + Return `True` if the connection has been closed. + + Used when a response is closed to determine if the connection may be + returned to the connection pool or not. + """ + raise NotImplementedError() # pragma: nocover diff --git a/lib/python3.12/site-packages/httpcore/_async/socks_proxy.py b/lib/python3.12/site-packages/httpcore/_async/socks_proxy.py new file mode 100644 index 0000000000000000000000000000000000000000..b363f55a0b071de6c5f377726be82dc2110e373c --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_async/socks_proxy.py @@ -0,0 +1,341 @@ +from __future__ import annotations + +import logging +import ssl + +import socksio + +from .._backends.auto import AutoBackend +from .._backends.base import AsyncNetworkBackend, AsyncNetworkStream +from .._exceptions import ConnectionNotAvailable, ProxyError +from .._models import URL, Origin, Request, Response, enforce_bytes, enforce_url +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore.socks") + + +AUTH_METHODS = { + b"\x00": "NO AUTHENTICATION REQUIRED", + b"\x01": "GSSAPI", + b"\x02": "USERNAME/PASSWORD", + b"\xff": "NO ACCEPTABLE METHODS", +} + +REPLY_CODES = { + b"\x00": "Succeeded", + b"\x01": "General SOCKS server failure", + b"\x02": "Connection not allowed by ruleset", + b"\x03": "Network unreachable", + b"\x04": "Host unreachable", + b"\x05": "Connection refused", + b"\x06": "TTL expired", + b"\x07": "Command not supported", + b"\x08": "Address type not supported", +} + + +async def _init_socks5_connection( + stream: AsyncNetworkStream, + *, + host: bytes, + port: int, + auth: tuple[bytes, bytes] | None = None, +) -> None: + conn = socksio.socks5.SOCKS5Connection() + + # Auth method request + auth_method = ( + socksio.socks5.SOCKS5AuthMethod.NO_AUTH_REQUIRED + if auth is None + else socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD + ) + conn.send(socksio.socks5.SOCKS5AuthMethodsRequest([auth_method])) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes) + + # Auth method response + incoming_bytes = await stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5AuthReply) + if response.method != auth_method: + requested = AUTH_METHODS.get(auth_method, "UNKNOWN") + responded = AUTH_METHODS.get(response.method, "UNKNOWN") + raise ProxyError( + f"Requested {requested} from proxy server, but got {responded}." + ) + + if response.method == socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD: + # Username/password request + assert auth is not None + username, password = auth + conn.send(socksio.socks5.SOCKS5UsernamePasswordRequest(username, password)) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes) + + # Username/password response + incoming_bytes = await stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5UsernamePasswordReply) + if not response.success: + raise ProxyError("Invalid username/password") + + # Connect request + conn.send( + socksio.socks5.SOCKS5CommandRequest.from_address( + socksio.socks5.SOCKS5Command.CONNECT, (host, port) + ) + ) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes) + + # Connect response + incoming_bytes = await stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5Reply) + if response.reply_code != socksio.socks5.SOCKS5ReplyCode.SUCCEEDED: + reply_code = REPLY_CODES.get(response.reply_code, "UNKOWN") + raise ProxyError(f"Proxy Server could not connect: {reply_code}.") + + +class AsyncSOCKSProxy(AsyncConnectionPool): # pragma: nocover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + network_backend: AsyncNetworkBackend | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + ) + self._ssl_context = ssl_context + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if proxy_auth is not None: + username, password = proxy_auth + username_bytes = enforce_bytes(username, name="proxy_auth") + password_bytes = enforce_bytes(password, name="proxy_auth") + self._proxy_auth: tuple[bytes, bytes] | None = ( + username_bytes, + password_bytes, + ) + else: + self._proxy_auth = None + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + return AsyncSocks5Connection( + proxy_origin=self._proxy_url.origin, + remote_origin=origin, + proxy_auth=self._proxy_auth, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class AsyncSocks5Connection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_auth: tuple[bytes, bytes] | None = None, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: AsyncNetworkBackend | None = None, + ) -> None: + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._proxy_auth = proxy_auth + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + + self._network_backend: AsyncNetworkBackend = ( + AutoBackend() if network_backend is None else network_backend + ) + self._connect_lock = AsyncLock() + self._connection: AsyncConnectionInterface | None = None + self._connect_failed = False + + async def handle_async_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + async with self._connect_lock: + if self._connection is None: + try: + # Connect to the proxy + kwargs = { + "host": self._proxy_origin.host.decode("ascii"), + "port": self._proxy_origin.port, + "timeout": timeout, + } + async with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = await self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + + # Connect to the remote host using socks5 + kwargs = { + "stream": stream, + "host": self._remote_origin.host.decode("ascii"), + "port": self._remote_origin.port, + "auth": self._proxy_auth, + } + async with Trace( + "setup_socks5_connection", logger, request, kwargs + ) as trace: + await _init_socks5_connection(**kwargs) + trace.return_value = stream + + # Upgrade the stream to SSL + if self._remote_origin.scheme == b"https": + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ( + ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ) + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or ( + self._http2 and not self._http1 + ): # pragma: nocover + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except Exception as exc: + self._connect_failed = True + raise exc + elif not self._connection.is_available(): # pragma: nocover + raise ConnectionNotAvailable() + + return await self._connection.handle_async_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + if self._connection is not None: + await self._connection.aclose() + + def is_available(self) -> bool: + if self._connection is None: # pragma: nocover + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._remote_origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: # pragma: nocover + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/lib/python3.12/site-packages/httpcore/_backends/__init__.py b/lib/python3.12/site-packages/httpcore/_backends/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/httpcore/_backends/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..281559c6e8da78758f31bc53db5438b2b2bc7b9b Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_backends/__pycache__/anyio.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/anyio.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a13c6f891643ca4ad93e810e02ab78802b15ea1 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/anyio.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_backends/__pycache__/auto.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/auto.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7425577b986cb1c1e58c1860c9f68da32304443 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/auto.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_backends/__pycache__/base.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..147e1e9cc5d1fd4b3c7eadc0c4457c46c46e540d Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/base.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_backends/__pycache__/mock.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/mock.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf9ee42e44f92fe137d4576d4a6c6bd73ddc1210 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/mock.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_backends/__pycache__/sync.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/sync.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd06d7a1acd877109a07601a047ec02486c15306 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/sync.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_backends/__pycache__/trio.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/trio.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d83bd98f4988331ddb929ff8f2e166f3099fd09 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_backends/__pycache__/trio.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_backends/anyio.py b/lib/python3.12/site-packages/httpcore/_backends/anyio.py new file mode 100644 index 0000000000000000000000000000000000000000..a140095e1b8de022f321a41c0125e0e5febc0749 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_backends/anyio.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +import ssl +import typing + +import anyio + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .._utils import is_socket_readable +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class AnyIOStream(AsyncNetworkStream): + def __init__(self, stream: anyio.abc.ByteStream) -> None: + self._stream = stream + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + exc_map = { + TimeoutError: ReadTimeout, + anyio.BrokenResourceError: ReadError, + anyio.ClosedResourceError: ReadError, + anyio.EndOfStream: ReadError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + try: + return await self._stream.receive(max_bytes=max_bytes) + except anyio.EndOfStream: # pragma: nocover + return b"" + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + if not buffer: + return + + exc_map = { + TimeoutError: WriteTimeout, + anyio.BrokenResourceError: WriteError, + anyio.ClosedResourceError: WriteError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + await self._stream.send(item=buffer) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + exc_map = { + TimeoutError: ConnectTimeout, + anyio.BrokenResourceError: ConnectError, + anyio.EndOfStream: ConnectError, + ssl.SSLError: ConnectError, + } + with map_exceptions(exc_map): + try: + with anyio.fail_after(timeout): + ssl_stream = await anyio.streams.tls.TLSStream.wrap( + self._stream, + ssl_context=ssl_context, + hostname=server_hostname, + standard_compatible=False, + server_side=False, + ) + except Exception as exc: # pragma: nocover + await self.aclose() + raise exc + return AnyIOStream(ssl_stream) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object": + return self._stream.extra(anyio.streams.tls.TLSAttribute.ssl_object, None) + if info == "client_addr": + return self._stream.extra(anyio.abc.SocketAttribute.local_address, None) + if info == "server_addr": + return self._stream.extra(anyio.abc.SocketAttribute.remote_address, None) + if info == "socket": + return self._stream.extra(anyio.abc.SocketAttribute.raw_socket, None) + if info == "is_readable": + sock = self._stream.extra(anyio.abc.SocketAttribute.raw_socket, None) + return is_socket_readable(sock) + return None + + +class AnyIOBackend(AsyncNetworkBackend): + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: nocover + if socket_options is None: + socket_options = [] + exc_map = { + TimeoutError: ConnectTimeout, + OSError: ConnectError, + anyio.BrokenResourceError: ConnectError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + stream: anyio.abc.ByteStream = await anyio.connect_tcp( + remote_host=host, + remote_port=port, + local_host=local_address, + ) + # By default TCP sockets opened in `asyncio` include TCP_NODELAY. + for option in socket_options: + stream._raw_socket.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return AnyIOStream(stream) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: nocover + if socket_options is None: + socket_options = [] + exc_map = { + TimeoutError: ConnectTimeout, + OSError: ConnectError, + anyio.BrokenResourceError: ConnectError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + stream: anyio.abc.ByteStream = await anyio.connect_unix(path) + for option in socket_options: + stream._raw_socket.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return AnyIOStream(stream) + + async def sleep(self, seconds: float) -> None: + await anyio.sleep(seconds) # pragma: nocover diff --git a/lib/python3.12/site-packages/httpcore/_backends/auto.py b/lib/python3.12/site-packages/httpcore/_backends/auto.py new file mode 100644 index 0000000000000000000000000000000000000000..49f0e698c97ad5623f376d8182675352e21c2c3c --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_backends/auto.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import typing + +from .._synchronization import current_async_library +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class AutoBackend(AsyncNetworkBackend): + async def _init_backend(self) -> None: + if not (hasattr(self, "_backend")): + backend = current_async_library() + if backend == "trio": + from .trio import TrioBackend + + self._backend: AsyncNetworkBackend = TrioBackend() + else: + from .anyio import AnyIOBackend + + self._backend = AnyIOBackend() + + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + await self._init_backend() + return await self._backend.connect_tcp( + host, + port, + timeout=timeout, + local_address=local_address, + socket_options=socket_options, + ) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: nocover + await self._init_backend() + return await self._backend.connect_unix_socket( + path, timeout=timeout, socket_options=socket_options + ) + + async def sleep(self, seconds: float) -> None: # pragma: nocover + await self._init_backend() + return await self._backend.sleep(seconds) diff --git a/lib/python3.12/site-packages/httpcore/_backends/base.py b/lib/python3.12/site-packages/httpcore/_backends/base.py new file mode 100644 index 0000000000000000000000000000000000000000..cf55c8b10eb543872550be863206fe2f760d0d8d --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_backends/base.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +import ssl +import time +import typing + +SOCKET_OPTION = typing.Union[ + typing.Tuple[int, int, int], + typing.Tuple[int, int, typing.Union[bytes, bytearray]], + typing.Tuple[int, int, None, int], +] + + +class NetworkStream: + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + raise NotImplementedError() # pragma: nocover + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + raise NotImplementedError() # pragma: nocover + + def close(self) -> None: + raise NotImplementedError() # pragma: nocover + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: nocover + + def get_extra_info(self, info: str) -> typing.Any: + return None # pragma: nocover + + +class NetworkBackend: + def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: nocover + + def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: nocover + + def sleep(self, seconds: float) -> None: + time.sleep(seconds) # pragma: nocover + + +class AsyncNetworkStream: + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + raise NotImplementedError() # pragma: nocover + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + raise NotImplementedError() # pragma: nocover + + async def aclose(self) -> None: + raise NotImplementedError() # pragma: nocover + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: nocover + + def get_extra_info(self, info: str) -> typing.Any: + return None # pragma: nocover + + +class AsyncNetworkBackend: + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: nocover + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: nocover + + async def sleep(self, seconds: float) -> None: + raise NotImplementedError() # pragma: nocover diff --git a/lib/python3.12/site-packages/httpcore/_backends/mock.py b/lib/python3.12/site-packages/httpcore/_backends/mock.py new file mode 100644 index 0000000000000000000000000000000000000000..9b6edca03d4d4b34f355fd53e49d4b4c699c972c --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_backends/mock.py @@ -0,0 +1,143 @@ +from __future__ import annotations + +import ssl +import typing + +from .._exceptions import ReadError +from .base import ( + SOCKET_OPTION, + AsyncNetworkBackend, + AsyncNetworkStream, + NetworkBackend, + NetworkStream, +) + + +class MockSSLObject: + def __init__(self, http2: bool): + self._http2 = http2 + + def selected_alpn_protocol(self) -> str: + return "h2" if self._http2 else "http/1.1" + + +class MockStream(NetworkStream): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + self._closed = False + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._closed: + raise ReadError("Connection closed") + if not self._buffer: + return b"" + return self._buffer.pop(0) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + pass + + def close(self) -> None: + self._closed = True + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + return self + + def get_extra_info(self, info: str) -> typing.Any: + return MockSSLObject(http2=self._http2) if info == "ssl_object" else None + + def __repr__(self) -> str: + return "" + + +class MockBackend(NetworkBackend): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + + def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + return MockStream(list(self._buffer), http2=self._http2) + + def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + return MockStream(list(self._buffer), http2=self._http2) + + def sleep(self, seconds: float) -> None: + pass + + +class AsyncMockStream(AsyncNetworkStream): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + self._closed = False + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._closed: + raise ReadError("Connection closed") + if not self._buffer: + return b"" + return self._buffer.pop(0) + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + pass + + async def aclose(self) -> None: + self._closed = True + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + return self + + def get_extra_info(self, info: str) -> typing.Any: + return MockSSLObject(http2=self._http2) if info == "ssl_object" else None + + def __repr__(self) -> str: + return "" + + +class AsyncMockBackend(AsyncNetworkBackend): + def __init__(self, buffer: list[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + return AsyncMockStream(list(self._buffer), http2=self._http2) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + return AsyncMockStream(list(self._buffer), http2=self._http2) + + async def sleep(self, seconds: float) -> None: + pass diff --git a/lib/python3.12/site-packages/httpcore/_backends/sync.py b/lib/python3.12/site-packages/httpcore/_backends/sync.py new file mode 100644 index 0000000000000000000000000000000000000000..4018a09c6fb1e0ef1b03ab8d84b13ebef4031f7c --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_backends/sync.py @@ -0,0 +1,241 @@ +from __future__ import annotations + +import functools +import socket +import ssl +import sys +import typing + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ExceptionMapping, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .._utils import is_socket_readable +from .base import SOCKET_OPTION, NetworkBackend, NetworkStream + + +class TLSinTLSStream(NetworkStream): # pragma: no cover + """ + Because the standard `SSLContext.wrap_socket` method does + not work for `SSLSocket` objects, we need this class + to implement TLS stream using an underlying `SSLObject` + instance in order to support TLS on top of TLS. + """ + + # Defined in RFC 8449 + TLS_RECORD_SIZE = 16384 + + def __init__( + self, + sock: socket.socket, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ): + self._sock = sock + self._incoming = ssl.MemoryBIO() + self._outgoing = ssl.MemoryBIO() + + self.ssl_obj = ssl_context.wrap_bio( + incoming=self._incoming, + outgoing=self._outgoing, + server_hostname=server_hostname, + ) + + self._sock.settimeout(timeout) + self._perform_io(self.ssl_obj.do_handshake) + + def _perform_io( + self, + func: typing.Callable[..., typing.Any], + ) -> typing.Any: + ret = None + + while True: + errno = None + try: + ret = func() + except (ssl.SSLWantReadError, ssl.SSLWantWriteError) as e: + errno = e.errno + + self._sock.sendall(self._outgoing.read()) + + if errno == ssl.SSL_ERROR_WANT_READ: + buf = self._sock.recv(self.TLS_RECORD_SIZE) + + if buf: + self._incoming.write(buf) + else: + self._incoming.write_eof() + if errno is None: + return ret + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + exc_map: ExceptionMapping = {socket.timeout: ReadTimeout, OSError: ReadError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + return typing.cast( + bytes, self._perform_io(functools.partial(self.ssl_obj.read, max_bytes)) + ) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + exc_map: ExceptionMapping = {socket.timeout: WriteTimeout, OSError: WriteError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + while buffer: + nsent = self._perform_io(functools.partial(self.ssl_obj.write, buffer)) + buffer = buffer[nsent:] + + def close(self) -> None: + self._sock.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + raise NotImplementedError() + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object": + return self.ssl_obj + if info == "client_addr": + return self._sock.getsockname() + if info == "server_addr": + return self._sock.getpeername() + if info == "socket": + return self._sock + if info == "is_readable": + return is_socket_readable(self._sock) + return None + + +class SyncStream(NetworkStream): + def __init__(self, sock: socket.socket) -> None: + self._sock = sock + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + exc_map: ExceptionMapping = {socket.timeout: ReadTimeout, OSError: ReadError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + return self._sock.recv(max_bytes) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + if not buffer: + return + + exc_map: ExceptionMapping = {socket.timeout: WriteTimeout, OSError: WriteError} + with map_exceptions(exc_map): + while buffer: + self._sock.settimeout(timeout) + n = self._sock.send(buffer) + buffer = buffer[n:] + + def close(self) -> None: + self._sock.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + with map_exceptions(exc_map): + try: + if isinstance(self._sock, ssl.SSLSocket): # pragma: no cover + # If the underlying socket has already been upgraded + # to the TLS layer (i.e. is an instance of SSLSocket), + # we need some additional smarts to support TLS-in-TLS. + return TLSinTLSStream( + self._sock, ssl_context, server_hostname, timeout + ) + else: + self._sock.settimeout(timeout) + sock = ssl_context.wrap_socket( + self._sock, server_hostname=server_hostname + ) + except Exception as exc: # pragma: nocover + self.close() + raise exc + return SyncStream(sock) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object" and isinstance(self._sock, ssl.SSLSocket): + return self._sock._sslobj # type: ignore + if info == "client_addr": + return self._sock.getsockname() + if info == "server_addr": + return self._sock.getpeername() + if info == "socket": + return self._sock + if info == "is_readable": + return is_socket_readable(self._sock) + return None + + +class SyncBackend(NetworkBackend): + def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: + # Note that we automatically include `TCP_NODELAY` + # in addition to any other custom socket options. + if socket_options is None: + socket_options = [] # pragma: no cover + address = (host, port) + source_address = None if local_address is None else (local_address, 0) + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + + with map_exceptions(exc_map): + sock = socket.create_connection( + address, + timeout, + source_address=source_address, + ) + for option in socket_options: + sock.setsockopt(*option) # pragma: no cover + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + return SyncStream(sock) + + def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> NetworkStream: # pragma: nocover + if sys.platform == "win32": + raise RuntimeError( + "Attempted to connect to a UNIX socket on a Windows system." + ) + if socket_options is None: + socket_options = [] + + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + with map_exceptions(exc_map): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + for option in socket_options: + sock.setsockopt(*option) + sock.settimeout(timeout) + sock.connect(path) + return SyncStream(sock) diff --git a/lib/python3.12/site-packages/httpcore/_backends/trio.py b/lib/python3.12/site-packages/httpcore/_backends/trio.py new file mode 100644 index 0000000000000000000000000000000000000000..6f53f5f2a025e01e9949e2530bd9ca6928859251 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_backends/trio.py @@ -0,0 +1,159 @@ +from __future__ import annotations + +import ssl +import typing + +import trio + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ExceptionMapping, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class TrioStream(AsyncNetworkStream): + def __init__(self, stream: trio.abc.Stream) -> None: + self._stream = stream + + async def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ReadTimeout, + trio.BrokenResourceError: ReadError, + trio.ClosedResourceError: ReadError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + data: bytes = await self._stream.receive_some(max_bytes=max_bytes) + return data + + async def write(self, buffer: bytes, timeout: float | None = None) -> None: + if not buffer: + return + + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: WriteTimeout, + trio.BrokenResourceError: WriteError, + trio.ClosedResourceError: WriteError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + await self._stream.send_all(data=buffer) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> AsyncNetworkStream: + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + } + ssl_stream = trio.SSLStream( + self._stream, + ssl_context=ssl_context, + server_hostname=server_hostname, + https_compatible=True, + server_side=False, + ) + with map_exceptions(exc_map): + try: + with trio.fail_after(timeout_or_inf): + await ssl_stream.do_handshake() + except Exception as exc: # pragma: nocover + await self.aclose() + raise exc + return TrioStream(ssl_stream) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object" and isinstance(self._stream, trio.SSLStream): + # Type checkers cannot see `_ssl_object` attribute because trio._ssl.SSLStream uses __getattr__/__setattr__. + # Tracked at https://github.com/python-trio/trio/issues/542 + return self._stream._ssl_object # type: ignore[attr-defined] + if info == "client_addr": + return self._get_socket_stream().socket.getsockname() + if info == "server_addr": + return self._get_socket_stream().socket.getpeername() + if info == "socket": + stream = self._stream + while isinstance(stream, trio.SSLStream): + stream = stream.transport_stream + assert isinstance(stream, trio.SocketStream) + return stream.socket + if info == "is_readable": + socket = self.get_extra_info("socket") + return socket.is_readable() + return None + + def _get_socket_stream(self) -> trio.SocketStream: + stream = self._stream + while isinstance(stream, trio.SSLStream): + stream = stream.transport_stream + assert isinstance(stream, trio.SocketStream) + return stream + + +class TrioBackend(AsyncNetworkBackend): + async def connect_tcp( + self, + host: str, + port: int, + timeout: float | None = None, + local_address: str | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: + # By default for TCP sockets, trio enables TCP_NODELAY. + # https://trio.readthedocs.io/en/stable/reference-io.html#trio.SocketStream + if socket_options is None: + socket_options = [] # pragma: no cover + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + OSError: ConnectError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + stream: trio.abc.Stream = await trio.open_tcp_stream( + host=host, port=port, local_address=local_address + ) + for option in socket_options: + stream.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return TrioStream(stream) + + async def connect_unix_socket( + self, + path: str, + timeout: float | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> AsyncNetworkStream: # pragma: nocover + if socket_options is None: + socket_options = [] + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + OSError: ConnectError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + stream: trio.abc.Stream = await trio.open_unix_socket(path) + for option in socket_options: + stream.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return TrioStream(stream) + + async def sleep(self, seconds: float) -> None: + await trio.sleep(seconds) # pragma: nocover diff --git a/lib/python3.12/site-packages/httpcore/_exceptions.py b/lib/python3.12/site-packages/httpcore/_exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..bc28d44f55bdc4b872951a74780469a3999d9ab4 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_exceptions.py @@ -0,0 +1,81 @@ +import contextlib +import typing + +ExceptionMapping = typing.Mapping[typing.Type[Exception], typing.Type[Exception]] + + +@contextlib.contextmanager +def map_exceptions(map: ExceptionMapping) -> typing.Iterator[None]: + try: + yield + except Exception as exc: # noqa: PIE786 + for from_exc, to_exc in map.items(): + if isinstance(exc, from_exc): + raise to_exc(exc) from exc + raise # pragma: nocover + + +class ConnectionNotAvailable(Exception): + pass + + +class ProxyError(Exception): + pass + + +class UnsupportedProtocol(Exception): + pass + + +class ProtocolError(Exception): + pass + + +class RemoteProtocolError(ProtocolError): + pass + + +class LocalProtocolError(ProtocolError): + pass + + +# Timeout errors + + +class TimeoutException(Exception): + pass + + +class PoolTimeout(TimeoutException): + pass + + +class ConnectTimeout(TimeoutException): + pass + + +class ReadTimeout(TimeoutException): + pass + + +class WriteTimeout(TimeoutException): + pass + + +# Network errors + + +class NetworkError(Exception): + pass + + +class ConnectError(NetworkError): + pass + + +class ReadError(NetworkError): + pass + + +class WriteError(NetworkError): + pass diff --git a/lib/python3.12/site-packages/httpcore/_models.py b/lib/python3.12/site-packages/httpcore/_models.py new file mode 100644 index 0000000000000000000000000000000000000000..8a65f13347d6621289a166d08123cbc8e1ad0157 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_models.py @@ -0,0 +1,516 @@ +from __future__ import annotations + +import base64 +import ssl +import typing +import urllib.parse + +# Functions for typechecking... + + +ByteOrStr = typing.Union[bytes, str] +HeadersAsSequence = typing.Sequence[typing.Tuple[ByteOrStr, ByteOrStr]] +HeadersAsMapping = typing.Mapping[ByteOrStr, ByteOrStr] +HeaderTypes = typing.Union[HeadersAsSequence, HeadersAsMapping, None] + +Extensions = typing.MutableMapping[str, typing.Any] + + +def enforce_bytes(value: bytes | str, *, name: str) -> bytes: + """ + Any arguments that are ultimately represented as bytes can be specified + either as bytes or as strings. + + However we enforce that any string arguments must only contain characters in + the plain ASCII range. chr(0)...chr(127). If you need to use characters + outside that range then be precise, and use a byte-wise argument. + """ + if isinstance(value, str): + try: + return value.encode("ascii") + except UnicodeEncodeError: + raise TypeError(f"{name} strings may not include unicode characters.") + elif isinstance(value, bytes): + return value + + seen_type = type(value).__name__ + raise TypeError(f"{name} must be bytes or str, but got {seen_type}.") + + +def enforce_url(value: URL | bytes | str, *, name: str) -> URL: + """ + Type check for URL parameters. + """ + if isinstance(value, (bytes, str)): + return URL(value) + elif isinstance(value, URL): + return value + + seen_type = type(value).__name__ + raise TypeError(f"{name} must be a URL, bytes, or str, but got {seen_type}.") + + +def enforce_headers( + value: HeadersAsMapping | HeadersAsSequence | None = None, *, name: str +) -> list[tuple[bytes, bytes]]: + """ + Convienence function that ensure all items in request or response headers + are either bytes or strings in the plain ASCII range. + """ + if value is None: + return [] + elif isinstance(value, typing.Mapping): + return [ + ( + enforce_bytes(k, name="header name"), + enforce_bytes(v, name="header value"), + ) + for k, v in value.items() + ] + elif isinstance(value, typing.Sequence): + return [ + ( + enforce_bytes(k, name="header name"), + enforce_bytes(v, name="header value"), + ) + for k, v in value + ] + + seen_type = type(value).__name__ + raise TypeError( + f"{name} must be a mapping or sequence of two-tuples, but got {seen_type}." + ) + + +def enforce_stream( + value: bytes | typing.Iterable[bytes] | typing.AsyncIterable[bytes] | None, + *, + name: str, +) -> typing.Iterable[bytes] | typing.AsyncIterable[bytes]: + if value is None: + return ByteStream(b"") + elif isinstance(value, bytes): + return ByteStream(value) + return value + + +# * https://tools.ietf.org/html/rfc3986#section-3.2.3 +# * https://url.spec.whatwg.org/#url-miscellaneous +# * https://url.spec.whatwg.org/#scheme-state +DEFAULT_PORTS = { + b"ftp": 21, + b"http": 80, + b"https": 443, + b"ws": 80, + b"wss": 443, +} + + +def include_request_headers( + headers: list[tuple[bytes, bytes]], + *, + url: "URL", + content: None | bytes | typing.Iterable[bytes] | typing.AsyncIterable[bytes], +) -> list[tuple[bytes, bytes]]: + headers_set = set(k.lower() for k, v in headers) + + if b"host" not in headers_set: + default_port = DEFAULT_PORTS.get(url.scheme) + if url.port is None or url.port == default_port: + header_value = url.host + else: + header_value = b"%b:%d" % (url.host, url.port) + headers = [(b"Host", header_value)] + headers + + if ( + content is not None + and b"content-length" not in headers_set + and b"transfer-encoding" not in headers_set + ): + if isinstance(content, bytes): + content_length = str(len(content)).encode("ascii") + headers += [(b"Content-Length", content_length)] + else: + headers += [(b"Transfer-Encoding", b"chunked")] # pragma: nocover + + return headers + + +# Interfaces for byte streams... + + +class ByteStream: + """ + A container for non-streaming content, and that supports both sync and async + stream iteration. + """ + + def __init__(self, content: bytes) -> None: + self._content = content + + def __iter__(self) -> typing.Iterator[bytes]: + yield self._content + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + yield self._content + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{len(self._content)} bytes]>" + + +class Origin: + def __init__(self, scheme: bytes, host: bytes, port: int) -> None: + self.scheme = scheme + self.host = host + self.port = port + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, Origin) + and self.scheme == other.scheme + and self.host == other.host + and self.port == other.port + ) + + def __str__(self) -> str: + scheme = self.scheme.decode("ascii") + host = self.host.decode("ascii") + port = str(self.port) + return f"{scheme}://{host}:{port}" + + +class URL: + """ + Represents the URL against which an HTTP request may be made. + + The URL may either be specified as a plain string, for convienence: + + ```python + url = httpcore.URL("https://www.example.com/") + ``` + + Or be constructed with explicitily pre-parsed components: + + ```python + url = httpcore.URL(scheme=b'https', host=b'www.example.com', port=None, target=b'/') + ``` + + Using this second more explicit style allows integrations that are using + `httpcore` to pass through URLs that have already been parsed in order to use + libraries such as `rfc-3986` rather than relying on the stdlib. It also ensures + that URL parsing is treated identically at both the networking level and at any + higher layers of abstraction. + + The four components are important here, as they allow the URL to be precisely + specified in a pre-parsed format. They also allow certain types of request to + be created that could not otherwise be expressed. + + For example, an HTTP request to `http://www.example.com/` forwarded via a proxy + at `http://localhost:8080`... + + ```python + # Constructs an HTTP request with a complete URL as the target: + # GET https://www.example.com/ HTTP/1.1 + url = httpcore.URL( + scheme=b'http', + host=b'localhost', + port=8080, + target=b'https://www.example.com/' + ) + request = httpcore.Request( + method="GET", + url=url + ) + ``` + + Another example is constructing an `OPTIONS *` request... + + ```python + # Constructs an 'OPTIONS *' HTTP request: + # OPTIONS * HTTP/1.1 + url = httpcore.URL(scheme=b'https', host=b'www.example.com', target=b'*') + request = httpcore.Request(method="OPTIONS", url=url) + ``` + + This kind of request is not possible to formulate with a URL string, + because the `/` delimiter is always used to demark the target from the + host/port portion of the URL. + + For convenience, string-like arguments may be specified either as strings or + as bytes. However, once a request is being issue over-the-wire, the URL + components are always ultimately required to be a bytewise representation. + + In order to avoid any ambiguity over character encodings, when strings are used + as arguments, they must be strictly limited to the ASCII range `chr(0)`-`chr(127)`. + If you require a bytewise representation that is outside this range you must + handle the character encoding directly, and pass a bytes instance. + """ + + def __init__( + self, + url: bytes | str = "", + *, + scheme: bytes | str = b"", + host: bytes | str = b"", + port: int | None = None, + target: bytes | str = b"", + ) -> None: + """ + Parameters: + url: The complete URL as a string or bytes. + scheme: The URL scheme as a string or bytes. + Typically either `"http"` or `"https"`. + host: The URL host as a string or bytes. Such as `"www.example.com"`. + port: The port to connect to. Either an integer or `None`. + target: The target of the HTTP request. Such as `"/items?search=red"`. + """ + if url: + parsed = urllib.parse.urlparse(enforce_bytes(url, name="url")) + self.scheme = parsed.scheme + self.host = parsed.hostname or b"" + self.port = parsed.port + self.target = (parsed.path or b"/") + ( + b"?" + parsed.query if parsed.query else b"" + ) + else: + self.scheme = enforce_bytes(scheme, name="scheme") + self.host = enforce_bytes(host, name="host") + self.port = port + self.target = enforce_bytes(target, name="target") + + @property + def origin(self) -> Origin: + default_port = { + b"http": 80, + b"https": 443, + b"ws": 80, + b"wss": 443, + b"socks5": 1080, + b"socks5h": 1080, + }[self.scheme] + return Origin( + scheme=self.scheme, host=self.host, port=self.port or default_port + ) + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, URL) + and other.scheme == self.scheme + and other.host == self.host + and other.port == self.port + and other.target == self.target + ) + + def __bytes__(self) -> bytes: + if self.port is None: + return b"%b://%b%b" % (self.scheme, self.host, self.target) + return b"%b://%b:%d%b" % (self.scheme, self.host, self.port, self.target) + + def __repr__(self) -> str: + return ( + f"{self.__class__.__name__}(scheme={self.scheme!r}, " + f"host={self.host!r}, port={self.port!r}, target={self.target!r})" + ) + + +class Request: + """ + An HTTP request. + """ + + def __init__( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes + | typing.Iterable[bytes] + | typing.AsyncIterable[bytes] + | None = None, + extensions: Extensions | None = None, + ) -> None: + """ + Parameters: + method: The HTTP request method, either as a string or bytes. + For example: `GET`. + url: The request URL, either as a `URL` instance, or as a string or bytes. + For example: `"https://www.example.com".` + headers: The HTTP request headers. + content: The content of the request body. + extensions: A dictionary of optional extra information included on + the request. Possible keys include `"timeout"`, and `"trace"`. + """ + self.method: bytes = enforce_bytes(method, name="method") + self.url: URL = enforce_url(url, name="url") + self.headers: list[tuple[bytes, bytes]] = enforce_headers( + headers, name="headers" + ) + self.stream: typing.Iterable[bytes] | typing.AsyncIterable[bytes] = ( + enforce_stream(content, name="content") + ) + self.extensions = {} if extensions is None else extensions + + if "target" in self.extensions: + self.url = URL( + scheme=self.url.scheme, + host=self.url.host, + port=self.url.port, + target=self.extensions["target"], + ) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.method!r}]>" + + +class Response: + """ + An HTTP response. + """ + + def __init__( + self, + status: int, + *, + headers: HeaderTypes = None, + content: bytes + | typing.Iterable[bytes] + | typing.AsyncIterable[bytes] + | None = None, + extensions: Extensions | None = None, + ) -> None: + """ + Parameters: + status: The HTTP status code of the response. For example `200`. + headers: The HTTP response headers. + content: The content of the response body. + extensions: A dictionary of optional extra information included on + the responseself.Possible keys include `"http_version"`, + `"reason_phrase"`, and `"network_stream"`. + """ + self.status: int = status + self.headers: list[tuple[bytes, bytes]] = enforce_headers( + headers, name="headers" + ) + self.stream: typing.Iterable[bytes] | typing.AsyncIterable[bytes] = ( + enforce_stream(content, name="content") + ) + self.extensions = {} if extensions is None else extensions + + self._stream_consumed = False + + @property + def content(self) -> bytes: + if not hasattr(self, "_content"): + if isinstance(self.stream, typing.Iterable): + raise RuntimeError( + "Attempted to access 'response.content' on a streaming response. " + "Call 'response.read()' first." + ) + else: + raise RuntimeError( + "Attempted to access 'response.content' on a streaming response. " + "Call 'await response.aread()' first." + ) + return self._content + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.status}]>" + + # Sync interface... + + def read(self) -> bytes: + if not isinstance(self.stream, typing.Iterable): # pragma: nocover + raise RuntimeError( + "Attempted to read an asynchronous response using 'response.read()'. " + "You should use 'await response.aread()' instead." + ) + if not hasattr(self, "_content"): + self._content = b"".join([part for part in self.iter_stream()]) + return self._content + + def iter_stream(self) -> typing.Iterator[bytes]: + if not isinstance(self.stream, typing.Iterable): # pragma: nocover + raise RuntimeError( + "Attempted to stream an asynchronous response using 'for ... in " + "response.iter_stream()'. " + "You should use 'async for ... in response.aiter_stream()' instead." + ) + if self._stream_consumed: + raise RuntimeError( + "Attempted to call 'for ... in response.iter_stream()' more than once." + ) + self._stream_consumed = True + for chunk in self.stream: + yield chunk + + def close(self) -> None: + if not isinstance(self.stream, typing.Iterable): # pragma: nocover + raise RuntimeError( + "Attempted to close an asynchronous response using 'response.close()'. " + "You should use 'await response.aclose()' instead." + ) + if hasattr(self.stream, "close"): + self.stream.close() + + # Async interface... + + async def aread(self) -> bytes: + if not isinstance(self.stream, typing.AsyncIterable): # pragma: nocover + raise RuntimeError( + "Attempted to read an synchronous response using " + "'await response.aread()'. " + "You should use 'response.read()' instead." + ) + if not hasattr(self, "_content"): + self._content = b"".join([part async for part in self.aiter_stream()]) + return self._content + + async def aiter_stream(self) -> typing.AsyncIterator[bytes]: + if not isinstance(self.stream, typing.AsyncIterable): # pragma: nocover + raise RuntimeError( + "Attempted to stream an synchronous response using 'async for ... in " + "response.aiter_stream()'. " + "You should use 'for ... in response.iter_stream()' instead." + ) + if self._stream_consumed: + raise RuntimeError( + "Attempted to call 'async for ... in response.aiter_stream()' " + "more than once." + ) + self._stream_consumed = True + async for chunk in self.stream: + yield chunk + + async def aclose(self) -> None: + if not isinstance(self.stream, typing.AsyncIterable): # pragma: nocover + raise RuntimeError( + "Attempted to close a synchronous response using " + "'await response.aclose()'. " + "You should use 'response.close()' instead." + ) + if hasattr(self.stream, "aclose"): + await self.stream.aclose() + + +class Proxy: + def __init__( + self, + url: URL | bytes | str, + auth: tuple[bytes | str, bytes | str] | None = None, + headers: HeadersAsMapping | HeadersAsSequence | None = None, + ssl_context: ssl.SSLContext | None = None, + ): + self.url = enforce_url(url, name="url") + self.headers = enforce_headers(headers, name="headers") + self.ssl_context = ssl_context + + if auth is not None: + username = enforce_bytes(auth[0], name="auth") + password = enforce_bytes(auth[1], name="auth") + userpass = username + b":" + password + authorization = b"Basic " + base64.b64encode(userpass) + self.auth: tuple[bytes, bytes] | None = (username, password) + self.headers = [(b"Proxy-Authorization", authorization)] + self.headers + else: + self.auth = None diff --git a/lib/python3.12/site-packages/httpcore/_ssl.py b/lib/python3.12/site-packages/httpcore/_ssl.py new file mode 100644 index 0000000000000000000000000000000000000000..c99c5a67945b8a3a3544d481e979c791ab45fe23 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_ssl.py @@ -0,0 +1,9 @@ +import ssl + +import certifi + + +def default_ssl_context() -> ssl.SSLContext: + context = ssl.create_default_context() + context.load_verify_locations(certifi.where()) + return context diff --git a/lib/python3.12/site-packages/httpcore/_sync/__init__.py b/lib/python3.12/site-packages/httpcore/_sync/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b476d76d9a7ff45de8d18ec22d33d6af2982f92e --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_sync/__init__.py @@ -0,0 +1,39 @@ +from .connection import HTTPConnection +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .http_proxy import HTTPProxy +from .interfaces import ConnectionInterface + +try: + from .http2 import HTTP2Connection +except ImportError: # pragma: nocover + + class HTTP2Connection: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use http2 support, but the `h2` package is not " + "installed. Use 'pip install httpcore[http2]'." + ) + + +try: + from .socks_proxy import SOCKSProxy +except ImportError: # pragma: nocover + + class SOCKSProxy: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use SOCKS support, but the `socksio` package is not " + "installed. Use 'pip install httpcore[socks]'." + ) + + +__all__ = [ + "HTTPConnection", + "ConnectionPool", + "HTTPProxy", + "HTTP11Connection", + "HTTP2Connection", + "ConnectionInterface", + "SOCKSProxy", +] diff --git a/lib/python3.12/site-packages/httpcore/_sync/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59dea0cef0c5fb28a2b0452469e317dd274eb58d Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05f8126245f371a80a975042d6cf105ed2fb5573 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d141d6f3ce59f09b9c317bb888b6d2e04d1a2837 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http11.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http11.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f6476b631281cec51b7ee5779c7288dcaf4b383 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http11.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http2.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http2.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7df1e3043824ab5d706475ae16c3cb360ac12ea Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http2.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1803f04887cfea6333beebcec76a1dc715c9b18d Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..004c5c5661206c8712219b7d0e10bfa8e4fcb1d9 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-312.pyc b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7720ea9f2d58ae9d0677e385cb9be845cdafcfc1 Binary files /dev/null and b/lib/python3.12/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/httpcore/_sync/connection.py b/lib/python3.12/site-packages/httpcore/_sync/connection.py new file mode 100644 index 0000000000000000000000000000000000000000..363f8be819d2576ea65365e625dd1596ea40429a --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_sync/connection.py @@ -0,0 +1,222 @@ +from __future__ import annotations + +import itertools +import logging +import ssl +import types +import typing + +from .._backends.sync import SyncBackend +from .._backends.base import SOCKET_OPTION, NetworkBackend, NetworkStream +from .._exceptions import ConnectError, ConnectTimeout +from .._models import Origin, Request, Response +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +RETRIES_BACKOFF_FACTOR = 0.5 # 0s, 0.5s, 1s, 2s, 4s, etc. + + +logger = logging.getLogger("httpcore.connection") + + +def exponential_backoff(factor: float) -> typing.Iterator[float]: + """ + Generate a geometric sequence that has a ratio of 2 and starts with 0. + + For example: + - `factor = 2`: `0, 2, 4, 8, 16, 32, 64, ...` + - `factor = 3`: `0, 3, 6, 12, 24, 48, 96, ...` + """ + yield 0 + for n in itertools.count(): + yield factor * 2**n + + +class HTTPConnection(ConnectionInterface): + def __init__( + self, + origin: Origin, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._origin = origin + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend: NetworkBackend = ( + SyncBackend() if network_backend is None else network_backend + ) + self._connection: ConnectionInterface | None = None + self._connect_failed: bool = False + self._request_lock = Lock() + self._socket_options = socket_options + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection to {self._origin}" + ) + + try: + with self._request_lock: + if self._connection is None: + stream = self._connect(request) + + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except BaseException as exc: + self._connect_failed = True + raise exc + + return self._connection.handle_request(request) + + def _connect(self, request: Request) -> NetworkStream: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + retries_left = self._retries + delays = exponential_backoff(factor=RETRIES_BACKOFF_FACTOR) + + while True: + try: + if self._uds is None: + kwargs = { + "host": self._origin.host.decode("ascii"), + "port": self._origin.port, + "local_address": self._local_address, + "timeout": timeout, + "socket_options": self._socket_options, + } + with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + else: + kwargs = { + "path": self._uds, + "timeout": timeout, + "socket_options": self._socket_options, + } + with Trace( + "connect_unix_socket", logger, request, kwargs + ) as trace: + stream = self._network_backend.connect_unix_socket( + **kwargs + ) + trace.return_value = stream + + if self._origin.scheme in (b"https", b"wss"): + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + return stream + except (ConnectError, ConnectTimeout): + if retries_left <= 0: + raise + retries_left -= 1 + delay = next(delays) + with Trace("retry", logger, request, kwargs) as trace: + self._network_backend.sleep(delay) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def close(self) -> None: + if self._connection is not None: + with Trace("close", logger, None, {}): + self._connection.close() + + def is_available(self) -> bool: + if self._connection is None: + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> HTTPConnection: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() diff --git a/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py b/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..9ccfa53e597a29ee387f9d16f3af4f695ac0d33a --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py @@ -0,0 +1,420 @@ +from __future__ import annotations + +import ssl +import sys +import types +import typing + +from .._backends.sync import SyncBackend +from .._backends.base import SOCKET_OPTION, NetworkBackend +from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol +from .._models import Origin, Proxy, Request, Response +from .._synchronization import Event, ShieldCancellation, ThreadLock +from .connection import HTTPConnection +from .interfaces import ConnectionInterface, RequestInterface + + +class PoolRequest: + def __init__(self, request: Request) -> None: + self.request = request + self.connection: ConnectionInterface | None = None + self._connection_acquired = Event() + + def assign_to_connection(self, connection: ConnectionInterface | None) -> None: + self.connection = connection + self._connection_acquired.set() + + def clear_connection(self) -> None: + self.connection = None + self._connection_acquired = Event() + + def wait_for_connection( + self, timeout: float | None = None + ) -> ConnectionInterface: + if self.connection is None: + self._connection_acquired.wait(timeout=timeout) + assert self.connection is not None + return self.connection + + def is_queued(self) -> bool: + return self.connection is None + + +class ConnectionPool(RequestInterface): + """ + A connection pool for making HTTP requests. + """ + + def __init__( + self, + ssl_context: ssl.SSLContext | None = None, + proxy: Proxy | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish a + connection. + local_address: Local address to connect from. Can also be used to connect + using a particular address family. Using `local_address="0.0.0.0"` + will connect using an `AF_INET` address (IPv4), while using + `local_address="::"` will connect using an `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + socket_options: Socket options that have to be included + in the TCP socket when the connection was established. + """ + self._ssl_context = ssl_context + self._proxy = proxy + self._max_connections = ( + sys.maxsize if max_connections is None else max_connections + ) + self._max_keepalive_connections = ( + sys.maxsize + if max_keepalive_connections is None + else max_keepalive_connections + ) + self._max_keepalive_connections = min( + self._max_connections, self._max_keepalive_connections + ) + + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend = ( + SyncBackend() if network_backend is None else network_backend + ) + self._socket_options = socket_options + + # The mutable state on a connection pool is the queue of incoming requests, + # and the set of connections that are servicing those requests. + self._connections: list[ConnectionInterface] = [] + self._requests: list[PoolRequest] = [] + + # We only mutate the state of the connection pool within an 'optional_thread_lock' + # context. This holds a threading lock unless we're running in async mode, + # in which case it is a no-op. + self._optional_thread_lock = ThreadLock() + + def create_connection(self, origin: Origin) -> ConnectionInterface: + if self._proxy is not None: + if self._proxy.url.scheme in (b"socks5", b"socks5h"): + from .socks_proxy import Socks5Connection + + return Socks5Connection( + proxy_origin=self._proxy.url.origin, + proxy_auth=self._proxy.auth, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + elif origin.scheme == b"http": + from .http_proxy import ForwardHTTPConnection + + return ForwardHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + ) + from .http_proxy import TunnelHTTPConnection + + return TunnelHTTPConnection( + proxy_origin=self._proxy.url.origin, + proxy_headers=self._proxy.headers, + proxy_ssl_context=self._proxy.ssl_context, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + return HTTPConnection( + origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + retries=self._retries, + local_address=self._local_address, + uds=self._uds, + network_backend=self._network_backend, + socket_options=self._socket_options, + ) + + @property + def connections(self) -> list[ConnectionInterface]: + """ + Return a list of the connections currently in the pool. + + For example: + + ```python + >>> pool.connections + [ + , + , + , + ] + ``` + """ + return list(self._connections) + + def handle_request(self, request: Request) -> Response: + """ + Send an HTTP request, and return an HTTP response. + + This is the core implementation that is called into by `.request()` or `.stream()`. + """ + scheme = request.url.scheme.decode() + if scheme == "": + raise UnsupportedProtocol( + "Request URL is missing an 'http://' or 'https://' protocol." + ) + if scheme not in ("http", "https", "ws", "wss"): + raise UnsupportedProtocol( + f"Request URL has an unsupported protocol '{scheme}://'." + ) + + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("pool", None) + + with self._optional_thread_lock: + # Add the incoming request to our request queue. + pool_request = PoolRequest(request) + self._requests.append(pool_request) + + try: + while True: + with self._optional_thread_lock: + # Assign incoming requests to available connections, + # closing or creating new connections as required. + closing = self._assign_requests_to_connections() + self._close_connections(closing) + + # Wait until this request has an assigned connection. + connection = pool_request.wait_for_connection(timeout=timeout) + + try: + # Send the request on the assigned connection. + response = connection.handle_request( + pool_request.request + ) + except ConnectionNotAvailable: + # In some cases a connection may initially be available to + # handle a request, but then become unavailable. + # + # In this case we clear the connection and try again. + pool_request.clear_connection() + else: + break # pragma: nocover + + except BaseException as exc: + with self._optional_thread_lock: + # For any exception or cancellation we remove the request from + # the queue, and then re-assign requests to connections. + self._requests.remove(pool_request) + closing = self._assign_requests_to_connections() + + self._close_connections(closing) + raise exc from None + + # Return the response. Note that in this case we still have to manage + # the point at which the response is closed. + assert isinstance(response.stream, typing.Iterable) + return Response( + status=response.status, + headers=response.headers, + content=PoolByteStream( + stream=response.stream, pool_request=pool_request, pool=self + ), + extensions=response.extensions, + ) + + def _assign_requests_to_connections(self) -> list[ConnectionInterface]: + """ + Manage the state of the connection pool, assigning incoming + requests to connections as available. + + Called whenever a new request is added or removed from the pool. + + Any closing connections are returned, allowing the I/O for closing + those connections to be handled seperately. + """ + closing_connections = [] + + # First we handle cleaning up any connections that are closed, + # have expired their keep-alive, or surplus idle connections. + for connection in list(self._connections): + if connection.is_closed(): + # log: "removing closed connection" + self._connections.remove(connection) + elif connection.has_expired(): + # log: "closing expired connection" + self._connections.remove(connection) + closing_connections.append(connection) + elif ( + connection.is_idle() + and len([connection.is_idle() for connection in self._connections]) + > self._max_keepalive_connections + ): + # log: "closing idle connection" + self._connections.remove(connection) + closing_connections.append(connection) + + # Assign queued requests to connections. + queued_requests = [request for request in self._requests if request.is_queued()] + for pool_request in queued_requests: + origin = pool_request.request.url.origin + available_connections = [ + connection + for connection in self._connections + if connection.can_handle_request(origin) and connection.is_available() + ] + idle_connections = [ + connection for connection in self._connections if connection.is_idle() + ] + + # There are three cases for how we may be able to handle the request: + # + # 1. There is an existing connection that can handle the request. + # 2. We can create a new connection to handle the request. + # 3. We can close an idle connection and then create a new connection + # to handle the request. + if available_connections: + # log: "reusing existing connection" + connection = available_connections[0] + pool_request.assign_to_connection(connection) + elif len(self._connections) < self._max_connections: + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + elif idle_connections: + # log: "closing idle connection" + connection = idle_connections[0] + self._connections.remove(connection) + closing_connections.append(connection) + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + + return closing_connections + + def _close_connections(self, closing: list[ConnectionInterface]) -> None: + # Close connections which have been removed from the pool. + with ShieldCancellation(): + for connection in closing: + connection.close() + + def close(self) -> None: + # Explicitly close the connection pool. + # Clears all existing requests and connections. + with self._optional_thread_lock: + closing_connections = list(self._connections) + self._connections = [] + self._close_connections(closing_connections) + + def __enter__(self) -> ConnectionPool: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + with self._optional_thread_lock: + request_is_queued = [request.is_queued() for request in self._requests] + connection_is_idle = [ + connection.is_idle() for connection in self._connections + ] + + num_active_requests = request_is_queued.count(False) + num_queued_requests = request_is_queued.count(True) + num_active_connections = connection_is_idle.count(False) + num_idle_connections = connection_is_idle.count(True) + + requests_info = ( + f"Requests: {num_active_requests} active, {num_queued_requests} queued" + ) + connection_info = ( + f"Connections: {num_active_connections} active, {num_idle_connections} idle" + ) + + return f"<{class_name} [{requests_info} | {connection_info}]>" + + +class PoolByteStream: + def __init__( + self, + stream: typing.Iterable[bytes], + pool_request: PoolRequest, + pool: ConnectionPool, + ) -> None: + self._stream = stream + self._pool_request = pool_request + self._pool = pool + self._closed = False + + def __iter__(self) -> typing.Iterator[bytes]: + try: + for part in self._stream: + yield part + except BaseException as exc: + self.close() + raise exc from None + + def close(self) -> None: + if not self._closed: + self._closed = True + with ShieldCancellation(): + if hasattr(self._stream, "close"): + self._stream.close() + + with self._pool._optional_thread_lock: + self._pool._requests.remove(self._pool_request) + closing = self._pool._assign_requests_to_connections() + + self._pool._close_connections(closing) diff --git a/lib/python3.12/site-packages/httpcore/_sync/http11.py b/lib/python3.12/site-packages/httpcore/_sync/http11.py new file mode 100644 index 0000000000000000000000000000000000000000..ebd3a97480c720d418acb1285a7b75da19b62c8c --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_sync/http11.py @@ -0,0 +1,379 @@ +from __future__ import annotations + +import enum +import logging +import ssl +import time +import types +import typing + +import h11 + +from .._backends.base import NetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, + WriteError, + map_exceptions, +) +from .._models import Origin, Request, Response +from .._synchronization import Lock, ShieldCancellation +from .._trace import Trace +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore.http11") + + +# A subset of `h11.Event` types supported by `_send_event` +H11SendEvent = typing.Union[ + h11.Request, + h11.Data, + h11.EndOfMessage, +] + + +class HTTPConnectionState(enum.IntEnum): + NEW = 0 + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class HTTP11Connection(ConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + MAX_INCOMPLETE_EVENT_SIZE = 100 * 1024 + + def __init__( + self, + origin: Origin, + stream: NetworkStream, + keepalive_expiry: float | None = None, + ) -> None: + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._expire_at: float | None = None + self._state = HTTPConnectionState.NEW + self._state_lock = Lock() + self._request_count = 0 + self._h11_state = h11.Connection( + our_role=h11.CLIENT, + max_incomplete_event_size=self.MAX_INCOMPLETE_EVENT_SIZE, + ) + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + with self._state_lock: + if self._state in (HTTPConnectionState.NEW, HTTPConnectionState.IDLE): + self._request_count += 1 + self._state = HTTPConnectionState.ACTIVE + self._expire_at = None + else: + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request} + try: + with Trace( + "send_request_headers", logger, request, kwargs + ) as trace: + self._send_request_headers(**kwargs) + with Trace("send_request_body", logger, request, kwargs) as trace: + self._send_request_body(**kwargs) + except WriteError: + # If we get a write error while we're writing the request, + # then we supress this error and move on to attempting to + # read the response. Servers can sometimes close the request + # pre-emptively and then respond with a well formed HTTP + # error response. + pass + + with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + ( + http_version, + status, + reason_phrase, + headers, + trailing_data, + ) = self._receive_response_headers(**kwargs) + trace.return_value = ( + http_version, + status, + reason_phrase, + headers, + ) + + network_stream = self._network_stream + + # CONNECT or Upgrade request + if (status == 101) or ( + (request.method == b"CONNECT") and (200 <= status < 300) + ): + network_stream = HTTP11UpgradeStream(network_stream, trailing_data) + + return Response( + status=status, + headers=headers, + content=HTTP11ConnectionByteStream(self, request), + extensions={ + "http_version": http_version, + "reason_phrase": reason_phrase, + "network_stream": network_stream, + }, + ) + except BaseException as exc: + with ShieldCancellation(): + with Trace("response_closed", logger, request) as trace: + self._response_closed() + raise exc + + # Sending the request... + + def _send_request_headers(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with map_exceptions({h11.LocalProtocolError: LocalProtocolError}): + event = h11.Request( + method=request.method, + target=request.url.target, + headers=request.headers, + ) + self._send_event(event, timeout=timeout) + + def _send_request_body(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + assert isinstance(request.stream, typing.Iterable) + for chunk in request.stream: + event = h11.Data(data=chunk) + self._send_event(event, timeout=timeout) + + self._send_event(h11.EndOfMessage(), timeout=timeout) + + def _send_event(self, event: h11.Event, timeout: float | None = None) -> None: + bytes_to_send = self._h11_state.send(event) + if bytes_to_send is not None: + self._network_stream.write(bytes_to_send, timeout=timeout) + + # Receiving the response... + + def _receive_response_headers( + self, request: Request + ) -> tuple[bytes, int, bytes, list[tuple[bytes, bytes]], bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = self._receive_event(timeout=timeout) + if isinstance(event, h11.Response): + break + if ( + isinstance(event, h11.InformationalResponse) + and event.status_code == 101 + ): + break + + http_version = b"HTTP/" + event.http_version + + # h11 version 0.11+ supports a `raw_items` interface to get the + # raw header casing, rather than the enforced lowercase headers. + headers = event.headers.raw_items() + + trailing_data, _ = self._h11_state.trailing_data + + return http_version, event.status_code, event.reason, headers, trailing_data + + def _receive_response_body( + self, request: Request + ) -> typing.Iterator[bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = self._receive_event(timeout=timeout) + if isinstance(event, h11.Data): + yield bytes(event.data) + elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)): + break + + def _receive_event( + self, timeout: float | None = None + ) -> h11.Event | type[h11.PAUSED]: + while True: + with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}): + event = self._h11_state.next_event() + + if event is h11.NEED_DATA: + data = self._network_stream.read( + self.READ_NUM_BYTES, timeout=timeout + ) + + # If we feed this case through h11 we'll raise an exception like: + # + # httpcore.RemoteProtocolError: can't handle event type + # ConnectionClosed when role=SERVER and state=SEND_RESPONSE + # + # Which is accurate, but not very informative from an end-user + # perspective. Instead we handle this case distinctly and treat + # it as a ConnectError. + if data == b"" and self._h11_state.their_state == h11.SEND_RESPONSE: + msg = "Server disconnected without sending a response." + raise RemoteProtocolError(msg) + + self._h11_state.receive_data(data) + else: + # mypy fails to narrow the type in the above if statement above + return event # type: ignore[return-value] + + def _response_closed(self) -> None: + with self._state_lock: + if ( + self._h11_state.our_state is h11.DONE + and self._h11_state.their_state is h11.DONE + ): + self._state = HTTPConnectionState.IDLE + self._h11_state.start_next_cycle() + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + else: + self.close() + + # Once the connection is no longer required... + + def close(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._state = HTTPConnectionState.CLOSED + self._network_stream.close() + + # The ConnectionInterface methods provide information about the state of + # the connection, allowing for a connection pooling implementation to + # determine when to reuse and when to close the connection... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + # Note that HTTP/1.1 connections in the "NEW" state are not treated as + # being "available". The control flow which created the connection will + # be able to send an outgoing request, but the connection will not be + # acquired from the connection pool for any other request. + return self._state == HTTPConnectionState.IDLE + + def has_expired(self) -> bool: + now = time.monotonic() + keepalive_expired = self._expire_at is not None and now > self._expire_at + + # If the HTTP connection is idle but the socket is readable, then the + # only valid state is that the socket is about to return b"", indicating + # a server-initiated disconnect. + server_disconnected = ( + self._state == HTTPConnectionState.IDLE + and self._network_stream.get_extra_info("is_readable") + ) + + return keepalive_expired or server_disconnected + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/1.1, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> HTTP11Connection: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() + + +class HTTP11ConnectionByteStream: + def __init__(self, connection: HTTP11Connection, request: Request) -> None: + self._connection = connection + self._request = request + self._closed = False + + def __iter__(self) -> typing.Iterator[bytes]: + kwargs = {"request": self._request} + try: + with Trace("receive_response_body", logger, self._request, kwargs): + for chunk in self._connection._receive_response_body(**kwargs): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with ShieldCancellation(): + self.close() + raise exc + + def close(self) -> None: + if not self._closed: + self._closed = True + with Trace("response_closed", logger, self._request): + self._connection._response_closed() + + +class HTTP11UpgradeStream(NetworkStream): + def __init__(self, stream: NetworkStream, leading_data: bytes) -> None: + self._stream = stream + self._leading_data = leading_data + + def read(self, max_bytes: int, timeout: float | None = None) -> bytes: + if self._leading_data: + buffer = self._leading_data[:max_bytes] + self._leading_data = self._leading_data[max_bytes:] + return buffer + else: + return self._stream.read(max_bytes, timeout) + + def write(self, buffer: bytes, timeout: float | None = None) -> None: + self._stream.write(buffer, timeout) + + def close(self) -> None: + self._stream.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + timeout: float | None = None, + ) -> NetworkStream: + return self._stream.start_tls(ssl_context, server_hostname, timeout) + + def get_extra_info(self, info: str) -> typing.Any: + return self._stream.get_extra_info(info) diff --git a/lib/python3.12/site-packages/httpcore/_sync/http2.py b/lib/python3.12/site-packages/httpcore/_sync/http2.py new file mode 100644 index 0000000000000000000000000000000000000000..ddcc189001c50c37c6a03810dc21d955df919f10 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_sync/http2.py @@ -0,0 +1,592 @@ +from __future__ import annotations + +import enum +import logging +import time +import types +import typing + +import h2.config +import h2.connection +import h2.events +import h2.exceptions +import h2.settings + +from .._backends.base import NetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, +) +from .._models import Origin, Request, Response +from .._synchronization import Lock, Semaphore, ShieldCancellation +from .._trace import Trace +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore.http2") + + +def has_body_headers(request: Request) -> bool: + return any( + k.lower() == b"content-length" or k.lower() == b"transfer-encoding" + for k, v in request.headers + ) + + +class HTTPConnectionState(enum.IntEnum): + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class HTTP2Connection(ConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + CONFIG = h2.config.H2Configuration(validate_inbound_headers=False) + + def __init__( + self, + origin: Origin, + stream: NetworkStream, + keepalive_expiry: float | None = None, + ): + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: float | None = keepalive_expiry + self._h2_state = h2.connection.H2Connection(config=self.CONFIG) + self._state = HTTPConnectionState.IDLE + self._expire_at: float | None = None + self._request_count = 0 + self._init_lock = Lock() + self._state_lock = Lock() + self._read_lock = Lock() + self._write_lock = Lock() + self._sent_connection_init = False + self._used_all_stream_ids = False + self._connection_error = False + + # Mapping from stream ID to response stream events. + self._events: dict[ + int, + list[ + h2.events.ResponseReceived + | h2.events.DataReceived + | h2.events.StreamEnded + | h2.events.StreamReset, + ], + ] = {} + + # Connection terminated events are stored as state since + # we need to handle them for all streams. + self._connection_terminated: h2.events.ConnectionTerminated | None = None + + self._read_exception: Exception | None = None + self._write_exception: Exception | None = None + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + # This cannot occur in normal operation, since the connection pool + # will only send requests on connections that handle them. + # It's in place simply for resilience as a guard against incorrect + # usage, for anyone working directly with httpcore connections. + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + with self._state_lock: + if self._state in (HTTPConnectionState.ACTIVE, HTTPConnectionState.IDLE): + self._request_count += 1 + self._expire_at = None + self._state = HTTPConnectionState.ACTIVE + else: + raise ConnectionNotAvailable() + + with self._init_lock: + if not self._sent_connection_init: + try: + sci_kwargs = {"request": request} + with Trace( + "send_connection_init", logger, request, sci_kwargs + ): + self._send_connection_init(**sci_kwargs) + except BaseException as exc: + with ShieldCancellation(): + self.close() + raise exc + + self._sent_connection_init = True + + # Initially start with just 1 until the remote server provides + # its max_concurrent_streams value + self._max_streams = 1 + + local_settings_max_streams = ( + self._h2_state.local_settings.max_concurrent_streams + ) + self._max_streams_semaphore = Semaphore(local_settings_max_streams) + + for _ in range(local_settings_max_streams - self._max_streams): + self._max_streams_semaphore.acquire() + + self._max_streams_semaphore.acquire() + + try: + stream_id = self._h2_state.get_next_available_stream_id() + self._events[stream_id] = [] + except h2.exceptions.NoAvailableStreamIDError: # pragma: nocover + self._used_all_stream_ids = True + self._request_count -= 1 + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request, "stream_id": stream_id} + with Trace("send_request_headers", logger, request, kwargs): + self._send_request_headers(request=request, stream_id=stream_id) + with Trace("send_request_body", logger, request, kwargs): + self._send_request_body(request=request, stream_id=stream_id) + with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + status, headers = self._receive_response( + request=request, stream_id=stream_id + ) + trace.return_value = (status, headers) + + return Response( + status=status, + headers=headers, + content=HTTP2ConnectionByteStream(self, request, stream_id=stream_id), + extensions={ + "http_version": b"HTTP/2", + "network_stream": self._network_stream, + "stream_id": stream_id, + }, + ) + except BaseException as exc: # noqa: PIE786 + with ShieldCancellation(): + kwargs = {"stream_id": stream_id} + with Trace("response_closed", logger, request, kwargs): + self._response_closed(stream_id=stream_id) + + if isinstance(exc, h2.exceptions.ProtocolError): + # One case where h2 can raise a protocol error is when a + # closed frame has been seen by the state machine. + # + # This happens when one stream is reading, and encounters + # a GOAWAY event. Other flows of control may then raise + # a protocol error at any point they interact with the 'h2_state'. + # + # In this case we'll have stored the event, and should raise + # it as a RemoteProtocolError. + if self._connection_terminated: # pragma: nocover + raise RemoteProtocolError(self._connection_terminated) + # If h2 raises a protocol error in some other state then we + # must somehow have made a protocol violation. + raise LocalProtocolError(exc) # pragma: nocover + + raise exc + + def _send_connection_init(self, request: Request) -> None: + """ + The HTTP/2 connection requires some initial setup before we can start + using individual request/response streams on it. + """ + # Need to set these manually here instead of manipulating via + # __setitem__() otherwise the H2Connection will emit SettingsUpdate + # frames in addition to sending the undesired defaults. + self._h2_state.local_settings = h2.settings.Settings( + client=True, + initial_values={ + # Disable PUSH_PROMISE frames from the server since we don't do anything + # with them for now. Maybe when we support caching? + h2.settings.SettingCodes.ENABLE_PUSH: 0, + # These two are taken from h2 for safe defaults + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS: 100, + h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE: 65536, + }, + ) + + # Some websites (*cough* Yahoo *cough*) balk at this setting being + # present in the initial handshake since it's not defined in the original + # RFC despite the RFC mandating ignoring settings you don't know about. + del self._h2_state.local_settings[ + h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL + ] + + self._h2_state.initiate_connection() + self._h2_state.increment_flow_control_window(2**24) + self._write_outgoing_data(request) + + # Sending the request... + + def _send_request_headers(self, request: Request, stream_id: int) -> None: + """ + Send the request headers to a given stream ID. + """ + end_stream = not has_body_headers(request) + + # In HTTP/2 the ':authority' pseudo-header is used instead of 'Host'. + # In order to gracefully handle HTTP/1.1 and HTTP/2 we always require + # HTTP/1.1 style headers, and map them appropriately if we end up on + # an HTTP/2 connection. + authority = [v for k, v in request.headers if k.lower() == b"host"][0] + + headers = [ + (b":method", request.method), + (b":authority", authority), + (b":scheme", request.url.scheme), + (b":path", request.url.target), + ] + [ + (k.lower(), v) + for k, v in request.headers + if k.lower() + not in ( + b"host", + b"transfer-encoding", + ) + ] + + self._h2_state.send_headers(stream_id, headers, end_stream=end_stream) + self._h2_state.increment_flow_control_window(2**24, stream_id=stream_id) + self._write_outgoing_data(request) + + def _send_request_body(self, request: Request, stream_id: int) -> None: + """ + Iterate over the request body sending it to a given stream ID. + """ + if not has_body_headers(request): + return + + assert isinstance(request.stream, typing.Iterable) + for data in request.stream: + self._send_stream_data(request, stream_id, data) + self._send_end_stream(request, stream_id) + + def _send_stream_data( + self, request: Request, stream_id: int, data: bytes + ) -> None: + """ + Send a single chunk of data in one or more data frames. + """ + while data: + max_flow = self._wait_for_outgoing_flow(request, stream_id) + chunk_size = min(len(data), max_flow) + chunk, data = data[:chunk_size], data[chunk_size:] + self._h2_state.send_data(stream_id, chunk) + self._write_outgoing_data(request) + + def _send_end_stream(self, request: Request, stream_id: int) -> None: + """ + Send an empty data frame on on a given stream ID with the END_STREAM flag set. + """ + self._h2_state.end_stream(stream_id) + self._write_outgoing_data(request) + + # Receiving the response... + + def _receive_response( + self, request: Request, stream_id: int + ) -> tuple[int, list[tuple[bytes, bytes]]]: + """ + Return the response status code and headers for a given stream ID. + """ + while True: + event = self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.ResponseReceived): + break + + status_code = 200 + headers = [] + assert event.headers is not None + for k, v in event.headers: + if k == b":status": + status_code = int(v.decode("ascii", errors="ignore")) + elif not k.startswith(b":"): + headers.append((k, v)) + + return (status_code, headers) + + def _receive_response_body( + self, request: Request, stream_id: int + ) -> typing.Iterator[bytes]: + """ + Iterator that returns the bytes of the response body for a given stream ID. + """ + while True: + event = self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.DataReceived): + assert event.flow_controlled_length is not None + assert event.data is not None + amount = event.flow_controlled_length + self._h2_state.acknowledge_received_data(amount, stream_id) + self._write_outgoing_data(request) + yield event.data + elif isinstance(event, h2.events.StreamEnded): + break + + def _receive_stream_event( + self, request: Request, stream_id: int + ) -> h2.events.ResponseReceived | h2.events.DataReceived | h2.events.StreamEnded: + """ + Return the next available event for a given stream ID. + + Will read more data from the network if required. + """ + while not self._events.get(stream_id): + self._receive_events(request, stream_id) + event = self._events[stream_id].pop(0) + if isinstance(event, h2.events.StreamReset): + raise RemoteProtocolError(event) + return event + + def _receive_events( + self, request: Request, stream_id: int | None = None + ) -> None: + """ + Read some data from the network until we see one or more events + for a given stream ID. + """ + with self._read_lock: + if self._connection_terminated is not None: + last_stream_id = self._connection_terminated.last_stream_id + if stream_id and last_stream_id and stream_id > last_stream_id: + self._request_count -= 1 + raise ConnectionNotAvailable() + raise RemoteProtocolError(self._connection_terminated) + + # This conditional is a bit icky. We don't want to block reading if we've + # actually got an event to return for a given stream. We need to do that + # check *within* the atomic read lock. Though it also need to be optional, + # because when we call it from `_wait_for_outgoing_flow` we *do* want to + # block until we've available flow control, event when we have events + # pending for the stream ID we're attempting to send on. + if stream_id is None or not self._events.get(stream_id): + events = self._read_incoming_data(request) + for event in events: + if isinstance(event, h2.events.RemoteSettingsChanged): + with Trace( + "receive_remote_settings", logger, request + ) as trace: + self._receive_remote_settings_change(event) + trace.return_value = event + + elif isinstance( + event, + ( + h2.events.ResponseReceived, + h2.events.DataReceived, + h2.events.StreamEnded, + h2.events.StreamReset, + ), + ): + if event.stream_id in self._events: + self._events[event.stream_id].append(event) + + elif isinstance(event, h2.events.ConnectionTerminated): + self._connection_terminated = event + + self._write_outgoing_data(request) + + def _receive_remote_settings_change( + self, event: h2.events.RemoteSettingsChanged + ) -> None: + max_concurrent_streams = event.changed_settings.get( + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS + ) + if max_concurrent_streams: + new_max_streams = min( + max_concurrent_streams.new_value, + self._h2_state.local_settings.max_concurrent_streams, + ) + if new_max_streams and new_max_streams != self._max_streams: + while new_max_streams > self._max_streams: + self._max_streams_semaphore.release() + self._max_streams += 1 + while new_max_streams < self._max_streams: + self._max_streams_semaphore.acquire() + self._max_streams -= 1 + + def _response_closed(self, stream_id: int) -> None: + self._max_streams_semaphore.release() + del self._events[stream_id] + with self._state_lock: + if self._connection_terminated and not self._events: + self.close() + + elif self._state == HTTPConnectionState.ACTIVE and not self._events: + self._state = HTTPConnectionState.IDLE + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + if self._used_all_stream_ids: # pragma: nocover + self.close() + + def close(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._h2_state.close_connection() + self._state = HTTPConnectionState.CLOSED + self._network_stream.close() + + # Wrappers around network read/write operations... + + def _read_incoming_data(self, request: Request) -> list[h2.events.Event]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + if self._read_exception is not None: + raise self._read_exception # pragma: nocover + + try: + data = self._network_stream.read(self.READ_NUM_BYTES, timeout) + if data == b"": + raise RemoteProtocolError("Server disconnected") + except Exception as exc: + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future reads. + # (For example, this means that a single read timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._read_exception = exc + self._connection_error = True + raise exc + + events: list[h2.events.Event] = self._h2_state.receive_data(data) + + return events + + def _write_outgoing_data(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with self._write_lock: + data_to_send = self._h2_state.data_to_send() + + if self._write_exception is not None: + raise self._write_exception # pragma: nocover + + try: + self._network_stream.write(data_to_send, timeout) + except Exception as exc: # pragma: nocover + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future write. + # (For example, this means that a single write timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._write_exception = exc + self._connection_error = True + raise exc + + # Flow control... + + def _wait_for_outgoing_flow(self, request: Request, stream_id: int) -> int: + """ + Returns the maximum allowable outgoing flow for a given stream. + + If the allowable flow is zero, then waits on the network until + WindowUpdated frames have increased the flow rate. + https://tools.ietf.org/html/rfc7540#section-6.9 + """ + local_flow: int = self._h2_state.local_flow_control_window(stream_id) + max_frame_size: int = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + while flow == 0: + self._receive_events(request) + local_flow = self._h2_state.local_flow_control_window(stream_id) + max_frame_size = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + return flow + + # Interface for connection pooling... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + return ( + self._state != HTTPConnectionState.CLOSED + and not self._connection_error + and not self._used_all_stream_ids + and not ( + self._h2_state.state_machine.state + == h2.connection.ConnectionState.CLOSED + ) + ) + + def has_expired(self) -> bool: + now = time.monotonic() + return self._expire_at is not None and now > self._expire_at + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/2, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> HTTP2Connection: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self.close() + + +class HTTP2ConnectionByteStream: + def __init__( + self, connection: HTTP2Connection, request: Request, stream_id: int + ) -> None: + self._connection = connection + self._request = request + self._stream_id = stream_id + self._closed = False + + def __iter__(self) -> typing.Iterator[bytes]: + kwargs = {"request": self._request, "stream_id": self._stream_id} + try: + with Trace("receive_response_body", logger, self._request, kwargs): + for chunk in self._connection._receive_response_body( + request=self._request, stream_id=self._stream_id + ): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with ShieldCancellation(): + self.close() + raise exc + + def close(self) -> None: + if not self._closed: + self._closed = True + kwargs = {"stream_id": self._stream_id} + with Trace("response_closed", logger, self._request, kwargs): + self._connection._response_closed(stream_id=self._stream_id) diff --git a/lib/python3.12/site-packages/httpcore/_sync/http_proxy.py b/lib/python3.12/site-packages/httpcore/_sync/http_proxy.py new file mode 100644 index 0000000000000000000000000000000000000000..ecca88f7dc93b78f2aa26f16cf29d17a8a83ae27 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_sync/http_proxy.py @@ -0,0 +1,367 @@ +from __future__ import annotations + +import base64 +import logging +import ssl +import typing + +from .._backends.base import SOCKET_OPTION, NetworkBackend +from .._exceptions import ProxyError +from .._models import ( + URL, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, +) +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .connection import HTTPConnection +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +ByteOrStr = typing.Union[bytes, str] +HeadersAsSequence = typing.Sequence[typing.Tuple[ByteOrStr, ByteOrStr]] +HeadersAsMapping = typing.Mapping[ByteOrStr, ByteOrStr] + + +logger = logging.getLogger("httpcore.proxy") + + +def merge_headers( + default_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + override_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, +) -> list[tuple[bytes, bytes]]: + """ + Append default_headers and override_headers, de-duplicating if a key exists + in both cases. + """ + default_headers = [] if default_headers is None else list(default_headers) + override_headers = [] if override_headers is None else list(override_headers) + has_override = set(key.lower() for key, value in override_headers) + default_headers = [ + (key, value) + for key, value in default_headers + if key.lower() not in has_override + ] + return default_headers + override_headers + + +class HTTPProxy(ConnectionPool): # pragma: nocover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: str | None = None, + uds: str | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + proxy_auth: Any proxy authentication as a two-tuple of + (username, password). May be either bytes or ascii-only str. + proxy_headers: Any HTTP headers to use for the proxy requests. + For example `{"Proxy-Authorization": "Basic :"}`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + proxy_ssl_context: The same as `ssl_context`, but for a proxy server rather than a remote origin. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + local_address=local_address, + uds=uds, + socket_options=socket_options, + ) + + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if ( + self._proxy_url.scheme == b"http" and proxy_ssl_context is not None + ): # pragma: no cover + raise RuntimeError( + "The `proxy_ssl_context` argument is not allowed for the http scheme" + ) + + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + if proxy_auth is not None: + username = enforce_bytes(proxy_auth[0], name="proxy_auth") + password = enforce_bytes(proxy_auth[1], name="proxy_auth") + userpass = username + b":" + password + authorization = b"Basic " + base64.b64encode(userpass) + self._proxy_headers = [ + (b"Proxy-Authorization", authorization) + ] + self._proxy_headers + + def create_connection(self, origin: Origin) -> ConnectionInterface: + if origin.scheme == b"http": + return ForwardHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + proxy_ssl_context=self._proxy_ssl_context, + ) + return TunnelHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + ssl_context=self._ssl_context, + proxy_ssl_context=self._proxy_ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class ForwardHTTPConnection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_headers: HeadersAsMapping | HeadersAsSequence | None = None, + keepalive_expiry: float | None = None, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + ) -> None: + self._connection = HTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._remote_origin = remote_origin + + def handle_request(self, request: Request) -> Response: + headers = merge_headers(self._proxy_headers, request.headers) + url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=bytes(request.url), + ) + proxy_request = Request( + method=request.method, + url=url, + headers=headers, + content=request.stream, + extensions=request.extensions, + ) + return self._connection.handle_request(proxy_request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + self._connection.close() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + +class TunnelHTTPConnection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + ssl_context: ssl.SSLContext | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + proxy_headers: typing.Sequence[tuple[bytes, bytes]] | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: NetworkBackend | None = None, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + self._connection: ConnectionInterface = HTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._connect_lock = Lock() + self._connected = False + + def handle_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("connect", None) + + with self._connect_lock: + if not self._connected: + target = b"%b:%d" % (self._remote_origin.host, self._remote_origin.port) + + connect_url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=target, + ) + connect_headers = merge_headers( + [(b"Host", target), (b"Accept", b"*/*")], self._proxy_headers + ) + connect_request = Request( + method=b"CONNECT", + url=connect_url, + headers=connect_headers, + extensions=request.extensions, + ) + connect_response = self._connection.handle_request( + connect_request + ) + + if connect_response.status < 200 or connect_response.status > 299: + reason_bytes = connect_response.extensions.get("reason_phrase", b"") + reason_str = reason_bytes.decode("ascii", errors="ignore") + msg = "%d %s" % (connect_response.status, reason_str) + self._connection.close() + raise ProxyError(msg) + + stream = connect_response.extensions["network_stream"] + + # Upgrade the stream to SSL + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + + self._connected = True + return self._connection.handle_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + self._connection.close() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/lib/python3.12/site-packages/httpcore/_sync/interfaces.py b/lib/python3.12/site-packages/httpcore/_sync/interfaces.py new file mode 100644 index 0000000000000000000000000000000000000000..e673d4cc1b1dd7e7ecdbde91fd6ada386c3de03f --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_sync/interfaces.py @@ -0,0 +1,137 @@ +from __future__ import annotations + +import contextlib +import typing + +from .._models import ( + URL, + Extensions, + HeaderTypes, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, + include_request_headers, +) + + +class RequestInterface: + def request( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> Response: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = self.handle_request(request) + try: + response.read() + finally: + response.close() + return response + + @contextlib.contextmanager + def stream( + self, + method: bytes | str, + url: URL | bytes | str, + *, + headers: HeaderTypes = None, + content: bytes | typing.Iterator[bytes] | None = None, + extensions: Extensions | None = None, + ) -> typing.Iterator[Response]: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = self.handle_request(request) + try: + yield response + finally: + response.close() + + def handle_request(self, request: Request) -> Response: + raise NotImplementedError() # pragma: nocover + + +class ConnectionInterface(RequestInterface): + def close(self) -> None: + raise NotImplementedError() # pragma: nocover + + def info(self) -> str: + raise NotImplementedError() # pragma: nocover + + def can_handle_request(self, origin: Origin) -> bool: + raise NotImplementedError() # pragma: nocover + + def is_available(self) -> bool: + """ + Return `True` if the connection is currently able to accept an + outgoing request. + + An HTTP/1.1 connection will only be available if it is currently idle. + + An HTTP/2 connection will be available so long as the stream ID space is + not yet exhausted, and the connection is not in an error state. + + While the connection is being established we may not yet know if it is going + to result in an HTTP/1.1 or HTTP/2 connection. The connection should be + treated as being available, but might ultimately raise `NewConnectionRequired` + required exceptions if multiple requests are attempted over a connection + that ends up being established as HTTP/1.1. + """ + raise NotImplementedError() # pragma: nocover + + def has_expired(self) -> bool: + """ + Return `True` if the connection is in a state where it should be closed. + + This either means that the connection is idle and it has passed the + expiry time on its keep-alive, or that server has sent an EOF. + """ + raise NotImplementedError() # pragma: nocover + + def is_idle(self) -> bool: + """ + Return `True` if the connection is currently idle. + """ + raise NotImplementedError() # pragma: nocover + + def is_closed(self) -> bool: + """ + Return `True` if the connection has been closed. + + Used when a response is closed to determine if the connection may be + returned to the connection pool or not. + """ + raise NotImplementedError() # pragma: nocover diff --git a/lib/python3.12/site-packages/httpcore/_sync/socks_proxy.py b/lib/python3.12/site-packages/httpcore/_sync/socks_proxy.py new file mode 100644 index 0000000000000000000000000000000000000000..0ca96ddfb580b19413797f41e79f7abcecdd9d79 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_sync/socks_proxy.py @@ -0,0 +1,341 @@ +from __future__ import annotations + +import logging +import ssl + +import socksio + +from .._backends.sync import SyncBackend +from .._backends.base import NetworkBackend, NetworkStream +from .._exceptions import ConnectionNotAvailable, ProxyError +from .._models import URL, Origin, Request, Response, enforce_bytes, enforce_url +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore.socks") + + +AUTH_METHODS = { + b"\x00": "NO AUTHENTICATION REQUIRED", + b"\x01": "GSSAPI", + b"\x02": "USERNAME/PASSWORD", + b"\xff": "NO ACCEPTABLE METHODS", +} + +REPLY_CODES = { + b"\x00": "Succeeded", + b"\x01": "General SOCKS server failure", + b"\x02": "Connection not allowed by ruleset", + b"\x03": "Network unreachable", + b"\x04": "Host unreachable", + b"\x05": "Connection refused", + b"\x06": "TTL expired", + b"\x07": "Command not supported", + b"\x08": "Address type not supported", +} + + +def _init_socks5_connection( + stream: NetworkStream, + *, + host: bytes, + port: int, + auth: tuple[bytes, bytes] | None = None, +) -> None: + conn = socksio.socks5.SOCKS5Connection() + + # Auth method request + auth_method = ( + socksio.socks5.SOCKS5AuthMethod.NO_AUTH_REQUIRED + if auth is None + else socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD + ) + conn.send(socksio.socks5.SOCKS5AuthMethodsRequest([auth_method])) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes) + + # Auth method response + incoming_bytes = stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5AuthReply) + if response.method != auth_method: + requested = AUTH_METHODS.get(auth_method, "UNKNOWN") + responded = AUTH_METHODS.get(response.method, "UNKNOWN") + raise ProxyError( + f"Requested {requested} from proxy server, but got {responded}." + ) + + if response.method == socksio.socks5.SOCKS5AuthMethod.USERNAME_PASSWORD: + # Username/password request + assert auth is not None + username, password = auth + conn.send(socksio.socks5.SOCKS5UsernamePasswordRequest(username, password)) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes) + + # Username/password response + incoming_bytes = stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5UsernamePasswordReply) + if not response.success: + raise ProxyError("Invalid username/password") + + # Connect request + conn.send( + socksio.socks5.SOCKS5CommandRequest.from_address( + socksio.socks5.SOCKS5Command.CONNECT, (host, port) + ) + ) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes) + + # Connect response + incoming_bytes = stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socksio.socks5.SOCKS5Reply) + if response.reply_code != socksio.socks5.SOCKS5ReplyCode.SUCCEEDED: + reply_code = REPLY_CODES.get(response.reply_code, "UNKOWN") + raise ProxyError(f"Proxy Server could not connect: {reply_code}.") + + +class SOCKSProxy(ConnectionPool): # pragma: nocover + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: URL | bytes | str, + proxy_auth: tuple[bytes | str, bytes | str] | None = None, + ssl_context: ssl.SSLContext | None = None, + max_connections: int | None = 10, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + network_backend: NetworkBackend | None = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + ) + self._ssl_context = ssl_context + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if proxy_auth is not None: + username, password = proxy_auth + username_bytes = enforce_bytes(username, name="proxy_auth") + password_bytes = enforce_bytes(password, name="proxy_auth") + self._proxy_auth: tuple[bytes, bytes] | None = ( + username_bytes, + password_bytes, + ) + else: + self._proxy_auth = None + + def create_connection(self, origin: Origin) -> ConnectionInterface: + return Socks5Connection( + proxy_origin=self._proxy_url.origin, + remote_origin=origin, + proxy_auth=self._proxy_auth, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class Socks5Connection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_auth: tuple[bytes, bytes] | None = None, + ssl_context: ssl.SSLContext | None = None, + keepalive_expiry: float | None = None, + http1: bool = True, + http2: bool = False, + network_backend: NetworkBackend | None = None, + ) -> None: + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._proxy_auth = proxy_auth + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + + self._network_backend: NetworkBackend = ( + SyncBackend() if network_backend is None else network_backend + ) + self._connect_lock = Lock() + self._connection: ConnectionInterface | None = None + self._connect_failed = False + + def handle_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + with self._connect_lock: + if self._connection is None: + try: + # Connect to the proxy + kwargs = { + "host": self._proxy_origin.host.decode("ascii"), + "port": self._proxy_origin.port, + "timeout": timeout, + } + with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + + # Connect to the remote host using socks5 + kwargs = { + "stream": stream, + "host": self._remote_origin.host.decode("ascii"), + "port": self._remote_origin.port, + "auth": self._proxy_auth, + } + with Trace( + "setup_socks5_connection", logger, request, kwargs + ) as trace: + _init_socks5_connection(**kwargs) + trace.return_value = stream + + # Upgrade the stream to SSL + if self._remote_origin.scheme == b"https": + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ( + ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ) + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or ( + self._http2 and not self._http1 + ): # pragma: nocover + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except Exception as exc: + self._connect_failed = True + raise exc + elif not self._connection.is_available(): # pragma: nocover + raise ConnectionNotAvailable() + + return self._connection.handle_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + if self._connection is not None: + self._connection.close() + + def is_available(self) -> bool: + if self._connection is None: # pragma: nocover + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._remote_origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: # pragma: nocover + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/lib/python3.12/site-packages/httpcore/_synchronization.py b/lib/python3.12/site-packages/httpcore/_synchronization.py new file mode 100644 index 0000000000000000000000000000000000000000..2ecc9e9c363e2f16c4f934cf41cf871826d6a495 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_synchronization.py @@ -0,0 +1,318 @@ +from __future__ import annotations + +import threading +import types + +from ._exceptions import ExceptionMapping, PoolTimeout, map_exceptions + +# Our async synchronization primatives use either 'anyio' or 'trio' depending +# on if they're running under asyncio or trio. + +try: + import trio +except (ImportError, NotImplementedError): # pragma: nocover + trio = None # type: ignore + +try: + import anyio +except ImportError: # pragma: nocover + anyio = None # type: ignore + + +def current_async_library() -> str: + # Determine if we're running under trio or asyncio. + # See https://sniffio.readthedocs.io/en/latest/ + try: + import sniffio + except ImportError: # pragma: nocover + environment = "asyncio" + else: + environment = sniffio.current_async_library() + + if environment not in ("asyncio", "trio"): # pragma: nocover + raise RuntimeError("Running under an unsupported async environment.") + + if environment == "asyncio" and anyio is None: # pragma: nocover + raise RuntimeError( + "Running with asyncio requires installation of 'httpcore[asyncio]'." + ) + + if environment == "trio" and trio is None: # pragma: nocover + raise RuntimeError( + "Running with trio requires installation of 'httpcore[trio]'." + ) + + return environment + + +class AsyncLock: + """ + This is a standard lock. + + In the sync case `Lock` provides thread locking. + In the async case `AsyncLock` provides async locking. + """ + + def __init__(self) -> None: + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a lock with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_lock = trio.Lock() + elif self._backend == "asyncio": + self._anyio_lock = anyio.Lock() + + async def __aenter__(self) -> AsyncLock: + if not self._backend: + self.setup() + + if self._backend == "trio": + await self._trio_lock.acquire() + elif self._backend == "asyncio": + await self._anyio_lock.acquire() + + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self._backend == "trio": + self._trio_lock.release() + elif self._backend == "asyncio": + self._anyio_lock.release() + + +class AsyncThreadLock: + """ + This is a threading-only lock for no-I/O contexts. + + In the sync case `ThreadLock` provides thread locking. + In the async case `AsyncThreadLock` is a no-op. + """ + + def __enter__(self) -> AsyncThreadLock: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + pass + + +class AsyncEvent: + def __init__(self) -> None: + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a lock with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_event = trio.Event() + elif self._backend == "asyncio": + self._anyio_event = anyio.Event() + + def set(self) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + self._trio_event.set() + elif self._backend == "asyncio": + self._anyio_event.set() + + async def wait(self, timeout: float | None = None) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + trio_exc_map: ExceptionMapping = {trio.TooSlowError: PoolTimeout} + timeout_or_inf = float("inf") if timeout is None else timeout + with map_exceptions(trio_exc_map): + with trio.fail_after(timeout_or_inf): + await self._trio_event.wait() + elif self._backend == "asyncio": + anyio_exc_map: ExceptionMapping = {TimeoutError: PoolTimeout} + with map_exceptions(anyio_exc_map): + with anyio.fail_after(timeout): + await self._anyio_event.wait() + + +class AsyncSemaphore: + def __init__(self, bound: int) -> None: + self._bound = bound + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a semaphore with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_semaphore = trio.Semaphore( + initial_value=self._bound, max_value=self._bound + ) + elif self._backend == "asyncio": + self._anyio_semaphore = anyio.Semaphore( + initial_value=self._bound, max_value=self._bound + ) + + async def acquire(self) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + await self._trio_semaphore.acquire() + elif self._backend == "asyncio": + await self._anyio_semaphore.acquire() + + async def release(self) -> None: + if self._backend == "trio": + self._trio_semaphore.release() + elif self._backend == "asyncio": + self._anyio_semaphore.release() + + +class AsyncShieldCancellation: + # For certain portions of our codebase where we're dealing with + # closing connections during exception handling we want to shield + # the operation from being cancelled. + # + # with AsyncShieldCancellation(): + # ... # clean-up operations, shielded from cancellation. + + def __init__(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a shielded scope with the correct implementation. + """ + self._backend = current_async_library() + + if self._backend == "trio": + self._trio_shield = trio.CancelScope(shield=True) + elif self._backend == "asyncio": + self._anyio_shield = anyio.CancelScope(shield=True) + + def __enter__(self) -> AsyncShieldCancellation: + if self._backend == "trio": + self._trio_shield.__enter__() + elif self._backend == "asyncio": + self._anyio_shield.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self._backend == "trio": + self._trio_shield.__exit__(exc_type, exc_value, traceback) + elif self._backend == "asyncio": + self._anyio_shield.__exit__(exc_type, exc_value, traceback) + + +# Our thread-based synchronization primitives... + + +class Lock: + """ + This is a standard lock. + + In the sync case `Lock` provides thread locking. + In the async case `AsyncLock` provides async locking. + """ + + def __init__(self) -> None: + self._lock = threading.Lock() + + def __enter__(self) -> Lock: + self._lock.acquire() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self._lock.release() + + +class ThreadLock: + """ + This is a threading-only lock for no-I/O contexts. + + In the sync case `ThreadLock` provides thread locking. + In the async case `AsyncThreadLock` is a no-op. + """ + + def __init__(self) -> None: + self._lock = threading.Lock() + + def __enter__(self) -> ThreadLock: + self._lock.acquire() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + self._lock.release() + + +class Event: + def __init__(self) -> None: + self._event = threading.Event() + + def set(self) -> None: + self._event.set() + + def wait(self, timeout: float | None = None) -> None: + if timeout == float("inf"): # pragma: no cover + timeout = None + if not self._event.wait(timeout=timeout): + raise PoolTimeout() # pragma: nocover + + +class Semaphore: + def __init__(self, bound: int) -> None: + self._semaphore = threading.Semaphore(value=bound) + + def acquire(self) -> None: + self._semaphore.acquire() + + def release(self) -> None: + self._semaphore.release() + + +class ShieldCancellation: + # Thread-synchronous codebases don't support cancellation semantics. + # We have this class because we need to mirror the async and sync + # cases within our package, but it's just a no-op. + def __enter__(self) -> ShieldCancellation: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + pass diff --git a/lib/python3.12/site-packages/httpcore/_trace.py b/lib/python3.12/site-packages/httpcore/_trace.py new file mode 100644 index 0000000000000000000000000000000000000000..5f1cd7c47829ce17dbcf651ab56b4ffdce04a485 --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_trace.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +import inspect +import logging +import types +import typing + +from ._models import Request + + +class Trace: + def __init__( + self, + name: str, + logger: logging.Logger, + request: Request | None = None, + kwargs: dict[str, typing.Any] | None = None, + ) -> None: + self.name = name + self.logger = logger + self.trace_extension = ( + None if request is None else request.extensions.get("trace") + ) + self.debug = self.logger.isEnabledFor(logging.DEBUG) + self.kwargs = kwargs or {} + self.return_value: typing.Any = None + self.should_trace = self.debug or self.trace_extension is not None + self.prefix = self.logger.name.split(".")[-1] + + def trace(self, name: str, info: dict[str, typing.Any]) -> None: + if self.trace_extension is not None: + prefix_and_name = f"{self.prefix}.{name}" + ret = self.trace_extension(prefix_and_name, info) + if inspect.iscoroutine(ret): # pragma: no cover + raise TypeError( + "If you are using a synchronous interface, " + "the callback of the `trace` extension should " + "be a normal function instead of an asynchronous function." + ) + + if self.debug: + if not info or "return_value" in info and info["return_value"] is None: + message = name + else: + args = " ".join([f"{key}={value!r}" for key, value in info.items()]) + message = f"{name} {args}" + self.logger.debug(message) + + def __enter__(self) -> Trace: + if self.should_trace: + info = self.kwargs + self.trace(f"{self.name}.started", info) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self.should_trace: + if exc_value is None: + info = {"return_value": self.return_value} + self.trace(f"{self.name}.complete", info) + else: + info = {"exception": exc_value} + self.trace(f"{self.name}.failed", info) + + async def atrace(self, name: str, info: dict[str, typing.Any]) -> None: + if self.trace_extension is not None: + prefix_and_name = f"{self.prefix}.{name}" + coro = self.trace_extension(prefix_and_name, info) + if not inspect.iscoroutine(coro): # pragma: no cover + raise TypeError( + "If you're using an asynchronous interface, " + "the callback of the `trace` extension should " + "be an asynchronous function rather than a normal function." + ) + await coro + + if self.debug: + if not info or "return_value" in info and info["return_value"] is None: + message = name + else: + args = " ".join([f"{key}={value!r}" for key, value in info.items()]) + message = f"{name} {args}" + self.logger.debug(message) + + async def __aenter__(self) -> Trace: + if self.should_trace: + info = self.kwargs + await self.atrace(f"{self.name}.started", info) + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: types.TracebackType | None = None, + ) -> None: + if self.should_trace: + if exc_value is None: + info = {"return_value": self.return_value} + await self.atrace(f"{self.name}.complete", info) + else: + info = {"exception": exc_value} + await self.atrace(f"{self.name}.failed", info) diff --git a/lib/python3.12/site-packages/httpcore/_utils.py b/lib/python3.12/site-packages/httpcore/_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..c44ff93cb2f572afc6e679308024b744b65c3b0a --- /dev/null +++ b/lib/python3.12/site-packages/httpcore/_utils.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import select +import socket +import sys + + +def is_socket_readable(sock: socket.socket | None) -> bool: + """ + Return whether a socket, as identifed by its file descriptor, is readable. + "A socket is readable" means that the read buffer isn't empty, i.e. that calling + .recv() on it would immediately return some data. + """ + # NOTE: we want check for readability without actually attempting to read, because + # we don't want to block forever if it's not readable. + + # In the case that the socket no longer exists, or cannot return a file + # descriptor, we treat it as being readable, as if it the next read operation + # on it is ready to return the terminating `b""`. + sock_fd = None if sock is None else sock.fileno() + if sock_fd is None or sock_fd < 0: # pragma: nocover + return True + + # The implementation below was stolen from: + # https://github.com/python-trio/trio/blob/20ee2b1b7376db637435d80e266212a35837ddcc/trio/_socket.py#L471-L478 + # See also: https://github.com/encode/httpcore/pull/193#issuecomment-703129316 + + # Use select.select on Windows, and when poll is unavailable and select.poll + # everywhere else. (E.g. When eventlet is in use. See #327) + if ( + sys.platform == "win32" or getattr(select, "poll", None) is None + ): # pragma: nocover + rready, _, _ = select.select([sock_fd], [], [], 0) + return bool(rready) + p = select.poll() + p.register(sock_fd, select.POLLIN) + return bool(p.poll(0)) diff --git a/lib/python3.12/site-packages/httpcore/py.typed b/lib/python3.12/site-packages/httpcore/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/INSTALLER b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/LICENSE b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..b96dcb0480a0b0be0727976e5202a1e7b23edc3f --- /dev/null +++ b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Facebook, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/METADATA b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..89017efbd21c66a707b946cd6d06e0d7d253d301 --- /dev/null +++ b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/METADATA @@ -0,0 +1,110 @@ +Metadata-Version: 2.1 +Name: hydra-core +Version: 1.3.2 +Summary: A framework for elegantly configuring complex applications +Home-page: https://github.com/facebookresearch/hydra +Author: Omry Yadan +Author-email: omry@fb.com +License: MIT +Keywords: command-line configuration yaml tab-completion +Classifier: License :: OSI Approved :: MIT License +Classifier: Development Status :: 4 - Beta +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: MacOS +Classifier: Operating System :: Microsoft :: Windows +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: omegaconf (<2.4,>=2.2) +Requires-Dist: antlr4-python3-runtime (==4.9.*) +Requires-Dist: packaging +Requires-Dist: importlib-resources ; python_version < "3.9" + +

logo

+ +

+ + PyPI + + + CircleCI + + + PyPI - License + + + PyPI - Python Version + + + Downloads + + + Code style: black + + + Total alerts + + + Language grade: Python + +

+ A framework for elegantly configuring complex applications. +

+

+ Check the website for more information,
+ or click the thumbnail below for a one-minute video introduction to Hydra.
+

+

+ + 1 minute overview + +

+

+ +---------------------- + + +### Releases + +#### Stable + +**Hydra 1.3** is the stable version of Hydra. +- [Documentation](https://hydra.cc/docs/1.3/intro/) +- Installation : `pip install hydra-core --upgrade` + +See the [NEWS.md](NEWS.md) file for a summary of recent changes to Hydra. + +### License +Hydra is licensed under [MIT License](LICENSE). + +## Hydra Ecosystem + +#### Check out these third-party libraries that build on Hydra's functionality: +* [hydra-zen](https://github.com/mit-ll-responsible-ai/hydra-zen): Pythonic utilities for working with Hydra. Dynamic config generation capabilities, enhanced config store features, a Python API for launching Hydra jobs, and more. +* [lightning-hydra-template](https://github.com/ashleve/lightning-hydra-template): user-friendly template combining Hydra with [Pytorch-Lightning](https://github.com/Lightning-AI/lightning) for ML experimentation. +* [hydra-torch](https://github.com/pytorch/hydra-torch): [configen](https://github.com/facebookresearch/hydra/tree/main/tools/configen)-generated configuration classes enabling type-safe PyTorch configuration for Hydra apps. +* NVIDIA's DeepLearningExamples repository contains a Hydra Launcher plugin, the [distributed_launcher](https://github.com/NVIDIA/DeepLearningExamples/tree/9c34e35c218514b8607d7cf381d8a982a01175e9/Tools/PyTorch/TimeSeriesPredictionPlatform/distributed_launcher), which makes use of the pytorch [distributed.launch](https://pytorch.org/docs/stable/distributed.html#launch-utility) API. + +#### Ask questions in Github Discussions or StackOverflow (Use the tag #fb-hydra or #omegaconf): +* [Github Discussions](https://github.com/facebookresearch/hydra/discussions) +* [StackOverflow](https://stackexchange.com/filters/391828/hydra-questions) +* [Twitter](https://twitter.com/Hydra_Framework) + +Check out the Meta AI [blog post](https://ai.facebook.com/blog/reengineering-facebook-ais-deep-learning-platforms-for-interoperability/) to learn about how Hydra fits into Meta's efforts to reengineer deep learning platforms for interoperability. + +### Citing Hydra +If you use Hydra in your research please use the following BibTeX entry: +```BibTeX +@Misc{Yadan2019Hydra, + author = {Omry Yadan}, + title = {Hydra - A framework for elegantly configuring complex applications}, + howpublished = {Github}, + year = {2019}, + url = {https://github.com/facebookresearch/hydra} +} +``` + diff --git a/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/RECORD b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..265bfb62113a44b31fb072100c3d425022541948 --- /dev/null +++ b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/RECORD @@ -0,0 +1,229 @@ +hydra/__init__.py,sha256=RfXj7duXieCLncBhvAP-dK_yiBX7AP1c5OT1ZJ4mzqQ,586 +hydra/__pycache__/__init__.cpython-312.pyc,, +hydra/__pycache__/compose.cpython-312.pyc,, +hydra/__pycache__/errors.cpython-312.pyc,, +hydra/__pycache__/initialize.cpython-312.pyc,, +hydra/__pycache__/main.cpython-312.pyc,, +hydra/__pycache__/types.cpython-312.pyc,, +hydra/__pycache__/utils.cpython-312.pyc,, +hydra/__pycache__/version.cpython-312.pyc,, +hydra/_internal/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/_internal/__pycache__/__init__.cpython-312.pyc,, +hydra/_internal/__pycache__/callbacks.cpython-312.pyc,, +hydra/_internal/__pycache__/config_loader_impl.cpython-312.pyc,, +hydra/_internal/__pycache__/config_repository.cpython-312.pyc,, +hydra/_internal/__pycache__/config_search_path_impl.cpython-312.pyc,, +hydra/_internal/__pycache__/defaults_list.cpython-312.pyc,, +hydra/_internal/__pycache__/deprecation_warning.cpython-312.pyc,, +hydra/_internal/__pycache__/hydra.cpython-312.pyc,, +hydra/_internal/__pycache__/sources_registry.cpython-312.pyc,, +hydra/_internal/__pycache__/utils.cpython-312.pyc,, +hydra/_internal/callbacks.py,sha256=8oihvw_Qgjl462wvC-51IymMlDw92vJ326WUhnIflH8,2310 +hydra/_internal/config_loader_impl.py,sha256=pfp4-GhTZUmQj33IvdqbllTEUDAd41mobB0aO7S4Aik,23925 +hydra/_internal/config_repository.py,sha256=xFZxs-OrN9EWT9gInHO-2RWIqv2VD02J74utekteEsU,13541 +hydra/_internal/config_search_path_impl.py,sha256=EZNyyd4bupjj6BZkRcG6Sf_lHrY6B-rZwS9Ggy1_7LU,3565 +hydra/_internal/core_plugins/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/_internal/core_plugins/__pycache__/__init__.cpython-312.pyc,, +hydra/_internal/core_plugins/__pycache__/bash_completion.cpython-312.pyc,, +hydra/_internal/core_plugins/__pycache__/basic_launcher.cpython-312.pyc,, +hydra/_internal/core_plugins/__pycache__/basic_sweeper.cpython-312.pyc,, +hydra/_internal/core_plugins/__pycache__/file_config_source.cpython-312.pyc,, +hydra/_internal/core_plugins/__pycache__/fish_completion.cpython-312.pyc,, +hydra/_internal/core_plugins/__pycache__/importlib_resources_config_source.cpython-312.pyc,, +hydra/_internal/core_plugins/__pycache__/structured_config_source.cpython-312.pyc,, +hydra/_internal/core_plugins/__pycache__/zsh_completion.cpython-312.pyc,, +hydra/_internal/core_plugins/bash_completion.py,sha256=Cat1tWgNyO2XCx2hmJ9ccT7igmAXnZd5tS0Kjd-yBuI,2957 +hydra/_internal/core_plugins/basic_launcher.py,sha256=pwa-Fkyguojr7EhL9rTM-JKSLASJ_VDwos4_W-OVux8,2755 +hydra/_internal/core_plugins/basic_sweeper.py,sha256=08K6gKtgb5XOStc4RgZ0Qi1w2eO4Q_DDIFmyQhgSigc,6580 +hydra/_internal/core_plugins/file_config_source.py,sha256=DXFmLksisy-lUK1FV8rkkww7C8CkSY3gymI6DVRtX3w,2317 +hydra/_internal/core_plugins/fish_completion.py,sha256=-koFBCRDZuUdqa_4sX6rNcHAo8e4VDLOyQmBtMHYpIM,2395 +hydra/_internal/core_plugins/importlib_resources_config_source.py,sha256=1hNPJ_Bx56qqaUhk9297iji5bOkdjUDOYL457GOmgyw,3686 +hydra/_internal/core_plugins/structured_config_source.py,sha256=Rfnn2JQEIrA5JWkT2WtpMLAl8xXVKVd_oaUduomippw,2348 +hydra/_internal/core_plugins/zsh_completion.py,sha256=QyJYgft0iFi5oCMA67b10N1v0DOzLypII5gPjIA-a5A,1564 +hydra/_internal/defaults_list.py,sha256=P8t0hgLaxeTKOyVJEZgdSdci3ojXhggl__aasNVEvF0,27080 +hydra/_internal/deprecation_warning.py,sha256=LnpqHkxTd-nCol4ZRsSsIWvmw3QmXW0640Q6AH76EmI,435 +hydra/_internal/grammar/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/_internal/grammar/__pycache__/__init__.cpython-312.pyc,, +hydra/_internal/grammar/__pycache__/functions.cpython-312.pyc,, +hydra/_internal/grammar/__pycache__/grammar_functions.cpython-312.pyc,, +hydra/_internal/grammar/__pycache__/utils.cpython-312.pyc,, +hydra/_internal/grammar/functions.py,sha256=qqH0PUgKCsTbucVB-HaJurm7kofhqKhXSYwMcD65hY0,2657 +hydra/_internal/grammar/grammar_functions.py,sha256=Tm41GXsVVRXr7t8PNAlvtuHPUTdYPXV32q04BfFdhOg,11797 +hydra/_internal/grammar/utils.py,sha256=yAjqmRXhub_TyUFLwRPWdIMqzvEBIgiX7mM-MnKaRlA,2133 +hydra/_internal/hydra.py,sha256=dLGZgMjrn-68mrF_yBcgoaCnZYbuHUWdr4Zwk4LDT4A,24147 +hydra/_internal/instantiate/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/_internal/instantiate/__pycache__/__init__.cpython-312.pyc,, +hydra/_internal/instantiate/__pycache__/_instantiate2.cpython-312.pyc,, +hydra/_internal/instantiate/_instantiate2.py,sha256=nk1NlHv-5M4uLfJunM_2sPIith5fTIIJb5Pl-irgv_M,14582 +hydra/_internal/sources_registry.py,sha256=a4rbFFdJjUWNJeqMqSq7XDI4gA4f4qcvZ40nsev3zro,1298 +hydra/_internal/utils.py,sha256=fXv__IqF6nHyO4IDfdYrAjgJw8yUb0_KUuwYt8AHRRs,22368 +hydra/compose.py,sha256=l1yF3TNFc3-7gzUYNO1DG6N2MO8hG5Yn0ibv22T8sAg,2105 +hydra/conf/__init__.py,sha256=SJas-v9d3FLB4MoY2_ZgixQH0z2hrTZCNcJ0SC4W5sA,5367 +hydra/conf/__pycache__/__init__.cpython-312.pyc,, +hydra/conf/hydra/env/default.yaml,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +hydra/conf/hydra/help/default.yaml,sha256=AUD1lPqLfdy2r53PCTFHNUuowx9jJMIRbC4lgvHCbH8,849 +hydra/conf/hydra/hydra_help/default.yaml,sha256=ze_nZtRYnL9NbKsyVuZPMlOopBCA_KhA4Ef0yzzh_Wo,642 +hydra/conf/hydra/hydra_logging/default.yaml,sha256=MeOAlUeXmwvu_CuhjQ9VxAFMjeopDm8_9ra2vp-B3BA,306 +hydra/conf/hydra/hydra_logging/disabled.yaml,sha256=mRf0I709p7OCmm0y5QTxMKwQumK13Ma-N4VmTXe_yu4,63 +hydra/conf/hydra/hydra_logging/hydra_debug.yaml,sha256=xA2oHZaKsrhjeMq7w1KWOnGdQeIhAxyh_qFO_sPZ8BQ,453 +hydra/conf/hydra/hydra_logging/none.yaml,sha256=gSCRtxF28DB3-gedm1zek7kiN8kyaNYD_m6tRJUaol4,54 +hydra/conf/hydra/job_logging/default.yaml,sha256=7fW0NU3vyIKV6HbtiLuCLCT0gtk3y-QXgwBsCPW2ooA,477 +hydra/conf/hydra/job_logging/disabled.yaml,sha256=mRf0I709p7OCmm0y5QTxMKwQumK13Ma-N4VmTXe_yu4,63 +hydra/conf/hydra/job_logging/none.yaml,sha256=gSCRtxF28DB3-gedm1zek7kiN8kyaNYD_m6tRJUaol4,54 +hydra/conf/hydra/job_logging/stdout.yaml,sha256=7hHFp_oMHxrMKILBbc2fJySl_7y2u_9RbEentTs2zRY,280 +hydra/conf/hydra/output/default.yaml,sha256=reeuSYxBy0_oqnzUM9sSULVAzjb8p7IsxJBHUccxtD8,152 +hydra/core/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/core/__pycache__/__init__.cpython-312.pyc,, +hydra/core/__pycache__/config_loader.cpython-312.pyc,, +hydra/core/__pycache__/config_search_path.cpython-312.pyc,, +hydra/core/__pycache__/config_store.cpython-312.pyc,, +hydra/core/__pycache__/default_element.cpython-312.pyc,, +hydra/core/__pycache__/global_hydra.cpython-312.pyc,, +hydra/core/__pycache__/hydra_config.cpython-312.pyc,, +hydra/core/__pycache__/object_type.cpython-312.pyc,, +hydra/core/__pycache__/plugins.cpython-312.pyc,, +hydra/core/__pycache__/singleton.cpython-312.pyc,, +hydra/core/__pycache__/utils.cpython-312.pyc,, +hydra/core/config_loader.py,sha256=Tad4xnUwFilgY26eDyW2ufKTfH3unABQRyZf3GHlVAc,1577 +hydra/core/config_search_path.py,sha256=VGfSUlZTuE1XzMFoTluk6TflRWilMC237c3HIuDZa5U,2070 +hydra/core/config_store.py,sha256=jricEy6sBv-IfOxBrv4oYxHR1Afl9aAfiiqdtcoFaN0,4609 +hydra/core/default_element.py,sha256=dWnHKVU7L9C2n0uQevrgpXnGXG3nGPrw8yFACFujyrA,18196 +hydra/core/global_hydra.py,sha256=iEap-YFkxNhkbjuu5vElm7-nWpAmyB9x8Yk420YFsOc,1324 +hydra/core/hydra_config.py,sha256=H6kHPrsG8Cuyd8f4vvrWhqS4RTW1RcK-pdSUodPJnPI,1561 +hydra/core/object_type.py,sha256=usNw3bC9dhY0cNCRgjd0V8rKka2KswBDukSWGM9uVgk,166 +hydra/core/override_parser/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/core/override_parser/__pycache__/__init__.cpython-312.pyc,, +hydra/core/override_parser/__pycache__/overrides_parser.cpython-312.pyc,, +hydra/core/override_parser/__pycache__/overrides_visitor.cpython-312.pyc,, +hydra/core/override_parser/__pycache__/types.cpython-312.pyc,, +hydra/core/override_parser/overrides_parser.py,sha256=nK8k8aDznA6zozWdSih8KIJN5qKUJadFLuW8SqQveno,4720 +hydra/core/override_parser/overrides_visitor.py,sha256=Mz3LJfUUMad5oDYPvJ4ikd3VF2g6SwpWF1-HknxAUUM,15505 +hydra/core/override_parser/types.py,sha256=1Yn1g1tap6uw4NdSr4nwMDwoqkQ3TCBUkyy1Bk6CELc,15969 +hydra/core/plugins.py,sha256=uwNG8A-68rAHUybO2rPimnmn94JTRU4FxfrQAgS3vr0,10390 +hydra/core/singleton.py,sha256=JFVdknkZ57Q91clfnvNUjOL0k85ksOXfTgFozBjbwTk,1338 +hydra/core/utils.py,sha256=vetFUh9jAa-Zfx-XetioCKcSDf7jk4mJkVSY8sjMONg,10502 +hydra/errors.py,sha256=UUYsCUnmruoxXJ4rdEZzEEhGlmO0XpmitPV52rWgCj4,1071 +hydra/experimental/__init__.py,sha256=LXsinWfSh6NWcg5ykYlh39w6YP4K0eTL66enyb5msx4,293 +hydra/experimental/__pycache__/__init__.cpython-312.pyc,, +hydra/experimental/__pycache__/callback.cpython-312.pyc,, +hydra/experimental/__pycache__/callbacks.cpython-312.pyc,, +hydra/experimental/__pycache__/compose.cpython-312.pyc,, +hydra/experimental/__pycache__/initialize.cpython-312.pyc,, +hydra/experimental/callback.py,sha256=C81NmVIkrO_x_Hx6cP9Gk2Bha1eZUZcUFqQ7h5jt5Lw,2378 +hydra/experimental/callbacks.py,sha256=hNeJHD6P97RCuWKSdh2PQM_0RAnTApWS6-5itREpWQY,2336 +hydra/experimental/compose.py,sha256=1R8-RGoR8ShhL6XFOgSjtoYsMw2X3x99fOsER13JspY,845 +hydra/experimental/initialize.py,sha256=qRJgHq3ny53fWyMg2WRRs6mTVMJveh-uDafNOtd-sKE,4280 +hydra/extra/__pycache__/pytest_plugin.cpython-312.pyc,, +hydra/extra/pytest_plugin.py,sha256=XwdmKS_Xat8PgtXcZGsLECC_KV17SlWB5HIjgytEwlQ,2612 +hydra/grammar/.gitignore,sha256=JvbRQEPLotalz9jq9ech5OCzehPqN1O-hKph7r5X0X8,21 +hydra/grammar/OverrideLexer.g4,sha256=SLf4ErlQqLhuqYcxm1-csIdcu-60bvVxSpiZ5EVnlEY,2825 +hydra/grammar/OverrideParser.g4,sha256=3_p5QSlZ8KloLGKQecC0_rZlFGe6t3cfLQz-EMHwiB8,3115 +hydra/grammar/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/grammar/__pycache__/__init__.cpython-312.pyc,, +hydra/grammar/gen/.gitignore,sha256=vdSMzqaxj4aI7cH5OV3jvNZV3zoOSCfHoloOw1mjc1g,15 +hydra/grammar/gen/OverrideLexer.interp,sha256=CAB_gEC1QNK8Z2PWuVfqdtJ2JB8UohDbFU9gIOCgJi8,14138 +hydra/grammar/gen/OverrideLexer.py,sha256=QnpWqy5IwvVEwj6lUMtCQNOX7TTAxyuIiViIQpcb3n4,16474 +hydra/grammar/gen/OverrideLexer.tokens,sha256=ACZgg3CkJwkQxl_n0s6d_3rRUzkbHEchRf7FHa2xZoQ,289 +hydra/grammar/gen/OverrideParser.interp,sha256=Xw3O4PVsBEG721C5fZHbRaNDXYYnNucMfYBnlh6nipI,5685 +hydra/grammar/gen/OverrideParser.py,sha256=EUpl4DORSkb_uOk40yISotNgzm9PBmc4D37KcOurmPU,50516 +hydra/grammar/gen/OverrideParser.tokens,sha256=ACZgg3CkJwkQxl_n0s6d_3rRUzkbHEchRf7FHa2xZoQ,289 +hydra/grammar/gen/OverrideParserListener.py,sha256=ebXxzEWA3YV3Z6duMMb4OljCOSSpR2gGhWztc8IL-iM,4476 +hydra/grammar/gen/OverrideParserVisitor.py,sha256=N6Ga-PNK5KM0s9YLPXO07k0dCk-2r5kUMUzh0ulBpeM,2834 +hydra/grammar/gen/__pycache__/OverrideLexer.cpython-312.pyc,, +hydra/grammar/gen/__pycache__/OverrideParser.cpython-312.pyc,, +hydra/grammar/gen/__pycache__/OverrideParserListener.cpython-312.pyc,, +hydra/grammar/gen/__pycache__/OverrideParserVisitor.cpython-312.pyc,, +hydra/initialize.py,sha256=HgB87jmyNJwImdGQ1xyw_B1z_Hd8QsIwEUFWOBgy6Q4,6035 +hydra/main.py,sha256=t6_RYPzn-nrp_6Rae3dVxONXVu4OVgNNtS9q2OKLcOA,3999 +hydra/plugins/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/plugins/__pycache__/__init__.cpython-312.pyc,, +hydra/plugins/__pycache__/completion_plugin.cpython-312.pyc,, +hydra/plugins/__pycache__/config_source.cpython-312.pyc,, +hydra/plugins/__pycache__/launcher.cpython-312.pyc,, +hydra/plugins/__pycache__/plugin.cpython-312.pyc,, +hydra/plugins/__pycache__/search_path_plugin.cpython-312.pyc,, +hydra/plugins/__pycache__/sweeper.cpython-312.pyc,, +hydra/plugins/completion_plugin.py,sha256=_1VjimIuA0QDJm_LI98SoOPpi_L6Oad8ljP5FRbAU-I,11275 +hydra/plugins/config_source.py,sha256=W1E60f-J18e_29G6PV32P-MlWAY438uZGbjMkQHMtHQ,5109 +hydra/plugins/launcher.py,sha256=v1ojrXmKmG69lbubCppepJt6URDJwFnhO4gTTZuo6BM,974 +hydra/plugins/plugin.py,sha256=eTvzaLxlJCmbMbroP9Z1u8wxNWa4R0qPAGqLzh6WM7M,120 +hydra/plugins/search_path_plugin.py,sha256=bn78U0cxqP2QZUkCh23LbtR0rDthewm8iOCGfv5gcF8,333 +hydra/plugins/sweeper.py,sha256=J6fVEpbiiDxHCLadLX1wDX3484oxYoDv0q0Jv9TNfaY,2108 +hydra/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +hydra/test_utils/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/test_utils/__pycache__/__init__.cpython-312.pyc,, +hydra/test_utils/__pycache__/a_module.cpython-312.pyc,, +hydra/test_utils/__pycache__/completion.cpython-312.pyc,, +hydra/test_utils/__pycache__/config_source_common_tests.cpython-312.pyc,, +hydra/test_utils/__pycache__/example_app.cpython-312.pyc,, +hydra/test_utils/__pycache__/launcher_common_tests.cpython-312.pyc,, +hydra/test_utils/__pycache__/test_utils.cpython-312.pyc,, +hydra/test_utils/a_module.py,sha256=QHSQjj55BP3g0uz7ESTGNn_p6TJirYdFer8Xbh5M6Wg,228 +hydra/test_utils/completion.py,sha256=ctgyUAoLRKQ05BrNweexBvA-9aeITu1HrUFAtfIrCi0,344 +hydra/test_utils/config_source_common_tests.py,sha256=zWi3KaWhEJ76EnN-5Q6qiuoNvqUpM5LlbzvgOJI1YLk,10900 +hydra/test_utils/configs/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/test_utils/configs/__pycache__/__init__.cpython-312.pyc,, +hydra/test_utils/configs/accessing_hydra_config.yaml,sha256=f8yFOBhVJ3I0hga2uDxi-xHk7QUqIuYozrOIkrvh4G8,92 +hydra/test_utils/configs/completion_test/additional_searchpath.yaml,sha256=RGca4IOnI2g0ha3W29uMBrw4uKKaaAu-r43SFKpb3JY,201 +hydra/test_utils/configs/completion_test/config.yaml,sha256=onD8qwP45YO6uUiaNK6sdQYteWtCeu6stg_JUxWc4Zs,276 +hydra/test_utils/configs/completion_test/group/dict.yaml,sha256=oWGInDFSV2mNHsuy5WaltWR-0jzJr5OsgVvdx6AxgS8,117 +hydra/test_utils/configs/completion_test/group/list.yaml,sha256=2-PX667W1Zto3zIrEvYD4UTH9jEtWR6FEIthukyiiRs,106 +hydra/test_utils/configs/completion_test/hydra/launcher/fairtask.yaml,sha256=5d7flmlKaueWrLwxJRrly8h8Yu14z-zmEH6oVcUAB6w,44 +hydra/test_utils/configs/completion_test/missing_default.yaml,sha256=BXNHsVCvz997Bd1JV43lhwlzYOGDnGA7hcA5AGcXMks,25 +hydra/test_utils/configs/completion_test/test_hydra/launcher/fairtask.yaml,sha256=5d7flmlKaueWrLwxJRrly8h8Yu14z-zmEH6oVcUAB6w,44 +hydra/test_utils/configs/completion_test_additional_file/additional_group/file_opt_additional.yaml,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +hydra/test_utils/configs/completion_test_additional_file/group/file_opt.yaml,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +hydra/test_utils/configs/completion_test_additional_package/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/test_utils/configs/completion_test_additional_package/__pycache__/__init__.cpython-312.pyc,, +hydra/test_utils/configs/completion_test_additional_package/additional_group/pkg_opt_additional.yaml,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +hydra/test_utils/configs/completion_test_additional_package/group/pkg_opt.yaml,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +hydra/test_utils/configs/compose.yaml,sha256=PbvagKNJBdD8K9iV5kHd1k0wSYUPxfCjTv5rlkpiAwY,66 +hydra/test_utils/configs/conf.zip,sha256=XEcej6nwGh_GVQdJliLaZt_yvpbxv40AlH8UMyJwpLs,210 +hydra/test_utils/configs/config.yaml,sha256=jvSKJD57W9fusuDdeWzGMmEkue2eB4Y8z67YK4R1fo0,45 +hydra/test_utils/configs/config.yml,sha256=OXO4U6morVmVmXOIP1_09a7VaWKWLK_8itBFpdBIXLY,55 +hydra/test_utils/configs/custom_resolver.yaml,sha256=1vVJqYjuhrLZljWaZSiRldUNIclX01dup0C-wfwXhnk,26 +hydra/test_utils/configs/db/mysql.yaml,sha256=OhvOLGF11LrYqD7F4hElfJDGWrRAiN2P5FzzebYvgWM,42 +hydra/test_utils/configs/db/postgresql.yaml,sha256=YLxrF76CCl4ezdCkxnVAH2xHS6w_1-R2pko748n7B_c,70 +hydra/test_utils/configs/db/validated_mysql.yaml,sha256=4t357FDiODkD7eY1YveNsbuiLpl6tLnh3zQH9viHCYk,68 +hydra/test_utils/configs/db/validated_postgresql.yaml,sha256=NaMWjp4CC_M2Ef5njP0g-5F7docESNDRJ9q66Rig0G8,101 +hydra/test_utils/configs/db_conf.yaml,sha256=EwJbm3o9wfYfPc5WdU6anorcatikENpMSOrLYMSPe5c,24 +hydra/test_utils/configs/defaults_not_list.yaml,sha256=TGijnGK1kGosfiL11q6QCBaOd1h5AoYqRwW4rm4srY4,41 +hydra/test_utils/configs/group1/abc.cde.yaml,sha256=38OuMSH0NPr_GHZLyRZj_MKQ3Eh6DkmimFSH66_3Y8E,28 +hydra/test_utils/configs/group1/file1.yaml,sha256=YQt7eI9ChNBTwhWz6TaMal5wEbrEwYIHx65Vy1eJoPU,28 +hydra/test_utils/configs/group1/file2.yaml,sha256=wAju0MHYv7R9IrF4MukME4pqGq0TsEddGfu1mghEOE8,28 +hydra/test_utils/configs/group2/file1.yaml,sha256=1nGQpv2YZPZBARi-PBsBxM9USDisHC9gS3idm1xxU4c,29 +hydra/test_utils/configs/group2/file2.yaml,sha256=CxJUCyfUgvv129J-CuEQobot0H0Y0W9yq_hf7fjdcSk,29 +hydra/test_utils/configs/missing-default.yaml,sha256=h_tYW2f9FHU194574y4nW1eTcOqRJ2NNFdWWD74Ztb8,25 +hydra/test_utils/configs/missing-optional-default.yaml,sha256=SnHTJeh9op11czTFCIoMFwCci9G3Wtr6S-1DQAHqxbs,36 +hydra/test_utils/configs/missing_init_py/.gitignore,sha256=kCpRPdl3S_jqYYZaOrc0-xa6-l3KqVjNRXc6jCkd_-Q,12 +hydra/test_utils/configs/missing_init_py/test.yaml,sha256=RPxWgmo0Tv8QFgW5AMXByB91h7259MlDoTZKqrWso-I,6 +hydra/test_utils/configs/optional-default.yaml,sha256=jwH3O7Wn5fTdDYpXzCMFxSY8KEkU2bJPSZ3d_wLaF3Y,37 +hydra/test_utils/configs/overriding_output_dir.yaml,sha256=30luCOSutKoCnSKOf0ixoUkRagpUejbserz4VH5mEgk,27 +hydra/test_utils/configs/overriding_run_dir.yaml,sha256=qkstQzIBVkqOJFlziQdurKeNUshx3WoJZG13WsVg1js,27 +hydra/test_utils/configs/package_tests/__init__.py,sha256=nlNcRa8JrCTvjVYxoVH3f-uv5yqSrWv-stlB8MSlTjU,71 +hydra/test_utils/configs/package_tests/__pycache__/__init__.cpython-312.pyc,, +hydra/test_utils/configs/package_tests/group1/option1.yaml,sha256=IvqLmZEXrlkuj0ZTGo5NzPGfi2UxXKUAmKx_xVlpHt4,41 +hydra/test_utils/configs/package_tests/group1/option2.yaml,sha256=ZY-wEvZgZ9nQwB8oytNd_0uzgoxqUk7sza5f1PC4RLI,41 +hydra/test_utils/configs/package_tests/group2/option1.yaml,sha256=mPUhhwInAsKJEA1Z9laGXcbi8y36vPlkqP9LTexQwwc,41 +hydra/test_utils/configs/package_tests/group2/option2.yaml,sha256=LLBCZrMTnfsAFNaG2uOORaHm8lmbMWKtTzDpQFDkNdw,41 +hydra/test_utils/configs/package_tests/pkg_override.yaml,sha256=K7UZYvrYuPJnvVbC6ukhJDWvoQfSV84l3iUFWXs1Psw,55 +hydra/test_utils/configs/package_tests/two_packages_one_group.yaml,sha256=D8qyM20PXYvYC43lokYet8WdknJG-itUnYfDD9okWRI,60 +hydra/test_utils/configs/schema_key_error.yaml,sha256=BgPV_otZ8OxZfcn3xvSquLGdxM7f44KWNPJ2jFcxafU,56 +hydra/test_utils/configs/schema_validation_error.yaml,sha256=T61LUtehGtAMU_t44J9btkD6vxjDikdNGWcS2AKGHjY,54 +hydra/test_utils/configs/some_config.yaml,sha256=WfxBETosvbSbpDYGusT2KQk2wgHOaCo0WaXBGUzU3oI,38 +hydra/test_utils/configs/top_level_list/file1.yaml,sha256=JPFXvrO7aYFybvktEExKoefg40ux6Eo7IzoCCpY9_VM,4 +hydra/test_utils/configs/unspecified_mandatory_default.yaml,sha256=UOFn7b-MBSvyuuAoco3t3uEHDMWiTxgM5-iOGhcVejQ,26 +hydra/test_utils/example_app.py,sha256=EeviU2MudEMera8AfGJ1wQ5WyWqwu6NQ7GF4KvVpzFo,323 +hydra/test_utils/launcher_common_tests.py,sha256=JE-a9e6QzzwIEwGBCxn501HTOvQOl1054CzgL8bOUiw,23552 +hydra/test_utils/test_utils.py,sha256=jL_MWw3nuII4JrS_GOoPHD0OFcgpx2ezmAAy2aSD7hU,15335 +hydra/types.py,sha256=ek7sEeAiaDpkVmndhHFUXSQIJnVKi4IqUf-JYhy86xY,2913 +hydra/utils.py,sha256=IIILg5TmAZi4F_if49eliLZ3SOxY95ey5wKQ_DPGAs0,3272 +hydra/version.py,sha256=07N_T8N48zL9VAifYpP39T2UZ_U15ttnlysdGWDKoh4,2573 +hydra_core-1.3.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +hydra_core-1.3.2.dist-info/LICENSE,sha256=UkEte8fOQVfqYou6rLiCngqcs8WPV_mRdhJryM8r_IU,1086 +hydra_core-1.3.2.dist-info/METADATA,sha256=OZBGy_mufrq439AJ4rT3SCEscQoOdcpQGnK7stRW4uc,5480 +hydra_core-1.3.2.dist-info/RECORD,, +hydra_core-1.3.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +hydra_core-1.3.2.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +hydra_core-1.3.2.dist-info/entry_points.txt,sha256=rDwQ60t46fQ9gkvlsjN-RvQcJdbDUhRdSxPANxU_32E,52 +hydra_core-1.3.2.dist-info/top_level.txt,sha256=izgzyEMiRL3xiwDnTKYJY7UL0mWcGbI7tqJ9o3FMbQI,6 diff --git a/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/REQUESTED b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/WHEEL b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..57e3d840d59a650ac5bccbad5baeec47d155f0ad --- /dev/null +++ b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/entry_points.txt b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..9fdfdc88316d9a34270bff43520752ae1a06f636 --- /dev/null +++ b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[pytest11] +hydra_pytest = hydra.extra.pytest_plugin diff --git a/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/top_level.txt b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..83f02065ddfc215f8f31d8b9c7fbdf33aa6031b4 --- /dev/null +++ b/lib/python3.12/site-packages/hydra_core-1.3.2.dist-info/top_level.txt @@ -0,0 +1 @@ +hydra diff --git a/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/INSTALLER b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/License.txt b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/License.txt new file mode 100644 index 0000000000000000000000000000000000000000..b491c70e0aef319022ded661e111ddbd45b8a17f --- /dev/null +++ b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/License.txt @@ -0,0 +1,1568 @@ +End User License Agreement +-------------------------- + + +Preface +------- + +The Software License Agreement in Chapter 1 and the Supplement +in Chapter 2 contain license terms and conditions that govern +the use of NVIDIA software. By accepting this agreement, you +agree to comply with all the terms and conditions applicable +to the product(s) included herein. + + +NVIDIA Driver + + +Description + +This package contains the operating system driver and +fundamental system software components for NVIDIA GPUs. + + +NVIDIA CUDA Toolkit + + +Description + +The NVIDIA CUDA Toolkit provides command-line and graphical +tools for building, debugging and optimizing the performance +of applications accelerated by NVIDIA GPUs, runtime and math +libraries, and documentation including programming guides, +user manuals, and API references. + + +Default Install Location of CUDA Toolkit + +Windows platform: + +%ProgramFiles%\NVIDIA GPU Computing Toolkit\CUDA\v#.# + +Linux platform: + +/usr/local/cuda-#.# + +Mac platform: + +/Developer/NVIDIA/CUDA-#.# + + +NVIDIA CUDA Samples + + +Description + +This package includes over 100+ CUDA examples that demonstrate +various CUDA programming principles, and efficient CUDA +implementation of algorithms in specific application domains. + + +Default Install Location of CUDA Samples + +Windows platform: + +%ProgramData%\NVIDIA Corporation\CUDA Samples\v#.# + +Linux platform: + +/usr/local/cuda-#.#/samples + +and + +$HOME/NVIDIA_CUDA-#.#_Samples + +Mac platform: + +/Developer/NVIDIA/CUDA-#.#/samples + + +NVIDIA Nsight Visual Studio Edition (Windows only) + + +Description + +NVIDIA Nsight Development Platform, Visual Studio Edition is a +development environment integrated into Microsoft Visual +Studio that provides tools for debugging, profiling, analyzing +and optimizing your GPU computing and graphics applications. + + +Default Install Location of Nsight Visual Studio Edition + +Windows platform: + +%ProgramFiles(x86)%\NVIDIA Corporation\Nsight Visual Studio Edition #.# + + +1. License Agreement for NVIDIA Software Development Kits +--------------------------------------------------------- + + +Release Date: July 26, 2018 +--------------------------- + + +Important NoticeRead before downloading, installing, +copying or using the licensed software: +------------------------------------------------------- + +This license agreement, including exhibits attached +("Agreement”) is a legal agreement between you and NVIDIA +Corporation ("NVIDIA") and governs your use of a NVIDIA +software development kit (“SDK”). + +Each SDK has its own set of software and materials, but here +is a description of the types of items that may be included in +a SDK: source code, header files, APIs, data sets and assets +(examples include images, textures, models, scenes, videos, +native API input/output files), binary software, sample code, +libraries, utility programs, programming code and +documentation. + +This Agreement can be accepted only by an adult of legal age +of majority in the country in which the SDK is used. + +If you are entering into this Agreement on behalf of a company +or other legal entity, you represent that you have the legal +authority to bind the entity to this Agreement, in which case +“you” will mean the entity you represent. + +If you don’t have the required age or authority to accept +this Agreement, or if you don’t accept all the terms and +conditions of this Agreement, do not download, install or use +the SDK. + +You agree to use the SDK only for purposes that are permitted +by (a) this Agreement, and (b) any applicable law, regulation +or generally accepted practices or guidelines in the relevant +jurisdictions. + + +1.1. License + + +1.1.1. License Grant + +Subject to the terms of this Agreement, NVIDIA hereby grants +you a non-exclusive, non-transferable license, without the +right to sublicense (except as expressly provided in this +Agreement) to: + + 1. Install and use the SDK, + + 2. Modify and create derivative works of sample source code + delivered in the SDK, and + + 3. Distribute those portions of the SDK that are identified + in this Agreement as distributable, as incorporated in + object code format into a software application that meets + the distribution requirements indicated in this Agreement. + + +1.1.2. Distribution Requirements + +These are the distribution requirements for you to exercise +the distribution grant: + + 1. Your application must have material additional + functionality, beyond the included portions of the SDK. + + 2. The distributable portions of the SDK shall only be + accessed by your application. + + 3. The following notice shall be included in modifications + and derivative works of sample source code distributed: + “This software contains source code provided by NVIDIA + Corporation.” + + 4. Unless a developer tool is identified in this Agreement + as distributable, it is delivered for your internal use + only. + + 5. The terms under which you distribute your application + must be consistent with the terms of this Agreement, + including (without limitation) terms relating to the + license grant and license restrictions and protection of + NVIDIA’s intellectual property rights. Additionally, you + agree that you will protect the privacy, security and + legal rights of your application users. + + 6. You agree to notify NVIDIA in writing of any known or + suspected distribution or use of the SDK not in compliance + with the requirements of this Agreement, and to enforce + the terms of your agreements with respect to distributed + SDK. + + +1.1.3. Authorized Users + +You may allow employees and contractors of your entity or of +your subsidiary(ies) to access and use the SDK from your +secure network to perform work on your behalf. + +If you are an academic institution you may allow users +enrolled or employed by the academic institution to access and +use the SDK from your secure network. + +You are responsible for the compliance with the terms of this +Agreement by your authorized users. If you become aware that +your authorized users didn’t follow the terms of this +Agreement, you agree to take reasonable steps to resolve the +non-compliance and prevent new occurrences. + + +1.1.4. Pre-Release SDK + +The SDK versions identified as alpha, beta, preview or +otherwise as pre-release, may not be fully functional, may +contain errors or design flaws, and may have reduced or +different security, privacy, accessibility, availability, and +reliability standards relative to commercial versions of +NVIDIA software and materials. Use of a pre-release SDK may +result in unexpected results, loss of data, project delays or +other unpredictable damage or loss. + +You may use a pre-release SDK at your own risk, understanding +that pre-release SDKs are not intended for use in production +or business-critical systems. + +NVIDIA may choose not to make available a commercial version +of any pre-release SDK. NVIDIA may also choose to abandon +development and terminate the availability of a pre-release +SDK at any time without liability. + + +1.1.5. Updates + +NVIDIA may, at its option, make available patches, workarounds +or other updates to this SDK. Unless the updates are provided +with their separate governing terms, they are deemed part of +the SDK licensed to you as provided in this Agreement. You +agree that the form and content of the SDK that NVIDIA +provides may change without prior notice to you. While NVIDIA +generally maintains compatibility between versions, NVIDIA may +in some cases make changes that introduce incompatibilities in +future versions of the SDK. + + +1.1.6. Third Party Licenses + +The SDK may come bundled with, or otherwise include or be +distributed with, third party software licensed by a NVIDIA +supplier and/or open source software provided under an open +source license. Use of third party software is subject to the +third-party license terms, or in the absence of third party +terms, the terms of this Agreement. Copyright to third party +software is held by the copyright holders indicated in the +third-party software or license. + + +1.1.7. Reservation of Rights + +NVIDIA reserves all rights, title, and interest in and to the +SDK, not expressly granted to you under this Agreement. + + +1.2. Limitations + +The following license limitations apply to your use of the +SDK: + + 1. You may not reverse engineer, decompile or disassemble, + or remove copyright or other proprietary notices from any + portion of the SDK or copies of the SDK. + + 2. Except as expressly provided in this Agreement, you may + not copy, sell, rent, sublicense, transfer, distribute, + modify, or create derivative works of any portion of the + SDK. For clarity, you may not distribute or sublicense the + SDK as a stand-alone product. + + 3. Unless you have an agreement with NVIDIA for this + purpose, you may not indicate that an application created + with the SDK is sponsored or endorsed by NVIDIA. + + 4. You may not bypass, disable, or circumvent any + encryption, security, digital rights management or + authentication mechanism in the SDK. + + 5. You may not use the SDK in any manner that would cause it + to become subject to an open source software license. As + examples, licenses that require as a condition of use, + modification, and/or distribution that the SDK be: + + a. Disclosed or distributed in source code form; + + b. Licensed for the purpose of making derivative works; + or + + c. Redistributable at no charge. + + 6. Unless you have an agreement with NVIDIA for this + purpose, you may not use the SDK with any system or + application where the use or failure of the system or + application can reasonably be expected to threaten or + result in personal injury, death, or catastrophic loss. + Examples include use in avionics, navigation, military, + medical, life support or other life critical applications. + NVIDIA does not design, test or manufacture the SDK for + these critical uses and NVIDIA shall not be liable to you + or any third party, in whole or in part, for any claims or + damages arising from such uses. + + 7. You agree to defend, indemnify and hold harmless NVIDIA + and its affiliates, and their respective employees, + contractors, agents, officers and directors, from and + against any and all claims, damages, obligations, losses, + liabilities, costs or debt, fines, restitutions and + expenses (including but not limited to attorney’s fees + and costs incident to establishing the right of + indemnification) arising out of or related to your use of + the SDK outside of the scope of this Agreement, or not in + compliance with its terms. + + +1.3. Ownership + + 1. NVIDIA or its licensors hold all rights, title and + interest in and to the SDK and its modifications and + derivative works, including their respective intellectual + property rights, subject to your rights described in this + section. This SDK may include software and materials from + NVIDIA’s licensors, and these licensors are intended + third party beneficiaries that may enforce this Agreement + with respect to their intellectual property rights. + + 2. You hold all rights, title and interest in and to your + applications and your derivative works of the sample + source code delivered in the SDK, including their + respective intellectual property rights, subject to + NVIDIA’s rights described in this section. + + 3. You may, but don’t have to, provide to NVIDIA + suggestions, feature requests or other feedback regarding + the SDK, including possible enhancements or modifications + to the SDK. For any feedback that you voluntarily provide, + you hereby grant NVIDIA and its affiliates a perpetual, + non-exclusive, worldwide, irrevocable license to use, + reproduce, modify, license, sublicense (through multiple + tiers of sublicensees), and distribute (through multiple + tiers of distributors) it without the payment of any + royalties or fees to you. NVIDIA will use feedback at its + choice. NVIDIA is constantly looking for ways to improve + its products, so you may send feedback to NVIDIA through + the developer portal at https://developer.nvidia.com. + + +1.4. No Warranties + +THE SDK IS PROVIDED BY NVIDIA “AS IS” AND “WITH ALL +FAULTS.” TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND +ITS AFFILIATES EXPRESSLY DISCLAIM ALL WARRANTIES OF ANY KIND +OR NATURE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, +BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, OR THE +ABSENCE OF ANY DEFECTS THEREIN, WHETHER LATENT OR PATENT. NO +WARRANTY IS MADE ON THE BASIS OF TRADE USAGE, COURSE OF +DEALING OR COURSE OF TRADE. + + +1.5. Limitation of Liability + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND ITS +AFFILIATES SHALL NOT BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +PUNITIVE OR CONSEQUENTIAL DAMAGES, OR ANY LOST PROFITS, LOSS +OF USE, LOSS OF DATA OR LOSS OF GOODWILL, OR THE COSTS OF +PROCURING SUBSTITUTE PRODUCTS, ARISING OUT OF OR IN CONNECTION +WITH THIS AGREEMENT OR THE USE OR PERFORMANCE OF THE SDK, +WHETHER SUCH LIABILITY ARISES FROM ANY CLAIM BASED UPON BREACH +OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE), +PRODUCT LIABILITY OR ANY OTHER CAUSE OF ACTION OR THEORY OF +LIABILITY. IN NO EVENT WILL NVIDIA’S AND ITS AFFILIATES +TOTAL CUMULATIVE LIABILITY UNDER OR ARISING OUT OF THIS +AGREEMENT EXCEED US$10.00. THE NATURE OF THE LIABILITY OR THE +NUMBER OF CLAIMS OR SUITS SHALL NOT ENLARGE OR EXTEND THIS +LIMIT. + +These exclusions and limitations of liability shall apply +regardless if NVIDIA or its affiliates have been advised of +the possibility of such damages, and regardless of whether a +remedy fails its essential purpose. These exclusions and +limitations of liability form an essential basis of the +bargain between the parties, and, absent any of these +exclusions or limitations of liability, the provisions of this +Agreement, including, without limitation, the economic terms, +would be substantially different. + + +1.6. Termination + + 1. This Agreement will continue to apply until terminated by + either you or NVIDIA as described below. + + 2. If you want to terminate this Agreement, you may do so by + stopping to use the SDK. + + 3. NVIDIA may, at any time, terminate this Agreement if: + + a. (i) you fail to comply with any term of this + Agreement and the non-compliance is not fixed within + thirty (30) days following notice from NVIDIA (or + immediately if you violate NVIDIA’s intellectual + property rights); + + b. (ii) you commence or participate in any legal + proceeding against NVIDIA with respect to the SDK; or + + c. (iii) NVIDIA decides to no longer provide the SDK in + a country or, in NVIDIA’s sole discretion, the + continued use of it is no longer commercially viable. + + 4. Upon any termination of this Agreement, you agree to + promptly discontinue use of the SDK and destroy all copies + in your possession or control. Your prior distributions in + accordance with this Agreement are not affected by the + termination of this Agreement. Upon written request, you + will certify in writing that you have complied with your + commitments under this section. Upon any termination of + this Agreement all provisions survive except for the + license grant provisions. + + +1.7. General + +If you wish to assign this Agreement or your rights and +obligations, including by merger, consolidation, dissolution +or operation of law, contact NVIDIA to ask for permission. Any +attempted assignment not approved by NVIDIA in writing shall +be void and of no effect. NVIDIA may assign, delegate or +transfer this Agreement and its rights and obligations, and if +to a non-affiliate you will be notified. + +You agree to cooperate with NVIDIA and provide reasonably +requested information to verify your compliance with this +Agreement. + +This Agreement will be governed in all respects by the laws of +the United States and of the State of Delaware as those laws +are applied to contracts entered into and performed entirely +within Delaware by Delaware residents, without regard to the +conflicts of laws principles. The United Nations Convention on +Contracts for the International Sale of Goods is specifically +disclaimed. You agree to all terms of this Agreement in the +English language. + +The state or federal courts residing in Santa Clara County, +California shall have exclusive jurisdiction over any dispute +or claim arising out of this Agreement. Notwithstanding this, +you agree that NVIDIA shall still be allowed to apply for +injunctive remedies or an equivalent type of urgent legal +relief in any jurisdiction. + +If any court of competent jurisdiction determines that any +provision of this Agreement is illegal, invalid or +unenforceable, such provision will be construed as limited to +the extent necessary to be consistent with and fully +enforceable under the law and the remaining provisions will +remain in full force and effect. Unless otherwise specified, +remedies are cumulative. + +Each party acknowledges and agrees that the other is an +independent contractor in the performance of this Agreement. + +The SDK has been developed entirely at private expense and is +“commercial items” consisting of “commercial computer +software” and “commercial computer software +documentation” provided with RESTRICTED RIGHTS. Use, +duplication or disclosure by the U.S. Government or a U.S. +Government subcontractor is subject to the restrictions in +this Agreement pursuant to DFARS 227.7202-3(a) or as set forth +in subparagraphs (c)(1) and (2) of the Commercial Computer +Software - Restricted Rights clause at FAR 52.227-19, as +applicable. Contractor/manufacturer is NVIDIA, 2788 San Tomas +Expressway, Santa Clara, CA 95051. + +The SDK is subject to United States export laws and +regulations. You agree that you will not ship, transfer or +export the SDK into any country, or use the SDK in any manner, +prohibited by the United States Bureau of Industry and +Security or economic sanctions regulations administered by the +U.S. Department of Treasury’s Office of Foreign Assets +Control (OFAC), or any applicable export laws, restrictions or +regulations. These laws include restrictions on destinations, +end users and end use. By accepting this Agreement, you +confirm that you are not a resident or citizen of any country +currently embargoed by the U.S. and that you are not otherwise +prohibited from receiving the SDK. + +Any notice delivered by NVIDIA to you under this Agreement +will be delivered via mail, email or fax. You agree that any +notices that NVIDIA sends you electronically will satisfy any +legal communication requirements. Please direct your legal +notices or other correspondence to NVIDIA Corporation, 2788 +San Tomas Expressway, Santa Clara, California 95051, United +States of America, Attention: Legal Department. + +This Agreement and any exhibits incorporated into this +Agreement constitute the entire agreement of the parties with +respect to the subject matter of this Agreement and supersede +all prior negotiations or documentation exchanged between the +parties relating to this SDK license. Any additional and/or +conflicting terms on documents issued by you are null, void, +and invalid. Any amendment or waiver under this Agreement +shall be in writing and signed by representatives of both +parties. + + +2. CUDA Toolkit Supplement to Software License Agreement for +NVIDIA Software Development Kits +------------------------------------------------------------ + + +Release date: August 16, 2018 +----------------------------- + +The terms in this supplement govern your use of the NVIDIA +CUDA Toolkit SDK under the terms of your license agreement +(“Agreement”) as modified by this supplement. Capitalized +terms used but not defined below have the meaning assigned to +them in the Agreement. + +This supplement is an exhibit to the Agreement and is +incorporated as an integral part of the Agreement. In the +event of conflict between the terms in this supplement and the +terms in the Agreement, the terms in this supplement govern. + + +2.1. License Scope + +The SDK is licensed for you to develop applications only for +use in systems with NVIDIA GPUs. + + +2.2. Distribution + +The portions of the SDK that are distributable under the +Agreement are listed in Attachment A. + + +2.3. Operating Systems + +Those portions of the SDK designed exclusively for use on the +Linux or FreeBSD operating systems, or other operating systems +derived from the source code to these operating systems, may +be copied and redistributed for use in accordance with this +Agreement, provided that the object code files are not +modified in any way (except for unzipping of compressed +files). + + +2.4. Audio and Video Encoders and Decoders + +You acknowledge and agree that it is your sole responsibility +to obtain any additional third-party licenses required to +make, have made, use, have used, sell, import, and offer for +sale your products or services that include or incorporate any +third-party software and content relating to audio and/or +video encoders and decoders from, including but not limited +to, Microsoft, Thomson, Fraunhofer IIS, Sisvel S.p.A., +MPEG-LA, and Coding Technologies. NVIDIA does not grant to you +under this Agreement any necessary patent or other rights with +respect to any audio and/or video encoders and decoders. + + +2.5. Licensing + +If the distribution terms in this Agreement are not suitable +for your organization, or for any questions regarding this +Agreement, please contact NVIDIA at +nvidia-compute-license-questions@nvidia.com. + + +2.6. Attachment A + +The following portions of the SDK are distributable under the +Agreement: + +Component + +CUDA Runtime + +Windows + +cudart.dll, cudart_static.lib, cudadevrt.lib + +Mac OSX + +libcudart.dylib, libcudart_static.a, libcudadevrt.a + +Linux + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Android + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Component + +CUDA FFT Library + +Windows + +cufft.dll, cufftw.dll, cufft.lib, cufftw.lib + +Mac OSX + +libcufft.dylib, libcufft_static.a, libcufftw.dylib, +libcufftw_static.a + +Linux + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Android + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Component + +CUDA BLAS Library + +Windows + +cublas.dll, cublasLt.dll + +Mac OSX + +libcublas.dylib, libcublasLt.dylib, libcublas_static.a, +libcublasLt_static.a + +Linux + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Android + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Component + +NVIDIA "Drop-in" BLAS Library + +Windows + +nvblas.dll + +Mac OSX + +libnvblas.dylib + +Linux + +libnvblas.so + +Component + +CUDA Sparse Matrix Library + +Windows + +cusparse.dll, cusparse.lib + +Mac OSX + +libcusparse.dylib, libcusparse_static.a + +Linux + +libcusparse.so, libcusparse_static.a + +Android + +libcusparse.so, libcusparse_static.a + +Component + +CUDA Linear Solver Library + +Windows + +cusolver.dll, cusolver.lib + +Mac OSX + +libcusolver.dylib, libcusolver_static.a + +Linux + +libcusolver.so, libcusolver_static.a + +Android + +libcusolver.so, libcusolver_static.a + +Component + +CUDA Random Number Generation Library + +Windows + +curand.dll, curand.lib + +Mac OSX + +libcurand.dylib, libcurand_static.a + +Linux + +libcurand.so, libcurand_static.a + +Android + +libcurand.so, libcurand_static.a + +Component + +CUDA Accelerated Graph Library + +Component + +NVIDIA Performance Primitives Library + +Windows + +nppc.dll, nppc.lib, nppial.dll, nppial.lib, nppicc.dll, +nppicc.lib, nppicom.dll, nppicom.lib, nppidei.dll, +nppidei.lib, nppif.dll, nppif.lib, nppig.dll, nppig.lib, +nppim.dll, nppim.lib, nppist.dll, nppist.lib, nppisu.dll, +nppisu.lib, nppitc.dll, nppitc.lib, npps.dll, npps.lib + +Mac OSX + +libnppc.dylib, libnppc_static.a, libnppial.dylib, +libnppial_static.a, libnppicc.dylib, libnppicc_static.a, +libnppicom.dylib, libnppicom_static.a, libnppidei.dylib, +libnppidei_static.a, libnppif.dylib, libnppif_static.a, +libnppig.dylib, libnppig_static.a, libnppim.dylib, +libnppisu_static.a, libnppitc.dylib, libnppitc_static.a, +libnpps.dylib, libnpps_static.a + +Linux + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Android + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Component + +NVIDIA JPEG Library + +Linux + +libnvjpeg.so, libnvjpeg_static.a + +Component + +Internal common library required for statically linking to +cuBLAS, cuSPARSE, cuFFT, cuRAND, nvJPEG and NPP + +Mac OSX + +libculibos.a + +Linux + +libculibos.a + +Component + +NVIDIA Runtime Compilation Library and Header + +All + +nvrtc.h + +Windows + +nvrtc.dll, nvrtc-builtins.dll + +Mac OSX + +libnvrtc.dylib, libnvrtc-builtins.dylib + +Linux + +libnvrtc.so, libnvrtc-builtins.so + +Component + +NVIDIA Optimizing Compiler Library + +Windows + +nvvm.dll + +Mac OSX + +libnvvm.dylib + +Linux + +libnvvm.so + +Component + +NVIDIA Common Device Math Functions Library + +Windows + +libdevice.10.bc + +Mac OSX + +libdevice.10.bc + +Linux + +libdevice.10.bc + +Component + +CUDA Occupancy Calculation Header Library + +All + +cuda_occupancy.h + +Component + +CUDA Half Precision Headers + +All + +cuda_fp16.h, cuda_fp16.hpp + +Component + +CUDA Profiling Tools Interface (CUPTI) Library + +Windows + +cupti.dll + +Mac OSX + +libcupti.dylib + +Linux + +libcupti.so + +Component + +NVIDIA Tools Extension Library + +Windows + +nvToolsExt.dll, nvToolsExt.lib + +Mac OSX + +libnvToolsExt.dylib + +Linux + +libnvToolsExt.so + +Component + +NVIDIA CUDA Driver Libraries + +Linux + +libcuda.so, libnvidia-fatbinaryloader.so, +libnvidia-ptxjitcompiler.so + +The NVIDIA CUDA Driver Libraries are only distributable in +applications that meet this criteria: + + 1. The application was developed starting from a NVIDIA CUDA + container obtained from Docker Hub or the NVIDIA GPU + Cloud, and + + 2. The resulting application is packaged as a Docker + container and distributed to users on Docker Hub or the + NVIDIA GPU Cloud only. + + +2.7. Attachment B + + +Additional Licensing Obligations + +The following third party components included in the SOFTWARE +are licensed to Licensee pursuant to the following terms and +conditions: + + 1. Licensee's use of the GDB third party component is + subject to the terms and conditions of GNU GPL v3: + + This product includes copyrighted third-party software licensed + under the terms of the GNU General Public License v3 ("GPL v3"). + All third-party software packages are copyright by their respective + authors. GPL v3 terms and conditions are hereby incorporated into + the Agreement by this reference: http://www.gnu.org/licenses/gpl.txt + + Consistent with these licensing requirements, the software + listed below is provided under the terms of the specified + open source software licenses. To obtain source code for + software provided under licenses that require + redistribution of source code, including the GNU General + Public License (GPL) and GNU Lesser General Public License + (LGPL), contact oss-requests@nvidia.com. This offer is + valid for a period of three (3) years from the date of the + distribution of this product by NVIDIA CORPORATION. + + Component License + CUDA-GDB GPL v3 + + 2. Licensee represents and warrants that any and all third + party licensing and/or royalty payment obligations in + connection with Licensee's use of the H.264 video codecs + are solely the responsibility of Licensee. + + 3. Licensee's use of the Thrust library is subject to the + terms and conditions of the Apache License Version 2.0. + All third-party software packages are copyright by their + respective authors. Apache License Version 2.0 terms and + conditions are hereby incorporated into the Agreement by + this reference. + http://www.apache.org/licenses/LICENSE-2.0.html + + In addition, Licensee acknowledges the following notice: + Thrust includes source code from the Boost Iterator, + Tuple, System, and Random Number libraries. + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 4. Licensee's use of the LLVM third party component is + subject to the following terms and conditions: + + ====================================================== + LLVM Release License + ====================================================== + University of Illinois/NCSA + Open Source License + + Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. + All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal with the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at Urbana- + Champaign, nor the names of its contributors may be used to endorse or + promote products derived from this Software without specific prior + written permission. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS WITH THE SOFTWARE. + + 5. Licensee's use (e.g. nvprof) of the PCRE third party + component is subject to the following terms and + conditions: + + ------------ + PCRE LICENCE + ------------ + PCRE is a library of functions to support regular expressions whose syntax + and semantics are as close as possible to those of the Perl 5 language. + Release 8 of PCRE is distributed under the terms of the "BSD" licence, as + specified below. The documentation for PCRE, supplied in the "doc" + directory, is distributed under the same terms as the software itself. The + basic library functions are written in C and are freestanding. Also + included in the distribution is a set of C++ wrapper functions, and a just- + in-time compiler that can be used to optimize pattern matching. These are + both optional features that can be omitted when the library is built. + + THE BASIC LIBRARY FUNCTIONS + --------------------------- + Written by: Philip Hazel + Email local part: ph10 + Email domain: cam.ac.uk + University of Cambridge Computing Service, + Cambridge, England. + Copyright (c) 1997-2012 University of Cambridge + All rights reserved. + + PCRE JUST-IN-TIME COMPILATION SUPPORT + ------------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2010-2012 Zoltan Herczeg + All rights reserved. + + STACK-LESS JUST-IN-TIME COMPILER + -------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2009-2012 Zoltan Herczeg + All rights reserved. + + THE C++ WRAPPER FUNCTIONS + ------------------------- + Contributed by: Google Inc. + Copyright (c) 2007-2012, Google Inc. + All rights reserved. + + THE "BSD" LICENCE + ----------------- + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the name of Google + Inc. nor the names of their contributors may be used to endorse or + promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + 6. Some of the cuBLAS library routines were written by or + derived from code written by Vasily Volkov and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2007-2009, Regents of the University of California + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the University of California, Berkeley nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + 7. Some of the cuBLAS library routines were written by or + derived from code written by Davide Barbieri and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2008-2009 Davide Barbieri @ University of Rome Tor Vergata. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + 8. Some of the cuBLAS library routines were derived from + code developed by the University of Tennessee and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2010 The University of Tennessee. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer listed in this license in the documentation and/or + other materials provided with the distribution. + * Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 9. Some of the cuBLAS library routines were written by or + derived from code written by Jonathan Hogg and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2012, The Science and Technology Facilities Council (STFC). + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the STFC nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE STFC BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 10. Some of the cuBLAS library routines were written by or + derived from code written by Ahmad M. Abdelfattah, David + Keyes, and Hatem Ltaief, and are subject to the Apache + License, Version 2.0, as follows: + + -- (C) Copyright 2013 King Abdullah University of Science and Technology + Authors: + Ahmad Abdelfattah (ahmad.ahmad@kaust.edu.sa) + David Keyes (david.keyes@kaust.edu.sa) + Hatem Ltaief (hatem.ltaief@kaust.edu.sa) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the King Abdullah University of Science and + Technology nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + + 11. Some of the cuSPARSE library routines were written by or + derived from code written by Li-Wen Chang and are subject + to the NCSA Open Source License as follows: + + Copyright (c) 2012, University of Illinois. + + All rights reserved. + + Developed by: IMPACT Group, University of Illinois, http://impact.crhc.illinois.edu + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal with the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimers in the documentation and/or other materials provided + with the distribution. + * Neither the names of IMPACT Group, University of Illinois, nor + the names of its contributors may be used to endorse or promote + products derived from this Software without specific prior + written permission. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE + SOFTWARE. + + 12. Some of the cuRAND library routines were written by or + derived from code written by Mutsuo Saito and Makoto + Matsumoto and are subject to the following license: + + Copyright (c) 2009, 2010 Mutsuo Saito, Makoto Matsumoto and Hiroshima + University. All rights reserved. + + Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima + University and University of Tokyo. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the Hiroshima University nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 13. Some of the cuRAND library routines were derived from + code developed by D. E. Shaw Research and are subject to + the following license: + + Copyright 2010-2011, D. E. Shaw Research. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of D. E. Shaw Research nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 14. Some of the Math library routines were written by or + derived from code developed by Norbert Juffa and are + subject to the following license: + + Copyright (c) 2015-2017, Norbert Juffa + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 15. Licensee's use of the lz4 third party component is + subject to the following terms and conditions: + + Copyright (C) 2011-2013, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 16. The NPP library uses code from the Boost Math Toolkit, + and is subject to the following license: + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 17. Portions of the Nsight Eclipse Edition is subject to the + following license: + + The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content is provided + to you under the terms and conditions of the Eclipse Public License + Version 1.0 ("EPL"). A copy of the EPL is available at http:// + www.eclipse.org/legal/epl-v10.html. For purposes of the EPL, "Program" + will mean the Content. + + If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may apply to your + use of any object code in the Content. Check the Redistributor's + license that was provided with the Content. If no such license exists, + contact the Redistributor. Unless otherwise indicated below, the terms + and conditions of the EPL still apply to any source code in the + Content and such source code may be obtained at http://www.eclipse.org. + + 18. Some of the cuBLAS library routines uses code from + OpenAI, which is subject to the following license: + + License URL + https://github.com/openai/openai-gemm/blob/master/LICENSE + + License Text + The MIT License + + Copyright (c) 2016 OpenAI (http://openai.com), 2016 Google Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + 19. Licensee's use of the Visual Studio Setup Configuration + Samples is subject to the following license: + + The MIT License (MIT) + Copyright (C) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + 20. Licensee's use of linmath.h header for CPU functions for + GL vector/matrix operations from lunarG is subject to the + Apache License Version 2.0. + + 21. The DX12-CUDA sample uses the d3dx12.h header, which is + subject to the MIT license . + +----------------- diff --git a/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/METADATA b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..c0d874a42fb5a6bdb8f0cb78bb40f3d9cb708137 --- /dev/null +++ b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/METADATA @@ -0,0 +1,35 @@ +Metadata-Version: 2.1 +Name: nvidia-cuda-cupti-cu12 +Version: 12.4.127 +Summary: CUDA profiling tools runtime libs. +Home-page: https://developer.nvidia.com/cuda-zone +Author: Nvidia CUDA Installer Team +Author-email: cuda_installer@nvidia.com +License: NVIDIA Proprietary Software +Keywords: cuda,nvidia,runtime,machine learning,deep learning +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: Other/Proprietary License +Classifier: Natural Language :: English +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Scientific/Engineering :: Mathematics +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: Topic :: Software Development +Classifier: Topic :: Software Development :: Libraries +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX :: Linux +Requires-Python: >=3 +License-File: License.txt + +Provides libraries to enable third party tools using GPU profiling APIs. diff --git a/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/RECORD b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..fe9075741deb35f37fbe0a17bf262a44ee0e977c --- /dev/null +++ b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/RECORD @@ -0,0 +1,53 @@ +nvidia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/__pycache__/__init__.cpython-312.pyc,, +nvidia/cuda_cupti/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cuda_cupti/__pycache__/__init__.cpython-312.pyc,, +nvidia/cuda_cupti/include/Openacc/cupti_openacc.h,sha256=Z0OM5e_hbd3cxdXyn3SCHqBBQawLg4QORnlm57Cr2-M,3513 +nvidia/cuda_cupti/include/Openmp/cupti_openmp.h,sha256=E1WNmeb_7HaUSmBegtUNe4IV1i7pXeNxgzIlyKn1zrM,3491 +nvidia/cuda_cupti/include/Openmp/omp-tools.h,sha256=AmuC_xPC7VPu3B-W4PmXuCNufFawhY8PjNXePaQFAOg,37403 +nvidia/cuda_cupti/include/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cuda_cupti/include/__pycache__/__init__.cpython-312.pyc,, +nvidia/cuda_cupti/include/cuda_stdint.h,sha256=XbFOk9CtJjKqk7PpYNqbSVsDxAsVM8avA4rWpPi0BjQ,4093 +nvidia/cuda_cupti/include/cupti.h,sha256=JkVyAGTIMYzwm62dfVqas3nMcILhgP_Wdz6fh4_NED0,4697 +nvidia/cuda_cupti/include/cupti_activity.h,sha256=RB7VYrdiOBpdH_LbXb2o-CcGthnk3-NE2Cqq-jQbN7Q,210611 +nvidia/cuda_cupti/include/cupti_activity_deprecated.h,sha256=rYJsoAJxA2BTT50-olN8EYcSzdlXBpRbR1ATLG3rVIM,121526 +nvidia/cuda_cupti/include/cupti_callbacks.h,sha256=zrEVRb0hubSfD69QUmHsJiL8oAfvqyuKGcTVRihQrnc,29729 +nvidia/cuda_cupti/include/cupti_checkpoint.h,sha256=rTz8JoWxqESBXyZWUhZJGm4xeYcx4OJOtJ7Ld13T_b0,5264 +nvidia/cuda_cupti/include/cupti_common.h,sha256=85m74bxUgXp3tEaPQpezeazmpsNMw41PsjNSYmQdT20,3514 +nvidia/cuda_cupti/include/cupti_driver_cbid.h,sha256=dHKyQYZbBbdlxixzFkIoNHg5IfGXdgriyjN1Bu1i6g4,74462 +nvidia/cuda_cupti/include/cupti_events.h,sha256=f7lLGmD2e8FzvMhRgnn0-v7U0vTpUkiQHIpQxgARGb0,51896 +nvidia/cuda_cupti/include/cupti_metrics.h,sha256=iLAOlDrcbHEsIIUmgq0Tp1ZOY9O3Ot3wj2-bI8iYbSs,32148 +nvidia/cuda_cupti/include/cupti_nvtx_cbid.h,sha256=_azPtR1g4qivvX7qbvHRUg0RHCWF7iEOJyHMN9qZe9E,5912 +nvidia/cuda_cupti/include/cupti_pcsampling.h,sha256=ycJHT36DmPIaVzHsB3xxjXkhFyEfMCJOl3LbCsHFgyA,32144 +nvidia/cuda_cupti/include/cupti_pcsampling_util.h,sha256=lx8CaNXowJe5Zvc06LE-u_Zry_jODs1mM6j9Q5WIX9E,12430 +nvidia/cuda_cupti/include/cupti_profiler_target.h,sha256=JsceoDuhllWNEzaO0xxT81dJ55NrbF0UtRJJgit0P_E,32131 +nvidia/cuda_cupti/include/cupti_result.h,sha256=a-C4Y7LAYCiCT1ngOfoDuTi2stEG1YTafwwn6UfL-LU,12603 +nvidia/cuda_cupti/include/cupti_runtime_cbid.h,sha256=11pXl0MdmTtxUngel-ru4JdqWvF_gEIG14aQExRyfzI,46436 +nvidia/cuda_cupti/include/cupti_sass_metrics.h,sha256=3RW9snJuFQdOhrEn3wDJOru05q0V_zssWrqD7tvVJKw,19674 +nvidia/cuda_cupti/include/cupti_target.h,sha256=x4Vz1Upb6m9ixmVpmGaKQldDWYQI3OZ-ocEXGzNK0EE,1263 +nvidia/cuda_cupti/include/cupti_version.h,sha256=sjd-aUoTGkEWyvA2VUWIpZwXyXAaclqC8gbwNnuK5D0,4425 +nvidia/cuda_cupti/include/generated_cudaGL_meta.h,sha256=dfd2QuaRdEjbStOKvaQLi1Md_qrpRQh8PfyZznJ8bWY,3115 +nvidia/cuda_cupti/include/generated_cudaVDPAU_meta.h,sha256=fAedsoQxaU3hIAApAWDOKsa9kgcuQw4tdyf8klLm-3k,1453 +nvidia/cuda_cupti/include/generated_cuda_gl_interop_meta.h,sha256=LXOqvQCej0sCgAT1LUKKYZ466EFxN4hIwf9oIhXOLF0,2250 +nvidia/cuda_cupti/include/generated_cuda_meta.h,sha256=hawYpDe0xpaDFDnClXI91JjwCRxWb-AS0FS8ydUMgxc,94639 +nvidia/cuda_cupti/include/generated_cuda_runtime_api_meta.h,sha256=D8CbAN3-jLuF2KGfsBHXEELSgL92KrUAiDvugWE8B8M,69706 +nvidia/cuda_cupti/include/generated_cuda_vdpau_interop_meta.h,sha256=8OLqWN26aEYpTWUXtbHJvA5GYhVv3ybYVOTW7yK37z8,1367 +nvidia/cuda_cupti/include/generated_cudart_removed_meta.h,sha256=X3I5WXmhtsJNNlgY7coJ5vg4t11G5FRR6Xo7MboIeck,5172 +nvidia/cuda_cupti/include/generated_nvtx_meta.h,sha256=YHb_RD8g3s4m8PJn7Z0wnxvUHarl7BOAX5ADr-BL3HI,7513 +nvidia/cuda_cupti/include/nvperf_common.h,sha256=BqPml9AxyN10-ptWT3hQzh2JUWqQX57Q5BjQ3ZuaKNs,17255 +nvidia/cuda_cupti/include/nvperf_cuda_host.h,sha256=aBnyIr_hexPDGBkP6WSujN1mI_DYP25sEIXWYY1O7VI,8298 +nvidia/cuda_cupti/include/nvperf_host.h,sha256=afdHG6eraeo4ltlF9ihskqhU7IccxcRCaZDZ6_ikjkg,68506 +nvidia/cuda_cupti/include/nvperf_target.h,sha256=ZDA-JI459tLBW4iLLCQjYYRAMeHwfqDIgXbVqVLDYZ4,22539 +nvidia/cuda_cupti/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cuda_cupti/lib/__pycache__/__init__.cpython-312.pyc,, +nvidia/cuda_cupti/lib/libcheckpoint.so,sha256=cCTAB7_UNqvoU7zKMCHkklcmM1GGr3atZmZoZksdAKM,1501336 +nvidia/cuda_cupti/lib/libcupti.so.12,sha256=-yp8WxXITflQXdR-VT_kbzEhpX0wOR_KJBedIC9z8_c,7748112 +nvidia/cuda_cupti/lib/libnvperf_host.so,sha256=tZsmsdNdAik8jdiVaro3V8FGa3FzLGaHq6QSxQ2VC2k,28132984 +nvidia/cuda_cupti/lib/libnvperf_target.so,sha256=ztN3NKnf_9XyEogyuHjyOAcTvqYBn6lE0psxejPTeYw,5592368 +nvidia/cuda_cupti/lib/libpcsamplingutil.so,sha256=ZDY0bEGLzy-pA3yfFtc6jfvo-Cu8vWwUCQYatGJrb0Q,912728 +nvidia_cuda_cupti_cu12-12.4.127.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +nvidia_cuda_cupti_cu12-12.4.127.dist-info/License.txt,sha256=rW9YU_ugyg0VnQ9Y1JrkmDDC-Mk_epJki5zpCttMbM0,59262 +nvidia_cuda_cupti_cu12-12.4.127.dist-info/METADATA,sha256=UiXYPD5hc55tQSSNiYNq5AqkD68jq1KHNCtG-PJvPds,1553 +nvidia_cuda_cupti_cu12-12.4.127.dist-info/RECORD,, +nvidia_cuda_cupti_cu12-12.4.127.dist-info/WHEEL,sha256=XDTs3wIbcE-BcRO08VJlZpA6z9OaC1mOKPCGGGwuM2g,109 +nvidia_cuda_cupti_cu12-12.4.127.dist-info/top_level.txt,sha256=fTkAtiFuL16nUrB9ytDDtpytz2t0B4NvYTnRzwAhO14,7 diff --git a/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/WHEEL b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e6c30e957cfb045017a9fef3430bb8ee87c4a074 --- /dev/null +++ b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: true +Tag: py3-none-manylinux2014_x86_64 + diff --git a/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/top_level.txt b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..862f7abf232cdfbb928609856247292e81c9decb --- /dev/null +++ b/lib/python3.12/site-packages/nvidia_cuda_cupti_cu12-12.4.127.dist-info/top_level.txt @@ -0,0 +1 @@ +nvidia diff --git a/lib/python3.12/site-packages/parso-0.8.5.dist-info/INSTALLER b/lib/python3.12/site-packages/parso-0.8.5.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/parso-0.8.5.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/parso-0.8.5.dist-info/METADATA b/lib/python3.12/site-packages/parso-0.8.5.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..3b138239f9d1e61575fa049beedeb7039d6e03d3 --- /dev/null +++ b/lib/python3.12/site-packages/parso-0.8.5.dist-info/METADATA @@ -0,0 +1,312 @@ +Metadata-Version: 2.4 +Name: parso +Version: 0.8.5 +Summary: A Python Parser +Home-page: https://github.com/davidhalter/parso +Author: David Halter +Author-email: davidhalter88@gmail.com +Maintainer: David Halter +Maintainer-email: davidhalter88@gmail.com +License: MIT +Keywords: python parser parsing +Platform: any +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Plugins +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Editors :: Integrated Development Environments (IDE) +Classifier: Topic :: Utilities +Classifier: Typing :: Typed +Requires-Python: >=3.6 +License-File: LICENSE.txt +License-File: AUTHORS.txt +Provides-Extra: testing +Requires-Dist: pytest; extra == "testing" +Requires-Dist: docopt; extra == "testing" +Provides-Extra: qa +Requires-Dist: flake8==5.0.4; extra == "qa" +Requires-Dist: mypy==0.971; extra == "qa" +Requires-Dist: types-setuptools==67.2.0.1; extra == "qa" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: license-file +Dynamic: maintainer +Dynamic: maintainer-email +Dynamic: platform +Dynamic: provides-extra +Dynamic: requires-python +Dynamic: summary + +################################################################### +parso - A Python Parser +################################################################### + + +.. image:: https://github.com/davidhalter/parso/workflows/Build/badge.svg?branch=master + :target: https://github.com/davidhalter/parso/actions + :alt: GitHub Actions build status + +.. image:: https://coveralls.io/repos/github/davidhalter/parso/badge.svg?branch=master + :target: https://coveralls.io/github/davidhalter/parso?branch=master + :alt: Coverage Status + +.. image:: https://pepy.tech/badge/parso + :target: https://pepy.tech/project/parso + :alt: PyPI Downloads + +.. image:: https://raw.githubusercontent.com/davidhalter/parso/master/docs/_static/logo_characters.png + +Parso is a Python parser that supports error recovery and round-trip parsing +for different Python versions (in multiple Python versions). Parso is also able +to list multiple syntax errors in your python file. + +Parso has been battle-tested by jedi_. It was pulled out of jedi to be useful +for other projects as well. + +Parso consists of a small API to parse Python and analyse the syntax tree. + +A simple example: + +.. code-block:: python + + >>> import parso + >>> module = parso.parse('hello + 1', version="3.9") + >>> expr = module.children[0] + >>> expr + PythonNode(arith_expr, [, , ]) + >>> print(expr.get_code()) + hello + 1 + >>> name = expr.children[0] + >>> name + + >>> name.end_pos + (1, 5) + >>> expr.end_pos + (1, 9) + +To list multiple issues: + +.. code-block:: python + + >>> grammar = parso.load_grammar() + >>> module = grammar.parse('foo +\nbar\ncontinue') + >>> error1, error2 = grammar.iter_errors(module) + >>> error1.message + 'SyntaxError: invalid syntax' + >>> error2.message + "SyntaxError: 'continue' not properly in loop" + +Resources +========= + +- `Testing `_ +- `PyPI `_ +- `Docs `_ +- Uses `semantic versioning `_ + +Installation +============ + +.. code-block:: bash + + pip install parso + +Future +====== + +- There will be better support for refactoring and comments. Stay tuned. +- There's a WIP PEP8 validator. It's however not in a good shape, yet. + +Known Issues +============ + +- `async`/`await` are already used as keywords in Python3.6. +- `from __future__ import print_function` is not ignored. + + +Acknowledgements +================ + +- Guido van Rossum (@gvanrossum) for creating the parser generator pgen2 + (originally used in lib2to3). +- Salome Schneider for the extremely awesome parso logo. + + +.. _jedi: https://github.com/davidhalter/jedi + + +.. :changelog: + +Changelog +--------- + +Unreleased +++++++++++ + +0.8.5 (2025-08-23) +++++++++++++++++++ + +- Add a fallback grammar for Python 3.14+ + +0.8.4 (2024-04-05) +++++++++++++++++++ + +- Add basic support for Python 3.13 + +0.8.3 (2021-11-30) +++++++++++++++++++ + +- Add basic support for Python 3.11 and 3.12 + +0.8.2 (2021-03-30) +++++++++++++++++++ + +- Various small bugfixes + +0.8.1 (2020-12-10) +++++++++++++++++++ + +- Various small bugfixes + +0.8.0 (2020-08-05) +++++++++++++++++++ + +- Dropped Support for Python 2.7, 3.4, 3.5 +- It's possible to use ``pathlib.Path`` objects now in the API +- The stubs are gone, we are now using annotations +- ``namedexpr_test`` nodes are now a proper class called ``NamedExpr`` +- A lot of smaller refactorings + +0.7.1 (2020-07-24) +++++++++++++++++++ + +- Fixed a couple of smaller bugs (mostly syntax error detection in + ``Grammar.iter_errors``) + +This is going to be the last release that supports Python 2.7, 3.4 and 3.5. + +0.7.0 (2020-04-13) +++++++++++++++++++ + +- Fix a lot of annoying bugs in the diff parser. The fuzzer did not find + issues anymore even after running it for more than 24 hours (500k tests). +- Small grammar change: suites can now contain newlines even after a newline. + This should really not matter if you don't use error recovery. It allows for + nicer error recovery. + +0.6.2 (2020-02-27) +++++++++++++++++++ + +- Bugfixes +- Add Grammar.refactor (might still be subject to change until 0.7.0) + +0.6.1 (2020-02-03) +++++++++++++++++++ + +- Add ``parso.normalizer.Issue.end_pos`` to make it possible to know where an + issue ends + +0.6.0 (2020-01-26) +++++++++++++++++++ + +- Dropped Python 2.6/Python 3.3 support +- del_stmt names are now considered as a definition + (for ``name.is_definition()``) +- Bugfixes + +0.5.2 (2019-12-15) +++++++++++++++++++ + +- Add include_setitem to get_definition/is_definition and get_defined_names (#66) +- Fix named expression error listing (#89, #90) +- Fix some f-string tokenizer issues (#93) + +0.5.1 (2019-07-13) +++++++++++++++++++ + +- Fix: Some unicode identifiers were not correctly tokenized +- Fix: Line continuations in f-strings are now working + +0.5.0 (2019-06-20) +++++++++++++++++++ + +- **Breaking Change** comp_for is now called sync_comp_for for all Python + versions to be compatible with the Python 3.8 Grammar +- Added .pyi stubs for a lot of the parso API +- Small FileIO changes + +0.4.0 (2019-04-05) +++++++++++++++++++ + +- Python 3.8 support +- FileIO support, it's now possible to use abstract file IO, support is alpha + +0.3.4 (2019-02-13) ++++++++++++++++++++ + +- Fix an f-string tokenizer error + +0.3.3 (2019-02-06) ++++++++++++++++++++ + +- Fix async errors in the diff parser +- A fix in iter_errors +- This is a very small bugfix release + +0.3.2 (2019-01-24) ++++++++++++++++++++ + +- 20+ bugfixes in the diff parser and 3 in the tokenizer +- A fuzzer for the diff parser, to give confidence that the diff parser is in a + good shape. +- Some bugfixes for f-string + +0.3.1 (2018-07-09) ++++++++++++++++++++ + +- Bugfixes in the diff parser and keyword-only arguments + +0.3.0 (2018-06-30) ++++++++++++++++++++ + +- Rewrote the pgen2 parser generator. + +0.2.1 (2018-05-21) ++++++++++++++++++++ + +- A bugfix for the diff parser. +- Grammar files can now be loaded from a specific path. + +0.2.0 (2018-04-15) ++++++++++++++++++++ + +- f-strings are now parsed as a part of the normal Python grammar. This makes + it way easier to deal with them. + +0.1.1 (2017-11-05) ++++++++++++++++++++ + +- Fixed a few bugs in the caching layer +- Added support for Python 3.7 + +0.1.0 (2017-09-04) ++++++++++++++++++++ + +- Pulling the library out of Jedi. Some APIs will definitely change. diff --git a/lib/python3.12/site-packages/parso-0.8.5.dist-info/RECORD b/lib/python3.12/site-packages/parso-0.8.5.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..3e6c48935a198a87eee254cecf7697e90e008f75 --- /dev/null +++ b/lib/python3.12/site-packages/parso-0.8.5.dist-info/RECORD @@ -0,0 +1,59 @@ +parso-0.8.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +parso-0.8.5.dist-info/METADATA,sha256=McgMidhsXJ8k0WRgK-EX_RHdEpy7WDAZ-nZeRbAsSV8,8256 +parso-0.8.5.dist-info/RECORD,, +parso-0.8.5.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109 +parso-0.8.5.dist-info/licenses/AUTHORS.txt,sha256=SDCgu8hXlBBcjPPyUT-SKW20_IM2MxW-95hKFaRIqyI,2029 +parso-0.8.5.dist-info/licenses/LICENSE.txt,sha256=-meXMHN1PRdiTK-GhNXugW1wyJ2RLFvKfKDwjnsVDts,4176 +parso-0.8.5.dist-info/top_level.txt,sha256=GOOKQCPcnr0_7IRArxyI0CX5LLu4WLlzIRAVWS-vJ4s,6 +parso/__init__.py,sha256=gJK-bMRgg9_JFuvnfK69nrxOZSVqr32rbnoxJxkp0lE,1607 +parso/__pycache__/__init__.cpython-312.pyc,, +parso/__pycache__/_compatibility.cpython-312.pyc,, +parso/__pycache__/cache.cpython-312.pyc,, +parso/__pycache__/file_io.cpython-312.pyc,, +parso/__pycache__/grammar.cpython-312.pyc,, +parso/__pycache__/normalizer.cpython-312.pyc,, +parso/__pycache__/parser.cpython-312.pyc,, +parso/__pycache__/tree.cpython-312.pyc,, +parso/__pycache__/utils.cpython-312.pyc,, +parso/_compatibility.py,sha256=y-fATJ1dyaoVry175CMDBA088IGTxChkCKD2dUAnsrU,70 +parso/cache.py,sha256=KyQBZdTuBXhDjLmwTSLOgyQoq4NLt_wNr1882DTkOW4,8452 +parso/file_io.py,sha256=2SbXQuMpjAaQ0OYvxZXOgl-oU945-CrIei3eEamWWmk,1023 +parso/grammar.py,sha256=YWet-eatprA6sDbLvIHmfxJ7ygO4dl37Gxn_0-P4-JM,11081 +parso/normalizer.py,sha256=geYG9UZQ6ZpafTc_CiXQoBt8VImdBsiNw6_GJLeSGbg,5597 +parso/parser.py,sha256=qlIrRikSxAccfsC6B6Y9sPWyEhR0HIBaCbNveV1OcAE,7182 +parso/pgen2/__init__.py,sha256=kFfRZsSReM49V0YIJ_cG0_TMTew2t4IMbG95KO2BI8E,382 +parso/pgen2/__pycache__/__init__.cpython-312.pyc,, +parso/pgen2/__pycache__/generator.cpython-312.pyc,, +parso/pgen2/__pycache__/grammar_parser.cpython-312.pyc,, +parso/pgen2/generator.py,sha256=PHjCpx7QM2duGZqGw5GOQQIgc6RE3jcKV7IwXcOgJhw,14580 +parso/pgen2/grammar_parser.py,sha256=knJh3a40_JxUkb0HePG78ZZoqjpPNk3uZwNOz2EkkV4,5515 +parso/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +parso/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +parso/python/__pycache__/__init__.cpython-312.pyc,, +parso/python/__pycache__/diff.cpython-312.pyc,, +parso/python/__pycache__/errors.cpython-312.pyc,, +parso/python/__pycache__/parser.cpython-312.pyc,, +parso/python/__pycache__/pep8.cpython-312.pyc,, +parso/python/__pycache__/prefix.cpython-312.pyc,, +parso/python/__pycache__/token.cpython-312.pyc,, +parso/python/__pycache__/tokenize.cpython-312.pyc,, +parso/python/__pycache__/tree.cpython-312.pyc,, +parso/python/diff.py,sha256=jyrqWRKklyPPezZRKRHxoKbhkywiCUoGH_K1HcRSyMA,34206 +parso/python/errors.py,sha256=Vlmxc0MLUNTYnVNMEWVO68JTpkmXzLEG_sh7rkEt6dA,49113 +parso/python/grammar310.txt,sha256=QwXaHqJcJ_zgi9FAAbdv1U_kKgcku9UWjHZoClbtpb4,7511 +parso/python/grammar311.txt,sha256=QwXaHqJcJ_zgi9FAAbdv1U_kKgcku9UWjHZoClbtpb4,7511 +parso/python/grammar312.txt,sha256=QwXaHqJcJ_zgi9FAAbdv1U_kKgcku9UWjHZoClbtpb4,7511 +parso/python/grammar313.txt,sha256=QwXaHqJcJ_zgi9FAAbdv1U_kKgcku9UWjHZoClbtpb4,7511 +parso/python/grammar314.txt,sha256=QwXaHqJcJ_zgi9FAAbdv1U_kKgcku9UWjHZoClbtpb4,7511 +parso/python/grammar36.txt,sha256=ezjXEeLpG9BBMrN0rbM3Z77mcn0XESxSlAaZEy2er-k,6948 +parso/python/grammar37.txt,sha256=Ke73_sTcivtBt2rkJaoNYiXa_zLenhCr96HOVPpZB_E,6804 +parso/python/grammar38.txt,sha256=OhPReVYqhsX2RWyVryca3RUGcvLb-R1dcbwdbgPIvBI,7591 +parso/python/grammar39.txt,sha256=cVrVbF9Pg5UJLFi2tvLetPkG-BOAkpqDa9hqslNjSHU,7499 +parso/python/parser.py,sha256=5OMU32ybPF6kcKUdbcfNNkDOK8hJy0B7fqi6b-Gfwqw,8108 +parso/python/pep8.py,sha256=tsuRslXZvfio8LTBIAbfExjBIT1f3Xjx3igt28fm3G4,33779 +parso/python/prefix.py,sha256=BM93VenBA1Vs-qk2AJSLBMJNn5BDbyVZLIZ5ScT4FIU,2743 +parso/python/token.py,sha256=0dzmQf6L59bEJb9MXYbrDtq3bAHNdTuk-PmOhox81G4,909 +parso/python/tokenize.py,sha256=kqmG8SEdkbLG3Gf6gQyeQZerp4yhzzXX14FCYOZJ5mI,25795 +parso/python/tree.py,sha256=bwJ54y4Nt_ebUqXiFE7QZbBplNmicL1JDvy-8o-Av7o,37226 +parso/tree.py,sha256=deZ68uAq0jodEeumJpBYWXWciIXcBYfpsLpe1f1WLO8,16153 +parso/utils.py,sha256=qW8kJuw9pyK8WaIi37FX44kNjAaIgu15EGv7V_R4PmE,6620 diff --git a/lib/python3.12/site-packages/parso-0.8.5.dist-info/WHEEL b/lib/python3.12/site-packages/parso-0.8.5.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..5f133dbb5cfac001f2e84cda817210c03ce6484e --- /dev/null +++ b/lib/python3.12/site-packages/parso-0.8.5.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/parso-0.8.5.dist-info/licenses/AUTHORS.txt b/lib/python3.12/site-packages/parso-0.8.5.dist-info/licenses/AUTHORS.txt new file mode 100644 index 0000000000000000000000000000000000000000..9737530ba98e52501d37f2764f896d66dd1c6587 --- /dev/null +++ b/lib/python3.12/site-packages/parso-0.8.5.dist-info/licenses/AUTHORS.txt @@ -0,0 +1,58 @@ +Main Authors +============ + +David Halter (@davidhalter) + +Code Contributors +================= +Alisdair Robertson (@robodair) +Bryan Forbes (@bryanforbes) + + +Code Contributors (to Jedi and therefore possibly to this library) +================================================================== + +Takafumi Arakaki (@tkf) +Danilo Bargen (@dbrgn) +Laurens Van Houtven (@lvh) <_@lvh.cc> +Aldo Stracquadanio (@Astrac) +Jean-Louis Fuchs (@ganwell) +tek (@tek) +Yasha Borevich (@jjay) +Aaron Griffin +andviro (@andviro) +Mike Gilbert (@floppym) +Aaron Meurer (@asmeurer) +Lubos Trilety +Akinori Hattori (@hattya) +srusskih (@srusskih) +Steven Silvester (@blink1073) +Colin Duquesnoy (@ColinDuquesnoy) +Jorgen Schaefer (@jorgenschaefer) +Fredrik Bergroth (@fbergroth) +Mathias Fußenegger (@mfussenegger) +Syohei Yoshida (@syohex) +ppalucky (@ppalucky) +immerrr (@immerrr) immerrr@gmail.com +Albertas Agejevas (@alga) +Savor d'Isavano (@KenetJervet) +Phillip Berndt (@phillipberndt) +Ian Lee (@IanLee1521) +Farkhad Khatamov (@hatamov) +Kevin Kelley (@kelleyk) +Sid Shanker (@squidarth) +Reinoud Elhorst (@reinhrst) +Guido van Rossum (@gvanrossum) +Dmytro Sadovnychyi (@sadovnychyi) +Cristi Burcă (@scribu) +bstaint (@bstaint) +Mathias Rav (@Mortal) +Daniel Fiterman (@dfit99) +Simon Ruggier (@sruggier) +Élie Gouzien (@ElieGouzien) +Tim Gates (@timgates42) +Batuhan Taskaya (@isidentical) +Jocelyn Boullier (@Kazy) + + +Note: (@user) means a github user name. diff --git a/lib/python3.12/site-packages/parso-0.8.5.dist-info/licenses/LICENSE.txt b/lib/python3.12/site-packages/parso-0.8.5.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..08c41db014ea40b0f01e9f5c56793b3c1d138aea --- /dev/null +++ b/lib/python3.12/site-packages/parso-0.8.5.dist-info/licenses/LICENSE.txt @@ -0,0 +1,86 @@ +All contributions towards parso are MIT licensed. + +Some Python files have been taken from the standard library and are therefore +PSF licensed. Modifications on these files are dual licensed (both MIT and +PSF). These files are: + +- parso/pgen2/* +- parso/tokenize.py +- parso/token.py +- test/test_pgen2.py + +Also some test files under test/normalizer_issue_files have been copied from +https://github.com/PyCQA/pycodestyle (Expat License == MIT License). + +------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) <2013-2017> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------------------------------------- + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015 Python Software Foundation; All Rights Reserved" +are retained in Python alone or in any derivative version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. diff --git a/lib/python3.12/site-packages/parso-0.8.5.dist-info/top_level.txt b/lib/python3.12/site-packages/parso-0.8.5.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..0e2334404713dd9c161c1d61e779798c4476a237 --- /dev/null +++ b/lib/python3.12/site-packages/parso-0.8.5.dist-info/top_level.txt @@ -0,0 +1 @@ +parso diff --git a/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/INSTALLER b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/METADATA b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..155ce8b6f2797dca15e2e2062427319ec8a86d93 --- /dev/null +++ b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/METADATA @@ -0,0 +1,40 @@ +Metadata-Version: 2.4 +Name: python-multipart +Version: 0.0.20 +Summary: A streaming multipart parser for Python +Project-URL: Homepage, https://github.com/Kludex/python-multipart +Project-URL: Documentation, https://kludex.github.io/python-multipart/ +Project-URL: Changelog, https://github.com/Kludex/python-multipart/blob/master/CHANGELOG.md +Project-URL: Source, https://github.com/Kludex/python-multipart +Author-email: Andrew Dunham , Marcelo Trylesinski +License-Expression: Apache-2.0 +License-File: LICENSE.txt +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.8 +Description-Content-Type: text/markdown + +# [Python-Multipart](https://kludex.github.io/python-multipart/) + +[![Package version](https://badge.fury.io/py/python-multipart.svg)](https://pypi.python.org/pypi/python-multipart) +[![Supported Python Version](https://img.shields.io/pypi/pyversions/python-multipart.svg?color=%2334D058)](https://pypi.org/project/python-multipart) + +--- + +`python-multipart` is an Apache2-licensed streaming multipart parser for Python. +Test coverage is currently 100%. + +## Why? + +Because streaming uploads are awesome for large files. diff --git a/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/RECORD b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..c44a464bcfca7ca094437d4060e95a16dcfaed6f --- /dev/null +++ b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/RECORD @@ -0,0 +1,23 @@ +multipart/__init__.py,sha256=_ttxOAFnTN4jeac-_8NeXpaXYYo0PPEIp8Ogo4YFNHE,935 +multipart/__pycache__/__init__.cpython-312.pyc,, +multipart/__pycache__/decoders.cpython-312.pyc,, +multipart/__pycache__/exceptions.cpython-312.pyc,, +multipart/__pycache__/multipart.cpython-312.pyc,, +multipart/decoders.py,sha256=XvkAwTU9UFPiXkc0hkvovHf0W6H3vK-2ieWlhav02hQ,40 +multipart/exceptions.py,sha256=6D_X-seiOmMAlIeiGlPGUs8-vpcvIGJeQycFMDb1f7A,42 +multipart/multipart.py,sha256=8fDH14j_VMbrch_58wlzi63XNARGv80kOZAyN72aG7A,41 +python_multipart-0.0.20.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +python_multipart-0.0.20.dist-info/METADATA,sha256=h2GtPOVShbVkpBUrjp5KE3t6eiJJhd0_WCaCXrb5TgU,1817 +python_multipart-0.0.20.dist-info/RECORD,, +python_multipart-0.0.20.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +python_multipart-0.0.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +python_multipart-0.0.20.dist-info/licenses/LICENSE.txt,sha256=qOgzF2zWF9rwC51tOfoVyo7evG0WQwec0vSJPAwom-I,556 +python_multipart/__init__.py,sha256=Nlw6Yrc__qXnCZLo17OzbJR2w2mwiSFk69IG4Wl35EU,512 +python_multipart/__pycache__/__init__.cpython-312.pyc,, +python_multipart/__pycache__/decoders.cpython-312.pyc,, +python_multipart/__pycache__/exceptions.cpython-312.pyc,, +python_multipart/__pycache__/multipart.cpython-312.pyc,, +python_multipart/decoders.py,sha256=JM43FMNn_EKP0MI2ZkuZHhNa0MOASoIR0U5TvdG585k,6669 +python_multipart/exceptions.py,sha256=a9buSOv_eiHZoukEJhdWX9LJYSJ6t7XOK3ZEaWoQZlk,992 +python_multipart/multipart.py,sha256=pk3o3eB3KXbNxzOBxbEjCdz-1ESEZIMXVIfl12grG-o,76427 +python_multipart/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/REQUESTED b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/WHEEL b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..12228d414b6cfed7c39d3781c85c63256a1d7fb5 --- /dev/null +++ b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/licenses/LICENSE.txt b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..303a1bf5015ca5c7374a9bde74dee5e2b205fbe3 --- /dev/null +++ b/lib/python3.12/site-packages/python_multipart-0.0.20.dist-info/licenses/LICENSE.txt @@ -0,0 +1,14 @@ +Copyright 2012, Andrew Dunham + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/lib/python3.12/site-packages/ray-2.52.1.dist-info/INSTALLER b/lib/python3.12/site-packages/ray-2.52.1.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/ray-2.52.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/ray-2.52.1.dist-info/METADATA b/lib/python3.12/site-packages/ray-2.52.1.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..7728c201e65efb548a5cfe1631ffb4d1e615a4f4 --- /dev/null +++ b/lib/python3.12/site-packages/ray-2.52.1.dist-info/METADATA @@ -0,0 +1,442 @@ +Metadata-Version: 2.4 +Name: ray +Version: 2.52.1 +Summary: Ray provides a simple, universal API for building distributed applications. +Home-page: https://github.com/ray-project/ray +Author: Ray Team +Author-email: ray-dev@googlegroups.com +License: Apache 2.0 +Keywords: ray distributed parallel machine-learning hyperparameter-tuningreinforcement-learning deep-learning serving python +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Requires-Python: >=3.9 +Requires-Dist: click!=8.3.*,>=7.0 +Requires-Dist: filelock +Requires-Dist: jsonschema +Requires-Dist: msgpack<2.0.0,>=1.0.0 +Requires-Dist: packaging +Requires-Dist: protobuf>=3.20.3 +Requires-Dist: pyyaml +Requires-Dist: requests +Provides-Extra: cgraph +Requires-Dist: cupy-cuda12x; sys_platform != "darwin" and extra == "cgraph" +Provides-Extra: client +Requires-Dist: grpcio!=1.56.0; sys_platform == "darwin" and extra == "client" +Requires-Dist: grpcio; extra == "client" +Provides-Extra: data +Requires-Dist: numpy>=1.20; extra == "data" +Requires-Dist: pandas>=1.3; extra == "data" +Requires-Dist: pyarrow>=9.0.0; extra == "data" +Requires-Dist: fsspec; extra == "data" +Provides-Extra: default +Requires-Dist: aiohttp>=3.7; extra == "default" +Requires-Dist: aiohttp_cors; extra == "default" +Requires-Dist: colorful; extra == "default" +Requires-Dist: py-spy>=0.2.0; python_version < "3.12" and extra == "default" +Requires-Dist: py-spy>=0.4.0; python_version >= "3.12" and extra == "default" +Requires-Dist: requests; extra == "default" +Requires-Dist: grpcio>=1.32.0; python_version < "3.10" and extra == "default" +Requires-Dist: grpcio>=1.42.0; python_version >= "3.10" and extra == "default" +Requires-Dist: opencensus; extra == "default" +Requires-Dist: opentelemetry-sdk>=1.30.0; extra == "default" +Requires-Dist: opentelemetry-exporter-prometheus; extra == "default" +Requires-Dist: opentelemetry-proto; extra == "default" +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "default" +Requires-Dist: prometheus_client>=0.7.1; extra == "default" +Requires-Dist: smart_open; extra == "default" +Requires-Dist: virtualenv!=20.21.1,>=20.0.24; extra == "default" +Provides-Extra: observability +Requires-Dist: memray; sys_platform != "win32" and extra == "observability" +Provides-Extra: serve +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "serve" +Requires-Dist: requests; extra == "serve" +Requires-Dist: virtualenv!=20.21.1,>=20.0.24; extra == "serve" +Requires-Dist: opencensus; extra == "serve" +Requires-Dist: watchfiles; extra == "serve" +Requires-Dist: opentelemetry-sdk>=1.30.0; extra == "serve" +Requires-Dist: aiohttp>=3.7; extra == "serve" +Requires-Dist: py-spy>=0.2.0; python_version < "3.12" and extra == "serve" +Requires-Dist: opentelemetry-exporter-prometheus; extra == "serve" +Requires-Dist: grpcio>=1.32.0; python_version < "3.10" and extra == "serve" +Requires-Dist: fastapi; extra == "serve" +Requires-Dist: opentelemetry-proto; extra == "serve" +Requires-Dist: uvicorn[standard]; extra == "serve" +Requires-Dist: prometheus_client>=0.7.1; extra == "serve" +Requires-Dist: aiohttp_cors; extra == "serve" +Requires-Dist: grpcio>=1.42.0; python_version >= "3.10" and extra == "serve" +Requires-Dist: py-spy>=0.4.0; python_version >= "3.12" and extra == "serve" +Requires-Dist: smart_open; extra == "serve" +Requires-Dist: colorful; extra == "serve" +Requires-Dist: starlette; extra == "serve" +Provides-Extra: tune +Requires-Dist: pandas; extra == "tune" +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "tune" +Requires-Dist: tensorboardX>=1.9; extra == "tune" +Requires-Dist: requests; extra == "tune" +Requires-Dist: pyarrow>=9.0.0; extra == "tune" +Requires-Dist: fsspec; extra == "tune" +Provides-Extra: adag +Requires-Dist: cupy-cuda12x; sys_platform != "darwin" and extra == "adag" +Provides-Extra: serve-grpc +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "serve-grpc" +Requires-Dist: requests; extra == "serve-grpc" +Requires-Dist: virtualenv!=20.21.1,>=20.0.24; extra == "serve-grpc" +Requires-Dist: opencensus; extra == "serve-grpc" +Requires-Dist: watchfiles; extra == "serve-grpc" +Requires-Dist: opentelemetry-sdk>=1.30.0; extra == "serve-grpc" +Requires-Dist: aiohttp>=3.7; extra == "serve-grpc" +Requires-Dist: py-spy>=0.2.0; python_version < "3.12" and extra == "serve-grpc" +Requires-Dist: opentelemetry-exporter-prometheus; extra == "serve-grpc" +Requires-Dist: grpcio>=1.32.0; python_version < "3.10" and extra == "serve-grpc" +Requires-Dist: fastapi; extra == "serve-grpc" +Requires-Dist: opentelemetry-proto; extra == "serve-grpc" +Requires-Dist: uvicorn[standard]; extra == "serve-grpc" +Requires-Dist: prometheus_client>=0.7.1; extra == "serve-grpc" +Requires-Dist: aiohttp_cors; extra == "serve-grpc" +Requires-Dist: grpcio>=1.42.0; python_version >= "3.10" and extra == "serve-grpc" +Requires-Dist: pyOpenSSL; extra == "serve-grpc" +Requires-Dist: py-spy>=0.4.0; python_version >= "3.12" and extra == "serve-grpc" +Requires-Dist: smart_open; extra == "serve-grpc" +Requires-Dist: colorful; extra == "serve-grpc" +Requires-Dist: starlette; extra == "serve-grpc" +Provides-Extra: serve-async-inference +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "serve-async-inference" +Requires-Dist: requests; extra == "serve-async-inference" +Requires-Dist: virtualenv!=20.21.1,>=20.0.24; extra == "serve-async-inference" +Requires-Dist: opencensus; extra == "serve-async-inference" +Requires-Dist: watchfiles; extra == "serve-async-inference" +Requires-Dist: celery; extra == "serve-async-inference" +Requires-Dist: opentelemetry-sdk>=1.30.0; extra == "serve-async-inference" +Requires-Dist: aiohttp>=3.7; extra == "serve-async-inference" +Requires-Dist: py-spy>=0.2.0; python_version < "3.12" and extra == "serve-async-inference" +Requires-Dist: opentelemetry-exporter-prometheus; extra == "serve-async-inference" +Requires-Dist: grpcio>=1.32.0; python_version < "3.10" and extra == "serve-async-inference" +Requires-Dist: fastapi; extra == "serve-async-inference" +Requires-Dist: opentelemetry-proto; extra == "serve-async-inference" +Requires-Dist: uvicorn[standard]; extra == "serve-async-inference" +Requires-Dist: prometheus_client>=0.7.1; extra == "serve-async-inference" +Requires-Dist: aiohttp_cors; extra == "serve-async-inference" +Requires-Dist: grpcio>=1.42.0; python_version >= "3.10" and extra == "serve-async-inference" +Requires-Dist: py-spy>=0.4.0; python_version >= "3.12" and extra == "serve-async-inference" +Requires-Dist: smart_open; extra == "serve-async-inference" +Requires-Dist: colorful; extra == "serve-async-inference" +Requires-Dist: starlette; extra == "serve-async-inference" +Provides-Extra: cpp +Requires-Dist: ray-cpp==2.52.1; extra == "cpp" +Provides-Extra: rllib +Requires-Dist: pandas; extra == "rllib" +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "rllib" +Requires-Dist: tensorboardX>=1.9; extra == "rllib" +Requires-Dist: requests; extra == "rllib" +Requires-Dist: pyarrow>=9.0.0; extra == "rllib" +Requires-Dist: fsspec; extra == "rllib" +Requires-Dist: dm_tree; extra == "rllib" +Requires-Dist: gymnasium==1.1.1; extra == "rllib" +Requires-Dist: lz4; extra == "rllib" +Requires-Dist: ormsgpack==1.7.0; extra == "rllib" +Requires-Dist: pyyaml; extra == "rllib" +Requires-Dist: scipy; extra == "rllib" +Provides-Extra: train +Requires-Dist: pandas; extra == "train" +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "train" +Requires-Dist: tensorboardX>=1.9; extra == "train" +Requires-Dist: requests; extra == "train" +Requires-Dist: pyarrow>=9.0.0; extra == "train" +Requires-Dist: fsspec; extra == "train" +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "train" +Provides-Extra: air +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "air" +Requires-Dist: pyarrow>=9.0.0; extra == "air" +Requires-Dist: requests; extra == "air" +Requires-Dist: virtualenv!=20.21.1,>=20.0.24; extra == "air" +Requires-Dist: opencensus; extra == "air" +Requires-Dist: watchfiles; extra == "air" +Requires-Dist: tensorboardX>=1.9; extra == "air" +Requires-Dist: opentelemetry-sdk>=1.30.0; extra == "air" +Requires-Dist: aiohttp>=3.7; extra == "air" +Requires-Dist: py-spy>=0.2.0; python_version < "3.12" and extra == "air" +Requires-Dist: opentelemetry-exporter-prometheus; extra == "air" +Requires-Dist: grpcio>=1.32.0; python_version < "3.10" and extra == "air" +Requires-Dist: fastapi; extra == "air" +Requires-Dist: pandas; extra == "air" +Requires-Dist: opentelemetry-proto; extra == "air" +Requires-Dist: uvicorn[standard]; extra == "air" +Requires-Dist: pandas>=1.3; extra == "air" +Requires-Dist: prometheus_client>=0.7.1; extra == "air" +Requires-Dist: aiohttp_cors; extra == "air" +Requires-Dist: fsspec; extra == "air" +Requires-Dist: numpy>=1.20; extra == "air" +Requires-Dist: grpcio>=1.42.0; python_version >= "3.10" and extra == "air" +Requires-Dist: py-spy>=0.4.0; python_version >= "3.12" and extra == "air" +Requires-Dist: smart_open; extra == "air" +Requires-Dist: colorful; extra == "air" +Requires-Dist: starlette; extra == "air" +Provides-Extra: all +Requires-Dist: scipy; extra == "all" +Requires-Dist: memray; sys_platform != "win32" and extra == "all" +Requires-Dist: grpcio!=1.56.0; sys_platform == "darwin" and extra == "all" +Requires-Dist: pyarrow>=9.0.0; extra == "all" +Requires-Dist: requests; extra == "all" +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "all" +Requires-Dist: virtualenv!=20.21.1,>=20.0.24; extra == "all" +Requires-Dist: opencensus; extra == "all" +Requires-Dist: watchfiles; extra == "all" +Requires-Dist: celery; extra == "all" +Requires-Dist: opentelemetry-sdk>=1.30.0; extra == "all" +Requires-Dist: aiohttp>=3.7; extra == "all" +Requires-Dist: py-spy>=0.2.0; python_version < "3.12" and extra == "all" +Requires-Dist: tensorboardX>=1.9; extra == "all" +Requires-Dist: opentelemetry-exporter-prometheus; extra == "all" +Requires-Dist: grpcio>=1.32.0; python_version < "3.10" and extra == "all" +Requires-Dist: opentelemetry-proto; extra == "all" +Requires-Dist: fastapi; extra == "all" +Requires-Dist: pandas; extra == "all" +Requires-Dist: dm_tree; extra == "all" +Requires-Dist: uvicorn[standard]; extra == "all" +Requires-Dist: pandas>=1.3; extra == "all" +Requires-Dist: prometheus_client>=0.7.1; extra == "all" +Requires-Dist: aiohttp_cors; extra == "all" +Requires-Dist: grpcio; extra == "all" +Requires-Dist: fsspec; extra == "all" +Requires-Dist: lz4; extra == "all" +Requires-Dist: numpy>=1.20; extra == "all" +Requires-Dist: grpcio>=1.42.0; python_version >= "3.10" and extra == "all" +Requires-Dist: cupy-cuda12x; sys_platform != "darwin" and extra == "all" +Requires-Dist: pyOpenSSL; extra == "all" +Requires-Dist: pyyaml; extra == "all" +Requires-Dist: ormsgpack==1.7.0; extra == "all" +Requires-Dist: py-spy>=0.4.0; python_version >= "3.12" and extra == "all" +Requires-Dist: gymnasium==1.1.1; extra == "all" +Requires-Dist: smart_open; extra == "all" +Requires-Dist: colorful; extra == "all" +Requires-Dist: starlette; extra == "all" +Provides-Extra: all-cpp +Requires-Dist: scipy; extra == "all-cpp" +Requires-Dist: memray; sys_platform != "win32" and extra == "all-cpp" +Requires-Dist: pyarrow>=9.0.0; extra == "all-cpp" +Requires-Dist: grpcio!=1.56.0; sys_platform == "darwin" and extra == "all-cpp" +Requires-Dist: requests; extra == "all-cpp" +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "all-cpp" +Requires-Dist: virtualenv!=20.21.1,>=20.0.24; extra == "all-cpp" +Requires-Dist: ray-cpp==2.52.1; extra == "all-cpp" +Requires-Dist: opencensus; extra == "all-cpp" +Requires-Dist: watchfiles; extra == "all-cpp" +Requires-Dist: celery; extra == "all-cpp" +Requires-Dist: opentelemetry-sdk>=1.30.0; extra == "all-cpp" +Requires-Dist: aiohttp>=3.7; extra == "all-cpp" +Requires-Dist: py-spy>=0.2.0; python_version < "3.12" and extra == "all-cpp" +Requires-Dist: tensorboardX>=1.9; extra == "all-cpp" +Requires-Dist: opentelemetry-exporter-prometheus; extra == "all-cpp" +Requires-Dist: grpcio>=1.32.0; python_version < "3.10" and extra == "all-cpp" +Requires-Dist: opentelemetry-proto; extra == "all-cpp" +Requires-Dist: fastapi; extra == "all-cpp" +Requires-Dist: pandas; extra == "all-cpp" +Requires-Dist: dm_tree; extra == "all-cpp" +Requires-Dist: uvicorn[standard]; extra == "all-cpp" +Requires-Dist: pandas>=1.3; extra == "all-cpp" +Requires-Dist: prometheus_client>=0.7.1; extra == "all-cpp" +Requires-Dist: aiohttp_cors; extra == "all-cpp" +Requires-Dist: grpcio; extra == "all-cpp" +Requires-Dist: smart_open; extra == "all-cpp" +Requires-Dist: fsspec; extra == "all-cpp" +Requires-Dist: lz4; extra == "all-cpp" +Requires-Dist: numpy>=1.20; extra == "all-cpp" +Requires-Dist: grpcio>=1.42.0; python_version >= "3.10" and extra == "all-cpp" +Requires-Dist: cupy-cuda12x; sys_platform != "darwin" and extra == "all-cpp" +Requires-Dist: pyOpenSSL; extra == "all-cpp" +Requires-Dist: pyyaml; extra == "all-cpp" +Requires-Dist: ormsgpack==1.7.0; extra == "all-cpp" +Requires-Dist: py-spy>=0.4.0; python_version >= "3.12" and extra == "all-cpp" +Requires-Dist: gymnasium==1.1.1; extra == "all-cpp" +Requires-Dist: colorful; extra == "all-cpp" +Requires-Dist: starlette; extra == "all-cpp" +Provides-Extra: llm +Requires-Dist: pybind11; extra == "llm" +Requires-Dist: pyarrow>=9.0.0; extra == "llm" +Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3; extra == "llm" +Requires-Dist: requests; extra == "llm" +Requires-Dist: virtualenv!=20.21.1,>=20.0.24; extra == "llm" +Requires-Dist: hf_transfer; extra == "llm" +Requires-Dist: opencensus; extra == "llm" +Requires-Dist: watchfiles; extra == "llm" +Requires-Dist: opentelemetry-sdk>=1.30.0; extra == "llm" +Requires-Dist: aiohttp>=3.7; extra == "llm" +Requires-Dist: py-spy>=0.2.0; python_version < "3.12" and extra == "llm" +Requires-Dist: typer; extra == "llm" +Requires-Dist: jsonref>=1.1.0; extra == "llm" +Requires-Dist: opentelemetry-exporter-prometheus; extra == "llm" +Requires-Dist: grpcio>=1.32.0; python_version < "3.10" and extra == "llm" +Requires-Dist: meson; extra == "llm" +Requires-Dist: fastapi; extra == "llm" +Requires-Dist: opentelemetry-proto; extra == "llm" +Requires-Dist: uvicorn[standard]; extra == "llm" +Requires-Dist: pandas>=1.3; extra == "llm" +Requires-Dist: prometheus_client>=0.7.1; extra == "llm" +Requires-Dist: aiohttp_cors; extra == "llm" +Requires-Dist: ninja; extra == "llm" +Requires-Dist: vllm[audio]>=0.11.0; extra == "llm" +Requires-Dist: fsspec; extra == "llm" +Requires-Dist: numpy>=1.20; extra == "llm" +Requires-Dist: async-timeout; python_version < "3.11" and extra == "llm" +Requires-Dist: grpcio>=1.42.0; python_version >= "3.10" and extra == "llm" +Requires-Dist: py-spy>=0.4.0; python_version >= "3.12" and extra == "llm" +Requires-Dist: jsonschema; extra == "llm" +Requires-Dist: nixl>=0.6.1; extra == "llm" +Requires-Dist: smart_open; extra == "llm" +Requires-Dist: colorful; extra == "llm" +Requires-Dist: starlette; extra == "llm" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: provides-extra +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + +.. image:: https://github.com/ray-project/ray/raw/master/doc/source/images/ray_header_logo.png + +.. image:: https://readthedocs.org/projects/ray/badge/?version=master + :target: http://docs.ray.io/en/master/?badge=master + +.. image:: https://img.shields.io/badge/Ray-Join%20Slack-blue + :target: https://www.ray.io/join-slack + +.. image:: https://img.shields.io/badge/Discuss-Ask%20Questions-blue + :target: https://discuss.ray.io/ + +.. image:: https://img.shields.io/twitter/follow/raydistributed.svg?style=social&logo=twitter + :target: https://x.com/raydistributed + +.. image:: https://img.shields.io/badge/Get_started_for_free-3C8AE9?logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAABKElEQVQ4Ea2TvWoCQRRGnWCVWChIIlikC9hpJdikSbGgaONbpAoY8gKBdAGfwkfwKQypLQ1sEGyMYhN1Pd%2B6A8PqwBZeOHt%2FvsvMnd3ZXBRFPQjBZ9K6OY8ZxF%2B0IYw9PW3qz8aY6lk92bZ%2BVqSI3oC9T7%2FyCVnrF1ngj93us%2B540sf5BrCDfw9b6jJ5lx%2FyjtGKBBXc3cnqx0INN4ImbI%2Bl%2BPnI8zWfFEr4chLLrWHCp9OO9j19Kbc91HX0zzzBO8EbLK2Iv4ZvNO3is3h6jb%2BCwO0iL8AaWqB7ILPTxq3kDypqvBuYuwswqo6wgYJbT8XxBPZ8KS1TepkFdC79TAHHce%2F7LbVioi3wEfTpmeKtPRGEeoldSP%2FOeoEftpP4BRbgXrYZefsAI%2BP9JU7ImyEAAAAASUVORK5CYII%3D + :target: https://www.anyscale.com/ray-on-anyscale?utm_source=github&utm_medium=ray_readme&utm_campaign=get_started_badge + +Ray is a unified framework for scaling AI and Python applications. Ray consists of a core distributed runtime and a set of AI libraries for simplifying ML compute: + +.. image:: https://github.com/ray-project/ray/raw/master/doc/source/images/what-is-ray-padded.svg + +.. + https://docs.google.com/drawings/d/1Pl8aCYOsZCo61cmp57c7Sja6HhIygGCvSZLi_AuBuqo/edit + +Learn more about `Ray AI Libraries`_: + +- `Data`_: Scalable Datasets for ML +- `Train`_: Distributed Training +- `Tune`_: Scalable Hyperparameter Tuning +- `RLlib`_: Scalable Reinforcement Learning +- `Serve`_: Scalable and Programmable Serving + +Or more about `Ray Core`_ and its key abstractions: + +- `Tasks`_: Stateless functions executed in the cluster. +- `Actors`_: Stateful worker processes created in the cluster. +- `Objects`_: Immutable values accessible across the cluster. + +Learn more about Monitoring and Debugging: + +- Monitor Ray apps and clusters with the `Ray Dashboard `__. +- Debug Ray apps with the `Ray Distributed Debugger `__. + +Ray runs on any machine, cluster, cloud provider, and Kubernetes, and features a growing +`ecosystem of community integrations`_. + +Install Ray with: ``pip install ray``. For nightly wheels, see the +`Installation page `__. + +.. _`Serve`: https://docs.ray.io/en/latest/serve/index.html +.. _`Data`: https://docs.ray.io/en/latest/data/dataset.html +.. _`Workflow`: https://docs.ray.io/en/latest/workflows/ +.. _`Train`: https://docs.ray.io/en/latest/train/train.html +.. _`Tune`: https://docs.ray.io/en/latest/tune/index.html +.. _`RLlib`: https://docs.ray.io/en/latest/rllib/index.html +.. _`ecosystem of community integrations`: https://docs.ray.io/en/latest/ray-overview/ray-libraries.html + + +Why Ray? +-------- + +Today's ML workloads are increasingly compute-intensive. As convenient as they are, single-node development environments such as your laptop cannot scale to meet these demands. + +Ray is a unified way to scale Python and AI applications from a laptop to a cluster. + +With Ray, you can seamlessly scale the same code from a laptop to a cluster. Ray is designed to be general-purpose, meaning that it can performantly run any kind of workload. If your application is written in Python, you can scale it with Ray, no other infrastructure required. + +More Information +---------------- + +- `Documentation`_ +- `Ray Architecture whitepaper`_ +- `Exoshuffle: large-scale data shuffle in Ray`_ +- `Ownership: a distributed futures system for fine-grained tasks`_ +- `RLlib paper`_ +- `Tune paper`_ + +*Older documents:* + +- `Ray paper`_ +- `Ray HotOS paper`_ +- `Ray Architecture v1 whitepaper`_ + +.. _`Ray AI Libraries`: https://docs.ray.io/en/latest/ray-air/getting-started.html +.. _`Ray Core`: https://docs.ray.io/en/latest/ray-core/walkthrough.html +.. _`Tasks`: https://docs.ray.io/en/latest/ray-core/tasks.html +.. _`Actors`: https://docs.ray.io/en/latest/ray-core/actors.html +.. _`Objects`: https://docs.ray.io/en/latest/ray-core/objects.html +.. _`Documentation`: http://docs.ray.io/en/latest/index.html +.. _`Ray Architecture v1 whitepaper`: https://docs.google.com/document/d/1lAy0Owi-vPz2jEqBSaHNQcy2IBSDEHyXNOQZlGuj93c/preview +.. _`Ray Architecture whitepaper`: https://docs.google.com/document/d/1tBw9A4j62ruI5omIJbMxly-la5w4q_TjyJgJL_jN2fI/preview +.. _`Exoshuffle: large-scale data shuffle in Ray`: https://arxiv.org/abs/2203.05072 +.. _`Ownership: a distributed futures system for fine-grained tasks`: https://www.usenix.org/system/files/nsdi21-wang.pdf +.. _`Ray paper`: https://arxiv.org/abs/1712.05889 +.. _`Ray HotOS paper`: https://arxiv.org/abs/1703.03924 +.. _`RLlib paper`: https://arxiv.org/abs/1712.09381 +.. _`Tune paper`: https://arxiv.org/abs/1807.05118 + +Getting Involved +---------------- + +.. list-table:: + :widths: 25 50 25 25 + :header-rows: 1 + + * - Platform + - Purpose + - Estimated Response Time + - Support Level + * - `Discourse Forum`_ + - For discussions about development and questions about usage. + - < 1 day + - Community + * - `GitHub Issues`_ + - For reporting bugs and filing feature requests. + - < 2 days + - Ray OSS Team + * - `Slack`_ + - For collaborating with other Ray users. + - < 2 days + - Community + * - `StackOverflow`_ + - For asking questions about how to use Ray. + - 3-5 days + - Community + * - `Meetup Group`_ + - For learning about Ray projects and best practices. + - Monthly + - Ray DevRel + * - `Twitter`_ + - For staying up-to-date on new features. + - Daily + - Ray DevRel + +.. _`Discourse Forum`: https://discuss.ray.io/ +.. _`GitHub Issues`: https://github.com/ray-project/ray/issues +.. _`StackOverflow`: https://stackoverflow.com/questions/tagged/ray +.. _`Meetup Group`: https://www.meetup.com/Bay-Area-Ray-Meetup/ +.. _`Twitter`: https://x.com/raydistributed +.. _`Slack`: https://www.ray.io/join-slack?utm_source=github&utm_medium=ray_readme&utm_campaign=getting_involved diff --git a/lib/python3.12/site-packages/ray-2.52.1.dist-info/RECORD b/lib/python3.12/site-packages/ray-2.52.1.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..1cef673da6701535c874796e0ebc5c6096ecdb55 --- /dev/null +++ b/lib/python3.12/site-packages/ray-2.52.1.dist-info/RECORD @@ -0,0 +1,4871 @@ +../../../bin/ray,sha256=C9V05DbIE0tCouyEGGLeupJTWhcZUYtr7K2e3iEGlhk,254 +../../../bin/serve,sha256=ApdHl81vyOuf0AHL8Z1DFgchFFLEnYYYTHRKn00y6LE,250 +../../../bin/tune,sha256=ciP0qydQlSSBAj-x63NZGOP-zU6u0wHKFFG4pzYGIfg,253 +ray-2.52.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray-2.52.1.dist-info/METADATA,sha256=9ZbIfO555OXtQZZE5YfxlVvENlASuppNWbJyF_9kWyM,21837 +ray-2.52.1.dist-info/RECORD,, +ray-2.52.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray-2.52.1.dist-info/WHEEL,sha256=6TsICjgOR7isz_jYr-ssV7RSRmh1_0Z7_b5ESlzfzVY,104 +ray-2.52.1.dist-info/entry_points.txt,sha256=HzQIZGspUzWojBmR9Xvb28m4bPhi-yJjV1U1pdlRkyY,111 +ray-2.52.1.dist-info/top_level.txt,sha256=GJvmpjCEmVfQsBskWDIj2hraOPW3F8A46rlbd9c3vc0,4 +ray/__init__.py,sha256=VOgUobwPfCapLKLkEbVtcwTC0-HkSQ38b9yoG-WkgOg,7661 +ray/__pycache__/__init__.cpython-312.pyc,, +ray/__pycache__/_version.cpython-312.pyc,, +ray/__pycache__/actor.cpython-312.pyc,, +ray/__pycache__/client_builder.cpython-312.pyc,, +ray/__pycache__/cluster_utils.cpython-312.pyc,, +ray/__pycache__/cross_language.cpython-312.pyc,, +ray/__pycache__/exceptions.cpython-312.pyc,, +ray/__pycache__/job_config.cpython-312.pyc,, +ray/__pycache__/remote_function.cpython-312.pyc,, +ray/__pycache__/runtime_context.cpython-312.pyc,, +ray/__pycache__/setup-dev.cpython-312.pyc,, +ray/__pycache__/types.cpython-312.pyc,, +ray/_common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_common/__pycache__/__init__.cpython-312.pyc,, +ray/_common/__pycache__/constants.cpython-312.pyc,, +ray/_common/__pycache__/deprecation.cpython-312.pyc,, +ray/_common/__pycache__/filters.cpython-312.pyc,, +ray/_common/__pycache__/formatters.cpython-312.pyc,, +ray/_common/__pycache__/network_utils.cpython-312.pyc,, +ray/_common/__pycache__/pydantic_compat.cpython-312.pyc,, +ray/_common/__pycache__/ray_constants.cpython-312.pyc,, +ray/_common/__pycache__/ray_option_utils.cpython-312.pyc,, +ray/_common/__pycache__/retry.cpython-312.pyc,, +ray/_common/__pycache__/serialization.cpython-312.pyc,, +ray/_common/__pycache__/signature.cpython-312.pyc,, +ray/_common/__pycache__/test_utils.cpython-312.pyc,, +ray/_common/__pycache__/utils.cpython-312.pyc,, +ray/_common/constants.py,sha256=G0Wt7SmWzDCbXmp6MjH3lc0WiD8Q25wl7JEdOAZAWhU,343 +ray/_common/deprecation.py,sha256=xIhQO1VjNzgYy7h_Wg3qZ4SfBv003xfVY2cU_5qsi_U,4883 +ray/_common/filters.py,sha256=ZB8lAsvICQFfRiZeQLSuAaFnJDuZ6tDQNZmvQRBV0v4,1962 +ray/_common/formatters.py,sha256=q8BOWvHgcRU8UsK7x_biFvcNHNRVw44BLlBvMabx3F4,4585 +ray/_common/network_utils.py,sha256=fv4WtXQAPEkPjxJ6isENG5xx1cBSConQaVQdrzwZzCA,3099 +ray/_common/pydantic_compat.py,sha256=818OJn_pmwdL2GJQCZaR8jEjOEnmore6nK7PDJcj6YI,3067 +ray/_common/ray_constants.py,sha256=gdIwSG7kK6oUKNxCUSHmI2OSqQ4dNOottdF-8bXQntM,213 +ray/_common/ray_option_utils.py,sha256=yRLc9flF25v20HH19_6V4O9PlKeP1cbxGyPqxNG38r0,14743 +ray/_common/retry.py,sha256=72BIf2SJYS4cOKEqoIuulelowKokJtCM-1uDe-f7klM,2683 +ray/_common/serialization.py,sha256=JYidd4HyewNdQCwa1LsxzPbyGamOMbA8npJ9j2tBtuI,1010 +ray/_common/signature.py,sha256=Ij0PgmvRHKgapPAke7DKf-mqB-1ii1YWXvMhrTsFjDQ,5829 +ray/_common/test_utils.py,sha256=JN8Rv4aR3dqgmsaY8pL3uPXF8igP23x0n2dRcT69sWs,7401 +ray/_common/usage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_common/usage/__pycache__/__init__.cpython-312.pyc,, +ray/_common/usage/__pycache__/usage_constants.cpython-312.pyc,, +ray/_common/usage/__pycache__/usage_lib.cpython-312.pyc,, +ray/_common/usage/usage_constants.py,sha256=wYcxpwBfh6U-qNQ2BlAp1W938ie486rghfNwOXtOQPk,2421 +ray/_common/usage/usage_lib.py,sha256=wUn8BrcodjZx58uNcBh3PBtgWz3pYb_DldyE8z5Macs,35846 +ray/_common/utils.py,sha256=7ovGI-nmg_riHdpQTIc22-h47huUxT-6wkA9DNGnxJc,12440 +ray/_private/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/__pycache__/__init__.cpython-312.pyc,, +ray/_private/__pycache__/arrow_serialization.cpython-312.pyc,, +ray/_private/__pycache__/arrow_utils.cpython-312.pyc,, +ray/_private/__pycache__/async_compat.cpython-312.pyc,, +ray/_private/__pycache__/async_utils.cpython-312.pyc,, +ray/_private/__pycache__/authentication_test_utils.cpython-312.pyc,, +ray/_private/__pycache__/auto_init_hook.cpython-312.pyc,, +ray/_private/__pycache__/client_mode_hook.cpython-312.pyc,, +ray/_private/__pycache__/collections_utils.cpython-312.pyc,, +ray/_private/__pycache__/compat.cpython-312.pyc,, +ray/_private/__pycache__/conftest_utils.cpython-312.pyc,, +ray/_private/__pycache__/custom_types.cpython-312.pyc,, +ray/_private/__pycache__/dict.cpython-312.pyc,, +ray/_private/__pycache__/external_storage.cpython-312.pyc,, +ray/_private/__pycache__/function_manager.cpython-312.pyc,, +ray/_private/__pycache__/gc_collect_manager.cpython-312.pyc,, +ray/_private/__pycache__/gcs_pubsub.cpython-312.pyc,, +ray/_private/__pycache__/gcs_utils.cpython-312.pyc,, +ray/_private/__pycache__/grpc_utils.cpython-312.pyc,, +ray/_private/__pycache__/inspect_util.cpython-312.pyc,, +ray/_private/__pycache__/internal_api.cpython-312.pyc,, +ray/_private/__pycache__/label_utils.cpython-312.pyc,, +ray/_private/__pycache__/log.cpython-312.pyc,, +ray/_private/__pycache__/log_monitor.cpython-312.pyc,, +ray/_private/__pycache__/logging_utils.cpython-312.pyc,, +ray/_private/__pycache__/memory_monitor.cpython-312.pyc,, +ray/_private/__pycache__/metrics_agent.cpython-312.pyc,, +ray/_private/__pycache__/node.cpython-312.pyc,, +ray/_private/__pycache__/object_ref_generator.cpython-312.pyc,, +ray/_private/__pycache__/parameter.cpython-312.pyc,, +ray/_private/__pycache__/path_utils.cpython-312.pyc,, +ray/_private/__pycache__/process_watcher.cpython-312.pyc,, +ray/_private/__pycache__/profiling.cpython-312.pyc,, +ray/_private/__pycache__/prometheus_exporter.cpython-312.pyc,, +ray/_private/__pycache__/protobuf_compat.cpython-312.pyc,, +ray/_private/__pycache__/ray_client_microbenchmark.cpython-312.pyc,, +ray/_private/__pycache__/ray_cluster_perf.cpython-312.pyc,, +ray/_private/__pycache__/ray_constants.cpython-312.pyc,, +ray/_private/__pycache__/ray_experimental_perf.cpython-312.pyc,, +ray/_private/__pycache__/ray_microbenchmark_helpers.cpython-312.pyc,, +ray/_private/__pycache__/ray_perf.cpython-312.pyc,, +ray/_private/__pycache__/ray_process_reaper.cpython-312.pyc,, +ray/_private/__pycache__/resource_and_label_spec.cpython-312.pyc,, +ray/_private/__pycache__/resource_isolation_config.cpython-312.pyc,, +ray/_private/__pycache__/serialization.cpython-312.pyc,, +ray/_private/__pycache__/services.cpython-312.pyc,, +ray/_private/__pycache__/state.cpython-312.pyc,, +ray/_private/__pycache__/state_api_test_utils.cpython-312.pyc,, +ray/_private/__pycache__/test_utils.cpython-312.pyc,, +ray/_private/__pycache__/tls_utils.cpython-312.pyc,, +ray/_private/__pycache__/utils.cpython-312.pyc,, +ray/_private/__pycache__/worker.cpython-312.pyc,, +ray/_private/accelerators/__init__.py,sha256=Nfc8eg0scyKhoAx9pOt8lF_d2dGYFGml0t-lf7hHHi8,3268 +ray/_private/accelerators/__pycache__/__init__.cpython-312.pyc,, +ray/_private/accelerators/__pycache__/accelerator.cpython-312.pyc,, +ray/_private/accelerators/__pycache__/amd_gpu.cpython-312.pyc,, +ray/_private/accelerators/__pycache__/hpu.cpython-312.pyc,, +ray/_private/accelerators/__pycache__/intel_gpu.cpython-312.pyc,, +ray/_private/accelerators/__pycache__/neuron.cpython-312.pyc,, +ray/_private/accelerators/__pycache__/npu.cpython-312.pyc,, +ray/_private/accelerators/__pycache__/nvidia_gpu.cpython-312.pyc,, +ray/_private/accelerators/__pycache__/rbln.cpython-312.pyc,, +ray/_private/accelerators/__pycache__/tpu.cpython-312.pyc,, +ray/_private/accelerators/accelerator.py,sha256=7UjYLw90zFzcbGy15IAzSBabcBgo7Z7SIQ2CE57C3nA,5743 +ray/_private/accelerators/amd_gpu.py,sha256=d7eJWRxigQ7J8-pQOkLLThU2ej1YeuO8hOnN1X-ltSQ,5021 +ray/_private/accelerators/hpu.py,sha256=iOt_6hd57pixHxaLcjt47XSeb-RZqaUsf73t8Mx8Iek,3701 +ray/_private/accelerators/intel_gpu.py,sha256=38ugVUTogY3xkJqUD8Nluuk2_b_6FiUP3JcKVgaBuVI,3176 +ray/_private/accelerators/neuron.py,sha256=al2WvKAz1wcE_XjnQfRWsCVIAuYLylmeONZqt4P4loo,4503 +ray/_private/accelerators/npu.py,sha256=tK9CqENLFGPyoAng4spoULhacKnC0jmK6sqfXtoqe7I,2879 +ray/_private/accelerators/nvidia_gpu.py,sha256=AEodc2q52F7esqskIaaOfM1eVmCKCQSa2kGyWaMbm2g,4087 +ray/_private/accelerators/rbln.py,sha256=nsM30aGvyDrbbeKbFkzHi509b1SyQf3_XWXpJUKXu9E,2533 +ray/_private/accelerators/tpu.py,sha256=oyEmdDoOrNUi5Pt2265KiqJe_GB4V3z91zLkEqyGQWg,24928 +ray/_private/arrow_serialization.py,sha256=Jd4cx2drO2M3yta3k3vYE69swhjUkxYinAu6HnxTn18,27723 +ray/_private/arrow_utils.py,sha256=Vb1w1DmWbedPwzu17dCHFvJEmnOLP93IBkRM6olmTfc,4771 +ray/_private/async_compat.py,sha256=apwUkhuQ2tpaAf5C-YAStn17TtoudHIP8RIFjzvZTBQ,1411 +ray/_private/async_utils.py,sha256=sHHqklv7NYpjp8lOOG8QCkNcerAxGg1zyLUvDdR7GDk,1883 +ray/_private/authentication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/authentication/__pycache__/__init__.cpython-312.pyc,, +ray/_private/authentication/__pycache__/authentication_constants.cpython-312.pyc,, +ray/_private/authentication/__pycache__/authentication_token_generator.cpython-312.pyc,, +ray/_private/authentication/__pycache__/authentication_token_setup.cpython-312.pyc,, +ray/_private/authentication/__pycache__/authentication_utils.cpython-312.pyc,, +ray/_private/authentication/__pycache__/grpc_authentication_client_interceptor.cpython-312.pyc,, +ray/_private/authentication/__pycache__/grpc_authentication_server_interceptor.cpython-312.pyc,, +ray/_private/authentication/__pycache__/http_token_authentication.cpython-312.pyc,, +ray/_private/authentication/authentication_constants.py,sha256=bc4hU4lOL9hYkgMMzxZMUHhX1ud9JTZE3YCRa7NZ5Io,604 +ray/_private/authentication/authentication_token_generator.py,sha256=YWqm8kHostOnCkAnLyAh1JQsavlsCDxzU1roR1fjVek,251 +ray/_private/authentication/authentication_token_setup.py,sha256=Hfyes2tcd9ZjX0hcsE33yMQ2ySyGNGtlxsqWE9s4RAI,3504 +ray/_private/authentication/authentication_utils.py,sha256=oV9kmM48kZ7tv29AJQK84PhcEu2zQ8mm2kEV0ix004U,1157 +ray/_private/authentication/grpc_authentication_client_interceptor.py,sha256=ZQF7iuzX2J3DtbXlivJ-lbgIo0JiynFAUGQXVZnH6ak,4774 +ray/_private/authentication/grpc_authentication_server_interceptor.py,sha256=U8135Sls9yWZvesKTPgu0VUlXwyKTZAsmTDe4leFgh8,7365 +ray/_private/authentication/http_token_authentication.py,sha256=Q1LF0tHOFQDOW5c77iTS7WghrtZvwpv9s16LPRDS0Jo,4503 +ray/_private/authentication_test_utils.py,sha256=_zKSxyMXBjK15p6qM9R83Zn18bRxS6JAdHzY0tXfEkU,5202 +ray/_private/auto_init_hook.py,sha256=bRvPtr5a0S1q_NOTjKe50K4bc0AYpvxbvnDTcSTdTtc,793 +ray/_private/client_mode_hook.py,sha256=yXf6EJSaLzuaKcj5_BAfxUkHv1V0OOa2-YCkuChpprw,6450 +ray/_private/collections_utils.py,sha256=MbM42Y4jvTPGyNDtobFFDxjLjRZxqmjWLi9W3i-MEHE,277 +ray/_private/compat.py,sha256=UnC8qxGUeAU4hWqhF1nGecRrBkYC-8yUsidlKMz5v7g,1343 +ray/_private/conftest_utils.py,sha256=92hVLjQ-FyzC35_HcR03jhqrSGGqB7YBnm1vm_OLauw,417 +ray/_private/custom_types.py,sha256=5LShTHT9A45y0NTLHbEQMsD1iPAzR8_euzrmEMIfANE,5027 +ray/_private/dict.py,sha256=FACFqxBCWLeOG_dQ-m080ykso0dfWSf5ilYO4mJzhh8,8463 +ray/_private/event/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/event/__pycache__/__init__.cpython-312.pyc,, +ray/_private/event/__pycache__/event_logger.cpython-312.pyc,, +ray/_private/event/__pycache__/export_event_logger.cpython-312.pyc,, +ray/_private/event/event_logger.py,sha256=vrCBM6tE1JxlsE4vp7vhlsiKDAeuAdscVQ0E0rCyQ_o,6184 +ray/_private/event/export_event_logger.py,sha256=leXFq1wyqBB-K1kgROPRYt33WvHNaIOS_Br7aSCtyhY,9091 +ray/_private/external_storage.py,sha256=wIZoEoY5Sz0oVOHzQ9OuULUF8-45fcjG7mmBt_KI-2c,24748 +ray/_private/function_manager.py,sha256=CdMrZVcOCfBFaNWkklnTlJwMfdMgb1mAGOrWnhY3sKE,29610 +ray/_private/gc_collect_manager.py,sha256=RDVWH9ymn_Ae1UV6Mzo9rEByi1EzW_jVjJO7KyWikl4,2203 +ray/_private/gcs_pubsub.py,sha256=a77TsssTDX4AvrFutLt_4RxFrXJRxs-DEU1iml0qrRk,9446 +ray/_private/gcs_utils.py,sha256=Tz0N3agPN5rgpzHyNY11E97WDVAxw7Ft9KGiQ4ZPu2Y,4270 +ray/_private/grpc_utils.py,sha256=AvVDcnkAYx2VqJZYjvoc0_cSeApvYqNtHu_Q7XuhnJ0,5706 +ray/_private/inspect_util.py,sha256=Qyqz0xhcpPobIpAq1yXATbAf8dgD3i9lGjVG1cp0fJU,1560 +ray/_private/internal_api.py,sha256=cIzcUGire1c6loxUnqeiIkMdz_0UTqwnKC4RrUSmIGY,9204 +ray/_private/label_utils.py,sha256=CvcLWdZdeY8HbvJdf38MUZ189v9nQk7QSM4sBTFjcng,8622 +ray/_private/log.py,sha256=ufxXoHtFmFVy32ZR9WhSyuKmW5p_r-rhfXwVKKlrvjA,4780 +ray/_private/log_monitor.py,sha256=QJ4iM1QMoWzOlwZ24bp7ID0mXVz1mVxWM6m7mKbG3A0,24538 +ray/_private/logging_utils.py,sha256=zxDA4j-lK0zajUIFyKpvcvAs2hCOKJJ3QtcFRwgobNE,1885 +ray/_private/memory_monitor.py,sha256=qfSW3Ok-VYoI-o6nkOUf_AVBaU-7JPNaDy7FFhZ2WfU,6086 +ray/_private/metrics_agent.py,sha256=FFhLggayDQePxpXcUnQa27aOOyuYoo3pOePo7eZbqMI,34241 +ray/_private/node.py,sha256=lqhKl23b9RejxmVji586992zvjE2Pl_ps5ih22QX7gQ,74618 +ray/_private/object_ref_generator.py,sha256=a5inBBqEfjdL1LKjuUmNfQquclVtb0-EJL14ugzL3OI,10554 +ray/_private/parameter.py,sha256=ED0DTAmJoQzGmCoLC_l0P2rUX4SErpYVJ0_WE77MKII,22122 +ray/_private/path_utils.py,sha256=Sg1cSoEAYz-Q1XPHjo8IBvn3M35e2rcVRWRj8uYhp5M,1128 +ray/_private/process_watcher.py,sha256=z4sd82h9JWzJeizNqSoStgss6Kse_ACyxCRR86PTIyQ,7500 +ray/_private/profiling.py,sha256=vVs87pynjGXGrxCVZoZq8KgPMWOSectRgsmF-msdtm8,8786 +ray/_private/prometheus_exporter.py,sha256=7hdYfhQ4bz1dUpZeaT9jbzlDmG0ztVeXReK3MBcaLZg,13521 +ray/_private/protobuf_compat.py,sha256=otIASMKEZqdEEzoCwuu4dlKYw8qy6mTsZLHGtLcp5CY,1936 +ray/_private/ray_client_microbenchmark.py,sha256=0r5YblHrU_KJu-gzWvvjTefhpK3XQL74kHE0yPwuLOE,2901 +ray/_private/ray_cluster_perf.py,sha256=W9Xglrh2yZmpmM9C0RrL7sJ2h9HrzcdEOHnfXCS68FU,1208 +ray/_private/ray_constants.py,sha256=3bxP8vaV1xn2LzwC0o6j028n9IM8rt28qBDgW6egnO0,24609 +ray/_private/ray_experimental_perf.py,sha256=QxqDrtnwYlhQSr-ZPn8y_tpOJs9k2X_W9hRf1FPeKPc,10945 +ray/_private/ray_logging/__init__.py,sha256=TddjQTuLFJLr-8OhYZNSqPwhbXYFXs8ZI85yc5wC_xg,13219 +ray/_private/ray_logging/__pycache__/__init__.cpython-312.pyc,, +ray/_private/ray_logging/__pycache__/constants.cpython-312.pyc,, +ray/_private/ray_logging/__pycache__/default_impl.cpython-312.pyc,, +ray/_private/ray_logging/__pycache__/logging_config.cpython-312.pyc,, +ray/_private/ray_logging/constants.py,sha256=J_gyyWOxreGgau3kTcFF1AQuw5bxeNv067aQVq9Ww6Y,1317 +ray/_private/ray_logging/default_impl.py,sha256=U7yTVUfIsQKP3MBwXuJjT3sM3Kl9YIQ441_RR_pJ4yA,156 +ray/_private/ray_logging/logging_config.py,sha256=gu0RBLQEKvY_HkQlZlvzHZS8mIFY1n3bZd4d3xEqen8,6464 +ray/_private/ray_microbenchmark_helpers.py,sha256=34BCPDh53E8nSsBwgiaz7m-XjXbkoHLi9yrxgxaC7vI,2550 +ray/_private/ray_perf.py,sha256=dIEM_XlLlyZKz8atidagazHiX9UX4wlFsF_OeixQTAo,8982 +ray/_private/ray_process_reaper.py,sha256=g1aG_umFCw3dGql89o7Vuhlu8_ebqy60f6MUQEZUJN4,2069 +ray/_private/resource_and_label_spec.py,sha256=PhAJVZLiUyD8sHMR04f6NgdvMiI51AhYlw8_adsRNU4,19661 +ray/_private/resource_isolation_config.py,sha256=ygRbUp-sjq4qXQ7OAtdW3IazGPZu1q7gYTIXrTX7-EQ,13648 +ray/_private/runtime_env/__init__.py,sha256=FufsPD2IbH6u2OCTWBnZuhtn9qG4wc7EX7MaYJs73Fg,221 +ray/_private/runtime_env/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/_clonevirtualenv.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/conda.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/conda_utils.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/constants.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/context.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/default_impl.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/dependency_utils.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/image_uri.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/java_jars.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/nsight.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/packaging.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/pip.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/plugin.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/plugin_schema_manager.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/protocol.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/py_executable.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/py_modules.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/rocprof_sys.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/setup_hook.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/uri_cache.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/utils.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/uv.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/uv_runtime_env_hook.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/validation.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/virtualenv_utils.cpython-312.pyc,, +ray/_private/runtime_env/__pycache__/working_dir.cpython-312.pyc,, +ray/_private/runtime_env/_clonevirtualenv.py,sha256=Jv10azUwb2sIOoSyBS26XoLXhZdF0HK_HaGHSZUkpnI,10970 +ray/_private/runtime_env/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/runtime_env/agent/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/__pycache__/main.cpython-312.pyc,, +ray/_private/runtime_env/agent/__pycache__/runtime_env_agent.cpython-312.pyc,, +ray/_private/runtime_env/agent/__pycache__/runtime_env_consts.cpython-312.pyc,, +ray/_private/runtime_env/agent/main.py,sha256=BHx7XAzXfxy8loDF0bOG-cU1o-O_hGz0-Ec0K3AvmIU,7971 +ray/_private/runtime_env/agent/runtime_env_agent.py,sha256=A6rq2E-YBdbrP34s3GWS0OWT4qXTgugJnkQq7m0YstU,26193 +ray/_private/runtime_env/agent/runtime_env_consts.py,sha256=V5VegPHpS3RftGRZ-Y0EIFfiQT93kaRyM8fzQm0XUEo,750 +ray/_private/runtime_env/agent/thirdparty_files/__pycache__/typing_extensions.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs-2.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs-2.6.1.dist-info/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936 +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs-2.6.1.dist-info/METADATA,sha256=NSXlhJwAfi380eEjAo7BQ4P_TVal9xi0qkyZWibMsVM,5915 +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs-2.6.1.dist-info/RECORD,sha256=IAgr_a28hPlzJnG5WLMDjMZlrRJUBO3NEGCh2eKnL1M,1209 +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs-2.6.1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88 +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__init__.py,sha256=x7kktHEtaD9quBcWDJPuLeKyjuVAI-Jj14S9B_5hcTs,361 +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/_staggered.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/impl.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/types.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/__pycache__/utils.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/_staggered.py,sha256=edfVowFx-P-ywJjIEF3MdPtEMVODujV6CeMYr65otac,6900 +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/impl.py,sha256=Dlcm2mTJ28ucrGnxkb_fo9CZzLAkOOBizOt7dreBbXE,9681 +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/types.py,sha256=YZJIAnyoV4Dz0WFtlaf_OyE4EW7Xus1z7aIfNI6tDDQ,425 +ray/_private/runtime_env/agent/thirdparty_files/aiohappyeyeballs/utils.py,sha256=on9GxIR0LhEfZu8P6Twi9hepX9zDanuZM20MWsb3xlQ,3028 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.13.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.13.2.dist-info/METADATA,sha256=3xr8ZyYTInh909TqCdZhKIC37g5nTgyP-Nj_yglCs5A,8135 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.13.2.dist-info/RECORD,sha256=X_HwyHbPrFDJw4GtmIIdJxkgtWmqCMvcFMteuMFIefw,9920 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.13.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.13.2.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.13.2.dist-info/licenses/LICENSE.txt,sha256=n4DQ2311WpQdtFchcsJw7L2PCCuiFd3QlZhZQu2Uqes,588 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.13.2.dist-info/licenses/vendor/llhttp/LICENSE,sha256=68qFTgE0zSVtZzYnwgSZ9CV363S6zwi58ltianPJEnc,1105 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp-3.13.2.dist-info/top_level.txt,sha256=iv-JIaacmTl-hSho3QmphcKnbRRYx1st47yjz_178Ro,8 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/.hash/_cparser.pxd.hash,sha256=pjs-sEXNw_eijXGAedwG-BHnlFp8B7sOCgUagIWaU2A,121 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/.hash/_find_header.pxd.hash,sha256=_mbpD6vM-CVCKq3ulUvsOAz5Wdo88wrDzfpOsMQaMNA,125 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/.hash/_http_parser.pyx.hash,sha256=ju4DG_uNv8rTD6pu3IunE1ysx3ZbH4OjiQHUb_URSoA,125 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/.hash/_http_writer.pyx.hash,sha256=9txOh7t7c3y-vLmiuEY5dltmXvEo0CYyU4U853yyv9E,125 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/.hash/hdrs.py.hash,sha256=v6IaKbsxjsdQxBzhb5AjP0x_9G3rUe84D7avf7AI4cs,116 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__init__.py,sha256=YJ2jOOSU0hSTbloGbi5-jtcDfSmpBp2RTQEQAt0ccOA,8302 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/_cookie_helpers.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/abc.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/base_protocol.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/client.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/client_exceptions.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/client_middleware_digest_auth.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/client_middlewares.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/client_proto.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/client_reqrep.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/client_ws.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/compression_utils.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/connector.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/cookiejar.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/formdata.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/hdrs.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/helpers.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/http.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/http_exceptions.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/http_parser.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/http_websocket.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/http_writer.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/log.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/multipart.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/payload.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/payload_streamer.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/pytest_plugin.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/resolver.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/streams.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/tcp_helpers.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/test_utils.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/tracing.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/typedefs.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_app.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_exceptions.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_fileresponse.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_log.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_middlewares.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_protocol.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_request.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_response.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_routedef.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_runner.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_server.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_urldispatcher.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/web_ws.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/__pycache__/worker.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_cookie_helpers.py,sha256=INC-1MTQU7yJqBVmV48Fw30kzZH47KdVUrP_bbfpGvs,13647 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_cparser.pxd,sha256=UnbUYCHg4NdXfgyRVYAMv2KTLWClB4P-xCrvtj_r7ew,4295 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_find_header.pxd,sha256=0GfwFCPN2zxEKTO1_MA5sYq2UfzsG8kcV3aTqvwlz3g,68 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_headers.pxi,sha256=n701k28dVPjwRnx5j6LpJhLTfj7dqu2vJt7f0O60Oyg,2007 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_http_parser.cpython-312-x86_64-linux-gnu.so,sha256=3YrWjkYJoctR2UCDpht9IcnT4PE18AnSoHlgI11fN5k,2824440 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_http_parser.pyx,sha256=tmA1PaJn7H8U1nyXtoHJV44pxYVzqXAf1UgJaYPaw28,28219 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_http_writer.cpython-312-x86_64-linux-gnu.so,sha256=mWC-4rsbntVD1V5ZEKEpSW_sm63V0XRe1fDR0lygipo,539144 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_http_writer.pyx,sha256=VlFEBM6HoVv8a0AAJtc6JwFlsv2-cDE8-gB94p3dfhQ,4664 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/.hash/mask.pxd.hash,sha256=Y0zBddk_ck3pi9-BFzMcpkcvCKvwvZ4GTtZFb9u1nxQ,128 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/.hash/mask.pyx.hash,sha256=90owpXYM8_kIma4KUcOxhWSk-Uv4NVMBoCYeFM1B3d0,128 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/.hash/reader_c.pxd.hash,sha256=5xf3oobk6vx4xbJm-xtZ1_QufB8fYFtLQV2MNdqUc1w,132 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/__init__.py,sha256=Mar3R9_vBN_Ea4lsW7iTAVXD7OKswKPGqF5xgSyt77k,44 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/__pycache__/helpers.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/__pycache__/models.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/__pycache__/reader.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/__pycache__/reader_c.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/__pycache__/reader_py.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/__pycache__/writer.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/helpers.py,sha256=P-XLv8IUaihKzDenVUqfKU5DJbWE5HvG8uhvUZK8Ic4,5038 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/mask.cpython-312-x86_64-linux-gnu.so,sha256=EpRwPJm1K1yavMCd9llAWdT4AsqKx_QEN0rb0eJH_Kc,263512 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/mask.pxd,sha256=sBmZ1Amym9kW4Ge8lj1fLZ7mPPya4LzLdpkQExQXv5M,112 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/mask.pyx,sha256=BHjOtV0O0w7xp9p0LNADRJvGmgfPn9sGeJvSs0fL__4,1397 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/models.py,sha256=XAzjs_8JYszWXIgZ6R3ZRrF-tX9Q_6LiD49WRYojopM,2121 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/reader.py,sha256=eC4qS0c5sOeQ2ebAHLaBpIaTVFaSKX79pY2xvh3Pqyw,1030 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/reader_c.cpython-312-x86_64-linux-gnu.so,sha256=G-fK_d5U4UcA5kBo53v_5AYeOxksxvuy5b3y_XrZD04,1824528 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/reader_c.pxd,sha256=nl_njtDrzlQU0rjgGGjZDB-swguE0tX_bCPobkShVa4,2625 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/reader_c.py,sha256=gSsE_iSBr7-ORvOmgkCT7Jpj4_j3854i_Cp88Se1_6E,18791 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/reader_py.py,sha256=gSsE_iSBr7-ORvOmgkCT7Jpj4_j3854i_Cp88Se1_6E,18791 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/_websocket/writer.py,sha256=2OvSktPmNh_g20h1cXJt2Xu8u6IvswnPjdur7OwBbJk,11261 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/abc.py,sha256=M66F4S6m00bIEn7y4ha_XLTMDmVQ9dPihfOVB0pGfOo,7149 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/base_protocol.py,sha256=Tp8cxUPQvv9kUPk3w6lAzk6d2MAzV3scwI_3Go3C47c,3025 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/client.py,sha256=fOQfwcIUL1NGAVRV4DDj6-wipBzeD8KZpmzhO-LLKp4,58357 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/client_exceptions.py,sha256=uyKbxI2peZhKl7lELBMx3UeusNkfpemPWpGFq0r6JeM,11367 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/client_middleware_digest_auth.py,sha256=BIoQJ5eWL5NNkPOmezTGrceWIho8ETDvS8NKvX-3Xdw,17088 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/client_middlewares.py,sha256=kP5N9CMzQPMGPIEydeVUiLUTLsw8Vl8Gr4qAWYdu3vM,1918 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/client_proto.py,sha256=56_WtLStZGBFPYKzgEgY6v24JkhV1y6JEmmuxeJT2So,12110 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/client_reqrep.py,sha256=eEREDrZ0M8ZFTt1wjHduR-P8_sm40K65gNz-iMGYask,53391 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/client_ws.py,sha256=1CIjIXwyzOMIYw6AjUES4-qUwbyVHW1seJKQfg_Rta8,15109 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/compression_utils.py,sha256=Cmn4bim6iDYUST1Fp66EBRDzIz_3gUQBLg4HkbEljrc,10408 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/connector.py,sha256=WQetKoSW7XnHA9r4o9OWwO3-n7ymOwBd2Tg_xHNw0Bs,68456 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/cookiejar.py,sha256=e28ZMQwJ5P0vbPX1OX4Se7-k3zeGvocFEqzGhwpG53k,18922 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/formdata.py,sha256=xqYMbUo1qoLYPuzY92XeR4pyEe-w-DNcToARDF3GUhA,6384 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/hdrs.py,sha256=2rj5MyA-6yRdYPhW5UKkW4iNWhEAlGIOSBH5D4FmKNE,5111 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/helpers.py,sha256=Q1307PCEnWz4RP8crUw8dk58c0YF2Ei3JywkKfRxz5E,30629 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/http.py,sha256=8o8j8xH70OWjnfTWA9V44NR785QPxEPrUtzMXiAVpwc,1842 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/http_exceptions.py,sha256=AZafFHgtAkAgrKZf8zYPU8VX2dq32-VAoP-UZxBLU0c,2960 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/http_parser.py,sha256=fACBNI47n9hnVPWfm5AJufuezsoYOF_VLp4bptYjvQI,37377 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/http_websocket.py,sha256=8VXFKw6KQUEmPg48GtRMB37v0gTK7A0inoxXuDxMZEc,842 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/http_writer.py,sha256=fbRtKPYSqRbtAdr_gqpjF2-4sI1ESL8dPDF-xY_mAMY,12446 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/log.py,sha256=BbNKx9e3VMIm0xYjZI0IcBBoS7wjdeIeSaiJE7-qK2g,325 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/multipart.py,sha256=6q6QRjKFVqaWzTbc7bkuBtXsTaQq5b2BhHxLBvAElac,40040 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/payload.py,sha256=O6nsYNULL7AeM2cyJ6TYX73ncVnL5xJwt5AegxwMKqw,40874 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/payload_streamer.py,sha256=ZzEYyfzcjGWkVkK3XR2pBthSCSIykYvY3Wr5cGQ2eTc,2211 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/pytest_plugin.py,sha256=z4XwqmsKdyJCKxbGiA5kFf90zcedvomqk4RqjZbhKNk,12901 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/resolver.py,sha256=gsrfUpFf8iHlcHfJvY-1fiBHW3PRvRVNb5lNZBg3zlY,10031 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/streams.py,sha256=cQxo6Fyu_HDWDpbezGRVPIVYtVtTbSLRF7g511DNmSs,22601 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/tcp_helpers.py,sha256=BSadqVWaBpMFDRWnhaaR941N9MiDZ7bdTrxgCb0CW-M,961 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/test_utils.py,sha256=ZJSzZWjC76KSbtwddTKcP6vHpUl_ozfAf3F93ewmHRU,23016 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/tracing.py,sha256=-6aaW6l0J9uJD45LzR4cijYH0j62pt0U_nn_aVzFku4,14558 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/typedefs.py,sha256=wUlqwe9Mw9W8jT3HsYJcYk00qP3EMPz3nTkYXmeNN48,1657 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web.py,sha256=JzSNmejg5G6YeFAnkIgZfytqbU86sNu844yYKmoUpqs,17852 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_app.py,sha256=lGU_aAMN-h3wy-LTTHi6SeKH8ydt1G51BXcCspgD5ZA,19452 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_exceptions.py,sha256=7nIuiwhZ39vJJ9KrWqArA5QcWbUdqkz2CLwEpJapeN8,10360 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_fileresponse.py,sha256=Xzau8EMrWNrFg3u46h4UEteg93G4zYq94CU6vy0HiqE,16362 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_log.py,sha256=rX5D7xLOX2B6BMdiZ-chme_KfJfW5IXEoFwLfkfkajs,7865 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_middlewares.py,sha256=sFI0AgeNjdyAjuz92QtMIpngmJSOxrqe2Jfbs4BNUu0,4165 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_protocol.py,sha256=c8a0PKGqfhIAiq2RboMsy1NRza4dnj6gnXIWvJUeCF0,27015 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_request.py,sha256=zN96OlMRlrCFOMRpdh7y9rvHP0Hm8zavC0OFCj0wlSg,29833 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_response.py,sha256=PKcziNU4LmftXqKVvoRMrAbOeVClpSN-iznHsiWezmU,29341 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_routedef.py,sha256=VT1GAx6BrawoDh5RwBwBu5wSABSqgWwAe74AUCyZAEo,6110 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_runner.py,sha256=v1G1nKiOOQgFnTSR4IMc6I9ReEFDMaHtMLvO_roDM-A,11786 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_server.py,sha256=-9WDKUAiR9ll-rSdwXSqG6YjaoW79d1R4y0BGSqgUMA,2888 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_urldispatcher.py,sha256=3ryu1ZOpcq79IYNMd6EjYWmQ_i6JbsJzS_IaV0yoYBg,44203 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/web_ws.py,sha256=lItgmyatkXh0M6EY7JoZnSZkUl6R0wv8B88X4ILqQbU,22739 +ray/_private/runtime_env/agent/thirdparty_files/aiohttp/worker.py,sha256=zT0iWN5Xze194bO6_VjHou0x7lR_k0MviN6Kadnk22g,8152 +ray/_private/runtime_env/agent/thirdparty_files/aiosignal-1.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/_private/runtime_env/agent/thirdparty_files/aiosignal-1.4.0.dist-info/METADATA,sha256=CSR-8dqLxpZyjUcTDnAuQwf299EB1sSFv_nzpxznAI0,3662 +ray/_private/runtime_env/agent/thirdparty_files/aiosignal-1.4.0.dist-info/RECORD,sha256=D0fwXbaMUn7R_i8jx57x6EbhAJ7ATn70fzANuFXtnHQ,703 +ray/_private/runtime_env/agent/thirdparty_files/aiosignal-1.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +ray/_private/runtime_env/agent/thirdparty_files/aiosignal-1.4.0.dist-info/licenses/LICENSE,sha256=b9UkPpLdf5jsacesN3co50kFcJ_1J6W_mNbQJjwE9bY,11332 +ray/_private/runtime_env/agent/thirdparty_files/aiosignal-1.4.0.dist-info/top_level.txt,sha256=z45aNOKGDdrI1roqZY3BGXQ22kJFPHBmVdwtLYLtXC0,10 +ray/_private/runtime_env/agent/thirdparty_files/aiosignal/__init__.py,sha256=TIkmUG9HTBt4dfq2nISYBiZiRB2xwvFtEZydLP0HPL4,1537 +ray/_private/runtime_env/agent/thirdparty_files/aiosignal/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/aiosignal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/runtime_env/agent/thirdparty_files/attr/__init__.py,sha256=fOYIvt1eGSqQre4uCS3sJWKZ0mwAuC8UD6qba5OS9_U,2057 +ray/_private/runtime_env/agent/thirdparty_files/attr/__init__.pyi,sha256=IZkzIjvtbRqDWGkDBIF9dd12FgDa379JYq3GHnVOvFQ,11309 +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/_cmp.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/_compat.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/_config.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/_funcs.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/_make.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/_next_gen.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/_version_info.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/converters.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/exceptions.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/filters.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/setters.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/__pycache__/validators.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attr/_cmp.py,sha256=3Nn1TjxllUYiX_nJoVnEkXoDk0hM1DYKj5DE7GZe4i0,4117 +ray/_private/runtime_env/agent/thirdparty_files/attr/_cmp.pyi,sha256=U-_RU_UZOyPUEQzXE6RMYQQcjkZRY25wTH99sN0s7MM,368 +ray/_private/runtime_env/agent/thirdparty_files/attr/_compat.py,sha256=x0g7iEUOnBVJC72zyFCgb1eKqyxS-7f2LGnNyZ_r95s,2829 +ray/_private/runtime_env/agent/thirdparty_files/attr/_config.py,sha256=dGq3xR6fgZEF6UBt_L0T-eUHIB4i43kRmH0P28sJVw8,843 +ray/_private/runtime_env/agent/thirdparty_files/attr/_funcs.py,sha256=Ix5IETTfz5F01F-12MF_CSFomIn2h8b67EVVz2gCtBE,16479 +ray/_private/runtime_env/agent/thirdparty_files/attr/_make.py,sha256=NRJDGS8syg2h3YNflVNoK2FwR3CpdSZxx8M6lacwljA,104141 +ray/_private/runtime_env/agent/thirdparty_files/attr/_next_gen.py,sha256=BQtCUlzwg2gWHTYXBQvrEYBnzBUrDvO57u0Py6UCPhc,26274 +ray/_private/runtime_env/agent/thirdparty_files/attr/_typing_compat.pyi,sha256=XDP54TUn-ZKhD62TOQebmzrwFyomhUCoGRpclb6alRA,469 +ray/_private/runtime_env/agent/thirdparty_files/attr/_version_info.py,sha256=w4R-FYC3NK_kMkGUWJlYP4cVAlH9HRaC-um3fcjYkHM,2222 +ray/_private/runtime_env/agent/thirdparty_files/attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209 +ray/_private/runtime_env/agent/thirdparty_files/attr/converters.py,sha256=GlDeOzPeTFgeBBLbj9G57Ez5lAk68uhSALRYJ_exe84,3861 +ray/_private/runtime_env/agent/thirdparty_files/attr/converters.pyi,sha256=orU2bff-VjQa2kMDyvnMQV73oJT2WRyQuw4ZR1ym1bE,643 +ray/_private/runtime_env/agent/thirdparty_files/attr/exceptions.py,sha256=HRFq4iybmv7-DcZwyjl6M1euM2YeJVK_hFxuaBGAngI,1977 +ray/_private/runtime_env/agent/thirdparty_files/attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539 +ray/_private/runtime_env/agent/thirdparty_files/attr/filters.py,sha256=ZBiKWLp3R0LfCZsq7X11pn9WX8NslS2wXM4jsnLOGc8,1795 +ray/_private/runtime_env/agent/thirdparty_files/attr/filters.pyi,sha256=3J5BG-dTxltBk1_-RuNRUHrv2qu1v8v4aDNAQ7_mifA,208 +ray/_private/runtime_env/agent/thirdparty_files/attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/runtime_env/agent/thirdparty_files/attr/setters.py,sha256=5-dcT63GQK35ONEzSgfXCkbB7pPkaR-qv15mm4PVSzQ,1617 +ray/_private/runtime_env/agent/thirdparty_files/attr/setters.pyi,sha256=NnVkaFU1BB4JB8E4JuXyrzTUgvtMpj8p3wBdJY7uix4,584 +ray/_private/runtime_env/agent/thirdparty_files/attr/validators.py,sha256=1BnYGTuYvSucGEI4ju-RPNJteVzG0ZlfWpJiWoSFHQ8,21458 +ray/_private/runtime_env/agent/thirdparty_files/attr/validators.pyi,sha256=ftmW3m4KJ3pQcIXAj-BejT7BY4ZfqrC1G-5W7XvoPds,4082 +ray/_private/runtime_env/agent/thirdparty_files/attrs-25.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/_private/runtime_env/agent/thirdparty_files/attrs-25.4.0.dist-info/METADATA,sha256=2Rerxj7agcMRxiwdkt6lC2guqHAmkGKCH13nWWK7ZoQ,10473 +ray/_private/runtime_env/agent/thirdparty_files/attrs-25.4.0.dist-info/RECORD,sha256=Ae0xsWI-FFmmKsFFg8E7NMfWEpT_gWxiINEPqFpY47M,3557 +ray/_private/runtime_env/agent/thirdparty_files/attrs-25.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +ray/_private/runtime_env/agent/thirdparty_files/attrs-25.4.0.dist-info/licenses/LICENSE,sha256=iCEVyV38KvHutnFPjsbVy8q_Znyv-HKfQkINpj9xTp8,1109 +ray/_private/runtime_env/agent/thirdparty_files/attrs/__init__.py,sha256=RxaAZNwYiEh-fcvHLZNpQ_DWKni73M_jxEPEftiq1Zc,1183 +ray/_private/runtime_env/agent/thirdparty_files/attrs/__init__.pyi,sha256=2gV79g9UxJppGSM48hAZJ6h_MHb70dZoJL31ZNJeZYI,9416 +ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/converters.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/exceptions.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/filters.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/setters.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attrs/__pycache__/validators.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/attrs/converters.py,sha256=8kQljrVwfSTRu8INwEk8SI0eGrzmWftsT7rM0EqyohM,76 +ray/_private/runtime_env/agent/thirdparty_files/attrs/exceptions.py,sha256=ACCCmg19-vDFaDPY9vFl199SPXCQMN_bENs4DALjzms,76 +ray/_private/runtime_env/agent/thirdparty_files/attrs/filters.py,sha256=VOUMZug9uEU6dUuA0dF1jInUK0PL3fLgP0VBS5d-CDE,73 +ray/_private/runtime_env/agent/thirdparty_files/attrs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/runtime_env/agent/thirdparty_files/attrs/setters.py,sha256=eL1YidYQV3T2h9_SYIZSZR1FAcHGb1TuCTy0E0Lv2SU,73 +ray/_private/runtime_env/agent/thirdparty_files/attrs/validators.py,sha256=xcy6wD5TtTkdCG1f4XWbocPSO0faBjk5IfVJfP6SUj0,76 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.8.0.dist-info/METADATA,sha256=lGwi3J9-LHby1pjwCkMG2-Sagg4rPxoOIv5ShsvsNfE,20333 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.8.0.dist-info/RECORD,sha256=t68Ws4EK0Qk6aVRSpu4-C7VizJMWQaiZFhIHsAOAA6A,993 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.8.0.dist-info/WHEEL,sha256=mX4U4odf6w47aVjwZUmTYd1MF9BbrhVLKlaWSvZwHEk,186 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.8.0.dist-info/licenses/LICENSE,sha256=b9UkPpLdf5jsacesN3co50kFcJ_1J6W_mNbQJjwE9bY,11332 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist-1.8.0.dist-info/top_level.txt,sha256=jivtxsPXA3nK3WBWW2LW5Mtu_GHt8UZA13NeCs2cKuA,11 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist/__init__.py,sha256=xAIE2u9ncAbjATGIPfno_OJfe8AQ-1h7z_uc73dYsEA,2108 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist/__init__.pyi,sha256=vMEoES1xGegPtVXoCi9XydEeHsyuIq-KdeXwP5PdsaA,1470 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/frozenlist/_frozenlist.cpython-312-x86_64-linux-gnu.so,sha256=UzFjN2uN24Tz86SdErp07aJh4LOp8A0w9EhxkDP4i3E,786872 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist/_frozenlist.pyx,sha256=t-aGjuEiVt_MZPBJ0RnraavVmPBK6arz3i48ZvXuYsU,3708 +ray/_private/runtime_env/agent/thirdparty_files/frozenlist/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 +ray/_private/runtime_env/agent/thirdparty_files/idna-3.11.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/_private/runtime_env/agent/thirdparty_files/idna-3.11.dist-info/METADATA,sha256=fCwSww9SuiN8TIHllFSASUQCW55hAs8dzKnr9RaEEbA,8378 +ray/_private/runtime_env/agent/thirdparty_files/idna-3.11.dist-info/RECORD,sha256=i0Q6QatkmOqpa0ER_YTQU-D9aci8kxM9AUlJ_KywCmI,1392 +ray/_private/runtime_env/agent/thirdparty_files/idna-3.11.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +ray/_private/runtime_env/agent/thirdparty_files/idna-3.11.dist-info/licenses/LICENSE.md,sha256=t6M2q_OwThgOwGXN0W5wXQeeHMehT5EKpukYfza5zYc,1541 +ray/_private/runtime_env/agent/thirdparty_files/idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868 +ray/_private/runtime_env/agent/thirdparty_files/idna/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/idna/__pycache__/codec.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/idna/__pycache__/compat.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/idna/__pycache__/core.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/idna/__pycache__/idnadata.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/idna/__pycache__/intranges.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/idna/__pycache__/package_data.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/idna/__pycache__/uts46data.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/idna/codec.py,sha256=M2SGWN7cs_6B32QmKTyTN6xQGZeYQgQ2wiX3_DR6loE,3438 +ray/_private/runtime_env/agent/thirdparty_files/idna/compat.py,sha256=RzLy6QQCdl9784aFhb2EX9EKGCJjg0P3PilGdeXXcx8,316 +ray/_private/runtime_env/agent/thirdparty_files/idna/core.py,sha256=P26_XVycuMTZ1R2mNK1ZREVzM5mvTzdabBXfyZVU1Lc,13246 +ray/_private/runtime_env/agent/thirdparty_files/idna/idnadata.py,sha256=SG8jhaGE53iiD6B49pt2pwTv_UvClciWE-N54oR2p4U,79623 +ray/_private/runtime_env/agent/thirdparty_files/idna/intranges.py,sha256=amUtkdhYcQG8Zr-CoMM_kVRacxkivC1WgxN1b63KKdU,1898 +ray/_private/runtime_env/agent/thirdparty_files/idna/package_data.py,sha256=_CUavOxobnbyNG2FLyHoN8QHP3QM9W1tKuw7eq9QwBk,21 +ray/_private/runtime_env/agent/thirdparty_files/idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/runtime_env/agent/thirdparty_files/idna/uts46data.py,sha256=H9J35VkD0F9L9mKOqjeNGd2A-Va6FlPoz6Jz4K7h-ps,243725 +ray/_private/runtime_env/agent/thirdparty_files/multidict-6.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/_private/runtime_env/agent/thirdparty_files/multidict-6.7.0.dist-info/METADATA,sha256=q_zO5zLDpLVGLHpjw8BHBRhNX7yuB6_RlehDOZPYrZQ,5321 +ray/_private/runtime_env/agent/thirdparty_files/multidict-6.7.0.dist-info/RECORD,sha256=c8so-Jdt7UWEBgpsLCypYArFEAUDgRj6MRNzVJ4lRSs,1202 +ray/_private/runtime_env/agent/thirdparty_files/multidict-6.7.0.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190 +ray/_private/runtime_env/agent/thirdparty_files/multidict-6.7.0.dist-info/licenses/LICENSE,sha256=k9Ealo4vDzY3PECBH_bSDhc_WMPKtYhM1mF7v9eVSSo,611 +ray/_private/runtime_env/agent/thirdparty_files/multidict-6.7.0.dist-info/top_level.txt,sha256=-euDElkk5_qkmfIJ7WiqCab02ZlSFZWynejKg59qZQQ,10 +ray/_private/runtime_env/agent/thirdparty_files/multidict/__init__.py,sha256=vrqM7ruZH18zqUQumAaWtGekJFYb_oWvThnAdNuAxg4,1228 +ray/_private/runtime_env/agent/thirdparty_files/multidict/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/multidict/__pycache__/_abc.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/multidict/__pycache__/_compat.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/multidict/__pycache__/_multidict_py.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/multidict/_abc.py,sha256=e_0JDJi7E6LWS0A3gUJ17SkgDLlmg8ffjfylTu_vboc,2402 +ray/_private/runtime_env/agent/thirdparty_files/multidict/_compat.py,sha256=TcRjCStk2iIY1_DwDNj8kNpJRQ9rtLj92Xvk1z2G_ak,422 +ray/_private/runtime_env/agent/thirdparty_files/multidict/_multidict.cpython-312-x86_64-linux-gnu.so,sha256=xnz-7eI31DiuDjqpB2WfX4zbC-ht1BwpL7gTvIUrrXo,848984 +ray/_private/runtime_env/agent/thirdparty_files/multidict/_multidict_py.py,sha256=VGQ58P7VOd6lRf3WVAinb62aD16DPdAWRt68qmiJMXE,39955 +ray/_private/runtime_env/agent/thirdparty_files/multidict/py.typed,sha256=e9bmbH3UFxsabQrnNFPG9qxIXztwbcM6IKDYnvZwprY,15 +ray/_private/runtime_env/agent/thirdparty_files/propcache-0.4.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/_private/runtime_env/agent/thirdparty_files/propcache-0.4.1.dist-info/METADATA,sha256=_CPzxSszbVM1zQU_R9kf9jFpSeZa0fHj38zVKE9Nbs4,13745 +ray/_private/runtime_env/agent/thirdparty_files/propcache-0.4.1.dist-info/RECORD,sha256=3VFC39giLpRE7nkKLKDFVYHlm3r1CjeechjUUawjJ28,1379 +ray/_private/runtime_env/agent/thirdparty_files/propcache-0.4.1.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190 +ray/_private/runtime_env/agent/thirdparty_files/propcache-0.4.1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358 +ray/_private/runtime_env/agent/thirdparty_files/propcache-0.4.1.dist-info/licenses/NOTICE,sha256=VtasbIEFwKUTBMIdsGDjYa-ajqCvmnXCOcKLXRNpODg,609 +ray/_private/runtime_env/agent/thirdparty_files/propcache-0.4.1.dist-info/top_level.txt,sha256=pVF_GbqSAITPMiX27kfU3QP9-ufhRvkADmudDxWdF3w,10 +ray/_private/runtime_env/agent/thirdparty_files/propcache/__init__.py,sha256=8kebeGvYn7s-ow1AFmK0A4EvonZMpyM7Lkzs2Ktia3Y,965 +ray/_private/runtime_env/agent/thirdparty_files/propcache/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/propcache/__pycache__/_helpers.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/propcache/__pycache__/_helpers_py.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/propcache/__pycache__/api.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/propcache/_helpers.py,sha256=68SQm6kETN8Mnt9Ol26LJYgHgmB0mKy1tp92888zN4k,1553 +ray/_private/runtime_env/agent/thirdparty_files/propcache/_helpers_c.cpython-312-x86_64-linux-gnu.so,sha256=tGelusHoANUmokeGexAZA3jwXq-nYzlSbGJv8DEy6ug,745424 +ray/_private/runtime_env/agent/thirdparty_files/propcache/_helpers_c.pyx,sha256=kcJa1U5lh54TPCqAeZ0cVB7URcb3I8ZbJieOrkNhLQE,3265 +ray/_private/runtime_env/agent/thirdparty_files/propcache/_helpers_py.py,sha256=Wixs2zWA-FBU-j4zLPyBUU24FEfPhKk-UunFSp9q95U,1909 +ray/_private/runtime_env/agent/thirdparty_files/propcache/api.py,sha256=wvgB-ypkkI5uf72VVYl2NFGc_TnzUQA2CxC7dTlL5ak,179 +ray/_private/runtime_env/agent/thirdparty_files/propcache/py.typed,sha256=ay5OMO475PlcZ_Fbun9maHW7Y6MBTk0UXL4ztHx3Iug,14 +ray/_private/runtime_env/agent/thirdparty_files/typing_extensions-4.15.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/_private/runtime_env/agent/thirdparty_files/typing_extensions-4.15.0.dist-info/METADATA,sha256=wTg3j-jxiTSsmd4GBTXFPsbBOu7WXpTDJkHafuMZKnI,3259 +ray/_private/runtime_env/agent/thirdparty_files/typing_extensions-4.15.0.dist-info/RECORD,sha256=AvcKTtb4HDKYoAJMqdzGgHNgk404g2DOO3aCQ_cZzc4,580 +ray/_private/runtime_env/agent/thirdparty_files/typing_extensions-4.15.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +ray/_private/runtime_env/agent/thirdparty_files/typing_extensions-4.15.0.dist-info/licenses/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936 +ray/_private/runtime_env/agent/thirdparty_files/typing_extensions.py,sha256=Qz0R0XDTok0usGXrwb_oSM6n49fOaFZ6tSvqLUwvftg,160429 +ray/_private/runtime_env/agent/thirdparty_files/yarl-1.22.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/_private/runtime_env/agent/thirdparty_files/yarl-1.22.0.dist-info/METADATA,sha256=gATDmMXVC53tnEFDwFcOd1skK5FM8kJNbY8GFkCRDLg,75118 +ray/_private/runtime_env/agent/thirdparty_files/yarl-1.22.0.dist-info/RECORD,sha256=LT0xkYCXoNZVyWjojWZQyJgMyfa-xSYHc_lNBL9TGqw,1762 +ray/_private/runtime_env/agent/thirdparty_files/yarl-1.22.0.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190 +ray/_private/runtime_env/agent/thirdparty_files/yarl-1.22.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358 +ray/_private/runtime_env/agent/thirdparty_files/yarl-1.22.0.dist-info/licenses/NOTICE,sha256=VtasbIEFwKUTBMIdsGDjYa-ajqCvmnXCOcKLXRNpODg,609 +ray/_private/runtime_env/agent/thirdparty_files/yarl-1.22.0.dist-info/top_level.txt,sha256=vf3SJuQh-k7YtvsUrV_OPOrT9Kqn0COlk7IPYyhtGkQ,5 +ray/_private/runtime_env/agent/thirdparty_files/yarl/__init__.py,sha256=woYZp7KGli7_1P_hR7ZU9ckEj6ho41smyP-PLfEL-lk,281 +ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/__init__.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_parse.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_path.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_query.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoters.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoting.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_quoting_py.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/yarl/__pycache__/_url.cpython-312.pyc,, +ray/_private/runtime_env/agent/thirdparty_files/yarl/_parse.py,sha256=gNt8zxVFGr95ufUQpSMiiZ9vDrvg4zq6MEtT3f6_8J0,7185 +ray/_private/runtime_env/agent/thirdparty_files/yarl/_path.py,sha256=A0FJUylZyzmlT0a3UDOBbK-EzZXCAYuQQBvG9eAC9hs,1291 +ray/_private/runtime_env/agent/thirdparty_files/yarl/_query.py,sha256=nwGAYewdOU8nt5YZNZxqQ4BGES82Y3Y6LanxqTjnZxw,4068 +ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoters.py,sha256=z-BzsXfLnJK-bd-HrGaoKGri9L3GpDv6vxFEtmu-uCM,1154 +ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting.py,sha256=yKIqFTzFzWLVb08xy1DSxKNjFwo4f-oLlzxTuKwC57M,506 +ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_c.cpython-312-x86_64-linux-gnu.so,sha256=TapJ2HY3skYb6kJkICWISGliO6JvMgDiQtRQcD8JMxA,1170216 +ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_c.pyx,sha256=X40gvQSUB4l7nPKGeiS6pq2JreM36avLhVeBMxd5zmo,14297 +ray/_private/runtime_env/agent/thirdparty_files/yarl/_quoting_py.py,sha256=7WD7IHhgaJiLZWoIewvB0JRUsbz9McmfZw5TnjlVs9o,6783 +ray/_private/runtime_env/agent/thirdparty_files/yarl/_url.py,sha256=4K5gCdoQtVi9FmnQdssEqafdlJILKxSap8RNCBC4IGE,55608 +ray/_private/runtime_env/agent/thirdparty_files/yarl/py.typed,sha256=ay5OMO475PlcZ_Fbun9maHW7Y6MBTk0UXL4ztHx3Iug,14 +ray/_private/runtime_env/conda.py,sha256=2q6U8FaIEpw_t28fIuRfoNysi1lH7OcISH4plPMxPes,14648 +ray/_private/runtime_env/conda_utils.py,sha256=h9xsriQI1pCAIQsCTf93li12Ug5UXO3nl_MfiP1c-qA,9526 +ray/_private/runtime_env/constants.py,sha256=n59A0vJ6oaWT5zrEXujILathVNaFbW4wX7zEHDK2aKQ,1076 +ray/_private/runtime_env/context.py,sha256=TGk0YXgZ4iRbe_Fqc0lhOZVVQlc81N-vBNj8aDH1dgc,4264 +ray/_private/runtime_env/default_impl.py,sha256=MKaQpvf0FvCu3s8jvZ-s59CWNkKAB9qwtaOd8fl3lQI,122 +ray/_private/runtime_env/dependency_utils.py,sha256=tiAyF-29nsfmqXiHDmKcfsv29rsXHB6F_ecJjUK4i1Q,4462 +ray/_private/runtime_env/image_uri.py,sha256=DlmHNiy0NLOkSnralvaSO-YA-D3qB077687z8H6jAW4,7474 +ray/_private/runtime_env/java_jars.py,sha256=csZ8lqqvA6hYbA69vzYX3sb7X1XzEfXsYf6Cd026y1E,3639 +ray/_private/runtime_env/nsight.py,sha256=VvCWa9-xS4mtBglxXCoBbMMpVnfNb0NMCHN0arT_Mg0,5263 +ray/_private/runtime_env/packaging.py,sha256=DsJsU3Yws4S7Pag-N6sKE9Oab4XrxlXInUvfbibjDno,32846 +ray/_private/runtime_env/pip.py,sha256=GDqdAKApocF7f10_4GUeq6sZGiN3IXL9u8iWZGSK1rU,11998 +ray/_private/runtime_env/plugin.py,sha256=jf-nf38Qv3AUT513LW-XTtOPdM2Bh1nsKqjdMsutvgI,9423 +ray/_private/runtime_env/plugin_schema_manager.py,sha256=R_ZJv7EGMPbLAX7LIBfzsA9lNq5qz7oEHhuZhPop0Jk,3504 +ray/_private/runtime_env/protocol.py,sha256=wXHZn-2DH_voWFhoREJMq1NOJh_3UzCM9ODMBoiUSWY,9516 +ray/_private/runtime_env/py_executable.py,sha256=y2d_T0z7BM2qlA6_UsCkO4dYMjw7cOl1WTp5ZUBDN0w,1504 +ray/_private/runtime_env/py_modules.py,sha256=s7RUZL0wA7Ed89qv17smbNeYgIZjr1rSRKMDom0csT4,8781 +ray/_private/runtime_env/rocprof_sys.py,sha256=K5jggGkELQW8a_A3YMYVEvu0O-GonyJwy9-907ekbpI,6485 +ray/_private/runtime_env/setup_hook.py,sha256=WtdtpnRk3Py_VPo7jvSN-3intL6xaINpPBqYWpy5TlA,6772 +ray/_private/runtime_env/uri_cache.py,sha256=k_JI_nV4NDurIi-Vzlv1pUYc-y0pxlYg-ThWyc_rbJI,4332 +ray/_private/runtime_env/utils.py,sha256=CnZXCEXF_2_AnGSuZ5Lu2gZYMCOsJ3kfeb9uz0HTq-w,3989 +ray/_private/runtime_env/uv.py,sha256=4KX0g8JVw0ehxhHMh_U9D6E8o3x7qfvqE4kIxjiavBA,12336 +ray/_private/runtime_env/uv_runtime_env_hook.py,sha256=KN0ItbydFhNcPBWmX2e0r9vODJZXaiQq-vBWJcvfBpc,15839 +ray/_private/runtime_env/validation.py,sha256=1zNxAV2rvoPTcIgpX59UxgH5radPjZlMNt8QiOefVJ8,17608 +ray/_private/runtime_env/virtualenv_utils.py,sha256=xuNi1ImTUp-TiBX4RKyjq-AT_mt5vkIyV6PDmAFrLVs,3629 +ray/_private/runtime_env/working_dir.py,sha256=NXpqDHUlrxBOWhzhFY6QL2h3QTYw-2H0gJ-1VGdb3rY,8553 +ray/_private/serialization.py,sha256=5UGz_oyMUbqrOVC0WhEsBsfLqp8GVoUHgZqJxxXL7OI,31006 +ray/_private/services.py,sha256=pdIr-LuWyJCU-iSeALsEBIpJW7jqk7dWtUh7MvAC-CA,93871 +ray/_private/state.py,sha256=glj1gDqyh8QNI-lvb50HSomSGGAXDsERXcErR9a15yM,43929 +ray/_private/state_api_test_utils.py,sha256=e16wSdClxR5Xi_OOQt15R0FLPyP5UHoTZeCPhufa0Oc,19557 +ray/_private/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/telemetry/__pycache__/__init__.cpython-312.pyc,, +ray/_private/telemetry/__pycache__/metric_cardinality.cpython-312.pyc,, +ray/_private/telemetry/__pycache__/open_telemetry_metric_recorder.cpython-312.pyc,, +ray/_private/telemetry/metric_cardinality.py,sha256=hjNe-lTWXdmokEoZbpdwAbnCVLOVvQVUWN7RbaYHP6M,2395 +ray/_private/telemetry/open_telemetry_metric_recorder.py,sha256=woFNFtlT7czaI3kmsmf3UDJPpgDYRkpzC5kOfcJXShM,10305 +ray/_private/test_utils.py,sha256=3ve-6EfYrlZIJFKAgSXdhqrXX1uL2uh8tJoG_cjiF8A,70127 +ray/_private/thirdparty/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/thirdparty/__pycache__/__init__.cpython-312.pyc,, +ray/_private/thirdparty/dacite/__init__.py,sha256=OZNF6CStZkbIfJnwudZ8uDHRZCzO8yAHb4vLtusuLy8,81 +ray/_private/thirdparty/dacite/__pycache__/__init__.cpython-312.pyc,, +ray/_private/thirdparty/dacite/__pycache__/config.cpython-312.pyc,, +ray/_private/thirdparty/dacite/__pycache__/core.cpython-312.pyc,, +ray/_private/thirdparty/dacite/__pycache__/data.cpython-312.pyc,, +ray/_private/thirdparty/dacite/__pycache__/dataclasses.cpython-312.pyc,, +ray/_private/thirdparty/dacite/__pycache__/exceptions.cpython-312.pyc,, +ray/_private/thirdparty/dacite/__pycache__/types.cpython-312.pyc,, +ray/_private/thirdparty/dacite/config.py,sha256=d0zlO-jqhSvpXW53Gie97sJkPMzbuvHbesIBwU1f1MU,407 +ray/_private/thirdparty/dacite/core.py,sha256=lX2HzgT4lSsbJDIhEacuorX6_JIuExeWjcYEZ73fdaY,5493 +ray/_private/thirdparty/dacite/data.py,sha256=YSkQNaokRQcsBU5scc-g9Uw3EGAm-xabGOEwTgmYUe4,52 +ray/_private/thirdparty/dacite/dataclasses.py,sha256=HYg-Bi-eL8H3QY3ll91QW0LLRjscGMs_JLQ5ITzouUA,1032 +ray/_private/thirdparty/dacite/exceptions.py,sha256=p87iJx6OH_siwS-bULadqWeb1W7VgLqCtpDavV4Sdl0,2587 +ray/_private/thirdparty/dacite/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/thirdparty/dacite/types.py,sha256=sRPDh8FF-rWy_cPtuBWCavqtNJeofh9AQynYEmZ_Sk4,5823 +ray/_private/thirdparty/pathspec/__init__.py,sha256=Fx3qyGKKhKWkAVsb5x1zoMVzCr-4wIw4H9pyokvu7sw,2213 +ray/_private/thirdparty/pathspec/__pycache__/__init__.cpython-312.pyc,, +ray/_private/thirdparty/pathspec/__pycache__/compat.cpython-312.pyc,, +ray/_private/thirdparty/pathspec/__pycache__/pathspec.cpython-312.pyc,, +ray/_private/thirdparty/pathspec/__pycache__/pattern.cpython-312.pyc,, +ray/_private/thirdparty/pathspec/__pycache__/util.cpython-312.pyc,, +ray/_private/thirdparty/pathspec/compat.py,sha256=OZJedLvgxHbuKKn5Rnia_UDcyDZfnqCQerLoIvSR_Qc,777 +ray/_private/thirdparty/pathspec/pathspec.py,sha256=6DzqgsaxHFdSa1VChuk0yIhfL-hMekkdvhT6roBU8c0,7027 +ray/_private/thirdparty/pathspec/pattern.py,sha256=p-ibgRLK9kIkiXWoCtq4SangTZEBUaNIO0hgKQY4KEI,4405 +ray/_private/thirdparty/pathspec/patterns/__init__.py,sha256=Falv9rzI0S-Sjc-t-vCS9nUPcKwBptmdNderY9Kok50,184 +ray/_private/thirdparty/pathspec/patterns/__pycache__/__init__.cpython-312.pyc,, +ray/_private/thirdparty/pathspec/patterns/__pycache__/gitwildmatch.cpython-312.pyc,, +ray/_private/thirdparty/pathspec/patterns/gitwildmatch.py,sha256=cJfGcaj9rL9YhVhItJv-xuwYsveraCXFVQm_Xg_iMTE,9947 +ray/_private/thirdparty/pathspec/util.py,sha256=smB9T1UuQ7ZBvZZvdtRBr1C0W6BssfeFkNA-4c-cXRY,17929 +ray/_private/thirdparty/pyamdsmi/__init__.py,sha256=nM1QPuKAKqH1aMeiGk4HAd-6axHTpgQp-lpfcnKxQjk,56 +ray/_private/thirdparty/pyamdsmi/__pycache__/__init__.cpython-312.pyc,, +ray/_private/thirdparty/pyamdsmi/__pycache__/pyamdsmi.cpython-312.pyc,, +ray/_private/thirdparty/pyamdsmi/pyamdsmi.py,sha256=a8wRkHtpPK5oL39BRgSmBLsyeMpZsgFmbtFi-ZK0MrA,19979 +ray/_private/thirdparty/pynvml/__init__.py,sha256=TgNfbf6fM6IjY6jdUj-BdUUZzo020PKd5c6JqRndZWI,100 +ray/_private/thirdparty/pynvml/__pycache__/__init__.cpython-312.pyc,, +ray/_private/thirdparty/pynvml/__pycache__/pynvml.cpython-312.pyc,, +ray/_private/thirdparty/pynvml/pynvml.py,sha256=HA4fUHckOXZITXcfxi3lA5Koxo86qG7afGgazaUvsX8,264045 +ray/_private/thirdparty/tabulate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/thirdparty/tabulate/__pycache__/__init__.cpython-312.pyc,, +ray/_private/thirdparty/tabulate/__pycache__/tabulate.cpython-312.pyc,, +ray/_private/thirdparty/tabulate/tabulate.py,sha256=9ljbv_oiLnG5MFG_70ItjhROStVSok-8Ti1F8ImD1-0,95389 +ray/_private/tls_utils.py,sha256=dzo8OK6E6KPWZej5WdHGtf8eRAvOL5cftiTBDYDmR_M,3355 +ray/_private/utils.py,sha256=n6I1YwOFjONNoBU6tfHUNLn48epcfLB9n7CiXLN8o1U,58445 +ray/_private/worker.py,sha256=XdoOrnxXz6L1KOgWrQn4cVKXnSKY9tCKdCP44640PTI,145052 +ray/_private/workers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/_private/workers/__pycache__/__init__.cpython-312.pyc,, +ray/_private/workers/__pycache__/default_worker.cpython-312.pyc,, +ray/_private/workers/__pycache__/setup_worker.cpython-312.pyc,, +ray/_private/workers/default_worker.py,sha256=RDZ5q4MdAFhKQ14yTvBnSvhTyA9QdXbaID8qh16rMMk,10238 +ray/_private/workers/setup_worker.py,sha256=m5ncsOdAXj6IjshOZt0nhfiFghiLUQ_VOtX4HQI6CHQ,1154 +ray/_raylet.pxd,sha256=51swNxnOc_cyl6qVn0GojIYrlgLzYnX_dwp8hRFHV4Y,5585 +ray/_raylet.pyi,sha256=8VK-onooHO3rsl2ROINfFQ4aX7BAMo5EewoOaFUFd4M,625 +ray/_raylet.so,sha256=aeycxtCb6vaVLbX41ce4mgJGF2DXaiFLN-rNSVK6udE,41779624 +ray/_version.py,sha256=JQDWL3DMYSCa3tW2yY_mCotcvCqXAu1lqKNPr5k8O8c,199 +ray/actor.py,sha256=uh2SIo8Rfp0adDaCdyLFnZr2JqTWP8WY-fVwvqlUdWg,102296 +ray/air/__init__.py,sha256=Csh1wBNLALWtq5Idvw_7WsN4G223U91Vi-h847bYPyg,533 +ray/air/__pycache__/__init__.cpython-312.pyc,, +ray/air/__pycache__/config.cpython-312.pyc,, +ray/air/__pycache__/constants.cpython-312.pyc,, +ray/air/__pycache__/data_batch_type.cpython-312.pyc,, +ray/air/__pycache__/result.cpython-312.pyc,, +ray/air/__pycache__/session.cpython-312.pyc,, +ray/air/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/air/_internal/__pycache__/__init__.cpython-312.pyc,, +ray/air/_internal/__pycache__/config.cpython-312.pyc,, +ray/air/_internal/__pycache__/filelock.cpython-312.pyc,, +ray/air/_internal/__pycache__/json.cpython-312.pyc,, +ray/air/_internal/__pycache__/mlflow.cpython-312.pyc,, +ray/air/_internal/__pycache__/tensorflow_utils.cpython-312.pyc,, +ray/air/_internal/__pycache__/torch_utils.cpython-312.pyc,, +ray/air/_internal/__pycache__/uri_utils.cpython-312.pyc,, +ray/air/_internal/__pycache__/usage.cpython-312.pyc,, +ray/air/_internal/__pycache__/util.cpython-312.pyc,, +ray/air/_internal/config.py,sha256=T75Sv59GqB1uo-q5ylMAexoI3U5SvJzzAdWZ92EQAr4,1585 +ray/air/_internal/device_manager/__init__.py,sha256=db6XDVjQu9ZY4Qx-kyhAUj3YiLxl0vuAbEgJwo3w3J8,3310 +ray/air/_internal/device_manager/__pycache__/__init__.cpython-312.pyc,, +ray/air/_internal/device_manager/__pycache__/cpu.cpython-312.pyc,, +ray/air/_internal/device_manager/__pycache__/hpu.cpython-312.pyc,, +ray/air/_internal/device_manager/__pycache__/npu.cpython-312.pyc,, +ray/air/_internal/device_manager/__pycache__/nvidia_gpu.cpython-312.pyc,, +ray/air/_internal/device_manager/__pycache__/torch_device_manager.cpython-312.pyc,, +ray/air/_internal/device_manager/cpu.py,sha256=1Pumfvjt3aCOpRysunQDUhUHPS80aWCfPJdhQ_NUBus,813 +ray/air/_internal/device_manager/hpu.py,sha256=XmCa0NtfWm9D8YvYffECxTY3rsxcQYQPsusSBSxNY80,1481 +ray/air/_internal/device_manager/npu.py,sha256=SgpCsXteL7c0JdvDIiaOORtzkvPoaxuC1MFc9EVBXgA,3478 +ray/air/_internal/device_manager/nvidia_gpu.py,sha256=llLYHvGPq0j1Fes8lDKHLn0v4JMAWDZjddCp06dWr-4,2993 +ray/air/_internal/device_manager/torch_device_manager.py,sha256=dNG8HQ74BQI_CVFGw2ssnSQFunfb6Mn5NbJT7Irvtng,1138 +ray/air/_internal/filelock.py,sha256=UoSfpJdWGZJ12pOVB6BCUn2KOd3wlaEt8DIuFyjV7fc,1430 +ray/air/_internal/json.py,sha256=vLHL8K4BMeogr_LTcw_q8ni82F-L_89RvrbED_mYYTU,908 +ray/air/_internal/mlflow.py,sha256=aue99PbzpfCqLemZPuCwnsegC2xIw_XVg47RQgWYNIA,12627 +ray/air/_internal/tensorflow_utils.py,sha256=_MwYRouv0v9o3thwNMcEPfB2gKobRBGN12wXOqskevg,4825 +ray/air/_internal/torch_utils.py,sha256=7qeetsNwGGehQRDLJWLxRtNwNZciHGe410gJdi-MLgc,20677 +ray/air/_internal/uri_utils.py,sha256=S5DBhJ7UGebNsVI3UGGy58kMwwhWuvU7KFIzEKq9AjM,3096 +ray/air/_internal/usage.py,sha256=pMESth9loKbjqEGrf8mqJX7NGVr-IuF6tSAYTFJCRaw,9200 +ray/air/_internal/util.py,sha256=YDILk5X8oDqBFp5kV0F4pYM3OTrZsfbflEPLfcoykno,3876 +ray/air/config.py,sha256=4hgqMdt37F3BFelRzk-0K9tWqKMhtPsi3KaaDJxjWXQ,28893 +ray/air/constants.py,sha256=UWSXvYmwVVzy_JW1RHvYNWjWJYHjV8C8VtZoVlmCfdk,3211 +ray/air/data_batch_type.py,sha256=vD4v2oi-T1xXdbmd5lG2U6NMkuKGpzYH8GrU4j8EM3s,287 +ray/air/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/air/examples/__pycache__/__init__.cpython-312.pyc,, +ray/air/execution/__init__.py,sha256=cixguYR5h_W2aIooIQfmpeQB3rstw5DVEFcAbgd5CEw,460 +ray/air/execution/__pycache__/__init__.cpython-312.pyc,, +ray/air/execution/_internal/__init__.py,sha256=NH6qtho9rC7Tkf4lWcosAzZ2ductAX8wt6oCbxJMGKg,251 +ray/air/execution/_internal/__pycache__/__init__.cpython-312.pyc,, +ray/air/execution/_internal/__pycache__/actor_manager.cpython-312.pyc,, +ray/air/execution/_internal/__pycache__/barrier.cpython-312.pyc,, +ray/air/execution/_internal/__pycache__/event_manager.cpython-312.pyc,, +ray/air/execution/_internal/__pycache__/tracked_actor.cpython-312.pyc,, +ray/air/execution/_internal/__pycache__/tracked_actor_task.cpython-312.pyc,, +ray/air/execution/_internal/actor_manager.py,sha256=GHJU_mRE9Wq_lHAtkpOSWjLO1-MiES2frBjwgG99mO8,35022 +ray/air/execution/_internal/barrier.py,sha256=lcA7Lkx4il0v7CbqhHe9mlQwK4ZcRGVJ7Etydc4NRbY,2951 +ray/air/execution/_internal/event_manager.py,sha256=OAlhQJK9RTU2Rgj6y2Kjjc7robf1QmZ_h7ZRu6VIocw,4933 +ray/air/execution/_internal/tracked_actor.py,sha256=wAixW_J8iKPMPvxEknk5bBDsCam3goMyc1o4mnrnhfY,1715 +ray/air/execution/_internal/tracked_actor_task.py,sha256=iYqDQNCD9W6MRWpzJVzDuA69S5nqgE6paPgZR-1RirM,1261 +ray/air/execution/resources/__init__.py,sha256=cixguYR5h_W2aIooIQfmpeQB3rstw5DVEFcAbgd5CEw,460 +ray/air/execution/resources/__pycache__/__init__.cpython-312.pyc,, +ray/air/execution/resources/__pycache__/fixed.cpython-312.pyc,, +ray/air/execution/resources/__pycache__/placement_group.cpython-312.pyc,, +ray/air/execution/resources/__pycache__/request.cpython-312.pyc,, +ray/air/execution/resources/__pycache__/resource_manager.cpython-312.pyc,, +ray/air/execution/resources/fixed.py,sha256=PK7Fja8oPsU2VNnDXEovTfJsETn9_1IOmSomzYCrC8g,5544 +ray/air/execution/resources/placement_group.py,sha256=mWXzYUKvJoU6n-J9EPxxvQvhJoqCmvzhMBCop-Hdng4,8541 +ray/air/execution/resources/request.py,sha256=4gWzO9shDnn5zZoTflZjwmlm1n8_Fo6CcMIIFV28sp4,8541 +ray/air/execution/resources/resource_manager.py,sha256=5wRkNnqIFuImjjsrQmOWGpby70dkryFVZevO0F-_JgE,6231 +ray/air/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/air/integrations/__pycache__/__init__.cpython-312.pyc,, +ray/air/integrations/__pycache__/comet.cpython-312.pyc,, +ray/air/integrations/__pycache__/keras.cpython-312.pyc,, +ray/air/integrations/__pycache__/mlflow.cpython-312.pyc,, +ray/air/integrations/__pycache__/wandb.cpython-312.pyc,, +ray/air/integrations/comet.py,sha256=jnLWDSNGqOegx1wcKZ1N2YljIo2cn7P11HNXHc54mvo,9144 +ray/air/integrations/keras.py,sha256=oKN9ssU3dbhk2a5w1uZAvaCd4E-WjL-XOqLGVHuCCvc,6522 +ray/air/integrations/mlflow.py,sha256=HGaPwpL6GV9lzj_s8GtuDrZmh-j51T6y6JfHOmM34wc,12907 +ray/air/integrations/wandb.py,sha256=8fZ6Ig_CoJkj6SEuAni1kmJIP1rjgjI6WjqRKkiVc5I,29081 +ray/air/result.py,sha256=FM-FacZBiXjVMTHmvq-AGvb4NeKAczyvK1ikO7k-4oE,10982 +ray/air/session.py,sha256=WP3yXyfNlkwowGmN0Zey0QUTMTSNfyH9Ozc-I8Tz0Ok,61 +ray/air/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/air/util/__pycache__/__init__.cpython-312.pyc,, +ray/air/util/__pycache__/data_batch_conversion.cpython-312.pyc,, +ray/air/util/__pycache__/node.cpython-312.pyc,, +ray/air/util/__pycache__/transform_pyarrow.cpython-312.pyc,, +ray/air/util/data_batch_conversion.py,sha256=k2P_K2Jc6jGb5wqHXW10BFkfFcdJF8V7FQjdEPkxe3I,12539 +ray/air/util/node.py,sha256=ybbzkBSNSq5L_ZPPrQYb7yvogqT6P_F2MmT8UuXJWaE,2682 +ray/air/util/object_extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/air/util/object_extensions/__pycache__/__init__.cpython-312.pyc,, +ray/air/util/object_extensions/__pycache__/arrow.cpython-312.pyc,, +ray/air/util/object_extensions/__pycache__/pandas.cpython-312.pyc,, +ray/air/util/object_extensions/arrow.py,sha256=zgHWDm8AZNgl_yB_s3u_jXm9x7IRPt2dUnrpFiF_dS4,4358 +ray/air/util/object_extensions/pandas.py,sha256=isouYndAuTTRBr8jjOKnuQWh3_IFt4K_BW4CFv_myIc,4514 +ray/air/util/tensor_extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/air/util/tensor_extensions/__pycache__/__init__.cpython-312.pyc,, +ray/air/util/tensor_extensions/__pycache__/arrow.cpython-312.pyc,, +ray/air/util/tensor_extensions/__pycache__/pandas.cpython-312.pyc,, +ray/air/util/tensor_extensions/__pycache__/utils.cpython-312.pyc,, +ray/air/util/tensor_extensions/arrow.py,sha256=eaWINXnTR8qqW8kEztMxyenpxp1RSdtCXxAML2wt3Is,58255 +ray/air/util/tensor_extensions/pandas.py,sha256=Zx_qAJEBrR1gfmgGWL7u81kpzwYwtxbcVaodLdrWQO4,50741 +ray/air/util/tensor_extensions/utils.py,sha256=b5JhPVSUqpAOYdLQdBR6SUreuDGWf2VZQuyu1r4I7Ks,7457 +ray/air/util/transform_pyarrow.py,sha256=3mYzi2vibRmPNlgdG_vEh7eXl11_9it7CO5t9lb9J6E,1670 +ray/autoscaler/__init__.py,sha256=aiHhRCAyGsh8oAsf3fMsFDsoB-s3ZJ3Ec8dChpX52kI,158 +ray/autoscaler/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/__pycache__/batching_node_provider.cpython-312.pyc,, +ray/autoscaler/__pycache__/command_runner.cpython-312.pyc,, +ray/autoscaler/__pycache__/launch_and_verify_cluster.cpython-312.pyc,, +ray/autoscaler/__pycache__/node_launch_exception.cpython-312.pyc,, +ray/autoscaler/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/__pycache__/tags.cpython-312.pyc,, +ray/autoscaler/_private/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/autoscaler.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/cli_logger.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/cli_logger_demoall.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/cluster_dump.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/command_runner.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/commands.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/constants.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/docker.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/event_summarizer.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/event_system.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/legacy_info_string.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/load_metrics.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/loader.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/log_timer.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/monitor.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/node_launcher.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/node_provider_availability_tracker.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/node_tracker.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/prom_metrics.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/providers.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/resource_demand_scheduler.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/subprocess_output_util.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/updater.cpython-312.pyc,, +ray/autoscaler/_private/__pycache__/util.cpython-312.pyc,, +ray/autoscaler/_private/_azure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/_azure/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/_azure/__pycache__/config.cpython-312.pyc,, +ray/autoscaler/_private/_azure/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/_private/_azure/azure-config-template.json,sha256=PHdS9WWvEsWXe879zMVedYBPmlam4PzJ5zt1Xccbp1E,5200 +ray/autoscaler/_private/_azure/azure-vm-template.json,sha256=AJtY-dHXr7cF6ScAqXLhRTxL04Ft3sj2OK84ZegXDpc,12146 +ray/autoscaler/_private/_azure/config.py,sha256=YMW_-rENWqKZ9iz5P_XEVov770M2_qMu4jfG1b6WDk4,22024 +ray/autoscaler/_private/_azure/node_provider.py,sha256=sVRTZ99Kb-2Zchfr3Chos03GQALMQS4z42mVUt9aIjE,37276 +ray/autoscaler/_private/aliyun/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/aliyun/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/aliyun/__pycache__/config.cpython-312.pyc,, +ray/autoscaler/_private/aliyun/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/_private/aliyun/__pycache__/utils.cpython-312.pyc,, +ray/autoscaler/_private/aliyun/config.py,sha256=Xs_brOQGEEhBAcNxDWrcq2yPDeOU81DnIOnX-4zMVmA,3822 +ray/autoscaler/_private/aliyun/node_provider.py,sha256=ArOL1hVY_AFgev9QTicH9q3joIsh11RJC0M7iZ57q8o,12726 +ray/autoscaler/_private/aliyun/utils.py,sha256=DqU2FXDBW3o3Js1HivUP-dD9hhhcc8Ql-hInn2pUc9Y,18551 +ray/autoscaler/_private/autoscaler.py,sha256=UvgDp9ix-zHx5wUCXo5iH_vuYc2DiYzFbYPzaVw7Z1g,65646 +ray/autoscaler/_private/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/aws/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/aws/__pycache__/config.cpython-312.pyc,, +ray/autoscaler/_private/aws/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/_private/aws/__pycache__/utils.cpython-312.pyc,, +ray/autoscaler/_private/aws/cloudwatch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/aws/cloudwatch/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/aws/cloudwatch/__pycache__/cloudwatch_helper.cpython-312.pyc,, +ray/autoscaler/_private/aws/cloudwatch/cloudwatch_helper.py,sha256=p1FKhKNbzQZmTrb0UORyjUIyVICzyaO7xHdwMCECgOw,32701 +ray/autoscaler/_private/aws/config.py,sha256=QtIblr9uHm2ADZWW5NKvBFtb_awsqZrzHp9PLYz-ERU,45308 +ray/autoscaler/_private/aws/node_provider.py,sha256=tvFJC-tiuZ2tqo8v9OrLh5Lrf20nLOGPeQip1uwQmvA,28326 +ray/autoscaler/_private/aws/utils.py,sha256=lYPrLd_oFTwsqnowa9pw9YQTsTnlSdedkBXra0Npq7Y,5917 +ray/autoscaler/_private/cli_logger.py,sha256=1HoJnyjfpwpo5y7JNM1ub7MfcxgEESBFcI3brEXYwnY,25916 +ray/autoscaler/_private/cli_logger_demoall.py,sha256=UB12VUVWDG-Ey6q13DVnWLQGaVyycp-7kQxdC27vap4,1261 +ray/autoscaler/_private/cluster_dump.py,sha256=KcDsS92ZO1Q31i29VgMsyRgEMue2ggeVdQUwdbNfmJs,19664 +ray/autoscaler/_private/command_runner.py,sha256=jGVVIyLvMidkfMXoUdFws0J58lbd_llteh1YiLomKNo,37051 +ray/autoscaler/_private/commands.py,sha256=bah5gRe6v_gjCb6uEmlWYWQahClQBMaVmoWNQQTHHdI,60119 +ray/autoscaler/_private/constants.py,sha256=XGEg5ZckkXm19XeU6gx-ISOxTPcfES3JgIRY4CAduic,5766 +ray/autoscaler/_private/docker.py,sha256=g4FPTRY6wrt_fI4Dnk0MubZgRW9roLfwo8steo2T3Bc,3789 +ray/autoscaler/_private/event_summarizer.py,sha256=xdshB1iBm196bnGW02BsUDClYXEmUuU62wnAK0T0xw4,2881 +ray/autoscaler/_private/event_system.py,sha256=rd7wj33LgCSKcveGaqpSJ4RN3pwsqTlXCyTwAQGzVTQ,3870 +ray/autoscaler/_private/fake_multi_node/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/fake_multi_node/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/fake_multi_node/__pycache__/command_runner.cpython-312.pyc,, +ray/autoscaler/_private/fake_multi_node/__pycache__/docker_monitor.cpython-312.pyc,, +ray/autoscaler/_private/fake_multi_node/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/_private/fake_multi_node/__pycache__/test_utils.cpython-312.pyc,, +ray/autoscaler/_private/fake_multi_node/command_runner.py,sha256=pgeMrUV4GAYZakvDBuMliSbddZ_ASlG8LYFZV6HPx0Q,3222 +ray/autoscaler/_private/fake_multi_node/docker_monitor.py,sha256=Xl--oS_J6zpx8WhSoAKN4FXi0Sa70wCtDnOIKEpq2h4,7372 +ray/autoscaler/_private/fake_multi_node/node_provider.py,sha256=jqqKW9q8z7XbN15NZTRZgG4KkA4l7MBQc3OERN2awJo,25641 +ray/autoscaler/_private/fake_multi_node/test_utils.py,sha256=nFqRAIrfFIJVr9slRoCaCFaOrKLhd8CjMMCnSdEYfiw,12508 +ray/autoscaler/_private/gcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/gcp/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/gcp/__pycache__/config.cpython-312.pyc,, +ray/autoscaler/_private/gcp/__pycache__/node.cpython-312.pyc,, +ray/autoscaler/_private/gcp/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/_private/gcp/__pycache__/tpu_command_runner.cpython-312.pyc,, +ray/autoscaler/_private/gcp/config.py,sha256=AiqZpBU33WPTdTffa71dTdOp4cueazhPSkq7E8qmGe8,26277 +ray/autoscaler/_private/gcp/node.py,sha256=V9J1TAlebhg-JIb7h_m3IlvyrvUoSZF5ZEKhgnR10uc,27082 +ray/autoscaler/_private/gcp/node_provider.py,sha256=MA8WYUf5jrEGKyK8CEbuFVrbFt6V3dqgSR0sbxtYqRU,12623 +ray/autoscaler/_private/gcp/tpu_command_runner.py,sha256=oJ7WO4aLUIrInS0j2wX0t2jg4s2i316I7s0G6H2nePY,12021 +ray/autoscaler/_private/kuberay/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/kuberay/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/kuberay/__pycache__/autoscaling_config.cpython-312.pyc,, +ray/autoscaler/_private/kuberay/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/_private/kuberay/__pycache__/run_autoscaler.cpython-312.pyc,, +ray/autoscaler/_private/kuberay/__pycache__/utils.cpython-312.pyc,, +ray/autoscaler/_private/kuberay/autoscaling_config.py,sha256=tVMUu7An_3XaYEX_If6e9zYdXgXgKDmZihvr3m_-yac,20847 +ray/autoscaler/_private/kuberay/node_provider.py,sha256=H10PoMnGDQsPFcowH23XBuGbcQS8PsX_-M0tmJKWlpY,21205 +ray/autoscaler/_private/kuberay/run_autoscaler.py,sha256=euXj0sX9z4X-TWIkNjBvZhCn1kC85EhuMTmYl4-C2rk,4548 +ray/autoscaler/_private/kuberay/utils.py,sha256=jBfQKs9gZcEgWY4mueyhnNahOOMokDOkClFQefWqUR8,3477 +ray/autoscaler/_private/legacy_info_string.py,sha256=Z2geRjdUbWaUFBiFsz137O5pu_oHXVb28foPBgis2ek,1225 +ray/autoscaler/_private/load_metrics.py,sha256=6WUVSAdFqdgbS3kGWemUpm-WTkfOIjCSOmlymu738mc,14657 +ray/autoscaler/_private/loader.py,sha256=qUmuP9226LpEwIoDc__q2n0kWuS0p9-p6Ghr0k-unuw,495 +ray/autoscaler/_private/local/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/local/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/local/__pycache__/config.cpython-312.pyc,, +ray/autoscaler/_private/local/__pycache__/coordinator_node_provider.cpython-312.pyc,, +ray/autoscaler/_private/local/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/_private/local/config.py,sha256=mT-X9sbPT_s5v8n1oKJbpEUx2dkukONHKlzu5sZFkOk,4525 +ray/autoscaler/_private/local/coordinator_node_provider.py,sha256=8GagwaC3hTRo7fQXrff96V5Oa3YafqFXPxIV8XsLsEg,4142 +ray/autoscaler/_private/local/node_provider.py,sha256=QZPYjrWQw-jlLSXzJXLnC-uzibnzuIqVZ2fpvh5qpUg,11806 +ray/autoscaler/_private/log_timer.py,sha256=lhEtdlAAGhFbxGngzSmo5e3u9dE6fIl8cLdU3p10AL8,876 +ray/autoscaler/_private/monitor.py,sha256=K6UWS6ra5inQWDa8KilrcVQ_cO1vL84vUtcZ-S0anVM,28935 +ray/autoscaler/_private/node_launcher.py,sha256=LvaU6ZPROAGtOiIsMHCzK47AGACbZdqS-gKzmQumirA,8230 +ray/autoscaler/_private/node_provider_availability_tracker.py,sha256=QVn9Usn2uS0hnee9mKmb3YUBXXpFN_zrAOaMunV5BPQ,5862 +ray/autoscaler/_private/node_tracker.py,sha256=_rqXfHoZvLIXATduhIL-2x5V0zBZjiuiv6dHSYHoHhM,2744 +ray/autoscaler/_private/prom_metrics.py,sha256=SA_PWOIr7Ved6bbHx0DxpNMHDE_F2TSPF1qX8ws6CPY,11817 +ray/autoscaler/_private/providers.py,sha256=Fx9k0rPxl1lAemxCPRo5fzKtIu_4e2ndDaj_uOiaWyI,8779 +ray/autoscaler/_private/readonly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/readonly/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/readonly/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/_private/readonly/defaults.yaml,sha256=qumkmEJl35ftXlQP50f6QtrtURhDy5GpzrZ_UFHnldI,752 +ray/autoscaler/_private/readonly/node_provider.py,sha256=usZIRxb3rzTxtJrYeHo5tUUguhmBofCqHnArY5pV3IA,2545 +ray/autoscaler/_private/resource_demand_scheduler.py,sha256=S7itPzqax9IgnfZwe8lqvgkovseDl6wZKnsXq9a8Rcs,41312 +ray/autoscaler/_private/spark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/spark/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/spark/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/_private/spark/__pycache__/spark_job_server.cpython-312.pyc,, +ray/autoscaler/_private/spark/node_provider.py,sha256=-bjrrlhAG5EIVt_LIDf_KMvOt8q5cu_Sefr5KYq0N6Q,9074 +ray/autoscaler/_private/spark/spark_job_server.py,sha256=cnh0QlwSZL5ileKrc74awGsG_sEay8X4oSd-RI7d7gQ,11344 +ray/autoscaler/_private/subprocess_output_util.py,sha256=VwJTMnAo7u_HbJKeYTeCXIMWot8CBhkShN1YVpeB2KE,15003 +ray/autoscaler/_private/updater.py,sha256=f5PFXjAd_AxWMS3Un5FehUaagXmh4tsM157JCw_hkIE,24887 +ray/autoscaler/_private/util.py,sha256=H_n3QSPdMHiiXNJF5xnhYOF19rzugFlMK8nZyRg1eMQ,38174 +ray/autoscaler/_private/vsphere/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/_private/vsphere/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/_private/vsphere/__pycache__/cluster_operator_client.cpython-312.pyc,, +ray/autoscaler/_private/vsphere/__pycache__/config.cpython-312.pyc,, +ray/autoscaler/_private/vsphere/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/_private/vsphere/cluster_operator_client.py,sha256=uiEGX81DVCxgP8ijNSm-4Qk4Ab0icr1kAQTVhre94aU,27472 +ray/autoscaler/_private/vsphere/config.py,sha256=tpjlj_ZFjd62QL2NHxkihG-RWyON4hNlBvRgB2nKCd8,5326 +ray/autoscaler/_private/vsphere/node_provider.py,sha256=oBDDXs_zgo5jjTV17YlbCN9EYZZJvy6Npo9k-HenhKo,4696 +ray/autoscaler/aliyun/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/aliyun/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/aws/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/aws/cloudwatch/prometheus.yml,sha256=AbNQEhaJsc1ohI7uxrhaj9QlGysVd9-q8yOSGfZJROc,334 +ray/autoscaler/aws/cloudwatch/ray_prometheus_waiter.sh,sha256=CHggfSNMYdXjlPGDFIY_we12VcWdXIgxwEet_RklXbs,1142 +ray/autoscaler/aws/defaults.yaml,sha256=p6LYLDlLDXm09_6symscp9T-Df5PUd_sRJTngeiiy5U,6911 +ray/autoscaler/azure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/azure/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/azure/defaults.yaml,sha256=UsYrV2tIm1R0AFs09ku0aPMl2keqOmOidDI3xXL7yHY,7847 +ray/autoscaler/batching_node_provider.py,sha256=xV4B_AWUyvbNMuh638jJ0f1uqCNyH52nGpttu8wGY_o,10437 +ray/autoscaler/command_runner.py,sha256=hJ2_O2EPmyavYzmf17r2pkM5WfoUe14lCzWO1AzTg6Y,3464 +ray/autoscaler/gcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/gcp/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/gcp/defaults.yaml,sha256=6AF5PhG5g-QSnLtczbdq77ohai7bROJGhzXyi5X139g,7296 +ray/autoscaler/launch_and_verify_cluster.py,sha256=tyhLqM0uU7VWCUDvuItPP1oCoZgl8gxdPtcFHBx-39U,17657 +ray/autoscaler/local/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/local/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/local/__pycache__/coordinator_server.cpython-312.pyc,, +ray/autoscaler/local/coordinator_server.py,sha256=svex9PhyefQ8krqDK3cDujYKLv11ePBavx_0wOSvldw,4471 +ray/autoscaler/local/defaults.yaml,sha256=Pk5bSGGTK0UOHt8AT4TOEP8GVJMW8UFm8F9LOa6sHKU,893 +ray/autoscaler/node_launch_exception.py,sha256=EWnbG283fRiNah8-mUaYfFjnkk4p_0-yRnyvFYg0-RA,1238 +ray/autoscaler/node_provider.py,sha256=C2FOTTF3jZAEkMyLwqemdjfoM6Mpens_kyqJrvXS89U,10616 +ray/autoscaler/ray-schema.json,sha256=br14oaeRWEy0q3vzG57sbBqo4etuju9tlfGaxNmPTbQ,18165 +ray/autoscaler/sdk/__init__.py,sha256=gHjOa9a1uT-Q6hbb3AbA6tTvqkUGGrzvb-9B16JP4C0,652 +ray/autoscaler/sdk/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/sdk/__pycache__/sdk.cpython-312.pyc,, +ray/autoscaler/sdk/sdk.py,sha256=7A-HKQ_yi0MW5mcbRkWt3cVXO0Ocm2GFiSzhM-XeE5w,14732 +ray/autoscaler/spark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/spark/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/spark/defaults.yaml,sha256=fujd8XkhAEV5aO_hETPKYPAe_KlS5r1t4YMV8mtiFxs,1513 +ray/autoscaler/tags.py,sha256=XhIcZN3-3oaePfdqWwWsDgrh0yLbTMADqmlp8Wh6GDE,1946 +ray/autoscaler/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/v2/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/v2/__pycache__/autoscaler.cpython-312.pyc,, +ray/autoscaler/v2/__pycache__/event_logger.cpython-312.pyc,, +ray/autoscaler/v2/__pycache__/metrics_reporter.cpython-312.pyc,, +ray/autoscaler/v2/__pycache__/monitor.cpython-312.pyc,, +ray/autoscaler/v2/__pycache__/scheduler.cpython-312.pyc,, +ray/autoscaler/v2/__pycache__/schema.cpython-312.pyc,, +ray/autoscaler/v2/__pycache__/sdk.cpython-312.pyc,, +ray/autoscaler/v2/__pycache__/utils.cpython-312.pyc,, +ray/autoscaler/v2/autoscaler.py,sha256=4AgKLkjkTFUG789jv7fl8rr-cSKPy_XgVyQcHB8XCjQ,8574 +ray/autoscaler/v2/event_logger.py,sha256=77cKmY_VzH8GVCyVNNL8NpfhccKPvmCw3SSL58J5ujA,8141 +ray/autoscaler/v2/instance_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/v2/instance_manager/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/__pycache__/common.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/__pycache__/config.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/__pycache__/instance_manager.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/__pycache__/instance_storage.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/__pycache__/node_provider.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/__pycache__/ray_installer.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/__pycache__/reconciler.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/__pycache__/storage.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/cloud_providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/v2/instance_manager/cloud_providers/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/cloud_providers/kuberay/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/v2/instance_manager/cloud_providers/kuberay/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/cloud_providers/kuberay/__pycache__/cloud_provider.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/cloud_providers/kuberay/cloud_provider.py,sha256=iyvZbXYeTeCqIqfmlpviXIjvMFX6PiBFO5hOn53vC0A,23378 +ray/autoscaler/v2/instance_manager/cloud_providers/read_only/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/v2/instance_manager/cloud_providers/read_only/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/cloud_providers/read_only/__pycache__/cloud_provider.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/cloud_providers/read_only/cloud_provider.py,sha256=ZYHhOoJ8V_nUgMLjkw4O3swenqrTKJqLPZITQQG7xnc,2680 +ray/autoscaler/v2/instance_manager/common.py,sha256=A0FsMRGLUugd8pH3id6RPRucdtEH0U1N_xZCZQJCTn4,19207 +ray/autoscaler/v2/instance_manager/config.py,sha256=dGgBfGSz7eBoUPcnSy7DQkgnQBYJ1dqDeAgCm7maN8w,21169 +ray/autoscaler/v2/instance_manager/instance_manager.py,sha256=IieLYCi9Px6W1ChNKkDlLv4hWKQxg-vrxdtMoAGoay8,9731 +ray/autoscaler/v2/instance_manager/instance_storage.py,sha256=j3ecbqBvAB0zo1dRMlIOId_UL8KPW7wnYW2j3yFd5p4,5679 +ray/autoscaler/v2/instance_manager/node_provider.py,sha256=f3sd-4t8iJxlQyEz_D75Dcqu0C8pPymiNCIGaxa-O3o,18610 +ray/autoscaler/v2/instance_manager/ray_installer.py,sha256=pvdsAi56XorGUijHfJ29RLi-qv8WhnS1iuSz2Ck1Lkc,4143 +ray/autoscaler/v2/instance_manager/reconciler.py,sha256=RwQ48xmyjcWOUrS-BVSTNT77mhxmAHGmGN6MW-tlgFc,65606 +ray/autoscaler/v2/instance_manager/storage.py,sha256=sonkaNbQLA_KT7sTO09RI-fjpIKgKT6C5ao0Z14MRLw,6755 +ray/autoscaler/v2/instance_manager/subscribers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/v2/instance_manager/subscribers/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/subscribers/__pycache__/cloud_instance_updater.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/subscribers/__pycache__/ray_stopper.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/subscribers/__pycache__/threaded_ray_installer.cpython-312.pyc,, +ray/autoscaler/v2/instance_manager/subscribers/cloud_instance_updater.py,sha256=MHDvHSPuw0IB196psHjcrIqv8zb4G9qJGVZMFk8lbGI,3207 +ray/autoscaler/v2/instance_manager/subscribers/ray_stopper.py,sha256=SSGxPr-XIiZ64LQ5vzDqc9QAVBv-QGQ_56ZWn-6Wrgs,5263 +ray/autoscaler/v2/instance_manager/subscribers/threaded_ray_installer.py,sha256=FM4cCgaUlNjuCQX7Qv3liiiTGQRG25a9GcNJGXCwMvU,3307 +ray/autoscaler/v2/metrics_reporter.py,sha256=JQ1pojYgdoQQyIpC7P5PTLzfhmNV1K3K06nXfX2c7u0,4423 +ray/autoscaler/v2/monitor.py,sha256=YhSQbVPutRD9D1mJF6oMc3NBSetyJEWHUCBgHH594jQ,11193 +ray/autoscaler/v2/scheduler.py,sha256=CSzqRHz0vbBvUumTfP9IhzQP1xPxs_gcpj9LL-u_5Jg,70463 +ray/autoscaler/v2/schema.py,sha256=pKyT7Ax4XKThBTDNqV66N3ZmCjB-PJp36DfbfCde9U8,12681 +ray/autoscaler/v2/sdk.py,sha256=JEx9XmMymqJ-asmg3rJk9P4pYDE7SRoW-_8h9MPNdtk,3928 +ray/autoscaler/v2/utils.py,sha256=L4qYaGSlCSjFcB7BOVSFbZiza--mG_H9MYCwL0b0erA,38858 +ray/autoscaler/vsphere/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/autoscaler/vsphere/__pycache__/__init__.cpython-312.pyc,, +ray/autoscaler/vsphere/defaults.yaml,sha256=J6wBtj7QaZe8vZMCiC83XRk-SLDegiLcU7ilvaQV-EI,5557 +ray/client_builder.py,sha256=eHlfZUnIudi_rcfkdVbAFbXh1MQmhD5ateWzKH1akTw,14589 +ray/cloudpickle/__init__.py,sha256=lIwB8SqsfVuzH7agZGFPJfL_lum77ED2F6FF_nI9nE8,1510 +ray/cloudpickle/__pycache__/__init__.cpython-312.pyc,, +ray/cloudpickle/__pycache__/cloudpickle.cpython-312.pyc,, +ray/cloudpickle/__pycache__/cloudpickle_fast.cpython-312.pyc,, +ray/cloudpickle/__pycache__/compat.cpython-312.pyc,, +ray/cloudpickle/__pycache__/py_pickle.cpython-312.pyc,, +ray/cloudpickle/cloudpickle.py,sha256=ejxhXSXd0dq7yZQ0nawKqH0PhGT1Qj6H5Kb16zHICMs,55282 +ray/cloudpickle/cloudpickle_fast.py,sha256=1GqUD4nLKsv0vv9ty2La3eVLyeWNrPFlhUCN-aNI-30,322 +ray/cloudpickle/compat.py,sha256=UMzLRwgoQjqrL7x9ar71hQXQ4oOm-kHO9rzhg0E9Cj4,664 +ray/cloudpickle/py_pickle.py,sha256=ATzQ96P3YbYhf3Z1aZfzzKMmbiQBdp2J94CjUQ3-fx8,676 +ray/cluster_utils.py,sha256=fXCtc2WdEQBeT-p_3m0TP89S3hDSnlyov8MYC3e6Fbc,15512 +ray/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/core/__pycache__/__init__.cpython-312.pyc,, +ray/core/generated/__pycache__/autoscaler_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/autoscaler_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/common_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/common_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/core_worker_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/core_worker_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/dependency_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/dependency_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_actor_definition_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_actor_definition_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_actor_lifecycle_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_actor_lifecycle_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_actor_task_definition_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_actor_task_definition_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_base_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_base_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_driver_job_definition_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_driver_job_definition_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_driver_job_lifecycle_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_driver_job_lifecycle_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_event_aggregator_service_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_event_aggregator_service_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_node_definition_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_node_definition_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_node_lifecycle_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_node_lifecycle_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_task_definition_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_task_definition_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_task_lifecycle_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_task_lifecycle_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/events_task_profile_events_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/events_task_profile_events_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/export_actor_data_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/export_actor_data_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/export_dataset_metadata_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/export_dataset_metadata_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/export_dataset_operator_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/export_dataset_operator_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/export_driver_job_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/export_driver_job_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/export_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/export_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/export_node_data_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/export_node_data_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/export_runtime_env_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/export_runtime_env_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/export_submission_job_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/export_submission_job_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/export_task_event_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/export_task_event_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/export_train_state_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/export_train_state_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/gcs_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/gcs_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/gcs_service_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/gcs_service_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/instance_manager_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/instance_manager_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/logging_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/logging_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/metrics_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/metrics_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/node_manager_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/node_manager_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/profile_events_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/profile_events_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/pubsub_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/pubsub_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/ray_client_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/ray_client_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/reporter_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/reporter_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/resource_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/resource_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/runtime_env_agent_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/runtime_env_agent_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/runtime_env_common_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/runtime_env_common_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/runtime_environment_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/runtime_environment_pb2_grpc.cpython-312.pyc,, +ray/core/generated/__pycache__/usage_pb2.cpython-312.pyc,, +ray/core/generated/__pycache__/usage_pb2_grpc.cpython-312.pyc,, +ray/core/generated/autoscaler_pb2.py,sha256=KOS-KiEgXn47Z3TXK07LFMJwSViOLjQMJQ-rqIxbhs8,32086 +ray/core/generated/autoscaler_pb2_grpc.py,sha256=ni6xPqbxf8FrgQIBLRe1UUbX3rdxArr9GOeotlJ-NEg,12304 +ray/core/generated/common_pb2.py,sha256=ew8XV0oi_yllbQ9mojwg_dk4xdHb2jf5J3GufWlc7R4,72430 +ray/core/generated/common_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/core_worker_pb2.py,sha256=81LVl2NHpwBxCH0fJenZ9kjmavfL3fx3o-OBvaS6fpg,40293 +ray/core/generated/core_worker_pb2_grpc.py,sha256=hNBfAFBJT-B9sazo01Y9vY0hR_Z_TOUqXmNPcwxIXIs,44581 +ray/core/generated/dependency_pb2.py,sha256=aBlRlo3lu7F6HVRsVe32l2Gi-nk6t9A7E2afujBlMa8,1277 +ray/core/generated/dependency_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/event_pb2.py,sha256=kMIk-7gD5oCLjCnbrCHNwM5Tpiq_vWQ0rc_zfzXfn_I,3167 +ray/core/generated/event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_actor_definition_event_pb2.py,sha256=2MnnJYd08zN9k7bNYXrxyf6h9xqV-8ejkBbFIqJxutM,4017 +ray/core/generated/events_actor_definition_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_actor_lifecycle_event_pb2.py,sha256=8U1TYM-j4KYkbQv56So1actqnS5OIMO70R-sopAjqjI,3219 +ray/core/generated/events_actor_lifecycle_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_actor_task_definition_event_pb2.py,sha256=qCvcGVY2wWyY0h4B6MeXuEZs1OZBf2l7O51pmb1WksA,4556 +ray/core/generated/events_actor_task_definition_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_base_event_pb2.py,sha256=5lKDdgP5-ofgf6_xNRA7C7m2xcwM6GwpwE5bNQEKyQk,6532 +ray/core/generated/events_base_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_driver_job_definition_event_pb2.py,sha256=A5P5vbzKo1Ck1sknRUJIX9ywmW3P7wRA6RZ8K8TGXpQ,3561 +ray/core/generated/events_driver_job_definition_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_driver_job_lifecycle_event_pb2.py,sha256=0FtcjNuCs7_cFRKjrec08wdi6pWRuxx7mvMMhlKjriQ,2928 +ray/core/generated/events_driver_job_lifecycle_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_event_aggregator_service_pb2.py,sha256=skyu_uG3LOrnxC4M5xKTjJjXBOpuip9yfJCC2U9VZIg,4535 +ray/core/generated/events_event_aggregator_service_pb2_grpc.py,sha256=wpCp6RNwq75Oh9EILZmtk4jLKhZCHSzNTdYNOgWnLIQ,2945 +ray/core/generated/events_node_definition_event_pb2.py,sha256=QC-W30kz1v6hfE-6APWwUEaIlxS208wi1pdu1KhWuD4,2646 +ray/core/generated/events_node_definition_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_node_lifecycle_event_pb2.py,sha256=H2iZGuznos5pp83xfpSTHRcbKpq30wSTubT1ixFRQ74,5613 +ray/core/generated/events_node_lifecycle_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_task_definition_event_pb2.py,sha256=ld4cXb3fBiCINfTrRdzCd0qbJb3G0oJ19cntJEcCZKg,4364 +ray/core/generated/events_task_definition_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_task_lifecycle_event_pb2.py,sha256=giO4u1IhCmKHmxEDIjCFCqIkr4JBvAQepejeXet_RZI,2922 +ray/core/generated/events_task_lifecycle_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/events_task_profile_events_pb2.py,sha256=7W9keNG7BOR-VEB5zYLf4w6K_eMkACjAC6rxqDw1mIA,1711 +ray/core/generated/events_task_profile_events_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/export_actor_data_pb2.py,sha256=X1wzSuCOW_pS4MUNvi8iiGlYddjt2YOySI4CWR2BVlU,5479 +ray/core/generated/export_actor_data_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/export_dataset_metadata_pb2.py,sha256=m0vv6UmePZ3dBV6dRygNX-Pvk5Cd0KQyV-xfiFRyYoU,5784 +ray/core/generated/export_dataset_metadata_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/export_dataset_operator_event_pb2.py,sha256=ZDLcZ_5RJU-Og7Y06oMOZzuzEMzzDWPBjwmS-0VytWY,2377 +ray/core/generated/export_dataset_operator_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/export_driver_job_event_pb2.py,sha256=OM9OGEEyWDXcad0DJJUZ3wKaQBKIp-x6zH8swzsVuyQ,3823 +ray/core/generated/export_driver_job_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/export_event_pb2.py,sha256=oHmMRk6MAZd_PTmWicRNuKjETrG2u_ArHWiz-SyqWjM,4319 +ray/core/generated/export_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/export_node_data_pb2.py,sha256=9wRPb_mf1A20Z0Hv6bcRC10xs0QQQciEW0GfxiM08XM,5067 +ray/core/generated/export_node_data_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/export_runtime_env_pb2.py,sha256=vy2YSsHQK1CnbNajh_rMDjOie0XyWn0LWvdz41sAp7U,3203 +ray/core/generated/export_runtime_env_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/export_submission_job_event_pb2.py,sha256=MK-5-x63yBYqSALYkq_hoq1F0_3iDvQYEXtbB2E6Y0Y,3882 +ray/core/generated/export_submission_job_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/export_task_event_pb2.py,sha256=XrzvH7gTYKpRFxeuRD-xm27iSj95aoqm2kIGpllmT2Q,11253 +ray/core/generated/export_task_event_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/export_train_state_pb2.py,sha256=yz_nsMD8rVtwnmphPsi07oYZnSGOdigJXRXXMCvFq_8,9531 +ray/core/generated/export_train_state_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/gcs_pb2.py,sha256=FeepmzTHIqD62vYP9Aiiz8y3LO0IAGO4bPxoutycUTI,45126 +ray/core/generated/gcs_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/gcs_service_pb2.py,sha256=fva680lUw7puHQybDdJtKk6GQA8JlkdkUeKJ0jEtSp8,89788 +ray/core/generated/gcs_service_pb2_grpc.py,sha256=oTgm3chP-ew7q72TBbkqqcTCJ79vymL4tplT25XWpPE,101874 +ray/core/generated/instance_manager_pb2.py,sha256=koFjEv9cAg5AMCcYnLDy9h1q94-KItAoM-666rH3IS8,14183 +ray/core/generated/instance_manager_pb2_grpc.py,sha256=jP2XnPAaMxdiWwYKaTqVvutS11CPZGqNNwNkSnAXNAo,5016 +ray/core/generated/logging_pb2.py,sha256=LtBFvE-49KMXyBnLuncN2SuVHMsmiWXOK4jIs-Cp_YI,1498 +ray/core/generated/logging_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/metrics_pb2.py,sha256=C0yB-61QWP8wd6BPEt_YrvdKjoRoZZ_6o-aWtd8VcaY,13773 +ray/core/generated/metrics_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/node_manager_pb2.py,sha256=ODicSSGGAVgYXWKjQHVS4atvELuAjYsM2Bdv0oYJB-0,49236 +ray/core/generated/node_manager_pb2_grpc.py,sha256=Tv6bxfrTlcmjonHUEu1U8TxJw-naoSpmlQ7ehKwiWUM,52356 +ray/core/generated/profile_events_pb2.py,sha256=1uTFQ248n0eMxam1mKn2Z9erOrMYBctGSZdpuV9w0-A,2366 +ray/core/generated/profile_events_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/pubsub_pb2.py,sha256=MeNinKmfvloTcs9VSLt532hU1sHMkNbXmSmWzM_4T20,14921 +ray/core/generated/pubsub_pb2_grpc.py,sha256=MFYtBqHwAumqX2OKBFocu2tnyIsWkhWhNV1vFJ8OObQ,4566 +ray/core/generated/ray_client_pb2.py,sha256=6v-nrcayZDKcC98AKpp_gwn_hKm507yGMdbMFXQwUXI,38306 +ray/core/generated/ray_client_pb2_grpc.py,sha256=kNp7RJIYJWyo61W_1aBCDMxsnFThVr8uXvehD9FEJrc,31474 +ray/core/generated/reporter_pb2.py,sha256=mqjVMNd8gGZEKUkLoUhkcNS5jPNJh7WCIebD5YeHljM,12371 +ray/core/generated/reporter_pb2_grpc.py,sha256=buOstCVjnd0b2AsGuooEQZyD2METh2-pMIAxX7BssAk,15462 +ray/core/generated/resource_pb2.py,sha256=ZnF3b1DBIuH_46s4J4KUAs1VEuNv9FBMOCcrqmh6UwA,2522 +ray/core/generated/resource_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/runtime_env_agent_pb2.py,sha256=IL1ys5eux9IjDsOeERbi7JeQUu1gBwe4MDvZ_zZp5Tc,6218 +ray/core/generated/runtime_env_agent_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/runtime_env_common_pb2.py,sha256=5KhwV6YZBCveGdz07K8LgJ4hzO1p-7-wmgjn_96i6nk,1771 +ray/core/generated/runtime_env_common_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/runtime_environment_pb2.py,sha256=kSTRpAIAeL8J-F_NP8U0T1Y4YtMfJYAL6-EJOvrGEnU,2939 +ray/core/generated/runtime_environment_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/generated/usage_pb2.py,sha256=ncMLO83D1yIWpolHNHz1A4FUKRcMQUcGSxMKnPqmyg8,9843 +ray/core/generated/usage_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159 +ray/core/libjemalloc.so,sha256=AoSRnbI_leaSAmA5g4rvibWWS1z-xKiKy5s6n0oib9U,885296 +ray/core/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/core/src/__pycache__/__init__.cpython-312.pyc,, +ray/core/src/plasma/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/core/src/plasma/__pycache__/__init__.cpython-312.pyc,, +ray/core/src/ray/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/core/src/ray/__pycache__/__init__.cpython-312.pyc,, +ray/core/src/ray/gcs/gcs_server,sha256=Xww5cu33ZVEDuqn_wVXjNb2eYRKuXE5M2O0g60uIhu8,30020208 +ray/core/src/ray/raylet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/core/src/ray/raylet/__pycache__/__init__.cpython-312.pyc,, +ray/core/src/ray/raylet/raylet,sha256=Vz4AgE4D5EudAR8pkeAyB9f-oME0H3pUyQ7bkfV7Y_g,33168328 +ray/cross_language.py,sha256=zLBhiTmHpQN1_46-8HzC-3payWYMZ3t1yRw5YuoIsNg,3777 +ray/dag/__init__.py,sha256=h637YhI7QhEgHgLYzdnkiEJ73PtkrcZujYVOXdwYoQo,1146 +ray/dag/__pycache__/__init__.cpython-312.pyc,, +ray/dag/__pycache__/base.cpython-312.pyc,, +ray/dag/__pycache__/class_node.cpython-312.pyc,, +ray/dag/__pycache__/collective_node.cpython-312.pyc,, +ray/dag/__pycache__/compiled_dag_node.cpython-312.pyc,, +ray/dag/__pycache__/conftest.cpython-312.pyc,, +ray/dag/__pycache__/constants.cpython-312.pyc,, +ray/dag/__pycache__/context.cpython-312.pyc,, +ray/dag/__pycache__/dag_node.cpython-312.pyc,, +ray/dag/__pycache__/dag_node_operation.cpython-312.pyc,, +ray/dag/__pycache__/dag_operation_future.cpython-312.pyc,, +ray/dag/__pycache__/format_utils.cpython-312.pyc,, +ray/dag/__pycache__/function_node.cpython-312.pyc,, +ray/dag/__pycache__/input_node.cpython-312.pyc,, +ray/dag/__pycache__/output_node.cpython-312.pyc,, +ray/dag/__pycache__/py_obj_scanner.cpython-312.pyc,, +ray/dag/__pycache__/utils.cpython-312.pyc,, +ray/dag/__pycache__/vis_utils.cpython-312.pyc,, +ray/dag/base.py,sha256=YtWL9E3ufvwgrIEAlrktYiDTSYslL3236q34V8yKHfk,236 +ray/dag/class_node.py,sha256=LVDn6TQgAuY769MNs00iwyYrUk2Oe-6MXrZTMf9rVeI,11265 +ray/dag/collective_node.py,sha256=ZHgymCMYZ9aQH8gRFm4CMA-IqmOJeWCWlejly5syGHI,11877 +ray/dag/compiled_dag_node.py,sha256=y_l2WF1nLg3tbAKjoy2jKSzXN8CTJAkTFg5GUB2NFik,143800 +ray/dag/conftest.py,sha256=GseP4k3QkeKz7ptsqU33Lv3VLiaFTyCBLPv3SUv60EA,451 +ray/dag/constants.py,sha256=sSJCymlPXEZuZjd65FEAP-vr5sR0mKOlCMo8hqZMO7M,1567 +ray/dag/context.py,sha256=Mv9wuC29RPMYe_yGwuXVOqvYmH2fffr-L9jIhTCf41U,4500 +ray/dag/dag_node.py,sha256=m1K8A6z22I8vgD9E5ebYyglYWxnvdDAH85qgbYs4CXk,29422 +ray/dag/dag_node_operation.py,sha256=n9UzDSqJTrs_IsCQEgrGlSZn_hVYS8H0rNjjTNBDUPE,36988 +ray/dag/dag_operation_future.py,sha256=SDpDNK4oI7EsqOFyfcG5jeO-LmS-UgwzscMdAMMH8P0,4961 +ray/dag/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dag/experimental/__pycache__/__init__.cpython-312.pyc,, +ray/dag/format_utils.py,sha256=z5PhuyA9F_zQe8dhiRJF_ICtP5q3IKjKzshMHCF91Ik,5432 +ray/dag/function_node.py,sha256=cfYj4_oqjEK1o2p3tlOj6gDHlpWlxFW1kadqHq3m730,1657 +ray/dag/input_node.py,sha256=qA26BlPCR10Ckc0XDGT5YRdmNGxVpUs6CTOA8ZOHX9w,11231 +ray/dag/output_node.py,sha256=J3M9hzW1EgvGewFKNVpguNcJAAfYJGThTBgzZ1HEc6Y,1363 +ray/dag/py_obj_scanner.py,sha256=IgSbQ8mrWQEsnspYoyqg_MyIMcmdfkoCxKjZ4i6bhKY,3676 +ray/dag/utils.py,sha256=7IGAyeKCcdcvKiK5lTLQ5C9LIsN1Lwaw1h-k1piTIN0,2244 +ray/dag/vis_utils.py,sha256=ghRvGDCjPOZ4vtZC2_rBC_0CcW5IisnH7Whm1qjXYf0,3017 +ray/dashboard/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/__pycache__/agent.cpython-312.pyc,, +ray/dashboard/__pycache__/consts.cpython-312.pyc,, +ray/dashboard/__pycache__/dashboard.cpython-312.pyc,, +ray/dashboard/__pycache__/dashboard_metrics.cpython-312.pyc,, +ray/dashboard/__pycache__/head.cpython-312.pyc,, +ray/dashboard/__pycache__/http_server_agent.cpython-312.pyc,, +ray/dashboard/__pycache__/http_server_head.cpython-312.pyc,, +ray/dashboard/__pycache__/k8s_utils.cpython-312.pyc,, +ray/dashboard/__pycache__/memory_utils.cpython-312.pyc,, +ray/dashboard/__pycache__/optional_deps.cpython-312.pyc,, +ray/dashboard/__pycache__/optional_utils.cpython-312.pyc,, +ray/dashboard/__pycache__/routes.cpython-312.pyc,, +ray/dashboard/__pycache__/state_aggregator.cpython-312.pyc,, +ray/dashboard/__pycache__/state_api_utils.cpython-312.pyc,, +ray/dashboard/__pycache__/timezone_utils.cpython-312.pyc,, +ray/dashboard/__pycache__/utils.cpython-312.pyc,, +ray/dashboard/agent.py,sha256=jxZLwOWCoD5ZxP87dZa76fZl1N-2xnuSoTjkV2gK1UE,16318 +ray/dashboard/client/build/asset-manifest.json,sha256=gII6T_nHcqeHUzYA7jUlVGgVH7TWVDFvhyg5TxN2xbQ,3403 +ray/dashboard/client/build/favicon.ico,sha256=rQx7hQCsoElXbbBeXhh0MRzi9zBoOEBFUWSOGhyUdlE,4286 +ray/dashboard/client/build/index.html,sha256=eeRCeAmn2cW78mnrzHFTEl7FyziQM6LrvYtjFpl90As,446 +ray/dashboard/client/build/speedscope-1.5.3/LICENSE,sha256=YkSxPpvyXP1C2ByTnWxTXZMllujDIAUCNPLCrFpvNqA,1067 +ray/dashboard/client/build/speedscope-1.5.3/README,sha256=-PRL6Fdn8PLdpgP1AVgD1Xl0JSblPENMwecjXerTccQ,124 +ray/dashboard/client/build/speedscope-1.5.3/demangle-cpp.8a387750.js,sha256=mv-Npn65w_JY38YBee4t9RzfyKOF-bXZ9NjnS_3CW74,168983 +ray/dashboard/client/build/speedscope-1.5.3/favicon-16x16.361d2b26.png,sha256=piptp3QrQ-55ixBrcJqoLq_RtQDvBQQnz-wF5NQdrKw,679 +ray/dashboard/client/build/speedscope-1.5.3/favicon-32x32.1165a94e.png,sha256=nqbm0Tt7gGQYeGbeuh6GdNi6JQcSebp7FfNLVN8lCNA,1585 +ray/dashboard/client/build/speedscope-1.5.3/file-format-schema.json,sha256=w9yvzZSfe6sSZt1MtB8yleqeqhPOuwoGVtQ1Pz3qkfs,9517 +ray/dashboard/client/build/speedscope-1.5.3/import.a03c2bef.js,sha256=Qe4Z_L5Rr7ULfRICH3SdWiqtBggNhFq2bu-x_vwjlDo,184993 +ray/dashboard/client/build/speedscope-1.5.3/index.html,sha256=nAQjhLPU6jkZXzxm5475aPI7-FXX-wjBz_P7Jo6JyjQ,611 +ray/dashboard/client/build/speedscope-1.5.3/perf-vertx-stacks-01-collapsed-all.3e0a632c.txt,sha256=3tqhyAVSZ2OAMO-iUIERYQX9DjV5RM78CXTnkt_Kvt0,263949 +ray/dashboard/client/build/speedscope-1.5.3/release.txt,sha256=osQRt7-ph11D7a46UXabHDHPoXrIkMjpH5f5cT9hLGM,87 +ray/dashboard/client/build/speedscope-1.5.3/reset.7ae984ff.css,sha256=jaknxOGdqznF5pYqR9NyjmKVx3nwhW_D3wAm8deN2nQ,835 +ray/dashboard/client/build/speedscope-1.5.3/speedscope.75eb7d8e.js,sha256=YFtVhhIYS0LG3C5nA0L637BvXxKjj0-5QoE4EZdz_eA,209212 +ray/dashboard/client/build/static/css/main.388a904b.css,sha256=BXM07M3GnaxZqhtnB1Jskw2rjD9chAgDM1vjal-h3Rs,5995 +ray/dashboard/client/build/static/css/main.388a904b.css.map,sha256=GyKVBvNhPKjtPFd9oOV2Q4H3FjCv-zi7ndYK4U7NCjU,9323 +ray/dashboard/client/build/static/js/495.01ff0983.chunk.js,sha256=sYE2hzD-JL8zgXIC03Vyvd3q_HQRPzpzMNaFl97i-5w,354 +ray/dashboard/client/build/static/js/495.01ff0983.chunk.js.map,sha256=xw0DE1CLbOUuFmzAlzhMMC5gpdyg1mIx6NPJnMX2oU4,685 +ray/dashboard/client/build/static/js/591.36c340d9.chunk.js,sha256=IvN5hpq3s7AgBqyn7m8MSghNLg9A7MpMzQSIvaZlC0c,1801 +ray/dashboard/client/build/static/js/591.36c340d9.chunk.js.map,sha256=I-77z7ZeS0_WQA6AiQM8mZM1xaMqKgkGd2OPlVfypR0,6625 +ray/dashboard/client/build/static/js/main.32399d4c.js,sha256=n3XQkfk1N-p3zMunNAtRJsrb7gdx815-LPOJD49PcrA,1066958 +ray/dashboard/client/build/static/js/main.32399d4c.js.LICENSE.txt,sha256=Snsan2qc7LP5d4xF0YKf3KYSJYPmFM5KONhSCevnlsc,2804 +ray/dashboard/client/build/static/js/main.32399d4c.js.map,sha256=qzTcwDFVQZ8Ix74C5rdLw1BqodVph17bUhvzHdibMz4,6409584 +ray/dashboard/client/build/static/media/logo.3704c1bbca650bb72a64b5d4c3fa5ced.svg,sha256=253qk71FS2ttzmMB_BbxrMj7DGBee1P7TRKe5vPYtNo,2625 +ray/dashboard/client/build/static/media/roboto-latin-100.a45108d3b34af91f9113.woff,sha256=xOrU3p96_yN9BrUw6thBPRNXQn9qkllENCu04rHc5tA,20368 +ray/dashboard/client/build/static/media/roboto-latin-100.c2aa4ab115bf9c6057cb.woff2,sha256=EoI9WFYFI4EhVUr_i7BgojXcNvN-_Z-x5-bqGpYivDU,15808 +ray/dashboard/client/build/static/media/roboto-latin-100italic.451d4e559d6f57cdf6a1.woff,sha256=WjqYQEFHaPouyYizPJ6Wb9_-LbflYKJws6nGugHxdxg,21704 +ray/dashboard/client/build/static/media/roboto-latin-100italic.7f839a8652da29745ce4.woff2,sha256=JskepDt5sdRWaV3kaPUD4BQenrdn_hZNr4vz86EBJW8,17008 +ray/dashboard/client/build/static/media/roboto-latin-300.37a7069dc30fc663c878.woff2,sha256=KfbaCowhxWgVEbubCGY9P9LF0Jyb2AVOw1TFY7jIt8E,15784 +ray/dashboard/client/build/static/media/roboto-latin-300.865f928cbabcc9f8f2b5.woff,sha256=drBUAP_52ltDhi43EwmeORORamKVYCZe0ksZ0DEifL8,20348 +ray/dashboard/client/build/static/media/roboto-latin-300italic.bd5b7a13f2c52b531a2a.woff,sha256=C-Cubv2FKzaVy3p2KGCW9g6Tt9McFuC3HKNeztf96PY,22204 +ray/dashboard/client/build/static/media/roboto-latin-300italic.c64e7e354c88e613c77c.woff2,sha256=ngJSTr7NgT_EvLQDNrsrAzhxsf3L0jQine5BidxEhQ0,17448 +ray/dashboard/client/build/static/media/roboto-latin-400.176f8f5bd5f02b3abfcf.woff2,sha256=SMP6b4bFTx2btRkiBxPUsKH4zRpYmjwDufqC6Y7LE-M,15736 +ray/dashboard/client/build/static/media/roboto-latin-400.49ae34d4cc6b98c00c69.woff,sha256=wdyH-Zx_8iiAYRfVjwhcbFcwV_ojcigIGAK32NPPdoQ,20268 +ray/dashboard/client/build/static/media/roboto-latin-400italic.b1d9d9904bfca8802a63.woff,sha256=gIFe_jvZMXxmbfDy5tcBM14XiVT2TrHpkQP-qBwqoTc,21952 +ray/dashboard/client/build/static/media/roboto-latin-400italic.d022bc70dc1bf7b3425d.woff2,sha256=QB5sJYAbotWXldBabdlz-VVmtBBw05ObqTB9ZYYK5Q4,17324 +ray/dashboard/client/build/static/media/roboto-latin-500.cea99d3e3e13a3a599a0.woff,sha256=upj5kdACxr-q97h0ZS_9zekmGoaSXbh98-0oYeoICt8,20464 +ray/dashboard/client/build/static/media/roboto-latin-500.f5b74d7ffcdf85b9dd60.woff2,sha256=JDaeGyRhr53O_sr5zJPWTPIqTFusMlBhALniEBRQe88,15872 +ray/dashboard/client/build/static/media/roboto-latin-500italic.0d8bb5b3ee5f5dac9e44.woff2,sha256=hoi2IEJzjro56Lwu34augykF6O4yQbVYNVJkZdnrjhs,17316 +ray/dashboard/client/build/static/media/roboto-latin-500italic.18d00f739ff1e1c52db1.woff,sha256=byl0o5bcBpXQcehCVR56-ccvDvjS0Hb-c6UjsaPC0Oc,22020 +ray/dashboard/client/build/static/media/roboto-latin-700.2267169ee7270a22a963.woff,sha256=gG6kbEJq-Pwk5c9CohAihzlpaTPTYpnrKK7mT2n8cfE,20356 +ray/dashboard/client/build/static/media/roboto-latin-700.c18ee39fb002ad58b6dc.woff2,sha256=tNB4ks3nFdULtpwZgt9JY4XR39j50YZ8MfGaPIY0z64,15816 +ray/dashboard/client/build/static/media/roboto-latin-700italic.7d8125ff7f707231fd89.woff2,sha256=XMLkdwHufcnguhYwPhcNsPyy3ymJt3Y6xwWJPTe04jc,17020 +ray/dashboard/client/build/static/media/roboto-latin-700italic.9360531f9bb817f917f0.woff,sha256=7sFCYI6LQX4qy25TAadQBHoE4sWmVjIjyq5JnhnqCO4,21588 +ray/dashboard/client/build/static/media/roboto-latin-900.870c8c1486f76054301a.woff2,sha256=7c3z9gJSpZh77cnIa1Qi2XK6UJu75g1YklMQx0SjPig,15712 +ray/dashboard/client/build/static/media/roboto-latin-900.bac8362e7a6ea60b6983.woff,sha256=6FhvnbfAUDqYTJRK0vH3g79gUa6ioGa8If3tyP5_poo,20392 +ray/dashboard/client/build/static/media/roboto-latin-900italic.c20d916c1a1b094c1cec.woff,sha256=aoDZy09JtZUbQH-JBc-oh_Hj8uLsQ2m_WOrGM7LgWUg,22304 +ray/dashboard/client/build/static/media/roboto-latin-900italic.cb5ad999740e9d8a8bd1.woff2,sha256=leYLk5GTHcTVzJs7DCiw6ydRuQZgN_dZS6dsR3OcGHs,17520 +ray/dashboard/consts.py,sha256=Tz7F-VjCYlsPuvkAhusP_KB6ZJ1mGbNIqi_25dQW2S8,4571 +ray/dashboard/dashboard.py,sha256=_pe0bJHRvdLH6i3e1kMScWXFK21mw7kZ7IEBxA6r5gE,10755 +ray/dashboard/dashboard_metrics.py,sha256=ZW162Ylx1W98i6NtykfnVE1D2TlB_rZQCQS9H92E0XI,4047 +ray/dashboard/head.py,sha256=lnmFxNEq0pGUyxW_u92pFDZmg2nO-yF9RDJYjAh15Wk,18787 +ray/dashboard/http_server_agent.py,sha256=ZFfaomMRzmuGkNpsYVeOWtB-1_utoPETAUyI9zsaWDo,4887 +ray/dashboard/http_server_head.py,sha256=AQeG8LlaAmc7qUuLElb2gQAkaB0LeBpnxaJ51y-DVOk,17715 +ray/dashboard/k8s_utils.py,sha256=iWQ_Y_KDM1iP-e6dmRaCR8Vf8qm_ct0C_ashuEhIR68,3928 +ray/dashboard/memory_utils.py,sha256=ZdpmVVs7WME0-_U6N3FNmIbkyGn71-qanrGu87jClYo,18614 +ray/dashboard/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/__pycache__/dashboard_sdk.cpython-312.pyc,, +ray/dashboard/modules/__pycache__/version.cpython-312.pyc,, +ray/dashboard/modules/aggregator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/aggregator/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/aggregator/__pycache__/aggregator_agent.cpython-312.pyc,, +ray/dashboard/modules/aggregator/__pycache__/constants.cpython-312.pyc,, +ray/dashboard/modules/aggregator/__pycache__/multi_consumer_event_buffer.cpython-312.pyc,, +ray/dashboard/modules/aggregator/aggregator_agent.py,sha256=lwQN4YtbariujH_fqQvMl7Hvj96cjPzZn1B1skEZt-w,8878 +ray/dashboard/modules/aggregator/constants.py,sha256=Bv5m9d_c5FQ36v0tHrXImyoWPFYt1Pwb2iTw5_YvWOw,82 +ray/dashboard/modules/aggregator/multi_consumer_event_buffer.py,sha256=6YfsISbn3T1iYdfVhqeZXsY2XO5qznXWM1nrpqANk_E,7796 +ray/dashboard/modules/aggregator/publisher/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/aggregator/publisher/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/aggregator/publisher/__pycache__/async_publisher_client.cpython-312.pyc,, +ray/dashboard/modules/aggregator/publisher/__pycache__/configs.cpython-312.pyc,, +ray/dashboard/modules/aggregator/publisher/__pycache__/metrics.cpython-312.pyc,, +ray/dashboard/modules/aggregator/publisher/__pycache__/ray_event_publisher.cpython-312.pyc,, +ray/dashboard/modules/aggregator/publisher/async_publisher_client.py,sha256=sPXaAkWotaHvZLdz9EtgyTPqrZG6YCvy0AtEBWC7L4Q,4820 +ray/dashboard/modules/aggregator/publisher/configs.py,sha256=tLUhKQ0vB9X13YzAsb0881YRwcIrf8UaWE8UzGGNBJQ,1287 +ray/dashboard/modules/aggregator/publisher/metrics.py,sha256=czbvpIs6uogbnwinW0IauhO6k3rjjL8DzFNSZIZHOgM,1838 +ray/dashboard/modules/aggregator/publisher/ray_event_publisher.py,sha256=ISw-x6rNoOlMhF7UrJcvXhQwar-2AB2vBnm_1bUwx0w,10506 +ray/dashboard/modules/dashboard_sdk.py,sha256=LWdpmkhOC7PNgBBwNeExG8nXAjePdH_d9DKhsHP__E4,15009 +ray/dashboard/modules/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/data/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/data/__pycache__/data_head.cpython-312.pyc,, +ray/dashboard/modules/data/data_head.py,sha256=LzDsvoOSCKbRnN4R4k_tGgYaFltYBZ2rGDboP0qJtnI,6633 +ray/dashboard/modules/event/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/event/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/event/__pycache__/event_agent.cpython-312.pyc,, +ray/dashboard/modules/event/__pycache__/event_consts.cpython-312.pyc,, +ray/dashboard/modules/event/__pycache__/event_head.cpython-312.pyc,, +ray/dashboard/modules/event/__pycache__/event_utils.cpython-312.pyc,, +ray/dashboard/modules/event/event_agent.py,sha256=RZ6AO4nmYvJe3VXUrQEYCZnjZvpaLIN0xh0PppoabRU,5042 +ray/dashboard/modules/event/event_consts.py,sha256=1EV8DXG78W10rjUYUqik8Kj4STj0VeZs-IvVGnwouEc,700 +ray/dashboard/modules/event/event_head.py,sha256=XA4akOivGR2L62pee65ctY4rknOS5dKvDE4XDxsapcQ,8767 +ray/dashboard/modules/event/event_utils.py,sha256=M0gT8evdvabUkeZavv0NMvELtYbA8eR5Ka2ly6njJbM,7500 +ray/dashboard/modules/job/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/job/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/cli.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/cli_utils.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/common.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/job_agent.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/job_head.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/job_log_storage_client.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/job_manager.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/job_supervisor.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/pydantic_models.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/sdk.cpython-312.pyc,, +ray/dashboard/modules/job/__pycache__/utils.cpython-312.pyc,, +ray/dashboard/modules/job/cli.py,sha256=rhMqfgep1KAb941sib-3C0EmyOdR0MnZgcaiw3aqz1w,15959 +ray/dashboard/modules/job/cli_utils.py,sha256=nqq_R36FiOCX_2guPGlDEo29C2SR6ZRjrU8hVF81Wn0,1462 +ray/dashboard/modules/job/common.py,sha256=SHtKAEVSmC09jK6ajCQ_UrqXHDW9mmI5qsWtsMJW3Uo,22518 +ray/dashboard/modules/job/job_agent.py,sha256=9dgbfM2lr_yvcNKbH-AOEH0gzVO5C3qMzADzOgIqn98,7832 +ray/dashboard/modules/job/job_head.py,sha256=v593v5HAVmsxl2kgGJIuupiDok-yqLnLWfYvtLTFQsA,28729 +ray/dashboard/modules/job/job_log_storage_client.py,sha256=gyyLtWNgYcXpYXBuf6KphEQU2s1Xp2YPSITn-ts5B_E,2079 +ray/dashboard/modules/job/job_manager.py,sha256=5OKCkiPOCNxItkQb66ti0Q2arYmy-vOgVIhSnORBBnA,29416 +ray/dashboard/modules/job/job_supervisor.py,sha256=PJlLFou4LI3ax4kkzYo7CTwxxMVMyqfHpUaFnQhH1w0,20874 +ray/dashboard/modules/job/pydantic_models.py,sha256=DvJGALl8d3wio4cIRNCQYkB1S6XykiWvok5ElVuvO90,4325 +ray/dashboard/modules/job/sdk.py,sha256=_LOuos6WbnrQUS_xSz-5x_4QJk05ux3lfeubAJH3-lM,21366 +ray/dashboard/modules/job/utils.py,sha256=mD-aFL6O85949QyCJcuRheiG_aWIndBFYsIIgH_RGrs,10051 +ray/dashboard/modules/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/log/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/log/__pycache__/log_agent.cpython-312.pyc,, +ray/dashboard/modules/log/__pycache__/log_consts.cpython-312.pyc,, +ray/dashboard/modules/log/__pycache__/log_manager.cpython-312.pyc,, +ray/dashboard/modules/log/__pycache__/log_utils.cpython-312.pyc,, +ray/dashboard/modules/log/log_agent.py,sha256=2glBjff-jrGZx-0hMlw10oLmuLYLFgJUqKS73w9iLeM,14040 +ray/dashboard/modules/log/log_consts.py,sha256=ppK99FPwkRHMMj0Ss2dhI_527lH4yoVo81M60R2rZSY,129 +ray/dashboard/modules/log/log_manager.py,sha256=Gda8jXFUNJSiqCZqk8wsQ9hh8uS3JL-uql8d6J0ccQ8,17162 +ray/dashboard/modules/log/log_utils.py,sha256=IRpef6B8H86jBPaGieD8hTN8_YWW6xKFe-NjJOGss6o,238 +ray/dashboard/modules/metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/metrics/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/metrics/__pycache__/grafana_dashboard_factory.cpython-312.pyc,, +ray/dashboard/modules/metrics/__pycache__/install_and_start_prometheus.cpython-312.pyc,, +ray/dashboard/modules/metrics/__pycache__/metrics_head.cpython-312.pyc,, +ray/dashboard/modules/metrics/__pycache__/templates.cpython-312.pyc,, +ray/dashboard/modules/metrics/dashboards/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/metrics/dashboards/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/metrics/dashboards/__pycache__/common.cpython-312.pyc,, +ray/dashboard/modules/metrics/dashboards/__pycache__/data_dashboard_panels.cpython-312.pyc,, +ray/dashboard/modules/metrics/dashboards/__pycache__/default_dashboard_panels.cpython-312.pyc,, +ray/dashboard/modules/metrics/dashboards/__pycache__/serve_dashboard_panels.cpython-312.pyc,, +ray/dashboard/modules/metrics/dashboards/__pycache__/serve_deployment_dashboard_panels.cpython-312.pyc,, +ray/dashboard/modules/metrics/dashboards/__pycache__/serve_llm_dashboard_panels.cpython-312.pyc,, +ray/dashboard/modules/metrics/dashboards/__pycache__/train_dashboard_panels.cpython-312.pyc,, +ray/dashboard/modules/metrics/dashboards/common.py,sha256=ZWid_YBLzEx-QItW6J4o0aCUfzrPP6CB30muZ2WoVfA,14091 +ray/dashboard/modules/metrics/dashboards/data_dashboard_panels.py,sha256=_7fnEn-VF7eI7AaJuiE-9v_BATrmaCVTAMIWFgXCR8M,36787 +ray/dashboard/modules/metrics/dashboards/data_grafana_dashboard_base.json,sha256=63xRCEGtgSzWkPc8rDwV-H5d9WokxcL88T995ab2x_E,5890 +ray/dashboard/modules/metrics/dashboards/default_dashboard_panels.py,sha256=lTdHZ8iuTqkJivQI0SycV4yngK-zcvdM513wFG6lPMI,31761 +ray/dashboard/modules/metrics/dashboards/default_grafana_dashboard_base.json,sha256=nvOlT7SJKOEpup5JOslfaOirORD3g5MFbHSIjEXvJWM,4855 +ray/dashboard/modules/metrics/dashboards/serve_dashboard_panels.py,sha256=F3D76g8kMolBkBEvvhQTgooAz8C_Dvp_JLQq0W4wa9Y,16653 +ray/dashboard/modules/metrics/dashboards/serve_deployment_dashboard_panels.py,sha256=zW_5QVryJcFOmXK6TgI_JICxnaeHboK0fnL0_ltqFuo,9388 +ray/dashboard/modules/metrics/dashboards/serve_deployment_grafana_dashboard_base.json,sha256=Mynl8dIpN8KlHeym0sq4zcGWWpn57zyqbomU9LIn7SI,6084 +ray/dashboard/modules/metrics/dashboards/serve_grafana_dashboard_base.json,sha256=Zfh3aSexcq5C3f9Es1Q0PQfecev9t_YHxAERchkdz_k,4940 +ray/dashboard/modules/metrics/dashboards/serve_llm_dashboard_panels.py,sha256=q3bAIwRMTPyvuntHIzOC8N56ITbBEKFfDmBiW1g7qkY,24135 +ray/dashboard/modules/metrics/dashboards/serve_llm_grafana_dashboard_base.json,sha256=nPXhi-YV5d4UF9bNafbCQWtCv3OO3DEk_gzazVdfV7k,4140 +ray/dashboard/modules/metrics/dashboards/train_dashboard_panels.py,sha256=Ffn0Zp4wxs2y089ltCVihbWhe1RbfxHiTPI5WTNHZO4,11548 +ray/dashboard/modules/metrics/dashboards/train_grafana_dashboard_base.json,sha256=nQAX42XTqukDqoDgFNK7P-2Rs46Bgo6HNLiO3CPphYI,7590 +ray/dashboard/modules/metrics/export/prometheus/prometheus.yml,sha256=OHUFwiB6QQGgsEukeC0WYEVeCvtqNDNrM-whkgmnzJM,476 +ray/dashboard/modules/metrics/grafana_dashboard_factory.py,sha256=H_4aDtFFWKEHGMluVCEwBLEKao9__MDt_q-ek8IupGQ,11107 +ray/dashboard/modules/metrics/install_and_start_prometheus.py,sha256=dA-PRXV4H3--wEZEgWBELVK1-0zWQI7V0_kGfUZspWU,6400 +ray/dashboard/modules/metrics/metrics_head.py,sha256=dbn-9CmWg3exitoo8l5IKF1q09wC4nJejniUuaTyfNA,16221 +ray/dashboard/modules/metrics/templates.py,sha256=2cLRGIynKXTsXflXzV3N9rEBlYZ6Q4WYZRRUOIneEoY,1494 +ray/dashboard/modules/node/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/node/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/node/__pycache__/actor_consts.cpython-312.pyc,, +ray/dashboard/modules/node/__pycache__/datacenter.cpython-312.pyc,, +ray/dashboard/modules/node/__pycache__/node_consts.cpython-312.pyc,, +ray/dashboard/modules/node/__pycache__/node_head.cpython-312.pyc,, +ray/dashboard/modules/node/actor_consts.py,sha256=-bkzQjaKE_nt1_sN9CpzgHN_wT-f5nmRGMHFcmfO0Mg,95 +ray/dashboard/modules/node/datacenter.py,sha256=HLreSUaUTI-qbuUafFeE9cmTlxgef1XPrxocjByv6mQ,9137 +ray/dashboard/modules/node/node_consts.py,sha256=wVB6RpZXdLONV0cNeAkvk90vdo8JHYTqfTda5Qfh9DU,636 +ray/dashboard/modules/node/node_head.py,sha256=UfQ23FKyT18C5wmd7fKOKnbUH7RazlNRwZMsSzC7yeU,30986 +ray/dashboard/modules/reporter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/reporter/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/reporter/__pycache__/gpu_profile_manager.cpython-312.pyc,, +ray/dashboard/modules/reporter/__pycache__/gpu_providers.cpython-312.pyc,, +ray/dashboard/modules/reporter/__pycache__/healthz_agent.cpython-312.pyc,, +ray/dashboard/modules/reporter/__pycache__/profile_manager.cpython-312.pyc,, +ray/dashboard/modules/reporter/__pycache__/reporter_agent.cpython-312.pyc,, +ray/dashboard/modules/reporter/__pycache__/reporter_consts.cpython-312.pyc,, +ray/dashboard/modules/reporter/__pycache__/reporter_head.cpython-312.pyc,, +ray/dashboard/modules/reporter/__pycache__/reporter_models.cpython-312.pyc,, +ray/dashboard/modules/reporter/__pycache__/utils.cpython-312.pyc,, +ray/dashboard/modules/reporter/gpu_profile_manager.py,sha256=PydpGXGrrCgQbwK484Q15EbYISgeVBAI51vu6QUCgWM,11956 +ray/dashboard/modules/reporter/gpu_providers.py,sha256=V57NGiLfP9QVgvl4tI2RhAG3heNiD9q5o_A3TifGi-o,20265 +ray/dashboard/modules/reporter/healthz_agent.py,sha256=ZNg3RChINdMJqJF4vOqrJ9g6bUZNZI_TvMLPpd-AwzU,2069 +ray/dashboard/modules/reporter/profile_manager.py,sha256=xm6ixWjImVGAwdNmwsSR2N5tF-OqSLVNV3oH_huvmJw,12578 +ray/dashboard/modules/reporter/reporter_agent.py,sha256=Xr2tArEtXfMfF-AuI5wIRH4s3IVWrYlcAFlMPUXWTMM,66535 +ray/dashboard/modules/reporter/reporter_consts.py,sha256=8E9gYcn59Pga7D2TO_wZKEfk4W8-o5uSDKQVJ8cywTM,254 +ray/dashboard/modules/reporter/reporter_head.py,sha256=L4Rlr78zO0I6PMp7Z8RsOKuUTW98VnZdwHVCZfQB6Ag,37350 +ray/dashboard/modules/reporter/reporter_models.py,sha256=lnCR45sSYoX-5PgP-D5cA9FHLhseXMsCn1r7obtXzts,7456 +ray/dashboard/modules/reporter/utils.py,sha256=vzORvld2-zlEqGpS59NBLKxPlDijzYrI4HJkssFlves,628 +ray/dashboard/modules/serve/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/serve/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/serve/__pycache__/sdk.cpython-312.pyc,, +ray/dashboard/modules/serve/__pycache__/serve_head.cpython-312.pyc,, +ray/dashboard/modules/serve/sdk.py,sha256=PObjPjxiA98VRymiErkcDeZgQNuidzGtOH7LosSXaUU,2916 +ray/dashboard/modules/serve/serve_head.py,sha256=OD_NQX7mKJ9W7MyKM6ovubapWehCkDIzB21m3OIO_4g,12582 +ray/dashboard/modules/state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/state/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/state/__pycache__/state_head.cpython-312.pyc,, +ray/dashboard/modules/state/state_head.py,sha256=VCyUDB3bV1GWgQhBX00dTMiz4mjYEoVrSl7d-J42tac,14729 +ray/dashboard/modules/train/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/train/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/train/__pycache__/train_head.cpython-312.pyc,, +ray/dashboard/modules/train/train_head.py,sha256=2U0Nwf6h-w_Uu6uHmpeLnSeF4qAm59Ibd08Bxdj4Ldg,19055 +ray/dashboard/modules/usage_stats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/modules/usage_stats/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/modules/usage_stats/__pycache__/usage_stats_head.cpython-312.pyc,, +ray/dashboard/modules/usage_stats/usage_stats_head.py,sha256=w5arE6jjasys2GR9cUuoEC49YjKv_yFyOtzXEYUTvo8,7996 +ray/dashboard/modules/version.py,sha256=McYMc3HBcNn5HSuq-uuVZHWuxxGZtq2eX7B_UFHONhI,630 +ray/dashboard/optional_deps.py,sha256=4JFlg6csFAZKGNlNFkSFeeXDCrRsEdldWPDM4jrm3t8,1014 +ray/dashboard/optional_utils.py,sha256=hu5LmXnVT1ofeBg3x6yE77-fzfUoogP5TVlWTjjXxYI,8187 +ray/dashboard/routes.py,sha256=Hrx2MakTfEG1Hdy_cMU1DFph0ApumMrqK_ajcA0Nv4Y,6670 +ray/dashboard/state_aggregator.py,sha256=gNZUR-GV1Q3UeQM4OXoz-BHQtcI8YI0MaIZircc-7JQ,26468 +ray/dashboard/state_api_utils.py,sha256=ANRPJ_-ELc5kdHTM6EhXdU8ythCUMJzmSUJFx_ZIOq8,9758 +ray/dashboard/subprocesses/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/dashboard/subprocesses/__pycache__/__init__.cpython-312.pyc,, +ray/dashboard/subprocesses/__pycache__/handle.cpython-312.pyc,, +ray/dashboard/subprocesses/__pycache__/module.cpython-312.pyc,, +ray/dashboard/subprocesses/__pycache__/routes.cpython-312.pyc,, +ray/dashboard/subprocesses/__pycache__/utils.cpython-312.pyc,, +ray/dashboard/subprocesses/handle.py,sha256=ncYBhzh-7y-hQuyYadvL1-bDV7AokeDPJdUOK8VN_VQ,12535 +ray/dashboard/subprocesses/module.py,sha256=6JuJw8qSsaNFYZ81mV3SxesoKh0-eOdPZ1z3MND2Cro,9187 +ray/dashboard/subprocesses/routes.py,sha256=jd67wPCEBK8uFX8lVb97SwTHaGEMwrGaaXgKZP6G-6A,3758 +ray/dashboard/subprocesses/utils.py,sha256=1bSDXhq1QfW0_q1o5wW72PUDN2xw8L7Bhw8RNcVwaDQ,1843 +ray/dashboard/timezone_utils.py,sha256=Y-JaOJUvwmQxPsRBeQunSDDClUN5_koGJlwwwP4WVk0,2352 +ray/dashboard/utils.py,sha256=VOcpZFwNdo3lt8O2qB9GQLs51V0p1dRKMcn29YoJWG4,23645 +ray/data/__init__.py,sha256=gPd8E7kYs0Ia5_QMM4e6FvVNNVdNczs70EZVCuWmySI,4587 +ray/data/__pycache__/__init__.cpython-312.pyc,, +ray/data/__pycache__/aggregate.cpython-312.pyc,, +ray/data/__pycache__/block.cpython-312.pyc,, +ray/data/__pycache__/collate_fn.cpython-312.pyc,, +ray/data/__pycache__/context.cpython-312.pyc,, +ray/data/__pycache__/dataset.cpython-312.pyc,, +ray/data/__pycache__/datatype.cpython-312.pyc,, +ray/data/__pycache__/exceptions.cpython-312.pyc,, +ray/data/__pycache__/expressions.cpython-312.pyc,, +ray/data/__pycache__/grouped_data.cpython-312.pyc,, +ray/data/__pycache__/iterator.cpython-312.pyc,, +ray/data/__pycache__/llm.cpython-312.pyc,, +ray/data/__pycache__/preprocessor.cpython-312.pyc,, +ray/data/__pycache__/random_access_dataset.cpython-312.pyc,, +ray/data/__pycache__/read_api.cpython-312.pyc,, +ray/data/__pycache__/stats.cpython-312.pyc,, +ray/data/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/_internal/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/__pycache__/aggregate.cpython-312.pyc,, +ray/data/_internal/__pycache__/arrow_block.cpython-312.pyc,, +ray/data/_internal/__pycache__/batcher.cpython-312.pyc,, +ray/data/_internal/__pycache__/block_builder.cpython-312.pyc,, +ray/data/_internal/__pycache__/block_list.cpython-312.pyc,, +ray/data/_internal/__pycache__/collections.cpython-312.pyc,, +ray/data/_internal/__pycache__/compute.cpython-312.pyc,, +ray/data/_internal/__pycache__/delegating_block_builder.cpython-312.pyc,, +ray/data/_internal/__pycache__/equalize.cpython-312.pyc,, +ray/data/_internal/__pycache__/logging.cpython-312.pyc,, +ray/data/_internal/__pycache__/memory_tracing.cpython-312.pyc,, +ray/data/_internal/__pycache__/metadata_exporter.cpython-312.pyc,, +ray/data/_internal/__pycache__/numpy_support.cpython-312.pyc,, +ray/data/_internal/__pycache__/operator_event_exporter.cpython-312.pyc,, +ray/data/_internal/__pycache__/output_buffer.cpython-312.pyc,, +ray/data/_internal/__pycache__/pandas_block.cpython-312.pyc,, +ray/data/_internal/__pycache__/plan.cpython-312.pyc,, +ray/data/_internal/__pycache__/progress_bar.cpython-312.pyc,, +ray/data/_internal/__pycache__/remote_fn.cpython-312.pyc,, +ray/data/_internal/__pycache__/row.cpython-312.pyc,, +ray/data/_internal/__pycache__/savemode.cpython-312.pyc,, +ray/data/_internal/__pycache__/size_estimator.cpython-312.pyc,, +ray/data/_internal/__pycache__/split.cpython-312.pyc,, +ray/data/_internal/__pycache__/stats.cpython-312.pyc,, +ray/data/_internal/__pycache__/streaming_repartition.cpython-312.pyc,, +ray/data/_internal/__pycache__/table_block.cpython-312.pyc,, +ray/data/_internal/__pycache__/torch_iterable_dataset.cpython-312.pyc,, +ray/data/_internal/__pycache__/util.cpython-312.pyc,, +ray/data/_internal/actor_autoscaler/__init__.py,sha256=WJ5XqGK1AWQL_Pir8_jenJm8VGBOPY1MWvJx6eSDf54,845 +ray/data/_internal/actor_autoscaler/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/actor_autoscaler/__pycache__/autoscaling_actor_pool.cpython-312.pyc,, +ray/data/_internal/actor_autoscaler/__pycache__/base_actor_autoscaler.cpython-312.pyc,, +ray/data/_internal/actor_autoscaler/__pycache__/default_actor_autoscaler.cpython-312.pyc,, +ray/data/_internal/actor_autoscaler/autoscaling_actor_pool.py,sha256=pHeG7KRaoqb56jdO6oudpzdUhR1G1nNWx8nJw3k3aR4,3305 +ray/data/_internal/actor_autoscaler/base_actor_autoscaler.py,sha256=RwMVmBgW7d5daoA9-xKye09uRILPc9vvHM4djADAk_I,918 +ray/data/_internal/actor_autoscaler/default_actor_autoscaler.py,sha256=qEsfUQa7kPR5cAM6t-kWq1ur8RdsQZQg9EVoN7bEExw,7773 +ray/data/_internal/aggregate.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/_internal/arrow_block.py,sha256=4HaCVk5NmOyexshyNxOOTnSZq1dSMSSw4OhA2WRJV1w,19996 +ray/data/_internal/arrow_ops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/_internal/arrow_ops/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/arrow_ops/__pycache__/transform_polars.cpython-312.pyc,, +ray/data/_internal/arrow_ops/__pycache__/transform_pyarrow.cpython-312.pyc,, +ray/data/_internal/arrow_ops/transform_polars.py,sha256=7gdAVU_0j_xKWoqR7YDos8p_XwaaFuymVkTJifscd88,1819 +ray/data/_internal/arrow_ops/transform_pyarrow.py,sha256=vOgdZiBI2W_3zOCJdyqG7yJJui604Om1QvwDN3O7onk,38045 +ray/data/_internal/batcher.py,sha256=XbDJArxSI6mdwdkZ_PauC3aF7QmZnclfnVWAWtuEyUI,15391 +ray/data/_internal/block_batching/__init__.py,sha256=yh48Q33rYuRur6LwWmnEwHdr_86mbP3li_LLSE6zNGM,102 +ray/data/_internal/block_batching/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/block_batching/__pycache__/block_batching.cpython-312.pyc,, +ray/data/_internal/block_batching/__pycache__/interfaces.cpython-312.pyc,, +ray/data/_internal/block_batching/__pycache__/iter_batches.cpython-312.pyc,, +ray/data/_internal/block_batching/__pycache__/util.cpython-312.pyc,, +ray/data/_internal/block_batching/block_batching.py,sha256=tH6EEOzhvlBJ6rywC6w6QRfSKFHgph6IuhLSwDycIa8,1914 +ray/data/_internal/block_batching/interfaces.py,sha256=RT11Sw3O-kRJsUn7e81KRmxAlPxca7GUB5bl9SSMC-Y,1178 +ray/data/_internal/block_batching/iter_batches.py,sha256=UOwL4YAZDCxVnHC9rpcsDgPHJT2OA5HqJxcvM-H09eY,17239 +ray/data/_internal/block_batching/util.py,sha256=hEvV0eOzyGdZZuiKv6dNj1dKjlFlyLh7EaOlHtIOfkM,10633 +ray/data/_internal/block_builder.py,sha256=5R8yM1VjfBxY4f7MJhowPOSO-QEKiIAOfD9SZsyFyzI,1197 +ray/data/_internal/block_list.py,sha256=PfIB7yPzclU1UUcsrdwr6y1JOT2B-lxtGtztu38Aueg,4015 +ray/data/_internal/cluster_autoscaler/__init__.py,sha256=Ry99lBA88SyBi-mBBZsCJOtHcPgxyi73dRQCbyArHek,610 +ray/data/_internal/cluster_autoscaler/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/cluster_autoscaler/__pycache__/base_cluster_autoscaler.cpython-312.pyc,, +ray/data/_internal/cluster_autoscaler/__pycache__/default_cluster_autoscaler.cpython-312.pyc,, +ray/data/_internal/cluster_autoscaler/base_cluster_autoscaler.py,sha256=eS-hnimw7oOwuEVG7FKXJgNlTpiHHRF-e3ywElSqu-s,1399 +ray/data/_internal/cluster_autoscaler/default_cluster_autoscaler.py,sha256=q0DgO1qUJzHcoBn3nbY6x533DpTu1BIqcuwFR7_KlaM,4179 +ray/data/_internal/collections.py,sha256=XI1dx_x-jBZDPeuZx2-vRjY1mqsqjDZubvI53-BKVm0,1445 +ray/data/_internal/compute.py,sha256=BzFHFjr4ORruAEys3DgvEHNPE6Bfu15VK07yvVY2vuE,7306 +ray/data/_internal/datasource/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/_internal/datasource/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/audio_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/avro_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/bigquery_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/bigquery_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/binary_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/clickhouse_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/clickhouse_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/csv_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/csv_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/databricks_uc_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/delta_sharing_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/hudi_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/huggingface_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/iceberg_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/iceberg_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/image_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/image_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/json_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/json_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/lance_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/lance_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/mcap_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/mongo_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/mongo_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/numpy_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/numpy_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/parquet_bulk_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/parquet_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/parquet_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/range_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/sql_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/sql_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/text_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/tfrecords_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/tfrecords_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/torch_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/uc_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/video_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/webdataset_datasink.cpython-312.pyc,, +ray/data/_internal/datasource/__pycache__/webdataset_datasource.cpython-312.pyc,, +ray/data/_internal/datasource/audio_datasource.py,sha256=6a5sdqJZi1XZcyRxTAuB5reJejp0pRYIFQPqZMKPq-E,1574 +ray/data/_internal/datasource/avro_datasource.py,sha256=6uejfzw2xRqsily8N_acJx9eKvxYgK1DEZKgRSMYpM8,1484 +ray/data/_internal/datasource/bigquery_datasink.py,sha256=c7rq5R8_GUf9ty4PTlOZOZ4x9mDWSak7AqERPp6EgEQ,5113 +ray/data/_internal/datasource/bigquery_datasource.py,sha256=HVashOXTQ3-0kCHCJmnBpXMSXVDBYUpEJu63r1CpzDI,5024 +ray/data/_internal/datasource/binary_datasource.py,sha256=vFwIi_j9QQRseiYDcCHSfKOc_37qlMbKnnWcNwDUHK0,625 +ray/data/_internal/datasource/clickhouse_datasink.py,sha256=UrhFyGH1tanaU_ZjiwRAS231oBqk-WBO5srQF3VGEqs,17442 +ray/data/_internal/datasource/clickhouse_datasource.py,sha256=H7YShknbciQ9HkWm1P0I7Oz82Fb6PHvd2rHsvlP9LKQ,15045 +ray/data/_internal/datasource/csv_datasink.py,sha256=x2iHSSfIOm49wsZ1MtzRP35QMPEdu7x4qOscSZWD_3E,1234 +ray/data/_internal/datasource/csv_datasource.py,sha256=LPbmQk9E_NhnqQ4FYmSh3_aUe5Hdw1zOz_y8oQLERUc,2778 +ray/data/_internal/datasource/databricks_uc_datasource.py,sha256=7LG-AMksEwG8-uZjMcoH7-XGc92pp2zBDAec9g_z4VQ,7347 +ray/data/_internal/datasource/delta_sharing_datasource.py,sha256=Fh5kClDWuS20vsRxY9w8gabSqlj1FwpbMgWe8UYHKDA,4692 +ray/data/_internal/datasource/hudi_datasource.py,sha256=e6a_l1xwbMZB3DjGwHhFuNztC6_3Adebgo6gUZnqpuk,5730 +ray/data/_internal/datasource/huggingface_datasource.py,sha256=49k4JJBg6gFjnAm6QAfzBqSO9Aj9SD-QFThd_EBpxpU,8474 +ray/data/_internal/datasource/iceberg_datasink.py,sha256=pkD0V_ENj_2IYGu0GpLmFWgFnm90Bj-7vhZwGXUBNg8,19467 +ray/data/_internal/datasource/iceberg_datasource.py,sha256=4zC58Jnt7FYYuzJVk90FstOcmMOGcuDlxp5r34kybWI,18522 +ray/data/_internal/datasource/image_datasink.py,sha256=9RR3qBnEQEd_uwKmLWYTjGh5-H25LP4gTRxEspL-5KY,705 +ray/data/_internal/datasource/image_datasource.py,sha256=LB2V6LI1Hx-Kf0LZpGc8sS_gQjtxOXPftSVWmAmOaXg,6598 +ray/data/_internal/datasource/json_datasink.py,sha256=FhVN9is4Um-hjqnARGKOuy5rqOBYVxhqveHsZazLerM,1280 +ray/data/_internal/datasource/json_datasource.py,sha256=BLWZ5edNM8bWGT5N37vOc54_A0ggylEfRwP7kAgGWVM,10899 +ray/data/_internal/datasource/lance_datasink.py,sha256=uMRpy7UTO9000wuCpFLmGuy5KmaVzyyHJipjJj-Ms7Y,7305 +ray/data/_internal/datasource/lance_datasource.py,sha256=0Kjphw6Cs04UmvU6UbNLBQPmKkNUJWYjQezzmGz6GUU,4480 +ray/data/_internal/datasource/mcap_datasource.py,sha256=Cf3ArVoOhVKlYKdTn41hKVxfW_KKQPPH1WAfKfQG_dE,9923 +ray/data/_internal/datasource/mongo_datasink.py,sha256=yoejPDlM97KY__8cp0AQpA0Sfq0qHScxjhtC4gI74QM,1586 +ray/data/_internal/datasource/mongo_datasource.py,sha256=2lu_nxGwjr2x_y1WexFbkHL4aciAfXU8MG5fNMCfyb0,4780 +ray/data/_internal/datasource/numpy_datasink.py,sha256=RICLpCrT0secP-szSPSYuBry7hkTDVG055ybsHD2K8I,617 +ray/data/_internal/datasource/numpy_datasource.py,sha256=Jznq_rFfN56e4xjBeZNmZmP_X2aw87cygjkiOFpCfwA,1251 +ray/data/_internal/datasource/parquet_bulk_datasource.py,sha256=JCfE_u1gVZuSIp1zwaVrSs9zX6qRfSA5fYwUShkUL-g,1549 +ray/data/_internal/datasource/parquet_datasink.py,sha256=wovJ_I_nvHlnPjOdWVyjgBrALvvCUbdd4ZJFhAVV2fE,11563 +ray/data/_internal/datasource/parquet_datasource.py,sha256=6iIOv_ijL6X-FZLhLAU_SlB2NzmJkf_3YuziaX4XauQ,35316 +ray/data/_internal/datasource/range_datasource.py,sha256=8-NZf9K7LhLbgCm1oB2hIYIsbVZBUBL8qbAB9_LG7EQ,5124 +ray/data/_internal/datasource/sql_datasink.py,sha256=ZH9A4_cqgUKPANh-GQ6R5t-dU3Xp9O7pncJTwrXjSNA,1251 +ray/data/_internal/datasource/sql_datasource.py,sha256=qQta5fWIUKUDitfRNYqWZfSHO4b5w7mNL8wq7Ew1R2Y,7187 +ray/data/_internal/datasource/text_datasource.py,sha256=xiht9OJRukt8yLNxqyymEyoSdBexgAJzhLpK7RLtJc8,1198 +ray/data/_internal/datasource/tfrecords_datasink.py,sha256=ITi8Oh21E2hlaE4hMWWhwqF9kCm0ejaktsjH7hBi26s,7860 +ray/data/_internal/datasource/tfrecords_datasource.py,sha256=CtzAy6fuoOQGb8i7XgxnCx-r687sKMnzImT6peIUi2c,16653 +ray/data/_internal/datasource/torch_datasource.py,sha256=0bZmYXgziQD40u67QT5t_MUVetzkGOAR-yQORSw9lhA,2199 +ray/data/_internal/datasource/uc_datasource.py,sha256=ndRMgP-D_xe6bGsVvmXoviPQKFIJT5jrWDKZEwvynTU,7082 +ray/data/_internal/datasource/video_datasource.py,sha256=HDDG6aOim-9Bn13Xh2UmT3Q_Xa3s8frEqkyS4wRM2CU,1856 +ray/data/_internal/datasource/webdataset_datasink.py,sha256=8cqVTLedeKBCPi6bNFXBDwTeWkuBvyGJTPSXmXBtl9c,1793 +ray/data/_internal/datasource/webdataset_datasource.py,sha256=7o76rnTNkYbd9aEhSjldldWFaHnczFiaONoCOPExb_k,12562 +ray/data/_internal/delegating_block_builder.py,sha256=-S-YEiwzGYdLDQmIBaN4BDN9PkE1PsZZfdgUNdDhE70,2696 +ray/data/_internal/equalize.py,sha256=8hMB3F8XnId5Lk2b6TvBBbJqvavoCx04KApqv_fbxyk,5492 +ray/data/_internal/execution/__init__.py,sha256=8fmWNoC53V87-Mggnrg5wi9L4Oh8dSqojDB877oJGRA,177 +ray/data/_internal/execution/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/execution/__pycache__/autoscaling_requester.cpython-312.pyc,, +ray/data/_internal/execution/__pycache__/dataset_state.cpython-312.pyc,, +ray/data/_internal/execution/__pycache__/execution_callback.cpython-312.pyc,, +ray/data/_internal/execution/__pycache__/legacy_compat.cpython-312.pyc,, +ray/data/_internal/execution/__pycache__/progress_manager.cpython-312.pyc,, +ray/data/_internal/execution/__pycache__/ranker.cpython-312.pyc,, +ray/data/_internal/execution/__pycache__/resource_manager.cpython-312.pyc,, +ray/data/_internal/execution/__pycache__/streaming_executor.cpython-312.pyc,, +ray/data/_internal/execution/__pycache__/streaming_executor_state.cpython-312.pyc,, +ray/data/_internal/execution/__pycache__/util.cpython-312.pyc,, +ray/data/_internal/execution/autoscaling_requester.py,sha256=4ouvfcU5epMVWpgWAZErsj8k_-DBj55D2TCmK7dotSM,5152 +ray/data/_internal/execution/backpressure_policy/__init__.py,sha256=8lGpZ0YzuGoY8XexjK7M4WpLNHl0CMf55j2H67yksMM,1488 +ray/data/_internal/execution/backpressure_policy/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/execution/backpressure_policy/__pycache__/backpressure_policy.cpython-312.pyc,, +ray/data/_internal/execution/backpressure_policy/__pycache__/concurrency_cap_backpressure_policy.cpython-312.pyc,, +ray/data/_internal/execution/backpressure_policy/__pycache__/downstream_capacity_backpressure_policy.cpython-312.pyc,, +ray/data/_internal/execution/backpressure_policy/__pycache__/resource_budget_backpressure_policy.cpython-312.pyc,, +ray/data/_internal/execution/backpressure_policy/backpressure_policy.py,sha256=9oCHgZi8iOW6iQTtEQ5WIyuvWA9aIuDRrEv6ZgPIwog,2190 +ray/data/_internal/execution/backpressure_policy/concurrency_cap_backpressure_policy.py,sha256=Z0-58przhAtgOVqGww_fTQGaVOslKRof1rybIOeqDyE,9906 +ray/data/_internal/execution/backpressure_policy/downstream_capacity_backpressure_policy.py,sha256=uCK7ToQ4XY8EBwbkoU4mVP-GHwKYdusf-wmt7icbdkE,3620 +ray/data/_internal/execution/backpressure_policy/resource_budget_backpressure_policy.py,sha256=CITiZUILklsa8wKPXOfp2yaVPU6laeFr02jHNKpGDYQ,1056 +ray/data/_internal/execution/bundle_queue/__init__.py,sha256=-5iP8h7pXGVIj56-Vpm7fGkKd5zFl1AhFX8YfuaNIdM,209 +ray/data/_internal/execution/bundle_queue/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/execution/bundle_queue/__pycache__/bundle_queue.cpython-312.pyc,, +ray/data/_internal/execution/bundle_queue/__pycache__/fifo_bundle_queue.cpython-312.pyc,, +ray/data/_internal/execution/bundle_queue/bundle_queue.py,sha256=4M2hzSItkc7whXGzSOTLjelsLVHWMWTky9wjA-KwCN0,1883 +ray/data/_internal/execution/bundle_queue/fifo_bundle_queue.py,sha256=3M0HaLiI98_-rDdmkR0H8YCKkF7rwXpucbx0sfVfPZg,4610 +ray/data/_internal/execution/callbacks/__init__.py,sha256=DnNfyZUYFpMGqO3Cd1Z0-uALKT9McSjmZNt1f02h0-U,169 +ray/data/_internal/execution/callbacks/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/execution/callbacks/__pycache__/insert_issue_detectors.cpython-312.pyc,, +ray/data/_internal/execution/callbacks/insert_issue_detectors.py,sha256=dYAJY8ny5D0DWaN4fQgH3VU7yM5MXqcEguDFQ9bAkS4,804 +ray/data/_internal/execution/dataset_state.py,sha256=XYs0yxhAngiClQq2YwcJr_lVHfJfuKD9VgjeXSOEaPU,478 +ray/data/_internal/execution/execution_callback.py,sha256=PAxfXziL9_1q8XG96G97VZNtZ5rGz4o39KQgixPKA9I,3198 +ray/data/_internal/execution/interfaces/__init__.py,sha256=l9Bz87qiHfmwYFijOqz1Bi8HnlxzczJWiU26icEUMjQ,608 +ray/data/_internal/execution/interfaces/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/execution/interfaces/__pycache__/common.cpython-312.pyc,, +ray/data/_internal/execution/interfaces/__pycache__/execution_options.cpython-312.pyc,, +ray/data/_internal/execution/interfaces/__pycache__/executor.cpython-312.pyc,, +ray/data/_internal/execution/interfaces/__pycache__/op_runtime_metrics.cpython-312.pyc,, +ray/data/_internal/execution/interfaces/__pycache__/physical_operator.cpython-312.pyc,, +ray/data/_internal/execution/interfaces/__pycache__/ref_bundle.cpython-312.pyc,, +ray/data/_internal/execution/interfaces/__pycache__/task_context.cpython-312.pyc,, +ray/data/_internal/execution/interfaces/__pycache__/transform_fn.cpython-312.pyc,, +ray/data/_internal/execution/interfaces/common.py,sha256=Y823QKako8kA06diBbqOxP_X3KXLAx3XQYgJWPdKBjM,5582 +ray/data/_internal/execution/interfaces/execution_options.py,sha256=E9SG58vPnh-EwSxfDF-4xtvN9KhezHt2kLwxisDr2Ks,13587 +ray/data/_internal/execution/interfaces/executor.py,sha256=YbMGuizvRttiM_gqA_FB4emZhrfZOuY97n0FZL5nbFY,3373 +ray/data/_internal/execution/interfaces/op_runtime_metrics.py,sha256=IAFqgFWZ0UVyBHu9fi0Xg22bf-J4dnLNZtQJpsVdjZ4,37327 +ray/data/_internal/execution/interfaces/physical_operator.py,sha256=laHAmxSH2JByqpdQMw6CoPvb6HUbZTU7j8rvkIcBUwY,34141 +ray/data/_internal/execution/interfaces/ref_bundle.py,sha256=n2ml0OKl7zHaaGQBZkb2agfExbHZpVvszLYGhTSuYAU,15968 +ray/data/_internal/execution/interfaces/task_context.py,sha256=KlDfIrkIodQfHlrXEpcHvnp-E6fpDr86_XWTXq2gaIw,2782 +ray/data/_internal/execution/interfaces/transform_fn.py,sha256=Zv1-SKz_Lw_0EC7GPYmbzq8Lswhxr2WV-P5f5xYPc9g,420 +ray/data/_internal/execution/legacy_compat.py,sha256=qseHsuYGRtgnuW_nry09Pb-zeKDrVYUhscbACVCZ5dM,6938 +ray/data/_internal/execution/node_trackers/__init__.py,sha256=u80V6vt1yxwh97wfdzwO5AW6aIlUZnBmW9R_nbkWNF0,163 +ray/data/_internal/execution/node_trackers/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/execution/node_trackers/__pycache__/actor_location.cpython-312.pyc,, +ray/data/_internal/execution/node_trackers/actor_location.py,sha256=KSfmSSFUBc1ypEVIRwrhMnCVFkChzgd3TNUVCFn_-jE,1384 +ray/data/_internal/execution/operators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/_internal/execution/operators/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/actor_pool_map_operator.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/aggregate_num_rows.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/base_physical_operator.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/hash_aggregate.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/hash_shuffle.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/input_data_buffer.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/join.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/limit_operator.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/map_operator.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/map_transformer.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/output_splitter.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/sub_progress.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/task_pool_map_operator.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/union_operator.cpython-312.pyc,, +ray/data/_internal/execution/operators/__pycache__/zip_operator.cpython-312.pyc,, +ray/data/_internal/execution/operators/actor_pool_map_operator.py,sha256=E2oASKeDfQOfdNEnXof-UdW6u-68oSm_AuVnJQBDTcE,48171 +ray/data/_internal/execution/operators/aggregate_num_rows.py,sha256=MECnD10AbFzpN1bK02rBXIZIONdC6X6h_2oYepmey_M,1971 +ray/data/_internal/execution/operators/base_physical_operator.py,sha256=Wp73Td8lfrcbPG7_F8gEoeNEufJFP9WIQ9kxn_0hHUc,9817 +ray/data/_internal/execution/operators/hash_aggregate.py,sha256=JOwhBEeJZoKSKwkgVpJtOwplozNYpOPAZ8lQlMw7HwU,8579 +ray/data/_internal/execution/operators/hash_shuffle.py,sha256=-Zlj80CQ3YkJDCgIRw1neU8c7XK35522xzG5WKFMQqQ,61844 +ray/data/_internal/execution/operators/input_data_buffer.py,sha256=koS-gNcR8XDZPkZNrhnDgVysk-dqlUN1G2ctrzZA8MU,3671 +ray/data/_internal/execution/operators/join.py,sha256=87gXsnDAccFOeWOO5kHDIddmhuGY2BqAUbInyn7y5Pw,19362 +ray/data/_internal/execution/operators/limit_operator.py,sha256=syqwX-X_tnd84i9yCpSoaRqjcWxl9P0u4HdBZPLJmO0,5332 +ray/data/_internal/execution/operators/map_operator.py,sha256=5F2sYDmVLywJ6Nj2B2OvVjOv3P6XwZ1otRvJ18VV5NQ,38690 +ray/data/_internal/execution/operators/map_transformer.py,sha256=KWufb6exWwsGsKY3Q8zgRE17Wv99OM90y8-NIOfTPpc,14580 +ray/data/_internal/execution/operators/output_splitter.py,sha256=X_3xGaI3MXryJSMYzJ02VU-irkJd5LL2Iwit9VJ_J8g,13991 +ray/data/_internal/execution/operators/sub_progress.py,sha256=QTte6yWJ4HqUYSRYXNchnxJdI9OQ9glQSqdRZZMUEcU,806 +ray/data/_internal/execution/operators/task_pool_map_operator.py,sha256=4td4dQpS5M8mQ8l4sP8HV4xF_FA38kfygfwLBqkydU4,7428 +ray/data/_internal/execution/operators/union_operator.py,sha256=_1dOp_ykNJ2NJgqG56XwIMqbx4Fr-EyvcoymYEzlY3Q,5105 +ray/data/_internal/execution/operators/zip_operator.py,sha256=vwkZc6TXNiq7J0H0WEoKNgyOMuAWq5LXc63sAA1cdv0,12753 +ray/data/_internal/execution/progress_manager.py,sha256=r6PSFtN65sJLyp1i2bps3JBy0NyfwI7euMKCAqqKyzM,17447 +ray/data/_internal/execution/ranker.py,sha256=uaRn7cyI6b7hL80TqXvihn3g9yLgS8wd_5o76C8cHXY,2853 +ray/data/_internal/execution/resource_manager.py,sha256=9LZ-fu1gS6KeHDfY_vaIX9vOZVWHu9dt_DmWAHjHzGY,40358 +ray/data/_internal/execution/streaming_executor.py,sha256=VMCCT8M4zH_nlrSL0w2p0ymphGrxeDAYKNkUpKH4dm4,32396 +ray/data/_internal/execution/streaming_executor_state.py,sha256=2SrqIRUM3P9ayFi3sgcgjmFX1ymkzNR0MRzwCXoOeBQ,32646 +ray/data/_internal/execution/util.py,sha256=XzL4yfxTkNE7qQpbaqGL-YgU-SKp1DFeNNrCtRRxBf0,2610 +ray/data/_internal/issue_detection/__init__.py,sha256=XQCCbWgKcRU4hD8ZP1pzwdUyIbbwQuEsoECgiKXxwZQ,642 +ray/data/_internal/issue_detection/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/issue_detection/__pycache__/issue_detector.cpython-312.pyc,, +ray/data/_internal/issue_detection/__pycache__/issue_detector_configuration.cpython-312.pyc,, +ray/data/_internal/issue_detection/__pycache__/issue_detector_manager.cpython-312.pyc,, +ray/data/_internal/issue_detection/detectors/__init__.py,sha256=gVk6RqbUK-9RtLCFMjpjj5lXdgrKjM4lmlWP8Ouj_MQ,628 +ray/data/_internal/issue_detection/detectors/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/issue_detection/detectors/__pycache__/hanging_detector.cpython-312.pyc,, +ray/data/_internal/issue_detection/detectors/__pycache__/hash_shuffle_detector.cpython-312.pyc,, +ray/data/_internal/issue_detection/detectors/__pycache__/high_memory_detector.cpython-312.pyc,, +ray/data/_internal/issue_detection/detectors/hanging_detector.py,sha256=mri39-zuv-5T7HtnqPIOuznJkc2K6zb1WZNTh850AC4,7110 +ray/data/_internal/issue_detection/detectors/hash_shuffle_detector.py,sha256=EGsbtu5TeJtnCoSlhvu65bLyPons1j1zIFlzZRPcUoA,4240 +ray/data/_internal/issue_detection/detectors/high_memory_detector.py,sha256=YGTmqllAi0CQL3Jv6gJma-Ehr_G8YqF9siYsuMeMpDY,3941 +ray/data/_internal/issue_detection/issue_detector.py,sha256=n-veqNPiskmPCh6ftak-bMWv1Jk-5OmN5vSNeq-KHe8,861 +ray/data/_internal/issue_detection/issue_detector_configuration.py,sha256=lXAPXKCYYNOA4KjL8GXCJqBnilbMQSAHz3mkNYANmYc,814 +ray/data/_internal/issue_detection/issue_detector_manager.py,sha256=TKiBpBqv2WmkNYuXOi8vACacXr17q8mDaBLYNIKZwjQ,3758 +ray/data/_internal/iterator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/_internal/iterator/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/iterator/__pycache__/iterator_impl.cpython-312.pyc,, +ray/data/_internal/iterator/__pycache__/stream_split_iterator.cpython-312.pyc,, +ray/data/_internal/iterator/iterator_impl.py,sha256=GtBYE8ZbH4bMHSS9y-32uZtyOlelZ_QnqEeYB9ZsIQI,1182 +ray/data/_internal/iterator/stream_split_iterator.py,sha256=Fm8BWqKhlK6JLcLoWOpSt3Mkxl_wWGPKqxI2ARtzikg,10630 +ray/data/_internal/logging.py,sha256=jrxzXmOMtQzqHSnuZ-vRrX14t9dCZ2C1Q2pCb1SPh5w,14811 +ray/data/_internal/logical/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/_internal/logical/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/logical/__pycache__/optimizers.cpython-312.pyc,, +ray/data/_internal/logical/__pycache__/ruleset.cpython-312.pyc,, +ray/data/_internal/logical/__pycache__/util.cpython-312.pyc,, +ray/data/_internal/logical/interfaces/__init__.py,sha256=FTUFNYnb3QT2DLVbyIknh81CzHNRoBmT_DWU2pYy9AI,786 +ray/data/_internal/logical/interfaces/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/logical/interfaces/__pycache__/logical_operator.cpython-312.pyc,, +ray/data/_internal/logical/interfaces/__pycache__/logical_plan.cpython-312.pyc,, +ray/data/_internal/logical/interfaces/__pycache__/operator.cpython-312.pyc,, +ray/data/_internal/logical/interfaces/__pycache__/optimizer.cpython-312.pyc,, +ray/data/_internal/logical/interfaces/__pycache__/physical_plan.cpython-312.pyc,, +ray/data/_internal/logical/interfaces/__pycache__/plan.cpython-312.pyc,, +ray/data/_internal/logical/interfaces/__pycache__/source_operator.cpython-312.pyc,, +ray/data/_internal/logical/interfaces/logical_operator.py,sha256=lyY9zWSyCCOxlkmhBqdjUnhcZxfPUw0eVJCJ2VeQv3c,5898 +ray/data/_internal/logical/interfaces/logical_plan.py,sha256=FmKIiKzStzYlB9PJKXOc5PQd4tDQA6NH3-CNXR6zxFc,936 +ray/data/_internal/logical/interfaces/operator.py,sha256=4PjqiOgZwb9ZGGN8ALbGl_Ema33IF4IG71SodIoWTLk,3264 +ray/data/_internal/logical/interfaces/optimizer.py,sha256=0JMJPhJuMZvT5lIfVIK6s_vhtg5uJMbOok1SSjzk8qc,1386 +ray/data/_internal/logical/interfaces/physical_plan.py,sha256=8QpVWKXKcvStShoy8a9rkJed8dmn5WjbgGKxXsTezng,930 +ray/data/_internal/logical/interfaces/plan.py,sha256=TJov8hvgA7zkPHAhh2DPv9h16gTvAKF8lqA072YJZNY,598 +ray/data/_internal/logical/interfaces/source_operator.py,sha256=Vz397oMwDMFM0_vbneY6bNUWgx8yzKvD5iYeIbyggH4,471 +ray/data/_internal/logical/operators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/_internal/logical/operators/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/all_to_all_operator.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/count_operator.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/from_operators.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/input_data_operator.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/join_operator.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/map_operator.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/n_ary_operator.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/one_to_one_operator.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/read_operator.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/streaming_split_operator.cpython-312.pyc,, +ray/data/_internal/logical/operators/__pycache__/write_operator.cpython-312.pyc,, +ray/data/_internal/logical/operators/all_to_all_operator.py,sha256=7fJ6iks1fi3qR1eKKFA_Bdg2B0O64Y05Aexe8nKhz-s,8027 +ray/data/_internal/logical/operators/count_operator.py,sha256=ad8H0LftaZAQzElXXN_m2C5GiRvTscUtwHD9-lYNiU0,583 +ray/data/_internal/logical/operators/from_operators.py,sha256=JwugXZ6CxLr3hu8YQAdTF93pm8JXSmODhd4G_4N3GJg,3098 +ray/data/_internal/logical/operators/input_data_operator.py,sha256=OedXrtRU1ptA5W32wyStWWQBYQ_Ed9O5-uk5l7jd250,1826 +ray/data/_internal/logical/operators/join_operator.py,sha256=c75mM-ZArIjR35lq4tCpNH8Ije1r7nlgo9uFPoaoVEo,8300 +ray/data/_internal/logical/operators/map_operator.py,sha256=EGHNILy_IqqirA7XfQhK3KlSjjZN5GKwo7eb8k_vttg,15560 +ray/data/_internal/logical/operators/n_ary_operator.py,sha256=teXb8ZSILt7VCilHZtLMyxXehFDEjpo232u0KAT2C7Q,1812 +ray/data/_internal/logical/operators/one_to_one_operator.py,sha256=T5uiCRFQsuBRVoD-Z3OOk89_nywNDm82rmFzf09yiL4,4385 +ray/data/_internal/logical/operators/read_operator.py,sha256=v71b2vr9WLdBl2w5DxSBoZSCdgwlExEDJ2u1JL9oI_k,7413 +ray/data/_internal/logical/operators/streaming_split_operator.py,sha256=aKXLEEjDp5cwbR_88wq6ZK-lOwFHHsy_ffUHVtd5XRg,673 +ray/data/_internal/logical/operators/write_operator.py,sha256=l0aMhQqKSsKxqSLiAM3A_QMXDwYJpx7D5J9CuzTSHHU,1215 +ray/data/_internal/logical/optimizers.py,sha256=OK5Qw9VvxVV5OiZjEtJUDqdW13NDF18FmdpXbFZtzEs,3390 +ray/data/_internal/logical/rules/__init__.py,sha256=2RvYumHx-e3eFVtvPqOA5CZGI0XkoJglgqAWbHnob98,104 +ray/data/_internal/logical/rules/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/logical/rules/__pycache__/configure_map_task_memory.cpython-312.pyc,, +ray/data/_internal/logical/rules/__pycache__/inherit_batch_format.cpython-312.pyc,, +ray/data/_internal/logical/rules/__pycache__/inherit_target_max_block_size.cpython-312.pyc,, +ray/data/_internal/logical/rules/__pycache__/limit_pushdown.cpython-312.pyc,, +ray/data/_internal/logical/rules/__pycache__/operator_fusion.cpython-312.pyc,, +ray/data/_internal/logical/rules/__pycache__/predicate_pushdown.cpython-312.pyc,, +ray/data/_internal/logical/rules/__pycache__/projection_pushdown.cpython-312.pyc,, +ray/data/_internal/logical/rules/__pycache__/set_read_parallelism.cpython-312.pyc,, +ray/data/_internal/logical/rules/configure_map_task_memory.py,sha256=Vyb1uIntBaSP907VTZpPkslyDcbJNySF-0azrv3563c,3709 +ray/data/_internal/logical/rules/inherit_batch_format.py,sha256=3liaenM0jJCx_3jhzZE6ET6rrTbRShMjSJhYSv00E2g,1684 +ray/data/_internal/logical/rules/inherit_target_max_block_size.py,sha256=-JXK4PzfjdBYVLbu3XJosfhKmt_3ZX1kf2IC_0_WC0Q,1250 +ray/data/_internal/logical/rules/limit_pushdown.py,sha256=2ktbb90snMvDkT5EIAQpFVzrmvbneyNUqh1RzNGA_Pw,8132 +ray/data/_internal/logical/rules/operator_fusion.py,sha256=cZlIbksQKaTyIOXLrPGwKnr7f3ZzLv573jbfqopMsh4,21214 +ray/data/_internal/logical/rules/predicate_pushdown.py,sha256=ZyFpzv2dBJpa5pNgiuYJWux9IsrJ29wM7k1aO9hb-T8,9454 +ray/data/_internal/logical/rules/projection_pushdown.py,sha256=9GkOxnTo5uuDGCO9GMwnbf2w89VTcV_Txt5Un49H3OI,16095 +ray/data/_internal/logical/rules/set_read_parallelism.py,sha256=1p8ql8f_VmThfc4riYaimH6et6yPHc9iPqyR_9s0sVQ,5748 +ray/data/_internal/logical/ruleset.py,sha256=M2gc_B4aV5CmFbagjKxTDpL5BihWJxVXzlz0lfUfn-w,3149 +ray/data/_internal/logical/util.py,sha256=q9o47Jyyb1-j79Es0dgOaXiwW3OlLsGOPhfqbMsai9Q,3078 +ray/data/_internal/memory_tracing.py,sha256=HC_9pDdGuOrDtxO5aKG98tMKySbesMLaD7ImjZJzmcc,5861 +ray/data/_internal/metadata_exporter.py,sha256=navGcs1I5HfBWIHgAx3uxJ6XWVzXXRywvwfSv1lTM9o,13199 +ray/data/_internal/numpy_support.py,sha256=ZWz4jGldEdrDfrBKD8mIc_NBYo3CLRQm-KwSB_ONXUo,8432 +ray/data/_internal/operator_event_exporter.py,sha256=3ykVXH286bagoveVIOTHlWWlrikhcxL7Sg4C9vYYHbw,5231 +ray/data/_internal/output_buffer.py,sha256=3gev4guTa1LWVvwA6-uIw9mIMI6131pZFEwP8QuoiL4,7385 +ray/data/_internal/pandas_block.py,sha256=565jCiGVOGTRFxVLfLP1jSJzXAHbiHz86hVxhVtvdwc,24933 +ray/data/_internal/plan.py,sha256=_fgaWlHxZi9-1XtB8GjvdeVfQsfr3w-bzXGUFclHP5Y,25618 +ray/data/_internal/planner/__init__.py,sha256=JBFixax2Wh-qQz1h_xbn4zCrgZxgcXZoihpgqWjH5YM,304 +ray/data/_internal/planner/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/aggregate.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/plan_all_to_all_op.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/plan_download_op.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/plan_read_op.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/plan_udf_map_op.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/plan_write_op.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/planner.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/random_shuffle.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/randomize_blocks.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/repartition.cpython-312.pyc,, +ray/data/_internal/planner/__pycache__/sort.cpython-312.pyc,, +ray/data/_internal/planner/aggregate.py,sha256=T0ab1mzaahlmq7cFShsCRUWvuR1K7lsmA_mlIWSNYi0,3334 +ray/data/_internal/planner/exchange/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/_internal/planner/exchange/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/planner/exchange/__pycache__/aggregate_task_spec.cpython-312.pyc,, +ray/data/_internal/planner/exchange/__pycache__/interfaces.cpython-312.pyc,, +ray/data/_internal/planner/exchange/__pycache__/pull_based_shuffle_task_scheduler.cpython-312.pyc,, +ray/data/_internal/planner/exchange/__pycache__/push_based_shuffle_task_scheduler.cpython-312.pyc,, +ray/data/_internal/planner/exchange/__pycache__/shuffle_task_spec.cpython-312.pyc,, +ray/data/_internal/planner/exchange/__pycache__/sort_task_spec.cpython-312.pyc,, +ray/data/_internal/planner/exchange/__pycache__/split_repartition_task_scheduler.cpython-312.pyc,, +ray/data/_internal/planner/exchange/aggregate_task_spec.py,sha256=IwvFeP52QWYY0KLCTf6GXysHLODqJpXbLwgYTI5GCO0,4129 +ray/data/_internal/planner/exchange/interfaces.py,sha256=gjkD550QdUWI84iEc-WfN4bUOXeMfNyJ4qDEWi885js,5320 +ray/data/_internal/planner/exchange/pull_based_shuffle_task_scheduler.py,sha256=nLeYj1r0zbfZAhQSnqDfyPqxIz6ZNlhuR0sHavAzFX4,6276 +ray/data/_internal/planner/exchange/push_based_shuffle_task_scheduler.py,sha256=Wvn_BoYfJyRUFYF3YKBztYzkF9rnsMg61Px-XMUBDX0,33537 +ray/data/_internal/planner/exchange/shuffle_task_spec.py,sha256=B5YqiLYMOgR9x2vp9p9KMREdXq1dbzQba-oqhlbvqpo,5532 +ray/data/_internal/planner/exchange/sort_task_spec.py,sha256=_8qqeRVbtcX9d1N8RVOwjeUOwY52fbLcw3WFrm0uqRw,9034 +ray/data/_internal/planner/exchange/split_repartition_task_scheduler.py,sha256=pMCE1RKhWZtlLo3nMdIsKvf-KFqEvBCG8VrnfZ0BWVk,6640 +ray/data/_internal/planner/plan_all_to_all_op.py,sha256=dN8IzWmmSxrTX1hD6vzZ1vKvmndMFJxa3se-vvwfQ1s,5801 +ray/data/_internal/planner/plan_download_op.py,sha256=tEvjXTRxleGOh8IrYy1oDiWSPgmPxYYuZOFTXoSyKfM,13726 +ray/data/_internal/planner/plan_expression/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/_internal/planner/plan_expression/__pycache__/__init__.cpython-312.pyc,, +ray/data/_internal/planner/plan_expression/__pycache__/expression_evaluator.cpython-312.pyc,, +ray/data/_internal/planner/plan_expression/__pycache__/expression_visitors.cpython-312.pyc,, +ray/data/_internal/planner/plan_expression/expression_evaluator.py,sha256=-t_amBmrU2BIhYjc7zPLjpncJpDgAl7bYolwc9EhJ_w,27519 +ray/data/_internal/planner/plan_expression/expression_visitors.py,sha256=pxy0mGeWKv_BRMpNnPoKj34Fh7m6BGQ0LLyRkBtdcHc,11333 +ray/data/_internal/planner/plan_read_op.py,sha256=LvrDLhXXEPwZeYuMx0X36bqtrvXnTzZx77tuD1-EXTs,4781 +ray/data/_internal/planner/plan_udf_map_op.py,sha256=51KmUPOZk_X1Jh1pe6nPPAECQH9SBX2DN1xJ4lCsfpo,27737 +ray/data/_internal/planner/plan_write_op.py,sha256=jM5kwTqBjQZ2rkRkLCjOhR0YSGpFiRY2j-A5Q9Xx4YM,4339 +ray/data/_internal/planner/planner.py,sha256=pQZjPTTkNva4nMJYDm9V9ds2YpHwyig3pPRwIOegG1g,9283 +ray/data/_internal/planner/random_shuffle.py,sha256=GYOQOCkXFHgfZzuQ8acs4ipAL2knEouPA5n7T4bqIdo,3475 +ray/data/_internal/planner/randomize_blocks.py,sha256=uq5_Ia3wiqPCjXOU9ji8fOOCsALcnzZHEdLfflDPTm4,1819 +ray/data/_internal/planner/repartition.py,sha256=mcWVrXUJ9bcKocvw5ntvSrdI0cuR15QSe-KyiF8_QQk,3248 +ray/data/_internal/planner/sort.py,sha256=eNRzzRgFUA5-1kdTlYmEw0Q3Q7n1ya_dfz-KKehnif0,3051 +ray/data/_internal/progress_bar.py,sha256=oNa_3XMeb3LP5tlLyeqq63MG9dDRTlASs0uzzeG6b2M,7881 +ray/data/_internal/remote_fn.py,sha256=NWhWFnzKhkPyERyQOgpjMiDpT5cqEDiPJDbUUQlhRPk,3127 +ray/data/_internal/row.py,sha256=ZCuvnmVYHsGaHwOpxBi1rFIPgtlUbs1crwkUf96mzvc,1299 +ray/data/_internal/savemode.py,sha256=JK5AWT9L1UEiAVtll_aRlC-FJIqAR5U0mx8_cK8JULo,638 +ray/data/_internal/size_estimator.py,sha256=ysmwWsXLZHvikQWAd8cUC1awnW-nz727yv6ICXkeODI,2869 +ray/data/_internal/split.py,sha256=0J-10zVv7dpGQhIFsKq-AC3bzJi80gpIP-vw00GwPBs,11139 +ray/data/_internal/stats.py,sha256=VrQ4jz70AYEF_kSL59hbftuiCrVP1hap2gOHcJFVurQ,74847 +ray/data/_internal/streaming_repartition.py,sha256=msZvFbo8DjFvsjgeyOg5IASndXDazZtCkRLyW4jvS44,3963 +ray/data/_internal/table_block.py,sha256=1IjlJi4kA2QYMA0tYPShHBFVCJRAD-K7TKH5zZgxHFA,21584 +ray/data/_internal/torch_iterable_dataset.py,sha256=WYYir0pFH5-xd-1M0-Hpr6wQ5-SRjJditEA-QaCDWTE,259 +ray/data/_internal/util.py,sha256=fepYNuQ69WyfYP8UUZIYOGOBhD_P7zx1paHbT2YahbY,62441 +ray/data/aggregate.py,sha256=HfKuw2v9-7m_QmW9MyShm8y-lMC3uRAEyBS1OrIuuD4,57604 +ray/data/block.py,sha256=5OJTzQhBkVAopX47RSFIiEG107lF-5ovO7xAAnaBXOQ,28749 +ray/data/collate_fn.py,sha256=aEsYwIXvf_IJM7gTnuMdtsdhoiQ5BCCrhfgsPSuP9gY,8227 +ray/data/context.py,sha256=RK4UX7dqTk4WGyfgV10fD2z7p-I8MBOFsrymIF-4E1c,34536 +ray/data/dataset.py,sha256=zHk6lK44ZNSUrVQVfYrosn3alWJbOzz_sOJIX7Zhb6Q,284769 +ray/data/datasource/__init__.py,sha256=z2GTP3wAn17VRmpfC2lHY9yPDS3RofWQo9mFaoKfO8I,2037 +ray/data/datasource/__pycache__/__init__.cpython-312.pyc,, +ray/data/datasource/__pycache__/datasink.cpython-312.pyc,, +ray/data/datasource/__pycache__/datasource.cpython-312.pyc,, +ray/data/datasource/__pycache__/file_based_datasource.cpython-312.pyc,, +ray/data/datasource/__pycache__/file_datasink.cpython-312.pyc,, +ray/data/datasource/__pycache__/file_meta_provider.cpython-312.pyc,, +ray/data/datasource/__pycache__/filename_provider.cpython-312.pyc,, +ray/data/datasource/__pycache__/partitioning.cpython-312.pyc,, +ray/data/datasource/__pycache__/path_util.cpython-312.pyc,, +ray/data/datasource/__pycache__/util.cpython-312.pyc,, +ray/data/datasource/datasink.py,sha256=LQYPHE2ZXVP6JBcFvHRC_bLDpJO7BUFZK6sUs3MCx3g,6023 +ray/data/datasource/datasource.py,sha256=zb1onvjJ9pcPR6mjML7wOTNf4ZiZc-uI3VY2yeI0o_c,17842 +ray/data/datasource/file_based_datasource.py,sha256=G6mLQm5UByk3nP-FtNaybIG_1IJ3UEJZkQ3ggPse8L8,19836 +ray/data/datasource/file_datasink.py,sha256=7DVoSaKDwvyK1smwWNoYzsvmCHDFrSCWYuQm9Afo2Vw,11042 +ray/data/datasource/file_meta_provider.py,sha256=rzH2czE1BnVjVWeBh6PozFCJnIIdALY_go6QyRXDdWI,18659 +ray/data/datasource/filename_provider.py,sha256=CiBU1k6n2FCP9bV4iO5L4C2PAoYMfOqpUn7gYyCulO8,4986 +ray/data/datasource/partitioning.py,sha256=9ChXsbY8l1eK7zzlX9SdDPRsovZuKcVMfk6gaNUEwjc,19650 +ray/data/datasource/path_util.py,sha256=3d_1JGiGBP_nsIpvSiR95XKnEgD7j6Fi5DTaoEalX98,8540 +ray/data/datasource/util.py,sha256=I6IhXLzPqaAWsipD_ES3aDrd2sa7VNpmkWVCRq6k_fU,859 +ray/data/datatype.py,sha256=SMy4prCwVEbF_x6cokvYyZQ_p_yhYmIS3agZQ5ajCJs,36840 +ray/data/exceptions.py,sha256=l6U837Uwrfgs6rvdwaHVf8P7zaTHikW4vtJFz5bT-Gs,3926 +ray/data/expressions.py,sha256=HYgcfIctmGtol_nGwYW6ZWqCWResRiu9ReqxTW2Wclg,36639 +ray/data/extensions/__init__.py,sha256=wfJu5w7Offu__LsrBGAR41UlKlZ1exB9TRLWjbuTgWg,1187 +ray/data/extensions/__pycache__/__init__.cpython-312.pyc,, +ray/data/extensions/__pycache__/object_extension.cpython-312.pyc,, +ray/data/extensions/__pycache__/tensor_extension.cpython-312.pyc,, +ray/data/extensions/object_extension.py,sha256=ZUSj0p2sfBBShI3mi3on2S7Z66H_Fj-4AM9i42t9-Gs,301 +ray/data/extensions/tensor_extension.py,sha256=xeEYpd5EgkEbCe1ufWiuKvinnZ5L7Q_1S_7re6s7V00,476 +ray/data/grouped_data.py,sha256=WwrmuhuSwUYEs3XGbBPvfZyhzeduqWLoxczn2arXtlc,25312 +ray/data/iterator.py,sha256=CJbelmeaOlM0LQIxOdEJyKeS0vtbd9kG9zBNI6mAcPg,47004 +ray/data/llm.py,sha256=c4bz6H4LxpBuJJwztjCG1tHYTIpFS0p9XZwEurY-KaE,22403 +ray/data/namespace_expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/data/namespace_expressions/__pycache__/__init__.cpython-312.pyc,, +ray/data/namespace_expressions/__pycache__/list_namespace.cpython-312.pyc,, +ray/data/namespace_expressions/__pycache__/string_namespace.cpython-312.pyc,, +ray/data/namespace_expressions/__pycache__/struct_namespace.cpython-312.pyc,, +ray/data/namespace_expressions/list_namespace.py,sha256=siQI-ZZZQro0gIpPWixXXFUykcvPBdjydyb2Hp1zj2Y,4049 +ray/data/namespace_expressions/string_namespace.py,sha256=P3C9Wwnify1c4v38tLw-OLDCzC4fEeysj9jUt8w0Mw4,13361 +ray/data/namespace_expressions/struct_namespace.py,sha256=3u3H95OkgTzB_ky_7goHBbXDbVR8hIdMQFM5JScTvhg,2535 +ray/data/preprocessor.py,sha256=DflMvYFitIEyXTANUjyOXrymgWG8tpIWuTw790jOvNQ,27311 +ray/data/preprocessors/__init__.py,sha256=cmLZN7WN66y806Cgjid9gdoG8bd_aWyhJQFJQliBiSo,1389 +ray/data/preprocessors/__pycache__/__init__.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/chain.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/concatenator.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/discretizer.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/encoder.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/hasher.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/imputer.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/normalizer.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/scaler.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/serialization_handlers.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/tokenizer.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/torch.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/transformer.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/utils.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/vectorizer.cpython-312.pyc,, +ray/data/preprocessors/__pycache__/version_support.cpython-312.pyc,, +ray/data/preprocessors/chain.py,sha256=FvTFX1N51S7F72EzvNgqr1kzB7_mVnlL5QI2lWW-kYk,4159 +ray/data/preprocessors/concatenator.py,sha256=fv89EFbZD_kbqgx4iGsJnSX2Dd8bQmcdbJRL_ihwcdo,6174 +ray/data/preprocessors/discretizer.py,sha256=q4-0MsIKcVkPO1ub4I2IllHoIBcRB2lqsN2B3rgTHxc,16408 +ray/data/preprocessors/encoder.py,sha256=ib-DzflpeTfbq9C1itdwbXxz4_awIvnpbF_deISwhxc,36495 +ray/data/preprocessors/hasher.py,sha256=B9DYSH_Gkr7igtfe7071FXqdNfETJkVXfaw-xBODAac,4708 +ray/data/preprocessors/imputer.py,sha256=CakkDBwu2NGDUSuLYV623YTIcFN8YqtydfZdVHvhvZE,9174 +ray/data/preprocessors/normalizer.py,sha256=i-IgwLu_zyweWWEoJmtyGbr0MLmppMPE8k74jHFpsdM,4530 +ray/data/preprocessors/scaler.py,sha256=CNRcrhrDWYkG9ds2AhAjPaUgm17q3EwDT3DrBVsjDXA,18740 +ray/data/preprocessors/serialization_handlers.py,sha256=nw8OZRrTGI4_slsSPzTiBSMd97hXmFDjLS-bU18NrdE,6524 +ray/data/preprocessors/tokenizer.py,sha256=PWSUw4ihicIhyQO3fBHqKiuLZBHgL2goqzOAXU0KKQ0,3523 +ray/data/preprocessors/torch.py,sha256=XH-nrMHwf3xOjZ3pfWyiJZTIpsqkAc1Y6Ze65-MIyy4,5745 +ray/data/preprocessors/transformer.py,sha256=LkJC5yOspBT-oRw8qLi0BmcKvi33uoyKYzV0npGNG-0,3816 +ray/data/preprocessors/utils.py,sha256=GY-_AwSvXejAzr68vlGxvx-q2PlyVaIBkuGZNfpVlbM,7397 +ray/data/preprocessors/vectorizer.py,sha256=MXwTo9-Tipj42pr68FIpe3HzPg8ujp4UeYM2KJC2ofA,13475 +ray/data/preprocessors/version_support.py,sha256=9U4NooYUt1VsShusLkrbnaZGGvYLp4V6Ft_VTwnPI9g,3170 +ray/data/random_access_dataset.py,sha256=1CANv48tZfvYFjV9Ln1XAtFblDa3sftmFkttyKDqPiA,10209 +ray/data/read_api.py,sha256=3VUyZqGxBfvukOGGNVo3Le5t-EKkyr_dklEwvBd5PnE,194884 +ray/data/stats.py,sha256=-YU2AhBScgCrJDUKfuvIsterUILcp1Z9TPbF0oKtIzg,4960 +ray/exceptions.py,sha256=t3ioTlBjgjoEOOS6oGmFXdUxfEUi2o1GNeN8c1eLCW8,31738 +ray/experimental/__init__.py,sha256=cz9cDOzRUzyRq0xKhnx5ZpfnbvWN4bv6qLDKqEuAafs,378 +ray/experimental/__pycache__/__init__.cpython-312.pyc,, +ray/experimental/__pycache__/compiled_dag_ref.cpython-312.pyc,, +ray/experimental/__pycache__/dynamic_resources.cpython-312.pyc,, +ray/experimental/__pycache__/gradio_utils.cpython-312.pyc,, +ray/experimental/__pycache__/internal_kv.cpython-312.pyc,, +ray/experimental/__pycache__/locations.cpython-312.pyc,, +ray/experimental/__pycache__/queue.cpython-312.pyc,, +ray/experimental/__pycache__/shuffle.cpython-312.pyc,, +ray/experimental/__pycache__/tf_utils.cpython-312.pyc,, +ray/experimental/__pycache__/tqdm_ray.cpython-312.pyc,, +ray/experimental/channel/__init__.py,sha256=L-msl2PLqIoO0kekIWSu-cJ93ZhefUS82LKYb7j-pl0,1240 +ray/experimental/channel/__pycache__/__init__.cpython-312.pyc,, +ray/experimental/channel/__pycache__/accelerator_context.cpython-312.pyc,, +ray/experimental/channel/__pycache__/auto_transport_type.cpython-312.pyc,, +ray/experimental/channel/__pycache__/cached_channel.cpython-312.pyc,, +ray/experimental/channel/__pycache__/common.cpython-312.pyc,, +ray/experimental/channel/__pycache__/communicator.cpython-312.pyc,, +ray/experimental/channel/__pycache__/communicator_handle.cpython-312.pyc,, +ray/experimental/channel/__pycache__/conftest.cpython-312.pyc,, +ray/experimental/channel/__pycache__/cpu_communicator.cpython-312.pyc,, +ray/experimental/channel/__pycache__/intra_process_channel.cpython-312.pyc,, +ray/experimental/channel/__pycache__/nccl_group.cpython-312.pyc,, +ray/experimental/channel/__pycache__/serialization_context.cpython-312.pyc,, +ray/experimental/channel/__pycache__/shared_memory_channel.cpython-312.pyc,, +ray/experimental/channel/__pycache__/torch_tensor_accelerator_channel.cpython-312.pyc,, +ray/experimental/channel/__pycache__/torch_tensor_type.cpython-312.pyc,, +ray/experimental/channel/__pycache__/utils.cpython-312.pyc,, +ray/experimental/channel/accelerator_context.py,sha256=ySycmztjv45NTq7PThsiQFnhyTLJiiBwVCMgeCkrmAI,8603 +ray/experimental/channel/auto_transport_type.py,sha256=KKlANsUXsBFdA2uQyZlT9Ro9nvILH9RV6bp0W-FToLA,6743 +ray/experimental/channel/cached_channel.py,sha256=MH9h0FVYmDxk4yY2NQiKzDQ5NQNKLaLlIriD4QyEWDw,4672 +ray/experimental/channel/common.py,sha256=nMgnyPsFTJm-amm-kwwRxiWizck_nqD7lGRdoG2HB9I,24600 +ray/experimental/channel/communicator.py,sha256=CmSmVbY-e_jrECzu9wKQIgBFuwvM_w7P-f5El_OIjas,5821 +ray/experimental/channel/communicator_handle.py,sha256=-u0j2R54JqL60D3SFYSQ6xbmbClB2yL5_Rj5B0ofTjg,689 +ray/experimental/channel/conftest.py,sha256=neF8StM_tt8hLCAvDe7GIncFq4ulitc0AivVSK1gQRE,6060 +ray/experimental/channel/cpu_communicator.py,sha256=qeHKXPQWWpc1wWNZba73eWhP39FHOPyWe96zr-XyImE,6791 +ray/experimental/channel/intra_process_channel.py,sha256=rwTORm7iXTCtBTq3k9gKBFGyFskOAUADX_JPBa90XqQ,2749 +ray/experimental/channel/nccl_group.py,sha256=AXfqECrQGxjcv5xntzjraYCOrUgYdkODGkSof5li5I0,13562 +ray/experimental/channel/serialization_context.py,sha256=r9Wg-H20Ze2wPiBM2ploFPdz9xXK5xzhlmIE9k5Xs9w,10055 +ray/experimental/channel/shared_memory_channel.py,sha256=wRQo_nOj8IsyAR14YMl9p6KEEPFe8mkDsIXslw2WBlg,31961 +ray/experimental/channel/torch_tensor_accelerator_channel.py,sha256=LquXqQ-Ma96VCb303qUNkATEN-Rvaqif4NxSTsq2sDM,34457 +ray/experimental/channel/torch_tensor_type.py,sha256=EUoNou61hxlPNXmXLZDYxDJUGpMao8IfE2ChKGzF6r8,7424 +ray/experimental/channel/utils.py,sha256=hvaySGkhrCF0a1SRH5HLyHeHHaso5dkdelipB45TS74,2788 +ray/experimental/collective/__init__.py,sha256=SrmAdbs3ujACvNNws_sh6m8bSLQV03sgGE3OKFJ7zpk,589 +ray/experimental/collective/__pycache__/__init__.cpython-312.pyc,, +ray/experimental/collective/__pycache__/collective.cpython-312.pyc,, +ray/experimental/collective/__pycache__/collective_tensor_transport.cpython-312.pyc,, +ray/experimental/collective/__pycache__/communicator.cpython-312.pyc,, +ray/experimental/collective/__pycache__/conftest.cpython-312.pyc,, +ray/experimental/collective/__pycache__/nixl_tensor_transport.cpython-312.pyc,, +ray/experimental/collective/__pycache__/operations.cpython-312.pyc,, +ray/experimental/collective/__pycache__/tensor_transport_manager.cpython-312.pyc,, +ray/experimental/collective/__pycache__/util.cpython-312.pyc,, +ray/experimental/collective/collective.py,sha256=kQFKcYzNoQOZjIbpHGffsLq-K4SzD6O8XsIgvbl-ugQ,8228 +ray/experimental/collective/collective_tensor_transport.py,sha256=NfczoIPij0TyRuf3hWNP8ukX_O6zgVcdX0I5Op9Og20,7237 +ray/experimental/collective/communicator.py,sha256=-MdyO8aSs4sIWouvL23fJPlbDEMMyR0UX3jkPyujza0,1643 +ray/experimental/collective/conftest.py,sha256=X1onN8UFtFglEH4zriOHsYPQM5HQ3MO2grreBYojZeE,6940 +ray/experimental/collective/nixl_tensor_transport.py,sha256=BIMsVXKx3EwPVf800EoNgxWY3-chGuzXerFWgc_jYVA,7153 +ray/experimental/collective/operations.py,sha256=VFlH4Sq2WBAdWBOvu4g-1D6IJ0Np-rRYnQ76giW_SzE,6968 +ray/experimental/collective/tensor_transport_manager.py,sha256=BUT9RaDHMsCsED_H_W5gStBUpjFpEJh0kEp9CtVKJJ8,5136 +ray/experimental/collective/util.py,sha256=Oo2TJWrJeVVCv6Dp-X_QKZW4M6tcXND9_fV7n9eS-FY,2713 +ray/experimental/compiled_dag_ref.py,sha256=5k93qI6mkcBvsYcpepB0L10TtFUq9iFRZuMjP7SzOSg,8628 +ray/experimental/dynamic_resources.py,sha256=xLcUITwjRz3VborMa-3irftI_kcfZIXy1akR2T6MIyA,366 +ray/experimental/gpu_object_manager/__init__.py,sha256=W_u6FQt66mqkUiNno5bO38AQBUGYrzGyCwbt1ZAXmg8,169 +ray/experimental/gpu_object_manager/__pycache__/__init__.cpython-312.pyc,, +ray/experimental/gpu_object_manager/__pycache__/gpu_object_manager.cpython-312.pyc,, +ray/experimental/gpu_object_manager/__pycache__/gpu_object_store.cpython-312.pyc,, +ray/experimental/gpu_object_manager/gpu_object_manager.py,sha256=4Q6f0c7OtpQosuYMYCtUN4-7zI4CN20BKbkUdXUjLMQ,28308 +ray/experimental/gpu_object_manager/gpu_object_store.py,sha256=V4GWN9O5OT_Njqc1vBWSO3KiGfEKKU7ZZoO-7yP-Gn8,16867 +ray/experimental/gradio_utils.py,sha256=yeAKHd5mSocJ_Kuvii43sndbwJFbZkibGn6NBO5Fhbo,426 +ray/experimental/internal_kv.py,sha256=70sXf6AyN4F2lDeW68Hbm2QWbZ2kxovc-XG9q7NyaD8,3369 +ray/experimental/job/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/experimental/job/__pycache__/__init__.cpython-312.pyc,, +ray/experimental/job/example_job/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/experimental/job/example_job/__pycache__/__init__.cpython-312.pyc,, +ray/experimental/job/example_job/__pycache__/demo_script.cpython-312.pyc,, +ray/experimental/job/example_job/demo_script.py,sha256=L9s61byeJoxKrEIxLY2bDqCMxNSWGh_ubuK8-bTHHyE,2258 +ray/experimental/locations.py,sha256=0kurQ-lHu1RHT7usojiVPoDHqPjq_hcCo-YcTbr90l0,2829 +ray/experimental/multiprocessing/__init__.py,sha256=hLGdYmPBcC5PUMJkdzHQtmkpoxB4zyFV78jtyM5G35A,101 +ray/experimental/multiprocessing/__pycache__/__init__.cpython-312.pyc,, +ray/experimental/multiprocessing/__pycache__/pool.cpython-312.pyc,, +ray/experimental/multiprocessing/pool.py,sha256=yg38ubcXojfwJfhHh3LyXDWTZgQ0cECr-U4fm3NjVbM,107 +ray/experimental/queue.py,sha256=PtN0yekRZRd6mPfLV9UZg7AUNOVDjpD1Vmy3k42cA0c,278 +ray/experimental/shuffle.py,sha256=bGL9biDRlMJBUwIik9BRHIZ9sEv0JTJRIS_89Iw1fwE,11888 +ray/experimental/state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/experimental/state/__pycache__/__init__.cpython-312.pyc,, +ray/experimental/state/__pycache__/api.cpython-312.pyc,, +ray/experimental/state/__pycache__/common.cpython-312.pyc,, +ray/experimental/state/__pycache__/custom_types.cpython-312.pyc,, +ray/experimental/state/__pycache__/exception.cpython-312.pyc,, +ray/experimental/state/__pycache__/state_cli.cpython-312.pyc,, +ray/experimental/state/__pycache__/state_manager.cpython-312.pyc,, +ray/experimental/state/__pycache__/util.cpython-312.pyc,, +ray/experimental/state/api.py,sha256=lTSJNmYiuJH7H6cl3KWBJJEJipQLahY5h4MUAd1qEjM,153 +ray/experimental/state/common.py,sha256=z0wOGro4JJknd9w8v4jT47rDL_vAAgAPBDhjzeXONH8,160 +ray/experimental/state/custom_types.py,sha256=sZThzLiKVyqpdBDYevFd3iZYs4vUHIU-5-TgzdXgrDA,164 +ray/experimental/state/exception.py,sha256=I3QCtFf7vzLryGd0EywtcQtWeWxDgiNWsvK0gjD7z7Y,163 +ray/experimental/state/state_cli.py,sha256=XbcE3Pf2LUYgYsZzIyMSNXeeKdJKKi99MNM0BBzoYP8,163 +ray/experimental/state/state_manager.py,sha256=J8L5kVU-z9JFDpO3dGU2kWft_YVCAzHzdY_xbK1Y6yI,167 +ray/experimental/state/util.py,sha256=elplAt5gVGD8C1pYaJveHZq-rPiSAVfJ5NT6upvjcGI,158 +ray/experimental/tf_utils.py,sha256=xlweVsthvDkiMpnjq-B50_Mo9_uiS3sTrBfHMexMmOY,121 +ray/experimental/tqdm_ray.py,sha256=DHgKwCpOicGacvgA3e9kikAsjiLr4Bz5vb-XVCNfUnc,13609 +ray/experimental/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/experimental/util/__pycache__/__init__.cpython-312.pyc,, +ray/experimental/util/__pycache__/types.cpython-312.pyc,, +ray/experimental/util/types.py,sha256=lLTv8ybRrhpSZQ3In7Gc02Ljfhr67H0WiH1Nvs0aX9Q,670 +ray/includes/__init__.pxd,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/includes/array.pxd,sha256=EBJP_Qw1M0_qXjWvsEVmON8T_hHpt_EVp5iDAB4ql30,218 +ray/includes/common.pxd,sha256=zg9f6NvE8_jEsAg7_8XKBcL_dSMPdeZe2UgyL3879xA,31881 +ray/includes/function_descriptor.pxd,sha256=Vy4rxapEoeNsSgtlHICzwBD48HEdWt1_e9fA65mEK2Y,2954 +ray/includes/global_state_accessor.pxd,sha256=cingMfL7-dxEplcHOS2LDfnkqZitvKF-_JHiG5D-Snk,5793 +ray/includes/libcoreworker.pxd,sha256=u4bDBZiY0dFiFqbxsLZ7aPhwcevZSTDgrnv-A3S2Ptk,19419 +ray/includes/metric.pxd,sha256=PBHPVEZ31z7xbkjWxH-cYfobYqoUs5NssLDDMZJ7Ess,1503 +ray/includes/network_util.pxd,sha256=0k2vJhRgIhLjNxP6k_TEcwvUTMSujMin6xaFcTmAWCA,538 +ray/includes/object_ref.pyi,sha256=RrgdjmOnviD45Ff3AmnqwVZ1-FWxiYx9jfC222fvQMQ,2187 +ray/includes/optional.pxd,sha256=oU2g1EOi9-fnxfZ6Y2Oj9LookffOAJd6Yk4n8Sd_E9g,1083 +ray/includes/ray_config.pxd,sha256=q2m5xHq6VCTh9xS5M4BSLDX-cTC7KsaUVutBeQgla8A,2416 +ray/includes/rpc_token_authentication.pxd,sha256=E3cdhab7lyMBvVyCfKT8NGSZvAL895miXqYPUP2XJpA,1709 +ray/includes/setproctitle.pxd,sha256=0JO7miSyizRhsrDH8STDu-3tYYlqz-vw9LXJGUcWk2U,355 +ray/includes/stream_redirection.pxd,sha256=r3lcrUN7--0adpTTw-yeOrtpMBQo7khliotuRq8M2wU,678 +ray/includes/unique_ids.pxd,sha256=bA7VEE4LUi-QksPyj4WqAI8UlOnrXSvpyFgyB2yIpks,4779 +ray/includes/unique_ids.pyi,sha256=kyIcG8JcHXISAuKIhAUI5d4zkr2X6FlAbAFl0pWKiPg,3374 +ray/internal/__init__.py,sha256=LYqgMsS5qemO2omV9My10UZ8pMcm_Ik_PwCBX5VwkNQ,63 +ray/internal/__pycache__/__init__.cpython-312.pyc,, +ray/jars/ray_dist.jar,sha256=MkXkj3oz8bIB9lkozWXubR-Wn6jlQMRGZWtNzoJfm3A,33546378 +ray/job_config.py,sha256=KDUNXzNrL8gXcde4gLk71TKJvUPXhiM8yOMMlcWckXg,9742 +ray/job_submission/__init__.py,sha256=VoyceKIY3jL0b9_YP2fd2y_n-H9YAEjCtSNN3T8K8MQ,371 +ray/job_submission/__pycache__/__init__.cpython-312.pyc,, +ray/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/batch/__init__.py,sha256=eFk3XkNKygF3kj7OYW7bGsiVi-Y_6hLeSBrEKZC-yus,253 +ray/llm/_internal/batch/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/batch/__pycache__/utils.cpython-312.pyc,, +ray/llm/_internal/batch/benchmark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/batch/benchmark/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/batch/benchmark/__pycache__/benchmark_processor.cpython-312.pyc,, +ray/llm/_internal/batch/benchmark/__pycache__/dataset.cpython-312.pyc,, +ray/llm/_internal/batch/benchmark/benchmark_processor.py,sha256=L_dqdJtX0uWJdBpXDyWlcQFLl9xywIN2bQegxMSVFa0,16048 +ray/llm/_internal/batch/benchmark/dataset.py,sha256=LIDgPJ9mqcHOUsdJ__FOvLhRfTZPgVzB1AM3DrY7cYI,5459 +ray/llm/_internal/batch/observability/__init__.py,sha256=_7VxkhMfGEH12Hy2Im0IDtUTKe_ansvqOhSsFYnkxCY,597 +ray/llm/_internal/batch/observability/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/batch/observability/logging/__init__.py,sha256=iUidR4k4lYW4lTPy6dWBLIm_2pOXYw--lhgvr_O_eE4,1278 +ray/llm/_internal/batch/observability/logging/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/batch/observability/logging/__pycache__/setup.cpython-312.pyc,, +ray/llm/_internal/batch/observability/logging/setup.py,sha256=qa9P-tMLg09Fb4p0gK_dENIqwopy7-VQnz-wcPpqqEk,789 +ray/llm/_internal/batch/observability/usage_telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/batch/observability/usage_telemetry/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/batch/observability/usage_telemetry/__pycache__/usage.cpython-312.pyc,, +ray/llm/_internal/batch/observability/usage_telemetry/usage.py,sha256=-w76UamvaOc6xKzSmg-A4ZAGjXsC9iiLAJouvO14Isk,5255 +ray/llm/_internal/batch/processor/__init__.py,sha256=9I8dpJoj1hz9zbELoMCF0FMPBrc18OZ8tLTgb6XznUY,522 +ray/llm/_internal/batch/processor/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/batch/processor/__pycache__/base.cpython-312.pyc,, +ray/llm/_internal/batch/processor/__pycache__/http_request_proc.cpython-312.pyc,, +ray/llm/_internal/batch/processor/__pycache__/serve_deployment_proc.cpython-312.pyc,, +ray/llm/_internal/batch/processor/__pycache__/sglang_engine_proc.cpython-312.pyc,, +ray/llm/_internal/batch/processor/__pycache__/vllm_engine_proc.cpython-312.pyc,, +ray/llm/_internal/batch/processor/base.py,sha256=5Nedhn3xDxhZ20gNdN9LMw7_D6qWgOFkYOuRzTDVO4Y,16028 +ray/llm/_internal/batch/processor/http_request_proc.py,sha256=BkJk0P5D3-n9LGnV4vTB9tSUV5bIEjUvnK6m9ify8Kc,4158 +ray/llm/_internal/batch/processor/serve_deployment_proc.py,sha256=KVLXWGo3jqH7W2VV9e3V2ZPUpsDKd1CDsvxFzvuSMaw,2921 +ray/llm/_internal/batch/processor/sglang_engine_proc.py,sha256=2Hr8ctVmMfL-b7EKiILRpaMOA2rpsaxS6AqmBrixcJ8,7395 +ray/llm/_internal/batch/processor/vllm_engine_proc.py,sha256=2QNUr6qr0DaFyBhCH50gu3hGEWDpNNZMVEuhmwe2oME,10934 +ray/llm/_internal/batch/stages/__init__.py,sha256=NzRbURJlo25skxqbPL-NfoCNxwd16XKYZSSm5MoaeXQ,966 +ray/llm/_internal/batch/stages/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/batch/stages/__pycache__/base.cpython-312.pyc,, +ray/llm/_internal/batch/stages/__pycache__/chat_template_stage.cpython-312.pyc,, +ray/llm/_internal/batch/stages/__pycache__/common.cpython-312.pyc,, +ray/llm/_internal/batch/stages/__pycache__/http_request_stage.cpython-312.pyc,, +ray/llm/_internal/batch/stages/__pycache__/prepare_image_stage.cpython-312.pyc,, +ray/llm/_internal/batch/stages/__pycache__/serve_deployment_stage.cpython-312.pyc,, +ray/llm/_internal/batch/stages/__pycache__/sglang_engine_stage.cpython-312.pyc,, +ray/llm/_internal/batch/stages/__pycache__/tokenize_stage.cpython-312.pyc,, +ray/llm/_internal/batch/stages/__pycache__/vllm_engine_stage.cpython-312.pyc,, +ray/llm/_internal/batch/stages/base.py,sha256=9i3y_og_SuGsETqLIZSGiPEnMo7Vli5SkktOAWPhVQw,10543 +ray/llm/_internal/batch/stages/chat_template_stage.py,sha256=cJXdDJplKrDQk5kcWklbQjpWZBP8Z637T1_DB30HahA,4966 +ray/llm/_internal/batch/stages/common.py,sha256=Jc-VvDHTnDsdru8wPUKyo8t8aKzu7AvyPi2-jviycvc,822 +ray/llm/_internal/batch/stages/http_request_stage.py,sha256=cXJFE6DRYCskamxGTHrut8YFiPUndp39lCYZ9KQlJOY,7103 +ray/llm/_internal/batch/stages/prepare_image_stage.py,sha256=zzUdh1aAC1uPPfAnXPmpk6adPqLta2nDmUOGU-5GBjU,13412 +ray/llm/_internal/batch/stages/serve_deployment_stage.py,sha256=00c5b7fZ0cf3G3PDfYPVm0KQfeOuKcHpKCK5UlYJEo4,5206 +ray/llm/_internal/batch/stages/sglang_engine_stage.py,sha256=EZX3DhqU9NH_qBU_XIXXqzAnnisoHfoSR3qi4OvGVNc,13870 +ray/llm/_internal/batch/stages/tokenize_stage.py,sha256=HVcP8xKAWSZAHyYSIK1clnH3fE1JtAG66rVKh43u0mM,4171 +ray/llm/_internal/batch/stages/vllm_engine_stage.py,sha256=6s_veT0rr8jUQ5u86ri1etADnTPGxZz4tqdZhYa53Oc,25968 +ray/llm/_internal/batch/utils.py,sha256=d18j-shp38m7X7dF350ntmpobVct44hW4lsIve5hf3o,2295 +ray/llm/_internal/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/common/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/common/__pycache__/base_pydantic.cpython-312.pyc,, +ray/llm/_internal/common/__pycache__/constants.cpython-312.pyc,, +ray/llm/_internal/common/__pycache__/dict_utils.cpython-312.pyc,, +ray/llm/_internal/common/__pycache__/models.cpython-312.pyc,, +ray/llm/_internal/common/base_pydantic.py,sha256=kmHLid-ia3MLf0g-Hd156d_V_6sELsSOlnyUYDm1djc,1162 +ray/llm/_internal/common/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/common/callbacks/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/common/callbacks/__pycache__/base.cpython-312.pyc,, +ray/llm/_internal/common/callbacks/__pycache__/cloud_downloader.cpython-312.pyc,, +ray/llm/_internal/common/callbacks/base.py,sha256=29MQxgeuY363i3C5HUwYYo6HlbcJGzIAiGK0WIlxtGA,5094 +ray/llm/_internal/common/callbacks/cloud_downloader.py,sha256=i3maHRCXZDg7jsrTy3c9cKqGVPHFeB_Ju5w7QXJUl3E,3105 +ray/llm/_internal/common/constants.py,sha256=q-V34_CrtVnD96jA2U1EPnEAExg5S6iOICbQRxxSe8k,391 +ray/llm/_internal/common/dict_utils.py,sha256=LMjB0CUtcowAM9EfkdhGjf9OYDUzvDwkVUCGA_ppSwU,1180 +ray/llm/_internal/common/models.py,sha256=6TPrnddXV-72Q2CKe2dpJjVFJhtow456H_aWXPdI6dk,1303 +ray/llm/_internal/common/observability/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/common/observability/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/common/observability/__pycache__/logging_utils.cpython-312.pyc,, +ray/llm/_internal/common/observability/__pycache__/telemetry_utils.cpython-312.pyc,, +ray/llm/_internal/common/observability/logging/__init__.py,sha256=0Q7iViwmVoSAVfs0Ho9JFTne5As-PZTyq5Gr6O6BGVk,1271 +ray/llm/_internal/common/observability/logging/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/common/observability/logging/__pycache__/setup.cpython-312.pyc,, +ray/llm/_internal/common/observability/logging/setup.py,sha256=mtFF6PDOcB9t_NuKam8f7NcxK1Dmf9-W2lvdsarewjM,815 +ray/llm/_internal/common/observability/logging_utils.py,sha256=SXfhzy_FkVKYbJeMGX-SjdVWv6ulHMuyyMumWiEMHIc,1746 +ray/llm/_internal/common/observability/telemetry_utils.py,sha256=6JEYpc0788SULFFOQ_PzyvKyR1HuupEXqYAbs5MzDEc,1156 +ray/llm/_internal/common/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/common/utils/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/common/utils/__pycache__/cloud_utils.cpython-312.pyc,, +ray/llm/_internal/common/utils/__pycache__/download_utils.cpython-312.pyc,, +ray/llm/_internal/common/utils/__pycache__/import_utils.cpython-312.pyc,, +ray/llm/_internal/common/utils/__pycache__/lora_utils.cpython-312.pyc,, +ray/llm/_internal/common/utils/__pycache__/upload_utils.cpython-312.pyc,, +ray/llm/_internal/common/utils/cloud_utils.py,sha256=GCXSQaKsxFLOUnlFxLjQU16K07Lq4XpwHuX61uc7Z6U,30599 +ray/llm/_internal/common/utils/download_utils.py,sha256=zM9zutIDaUSBgnkMiLpxXYbBUo6cVbwkDuIogydrcfA,11657 +ray/llm/_internal/common/utils/import_utils.py,sha256=i9QClr-9xoIpij4bBI1ZkjHEbFXXBkf1-DDID-L6NQ0,1219 +ray/llm/_internal/common/utils/lora_utils.py,sha256=sFpvR1CzInT2T4b1f5HZ6_xbwqKft1Fu5q9is7njO_Q,7406 +ray/llm/_internal/common/utils/upload_utils.py,sha256=iT5UzaYt0jaaHfGdxhd_9d5SHlYqVV5jGrT4AD_Gcsk,4513 +ray/llm/_internal/serve/__init__.py,sha256=AwPHQA5unCSlY7irtG3KT4RW0S4rkovQqDlXvxwIxFM,456 +ray/llm/_internal/serve/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/__pycache__/constants.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/config_generator/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/__pycache__/generator.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/__pycache__/inputs.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/__pycache__/start.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/base_configs/templates/base_serve_config.yaml,sha256=5pGx3azg40wWHRL5wh8qZVJB_ABDftYFtUla6w-RJes,137 +ray/llm/_internal/serve/config_generator/base_configs/templates/default_deployment_configs.yaml,sha256=XNdzSsuIO7lA1hn38Ox8AB4l_oMwDehCtjrER49IbZI,3186 +ray/llm/_internal/serve/config_generator/generator.py,sha256=fg7qEA8ZEf5puE-xWQbY64aMo7iFb33Bya9Uczw6JuM,4183 +ray/llm/_internal/serve/config_generator/inputs.py,sha256=3TlVFuJ51CmxpRy_X-VEiAmxAppfCwdlEMIox4w-4u0,7190 +ray/llm/_internal/serve/config_generator/start.py,sha256=uMuue6naMHaHNGo_B2kKPv-36aykRgDZUSnsOlr920M,7591 +ray/llm/_internal/serve/config_generator/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/config_generator/utils/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/utils/__pycache__/constants.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/utils/__pycache__/files.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/utils/__pycache__/gpu.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/utils/__pycache__/input_converter.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/utils/__pycache__/models.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/utils/__pycache__/overrides.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/utils/__pycache__/prompt.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/utils/__pycache__/text_completion.cpython-312.pyc,, +ray/llm/_internal/serve/config_generator/utils/constants.py,sha256=DRk8qYwWaiSKBM5265_UPCSKp98231jithTzSu707Zc,516 +ray/llm/_internal/serve/config_generator/utils/files.py,sha256=hHdhrBvxSUjLYh6XuGVt81AXAAsjr2f0tuBhGK6Fxfo,1353 +ray/llm/_internal/serve/config_generator/utils/gpu.py,sha256=lBi6dqIvx1dCI_-SMFmOcSw0vYfuAq3uQ99LFl5PeTY,1294 +ray/llm/_internal/serve/config_generator/utils/input_converter.py,sha256=Da0BHsP6dsg8SWbp8gO_7j0G1IX5__m508ZfHZuIYQc,879 +ray/llm/_internal/serve/config_generator/utils/models.py,sha256=v4HZah_HrgjvtVOFfe7Vj8fMPqu6UQ3HZD1vdlsHOog,1014 +ray/llm/_internal/serve/config_generator/utils/overrides.py,sha256=jWN9SUtXMRwNgQydZRaAEWtHt1m6WQhBC2AQwYVVD1w,3592 +ray/llm/_internal/serve/config_generator/utils/prompt.py,sha256=kaoWrAn6KzOAYN06tMNrEJU4HXWqNy9l1uTKwfgHs4E,465 +ray/llm/_internal/serve/config_generator/utils/text_completion.py,sha256=IbKRpSI8f5p9WAzqIGhFrTVF-ZXcfPu7VqU416THkUk,3626 +ray/llm/_internal/serve/constants.py,sha256=6ehZ37hABkXyg9zulSkZSbGryghqFKw8ZOSPrfi-BJg,3464 +ray/llm/_internal/serve/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/core/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/core/__pycache__/protocol.cpython-312.pyc,, +ray/llm/_internal/serve/core/configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/core/configs/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/core/configs/__pycache__/llm_config.cpython-312.pyc,, +ray/llm/_internal/serve/core/configs/__pycache__/openai_api_models.cpython-312.pyc,, +ray/llm/_internal/serve/core/configs/llm_config.py,sha256=WGEWyxrCdmeuUOmyr4cW8GeI94_D3abNIidjMJd3thM,19554 +ray/llm/_internal/serve/core/configs/openai_api_models.py,sha256=dC8kJ5BQfUWsjlIdgLF51xQ_ukPzKOppXV4PI23Dihs,7004 +ray/llm/_internal/serve/core/engine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/core/engine/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/core/engine/__pycache__/protocol.cpython-312.pyc,, +ray/llm/_internal/serve/core/engine/protocol.py,sha256=bMuJunPAQGY42dyteSbTLBLrHy6p5_xbmXwoSeRv8Ks,6445 +ray/llm/_internal/serve/core/ingress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/core/ingress/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/core/ingress/__pycache__/builder.cpython-312.pyc,, +ray/llm/_internal/serve/core/ingress/__pycache__/ingress.cpython-312.pyc,, +ray/llm/_internal/serve/core/ingress/__pycache__/middleware.cpython-312.pyc,, +ray/llm/_internal/serve/core/ingress/builder.py,sha256=V7w_aCm0AV_FMS6RKKjM9lNjrncxKGP-LtgWjIuYAiQ,5364 +ray/llm/_internal/serve/core/ingress/ingress.py,sha256=MaDGbeI484i3y7s1H5UpSbfNFa3OZXTpMkZFEtHVSo4,24443 +ray/llm/_internal/serve/core/ingress/middleware.py,sha256=8c9FWfjHdHNqCM-hZi2ZaeWYuFyXslNoyHuSD1Pa9BU,6362 +ray/llm/_internal/serve/core/protocol.py,sha256=MIIa4FFxBGTH4MOOFHJ0S7NK8BCBwwzNhndiTyGWmXk,2265 +ray/llm/_internal/serve/core/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/core/server/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/core/server/__pycache__/builder.cpython-312.pyc,, +ray/llm/_internal/serve/core/server/__pycache__/llm_server.cpython-312.pyc,, +ray/llm/_internal/serve/core/server/builder.py,sha256=Mers5DQPXPubyysnLV-p21dJJSvypR4EsRxHR0QzZCY,2657 +ray/llm/_internal/serve/core/server/llm_server.py,sha256=fBdmQulSDrDR37i6CHW7I92pr90ZipjKAISx9BhXWZE,19298 +ray/llm/_internal/serve/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/engines/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/engines/vllm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/engines/vllm/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/engines/vllm/__pycache__/vllm_engine.cpython-312.pyc,, +ray/llm/_internal/serve/engines/vllm/__pycache__/vllm_models.cpython-312.pyc,, +ray/llm/_internal/serve/engines/vllm/kv_transfer/__init__.py,sha256=_1Os5mOVrkhW0HQdIMl-ViWOa6No_mD42-2091izsYU,208 +ray/llm/_internal/serve/engines/vllm/kv_transfer/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/engines/vllm/kv_transfer/__pycache__/base.cpython-312.pyc,, +ray/llm/_internal/serve/engines/vllm/kv_transfer/__pycache__/factory.cpython-312.pyc,, +ray/llm/_internal/serve/engines/vllm/kv_transfer/__pycache__/lmcache.cpython-312.pyc,, +ray/llm/_internal/serve/engines/vllm/kv_transfer/__pycache__/multi_connector.cpython-312.pyc,, +ray/llm/_internal/serve/engines/vllm/kv_transfer/__pycache__/nixl.cpython-312.pyc,, +ray/llm/_internal/serve/engines/vllm/kv_transfer/base.py,sha256=Br15uT0EsUquxto4RTM4oQdY5Mka9uT6H1-Q-byZQGg,2914 +ray/llm/_internal/serve/engines/vllm/kv_transfer/factory.py,sha256=sk9C7ngxJPc0fhqmKVsYRi2gwbqjx_X-ECqnM3DU-Nw,4723 +ray/llm/_internal/serve/engines/vllm/kv_transfer/lmcache.py,sha256=-Ay4yDQcWXqWqIOdDASFcqio069QCEcwnOO6wFxxbO8,2020 +ray/llm/_internal/serve/engines/vllm/kv_transfer/multi_connector.py,sha256=RAQfkNywW1o8ZSmLD-usgO1QNbzjh_u4bJsy103n0IM,1824 +ray/llm/_internal/serve/engines/vllm/kv_transfer/nixl.py,sha256=Zuvxo0S0A71q9JF4Qs0TluAwWLHkVMaEeDnVhMefzJU,2540 +ray/llm/_internal/serve/engines/vllm/vllm_engine.py,sha256=7ttyOzO5yiI6K80rSFW-i-mL6chXwM9rKgZsUVSPLOU,21011 +ray/llm/_internal/serve/engines/vllm/vllm_models.py,sha256=u385LWA9SXTjfjIbcTOIr3zVfIJjnKPCHemq9wlaW4Q,12325 +ray/llm/_internal/serve/observability/__init__.py,sha256=Lfd_5T5dAf3Zh0xeRYyiuzA1ypQ89icxr55tbHRBFmU,597 +ray/llm/_internal/serve/observability/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/observability/logging/__init__.py,sha256=ftp87fUcHpLwFFwp2IHD-JVyLqhrtMwoTocS0wiha2o,1334 +ray/llm/_internal/serve/observability/logging/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/observability/logging/__pycache__/setup.cpython-312.pyc,, +ray/llm/_internal/serve/observability/logging/setup.py,sha256=4MZorCJzVm52I5tcXIsu3fylYIM-mDF-mVL_mjonK3k,897 +ray/llm/_internal/serve/observability/metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/observability/metrics/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/observability/metrics/__pycache__/event_loop_monitoring.cpython-312.pyc,, +ray/llm/_internal/serve/observability/metrics/__pycache__/fast_api_metrics.cpython-312.pyc,, +ray/llm/_internal/serve/observability/metrics/__pycache__/fastapi_utils.cpython-312.pyc,, +ray/llm/_internal/serve/observability/metrics/__pycache__/middleware.cpython-312.pyc,, +ray/llm/_internal/serve/observability/metrics/__pycache__/utils.cpython-312.pyc,, +ray/llm/_internal/serve/observability/metrics/event_loop_monitoring.py,sha256=z75K-iPmh6jImqbEyh5UVZSmVwN8SbbY5VvJzNBHEl0,1853 +ray/llm/_internal/serve/observability/metrics/fast_api_metrics.py,sha256=IllS8z2iDZJo56BWmZPVsEBVG8XVCgq65KSjGy_2vTw,4103 +ray/llm/_internal/serve/observability/metrics/fastapi_utils.py,sha256=zPmnLFhnbf_sBG6pnyI2bDCPyTfYiXg59njnGBH8QN4,773 +ray/llm/_internal/serve/observability/metrics/middleware.py,sha256=zJFfDPKyywSjGLHEHYpqKv6doUz6yyh3UhEWSlOZAbE,5169 +ray/llm/_internal/serve/observability/metrics/utils.py,sha256=V1RUJoMgZFhX8I1aLcT1kZyDNi0isbVHf8GlZ4zPVBE,3948 +ray/llm/_internal/serve/observability/usage_telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/observability/usage_telemetry/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/observability/usage_telemetry/__pycache__/usage.cpython-312.pyc,, +ray/llm/_internal/serve/observability/usage_telemetry/usage.py,sha256=HdR2nvDiUl466xPfe1MTJCukrlNnKNsDPhFGm1qR2YQ,12664 +ray/llm/_internal/serve/routing_policies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/routing_policies/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/routing_policies/prefix_aware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/routing_policies/prefix_aware/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/routing_policies/prefix_aware/__pycache__/prefix_aware_router.cpython-312.pyc,, +ray/llm/_internal/serve/routing_policies/prefix_aware/__pycache__/prefix_tree.cpython-312.pyc,, +ray/llm/_internal/serve/routing_policies/prefix_aware/prefix_aware_router.py,sha256=WcJy7aasUeWIQMzJUe2DSihu4ESxIcYMBHYz2yv5PTI,16827 +ray/llm/_internal/serve/routing_policies/prefix_aware/prefix_tree.py,sha256=Wwi8jdBZpPTvOpX6cmCP2yKuGa3c5fyqb9sFXEzFY80,26397 +ray/llm/_internal/serve/serving_patterns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/serving_patterns/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/serving_patterns/data_parallel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/serving_patterns/data_parallel/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/serving_patterns/data_parallel/__pycache__/builder.cpython-312.pyc,, +ray/llm/_internal/serve/serving_patterns/data_parallel/__pycache__/dp_rank_assigner.cpython-312.pyc,, +ray/llm/_internal/serve/serving_patterns/data_parallel/__pycache__/dp_server.cpython-312.pyc,, +ray/llm/_internal/serve/serving_patterns/data_parallel/builder.py,sha256=11hSji5S9fP5Jsz5bs4uMaFVJPFBl9y3oOucZc57JoM,4979 +ray/llm/_internal/serve/serving_patterns/data_parallel/dp_rank_assigner.py,sha256=RkYHVctZ1kobxBtETFV7W6ThGD7YXpJF5GqOrz2FTPA,4768 +ray/llm/_internal/serve/serving_patterns/data_parallel/dp_server.py,sha256=aUqXmtiwtRBDTk2SQI12Z7d1GcIaT7oCgOVOV8zcpPs,3803 +ray/llm/_internal/serve/serving_patterns/prefill_decode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/serving_patterns/prefill_decode/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/serving_patterns/prefill_decode/__pycache__/builder.cpython-312.pyc,, +ray/llm/_internal/serve/serving_patterns/prefill_decode/__pycache__/pd_server.cpython-312.pyc,, +ray/llm/_internal/serve/serving_patterns/prefill_decode/builder.py,sha256=pTYPM80TgRw0Lr25qEaEAnmoBKVl0scTha6sV_uKfaQ,5908 +ray/llm/_internal/serve/serving_patterns/prefill_decode/pd_server.py,sha256=L6OrAGj_kKK33MvjZ8EoqimtlLP7f6u5dby_SG0SLrc,6416 +ray/llm/_internal/serve/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/_internal/serve/utils/__pycache__/__init__.cpython-312.pyc,, +ray/llm/_internal/serve/utils/__pycache__/batcher.cpython-312.pyc,, +ray/llm/_internal/serve/utils/__pycache__/lora_serve_utils.cpython-312.pyc,, +ray/llm/_internal/serve/utils/__pycache__/node_initialization_utils.cpython-312.pyc,, +ray/llm/_internal/serve/utils/__pycache__/registry.cpython-312.pyc,, +ray/llm/_internal/serve/utils/__pycache__/server_utils.cpython-312.pyc,, +ray/llm/_internal/serve/utils/batcher.py,sha256=3jC6YyWY6_9j-f_RU0ADvAmss2GIvnlU39aBvjI_6rk,3662 +ray/llm/_internal/serve/utils/lora_serve_utils.py,sha256=nma6wbLVDfxPd8r8pKDHamvqbp-5OKyupEI3Yd5ETbs,8425 +ray/llm/_internal/serve/utils/node_initialization_utils.py,sha256=uP1wxikNT9rp_cubCJKPjJ9-aOcfr2WSIs_iQ8lrlXc,2485 +ray/llm/_internal/serve/utils/registry.py,sha256=rJUmUFhwNYjN3-3hcwMagR3k-EgDHh6rFqFJ9GNXlYQ,11148 +ray/llm/_internal/serve/utils/server_utils.py,sha256=gJLcor7Z2qYVNsb9G3nypQztp6b7PpZgktud5DQxqzE,4593 +ray/llm/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/llm/utils/__pycache__/__init__.cpython-312.pyc,, +ray/llm/utils/__pycache__/upload_model.cpython-312.pyc,, +ray/llm/utils/upload_model.py,sha256=ivD9WDKZFvM70KoIO6CXekySpT1ZaYt0CyQ90AZ0Qxg,429 +ray/nightly-wheels.yaml,sha256=39nLCMEmHDM-lPcov1Ea3Hokor2LA76wttE6AlfSUQA,674 +ray/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/remote_function.py,sha256=fYGTjks0u6PCmmMudZnrwjEKObq2PszM95sVwMCbdI8,24448 +ray/rllib/__init__.py,sha256=ATjt_l2VJxJp4AfDj5LgtsWrX6mk_GqgQqnAmLgLeWQ,1638 +ray/rllib/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/__init__.py,sha256=bvTdM5fxujli-X7GLEVX_S6_wtC3Z-khZu96u5ab5fU,959 +ray/rllib/algorithms/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/__pycache__/algorithm.cpython-312.pyc,, +ray/rllib/algorithms/__pycache__/algorithm_config.cpython-312.pyc,, +ray/rllib/algorithms/__pycache__/callbacks.cpython-312.pyc,, +ray/rllib/algorithms/__pycache__/mock.cpython-312.pyc,, +ray/rllib/algorithms/__pycache__/registry.cpython-312.pyc,, +ray/rllib/algorithms/__pycache__/utils.cpython-312.pyc,, +ray/rllib/algorithms/algorithm.py,sha256=LcZZWF0VEEsN708jhnVdupapPTftsQRdDvU-OazOmyw,211499 +ray/rllib/algorithms/algorithm_config.py,sha256=5EjuwEzPb1ArovNVp5tB3L8aAdlo2ME1FEHh8ysY2vo,320090 +ray/rllib/algorithms/appo/__init__.py,sha256=cRAMgsk4tf2GdjrmkiZIFNMBzyvFrpaaYRG_fTlLpd0,343 +ray/rllib/algorithms/appo/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/appo/__pycache__/appo.cpython-312.pyc,, +ray/rllib/algorithms/appo/__pycache__/appo_learner.cpython-312.pyc,, +ray/rllib/algorithms/appo/__pycache__/appo_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/appo/__pycache__/appo_tf_policy.cpython-312.pyc,, +ray/rllib/algorithms/appo/__pycache__/appo_torch_policy.cpython-312.pyc,, +ray/rllib/algorithms/appo/__pycache__/default_appo_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/appo/__pycache__/utils.cpython-312.pyc,, +ray/rllib/algorithms/appo/appo.py,sha256=WbHUSvjW4cAizUY2fn2f5dhWbc91kZcwUUMH1QoxHwk,18156 +ray/rllib/algorithms/appo/appo_learner.py,sha256=HERC2jMwGMIwsNB3T-xpwBmGrgUf6LNJwZaMt3BoPM8,5920 +ray/rllib/algorithms/appo/appo_rl_module.py,sha256=LBeVORs6uprwTLWMDPNSJ_5tAevu4-fJ90zNZnNTjVw,382 +ray/rllib/algorithms/appo/appo_tf_policy.py,sha256=YEz05dtFJ6kLPkz9whmJCrU4TxPanj6v-zVJEvp-yPo,15323 +ray/rllib/algorithms/appo/appo_torch_policy.py,sha256=savsNQs1Ju6iCqFv-E3sUvsrtvpoKTK_yX3Wuv6qI3o,15697 +ray/rllib/algorithms/appo/default_appo_rl_module.py,sha256=p48WSK07YkUkaAfzVkwcQJ4W6SK50uMzHJm7FDx78c4,2253 +ray/rllib/algorithms/appo/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/appo/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/appo/torch/__pycache__/appo_torch_learner.cpython-312.pyc,, +ray/rllib/algorithms/appo/torch/__pycache__/appo_torch_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/appo/torch/__pycache__/default_appo_torch_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/appo/torch/appo_torch_learner.py,sha256=ULqM4fHo-FJ9_Hxd4hDV2rQw0JKYRUC6EXifxoqXuaw,9029 +ray/rllib/algorithms/appo/torch/appo_torch_rl_module.py,sha256=abuZ2e98LGJDEtfgnujy_ei9PN72mFpbr8Ep6w8h7YI,446 +ray/rllib/algorithms/appo/torch/default_appo_torch_rl_module.py,sha256=6WFlKnJ0dHVza2j09F7CHIAkpww7gZTIHVQokRkOXDs,334 +ray/rllib/algorithms/appo/utils.py,sha256=AhyloQ6jjATStPn-sMf7snEfhfLwygHssx3k07pKcPs,6206 +ray/rllib/algorithms/bc/__init__.py,sha256=LF3kh1ZhSwu0p8BNsj4hKfr-fRgqiqb4w3PMgnGz2KA,93 +ray/rllib/algorithms/bc/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/bc/__pycache__/bc.cpython-312.pyc,, +ray/rllib/algorithms/bc/__pycache__/bc_catalog.cpython-312.pyc,, +ray/rllib/algorithms/bc/bc.py,sha256=GJFKa17FaaDnwJs6U2ZJw_I8WQvT_UDlL029h9331kc,3929 +ray/rllib/algorithms/bc/bc_catalog.py,sha256=4fY4g5CZTkbfLP2a8-VAerht7ivvPWF-uXtK9v6firw,4478 +ray/rllib/algorithms/bc/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/bc/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/bc/torch/__pycache__/default_bc_torch_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/bc/torch/default_bc_torch_rl_module.py,sha256=CwERN0owB4q7zZrBOC9kT4p_YX8dz7D6xaXXJh50Hrs,1712 +ray/rllib/algorithms/callbacks.py,sha256=sPg8YDYZL-E-Wg7egRFsQafM9chH7f9feMCSlSpDT1o,235 +ray/rllib/algorithms/cql/__init__.py,sha256=3PzQXLnbdxMQsmVrqdl-RXAVqkLTiOyqkt85ooIk2Zg,209 +ray/rllib/algorithms/cql/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/cql/__pycache__/cql.cpython-312.pyc,, +ray/rllib/algorithms/cql/__pycache__/cql_tf_policy.cpython-312.pyc,, +ray/rllib/algorithms/cql/__pycache__/cql_torch_policy.cpython-312.pyc,, +ray/rllib/algorithms/cql/cql.py,sha256=HN20SUyD6VQ4Qecw490Lguie3qTdElgUg4cfK0s-IoM,14430 +ray/rllib/algorithms/cql/cql_tf_policy.py,sha256=Y7VdiREK9CkIxCtFZp0jjM_OaJlYzHgy4RX71y2O1U0,15740 +ray/rllib/algorithms/cql/cql_torch_policy.py,sha256=xtpzlr4fqEzXBiXp8NDPm6rvxGMuVFtfokwxYf_KLH8,14688 +ray/rllib/algorithms/cql/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/cql/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/cql/torch/__pycache__/cql_torch_learner.cpython-312.pyc,, +ray/rllib/algorithms/cql/torch/__pycache__/default_cql_torch_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/cql/torch/cql_torch_learner.py,sha256=usbYmT-sZ7PMo0QrTNYkG31-kAPjVih4gx97-T22FC0,11774 +ray/rllib/algorithms/cql/torch/default_cql_torch_rl_module.py,sha256=NdvBhGBgvcOz2ppcIHxNp7uaf-W1duYcqO8KtM8T5H8,8421 +ray/rllib/algorithms/dqn/__init__.py,sha256=u7YA5eq0n3QsV_3-SUXbXHNkDCX2seBWA3vnvLlfiU8,272 +ray/rllib/algorithms/dqn/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/dqn/__pycache__/default_dqn_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/dqn/__pycache__/distributional_q_tf_model.cpython-312.pyc,, +ray/rllib/algorithms/dqn/__pycache__/dqn.cpython-312.pyc,, +ray/rllib/algorithms/dqn/__pycache__/dqn_catalog.cpython-312.pyc,, +ray/rllib/algorithms/dqn/__pycache__/dqn_learner.cpython-312.pyc,, +ray/rllib/algorithms/dqn/__pycache__/dqn_tf_policy.cpython-312.pyc,, +ray/rllib/algorithms/dqn/__pycache__/dqn_torch_model.cpython-312.pyc,, +ray/rllib/algorithms/dqn/__pycache__/dqn_torch_policy.cpython-312.pyc,, +ray/rllib/algorithms/dqn/default_dqn_rl_module.py,sha256=G0VYxyDB-kXau85cgEoqejQkUiCHSCBwe0WHC8LKyRo,6736 +ray/rllib/algorithms/dqn/distributional_q_tf_model.py,sha256=x8HWfXoKkMBeG_F9CPY138Hnm_CynSKnjmpRV0IgJs0,8020 +ray/rllib/algorithms/dqn/dqn.py,sha256=ZV-nxI1per_c_juTNnWH4dBxLFoKI0fXvJVfRPzVvz4,37035 +ray/rllib/algorithms/dqn/dqn_catalog.py,sha256=UnWiw70_v202vzvF3ZiaOyGZH3VJnEvfzRwK-s3TGIg,7427 +ray/rllib/algorithms/dqn/dqn_learner.py,sha256=Css9_UoogpIAVZOr8JI97njIwKwkUWJEPqhPRky6vgQ,4654 +ray/rllib/algorithms/dqn/dqn_tf_policy.py,sha256=k6vRboksOU_7RR1pGS29b3aN2cF6UHClvHn3aPAH2Gg,17643 +ray/rllib/algorithms/dqn/dqn_torch_model.py,sha256=nVQwevU5rFJH_je-HxBee6dsQzGSpoKoAs6Np0jeRWs,6865 +ray/rllib/algorithms/dqn/dqn_torch_policy.py,sha256=3_Q8zxR74KgA2kn-ehJhsPrPEGA7s0seoHFcN4VfCWI,17271 +ray/rllib/algorithms/dqn/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/dqn/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/dqn/torch/__pycache__/default_dqn_torch_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/dqn/torch/__pycache__/dqn_torch_learner.cpython-312.pyc,, +ray/rllib/algorithms/dqn/torch/default_dqn_torch_rl_module.py,sha256=VB7j-nxpBQrH0VS2LLfQyduCRafZBervryEvDmss4Y8,13850 +ray/rllib/algorithms/dqn/torch/dqn_torch_learner.py,sha256=XMYF0mGzeFVjZPJm3ER4-ATzjz-mfnJKGMixPpt6DvA,12435 +ray/rllib/algorithms/dreamerv3/__init__.py,sha256=k6k1cCGIdnzCUkAJ85FKlvfZXh1S90XrQy2pdgEfE1A,420 +ray/rllib/algorithms/dreamerv3/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/__pycache__/dreamerv3.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/__pycache__/dreamerv3_catalog.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/__pycache__/dreamerv3_learner.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/__pycache__/dreamerv3_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/dreamerv3.py,sha256=f-1IPmMPMz4E4PBXMY5GBbhFRbFF9N8Gudqp0FXVmqw,33531 +ray/rllib/algorithms/dreamerv3/dreamerv3_catalog.py,sha256=uH2IQgMi-Nhx4ZStOlRx-ebqQ486JZuevzpZq7unu3s,6576 +ray/rllib/algorithms/dreamerv3/dreamerv3_learner.py,sha256=nPld68nZgyGqtVvVzRtx_cCol_aMSZV5M9MEnTElbKE,1051 +ray/rllib/algorithms/dreamerv3/dreamerv3_rl_module.py,sha256=GaMoUonzQpsy5DIm6vhXjJfgScjHHrlZzikpebnThSc,2958 +ray/rllib/algorithms/dreamerv3/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/dreamerv3/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/__pycache__/dreamerv3_torch_learner.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/__pycache__/dreamerv3_torch_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/dreamerv3_torch_learner.py,sha256=wP8peJktV1bV4rUNDXm8SkeU55qOsRWEQ-_EdQxXZRs,39210 +ray/rllib/algorithms/dreamerv3/torch/dreamerv3_torch_rl_module.py,sha256=ldtd_kDcpzvpG1Xc4aYVC6NIedVbYPuOi17SxwZESRA,2982 +ray/rllib/algorithms/dreamerv3/torch/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/dreamerv3/torch/models/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/__pycache__/actor_network.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/__pycache__/critic_network.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/__pycache__/dreamer_model.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/__pycache__/world_model.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/actor_network.py,sha256=mUR0y_zgxS8f_nfHWTnHpfirSyaxcqtgxEe11RtTWQ0,7435 +ray/rllib/algorithms/dreamerv3/torch/models/components/__init__.py,sha256=kA_74iODH1dVV0C_QpFJsvC9HpeIrxStC_gGtydVbMM,1121 +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/cnn_atari.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/continue_predictor.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/conv_transpose_atari.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/dynamics_predictor.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/mlp.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/representation_layer.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/reward_predictor.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/reward_predictor_layer.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/sequence_model.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/__pycache__/vector_decoder.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/torch/models/components/cnn_atari.py,sha256=I1zib7rApl7l9ekLLZ27AEDQ_02-ojT8K88JOcSHf4g,2583 +ray/rllib/algorithms/dreamerv3/torch/models/components/continue_predictor.py,sha256=8TENyO7fbhKq7jxopyJwKgsV4j6AJRzPSTj-ti5d2DE,2383 +ray/rllib/algorithms/dreamerv3/torch/models/components/conv_transpose_atari.py,sha256=R-W08eKPgsVNE_dEC0kEYAAA9G4DMm5zdh2ib-5NUOE,3884 +ray/rllib/algorithms/dreamerv3/torch/models/components/dynamics_predictor.py,sha256=VQNyLd-qiLKHn6JwZsgX-74eQIG6Lx9NUM95R7MaTsQ,2918 +ray/rllib/algorithms/dreamerv3/torch/models/components/mlp.py,sha256=BIx8xduF2qcyuaG0JTDh3GdiF6Bq86RK8fXmPmyEnug,3337 +ray/rllib/algorithms/dreamerv3/torch/models/components/representation_layer.py,sha256=zJTpRUliGahUwsJVVG8Tg9WiG1QxUjf5SCO21Cqj7g0,5821 +ray/rllib/algorithms/dreamerv3/torch/models/components/reward_predictor.py,sha256=vxF-6bPKmmh755XQ0_VqxHngnmPuwcQ4sjalTKcBZEE,3559 +ray/rllib/algorithms/dreamerv3/torch/models/components/reward_predictor_layer.py,sha256=HFLyY6eSQssuFRzSfi79GLAR3spxcMjoxcBFITdU1xs,4482 +ray/rllib/algorithms/dreamerv3/torch/models/components/sequence_model.py,sha256=Sb5Mg_i-JWl8vG9mdfDoW-a7Vh-xamv-PDfN0kwn3wM,5203 +ray/rllib/algorithms/dreamerv3/torch/models/components/vector_decoder.py,sha256=FzTWj3Jhw6G3yCUJXbLKGNEUMKPAiREKLuCQ4Xl8pxQ,2245 +ray/rllib/algorithms/dreamerv3/torch/models/critic_network.py,sha256=wJboupiHNIgypx-cBPeW5UQcO2nquj9PbQJW0mRtQuQ,7376 +ray/rllib/algorithms/dreamerv3/torch/models/dreamer_model.py,sha256=GvC3iI-HKrRX2nj71bHpXpxOzZkm6CnNVKwIz9ow6XE,23272 +ray/rllib/algorithms/dreamerv3/torch/models/world_model.py,sha256=4kd_RrxpDH1Q2z4c9OTID8B7Tqwi-S5h6khVkHjUL0A,19349 +ray/rllib/algorithms/dreamerv3/utils/__init__.py,sha256=Xk4WbtFriNYnQDYpcl3M84NBuq0jribHjdo0lcV4cVU,3574 +ray/rllib/algorithms/dreamerv3/utils/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/utils/__pycache__/add_is_firsts_to_batch.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/utils/__pycache__/debugging.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/utils/__pycache__/summaries.cpython-312.pyc,, +ray/rllib/algorithms/dreamerv3/utils/add_is_firsts_to_batch.py,sha256=LTEJLsJPSQzcI_yrj_snZiskQzjBhkv5RYlhoX0ale4,1112 +ray/rllib/algorithms/dreamerv3/utils/debugging.py,sha256=HC95oVrsVSvR5IQg449ZApXy2CVT8nrjNGvHNXsbgrs,5970 +ray/rllib/algorithms/dreamerv3/utils/summaries.py,sha256=Y_v2OYZXuKAOHDAucBTpfghZ1zPfzdX04eqXzrh8HZc,14781 +ray/rllib/algorithms/impala/__init__.py,sha256=JEKyK70m7GHOMfR6oJnEhsY4vySY5odTj3PihQEtpWk,500 +ray/rllib/algorithms/impala/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/impala/__pycache__/impala.cpython-312.pyc,, +ray/rllib/algorithms/impala/__pycache__/impala_learner.cpython-312.pyc,, +ray/rllib/algorithms/impala/__pycache__/impala_tf_policy.cpython-312.pyc,, +ray/rllib/algorithms/impala/__pycache__/impala_torch_policy.cpython-312.pyc,, +ray/rllib/algorithms/impala/__pycache__/utils.cpython-312.pyc,, +ray/rllib/algorithms/impala/__pycache__/vtrace_tf.cpython-312.pyc,, +ray/rllib/algorithms/impala/__pycache__/vtrace_torch.cpython-312.pyc,, +ray/rllib/algorithms/impala/impala.py,sha256=EpYmHGCcV-GXk2c16uvftyXZoOmUwcVJfNJkt9qK3K4,61773 +ray/rllib/algorithms/impala/impala_learner.py,sha256=L3Ijxf5cLQ8WRd3M_EyNZJexzeBVJWgVHH_unGqNRpM,21534 +ray/rllib/algorithms/impala/impala_tf_policy.py,sha256=gDs1VPtmhNr9JN_wFWUkEBcMHwsbTiy2DTMboTk_ZXo,17546 +ray/rllib/algorithms/impala/impala_torch_policy.py,sha256=LnrBKmDmhGo1b82FyhzKsvtcdDVp0l_StUNwVOLWFI4,16340 +ray/rllib/algorithms/impala/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/impala/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/impala/torch/__pycache__/impala_torch_learner.cpython-312.pyc,, +ray/rllib/algorithms/impala/torch/__pycache__/vtrace_torch_v2.cpython-312.pyc,, +ray/rllib/algorithms/impala/torch/impala_torch_learner.py,sha256=7rpYI54O-PCatj64t3kQwuybOkEJPrLvtZYlqxLiQpo,6534 +ray/rllib/algorithms/impala/torch/vtrace_torch_v2.py,sha256=nKqRfOM18L_UKZ_3lBsiNyYc5APFkGWubTFRZ6p2sHY,7017 +ray/rllib/algorithms/impala/utils.py,sha256=D5-KOiw5o2e4yNO0u07jmgL1Df-9jnIONtNb9HEDqGs,3216 +ray/rllib/algorithms/impala/vtrace_tf.py,sha256=5Pf02sazw11lsQOx1Bw44qOlOnh7mKkT9qySO3u3NTw,16182 +ray/rllib/algorithms/impala/vtrace_torch.py,sha256=UhGszIEBNTFt1oFVoTfY7NtmqQQTIS5TecgDM9O5itQ,14496 +ray/rllib/algorithms/iql/__init__.py,sha256=SvJ1KKwUF3jJ5aOa9AoeQx6nTxqPRk2Vx4xFNfCCKTo,99 +ray/rllib/algorithms/iql/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/iql/__pycache__/default_iql_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/iql/__pycache__/iql.cpython-312.pyc,, +ray/rllib/algorithms/iql/__pycache__/iql_learner.cpython-312.pyc,, +ray/rllib/algorithms/iql/default_iql_rl_module.py,sha256=hjHC6sPJ8hzR_HkXc78Z6pe6AViFMkLos9rchnsW-dg,1579 +ray/rllib/algorithms/iql/iql.py,sha256=PZ-4E7gSwYa4sqdyEyz9_-OPIvNVV6zmIa0-J7FL8pk,8433 +ray/rllib/algorithms/iql/iql_learner.py,sha256=czimy9S3Y7tv5VS0XuXnU_YDwFfGRuvHiXDep1DGOjg,3194 +ray/rllib/algorithms/iql/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/iql/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/iql/torch/__pycache__/default_iql_torch_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/iql/torch/__pycache__/iql_torch_learner.cpython-312.pyc,, +ray/rllib/algorithms/iql/torch/default_iql_torch_rl_module.py,sha256=F-0WjEwRu4_Y8PrEb3M-E8V4PFdjatcAPr5O8CBkyO0,3050 +ray/rllib/algorithms/iql/torch/iql_torch_learner.py,sha256=ex-_W-IwW2g95JEb_KX2RbpN4dblVO9so_xAQZkk7UE,9270 +ray/rllib/algorithms/marwil/__init__.py,sha256=KosRDwnLVcvYDUpxC0x6gzZyz-renwL6XNHFlIlK2hI,401 +ray/rllib/algorithms/marwil/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/marwil/__pycache__/marwil.cpython-312.pyc,, +ray/rllib/algorithms/marwil/__pycache__/marwil_learner.cpython-312.pyc,, +ray/rllib/algorithms/marwil/__pycache__/marwil_tf_policy.cpython-312.pyc,, +ray/rllib/algorithms/marwil/__pycache__/marwil_torch_policy.cpython-312.pyc,, +ray/rllib/algorithms/marwil/marwil.py,sha256=Bklb1vnACpbtR4it6n2hMlS4VPoBYyQKud-aDVB8NbE,20778 +ray/rllib/algorithms/marwil/marwil_learner.py,sha256=0C1MwOzJvKFctGF6nhm7b7yt9oZb342m_syLIQYE0a0,1806 +ray/rllib/algorithms/marwil/marwil_tf_policy.py,sha256=qNRJSbhOsrgP81RhvvVjh7EZKlCtWpNCnCmPACse2WQ,9211 +ray/rllib/algorithms/marwil/marwil_torch_policy.py,sha256=W7xJmMUyuSyBTgPDtLT_f1VgZjD-c88G2CVY8HtMlnU,5350 +ray/rllib/algorithms/marwil/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/marwil/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/marwil/torch/__pycache__/marwil_torch_learner.cpython-312.pyc,, +ray/rllib/algorithms/marwil/torch/marwil_torch_learner.py,sha256=hwqxIz05nXVqPTEF2i14Xt09U2REHYpScBOPgg8JRE8,5555 +ray/rllib/algorithms/mock.py,sha256=fXS0U2qd4Np0xvipMh5NA1gGJ88Sll5TN1vuUhvvCfc,4429 +ray/rllib/algorithms/ppo/__init__.py,sha256=o658U_1tQrYKqAXRxBLSF9rEOmO4larYJnyRGxfHGCo,327 +ray/rllib/algorithms/ppo/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/ppo/__pycache__/default_ppo_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/ppo/__pycache__/ppo.cpython-312.pyc,, +ray/rllib/algorithms/ppo/__pycache__/ppo_catalog.cpython-312.pyc,, +ray/rllib/algorithms/ppo/__pycache__/ppo_learner.cpython-312.pyc,, +ray/rllib/algorithms/ppo/__pycache__/ppo_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/ppo/__pycache__/ppo_tf_policy.cpython-312.pyc,, +ray/rllib/algorithms/ppo/__pycache__/ppo_torch_policy.cpython-312.pyc,, +ray/rllib/algorithms/ppo/default_ppo_rl_module.py,sha256=ysMtQUgrJ5FdNVcZsGPMjkBoP5ZzQrPRbda4bnAYQCE,2543 +ray/rllib/algorithms/ppo/ppo.py,sha256=Z6WRmUgNkKDL1LHIuQ8kQuquY7RQsybHWUVekRqfQOY,23590 +ray/rllib/algorithms/ppo/ppo_catalog.py,sha256=CPRPU7-3ikzEBe2DdUmfiUYBGH2n9DwkOhsRlax1fJE,7752 +ray/rllib/algorithms/ppo/ppo_learner.py,sha256=CYBiZSdIIYkwSLmGsKACjaskVYrTn8Q7g4ZPaIp7kMg,5779 +ray/rllib/algorithms/ppo/ppo_rl_module.py,sha256=cnn9B8z6K78Euik1OaautvyAofuV32VL2kLefgnaYdg,372 +ray/rllib/algorithms/ppo/ppo_tf_policy.py,sha256=KvvGAB2r1eMoMCWcgqAS6pb8lJoKPT18ZAlXhiDgt_g,8775 +ray/rllib/algorithms/ppo/ppo_torch_policy.py,sha256=nRKzIgsUXAQiP4V64JuyXyDcEF5yESSs5EHocCv_JzA,7784 +ray/rllib/algorithms/ppo/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/ppo/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/ppo/torch/__pycache__/default_ppo_torch_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/ppo/torch/__pycache__/ppo_torch_learner.cpython-312.pyc,, +ray/rllib/algorithms/ppo/torch/__pycache__/ppo_torch_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/ppo/torch/default_ppo_torch_rl_module.py,sha256=fb0t_Ve96y15Tgqpe4DcKWEk_qZjxRb9TY5BxrW27XY,3126 +ray/rllib/algorithms/ppo/torch/ppo_torch_learner.py,sha256=7djuhSUn3yLExLjYErnV5qQ2ncANSBwXR5iBwd14tXY,6745 +ray/rllib/algorithms/ppo/torch/ppo_torch_rl_module.py,sha256=cKtR3RbbZ0XxizovEB_TMQwAiYRfJoLCfgDSNuGiHMk,436 +ray/rllib/algorithms/registry.py,sha256=WIjVkVM8gREj5U40Y3I0TS17DlTXgcpnarxOK3px5TQ,4995 +ray/rllib/algorithms/sac/__init__.py,sha256=sMmd9l6UmU6OCWgIFS6RHUdHfs_x4zHPDAL-4H5glBo,272 +ray/rllib/algorithms/sac/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/sac/__pycache__/default_sac_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/sac/__pycache__/sac.cpython-312.pyc,, +ray/rllib/algorithms/sac/__pycache__/sac_catalog.cpython-312.pyc,, +ray/rllib/algorithms/sac/__pycache__/sac_learner.cpython-312.pyc,, +ray/rllib/algorithms/sac/__pycache__/sac_tf_model.cpython-312.pyc,, +ray/rllib/algorithms/sac/__pycache__/sac_tf_policy.cpython-312.pyc,, +ray/rllib/algorithms/sac/__pycache__/sac_torch_model.cpython-312.pyc,, +ray/rllib/algorithms/sac/__pycache__/sac_torch_policy.cpython-312.pyc,, +ray/rllib/algorithms/sac/default_sac_rl_module.py,sha256=kahKJStYgj1zMM-nLWAfiGdNPexDbEyem3sXzPKvOKs,5618 +ray/rllib/algorithms/sac/sac.py,sha256=JXmWSt0MQLHbLPFXc3juOlaYX8h8whdaIx9-iezCt4Q,27799 +ray/rllib/algorithms/sac/sac_catalog.py,sha256=gmszXCJ2fPov6XSWWqnNHIdYquKNrE0Q580r5VRda7Q,12378 +ray/rllib/algorithms/sac/sac_learner.py,sha256=JSgc8SuizgZPW1CZuBBd2f5NWSbJMqqri18op5s6TPY,3983 +ray/rllib/algorithms/sac/sac_tf_model.py,sha256=xXElCRcYXKnFs1E-cS5XAu1d2baOqM8uhUVqj1PC5EM,12619 +ray/rllib/algorithms/sac/sac_tf_policy.py,sha256=wwrH0qq2-m_mmYJ5BHqS2bZW-AQ12WOjiuab71-3t8U,30599 +ray/rllib/algorithms/sac/sac_torch_model.py,sha256=LVqjLpPjvTKl2JGVHqDHyfbKa6LN7uDLJY4WpHYfFTw,13077 +ray/rllib/algorithms/sac/sac_torch_policy.py,sha256=ARZ2mb_N4I40ttNGwMl2z1-815RALLXgHObhDP_xmWo,20098 +ray/rllib/algorithms/sac/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/algorithms/sac/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/algorithms/sac/torch/__pycache__/default_sac_torch_rl_module.cpython-312.pyc,, +ray/rllib/algorithms/sac/torch/__pycache__/sac_torch_learner.cpython-312.pyc,, +ray/rllib/algorithms/sac/torch/default_sac_torch_rl_module.py,sha256=BE39px-zR2pQlnrSe96hK3p4opI9yC2jvo--U9JysI4,11509 +ray/rllib/algorithms/sac/torch/sac_torch_learner.py,sha256=AdOKMJheweuCEfmW3xpTj3cps0YKtO4keUAK-6kWpv8,17621 +ray/rllib/algorithms/utils.py,sha256=TqLbmWtXnU08Qq_sCeX5QdRpzT8Kd2q22B5IbR2895w,8731 +ray/rllib/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/callbacks/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/callbacks/__pycache__/callbacks.cpython-312.pyc,, +ray/rllib/callbacks/__pycache__/utils.cpython-312.pyc,, +ray/rllib/callbacks/callbacks.py,sha256=gAJMt3Gm38z-PlqhR_0JhDEm1c5HdUukQdY_6C5UT4s,30337 +ray/rllib/callbacks/utils.py,sha256=uJN53McKoPdK0-ihJrI_w0-OXwccb7ACKy-8IEgU4UI,5839 +ray/rllib/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/connectors/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/connectors/__pycache__/connector.cpython-312.pyc,, +ray/rllib/connectors/__pycache__/connector_pipeline_v2.cpython-312.pyc,, +ray/rllib/connectors/__pycache__/connector_v2.cpython-312.pyc,, +ray/rllib/connectors/__pycache__/registry.cpython-312.pyc,, +ray/rllib/connectors/__pycache__/util.cpython-312.pyc,, +ray/rllib/connectors/action/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/connectors/action/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/connectors/action/__pycache__/clip.cpython-312.pyc,, +ray/rllib/connectors/action/__pycache__/immutable.cpython-312.pyc,, +ray/rllib/connectors/action/__pycache__/lambdas.cpython-312.pyc,, +ray/rllib/connectors/action/__pycache__/normalize.cpython-312.pyc,, +ray/rllib/connectors/action/__pycache__/pipeline.cpython-312.pyc,, +ray/rllib/connectors/action/clip.py,sha256=reg4vQsOqX6yCUYUROUzkG1k9naw-Sk5qMm492OKt6g,1339 +ray/rllib/connectors/action/immutable.py,sha256=riy8vj5tVllyv_7U0VMXDk8mQh8UDUaKzR6_YGXIEUg,1240 +ray/rllib/connectors/action/lambdas.py,sha256=InePeRbpv0q0n56qcd1ddu5zBoCto4HiapZ10um9O8A,2317 +ray/rllib/connectors/action/normalize.py,sha256=QLyBWQaDcLFp-AW7JjuhhGpCpzTNW6_xabPr-jetmww,1385 +ray/rllib/connectors/action/pipeline.py,sha256=qizQHXFVWlEWjAq34UUc_PHxyTmD1oZEqXjcSbzjNfU,2086 +ray/rllib/connectors/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/connectors/agent/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/connectors/agent/__pycache__/clip_reward.cpython-312.pyc,, +ray/rllib/connectors/agent/__pycache__/env_sampling.cpython-312.pyc,, +ray/rllib/connectors/agent/__pycache__/lambdas.cpython-312.pyc,, +ray/rllib/connectors/agent/__pycache__/mean_std_filter.cpython-312.pyc,, +ray/rllib/connectors/agent/__pycache__/obs_preproc.cpython-312.pyc,, +ray/rllib/connectors/agent/__pycache__/pipeline.cpython-312.pyc,, +ray/rllib/connectors/agent/__pycache__/state_buffer.cpython-312.pyc,, +ray/rllib/connectors/agent/__pycache__/synced_filter.cpython-312.pyc,, +ray/rllib/connectors/agent/__pycache__/view_requirement.cpython-312.pyc,, +ray/rllib/connectors/agent/clip_reward.py,sha256=B4Pjvu3gKSe4lbtAHz6VOx02VhXiEqDdYxeAfZNbTyA,1723 +ray/rllib/connectors/agent/env_sampling.py,sha256=tlvwEMpcXdOtFfKpu0jXfNEbDG7hC9dIgQ8EuM369C0,964 +ray/rllib/connectors/agent/lambdas.py,sha256=yNqe7RA5ing6lAoD7h3WC1w9wKS_tOLxJOycG2Wqeuc,2607 +ray/rllib/connectors/agent/mean_std_filter.py,sha256=Ut9i1R-CrQOM38xj_oArKFMDOY-yeVSv_GuZCx8Ki0Y,6909 +ray/rllib/connectors/agent/obs_preproc.py,sha256=J5QM4BRqCV8yXTz7orY4dff6LLHc1OjYJRQxlF2lhl4,2520 +ray/rllib/connectors/agent/pipeline.py,sha256=yHMg_pAFf0vN4cdvHg5n2mBT7X0GhnPuuLKXD8bMZM0,2380 +ray/rllib/connectors/agent/state_buffer.py,sha256=JY8CocA4ntC3eZepGo1sVov9zW9Kat2xw_6-CPWq7MQ,4361 +ray/rllib/connectors/agent/synced_filter.py,sha256=QKnxWF5dGQ8e0YyLXc-oYuzhcbCVfO6iHAvOz1VdMJg,1938 +ray/rllib/connectors/agent/view_requirement.py,sha256=WEV6J9P8JLl99iaCVLVDW4rNybGVwZT7HO5fsGYozos,5288 +ray/rllib/connectors/common/__init__.py,sha256=0HY939bzZG3sBg-bwTLkdzF4C1nxJXrpZ1i88y5Qs2o,883 +ray/rllib/connectors/common/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/connectors/common/__pycache__/add_observations_from_episodes_to_batch.cpython-312.pyc,, +ray/rllib/connectors/common/__pycache__/add_states_from_episodes_to_batch.cpython-312.pyc,, +ray/rllib/connectors/common/__pycache__/add_time_dim_to_batch_and_zero_pad.cpython-312.pyc,, +ray/rllib/connectors/common/__pycache__/agent_to_module_mapping.cpython-312.pyc,, +ray/rllib/connectors/common/__pycache__/batch_individual_items.cpython-312.pyc,, +ray/rllib/connectors/common/__pycache__/flatten_observations.cpython-312.pyc,, +ray/rllib/connectors/common/__pycache__/frame_stacking.cpython-312.pyc,, +ray/rllib/connectors/common/__pycache__/module_to_agent_unmapping.cpython-312.pyc,, +ray/rllib/connectors/common/__pycache__/numpy_to_tensor.cpython-312.pyc,, +ray/rllib/connectors/common/__pycache__/tensor_to_numpy.cpython-312.pyc,, +ray/rllib/connectors/common/add_observations_from_episodes_to_batch.py,sha256=EyN4p7P025Kp4f5D3rp_Pm8Kpa0fy-hcSu-ZgDABGJA,7027 +ray/rllib/connectors/common/add_states_from_episodes_to_batch.py,sha256=buXP9-wC69G_0MMtxdaCqwlV9cEKLM2TGam8RCNWNFE,14630 +ray/rllib/connectors/common/add_time_dim_to_batch_and_zero_pad.py,sha256=f3ah_VoGmZMcOJFADZCsmXpJOWzaSfG-gaQpMPbtlUg,12363 +ray/rllib/connectors/common/agent_to_module_mapping.py,sha256=yw6G2UBglf7JxDmwVjtTO9MrK_CXaFr7PnsTqwbhBCg,12048 +ray/rllib/connectors/common/batch_individual_items.py,sha256=fm2pRVb0wO75dDjEYTSuTnsoMFKTj9y6dX3PcUKnYdc,8197 +ray/rllib/connectors/common/flatten_observations.py,sha256=F4tWaWho6f85u2Pt7wUyQydguk6G4gVH61RY5mUAC40,17378 +ray/rllib/connectors/common/frame_stacking.py,sha256=f8mIejjrRYndmfiM_DrpLAoUju5O2pKZ0VHmb8BL1S0,5971 +ray/rllib/connectors/common/module_to_agent_unmapping.py,sha256=A7cWhhH9gyN4YENugL_6t1fgQ_ZBq_fWFjtromJPx18,1636 +ray/rllib/connectors/common/numpy_to_tensor.py,sha256=UVAW_NX5CuHyFb63bpMpeP2TgmXfvSjXFvadIbdS7QQ,4632 +ray/rllib/connectors/common/tensor_to_numpy.py,sha256=3q0OGwkDp0bujobTa-4heEYmJTWZOWYf9VyfTJ4kuVc,826 +ray/rllib/connectors/connector.py,sha256=BgI-Q723i2_jsBP3YZ0CtDqMFMVnFXRb_wwOMb16qTk,16043 +ray/rllib/connectors/connector_pipeline_v2.py,sha256=t0We67aS050McCT4XfMwlG3jPPX30e-BQ-55tEsLXlM,15598 +ray/rllib/connectors/connector_v2.py,sha256=8SCFomroZ3i2XbYw42PXyXNtl2-oXrZZOMeWUXp2uHk,45905 +ray/rllib/connectors/env_to_module/__init__.py,sha256=zzF7_WPqAMN6VzaG1x_a18hUc3SfQNZQ1Gd1ZHDje0c,1425 +ray/rllib/connectors/env_to_module/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/connectors/env_to_module/__pycache__/env_to_module_pipeline.cpython-312.pyc,, +ray/rllib/connectors/env_to_module/__pycache__/flatten_observations.cpython-312.pyc,, +ray/rllib/connectors/env_to_module/__pycache__/frame_stacking.cpython-312.pyc,, +ray/rllib/connectors/env_to_module/__pycache__/mean_std_filter.cpython-312.pyc,, +ray/rllib/connectors/env_to_module/__pycache__/observation_preprocessor.cpython-312.pyc,, +ray/rllib/connectors/env_to_module/__pycache__/prev_actions_prev_rewards.cpython-312.pyc,, +ray/rllib/connectors/env_to_module/__pycache__/write_observations_to_episodes.cpython-312.pyc,, +ray/rllib/connectors/env_to_module/env_to_module_pipeline.py,sha256=5c-JWYyRej1N-NThkPgrxU-eq0cVUzsyO3eWi0McY54,1844 +ray/rllib/connectors/env_to_module/flatten_observations.py,sha256=rZ88HvttrIw3Tlk3NyeNNPuq-35e6-qbnvDYSX9-YWo,192 +ray/rllib/connectors/env_to_module/frame_stacking.py,sha256=alN05Z5HNNJIIWicEtWstDVJHgZaw5nXQOyJOm9QI0Q,179 +ray/rllib/connectors/env_to_module/mean_std_filter.py,sha256=UyfmadpoT4vSxx-klkAhrwJVbRxpHI6qUmwr-K3U23Q,10297 +ray/rllib/connectors/env_to_module/observation_preprocessor.py,sha256=W5iWB5mXUqElFJv8tVD9tnOhGShXmMdBP5vWPdIW6w8,7548 +ray/rllib/connectors/env_to_module/prev_actions_prev_rewards.py,sha256=k3BPsH8mjAxvI4G-QlS5xgMaLcnHa0tqHzi_1X9k9Xo,6813 +ray/rllib/connectors/env_to_module/write_observations_to_episodes.py,sha256=5ul-8hjJXWJndOqTscoRAzCcsE_ideYFhh3m3uy1PAw,5510 +ray/rllib/connectors/learner/__init__.py,sha256=MYzhZztMafxYE6kUDXGS4C8za6IecNn2b5MemnKlT8g,1861 +ray/rllib/connectors/learner/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/connectors/learner/__pycache__/add_columns_from_episodes_to_train_batch.cpython-312.pyc,, +ray/rllib/connectors/learner/__pycache__/add_infos_from_episodes_to_train_batch.cpython-312.pyc,, +ray/rllib/connectors/learner/__pycache__/add_next_observations_from_episodes_to_train_batch.cpython-312.pyc,, +ray/rllib/connectors/learner/__pycache__/add_one_ts_to_episodes_and_truncate.cpython-312.pyc,, +ray/rllib/connectors/learner/__pycache__/compute_returns_to_go.cpython-312.pyc,, +ray/rllib/connectors/learner/__pycache__/frame_stacking.cpython-312.pyc,, +ray/rllib/connectors/learner/__pycache__/general_advantage_estimation.cpython-312.pyc,, +ray/rllib/connectors/learner/__pycache__/learner_connector_pipeline.cpython-312.pyc,, +ray/rllib/connectors/learner/add_columns_from_episodes_to_train_batch.py,sha256=7jA3scEICDsKGCBLsU6OPIUcHHks5Z6sWoMDjFzpnWk,6617 +ray/rllib/connectors/learner/add_infos_from_episodes_to_train_batch.py,sha256=Gebvv7mbYMlEvel0svUkFsdUDDdD2fZR1-gSDAFjAeg,1850 +ray/rllib/connectors/learner/add_next_observations_from_episodes_to_train_batch.py,sha256=xEP5cQA1F3ryqxACfMz-cPxL0vpv2Bw7fKDXbL7NhNw,3880 +ray/rllib/connectors/learner/add_one_ts_to_episodes_and_truncate.py,sha256=nJ4u4dmILqgSgN29QuiaPWJgEYdPR01vPmP--5K779Y,6858 +ray/rllib/connectors/learner/compute_returns_to_go.py,sha256=y6Vhid0ypllVNzAFTy6CBaIOfVGpapVKRF_3zwalqJc,2337 +ray/rllib/connectors/learner/frame_stacking.py,sha256=jcBQbWYlOc_v3mbML2HvraM4CPdjUfPM3usLiDLewes,174 +ray/rllib/connectors/learner/general_advantage_estimation.py,sha256=P8IBRgEK6L2cY3A5XSGL4KvL1lKzNZRrk7--qUYAyCY,8785 +ray/rllib/connectors/learner/learner_connector_pipeline.py,sha256=ikS_aaHgRF0bs8xwoYtExe-v-mv35Jk01pUK8QzC4dA,2055 +ray/rllib/connectors/module_to_env/__init__.py,sha256=6Ypf4CYTqOl-jCUJuFgVTbi4oNGao1NzVkhYrdxqIWM,1022 +ray/rllib/connectors/module_to_env/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/connectors/module_to_env/__pycache__/get_actions.cpython-312.pyc,, +ray/rllib/connectors/module_to_env/__pycache__/listify_data_for_vector_env.cpython-312.pyc,, +ray/rllib/connectors/module_to_env/__pycache__/module_to_env_pipeline.cpython-312.pyc,, +ray/rllib/connectors/module_to_env/__pycache__/normalize_and_clip_actions.cpython-312.pyc,, +ray/rllib/connectors/module_to_env/__pycache__/remove_single_ts_time_rank_from_batch.cpython-312.pyc,, +ray/rllib/connectors/module_to_env/__pycache__/unbatch_to_individual_items.cpython-312.pyc,, +ray/rllib/connectors/module_to_env/get_actions.py,sha256=OWntRZ2w-cHAsrrA9KdXJ5xqllTCZUvLKssJ4cn9jZ8,3493 +ray/rllib/connectors/module_to_env/listify_data_for_vector_env.py,sha256=WdmZYbE7LnobeUTvqgpTdH8nmB7-HsIB_u0rsbEzwGQ,3427 +ray/rllib/connectors/module_to_env/module_to_env_pipeline.py,sha256=nWuf4DqMm9aqG5yokvcieC5m4zMMyTL6pKi-d--pooM,207 +ray/rllib/connectors/module_to_env/normalize_and_clip_actions.py,sha256=zYlvwtaJBdwVLyP7fst1FqLd8IjESCtmAgi7g2eYSng,5983 +ray/rllib/connectors/module_to_env/remove_single_ts_time_rank_from_batch.py,sha256=a5zDgP2HNNG_YALlOuCNmr9epnQFifPlm7OmgPqxZgo,2295 +ray/rllib/connectors/module_to_env/unbatch_to_individual_items.py,sha256=4mI2jID2eTs8x-GiL-x0DImlFKZxqXbLp-RNJ8Zb0yE,4829 +ray/rllib/connectors/registry.py,sha256=ENyUmgtqEQ8pbDRuM06LqGZZ6UAdez75TTvguK35xLg,1330 +ray/rllib/connectors/util.py,sha256=ngvSGhghrgjIWunCRe2IgH4tXgki3fMd44x28-hCTFU,6196 +ray/rllib/core/__init__.py,sha256=CycW41iYQHOEmWYtsk5fz3Qf0v-Yt5f_WFl5E4_-ep8,1014 +ray/rllib/core/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/__pycache__/columns.cpython-312.pyc,, +ray/rllib/core/columns.py,sha256=8NatwbdDuECeq4WtTkUJnHrxFRE8ZjerRQSRrTPG8FU,2560 +ray/rllib/core/distribution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/core/distribution/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/distribution/__pycache__/distribution.cpython-312.pyc,, +ray/rllib/core/distribution/distribution.py,sha256=lXWFQJ-MscG_FQlf1WjvBSPVhEGS8UErLoV7osiXGa4,8472 +ray/rllib/core/distribution/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/core/distribution/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/distribution/torch/__pycache__/torch_distribution.cpython-312.pyc,, +ray/rllib/core/distribution/torch/torch_distribution.py,sha256=p-6WYHmXyDeRGMJzk0utAcqWkKd6JKqB0_w-Tb72T34,25074 +ray/rllib/core/learner/__init__.py,sha256=ejJlUBwLQVS_N-sdSQE8QL30Dp7v_lwYvfXEOfsGPjw,163 +ray/rllib/core/learner/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/learner/__pycache__/differentiable_learner.cpython-312.pyc,, +ray/rllib/core/learner/__pycache__/differentiable_learner_config.cpython-312.pyc,, +ray/rllib/core/learner/__pycache__/learner.cpython-312.pyc,, +ray/rllib/core/learner/__pycache__/learner_group.cpython-312.pyc,, +ray/rllib/core/learner/__pycache__/training_data.cpython-312.pyc,, +ray/rllib/core/learner/__pycache__/utils.cpython-312.pyc,, +ray/rllib/core/learner/differentiable_learner.py,sha256=uF4c188VLVRRrBc5481I-k0ucSHXVPaMksdkYaLp0Hk,33046 +ray/rllib/core/learner/differentiable_learner_config.py,sha256=EDcfu2FP9qnv4tcZ6lw6FYvAfY81PvBG9fu4NyWwLao,6196 +ray/rllib/core/learner/learner.py,sha256=_Pp3YNH_ebhVf2GwtJO755F_0rs6Q--4igsp3qLMw0I,73635 +ray/rllib/core/learner/learner_group.py,sha256=m2FCce4KaC6pvXNaDD8c67t9fqlCFxd_hkBVNT11gIE,33314 +ray/rllib/core/learner/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/core/learner/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/learner/torch/__pycache__/torch_differentiable_learner.cpython-312.pyc,, +ray/rllib/core/learner/torch/__pycache__/torch_learner.cpython-312.pyc,, +ray/rllib/core/learner/torch/__pycache__/torch_meta_learner.cpython-312.pyc,, +ray/rllib/core/learner/torch/torch_differentiable_learner.py,sha256=K73D-BvLiTLSoMfRZCOiN0zhRk6mpzrMzpGUJvt96i0,16838 +ray/rllib/core/learner/torch/torch_learner.py,sha256=GCxBwYHjI9Ow8HT8paq9omeEGptFVxpbJS7Vh1LhsjA,28175 +ray/rllib/core/learner/torch/torch_meta_learner.py,sha256=-ki2My3pEM7xDuWHbv5swSShWMm-w3cQHDU8mowX6v4,19597 +ray/rllib/core/learner/training_data.py,sha256=Vd0Qh8Jze6lOK5bTcTD4u9qCQ4b4nLCWV83UhtCZdmU,5666 +ray/rllib/core/learner/utils.py,sha256=3bMHLA19b34GXLhaK2ylrZqk5b5UD4Ikbg8qucOdlUY,1925 +ray/rllib/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/core/models/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/models/__pycache__/base.cpython-312.pyc,, +ray/rllib/core/models/__pycache__/catalog.cpython-312.pyc,, +ray/rllib/core/models/__pycache__/configs.cpython-312.pyc,, +ray/rllib/core/models/base.py,sha256=Joum7y4lkLTruKhHMoH7ohs75CWPicR7ESfmCzL_Hmw,15770 +ray/rllib/core/models/catalog.py,sha256=3ezSQgmGoJmJk2EVs4hJG9eo7AIC3QeyHK6NxDqH9LU,27260 +ray/rllib/core/models/configs.py,sha256=5VPZZ1cnvvymJ9Rn8RtGDf4wpg5MCJQXl2o6um8NSuA,44201 +ray/rllib/core/models/specs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/core/models/specs/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/models/specs/__pycache__/specs_base.cpython-312.pyc,, +ray/rllib/core/models/specs/__pycache__/specs_dict.cpython-312.pyc,, +ray/rllib/core/models/specs/specs_base.py,sha256=Mi8o1EQSA2qVLiwgr5iOpSZMIcTj0P_t3WO3OzfArpw,513 +ray/rllib/core/models/specs/specs_dict.py,sha256=-UmstaoPV8tXv8pDLwDqnaj1XwGW6SVxLPhwaWZ9g3w,192 +ray/rllib/core/models/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/core/models/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/models/torch/__pycache__/base.cpython-312.pyc,, +ray/rllib/core/models/torch/__pycache__/encoder.cpython-312.pyc,, +ray/rllib/core/models/torch/__pycache__/heads.cpython-312.pyc,, +ray/rllib/core/models/torch/__pycache__/primitives.cpython-312.pyc,, +ray/rllib/core/models/torch/__pycache__/utils.cpython-312.pyc,, +ray/rllib/core/models/torch/base.py,sha256=weQnNJnJSyXcEBysl2Wwrv8MaJJVLPxluj403bWxn2U,3076 +ray/rllib/core/models/torch/encoder.py,sha256=rvh_ojKeAVu7M6yn1rcZcWJRDa8qOhNfTyf6cdvBlaU,10095 +ray/rllib/core/models/torch/heads.py,sha256=NnlQ4eY5zgWpIbKYmz3MKXGNzAeC2Wzyx2c8h_DvyNQ,8905 +ray/rllib/core/models/torch/primitives.py,sha256=E-IeH4NDbAqzjSmAE15Rfq4mSIWbhbblMx7nXBF60is,23303 +ray/rllib/core/models/torch/utils.py,sha256=DgVUlmSoFImdyFcIHTeYNLIoRXbWrDEJyxjCCIF2Bio,2945 +ray/rllib/core/rl_module/__init__.py,sha256=nzYrjbTZa8LszAGTX3Wm3cavEvbH7I4kOfQg9r5dVMs,1622 +ray/rllib/core/rl_module/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/rl_module/__pycache__/default_model_config.cpython-312.pyc,, +ray/rllib/core/rl_module/__pycache__/multi_rl_module.cpython-312.pyc,, +ray/rllib/core/rl_module/__pycache__/rl_module.cpython-312.pyc,, +ray/rllib/core/rl_module/apis/__init__.py,sha256=E1q3XJj4BxUuCpDuGUBuLHjmLc87WpkJMoCC04C9xk0,603 +ray/rllib/core/rl_module/apis/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/rl_module/apis/__pycache__/inference_only_api.cpython-312.pyc,, +ray/rllib/core/rl_module/apis/__pycache__/q_net_api.cpython-312.pyc,, +ray/rllib/core/rl_module/apis/__pycache__/self_supervised_loss_api.cpython-312.pyc,, +ray/rllib/core/rl_module/apis/__pycache__/target_network_api.cpython-312.pyc,, +ray/rllib/core/rl_module/apis/__pycache__/value_function_api.cpython-312.pyc,, +ray/rllib/core/rl_module/apis/inference_only_api.py,sha256=Qs0wI2675Ee9_TLUCOFXZqEQG_ddksIYsrJ6iqwaYF4,2610 +ray/rllib/core/rl_module/apis/q_net_api.py,sha256=-oSCciHQFLl5XTU0b4tQnZ7QuGw7jaf7KoFBcTVL7Nc,2045 +ray/rllib/core/rl_module/apis/self_supervised_loss_api.py,sha256=ypnFcqHYCd7fVx0jAmR-vfyoCY45z_t-LHqsTVV5feY,2321 +ray/rllib/core/rl_module/apis/target_network_api.py,sha256=uNheo4LC-Gx2fIR-DzctvxQbARGUrk7EvMPpisDGb98,2075 +ray/rllib/core/rl_module/apis/value_function_api.py,sha256=uEPU8bY3LpCs_YTM9KypthGi14Ab354cxrHJiBLEUSE,1286 +ray/rllib/core/rl_module/default_model_config.py,sha256=NbB8DKxNaq0l8oO1dLOWY22N0te1rS0dVvSA57p1c9A,11231 +ray/rllib/core/rl_module/multi_rl_module.py,sha256=QkZnJQZNKpWdiepYz8l-1IMMwtB2Yp9nU5FjsTVSfdM,32691 +ray/rllib/core/rl_module/rl_module.py,sha256=7rr4Jr1sWTs9NJhtgYaKBDf286C76hahpSAYB-jdRiI,33864 +ray/rllib/core/rl_module/torch/__init__.py,sha256=F_S_bYNHu1gUzXdAZ-C8Yjy0uf57CawBuSWT30VOrbs,72 +ray/rllib/core/rl_module/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/rl_module/torch/__pycache__/torch_compile_config.cpython-312.pyc,, +ray/rllib/core/rl_module/torch/__pycache__/torch_rl_module.cpython-312.pyc,, +ray/rllib/core/rl_module/torch/torch_compile_config.py,sha256=_QEt38oVKr9H3mNeY2HuPcMioGQIuwDCNKYu2A1iNPA,1601 +ray/rllib/core/rl_module/torch/torch_rl_module.py,sha256=aqxQpEdfXkUKtCl1TL-12BvPhneQVxY_50Xutxavurk,12126 +ray/rllib/core/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/core/testing/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/testing/__pycache__/bc_algorithm.cpython-312.pyc,, +ray/rllib/core/testing/__pycache__/testing_learner.cpython-312.pyc,, +ray/rllib/core/testing/bc_algorithm.py,sha256=0B8iOQA6h1-bidMS9OAYmvFkZ0noKyvZE4fVMi_HAr0,1437 +ray/rllib/core/testing/testing_learner.py,sha256=bqufAp-PdGJGOynMLWDoVf2B1kU3FqtL7M0psmlRGrg,2464 +ray/rllib/core/testing/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/core/testing/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/core/testing/torch/__pycache__/bc_learner.cpython-312.pyc,, +ray/rllib/core/testing/torch/__pycache__/bc_module.cpython-312.pyc,, +ray/rllib/core/testing/torch/bc_learner.py,sha256=uoD0aDwiUA69JMWO1oH3KO81K1GKBkEqji0VBzJTnvM,1158 +ray/rllib/core/testing/torch/bc_module.py,sha256=NX8mx4g1FBQQ0rFsgsbi7VXOzDfpMuX0OVCu4-B0JqU,5092 +ray/rllib/env/__init__.py,sha256=G-VtTYSTc4mem4K2UkTIPouG_FTxmUIPnyAVvUl2xCE,1190 +ray/rllib/env/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/env/__pycache__/base_env.cpython-312.pyc,, +ray/rllib/env/__pycache__/env_context.cpython-312.pyc,, +ray/rllib/env/__pycache__/env_errors.cpython-312.pyc,, +ray/rllib/env/__pycache__/env_runner.cpython-312.pyc,, +ray/rllib/env/__pycache__/env_runner_group.cpython-312.pyc,, +ray/rllib/env/__pycache__/external_env.cpython-312.pyc,, +ray/rllib/env/__pycache__/external_multi_agent_env.cpython-312.pyc,, +ray/rllib/env/__pycache__/multi_agent_env.cpython-312.pyc,, +ray/rllib/env/__pycache__/multi_agent_env_runner.cpython-312.pyc,, +ray/rllib/env/__pycache__/multi_agent_episode.cpython-312.pyc,, +ray/rllib/env/__pycache__/policy_client.cpython-312.pyc,, +ray/rllib/env/__pycache__/remote_base_env.cpython-312.pyc,, +ray/rllib/env/__pycache__/single_agent_env_runner.cpython-312.pyc,, +ray/rllib/env/__pycache__/single_agent_episode.cpython-312.pyc,, +ray/rllib/env/__pycache__/tcp_client_inference_env_runner.cpython-312.pyc,, +ray/rllib/env/__pycache__/vector_env.cpython-312.pyc,, +ray/rllib/env/base_env.py,sha256=7rMnXOo5yTxa0s1qost83p7XSLw73xbvf3Dy7G9RKGQ,15999 +ray/rllib/env/env_context.py,sha256=tQ41rRw9foahTDEtlbT4z6O9ydKupuIRxyoysnEwB48,5195 +ray/rllib/env/env_errors.py,sha256=SNHx_ht8G9iouGyPvHmGk6sGY25SZnYc7NJ0AtbszLA,810 +ray/rllib/env/env_runner.py,sha256=2T5MccMUaWf869IAj0uMI5UKRtxRgQL4x3THsC8p7mI,12065 +ray/rllib/env/env_runner_group.py,sha256=mMs0DDJEWKr54UAeKMcgWooHovur1jqybqzpIUMrG4M,55240 +ray/rllib/env/external/__init__.py,sha256=hpeSihAJU7O0ywP65gEi9NYtGmqesXmn5jBujb-WPxU,189 +ray/rllib/env/external/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/env/external/__pycache__/env_runner_server_for_external_inference.cpython-312.pyc,, +ray/rllib/env/external/__pycache__/rllink.cpython-312.pyc,, +ray/rllib/env/external/env_runner_server_for_external_inference.py,sha256=8p50rth-31oeiSAm2eJ40NSvb_JtmALVRH_lFBRhlv0,14138 +ray/rllib/env/external/rllink.py,sha256=TxiOGI6oM9fwvPqmKv04IBu8kQQaYnt8sDWx2YQi3BE,3514 +ray/rllib/env/external_env.py,sha256=qq8Kt-vIPKaQ10ke9exkqr5eaWz6c66J69Fg9MOOL5E,17118 +ray/rllib/env/external_multi_agent_env.py,sha256=6UxU6zYgD9r3unVzkp3aLsdLa9OCyw46s-i4MTC3mFM,5487 +ray/rllib/env/multi_agent_env.py,sha256=g3R5fB0l8dpgNCrsPe0Mi7qt3u6tm_tRyfyp7m592L0,30904 +ray/rllib/env/multi_agent_env_runner.py,sha256=EDBP341ZN43Gmsb1tgSrt0Yra8pnsj0fLduDB1f25ZI,45001 +ray/rllib/env/multi_agent_episode.py,sha256=dT-RVZNjKFrx1DvbpYFeiwXAPSpw24fDABtca0grAg8,135039 +ray/rllib/env/policy_client.py,sha256=Gj3QokHMo4q9dHtQSwcL-SPVtzBRdwAR2U6RxyHoX7g,10722 +ray/rllib/env/remote_base_env.py,sha256=XCRrSc-OCoFN2BRZjwO6PGVZ9dYUhnfIs0UmplUpm1w,19249 +ray/rllib/env/single_agent_env_runner.py,sha256=xD7ieTBjcjTSnCnNM-yZF-PGEUTTDKxwom1Y6UcmPuw,37311 +ray/rllib/env/single_agent_episode.py,sha256=_-36nmx8VBnGp3Z7F3-H2XkIIeQTQPg5nEpyTvibU-g,86900 +ray/rllib/env/tcp_client_inference_env_runner.py,sha256=Oa_KTIiyk5d5SZfdHDjudbs7LCxbtLRZQh6YbwGhY08,202 +ray/rllib/env/utils/__init__.py,sha256=dxUIiYZhKoOtvUd_VGaraLlDK8T5tOsGcWBiigY8Ixw,3452 +ray/rllib/env/utils/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/env/utils/__pycache__/external_env_protocol.cpython-312.pyc,, +ray/rllib/env/utils/__pycache__/infinite_lookback_buffer.cpython-312.pyc,, +ray/rllib/env/utils/external_env_protocol.py,sha256=74ELTKX8s736Szte99cthyzL05CtCy99t1G3vk8GNQ8,248 +ray/rllib/env/utils/infinite_lookback_buffer.py,sha256=GBR1FDYtUnXie9dmmVAX0UT_52WX8tROCmvZkXbbKEc,28798 +ray/rllib/env/vector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/env/vector/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/env/vector/__pycache__/registration.cpython-312.pyc,, +ray/rllib/env/vector/__pycache__/sync_vector_multi_agent_env.cpython-312.pyc,, +ray/rllib/env/vector/__pycache__/vector_multi_agent_env.cpython-312.pyc,, +ray/rllib/env/vector/registration.py,sha256=h10vKANqte3ZpO8bl9QMtXqJ3UtmvboFpgNGaGBJLsk,3130 +ray/rllib/env/vector/sync_vector_multi_agent_env.py,sha256=cOq2WMJ2u8ju3XTGQ4FjWUChrC4uHgPMR3cJi3TWgAU,8329 +ray/rllib/env/vector/vector_multi_agent_env.py,sha256=CEO_sTt4f7rXoDycsnT5n9ql0dSbgVsOidfX7jIBmpc,2910 +ray/rllib/env/vector_env.py,sha256=78bemt8rnmYg7FQbrKgvq7E37PzvnRZiUm5vpSchuoE,20231 +ray/rllib/env/wrappers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/env/wrappers/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/env/wrappers/__pycache__/atari_wrappers.cpython-312.pyc,, +ray/rllib/env/wrappers/__pycache__/dm_control_wrapper.cpython-312.pyc,, +ray/rllib/env/wrappers/__pycache__/dm_env_wrapper.cpython-312.pyc,, +ray/rllib/env/wrappers/__pycache__/group_agents_wrapper.cpython-312.pyc,, +ray/rllib/env/wrappers/__pycache__/multi_agent_env_compatibility.cpython-312.pyc,, +ray/rllib/env/wrappers/__pycache__/open_spiel.cpython-312.pyc,, +ray/rllib/env/wrappers/__pycache__/pettingzoo_env.cpython-312.pyc,, +ray/rllib/env/wrappers/__pycache__/unity3d_env.cpython-312.pyc,, +ray/rllib/env/wrappers/atari_wrappers.py,sha256=weOM-tmhsPIdRj5ou2b3H9_EmLxVn5OG-4Ffp92RBho,14215 +ray/rllib/env/wrappers/dm_control_wrapper.py,sha256=pG3f1JIB3osp8WJ37gYg8EJ5S8OGPjugnDR8sLtIfxo,8025 +ray/rllib/env/wrappers/dm_env_wrapper.py,sha256=gNOp_LbC8LLetIVOlYkkxssKYoIxhs5mtPFX0T5ubss,2792 +ray/rllib/env/wrappers/group_agents_wrapper.py,sha256=jtcPFpnHvmLa7AQdUq2x3L7nIbxNQaODl5tgRC_IiRI,5905 +ray/rllib/env/wrappers/multi_agent_env_compatibility.py,sha256=QbE2eYjEMW3ilX-TGA853Lxplu5sIRrYCcaZxCsJPXw,2574 +ray/rllib/env/wrappers/open_spiel.py,sha256=A_IHeSgo8s54j2wR_qrIp-ECv4teVqygRcFmUE9w-EI,4645 +ray/rllib/env/wrappers/pettingzoo_env.py,sha256=44p-MLpOMA17TffXee_Fubsx2Y0FBk0OcD5RdJKieuE,6645 +ray/rllib/env/wrappers/unity3d_env.py,sha256=c3mRF1p75cknZObVUkCq4iwkASjpPkjrQxl9cfF-FeY,12475 +ray/rllib/evaluation/__init__.py,sha256=-kNFm8XxIj28AVeKThXQZzTQ8fIXUkITq5Q8xIXtcjA,634 +ray/rllib/evaluation/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/evaluation/__pycache__/env_runner_v2.cpython-312.pyc,, +ray/rllib/evaluation/__pycache__/episode_v2.cpython-312.pyc,, +ray/rllib/evaluation/__pycache__/metrics.cpython-312.pyc,, +ray/rllib/evaluation/__pycache__/observation_function.cpython-312.pyc,, +ray/rllib/evaluation/__pycache__/postprocessing.cpython-312.pyc,, +ray/rllib/evaluation/__pycache__/rollout_worker.cpython-312.pyc,, +ray/rllib/evaluation/__pycache__/sample_batch_builder.cpython-312.pyc,, +ray/rllib/evaluation/__pycache__/sampler.cpython-312.pyc,, +ray/rllib/evaluation/__pycache__/worker_set.cpython-312.pyc,, +ray/rllib/evaluation/collectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/evaluation/collectors/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/evaluation/collectors/__pycache__/agent_collector.cpython-312.pyc,, +ray/rllib/evaluation/collectors/__pycache__/sample_collector.cpython-312.pyc,, +ray/rllib/evaluation/collectors/__pycache__/simple_list_collector.cpython-312.pyc,, +ray/rllib/evaluation/collectors/agent_collector.py,sha256=7MB4-8aRaWtndNZtFtPSf3aX8scKB8tg7408kWsEB_0,30961 +ray/rllib/evaluation/collectors/sample_collector.py,sha256=AUO0APgrL1Tgik2xSv-HHoDCizJ-iTY3eXgov5sSE3c,12275 +ray/rllib/evaluation/collectors/simple_list_collector.py,sha256=F5KmCE33jAORZvTCdiGFUHck4GocMdGfGozcLilHY_8,28704 +ray/rllib/evaluation/env_runner_v2.py,sha256=tPlWViNuo-TfOUQNwyChVTGBww2ocE6jrCEt6ovCroA,51737 +ray/rllib/evaluation/episode_v2.py,sha256=-OiPspi_Pw-6B5JQp8SmajaZDwrpeRvItmiqrlMRxWU,14949 +ray/rllib/evaluation/metrics.py,sha256=J4J_7FtGNvlQEV-c_wjb-6GNfcobq9c8lwkG3sFSV-A,9160 +ray/rllib/evaluation/observation_function.py,sha256=i4ekjaVFsGpTvDLlgmrPhz7Qgp89vHlzUwr4gvojX50,3182 +ray/rllib/evaluation/postprocessing.py,sha256=LlXqo-ExksaR6FrG0MOi8puy6jTpQmDXf90Ukol_k5k,12131 +ray/rllib/evaluation/rollout_worker.py,sha256=EX0e_uf07Z6gXgQoG-ACvDtYq8dsC3PUPPABfmHWfw0,80405 +ray/rllib/evaluation/sample_batch_builder.py,sha256=SX3EMwCMOsho5QM72E-TzWDeB38YgpZmJHDZTwwK-OA,10039 +ray/rllib/evaluation/sampler.py,sha256=3M-FwgRguI09bSFpjKvbRccY0l6YreSgLvmfR1eNEdc,9718 +ray/rllib/evaluation/worker_set.py,sha256=5CXIP_HJR_R1thYSWNBgP_G4PfTGyVpTNMXSOmAd1cM,239 +ray/rllib/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/__pycache__/centralized_critic.cpython-312.pyc,, +ray/rllib/examples/__pycache__/compute_adapted_gae_on_postprocess_trajectory.cpython-312.pyc,, +ray/rllib/examples/__pycache__/quadx_waypoints.cpython-312.pyc,, +ray/rllib/examples/__pycache__/replay_buffer_api.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/_old_api_stack/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/__pycache__/attention_net_supervised.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/__pycache__/parametric_actions_cartpole.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/__pycache__/parametric_actions_cartpole_embeddings_learnt_by_model.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/attention_net_supervised.py,sha256=9fSZRjDflHFtBinKRbeZZD3t_ux_q-yDLpEqx9Z8zjs,2380 +ray/rllib/examples/_old_api_stack/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/_old_api_stack/models/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/action_mask_model.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/autoregressive_action_dist.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/autoregressive_action_model.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/centralized_critic_models.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/custom_loss_model.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/fast_model.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/mobilenet_v2_encoder.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/mobilenet_v2_with_lstm_models.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/neural_computer.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/parametric_actions_model.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/shared_weights_model.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/__pycache__/simple_rpg_model.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/models/action_mask_model.py,sha256=zxjWk-VI7yMeB1SPSFevJoxGj8Oy6E3AK9YTJMkrsuE,4292 +ray/rllib/examples/_old_api_stack/models/autoregressive_action_dist.py,sha256=E9PQmUCzS5WDwIDShr_jF8HvMf51j4gUx3DCRro1x54,4994 +ray/rllib/examples/_old_api_stack/models/autoregressive_action_model.py,sha256=1UvpXHaw9N3ZNioNtwHx7f5crKjZL39IRgrB9muYjQ8,5697 +ray/rllib/examples/_old_api_stack/models/centralized_critic_models.py,sha256=nNzuY1l3_VYhJlSeJQbK5Pq6rNL8Ya5s5Z1DiVSKoKg,6916 +ray/rllib/examples/_old_api_stack/models/custom_loss_model.py,sha256=EU2xUr15XoRocFBAzqy_tAmQM1HXUdUF45_SHw-6TkI,5362 +ray/rllib/examples/_old_api_stack/models/fast_model.py,sha256=MPHPdJCawlICl-sL4Ux59BNjMNyzLuMib3KAvCzFzSI,2840 +ray/rllib/examples/_old_api_stack/models/mobilenet_v2_encoder.py,sha256=IBRFeN04mh4BUujFL6r_rHD6PtZfDllkSv-J6CmHOe4,1659 +ray/rllib/examples/_old_api_stack/models/mobilenet_v2_with_lstm_models.py,sha256=XD8NYd1UktRwDGWBgPqOgoAmzGqP8LJ-OGQoTU_zyqs,5790 +ray/rllib/examples/_old_api_stack/models/neural_computer.py,sha256=WxvhRtPFpu8IHvdsjbfMPaX11c6XFsxINxiqPvZYzGc,8596 +ray/rllib/examples/_old_api_stack/models/parametric_actions_model.py,sha256=DvU4aYxHJexBF9DHXxKLEFGw_oM6sXqvMhUV0CTgH3A,7332 +ray/rllib/examples/_old_api_stack/models/shared_weights_model.py,sha256=xH4R44E1T-rL2sJ6bpAfMwXuEcnDSQu2yD0UatDWO5k,6969 +ray/rllib/examples/_old_api_stack/models/simple_rpg_model.py,sha256=X_dewCnA_RzTi0TjlLwEe6k243MdndjgvRGwwFNuo3M,2632 +ray/rllib/examples/_old_api_stack/parametric_actions_cartpole.py,sha256=gq3NXEkKEVYjTVZQLNMuHEXu7uNtPBAADww2ttvUznk,3817 +ray/rllib/examples/_old_api_stack/parametric_actions_cartpole_embeddings_learnt_by_model.py,sha256=dHqdNw5VPWfVm52G8vAhcO-wRq3TkJDY6KycPNcC-SE,3530 +ray/rllib/examples/_old_api_stack/policy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/_old_api_stack/policy/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/policy/__pycache__/cliff_walking_wall_policy.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/policy/__pycache__/random_policy.cpython-312.pyc,, +ray/rllib/examples/_old_api_stack/policy/cliff_walking_wall_policy.py,sha256=et8-PQDcjX1iGrM-yQeCOKujFMZXdz0apJh6tskxcag,4181 +ray/rllib/examples/_old_api_stack/policy/random_policy.py,sha256=sI5pNR7ODv1qOs-Nsx4YUFUtIRjMueuMqIopnIh1NZc,3214 +ray/rllib/examples/actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/actions/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/actions/__pycache__/autoregressive_actions.cpython-312.pyc,, +ray/rllib/examples/actions/__pycache__/custom_action_distribution.cpython-312.pyc,, +ray/rllib/examples/actions/__pycache__/nested_action_spaces.cpython-312.pyc,, +ray/rllib/examples/actions/autoregressive_actions.py,sha256=O8gVVJjh5UNsNX-p3GdOou7KmiAQQTMLvBvbfIRT90M,4196 +ray/rllib/examples/actions/custom_action_distribution.py,sha256=7M4EJuIYpEt65oZi2jG5rOQbKut6DeMiq5xjVJFVQtk,4631 +ray/rllib/examples/actions/nested_action_spaces.py,sha256=JAmVbZz0H0_tmKki9PgMqkGuZnZ5y3xC1B_jCthbWP4,2696 +ray/rllib/examples/algorithms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/algorithms/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/algorithms/__pycache__/appo_custom_algorithm_w_shared_data_actor.cpython-312.pyc,, +ray/rllib/examples/algorithms/__pycache__/maml_lr_supervised_learning.cpython-312.pyc,, +ray/rllib/examples/algorithms/__pycache__/vpg_custom_algorithm.cpython-312.pyc,, +ray/rllib/examples/algorithms/appo_custom_algorithm_w_shared_data_actor.py,sha256=Y__8Ygl4nMySl7GRLz6pWSAAscjZbGlOXld9DXrxW3k,7729 +ray/rllib/examples/algorithms/classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/algorithms/classes/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/algorithms/classes/__pycache__/appo_w_shared_data_actor.cpython-312.pyc,, +ray/rllib/examples/algorithms/classes/__pycache__/maml_lr_differentiable_learner.cpython-312.pyc,, +ray/rllib/examples/algorithms/classes/__pycache__/maml_lr_differentiable_rlm.cpython-312.pyc,, +ray/rllib/examples/algorithms/classes/__pycache__/maml_lr_meta_learner.cpython-312.pyc,, +ray/rllib/examples/algorithms/classes/__pycache__/vpg.cpython-312.pyc,, +ray/rllib/examples/algorithms/classes/appo_w_shared_data_actor.py,sha256=6PEiDVoCZdrBFYZRr_IlpIZ9nvH_paxFomT1PFYyrEs,3076 +ray/rllib/examples/algorithms/classes/maml_lr_differentiable_learner.py,sha256=pux1WGky2moUlpdJKwCHVPybEe_KHLUNWMDjzFBwBLo,1100 +ray/rllib/examples/algorithms/classes/maml_lr_differentiable_rlm.py,sha256=-Ex5zdGX5MtAKa4AaKk2iF7OS6IR6DUlWgI1c_M7YiE,1566 +ray/rllib/examples/algorithms/classes/maml_lr_meta_learner.py,sha256=Gc8Rzr4U-d_wTNJ6EKN--IsQjZz_j0bk5eNkR5ZQgQo,1357 +ray/rllib/examples/algorithms/classes/vpg.py,sha256=MNY4HgB7u1CXKgLYO0kZ5_xeSQ0EN9MRxSz1F6h1UGQ,6842 +ray/rllib/examples/algorithms/maml_lr_supervised_learning.py,sha256=2ZgMRNe5X-xxm506MXI01w3Z2B0atPVs0VSZZNWLMq8,16320 +ray/rllib/examples/algorithms/vpg_custom_algorithm.py,sha256=q2LPRqbbAS1hxKDUvrtxrFy4kBnCr0wm4M8KpjSD2IQ,5078 +ray/rllib/examples/centralized_critic.py,sha256=Mf-vA8Bo12QgQa2RJdGjNM_CmJQeslzFLQ-MHQdJ4GE,11236 +ray/rllib/examples/checkpoints/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/checkpoints/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/checkpoints/__pycache__/cartpole_dqn_export.cpython-312.pyc,, +ray/rllib/examples/checkpoints/__pycache__/change_config_during_training.cpython-312.pyc,, +ray/rllib/examples/checkpoints/__pycache__/checkpoint_by_custom_criteria.cpython-312.pyc,, +ray/rllib/examples/checkpoints/__pycache__/continue_training_from_checkpoint.cpython-312.pyc,, +ray/rllib/examples/checkpoints/__pycache__/onnx_torch_lstm.cpython-312.pyc,, +ray/rllib/examples/checkpoints/__pycache__/restore_1_of_n_agents_from_checkpoint.cpython-312.pyc,, +ray/rllib/examples/checkpoints/cartpole_dqn_export.py,sha256=wFnCGffhCAXttUJl8YxpODTVuhrfO4802iIFc5GJ_Gw,2612 +ray/rllib/examples/checkpoints/change_config_during_training.py,sha256=_6Z88Q3w6uJH1kNaiC5w2qv_jw_VWo3lbeT0cMGc3R0,11867 +ray/rllib/examples/checkpoints/checkpoint_by_custom_criteria.py,sha256=fbq67nuF8UrypXFMFPYeN8zVK7e8X92j0m0P229hOtA,6381 +ray/rllib/examples/checkpoints/continue_training_from_checkpoint.py,sha256=iPPndR-0otj1d3fUDd3dnVloRyFXEgS4sD_SvfmvL5s,12512 +ray/rllib/examples/checkpoints/onnx_torch_lstm.py,sha256=wc5nQWQo7x7it9kTTO6zqTvpj4NJf5a7n-N_l_XZ9OA,3994 +ray/rllib/examples/checkpoints/restore_1_of_n_agents_from_checkpoint.py,sha256=4vMQIZ3wgPKMDwQTPYzAppUMK_E0AlW2lWGbipQaHXU,6147 +ray/rllib/examples/compute_adapted_gae_on_postprocess_trajectory.py,sha256=DId710piai4aqRNEwuJAIUJVNYOMkcY7QesZROV2XYo,5695 +ray/rllib/examples/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/connectors/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/connectors/__pycache__/count_based_curiosity.cpython-312.pyc,, +ray/rllib/examples/connectors/__pycache__/euclidian_distance_based_curiosity.cpython-312.pyc,, +ray/rllib/examples/connectors/__pycache__/flatten_observations_dict_space.cpython-312.pyc,, +ray/rllib/examples/connectors/__pycache__/frame_stacking.cpython-312.pyc,, +ray/rllib/examples/connectors/__pycache__/mean_std_filtering.cpython-312.pyc,, +ray/rllib/examples/connectors/__pycache__/multi_agent_observation_preprocessor.cpython-312.pyc,, +ray/rllib/examples/connectors/__pycache__/prev_actions_prev_rewards.cpython-312.pyc,, +ray/rllib/examples/connectors/__pycache__/single_agent_observation_preprocessor.cpython-312.pyc,, +ray/rllib/examples/connectors/classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/connectors/classes/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/connectors/classes/__pycache__/add_other_agents_row_index_to_xy_pos.cpython-312.pyc,, +ray/rllib/examples/connectors/classes/__pycache__/count_based_curiosity.cpython-312.pyc,, +ray/rllib/examples/connectors/classes/__pycache__/euclidian_distance_based_curiosity.cpython-312.pyc,, +ray/rllib/examples/connectors/classes/__pycache__/protobuf_cartpole_observation_decoder.cpython-312.pyc,, +ray/rllib/examples/connectors/classes/add_other_agents_row_index_to_xy_pos.py,sha256=VDIucsfy3jdgZypS-v_rDZsFzhbvVRrId6DeJIJzYHo,4829 +ray/rllib/examples/connectors/classes/count_based_curiosity.py,sha256=NUZByloofJqEBF-vvK7dpAWkNVh6ohuR5f6YzH_yR_8,3622 +ray/rllib/examples/connectors/classes/euclidian_distance_based_curiosity.py,sha256=6DCmXP4Uq2eAh3jYQCXR-Gxf2Zl_Ak6KytwWh8ytGEo,4959 +ray/rllib/examples/connectors/classes/protobuf_cartpole_observation_decoder.py,sha256=tbB8gKbWcMKNIbl7aULubi3F4cfya_qfcotDfQgGwq4,2930 +ray/rllib/examples/connectors/count_based_curiosity.py,sha256=8OgGt6d7QegkHNueG9XtyP290KQcrmXxplj_ju-TMok,424 +ray/rllib/examples/connectors/euclidian_distance_based_curiosity.py,sha256=Eur41PMN8HgpWnSOx7wiQKoWPwNKbaCFz7QJ6o1kVH8,437 +ray/rllib/examples/connectors/flatten_observations_dict_space.py,sha256=qYkHIEl5J_R0fWmKDh7pvEhaUyn9d16bFE1y9LXO410,6202 +ray/rllib/examples/connectors/frame_stacking.py,sha256=i2nvgDvU2G0VaxhdC2F1gYc0i2j6cMaiAcpVBVdMw7k,9494 +ray/rllib/examples/connectors/mean_std_filtering.py,sha256=GaKS9-AceUcWeqLGGI0wSJNW1GD0Q8FUd9SFvmRt94g,8179 +ray/rllib/examples/connectors/multi_agent_observation_preprocessor.py,sha256=djcRYMAXRbCB_OctM57gNvUtBwTcFwKHLjLOcRePbV8,6076 +ray/rllib/examples/connectors/prev_actions_prev_rewards.py,sha256=WzQp_jyKytG_QHN34GRIc7ZO_G7oFE2JiwB-rZxKxNM,7182 +ray/rllib/examples/connectors/single_agent_observation_preprocessor.py,sha256=1kr0_-kBOIfIAxflYlzKl_2N1ZHvoEQmRSEXCC0Z6dc,6480 +ray/rllib/examples/curiosity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/curiosity/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/curiosity/__pycache__/count_based_curiosity.cpython-312.pyc,, +ray/rllib/examples/curiosity/__pycache__/euclidian_distance_based_curiosity.cpython-312.pyc,, +ray/rllib/examples/curiosity/__pycache__/intrinsic_curiosity_model_based_curiosity.cpython-312.pyc,, +ray/rllib/examples/curiosity/count_based_curiosity.py,sha256=1LqDMpjstYAxAaRBhK9EAs_BfpFpQ40eNfDXIU61r_Y,5505 +ray/rllib/examples/curiosity/euclidian_distance_based_curiosity.py,sha256=8YI_HmEx1Mlu11NTpBIxbWoOxxmP-j7B3F90rGCU2Mc,5278 +ray/rllib/examples/curiosity/intrinsic_curiosity_model_based_curiosity.py,sha256=TROfAg0Ty1F0mJA290KmtYQvQxqWsvX-8TtssZs5p3M,11160 +ray/rllib/examples/curriculum/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/curriculum/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/curriculum/__pycache__/curriculum_learning.cpython-312.pyc,, +ray/rllib/examples/curriculum/__pycache__/pong_curriculum_learning.cpython-312.pyc,, +ray/rllib/examples/curriculum/curriculum_learning.py,sha256=F8hDSorp5y6zx1OCdGQsVt2AnRjZfO3vijIHB3pgBZw,9246 +ray/rllib/examples/curriculum/pong_curriculum_learning.py,sha256=39KVVT87dcpyBhYg9gd8JtayoqhVrVuM_ipAUhy4hyY,10629 +ray/rllib/examples/debugging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/debugging/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/debugging/__pycache__/deterministic_sampling_and_training.cpython-312.pyc,, +ray/rllib/examples/debugging/deterministic_sampling_and_training.py,sha256=5ffzEbz_xQ0JUlmGJwD5qWMEThhECSbtfCZjiSEpXqM,5743 +ray/rllib/examples/envs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/envs/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/envs/__pycache__/agents_act_in_sequence.cpython-312.pyc,, +ray/rllib/examples/envs/__pycache__/agents_act_simultaneously.cpython-312.pyc,, +ray/rllib/examples/envs/__pycache__/async_gym_env_vectorization.cpython-312.pyc,, +ray/rllib/examples/envs/__pycache__/custom_env_render_method.cpython-312.pyc,, +ray/rllib/examples/envs/__pycache__/custom_gym_env.cpython-312.pyc,, +ray/rllib/examples/envs/__pycache__/env_connecting_to_rllib_w_tcp_client.cpython-312.pyc,, +ray/rllib/examples/envs/__pycache__/env_rendering_and_recording.cpython-312.pyc,, +ray/rllib/examples/envs/__pycache__/env_w_protobuf_observations.cpython-312.pyc,, +ray/rllib/examples/envs/__pycache__/greyscale_env.cpython-312.pyc,, +ray/rllib/examples/envs/__pycache__/unity3d_env_local.cpython-312.pyc,, +ray/rllib/examples/envs/agents_act_in_sequence.py,sha256=qurqZlxZUhCeqe7xbHpDmwBTMltH0jVieQXFhr2noWg,3385 +ray/rllib/examples/envs/agents_act_simultaneously.py,sha256=Ty0tihYuWxTpcheORbXL837pzFI31XEyY9SNqm9quaY,4262 +ray/rllib/examples/envs/async_gym_env_vectorization.py,sha256=nPv_JWL3DGAvFE7hcwthmLv5jqf1oMPDA9atSXNuLNg,5170 +ray/rllib/examples/envs/classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/envs/classes/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/action_mask_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/cartpole_crashing.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/cartpole_sparse_rewards.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/cartpole_with_dict_observation_space.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/cartpole_with_large_observation_space.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/cartpole_with_protobuf_observation_space.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/cliff_walking_wall_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/correlated_actions_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/d4rl_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/debug_counter_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/deterministic_envs.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/dm_control_suite.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/env_using_remote_actor.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/env_with_subprocess.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/fast_image_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/gpu_requiring_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/look_and_push.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/memory_leaking_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/mock_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/nested_space_repeat_after_me_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/parametric_actions_cartpole.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/random_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/recommender_system_envs_with_recsim.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/repeat_after_me_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/repeat_initial_obs_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/simple_corridor.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/simple_rpg.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/six_room_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/stateless_cartpole.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/stateless_pendulum.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/transformed_action_space_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/__pycache__/windy_maze_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/action_mask_env.py,sha256=rZE2878ZeBs6XrOpAUGVlnvIp60nLyZCe2pmLgC9xVE,1490 +ray/rllib/examples/envs/classes/cartpole_crashing.py,sha256=7HJjHJgWZNDQgkdLE8gacmIMzLpuJ_rs4o3bN6PSkrg,6977 +ray/rllib/examples/envs/classes/cartpole_sparse_rewards.py,sha256=xmtI3owJVNFNtb1aHS_7FpQ_fqO-BnK-WQ2BPucLQYE,1597 +ray/rllib/examples/envs/classes/cartpole_with_dict_observation_space.py,sha256=KpoW8wJaEX8hcrIemlB2wAwGWhf4wsy7ESB4_brIE9k,2923 +ray/rllib/examples/envs/classes/cartpole_with_large_observation_space.py,sha256=4-5BTDLSJvKrevSOKm3Dvo3R4dpY8Vyvvfz8E0GWvDc,2483 +ray/rllib/examples/envs/classes/cartpole_with_protobuf_observation_space.py,sha256=bf8ahgwgTNyzJkrKTKJMIFa5RFctISQPhew9AH3gskc,2973 +ray/rllib/examples/envs/classes/cliff_walking_wall_env.py,sha256=BwNZqzH5DoMON2sDw22NbMSYeAc-fz6htPN5y7QatPI,2251 +ray/rllib/examples/envs/classes/correlated_actions_env.py,sha256=wgBYAcrGNJePJpbPhnp6hWmz5yn9M7IvAanjSfYk2rc,3236 +ray/rllib/examples/envs/classes/d4rl_env.py,sha256=6SZrXLhOwa_nY0aya4PP_c1agrVzCmUpRxxQ7zlEsxk,863 +ray/rllib/examples/envs/classes/debug_counter_env.py,sha256=0OVRScn2PsO1Ah6Bsj2_goQOq67faSP7abZTgLMDGHM,3091 +ray/rllib/examples/envs/classes/deterministic_envs.py,sha256=K9O5MbtVvZhw_3V2iNACPpBVRhDpvvpjiax3Z7EDrVo,296 +ray/rllib/examples/envs/classes/dm_control_suite.py,sha256=aBzx4GRrm84h5_UXmjb1r0OqdjTE8vDK3jAs43YzBWo,2888 +ray/rllib/examples/envs/classes/env_using_remote_actor.py,sha256=IZb3e57yQM-D2SmKDlyY0w28tjD0DXLD1l6qOBKmysc,2159 +ray/rllib/examples/envs/classes/env_with_subprocess.py,sha256=nix-R5-SFpy23E0nM8zfcn6pH49WCMsKUeVxnnbmKVo,1326 +ray/rllib/examples/envs/classes/fast_image_env.py,sha256=-Bt-OLU2v_hRiRNUdoGSy_QWkQTba8BJQFgWinNS-ec,574 +ray/rllib/examples/envs/classes/gpu_requiring_env.py,sha256=E-fEKBeMgCraCZ0L_oTR2pvleHuSThulFiJRZeKvB-w,1447 +ray/rllib/examples/envs/classes/look_and_push.py,sha256=QE3gFWKJ5nQs69nYTyaSOrB8o9TUL4VH4ItVz7IGnak,2121 +ray/rllib/examples/envs/classes/memory_leaking_env.py,sha256=vcPpsMDp7DJqBzaCaaokw9M3wFVCxuMRfRmgjKk6W50,915 +ray/rllib/examples/envs/classes/mock_env.py,sha256=hG9xMdxRRUvwffTQ7stN6C6ii4iVVhILJ6ZTeg1rq7k,7675 +ray/rllib/examples/envs/classes/multi_agent/__init__.py,sha256=2arIETeJ_Z8DFPEazmtgM810VRVIaGhe3cYcfgImdQk,1236 +ray/rllib/examples/envs/classes/multi_agent/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/__pycache__/bandit_envs_discrete.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/__pycache__/bandit_envs_recommender_system.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/__pycache__/double_row_corridor_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/__pycache__/guess_the_number_game.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/__pycache__/pettingzoo_chess.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/__pycache__/pettingzoo_connect4.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/__pycache__/rock_paper_scissors.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/__pycache__/tic_tac_toe.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/__pycache__/two_step_game.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/bandit_envs_discrete.py,sha256=_tbzLtFQzCgNfQtyNQCE8k1-JAvwh7k8OCwrtjOrEi0,6413 +ray/rllib/examples/envs/classes/multi_agent/bandit_envs_recommender_system.py,sha256=AKCZK9bojawxFVAt3Ckd-xgYRx66nScHJ_VHWpRyGbg,8857 +ray/rllib/examples/envs/classes/multi_agent/double_row_corridor_env.py,sha256=rmIdrEkB-YlTp2J40V--6uzO0895FKAxyAPzs_mlHgE,4930 +ray/rllib/examples/envs/classes/multi_agent/footsies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/envs/classes/multi_agent/footsies/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/footsies/__pycache__/encoder.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/footsies/__pycache__/fixed_rlmodules.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/footsies/__pycache__/footsies_env.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/footsies/__pycache__/utils.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/footsies/encoder.py,sha256=NZ31aS0JmQmyPYMh2CNRgAy-LK7uwdC2Jy83qyBt7ys,8564 +ray/rllib/examples/envs/classes/multi_agent/footsies/fixed_rlmodules.py,sha256=JafeRxlecMEWV_2Xv6RD87lUX15EbPzGLbpVSCh91E8,1675 +ray/rllib/examples/envs/classes/multi_agent/footsies/footsies_env.py,sha256=kCntk4GZNP9eIfv9QcLfUVH0Z9Y38z-W2wi_dpI0ev0,10329 +ray/rllib/examples/envs/classes/multi_agent/footsies/game/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/envs/classes/multi_agent/footsies/game/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/footsies/game/__pycache__/constants.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/footsies/game/__pycache__/footsies_binary.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/footsies/game/__pycache__/footsies_game.cpython-312.pyc,, +ray/rllib/examples/envs/classes/multi_agent/footsies/game/constants.py,sha256=CwG4hAVlqemFlGA0YF6cz5WeIMMU6MoBb9e8UyNT21U,5220 +ray/rllib/examples/envs/classes/multi_agent/footsies/game/footsies_binary.py,sha256=d10mne_vBkuOSGrO-_beNwtXPN4mWKT7Xq_W_H5YW6c,8071 +ray/rllib/examples/envs/classes/multi_agent/footsies/game/footsies_game.py,sha256=dU_f1wX23pnVQmNY8crRuMEQKGGFicVE2UgMNw4THxI,4780 +ray/rllib/examples/envs/classes/multi_agent/footsies/utils.py,sha256=PW__bqNMsw43VMOWvGYmgg33bjGbTV-eZaE4L93jl6A,12711 +ray/rllib/examples/envs/classes/multi_agent/guess_the_number_game.py,sha256=b_rmhpkHvkhiadtBT12vkKtwAlBd1KNsEpINwoZH6Bs,3536 +ray/rllib/examples/envs/classes/multi_agent/pettingzoo_chess.py,sha256=2jEqGp84huv72uruHMW7-_pUv6gVl6PUpcSTrE7o84E,6917 +ray/rllib/examples/envs/classes/multi_agent/pettingzoo_connect4.py,sha256=c_mOfPOLwqDwMz4mPbNjEmkXIuxNy42tPnBlIybTw1Y,6333 +ray/rllib/examples/envs/classes/multi_agent/rock_paper_scissors.py,sha256=maKcKnebqam01cvHqJ1nbWDICZw3EmnTWg8UUS1cuzk,4473 +ray/rllib/examples/envs/classes/multi_agent/tic_tac_toe.py,sha256=FbcvCE99N-1jQZMV2nQHCfOpXNwjxjYLFeLP6QKFtEQ,4977 +ray/rllib/examples/envs/classes/multi_agent/two_step_game.py,sha256=W_PO8CgOetNhMR-G2e7GvWlaTowZQSfGbvmg7DptyG4,4558 +ray/rllib/examples/envs/classes/nested_space_repeat_after_me_env.py,sha256=655ozyfhzwCH0I4CYvM5hmlqRay0wwvFEMzv9m0lWQc,1866 +ray/rllib/examples/envs/classes/parametric_actions_cartpole.py,sha256=SPt7neS5SjZzFHLNePINmYqkIIcnwM7bPgFXAU4QHMA,5484 +ray/rllib/examples/envs/classes/random_env.py,sha256=DBMzN_jOaGLpAgqwR7BXxRpGsFLJ0XuzISX5xlsJBZg,4630 +ray/rllib/examples/envs/classes/recommender_system_envs_with_recsim.py,sha256=luoRsLqqAO4aFyl5Q0qpB_JSKCebQQ5JS0_b0Oz4-3M,3386 +ray/rllib/examples/envs/classes/repeat_after_me_env.py,sha256=jc8rqSB42wpt3pquGLrbJ1DWvuDjY_-XHE4eQunmfAw,1591 +ray/rllib/examples/envs/classes/repeat_initial_obs_env.py,sha256=vLk2k_rRSW6PUVpCb40WtYCVMMvwph0dRu2CePsmGkk,905 +ray/rllib/examples/envs/classes/simple_corridor.py,sha256=Qhs5jE_9rark-fldueAnq90FSI3NOqpw3ajxocROKHk,1414 +ray/rllib/examples/envs/classes/simple_rpg.py,sha256=rdqxPtq8m-WecNeAkJPkv5b9pPXmHk8LBveiidySI_E,1564 +ray/rllib/examples/envs/classes/six_room_env.py,sha256=TnjvJTCspLjn0lqO3VBOZDLRXwTNHxtjHBpjPikQ6Qw,11253 +ray/rllib/examples/envs/classes/stateless_cartpole.py,sha256=RpGWjICUQHbiejxIrnTAsZN_GyKas3aAUnKr8OWfZTM,1332 +ray/rllib/examples/envs/classes/stateless_pendulum.py,sha256=yZPE4LtFRY5-Kb3_A8P0mQZ59j-KJbVFTCGjG9r3SQs,1279 +ray/rllib/examples/envs/classes/transformed_action_space_env.py,sha256=L0-21X4ZepivYn3s0Lp143JohzyuK20OgdlWkg3X8HY,2044 +ray/rllib/examples/envs/classes/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/envs/classes/utils/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/envs/classes/utils/__pycache__/cartpole_observations_proto.cpython-312.pyc,, +ray/rllib/examples/envs/classes/utils/__pycache__/dummy_external_client.cpython-312.pyc,, +ray/rllib/examples/envs/classes/utils/cartpole_observations_proto.py,sha256=AETSgAf9oAjtaCibpmMebm3P6CmeTuqftBFj7Bk1iYg,1213 +ray/rllib/examples/envs/classes/utils/dummy_external_client.py,sha256=y0JOgJpPxc9BbG7fWDH3v19Ww4qwxloKB3UcsrQbKbo,4081 +ray/rllib/examples/envs/classes/windy_maze_env.py,sha256=kiYjWo60mOc1Cw8N7D_617PoxdYY_OAmdHkI6FcGpdQ,5762 +ray/rllib/examples/envs/custom_env_render_method.py,sha256=9YoGhEV7FhUN0iP6m9nnYA21YziaJhWNlNT9GTgKR9M,7876 +ray/rllib/examples/envs/custom_gym_env.py,sha256=BoDgb4TE8MNE3hJsdrL1B4m-R64NsH8V-TwhC0m637g,5894 +ray/rllib/examples/envs/env_connecting_to_rllib_w_tcp_client.py,sha256=QVum1vPJ6rsb-az10LISHK_b1onKW2J9u4V4WSsFQWA,5358 +ray/rllib/examples/envs/env_rendering_and_recording.py,sha256=W18qR0bYA3vAiAAC8PIHjLuatDaLIUvDibu9_-z7-rY,12980 +ray/rllib/examples/envs/env_w_protobuf_observations.py,sha256=p4cymf56BR-8fn54IJUGGAJrhnpcACk-G8KnF_DNjZQ,3542 +ray/rllib/examples/envs/greyscale_env.py,sha256=XrMpD4Mxot0IR8s8KLuXydOmH93xolbUXW7sZPyxMjY,3831 +ray/rllib/examples/envs/unity3d_env_local.py,sha256=s4REX41URVkePZ4WhgPh73-95OIq0n6lUcBWKDgYFYQ,6636 +ray/rllib/examples/evaluation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/evaluation/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/evaluation/__pycache__/custom_evaluation.cpython-312.pyc,, +ray/rllib/examples/evaluation/__pycache__/evaluation_parallel_to_training.cpython-312.pyc,, +ray/rllib/examples/evaluation/custom_evaluation.py,sha256=rVcSHtQkfC5EkpUW3i_QGJ9ZM9ti6cyOB3zmme69PKg,9843 +ray/rllib/examples/evaluation/evaluation_parallel_to_training.py,sha256=XmALgDQDr1WdlnKxlE0gH0yqcqRHPIGtaRdyl93UC9U,11661 +ray/rllib/examples/fault_tolerance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/fault_tolerance/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/fault_tolerance/__pycache__/crashing_and_stalling_env.cpython-312.pyc,, +ray/rllib/examples/fault_tolerance/crashing_and_stalling_env.py,sha256=1XUA6ax1h0eD5RR1hDgvOQbIGTWsVEnzNMsmGsuGUDw,7678 +ray/rllib/examples/gpus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/gpus/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/gpus/__pycache__/float16_training_and_inference.cpython-312.pyc,, +ray/rllib/examples/gpus/__pycache__/fractional_gpus_per_learner.cpython-312.pyc,, +ray/rllib/examples/gpus/__pycache__/gpus_on_env_runners.cpython-312.pyc,, +ray/rllib/examples/gpus/__pycache__/mixed_precision_training_float16_inference.cpython-312.pyc,, +ray/rllib/examples/gpus/float16_training_and_inference.py,sha256=sEZPJFC7CkpIlEtkCWkS6qFwOd0H8qGie1f2mBLPueQ,10239 +ray/rllib/examples/gpus/fractional_gpus_per_learner.py,sha256=m4_YSpj-lWkJcHLOkfpumkP4TlRvo0B9pbYvmh_sV_Y,5253 +ray/rllib/examples/gpus/gpus_on_env_runners.py,sha256=66lhLK2wTWrKOxeBMPJ1bPXXbkV5nOwEE9L8oCrFrhI,3282 +ray/rllib/examples/gpus/mixed_precision_training_float16_inference.py,sha256=5nQcLqod2FJ0nagqlLbcvx6fVu5AVGm8B1zd8CteUlg,6733 +ray/rllib/examples/hierarchical/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/hierarchical/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/hierarchical/__pycache__/hierarchical_training.cpython-312.pyc,, +ray/rllib/examples/hierarchical/hierarchical_training.py,sha256=uCYc3o3ljiagFtbm-oTRpf7OnAeuNefF3989blJCKDQ,7296 +ray/rllib/examples/inference/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/inference/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/inference/__pycache__/policy_inference_after_training.cpython-312.pyc,, +ray/rllib/examples/inference/__pycache__/policy_inference_after_training_w_connector.cpython-312.pyc,, +ray/rllib/examples/inference/__pycache__/policy_inference_after_training_with_attention.cpython-312.pyc,, +ray/rllib/examples/inference/__pycache__/policy_inference_after_training_with_lstm.cpython-312.pyc,, +ray/rllib/examples/inference/policy_inference_after_training.py,sha256=E1M_mWPNcWALE0WlhzmFRxWie5QvB30n3QmdaVJ5KvA,9357 +ray/rllib/examples/inference/policy_inference_after_training_w_connector.py,sha256=Z5SFCGW4vfQ9Gz_C2p_m9YLRzExabtCoYRWwzkEslf4,12539 +ray/rllib/examples/inference/policy_inference_after_training_with_attention.py,sha256=ksCInBUVZGHM_Nmq8mlteRv5H9jphzP_8EZMC54qfNY,6276 +ray/rllib/examples/inference/policy_inference_after_training_with_lstm.py,sha256=GdLsSL-mNAvvxRDBxVF5rt7YTNHE0Sp5LDNJJgeMh4Y,5660 +ray/rllib/examples/learners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/learners/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/learners/__pycache__/ppo_with_custom_loss_fn.cpython-312.pyc,, +ray/rllib/examples/learners/__pycache__/ppo_with_torch_lr_schedulers.cpython-312.pyc,, +ray/rllib/examples/learners/__pycache__/separate_vf_lr_and_optimizer.cpython-312.pyc,, +ray/rllib/examples/learners/classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/learners/classes/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/learners/classes/__pycache__/custom_ppo_loss_fn_learner.cpython-312.pyc,, +ray/rllib/examples/learners/classes/__pycache__/intrinsic_curiosity_learners.cpython-312.pyc,, +ray/rllib/examples/learners/classes/__pycache__/separate_vf_lr_and_optimizer_learner.cpython-312.pyc,, +ray/rllib/examples/learners/classes/__pycache__/vpg_torch_learner.cpython-312.pyc,, +ray/rllib/examples/learners/classes/__pycache__/vpg_torch_learner_shared_optimizer.cpython-312.pyc,, +ray/rllib/examples/learners/classes/custom_ppo_loss_fn_learner.py,sha256=Ag_y2yPhGuuMflRvX0Bj42bgqHXhaWb433OEyv_V9_0,1774 +ray/rllib/examples/learners/classes/intrinsic_curiosity_learners.py,sha256=JERqD3eh387wCf31IpdytJC-VyI6kXGfDzm1wPwaN5E,6613 +ray/rllib/examples/learners/classes/separate_vf_lr_and_optimizer_learner.py,sha256=-4F9EZY4g_gf0LnBk9pFv29zAgehbqKFJc0jmxWCtB0,3772 +ray/rllib/examples/learners/classes/vpg_torch_learner.py,sha256=3CD5GX_4vsVnyZy9fjONMd9OhEvpzOuUSBKMqE6ubis,2835 +ray/rllib/examples/learners/classes/vpg_torch_learner_shared_optimizer.py,sha256=mkxTTPdYaZCFLh5Ao-lWxYD1Id7rz-vYk7zxm4mNBHs,1331 +ray/rllib/examples/learners/ppo_with_custom_loss_fn.py,sha256=kkaPLQd3DqHG2oH0UFaN9TxmAjcY8y_wyi5s7PF0gKk,5693 +ray/rllib/examples/learners/ppo_with_torch_lr_schedulers.py,sha256=tcT7snWb5hq0BqJT-lBIsDYGNJPE0eBJvOpJCm-Vac0,7846 +ray/rllib/examples/learners/separate_vf_lr_and_optimizer.py,sha256=00akyzLIr4-zYZiFfMYCG8DhSiKVl9iQ4UsIa0bMJMU,5788 +ray/rllib/examples/metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/metrics/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/metrics/__pycache__/custom_metrics_in_algorithm_training_step.cpython-312.pyc,, +ray/rllib/examples/metrics/__pycache__/custom_metrics_in_env_runners.cpython-312.pyc,, +ray/rllib/examples/metrics/custom_metrics_in_algorithm_training_step.py,sha256=GnaTtanRRaauE3Ltr16u7XGcm4n29isemUOlisF-QMo,4157 +ray/rllib/examples/metrics/custom_metrics_in_env_runners.py,sha256=S_C7TOD4ek7d4HveOBaIGB42CLDMkUoK6BEh1alryRg,14344 +ray/rllib/examples/multi_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/multi_agent/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/custom_heuristic_policy.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/different_spaces_for_agents.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/multi_agent_cartpole.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/multi_agent_pendulum.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/pettingzoo_independent_learning.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/pettingzoo_parameter_sharing.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/pettingzoo_shared_value_function.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/rock_paper_scissors_heuristic_vs_learned.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/rock_paper_scissors_learned_vs_learned.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/self_play_footsies.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/self_play_league_based_with_open_spiel.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/self_play_with_open_spiel.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/shared_encoder_cartpole.cpython-312.pyc,, +ray/rllib/examples/multi_agent/__pycache__/two_step_game_with_grouped_agents.cpython-312.pyc,, +ray/rllib/examples/multi_agent/custom_heuristic_policy.py,sha256=mPuCFy3t-jjnlQjJ2Cz1iJ0XwRFcycQStCiP_NaB6QA,3813 +ray/rllib/examples/multi_agent/different_spaces_for_agents.py,sha256=3D4l8oKDgu2ApWpijNFch6jnjJv_IjDA30r59ddRd-8,4009 +ray/rllib/examples/multi_agent/multi_agent_cartpole.py,sha256=8dd8QktdgQUQVUU883QGIdZUQo0TCIuYGhi54Drm82w,1986 +ray/rllib/examples/multi_agent/multi_agent_pendulum.py,sha256=0NuurT_8s_iz8qOPFdrLU0ffV39ZTWKw76efIN1CxU0,2364 +ray/rllib/examples/multi_agent/pettingzoo_independent_learning.py,sha256=vS9vUVFP0cYQPi_pR9HbMSPTjQJNV47fBZE32iHrfvU,4122 +ray/rllib/examples/multi_agent/pettingzoo_parameter_sharing.py,sha256=SQp2Z0BM0nabObM9Dc4wHAyRpbO_MxoyF0vSbePE_lg,3775 +ray/rllib/examples/multi_agent/pettingzoo_shared_value_function.py,sha256=ty_VEsbyNoDSYlb0gAJVou1Qt8KLgHvOpAsZuHnMhQ4,241 +ray/rllib/examples/multi_agent/rock_paper_scissors_heuristic_vs_learned.py,sha256=nb_6Ry4JL6G1n6_lkjhzYI--50eT1-i3EHtlPPIH4Os,5431 +ray/rllib/examples/multi_agent/rock_paper_scissors_learned_vs_learned.py,sha256=GQaPgVuSADPJJpA1xFJBBOhwD9dhP0DBI5nQhdndE4Y,3079 +ray/rllib/examples/multi_agent/self_play_footsies.py,sha256=TMJYnMg0t7-5F_CCCIJ1h4iNRGlwLWucnxhgyWmLBqw,4067 +ray/rllib/examples/multi_agent/self_play_league_based_with_open_spiel.py,sha256=apS2hpSvzBEogjrxu8RMIUJhJgSG1vcYigB8nX_XeH4,10913 +ray/rllib/examples/multi_agent/self_play_with_open_spiel.py,sha256=sOo6X8fTGAWFqtmSI7j6K48ysMHDkd6JIyppdKTVKLA,9753 +ray/rllib/examples/multi_agent/shared_encoder_cartpole.py,sha256=1YbcljK23yap-yT4_qUseFSWlV5BMeJOweAEOf3LBg4,6664 +ray/rllib/examples/multi_agent/two_step_game_with_grouped_agents.py,sha256=UWcGZkCMJ_s10Awg22eMMyQiwGEHnkc0Rk3v6bRPXOo,3343 +ray/rllib/examples/multi_agent/utils/__init__.py,sha256=PsTJ13qS4t0xAMOHOlQNqSR5LQOMCrRDbTPoLIP4aFg,1321 +ray/rllib/examples/multi_agent/utils/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/multi_agent/utils/__pycache__/self_play_callback.cpython-312.pyc,, +ray/rllib/examples/multi_agent/utils/__pycache__/self_play_callback_old_api_stack.cpython-312.pyc,, +ray/rllib/examples/multi_agent/utils/__pycache__/self_play_league_based_callback.cpython-312.pyc,, +ray/rllib/examples/multi_agent/utils/__pycache__/self_play_league_based_callback_old_api_stack.cpython-312.pyc,, +ray/rllib/examples/multi_agent/utils/self_play_callback.py,sha256=FKk2u8UiP_9OToCleFT5dYm3KzH_m0A3kR4SIp66PrY,3722 +ray/rllib/examples/multi_agent/utils/self_play_callback_old_api_stack.py,sha256=b0Qo1iJeEZ9cc0YXmbgHhUgTwV1r-3v3ywt-fDAKRnI,3553 +ray/rllib/examples/multi_agent/utils/self_play_league_based_callback.py,sha256=p6-k6dNwM_iH2A-GKSsB9euYMZvNx9brNWED62Xp5TM,13494 +ray/rllib/examples/multi_agent/utils/self_play_league_based_callback_old_api_stack.py,sha256=WWqmygCQv6LUG8fmH5UOkLfXBmVcigUK4EAJxuAssjQ,9232 +ray/rllib/examples/offline_rl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/offline_rl/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/offline_rl/__pycache__/cartpole_recording.cpython-312.pyc,, +ray/rllib/examples/offline_rl/__pycache__/custom_input_api.cpython-312.pyc,, +ray/rllib/examples/offline_rl/__pycache__/offline_rl.cpython-312.pyc,, +ray/rllib/examples/offline_rl/__pycache__/offline_rl_with_image_data.cpython-312.pyc,, +ray/rllib/examples/offline_rl/__pycache__/pretrain_bc_single_agent_evaluate_as_multi_agent.cpython-312.pyc,, +ray/rllib/examples/offline_rl/__pycache__/saving_experiences.cpython-312.pyc,, +ray/rllib/examples/offline_rl/__pycache__/train_w_bc_finetune_w_ppo.cpython-312.pyc,, +ray/rllib/examples/offline_rl/cartpole_recording.py,sha256=Gs0e-HbENgt-RlHwSSXUBRnAk9mw5KQZ6IQBHpkGqA4,5925 +ray/rllib/examples/offline_rl/classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/offline_rl/classes/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/offline_rl/classes/__pycache__/image_offline_data.cpython-312.pyc,, +ray/rllib/examples/offline_rl/classes/__pycache__/image_offline_prelearner.cpython-312.pyc,, +ray/rllib/examples/offline_rl/classes/image_offline_data.py,sha256=AkpyED_jn9IAcvZbR8c5H-pM4kG4EHGoi4wYUU5l2EA,2756 +ray/rllib/examples/offline_rl/classes/image_offline_prelearner.py,sha256=pBHW31PfIpp7yTycXrTWpXYz867TpUD-pAstpeXgzdw,3734 +ray/rllib/examples/offline_rl/custom_input_api.py,sha256=5qOU3h9QU_fhrrsYcXYosdhjNv8jdORlVYk3175V0GQ,4258 +ray/rllib/examples/offline_rl/offline_rl.py,sha256=UWZpeegrd9OHHpEFEAFvEYe97KsqJQiaBCUWL8gWiWQ,6219 +ray/rllib/examples/offline_rl/offline_rl_with_image_data.py,sha256=ApZHOmij_KPqvjzj1QTJOTQCTlnwBdabRMZtRybpcpU,4784 +ray/rllib/examples/offline_rl/pretrain_bc_single_agent_evaluate_as_multi_agent.py,sha256=jeT2pbqDwHcZINtRZc92QV6bXyTH_AW0x28vqH2DnU0,7218 +ray/rllib/examples/offline_rl/saving_experiences.py,sha256=VBi7qQ-3GZG2VDGhVJDcAiWtjvGkTamouByFVtB7szI,2239 +ray/rllib/examples/offline_rl/train_w_bc_finetune_w_ppo.py,sha256=YKHFYPTJIoq9Elh8ojvDDm7Gv2_97Hk3htcFTY2D-0E,12561 +ray/rllib/examples/quadx_waypoints.py,sha256=alLLNmqdJ44xYe6amnbC2Gq8wIR2JY5S0givK3HoFhs,4013 +ray/rllib/examples/ray_serve/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/ray_serve/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/ray_serve/__pycache__/ray_serve_with_rllib.cpython-312.pyc,, +ray/rllib/examples/ray_serve/classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/ray_serve/classes/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/ray_serve/classes/__pycache__/cartpole_deployment.cpython-312.pyc,, +ray/rllib/examples/ray_serve/classes/cartpole_deployment.py,sha256=587tUWemZnL32ZCLWdzo0p1rrIcKYTsLL3PgjDH0NmM,1832 +ray/rllib/examples/ray_serve/ray_serve_with_rllib.py,sha256=7uQ08IvVJi_qCnds7v8SB1RNoz3kLtUHQ1ps0nd1wN8,6712 +ray/rllib/examples/ray_tune/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/ray_tune/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/ray_tune/__pycache__/custom_experiment.cpython-312.pyc,, +ray/rllib/examples/ray_tune/__pycache__/custom_logger.cpython-312.pyc,, +ray/rllib/examples/ray_tune/__pycache__/custom_progress_reporter.cpython-312.pyc,, +ray/rllib/examples/ray_tune/custom_experiment.py,sha256=_jNZsuQnAKk_FIViIJSl0w_bZoDRvy-Joyeebk6bveE,8339 +ray/rllib/examples/ray_tune/custom_logger.py,sha256=vnjADBma3eg30wxjyntQ3p-bbmFD7rzYPfYYo8RdeCY,4762 +ray/rllib/examples/ray_tune/custom_progress_reporter.py,sha256=Axl5d_i19ltriCyuwnFOydGmdgTb_0U8q7Ggv32g8NA,4783 +ray/rllib/examples/replay_buffer_api.py,sha256=BNPQlKflh_hlXTPbrU4QMDIP_BQvh3bm2Zh3XeRHbbY,2545 +ray/rllib/examples/rl_modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/examples/rl_modules/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/rl_modules/__pycache__/action_masking_rl_module.cpython-312.pyc,, +ray/rllib/examples/rl_modules/__pycache__/custom_cnn_rl_module.cpython-312.pyc,, +ray/rllib/examples/rl_modules/__pycache__/custom_lstm_rl_module.cpython-312.pyc,, +ray/rllib/examples/rl_modules/__pycache__/migrate_modelv2_to_new_api_stack_by_config.cpython-312.pyc,, +ray/rllib/examples/rl_modules/__pycache__/migrate_modelv2_to_new_api_stack_by_policy_checkpoint.cpython-312.pyc,, +ray/rllib/examples/rl_modules/__pycache__/pretraining_single_agent_training_multi_agent.cpython-312.pyc,, +ray/rllib/examples/rl_modules/action_masking_rl_module.py,sha256=m2r3zKJOviJy7_1mC5CfoL3VL1qXu-wFcyA04-aWWDg,5313 +ray/rllib/examples/rl_modules/classes/__init__.py,sha256=Gjh1N_dKvUt0-pDRyrdWx_1z7PADABnMUFYOYX8EskU,215 +ray/rllib/examples/rl_modules/classes/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/action_masking_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/autoregressive_actions_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/custom_action_distribution_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/intrinsic_curiosity_model_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/lstm_containing_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/mobilenet_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/modelv2_to_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/random_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/rock_paper_scissors_heuristic_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/tiny_atari_cnn_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/vpg_torch_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/__pycache__/vpg_using_shared_encoder_rlm.cpython-312.pyc,, +ray/rllib/examples/rl_modules/classes/action_masking_rlm.py,sha256=F7KYAnzJMq3aXOUCg4wOf2ZtTy6oTMErqZINzYm_RQQ,9351 +ray/rllib/examples/rl_modules/classes/autoregressive_actions_rlm.py,sha256=Mjw_Pl08nJm2XYgBPRpuPEp7lGqEZ9BCp3QbXaqarPY,4697 +ray/rllib/examples/rl_modules/classes/custom_action_distribution_rlm.py,sha256=BxHvHVdDWHfxmXcMWxkUCfIZf1mhnhGfJRvntW_RanU,6716 +ray/rllib/examples/rl_modules/classes/intrinsic_curiosity_model_rlm.py,sha256=C0kcpdnR0FcBrOOyzYYTvk2XPrKxcnfwle5dXUy6_EI,9483 +ray/rllib/examples/rl_modules/classes/lstm_containing_rlm.py,sha256=iGMjGS_WuNhIuug8YQab_nio8aFjSHq6EEcnzPQjOJI,6039 +ray/rllib/examples/rl_modules/classes/mobilenet_rlm.py,sha256=qpXEWxzPnDhRVqB6BsttxZrlifvPC9zAHnHMto5IAOY,2895 +ray/rllib/examples/rl_modules/classes/modelv2_to_rlm.py,sha256=0ndciU5NoRwFHLQD-wpAFZB9Y6xstspXwY7HxnPdvgI,9244 +ray/rllib/examples/rl_modules/classes/random_rlm.py,sha256=l2SvAbV3b6qKT9vRCUFwhJDE65c5vH0fEo_Ly6B5nl8,2248 +ray/rllib/examples/rl_modules/classes/rock_paper_scissors_heuristic_rlm.py,sha256=9r2I_xGnVBHxugee181gGLD-cqZTnzarmmU3N4YMtV8,3264 +ray/rllib/examples/rl_modules/classes/tiny_atari_cnn_rlm.py,sha256=9qja87BlegAmbpx8_opeUlwb8-V4a3NXB2pkHjPpa4g,7812 +ray/rllib/examples/rl_modules/classes/vpg_torch_rlm.py,sha256=OlT03SZLfpSw1kfAhyJYTaupnqcRKlKEIffSDxgwJDI,2746 +ray/rllib/examples/rl_modules/classes/vpg_using_shared_encoder_rlm.py,sha256=R0DdSQnYWVsVssk5vQzKZHisxdw0AJqtll0FhOgZH58,9562 +ray/rllib/examples/rl_modules/custom_cnn_rl_module.py,sha256=gtmeQQ8HUmO8OAfQ61LakJ0X4ghSety7JmHzsvc4zX4,4760 +ray/rllib/examples/rl_modules/custom_lstm_rl_module.py,sha256=JcZRhEHdp1UmWmO5xQbqmUr4SvSoZurKZOuMV_rM33E,3906 +ray/rllib/examples/rl_modules/migrate_modelv2_to_new_api_stack_by_config.py,sha256=ml5cjnPx6KDqDE6YVSy8C9KRnrf904dK6onkHdE7ddo,2429 +ray/rllib/examples/rl_modules/migrate_modelv2_to_new_api_stack_by_policy_checkpoint.py,sha256=U4K-V5-3MYOCW6vbsZGkZqd7XuYVF4UBhoYqS-z1P0o,3962 +ray/rllib/examples/rl_modules/pretraining_single_agent_training_multi_agent.py,sha256=7QoPO_pH63i8ew9xOY7k4lohBVIAuTU0rlrGiOpbxcg,7575 +ray/rllib/execution/__init__.py,sha256=4dBf66RZBAEwKSSI8V2ZzhORdoa2a96pkorYV3Z1WWc,697 +ray/rllib/execution/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/execution/__pycache__/learner_thread.cpython-312.pyc,, +ray/rllib/execution/__pycache__/minibatch_buffer.cpython-312.pyc,, +ray/rllib/execution/__pycache__/multi_gpu_learner_thread.cpython-312.pyc,, +ray/rllib/execution/__pycache__/replay_ops.cpython-312.pyc,, +ray/rllib/execution/__pycache__/rollout_ops.cpython-312.pyc,, +ray/rllib/execution/__pycache__/segment_tree.cpython-312.pyc,, +ray/rllib/execution/__pycache__/train_ops.cpython-312.pyc,, +ray/rllib/execution/buffers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/execution/buffers/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/execution/buffers/__pycache__/mixin_replay_buffer.cpython-312.pyc,, +ray/rllib/execution/buffers/mixin_replay_buffer.py,sha256=1Q-L51bUm-27eLOkeWCaSXD6wHXMLWSgQfRpo3riNVA,6842 +ray/rllib/execution/learner_thread.py,sha256=J1gnTTobfOutcFYhsobV-oBjrydJMzLqa31SN6R7tO4,5733 +ray/rllib/execution/minibatch_buffer.py,sha256=3i9VqRqzLb4BXs1aHq9q7Qw_ekYw9fUaaGL_sS3iDo0,1952 +ray/rllib/execution/multi_gpu_learner_thread.py,sha256=X2VmceUzEwpEGwgSx0zV9S92-9nhE2rAJY4TeTwVTro,9662 +ray/rllib/execution/replay_ops.py,sha256=4R-LXLuQMaZQEFd58CA1_cQOAUPLHuB52Q-kEjre7fA,1255 +ray/rllib/execution/rollout_ops.py,sha256=PN8O1O53rJtEUQwcGMPSiH-CzkklnzvbB2XSWYtPRuo,8646 +ray/rllib/execution/segment_tree.py,sha256=Whdo3jl1E0GD-bsojYfsxpGIyxZfMuf8ZEQGNNRE5Pg,8081 +ray/rllib/execution/train_ops.py,sha256=VfjkpVcIkcDXyPVrg2uE9j7UvMUdfzBDbXybnNyfOTo,8127 +ray/rllib/models/__init__.py,sha256=F9n_z6iLEDa0RMte_293yjHlv3lJX44ZXbjRKXUNb8Y,345 +ray/rllib/models/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/models/__pycache__/action_dist.cpython-312.pyc,, +ray/rllib/models/__pycache__/catalog.cpython-312.pyc,, +ray/rllib/models/__pycache__/distributions.cpython-312.pyc,, +ray/rllib/models/__pycache__/modelv2.cpython-312.pyc,, +ray/rllib/models/__pycache__/preprocessors.cpython-312.pyc,, +ray/rllib/models/__pycache__/repeated_values.cpython-312.pyc,, +ray/rllib/models/__pycache__/utils.cpython-312.pyc,, +ray/rllib/models/action_dist.py,sha256=EzVJRjjwCP5wPP123lZlcpr44RQ0UzXx1WwavE_8QfE,3425 +ray/rllib/models/catalog.py,sha256=FFiQPXQhtp3Z4vXvvldx7oYvyt3Wembr3BimbstXFOc,36145 +ray/rllib/models/distributions.py,sha256=knkywbQV-gcOGdMgxLfiNS2NkBgtl_1M75BcSIhkABc,291 +ray/rllib/models/modelv2.py,sha256=_nb8_xpL6GH4Z_2HWbnXN14u7F2PZDYFfGuqRWtUG9Y,17924 +ray/rllib/models/preprocessors.py,sha256=7jIIqerBzIv5yaepI_9hOy2JHrJnqe8cJLAVOYyzmO4,16616 +ray/rllib/models/repeated_values.py,sha256=w63AevQf_lyFCGcTjYMfe_0l25hwM5WHMtbU3FDa1kQ,6791 +ray/rllib/models/tf/__init__.py,sha256=2Vp28M6xRc0h7fp4RmAUUlefjdorYjj15f5DuQUNBmw,338 +ray/rllib/models/tf/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/models/tf/__pycache__/attention_net.cpython-312.pyc,, +ray/rllib/models/tf/__pycache__/complex_input_net.cpython-312.pyc,, +ray/rllib/models/tf/__pycache__/fcnet.cpython-312.pyc,, +ray/rllib/models/tf/__pycache__/misc.cpython-312.pyc,, +ray/rllib/models/tf/__pycache__/recurrent_net.cpython-312.pyc,, +ray/rllib/models/tf/__pycache__/tf_action_dist.cpython-312.pyc,, +ray/rllib/models/tf/__pycache__/tf_modelv2.cpython-312.pyc,, +ray/rllib/models/tf/__pycache__/visionnet.cpython-312.pyc,, +ray/rllib/models/tf/attention_net.py,sha256=u9QpnPHKZfdhzLkSWBO0MQpwK9veDr5g_FiIW9E549Y,23011 +ray/rllib/models/tf/complex_input_net.py,sha256=qkaDtgY4lA7x5-Cg60oMbT9H9zLi79TCZQ-5n_qJClA,8352 +ray/rllib/models/tf/fcnet.py,sha256=N1vh4TZypqYiDhutjwU3LtWVr_S0DDJ4LGVr1ZGx46g,5564 +ray/rllib/models/tf/layers/__init__.py,sha256=0FKA_4lLjHLQ_ucnr7LhC7uR3r0Sjxp7Q67X0vsJnrM,554 +ray/rllib/models/tf/layers/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/models/tf/layers/__pycache__/gru_gate.cpython-312.pyc,, +ray/rllib/models/tf/layers/__pycache__/multi_head_attention.cpython-312.pyc,, +ray/rllib/models/tf/layers/__pycache__/noisy_layer.cpython-312.pyc,, +ray/rllib/models/tf/layers/__pycache__/relative_multi_head_attention.cpython-312.pyc,, +ray/rllib/models/tf/layers/__pycache__/skip_connection.cpython-312.pyc,, +ray/rllib/models/tf/layers/gru_gate.py,sha256=Ku4-9_xAvKfc_eOucrIjjT2PulpjiJzl2zRIpcy-_Cg,1968 +ray/rllib/models/tf/layers/multi_head_attention.py,sha256=K2gzjsw32AaYVhx7xER_jA9cDkKYd_PiG-aQwt499C0,2257 +ray/rllib/models/tf/layers/noisy_layer.py,sha256=EeBTYold_zbB2aqBTVe_a_hQHc6DzhU0vQZ3QmwwBug,3961 +ray/rllib/models/tf/layers/relative_multi_head_attention.py,sha256=vYmfDXGW90KQvWHVjCNizApH7x8jlJ92iHmRt0qvQpA,5757 +ray/rllib/models/tf/layers/skip_connection.py,sha256=-huaQt3Wz2_YPtbBDJDVsXUEgof1IA1cwASwNh5u94c,1653 +ray/rllib/models/tf/misc.py,sha256=4cAeBl2MgJP-2trUZeHxI3GFxUSaXyWwyUqpJ1iWwOU,2658 +ray/rllib/models/tf/recurrent_net.py,sha256=VU1NsZACDeJaUsIGX4hLGK_PGPGja9IXmELllRBl3lA,11569 +ray/rllib/models/tf/tf_action_dist.py,sha256=t28g0NBcJTA5zZ2gS_0uG09h_ybhxqanOpr6GdkK07A,27265 +ray/rllib/models/tf/tf_modelv2.py,sha256=pKThbofRegftxQm6zH7E3_whJIlmaTCBtvAU_pCcork,5125 +ray/rllib/models/tf/visionnet.py,sha256=Lr3ln7VvI-Nn7dTlnf32NTm-PZ81Hqz3J6egOl1WiOI,10646 +ray/rllib/models/torch/__init__.py,sha256=zM2oOUgiqMKv7qQW2pBM6XyIqqjddHvTfnVoSEbjoos,387 +ray/rllib/models/torch/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/models/torch/__pycache__/attention_net.cpython-312.pyc,, +ray/rllib/models/torch/__pycache__/complex_input_net.cpython-312.pyc,, +ray/rllib/models/torch/__pycache__/fcnet.cpython-312.pyc,, +ray/rllib/models/torch/__pycache__/mingpt.cpython-312.pyc,, +ray/rllib/models/torch/__pycache__/misc.cpython-312.pyc,, +ray/rllib/models/torch/__pycache__/recurrent_net.cpython-312.pyc,, +ray/rllib/models/torch/__pycache__/torch_action_dist.cpython-312.pyc,, +ray/rllib/models/torch/__pycache__/torch_distributions.cpython-312.pyc,, +ray/rllib/models/torch/__pycache__/torch_modelv2.cpython-312.pyc,, +ray/rllib/models/torch/__pycache__/visionnet.cpython-312.pyc,, +ray/rllib/models/torch/attention_net.py,sha256=LH-vPOKv_4PhSnYvWBD5YbVOQ0gQbvA-pEuZ2-X6yas,18225 +ray/rllib/models/torch/complex_input_net.py,sha256=k7xLVNbHtvVxp8mGU873zXJQs-ZaoyhItQq-xYXjMeg,9287 +ray/rllib/models/torch/fcnet.py,sha256=N8JRcEYSJmKvp2LkjhgiLWxCJc3qDrz_orkfbZKXKfM,5903 +ray/rllib/models/torch/mingpt.py,sha256=mHhJZ9sEPwmFwuRH5IsLHcS4bD8kEhCcZszP_uKndw8,11144 +ray/rllib/models/torch/misc.py,sha256=lWFkF6zcbyRPNSYHIZbMwtk0WeM1-TzW58er0eB1aSA,11632 +ray/rllib/models/torch/modules/__init__.py,sha256=HcEs6_p32uWqTvqexeyYWLAt707FcyNQZkTHKGZ7zLw,438 +ray/rllib/models/torch/modules/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/models/torch/modules/__pycache__/gru_gate.cpython-312.pyc,, +ray/rllib/models/torch/modules/__pycache__/multi_head_attention.cpython-312.pyc,, +ray/rllib/models/torch/modules/__pycache__/noisy_layer.cpython-312.pyc,, +ray/rllib/models/torch/modules/__pycache__/relative_multi_head_attention.cpython-312.pyc,, +ray/rllib/models/torch/modules/__pycache__/skip_connection.cpython-312.pyc,, +ray/rllib/models/torch/modules/gru_gate.py,sha256=JdVCD8KywnSzghNMwsjOrRUeFVP8C2fC8NDBVafo6yo,2334 +ray/rllib/models/torch/modules/multi_head_attention.py,sha256=hqxlkr7d77SmaCbAdguHFucXok2nmbg9KEnPCcjn--s,2467 +ray/rllib/models/torch/modules/noisy_layer.py,sha256=2s0RFCnmvPoMFJhlVzTyFFet4GGY_JPa9mHLvjBtcJc,3406 +ray/rllib/models/torch/modules/relative_multi_head_attention.py,sha256=f7L38tc2SwrbqUBJ6a6BFSPGGTEV08zaIEQE72K71C0,6336 +ray/rllib/models/torch/modules/skip_connection.py,sha256=8dHNpQv_nZmvNvYXHZWSqwg1mb7CEuHnMchM4Cges1E,1455 +ray/rllib/models/torch/recurrent_net.py,sha256=hbmKaRJULcVy0EroowFXFP7oc5vdZADw43qITx_zMWI,12224 +ray/rllib/models/torch/torch_action_dist.py,sha256=KQGtDeOYu_-fo5r0c0A6Uh7u-zfZmCWeqfhX5hV1qfw,24345 +ray/rllib/models/torch/torch_distributions.py,sha256=n_HO28J_IDRIhnrd_CpSeHHccNzy2dNCEdDeO1ZpFaI,502 +ray/rllib/models/torch/torch_modelv2.py,sha256=5HPalgNJ6qi5VfT3nvibHledwG-m_Zqdi45m7AKCO3w,2710 +ray/rllib/models/torch/visionnet.py,sha256=9L9B2tZ04ywmPrrLp6ZEabkjJCcC29ZE8XZ9TJ0xbzo,10804 +ray/rllib/models/utils.py,sha256=TTuHVZDKkhk08v09Vn0QToHX9DDQ6lNoO91ACbikNJ4,10459 +ray/rllib/offline/__init__.py,sha256=ZELTOibdcQJE0X5ToAr6vv18ZKZ9EDJ6wH-V6rfp0-0,1044 +ray/rllib/offline/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/offline/__pycache__/d4rl_reader.cpython-312.pyc,, +ray/rllib/offline/__pycache__/dataset_reader.cpython-312.pyc,, +ray/rllib/offline/__pycache__/dataset_writer.cpython-312.pyc,, +ray/rllib/offline/__pycache__/feature_importance.cpython-312.pyc,, +ray/rllib/offline/__pycache__/input_reader.cpython-312.pyc,, +ray/rllib/offline/__pycache__/io_context.cpython-312.pyc,, +ray/rllib/offline/__pycache__/is_estimator.cpython-312.pyc,, +ray/rllib/offline/__pycache__/json_reader.cpython-312.pyc,, +ray/rllib/offline/__pycache__/json_writer.cpython-312.pyc,, +ray/rllib/offline/__pycache__/mixed_input.cpython-312.pyc,, +ray/rllib/offline/__pycache__/off_policy_estimator.cpython-312.pyc,, +ray/rllib/offline/__pycache__/offline_data.cpython-312.pyc,, +ray/rllib/offline/__pycache__/offline_env_runner.cpython-312.pyc,, +ray/rllib/offline/__pycache__/offline_evaluation_runner.cpython-312.pyc,, +ray/rllib/offline/__pycache__/offline_evaluation_runner_group.cpython-312.pyc,, +ray/rllib/offline/__pycache__/offline_evaluation_utils.cpython-312.pyc,, +ray/rllib/offline/__pycache__/offline_evaluator.cpython-312.pyc,, +ray/rllib/offline/__pycache__/offline_policy_evaluation_runner.cpython-312.pyc,, +ray/rllib/offline/__pycache__/offline_prelearner.cpython-312.pyc,, +ray/rllib/offline/__pycache__/output_writer.cpython-312.pyc,, +ray/rllib/offline/__pycache__/resource.cpython-312.pyc,, +ray/rllib/offline/__pycache__/shuffled_input.cpython-312.pyc,, +ray/rllib/offline/__pycache__/wis_estimator.cpython-312.pyc,, +ray/rllib/offline/d4rl_reader.py,sha256=2WOVVLb3cD1ZhRwLnuo7VwXjomHAZLpzLr00SeRyBOg,1596 +ray/rllib/offline/dataset_reader.py,sha256=qAptsLhKP6Ps-aYOOORwz47pQPgTbECFN38IN23ksYo,11710 +ray/rllib/offline/dataset_writer.py,sha256=nq0LC7kXmodFf0_4GHGvYqWlcN6ZryiyOSNAQTuA-TI,2801 +ray/rllib/offline/estimators/__init__.py,sha256=EVEvrL1iEmIlfAu9NZLVrBMKl1BPgIgQqMmxY62N5qI,544 +ray/rllib/offline/estimators/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/offline/estimators/__pycache__/direct_method.cpython-312.pyc,, +ray/rllib/offline/estimators/__pycache__/doubly_robust.cpython-312.pyc,, +ray/rllib/offline/estimators/__pycache__/feature_importance.cpython-312.pyc,, +ray/rllib/offline/estimators/__pycache__/fqe_torch_model.cpython-312.pyc,, +ray/rllib/offline/estimators/__pycache__/importance_sampling.cpython-312.pyc,, +ray/rllib/offline/estimators/__pycache__/off_policy_estimator.cpython-312.pyc,, +ray/rllib/offline/estimators/__pycache__/weighted_importance_sampling.cpython-312.pyc,, +ray/rllib/offline/estimators/direct_method.py,sha256=mj0PQN_TBIt-05m_OvyLRQn--wYPyhdWEjh0I3OArws,6705 +ray/rllib/offline/estimators/doubly_robust.py,sha256=XsUz6K7CeJQ26MraXC60769VSiRQU0eaoOk3iWCFK94,9619 +ray/rllib/offline/estimators/feature_importance.py,sha256=LZoIH7OyoC3AFBJqKh0HbPpcatFKHT0w_-xaSdoL6Tc,332 +ray/rllib/offline/estimators/fqe_torch_model.py,sha256=X3LuXZFUhGrpjV8dBj6ow16HBRyoUzAFmVFou7Dfou4,11812 +ray/rllib/offline/estimators/importance_sampling.py,sha256=tf9XFASaecjzhIUmQ5oq5C89L5A0LKoSHdwIzMao8dc,4601 +ray/rllib/offline/estimators/off_policy_estimator.py,sha256=5u5kZKhn3lXF3XYAtJUG1XTI5BaONlXj_0OoImd819A,10124 +ray/rllib/offline/estimators/weighted_importance_sampling.py,sha256=s9Cr_L6RktN5D3thpVZ_TiW32BrNL1sos9OafgZdCIw,7047 +ray/rllib/offline/feature_importance.py,sha256=3T-dGEbymroNHWdZeYGHexhile8epWAHCO45loC8JiI,10680 +ray/rllib/offline/input_reader.py,sha256=MTiSz12QRjGcO_D6irNHdQIlBqZHIeZeDBIx1WxuoPc,4855 +ray/rllib/offline/io_context.py,sha256=k8bnvtWsWe5z8ggVHfS63JIVwolrpFkRxGxcLmFKbQ4,2543 +ray/rllib/offline/is_estimator.py,sha256=X5DZEbuYVNZiK0hHwc988QLRgADakuhFt3L4nFBWjkU,304 +ray/rllib/offline/json_reader.py,sha256=zOzeHs6MuHIF1H1U5bn4UZ1V6r43-XPIJFgyWM0LQXg,16889 +ray/rllib/offline/json_writer.py,sha256=2PJiRIYkDUfQv4lg6YsJh315WQfLdEAMUxnrvy_osu4,4962 +ray/rllib/offline/mixed_input.py,sha256=0PSXj4MjqxGpRON6Dbbs_c_u3Vv-99-BmxPdTawRVFU,2030 +ray/rllib/offline/off_policy_estimator.py,sha256=Mf9YSwEtBzzAgBbDOsyRhxjqN77p3eHDkDvfuc8mzzI,311 +ray/rllib/offline/offline_data.py,sha256=Vbs6STB5vndY6VLHjx6j1X_ztCxRjbJYpB5P3yt0quo,13006 +ray/rllib/offline/offline_env_runner.py,sha256=B2hiogCMHCQy8s5FCNFWBzWeQcGlJtc8zBPgf45qb10,13101 +ray/rllib/offline/offline_evaluation_runner.py,sha256=6pfvyfbdsNO5z_walb1iajgeyrE_dLGAjRBSHrUqhww,16329 +ray/rllib/offline/offline_evaluation_runner_group.py,sha256=9oTM9LfH4BNuF-aowyZvmDgBNU-PVZJ9xZGvfBhSUXo,7444 +ray/rllib/offline/offline_evaluation_utils.py,sha256=M7i7lGW_RSZjezSfRcghYfsJv8QI2sHMupL9D2Y2r7M,4744 +ray/rllib/offline/offline_evaluator.py,sha256=oLyNyrJs1B8kTvwKWJ7T7e-qoppW0enBgoMGp5tcBOs,2285 +ray/rllib/offline/offline_policy_evaluation_runner.py,sha256=Fe_yR5wGoS3C3F8qaeaYySnhcwifzAii2umsYylna9c,23736 +ray/rllib/offline/offline_prelearner.py,sha256=041dJVky7OlvrQjlZhvunzB_w1VZsHljLPh6CAudP2U,27770 +ray/rllib/offline/output_writer.py,sha256=9MJ5XuO3TM-2ZUXyRKu6NR9W0aoTZ_8fbh83lO20f5k,660 +ray/rllib/offline/resource.py,sha256=-a8SrRWxlnJkV2O0cYQA_Ki_Qt853rq6B3Aox7G0Qcw,1265 +ray/rllib/offline/shuffled_input.py,sha256=M57IjbpEwN1tSjM6ER_nNaIKxY3aeciDI_N3eAuGWz0,1355 +ray/rllib/offline/wis_estimator.py,sha256=jeKJasiF6-WJyntBmbQWksgjIEwjEF-8_tSNPbcMZNI,370 +ray/rllib/policy/__init__.py,sha256=uxssoPxnXJGyLSci4xX5igSeweAHJbhqEjVahFKSG2M,386 +ray/rllib/policy/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/policy/__pycache__/dynamic_tf_policy.cpython-312.pyc,, +ray/rllib/policy/__pycache__/dynamic_tf_policy_v2.cpython-312.pyc,, +ray/rllib/policy/__pycache__/eager_tf_policy.cpython-312.pyc,, +ray/rllib/policy/__pycache__/eager_tf_policy_v2.cpython-312.pyc,, +ray/rllib/policy/__pycache__/policy.cpython-312.pyc,, +ray/rllib/policy/__pycache__/policy_map.cpython-312.pyc,, +ray/rllib/policy/__pycache__/policy_template.cpython-312.pyc,, +ray/rllib/policy/__pycache__/rnn_sequencing.cpython-312.pyc,, +ray/rllib/policy/__pycache__/sample_batch.cpython-312.pyc,, +ray/rllib/policy/__pycache__/tf_mixins.cpython-312.pyc,, +ray/rllib/policy/__pycache__/tf_policy.cpython-312.pyc,, +ray/rllib/policy/__pycache__/tf_policy_template.cpython-312.pyc,, +ray/rllib/policy/__pycache__/torch_mixins.cpython-312.pyc,, +ray/rllib/policy/__pycache__/torch_policy.cpython-312.pyc,, +ray/rllib/policy/__pycache__/torch_policy_v2.cpython-312.pyc,, +ray/rllib/policy/__pycache__/view_requirement.cpython-312.pyc,, +ray/rllib/policy/dynamic_tf_policy.py,sha256=v9PdW_Zz_WXVYtNZDNH_qN4H6YjCY1vt1cBsDrsS--Q,57806 +ray/rllib/policy/dynamic_tf_policy_v2.py,sha256=aSiXPZZJRgWCQksZPOupPIY6dW3p79r8QW_ge4V9xNw,40728 +ray/rllib/policy/eager_tf_policy.py,sha256=6kYa4ZQvdmr6Aoc6sTKbIUnthOCckLbSuRr8Hbq_zjc,43457 +ray/rllib/policy/eager_tf_policy_v2.py,sha256=oJRnAehjjw8ShmN9ljuWERA9DBIgfALyoDUwsbRT_98,36066 +ray/rllib/policy/policy.py,sha256=aifAt_QAbR229psjb5JvNURQN3yWTr2NWfq8KyhTncI,69980 +ray/rllib/policy/policy_map.py,sha256=l7_b4rM2zXKkpbQBuK_ZKJYaRe_RsaQK4tXedx4jp3s,10247 +ray/rllib/policy/policy_template.py,sha256=l4PzcXJeUP7oJMaHz3vdqzEFMknO15tZMKCEcdmvMTA,20218 +ray/rllib/policy/rnn_sequencing.py,sha256=G91uYWvQZukMot4_ni8KXUjrPej3pJHJMeqK1YUTwGk,26832 +ray/rllib/policy/sample_batch.py,sha256=twj79blMHnbS_WeZlEU0hZQzIFFkT6ojwz1YWwBoRmM,68721 +ray/rllib/policy/tf_mixins.py,sha256=p5GS9h93l5C_s4rdwgaIlZvEOu1VlQfXhtnyKLYYugc,15277 +ray/rllib/policy/tf_policy.py,sha256=E5OHVE6D9HHa-B8-bR3tI2vSweA-4div6kUPFW7PWrs,48691 +ray/rllib/policy/tf_policy_template.py,sha256=ZM-VpLUmCLB2SV69c1a5dqd_fl5fqIxZX_Txf5f4wTI,16935 +ray/rllib/policy/torch_mixins.py,sha256=fF1me7JfH_gQg_p0lnOipe0zOYm_8nPQD0xz21kfkGk,8682 +ray/rllib/policy/torch_policy.py,sha256=cfo1ZT0vpHLM97AxOu_ydDDDmHGjW9IIja13AsuOP7E,49541 +ray/rllib/policy/torch_policy_v2.py,sha256=WGKI5hJgpRCM97_LsTFa0D4AQyMymxE5o7fYte8RvBA,49486 +ray/rllib/policy/view_requirement.py,sha256=GOR_xPwT_HW_KVCw8_08slvUsUY4_MpsAO1SKzBQS34,6294 +ray/rllib/tuned_examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/tuned_examples/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/tuned_examples/__pycache__/cleanup_experiment.cpython-312.pyc,, +ray/rllib/tuned_examples/cleanup_experiment.py,sha256=cqS1csVQzMPCd9oGYayEmeb5y7CqxQwpvZBCcDYATq8,7539 +ray/rllib/tuned_examples/dreamerv3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/tuned_examples/dreamerv3/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/__pycache__/atari_100k_dreamerv3.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/__pycache__/atari_200M_dreamerv3.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/__pycache__/cartpole_dreamerv3.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/__pycache__/dm_control_suite_vision_dreamerv3.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/__pycache__/flappy_bird_dreamerv3.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/__pycache__/frozenlake_2x2_dreamerv3.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/__pycache__/frozenlake_4x4_deterministic_dreamerv3.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/__pycache__/gymnasium_robotics_dreamerv3.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/__pycache__/highway_env_dreamerv3.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/__pycache__/pendulum_dreamerv3.cpython-312.pyc,, +ray/rllib/tuned_examples/dreamerv3/atari_100k_dreamerv3.py,sha256=6ijgVqdO0-eIX1s7-nkx6BvETq-BKBmx1aFZBigTCaU,3543 +ray/rllib/tuned_examples/dreamerv3/atari_200M_dreamerv3.py,sha256=q81JrY5-_kg3g1s3RTO0or6Xq-xCP9n1QtHOUdKZ6ks,3202 +ray/rllib/tuned_examples/dreamerv3/cartpole_dreamerv3.py,sha256=VoEdju6ANXYPXNoqXqCUbssrJYpR_YSoBT5ZhFeTvjU,541 +ray/rllib/tuned_examples/dreamerv3/dm_control_suite_vision_dreamerv3.py,sha256=-T0D-vOj5ZicVho7-_LxU-s_TpRGa-9x607KgimVJ7s,2606 +ray/rllib/tuned_examples/dreamerv3/flappy_bird_dreamerv3.py,sha256=b1XNP2-f9fPrJs9a8OvrSS1GMatrwR-of2IV5amR5NE,2503 +ray/rllib/tuned_examples/dreamerv3/frozenlake_2x2_dreamerv3.py,sha256=8JuU3Ql-4wlZ0RVOD7vbuhxqmC2kvVGiIVNKWM3VCIY,776 +ray/rllib/tuned_examples/dreamerv3/frozenlake_4x4_deterministic_dreamerv3.py,sha256=STdrZbDxbEGSzCQWs5GU8DbRvElJlhI4GuRmfRMwtBc,728 +ray/rllib/tuned_examples/dreamerv3/gymnasium_robotics_dreamerv3.py,sha256=1lB5JWrGegIY5E5AZhB35OiXBuUaX6WEdTNZQLE1m4o,2263 +ray/rllib/tuned_examples/dreamerv3/highway_env_dreamerv3.py,sha256=XSp_kW1TQ4Xwf9gUvMjH7NL2GdNMhu0hbC2UxQgvPpg,2280 +ray/rllib/tuned_examples/dreamerv3/pendulum_dreamerv3.py,sha256=aAblg7fmHYIGrJ99O6WWyXvLDf4IWn18yn7VDiXZzaE,1996 +ray/rllib/utils/__init__.py,sha256=DONkfdGnS4EBXZrEQqwD1vfUnnwLX72Scfe0fam46I0,5180 +ray/rllib/utils/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/utils/__pycache__/actor_manager.cpython-312.pyc,, +ray/rllib/utils/__pycache__/actors.cpython-312.pyc,, +ray/rllib/utils/__pycache__/annotations.cpython-312.pyc,, +ray/rllib/utils/__pycache__/checkpoints.cpython-312.pyc,, +ray/rllib/utils/__pycache__/compression.cpython-312.pyc,, +ray/rllib/utils/__pycache__/error.cpython-312.pyc,, +ray/rllib/utils/__pycache__/filter.cpython-312.pyc,, +ray/rllib/utils/__pycache__/filter_manager.cpython-312.pyc,, +ray/rllib/utils/__pycache__/framework.cpython-312.pyc,, +ray/rllib/utils/__pycache__/from_config.cpython-312.pyc,, +ray/rllib/utils/__pycache__/images.cpython-312.pyc,, +ray/rllib/utils/__pycache__/lambda_defaultdict.cpython-312.pyc,, +ray/rllib/utils/__pycache__/memory.cpython-312.pyc,, +ray/rllib/utils/__pycache__/minibatch_utils.cpython-312.pyc,, +ray/rllib/utils/__pycache__/numpy.cpython-312.pyc,, +ray/rllib/utils/__pycache__/policy.cpython-312.pyc,, +ray/rllib/utils/__pycache__/serialization.cpython-312.pyc,, +ray/rllib/utils/__pycache__/sgd.cpython-312.pyc,, +ray/rllib/utils/__pycache__/tensor_dtype.cpython-312.pyc,, +ray/rllib/utils/__pycache__/test_utils.cpython-312.pyc,, +ray/rllib/utils/__pycache__/tf_run_builder.cpython-312.pyc,, +ray/rllib/utils/__pycache__/tf_utils.cpython-312.pyc,, +ray/rllib/utils/__pycache__/threading.cpython-312.pyc,, +ray/rllib/utils/__pycache__/torch_utils.cpython-312.pyc,, +ray/rllib/utils/__pycache__/typing.cpython-312.pyc,, +ray/rllib/utils/actor_manager.py,sha256=PcV8UHpjmDDpbDXsvZfh0KHd8Tq5MSV2vD1JRhoyNVo,46065 +ray/rllib/utils/actors.py,sha256=etkCaERyTQNMV2vYI5xjhzcn9pnPlmacZgmr3-TLAp0,9964 +ray/rllib/utils/annotations.py,sha256=z1QH4kUEhNFaLciDRnZRMbVVX3xQnDaqVEXsuf9sInA,6828 +ray/rllib/utils/checkpoints.py,sha256=IANrZ2FhPDxt81IuYzjbYUrbFia0a_fwBikIYa7QNBM,43455 +ray/rllib/utils/compression.py,sha256=FF0M-0g8N2Vb4IGr8_8iMlOTbQWLv-ooTVIBKaFFPAw,2163 +ray/rllib/utils/debug/__init__.py,sha256=H0tWfXLpJbwxh2YZPOivg8FqhxJV1wE2GoqRYk-WrPc,289 +ray/rllib/utils/debug/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/utils/debug/__pycache__/deterministic.cpython-312.pyc,, +ray/rllib/utils/debug/__pycache__/memory.cpython-312.pyc,, +ray/rllib/utils/debug/__pycache__/summary.cpython-312.pyc,, +ray/rllib/utils/debug/deterministic.py,sha256=i0u4-aa1_01avg0BYeFKs7T8FMbPfAMl0id30GDrDZI,1032 +ray/rllib/utils/debug/memory.py,sha256=xjoQRShi2-AlIjr5HsyPsOepNBJMjkx5dNLxIO1Fxk4,7792 +ray/rllib/utils/debug/summary.py,sha256=Xg2L-iA6xZyTZgWnXUJwWi4gR9fnBGf7tkZy3RU5n9I,2337 +ray/rllib/utils/error.py,sha256=LZQSDeBuKoCxUNyy2ZlvS0bekLIW6TYxW0XHsUelm2I,5245 +ray/rllib/utils/exploration/__init__.py,sha256=C6G1mWMyWnbbTz_n5VEAOY2HL9Sv97JscP51_vyLiuM,1668 +ray/rllib/utils/exploration/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/curiosity.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/epsilon_greedy.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/exploration.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/gaussian_noise.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/ornstein_uhlenbeck_noise.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/parameter_noise.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/per_worker_epsilon_greedy.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/per_worker_gaussian_noise.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/per_worker_ornstein_uhlenbeck_noise.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/random.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/random_encoder.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/slate_epsilon_greedy.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/slate_soft_q.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/soft_q.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/stochastic_sampling.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/thompson_sampling.cpython-312.pyc,, +ray/rllib/utils/exploration/__pycache__/upper_confidence_bound.cpython-312.pyc,, +ray/rllib/utils/exploration/curiosity.py,sha256=PJfvnCtZ_Vnbiq3n5EQ9yLd3nl7iWaMNcF1wUEJqJ9o,18662 +ray/rllib/utils/exploration/epsilon_greedy.py,sha256=bqrxJDDBMABqh8nZ5TwmAyeQaWvwCTisUSyVFut5YFY,9429 +ray/rllib/utils/exploration/exploration.py,sha256=2FBIMHB_Q6_THx4SO3ykYKFnkL8vhNnVhCBJIPTDcY8,7177 +ray/rllib/utils/exploration/gaussian_noise.py,sha256=zxnORAjWgAI4LS2pkntKOphHYF5v9evknvtgsTJMaDY,9197 +ray/rllib/utils/exploration/ornstein_uhlenbeck_noise.py,sha256=UP2oj795iVEMThSOT44vnVkw_yfHlerUzRTcpQolw9U,10453 +ray/rllib/utils/exploration/parameter_noise.py,sha256=ZCItU8dbCBK4jo3_wBxYABHKtKfC26Tp0dLpB4pxUfw,17226 +ray/rllib/utils/exploration/per_worker_epsilon_greedy.py,sha256=Be-L3YmXWVH_PH04EylrC-wx_4TCmPDYPl6cnwskpqQ,2146 +ray/rllib/utils/exploration/per_worker_gaussian_noise.py,sha256=yCSoATQPTFKKMfPYUioHxNhveb6QOLCmsLLSA4r12UY,1779 +ray/rllib/utils/exploration/per_worker_ornstein_uhlenbeck_noise.py,sha256=X4pxf36lXnTRLPfSpBRKZ0Gs2s92rQ8E5QWNSC39YCE,1947 +ray/rllib/utils/exploration/random.py,sha256=mpFe8kT1N3HvKf5SjGrMJqq-vCQ3bahbgJOWLD_csJo,6661 +ray/rllib/utils/exploration/random_encoder.py,sha256=wyIhJHCnXe7HHnhT4265IpbwTLI2XtO6-OxrYXrQUjE,10695 +ray/rllib/utils/exploration/slate_epsilon_greedy.py,sha256=mS-xxsFlIl_2qq-E74Q-KU0KMNM0chj4GncY3PrKdVE,3871 +ray/rllib/utils/exploration/slate_soft_q.py,sha256=RBOHsroxQQIPXS9gwFFR9XtpJOLj1L_j5qkGZIhazJA,1509 +ray/rllib/utils/exploration/soft_q.py,sha256=xwfIj73GjUKVpeCcV2Q6vjHkWwkbkJu_BRE25AWX7W4,2115 +ray/rllib/utils/exploration/stochastic_sampling.py,sha256=5DKneMLQRjPhLrscbRz5nlFmxedGOoVmf3hXwTglVHk,5427 +ray/rllib/utils/exploration/thompson_sampling.py,sha256=P9WGW-CyCRIgIWoTevqbVDyf2dYYzIq0rkgBW0ER49E,1490 +ray/rllib/utils/exploration/upper_confidence_bound.py,sha256=fmvRbfNkX45JJB_XbGq5N8mWWXMDBTmd7y8h53z-iXE,1405 +ray/rllib/utils/filter.py,sha256=2DdS0pjBcX1DZfnSFrPMZdmmFs2CkHR6jE6RHoMw60w,17209 +ray/rllib/utils/filter_manager.py,sha256=8xIe_spWnh9A6BaMkp8RsntXWYmzcrZl5XLG9arxDqM,3238 +ray/rllib/utils/framework.py,sha256=mGU7sk_thhIqjXrxFseQcXPPrewb9dVQ965KxgEoy3o,13193 +ray/rllib/utils/from_config.py,sha256=IdDRz4BoPPGFcpY6QmPDmz0_k7OB4CWttUwvVv1rb5M,11848 +ray/rllib/utils/images.py,sha256=F0eEkTYeCdDwvXvaLmKZgZg7Hl9H0QSs_erjqbABEW0,1557 +ray/rllib/utils/lambda_defaultdict.py,sha256=Z7qlHYxnUsqLELE_AF585VGdG6GtbT-_bnpNjjuViyw,1998 +ray/rllib/utils/memory.py,sha256=P6KChN2h5tjD3YbQM5A3bN7ShjKC0jjAqyi5-qcldOM,247 +ray/rllib/utils/metrics/__init__.py,sha256=0UvwWxLqwrdHEN33YHjczzoEbuZROIULDHOdSDsBBZg,9616 +ray/rllib/utils/metrics/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/utils/metrics/__pycache__/learner_info.cpython-312.pyc,, +ray/rllib/utils/metrics/__pycache__/metrics_logger.cpython-312.pyc,, +ray/rllib/utils/metrics/__pycache__/ray_metrics.cpython-312.pyc,, +ray/rllib/utils/metrics/__pycache__/stats.cpython-312.pyc,, +ray/rllib/utils/metrics/__pycache__/utils.cpython-312.pyc,, +ray/rllib/utils/metrics/__pycache__/window_stat.cpython-312.pyc,, +ray/rllib/utils/metrics/learner_info.py,sha256=MmI1DWuwOAIDdkvwsruSPeunkWOEETIwHmYCVIsB89k,4441 +ray/rllib/utils/metrics/metrics_logger.py,sha256=FOaD8DbPaRLJJRJnHIObDNVRpDsTtIYp7u5U_MyQbi8,65398 +ray/rllib/utils/metrics/ray_metrics.py,sha256=7gk0fqZSqoB37aHxsxboF6aICQ6LaP7NuEgHLamNxi8,1498 +ray/rllib/utils/metrics/stats.py,sha256=cZmKUMPT7os4fIdyW69e1_BkU7QxGN_OlMmp9EontKM,44884 +ray/rllib/utils/metrics/utils.py,sha256=j3C_-vS2G2OT6KwITc29Z5Enei6hOEgX9A67aH6sra8,641 +ray/rllib/utils/metrics/window_stat.py,sha256=6m5JtxatluH1qJrdZFYR9DT6b3P1IyPEMGHqsvkPDRI,2553 +ray/rllib/utils/minibatch_utils.py,sha256=T9zo1EIEdIIeo7zOC-bTeS3sieFWMntLnULiq9RKG0k,16208 +ray/rllib/utils/numpy.py,sha256=JDcnNOOcSwnU8MgLXxh9IbIxb5LuWAJz-OvNBqODcjM,20024 +ray/rllib/utils/policy.py,sha256=kIuuSTYDVQuF4FCCFvCfrkhMkAo2WbkjHaXMgpJXxAU,11239 +ray/rllib/utils/postprocessing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/utils/postprocessing/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/utils/postprocessing/__pycache__/episodes.cpython-312.pyc,, +ray/rllib/utils/postprocessing/__pycache__/value_predictions.cpython-312.pyc,, +ray/rllib/utils/postprocessing/__pycache__/zero_padding.cpython-312.pyc,, +ray/rllib/utils/postprocessing/episodes.py,sha256=lXW2IZJCwH-k8V8jGgqngcl_jIjrPLPBmdm3ogQUnVQ,5263 +ray/rllib/utils/postprocessing/value_predictions.py,sha256=ts899IEI25w-vnNnJaMoXNDhvhxZKRFNS5GUDhEgm9o,4313 +ray/rllib/utils/postprocessing/zero_padding.py,sha256=FGLvU44UnhC_pjIj_KMq9FsI_c_y1s3uhOI5TBxPcag,9571 +ray/rllib/utils/pre_checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/utils/pre_checks/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/utils/pre_checks/__pycache__/env.cpython-312.pyc,, +ray/rllib/utils/pre_checks/env.py,sha256=iotuhWJAtGDbN211XbdaWpv3ThjJ6zqIEXSDnjIzt7Y,10850 +ray/rllib/utils/replay_buffers/__init__.py,sha256=Gaaj2ZdHmIfTqNt9vNqA5332DPLPGoClnt5tpEkGWZc,1632 +ray/rllib/utils/replay_buffers/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/base.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/episode_replay_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/fifo_replay_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/multi_agent_episode_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/multi_agent_mixin_replay_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/multi_agent_prioritized_episode_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/multi_agent_prioritized_replay_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/multi_agent_replay_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/prioritized_episode_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/prioritized_replay_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/replay_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/reservoir_replay_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/simple_replay_buffer.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/__pycache__/utils.cpython-312.pyc,, +ray/rllib/utils/replay_buffers/base.py,sha256=S4X19ys7b8xpgJX-rq5Nm-zRMGG_pMJWHPqBUWaB_LE,2271 +ray/rllib/utils/replay_buffers/episode_replay_buffer.py,sha256=QnBwGbJGNILwcNfjHab2SKavSTUprzhRsis8mFfePDg,66613 +ray/rllib/utils/replay_buffers/fifo_replay_buffer.py,sha256=it5NSwASkZDd5KCChpmqfo_uA-bD_c55HPlno3FgXjQ,3494 +ray/rllib/utils/replay_buffers/multi_agent_episode_buffer.py,sha256=Oepfi3GYYAw7we05sigDSalqpkbDCWuFPt7fNbOi3RA,54164 +ray/rllib/utils/replay_buffers/multi_agent_mixin_replay_buffer.py,sha256=ioNhKLlaF9YqFQLXY1btUl422z5ktwLymbQZqmDUb7A,16712 +ray/rllib/utils/replay_buffers/multi_agent_prioritized_episode_buffer.py,sha256=U4WSfyDsbVfEo9_gvzR8IlX1fvBz6xpshYFZ8fiSGa0,50957 +ray/rllib/utils/replay_buffers/multi_agent_prioritized_replay_buffer.py,sha256=C6AjxeggjpJ2tanUwlXxLkgAp42BVqhkGMkh0w30ruM,12279 +ray/rllib/utils/replay_buffers/multi_agent_replay_buffer.py,sha256=EHfZrkFXAxqdzECKGgeqC_mbVis-itBrMPQ_lQF5_Po,16374 +ray/rllib/utils/replay_buffers/prioritized_episode_buffer.py,sha256=aOM_MClXSTA-oho0Isf5GCpaKa0QfUIkUrsCjIj_2qA,33219 +ray/rllib/utils/replay_buffers/prioritized_replay_buffer.py,sha256=MNCvZm3h9k6nbMajVRIZc5ZsnG6kZLJhwbSK5mJFmuE,8803 +ray/rllib/utils/replay_buffers/replay_buffer.py,sha256=f706thKSB3FFJNd_FhLoQujggXcqiaBegiWWojyHSyI,14390 +ray/rllib/utils/replay_buffers/reservoir_replay_buffer.py,sha256=W_6tk7627x4_UyVeilGetA4f5LgmdwMa32Ox-TXAdAM,4533 +ray/rllib/utils/replay_buffers/simple_replay_buffer.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/utils/replay_buffers/utils.py,sha256=pERN2vY3LjbgBYGZ15Ncbxz78x21iPU7DJb0oBjEZ5w,18038 +ray/rllib/utils/runners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/utils/runners/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/utils/runners/__pycache__/runner.cpython-312.pyc,, +ray/rllib/utils/runners/__pycache__/runner_group.cpython-312.pyc,, +ray/rllib/utils/runners/runner.py,sha256=lZbQuJYy1byzfN5EGT_Iibq4kU7U-KGqvmn2nbQYEbU,3255 +ray/rllib/utils/runners/runner_group.py,sha256=A6-FE6jW6wV5QaS_oRQ8Elgj10QYPaBrG3gMWsD2S68,30324 +ray/rllib/utils/schedules/__init__.py,sha256=NH_Y_BjCwNgGQjIJ4ZItDAp7ff-dVmY9pgJKIutzfqo,584 +ray/rllib/utils/schedules/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/utils/schedules/__pycache__/constant_schedule.cpython-312.pyc,, +ray/rllib/utils/schedules/__pycache__/exponential_schedule.cpython-312.pyc,, +ray/rllib/utils/schedules/__pycache__/linear_schedule.cpython-312.pyc,, +ray/rllib/utils/schedules/__pycache__/piecewise_schedule.cpython-312.pyc,, +ray/rllib/utils/schedules/__pycache__/polynomial_schedule.cpython-312.pyc,, +ray/rllib/utils/schedules/__pycache__/schedule.cpython-312.pyc,, +ray/rllib/utils/schedules/__pycache__/scheduler.cpython-312.pyc,, +ray/rllib/utils/schedules/constant_schedule.py,sha256=SJhiuhFc6MbIikZbs6BNfZCiETedK9JF7vZ8vJa3VU0,1002 +ray/rllib/utils/schedules/exponential_schedule.py,sha256=nyZs9t8Ertg-QdakTH4bmKIIMNVTDX2afRvEHMZyZLc,1833 +ray/rllib/utils/schedules/linear_schedule.py,sha256=9Dn7AW7rmXNL4yeEYSKlQMymnLEiPlmEVshisb211_o,528 +ray/rllib/utils/schedules/piecewise_schedule.py,sha256=bQBq-nEVPfLv53A8EsxJsppI9E-Q2htzBzBPH1FdxB8,4172 +ray/rllib/utils/schedules/polynomial_schedule.py,sha256=7qamzs2z_cJ-aYiLl-yQjGx4qxPS_IQKMncX6Z5U3qk,2215 +ray/rllib/utils/schedules/schedule.py,sha256=27P7t8oIWDI4Op9KKurscfdSiCU0OUO5OC30ZPqZTzQ,2265 +ray/rllib/utils/schedules/scheduler.py,sha256=sOfErVaJGkOK6QP8A7jt30bk05HHNzGEZbwU02M77Z8,6825 +ray/rllib/utils/serialization.py,sha256=FRS4Z1ev7d2ANWZN7fCWtaR2bPOd61ICR45H3SPvWFo,13448 +ray/rllib/utils/sgd.py,sha256=SPkOOMynT7O8hZ2iZSqZVC9MceL7aSPRU6OshxN4xPk,4603 +ray/rllib/utils/spaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/rllib/utils/spaces/__pycache__/__init__.cpython-312.pyc,, +ray/rllib/utils/spaces/__pycache__/flexdict.cpython-312.pyc,, +ray/rllib/utils/spaces/__pycache__/repeated.cpython-312.pyc,, +ray/rllib/utils/spaces/__pycache__/simplex.cpython-312.pyc,, +ray/rllib/utils/spaces/__pycache__/space_utils.cpython-312.pyc,, +ray/rllib/utils/spaces/flexdict.py,sha256=IEl0J84bS8DSlTByosF3WcsJP6cIefPJlKJzTK94UJo,1298 +ray/rllib/utils/spaces/repeated.py,sha256=tXyq2rSD1jH91WDpeNQHTC3yohfTyRs16sAL8vJ2PYQ,1110 +ray/rllib/utils/spaces/simplex.py,sha256=75oReN-TAsFew2YKq-HZDlB9Rnt0FcW0XlxfugP8tSI,1881 +ray/rllib/utils/spaces/space_utils.py,sha256=e-oLSLDP2pORrJsvx5VljEUrdgEyL_c6L2u74QBd1-s,17983 +ray/rllib/utils/tensor_dtype.py,sha256=iMP2MNkXN5NDo2areVouQZ7E8iOMTGeZP6T8nLm40Xw,1893 +ray/rllib/utils/test_utils.py,sha256=9gEyx-6R7COKmAb7gQ53xZwgZYvt5qHRHto_xz5grJ4,73037 +ray/rllib/utils/tf_run_builder.py,sha256=BkviB1rYBghKi2CE2-nZ2RZdvbGe6BdcyC12JzyAgfw,3879 +ray/rllib/utils/tf_utils.py,sha256=IQQJ7iQDfMEwHAAO_Obe_x5DX4gPaZsWnG5qVFrsDK0,36914 +ray/rllib/utils/threading.py,sha256=e91Bv1KdeXLbaa7xx7Rfb1CMpx-dq34vTuaos6XWOGo,1032 +ray/rllib/utils/torch_utils.py,sha256=7JLLK7Q7CAQjRkDUqMsSbQxyeiLhkak0EFBBKOztq0g,30945 +ray/rllib/utils/typing.py,sha256=Hg_LuN7WuWwBNWrI1M1m2KiR_zpHS20autTD7nHXzJo,11848 +ray/runtime_context.py,sha256=Dvy0NCbAzqMV2ZW1nQl65hZcCD6JZTOSbFs-DrkLRc0,19883 +ray/runtime_env/__init__.py,sha256=-hdkF3iZF7KGkZFt7Fw3yy2rWR_CQNh970SUI5D67d0,145 +ray/runtime_env/__pycache__/__init__.cpython-312.pyc,, +ray/runtime_env/__pycache__/runtime_env.cpython-312.pyc,, +ray/runtime_env/runtime_env.py,sha256=KyFRJkBN9bKwb2l5Xc9tvwrbaxl2Ek8a2S4zLWecpYY,26189 +ray/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/scripts/__pycache__/__init__.cpython-312.pyc,, +ray/scripts/__pycache__/scripts.cpython-312.pyc,, +ray/scripts/__pycache__/symmetric_run.cpython-312.pyc,, +ray/scripts/scripts.py,sha256=D_wW8O8IBNiZ3cvXCX2XtrOcaJUnODWCGFx0jwxmh7s,92459 +ray/scripts/symmetric_run.py,sha256=QDtsIzRqWkP5gFcTlksExDMAlMJSjEPL3QTHxn7zi-Q,9055 +ray/serve/__init__.py,sha256=31_DECK9vsFFVa4V1egSDgPQjrwUyumnYCBMQruaAdw,1460 +ray/serve/__pycache__/__init__.cpython-312.pyc,, +ray/serve/__pycache__/api.cpython-312.pyc,, +ray/serve/__pycache__/autoscaling_policy.cpython-312.pyc,, +ray/serve/__pycache__/batching.cpython-312.pyc,, +ray/serve/__pycache__/config.cpython-312.pyc,, +ray/serve/__pycache__/context.cpython-312.pyc,, +ray/serve/__pycache__/dag.cpython-312.pyc,, +ray/serve/__pycache__/deployment.cpython-312.pyc,, +ray/serve/__pycache__/exceptions.cpython-312.pyc,, +ray/serve/__pycache__/gradio_integrations.cpython-312.pyc,, +ray/serve/__pycache__/grpc_util.cpython-312.pyc,, +ray/serve/__pycache__/handle.cpython-312.pyc,, +ray/serve/__pycache__/metrics.cpython-312.pyc,, +ray/serve/__pycache__/multiplex.cpython-312.pyc,, +ray/serve/__pycache__/request_router.cpython-312.pyc,, +ray/serve/__pycache__/schema.cpython-312.pyc,, +ray/serve/__pycache__/scripts.cpython-312.pyc,, +ray/serve/__pycache__/task_consumer.cpython-312.pyc,, +ray/serve/__pycache__/task_processor.cpython-312.pyc,, +ray/serve/_private/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/serve/_private/__pycache__/__init__.cpython-312.pyc,, +ray/serve/_private/__pycache__/api.cpython-312.pyc,, +ray/serve/_private/__pycache__/application_state.cpython-312.pyc,, +ray/serve/_private/__pycache__/autoscaling_state.cpython-312.pyc,, +ray/serve/_private/__pycache__/build_app.cpython-312.pyc,, +ray/serve/_private/__pycache__/client.cpython-312.pyc,, +ray/serve/_private/__pycache__/cluster_node_info_cache.cpython-312.pyc,, +ray/serve/_private/__pycache__/common.cpython-312.pyc,, +ray/serve/_private/__pycache__/config.cpython-312.pyc,, +ray/serve/_private/__pycache__/constants.cpython-312.pyc,, +ray/serve/_private/__pycache__/constants_utils.cpython-312.pyc,, +ray/serve/_private/__pycache__/controller.cpython-312.pyc,, +ray/serve/_private/__pycache__/controller_avatar.cpython-312.pyc,, +ray/serve/_private/__pycache__/default_impl.cpython-312.pyc,, +ray/serve/_private/__pycache__/deploy_utils.cpython-312.pyc,, +ray/serve/_private/__pycache__/deployment_info.cpython-312.pyc,, +ray/serve/_private/__pycache__/deployment_node.cpython-312.pyc,, +ray/serve/_private/__pycache__/deployment_scheduler.cpython-312.pyc,, +ray/serve/_private/__pycache__/deployment_state.cpython-312.pyc,, +ray/serve/_private/__pycache__/endpoint_state.cpython-312.pyc,, +ray/serve/_private/__pycache__/exceptions.cpython-312.pyc,, +ray/serve/_private/__pycache__/grpc_util.cpython-312.pyc,, +ray/serve/_private/__pycache__/handle_options.cpython-312.pyc,, +ray/serve/_private/__pycache__/http_util.cpython-312.pyc,, +ray/serve/_private/__pycache__/local_testing_mode.cpython-312.pyc,, +ray/serve/_private/__pycache__/logging_utils.cpython-312.pyc,, +ray/serve/_private/__pycache__/long_poll.cpython-312.pyc,, +ray/serve/_private/__pycache__/metrics_utils.cpython-312.pyc,, +ray/serve/_private/__pycache__/proxy.cpython-312.pyc,, +ray/serve/_private/__pycache__/proxy_request_response.cpython-312.pyc,, +ray/serve/_private/__pycache__/proxy_response_generator.cpython-312.pyc,, +ray/serve/_private/__pycache__/proxy_router.cpython-312.pyc,, +ray/serve/_private/__pycache__/proxy_state.cpython-312.pyc,, +ray/serve/_private/__pycache__/replica.cpython-312.pyc,, +ray/serve/_private/__pycache__/replica_result.cpython-312.pyc,, +ray/serve/_private/__pycache__/router.cpython-312.pyc,, +ray/serve/_private/__pycache__/task_consumer.cpython-312.pyc,, +ray/serve/_private/__pycache__/test_utils.cpython-312.pyc,, +ray/serve/_private/__pycache__/usage.cpython-312.pyc,, +ray/serve/_private/__pycache__/utils.cpython-312.pyc,, +ray/serve/_private/__pycache__/version.cpython-312.pyc,, +ray/serve/_private/api.py,sha256=K49F6EtR0AhSjNeGHlEMv4Lsx9aR88N7lKFCf0feZ_A,10401 +ray/serve/_private/application_state.py,sha256=NMlaSCgGzZhovqM7oFEWYZM5lymgpEbWSA02Tf1aB9U,66558 +ray/serve/_private/autoscaling_state.py,sha256=ZLxHr9fTZijt8ztnQxhale-H6XiZxV3aOIaCw-n_SGI,41937 +ray/serve/_private/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/serve/_private/benchmarks/__pycache__/__init__.cpython-312.pyc,, +ray/serve/_private/benchmarks/__pycache__/common.cpython-312.pyc,, +ray/serve/_private/benchmarks/__pycache__/handle_noop_latency.cpython-312.pyc,, +ray/serve/_private/benchmarks/__pycache__/handle_throughput.cpython-312.pyc,, +ray/serve/_private/benchmarks/__pycache__/http_noop_latency.cpython-312.pyc,, +ray/serve/_private/benchmarks/__pycache__/locust_utils.cpython-312.pyc,, +ray/serve/_private/benchmarks/__pycache__/microbenchmark.cpython-312.pyc,, +ray/serve/_private/benchmarks/__pycache__/proxy_benchmark.cpython-312.pyc,, +ray/serve/_private/benchmarks/common.py,sha256=DTGxmW42HS74UW-Y57fjU3SAloS72RpE9eRqtMpBvVo,8970 +ray/serve/_private/benchmarks/handle_noop_latency.py,sha256=8ujbO6wDskL3OYEpUHR85Rg5RKETg2THaxOxIAh3dD8,943 +ray/serve/_private/benchmarks/handle_throughput.py,sha256=jvGCdtFk35hL8fcXzg1wrtnev6EiEf9rJULYMfaZ600,1460 +ray/serve/_private/benchmarks/http_noop_latency.py,sha256=4txfJbA9s8RPUG69rOIu9NZM15wNRYrQgoDjVcApl8E,897 +ray/serve/_private/benchmarks/locust_utils.py,sha256=i5RJ4-r5aUyv-z3u-3Ky2rZIUwuj4aObu7v8KboUbtI,8707 +ray/serve/_private/benchmarks/microbenchmark.py,sha256=KzNS7CUr9qPWBbx_Grvoj9mGgDC8HJQaHWl0mwF7cig,5273 +ray/serve/_private/benchmarks/proxy_benchmark.py,sha256=RE98C15Ddmx23YbMwBdzQt45am2dprfWiI0bKyVgpR0,8997 +ray/serve/_private/benchmarks/serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/serve/_private/benchmarks/serialization/__pycache__/__init__.cpython-312.pyc,, +ray/serve/_private/benchmarks/serialization/__pycache__/common.cpython-312.pyc,, +ray/serve/_private/benchmarks/serialization/common.py,sha256=iu6KBTA1iUX0O9Uiz3T1qRcLHiSqhhMwwjO8reL_-T8,802 +ray/serve/_private/benchmarks/streaming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/serve/_private/benchmarks/streaming/__pycache__/__init__.cpython-312.pyc,, +ray/serve/_private/benchmarks/streaming/__pycache__/common.cpython-312.pyc,, +ray/serve/_private/benchmarks/streaming/__pycache__/streaming_core_throughput.cpython-312.pyc,, +ray/serve/_private/benchmarks/streaming/__pycache__/streaming_grpc_throughput.cpython-312.pyc,, +ray/serve/_private/benchmarks/streaming/__pycache__/streaming_handle_throughput.cpython-312.pyc,, +ray/serve/_private/benchmarks/streaming/__pycache__/streaming_http_throughput.cpython-312.pyc,, +ray/serve/_private/benchmarks/streaming/_grpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/serve/_private/benchmarks/streaming/_grpc/__pycache__/__init__.cpython-312.pyc,, +ray/serve/_private/benchmarks/streaming/_grpc/__pycache__/grpc_server.cpython-312.pyc,, +ray/serve/_private/benchmarks/streaming/_grpc/__pycache__/test_server_pb2_grpc.cpython-312.pyc,, +ray/serve/_private/benchmarks/streaming/_grpc/grpc_server.py,sha256=fgNQLAikGwUac2gWCot9IKPCaWzFO0BoDsxIOlTZcGc,1860 +ray/serve/_private/benchmarks/streaming/_grpc/test_server_pb2_grpc.py,sha256=jMH9tTMpZKGJ2fzoJtGyGVGzxZKseo81fd9FQ6rels8,8850 +ray/serve/_private/benchmarks/streaming/common.py,sha256=YBC2Uhk-quzsK2Ib0Mzz4bn6JD6GadrUvMteQ-sY-SA,3627 +ray/serve/_private/benchmarks/streaming/streaming_core_throughput.py,sha256=wtuA4VUjFCqs4tkjRk4ww3UKje5wFm4f10VCW6aD-kg,2350 +ray/serve/_private/benchmarks/streaming/streaming_grpc_throughput.py,sha256=OswRDb00-hcvwKvgGNYt825ETWc7EMvXI79ynIgDOTM,6015 +ray/serve/_private/benchmarks/streaming/streaming_handle_throughput.py,sha256=sR-N6tXV7yymHl0SG29c5gvXEqiEwuUGHfKblt8QqJw,2363 +ray/serve/_private/benchmarks/streaming/streaming_http_throughput.py,sha256=6sxqagqQpR48eJ0ha9q9OwT1aYm_0KbdCcHc7F6U0U8,3701 +ray/serve/_private/build_app.py,sha256=MWiIBKjIsTIPSete-9OPd2gckJGPeSUtFoZrEOfdZXY,8094 +ray/serve/_private/client.py,sha256=XEmboFsc1DvZoJBQKfeK46LTkEcAeZiC7ifoa1L1pPw,23351 +ray/serve/_private/cluster_node_info_cache.py,sha256=BFe4cv35R-SkrKg3nFpw1Hhf_0ul7Uwe9XLbY_R8Pwk,3721 +ray/serve/_private/common.py,sha256=OyWfqa7hk2CZSrs2dODBlqFjrnvrL6d-aGwM8p8TIe4,33355 +ray/serve/_private/config.py,sha256=1TO1_igA-vWgi3iH5oE3I0N-ckwgshga8DmAqFIO7m0,34922 +ray/serve/_private/constants.py,sha256=6Ih0WJyi6QOkEfh6L3nRx3sxhXxex3CweXg1ldi_L8Y,18797 +ray/serve/_private/constants_utils.py,sha256=V7WESPcWq3pU_J8ojI71lJiDPGoLpbF7XpkmHCZEJ7o,9681 +ray/serve/_private/controller.py,sha256=TVVFFFvNBXlyPjf6Bsf_jCsGlk5O4VRvb1N7V8pWABM,50331 +ray/serve/_private/controller_avatar.py,sha256=m5V41cV60w-X8FOCVE5jJT-jpdUdd9o4nkAQsh4Ojew,1442 +ray/serve/_private/default_impl.py,sha256=IDE7ozNGnp52_giRwBNWxiwol1mgET8bfFhl46ic0Us,8187 +ray/serve/_private/deploy_utils.py,sha256=JJfqSAgReFcFKO8V7K0uzH5Cm0cEgmI7l55zJqIv96w,4675 +ray/serve/_private/deployment_info.py,sha256=4JRsLF_P_G5iLN6TjTZndSCzmMGNMlDUMOCPl9rzF_E,6232 +ray/serve/_private/deployment_node.py,sha256=gv1qjTmG17li46BHpKqwroOfh6sx3L9b5mKPUMaVZ0Y,1770 +ray/serve/_private/deployment_scheduler.py,sha256=uqFQ-Bq_Yx8HUN9yQtPCIBanacEMRhiNWgd53LV99g0,31592 +ray/serve/_private/deployment_state.py,sha256=wUZpLf3YGnpxQI0Z_4v_wmdh3zsSgvsNC1MPrNpIq6I,150065 +ray/serve/_private/endpoint_state.py,sha256=snU37WHweQZN59NPxGEhu2G88z6RgrCV1r1lqmxYc7g,3806 +ray/serve/_private/exceptions.py,sha256=reotGz6jF0u3pc__T4fpga4Rjee_414kmwy85QdZI3c,145 +ray/serve/_private/grpc_util.py,sha256=QoVHTrtnFFbQ9fERm8sB-XTwytp2Q4lAm-sxUHe6QBY,6768 +ray/serve/_private/handle_options.py,sha256=3-9QWU7onmcgq2xeehX0s5JQXs9OR62-_DlPKGsHzYA,2263 +ray/serve/_private/http_util.py,sha256=EGEaCUpGx0BLbtp2fTkbn83DApuMLgtHSc8AJjyp2nU,30149 +ray/serve/_private/local_testing_mode.py,sha256=bXezRG2wxXvshuqIbEwsOWON6V6QCzuD15FzYPKO85Y,11922 +ray/serve/_private/logging_utils.py,sha256=3q-zc97WSe8zxqL9ZKACHlcQtQgXifLFnwZx3T6FogE,19041 +ray/serve/_private/long_poll.py,sha256=8lILtsGdNg3fqz_0Nbg1WdmczRfeG1vd3SmkWfAFKVM,20124 +ray/serve/_private/metrics_utils.py,sha256=uzsqeRkn-kDO46Kwem1RMRqjiUKb6FF7F6t-kXNDyCg,17070 +ray/serve/_private/proxy.py,sha256=IDaVV1qicnBAjKfoa-wEIAVNiUrsPvhxCNld4a7A68c,52588 +ray/serve/_private/proxy_request_response.py,sha256=tcyQv3wzoRdIrIY8jyBWoV9ITYoAX-qtEQ-jCmXsC2c,6058 +ray/serve/_private/proxy_response_generator.py,sha256=bBuvzu0ROZeGmYY6-JR8BxQ82VvElzDT9ddNGFCVnbo,6491 +ray/serve/_private/proxy_router.py,sha256=6G3o21e9E80LRtztKwrj46z76kWGzsh1fwueeQLh1HI,10213 +ray/serve/_private/proxy_state.py,sha256=Eq33B4GObVQF_t1thpIkiBysSh9MGT6TrfvI4fpXqec,30085 +ray/serve/_private/replica.py,sha256=w1mql_K7HXljIKOQdbK5wPMAwvPITeTlGzSwpR-96eo,90781 +ray/serve/_private/replica_result.py,sha256=X59SitUixmByv3y0xqW6cyjrF1MO6Hl0HNI7fjQDXEI,8817 +ray/serve/_private/request_router/__init__.py,sha256=imAkj50ssUFq1_2_5HMyElV-J8PZQl0zTW55qVu2WYA,393 +ray/serve/_private/request_router/__pycache__/__init__.cpython-312.pyc,, +ray/serve/_private/request_router/__pycache__/common.cpython-312.pyc,, +ray/serve/_private/request_router/__pycache__/pow_2_router.cpython-312.pyc,, +ray/serve/_private/request_router/__pycache__/replica_wrapper.cpython-312.pyc,, +ray/serve/_private/request_router/__pycache__/request_router.cpython-312.pyc,, +ray/serve/_private/request_router/common.py,sha256=4rpkOGxyCNUG7WlJuhUzxBKg9riPLXhTkRh-Z2ui49E,3602 +ray/serve/_private/request_router/pow_2_router.py,sha256=ceDWz25bSWe2P9797FTHvw7iPLeXL-mVTBvwXODG7UY,3129 +ray/serve/_private/request_router/replica_wrapper.py,sha256=p_to8gSaY7HJN5lZKqU6GJXsyQhbuGJyIm-pmxkVdPg,6898 +ray/serve/_private/request_router/request_router.py,sha256=5GDDcsuJC6yEXLUpL1hysZLv9RdIcWWC5Cl2JC4AeUk,49492 +ray/serve/_private/router.py,sha256=EdVhwQJsXMtYsJnKBf9M4xnckPmHQhQVm7K-3x-ZUcA,49950 +ray/serve/_private/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/serve/_private/storage/__pycache__/__init__.cpython-312.pyc,, +ray/serve/_private/storage/__pycache__/kv_store.cpython-312.pyc,, +ray/serve/_private/storage/__pycache__/kv_store_base.cpython-312.pyc,, +ray/serve/_private/storage/kv_store.py,sha256=cxvTn7CTCKwp8asu1Ol3E5evy0vuVXWdbU-Bfsc5H5g,3597 +ray/serve/_private/storage/kv_store_base.py,sha256=UwhSwo1fYAWDB77qJ2ksHpDIdSA5r7T41o9dESUUVOA,1532 +ray/serve/_private/task_consumer.py,sha256=cWUbpl1xc3uZFRQ0P-tBHOHAahiJ1_5DzuVuBTdqYrM,221 +ray/serve/_private/test_utils.py,sha256=k7wJfmM0vDGniDTakC5-OEna34m8YM0ueTRF08PIPK0,27147 +ray/serve/_private/thirdparty/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/serve/_private/thirdparty/__pycache__/__init__.cpython-312.pyc,, +ray/serve/_private/thirdparty/__pycache__/get_asgi_route_name.cpython-312.pyc,, +ray/serve/_private/thirdparty/get_asgi_route_name.py,sha256=woqYXpQm3ce2pdkjWphTgm2fJm7zsLf9NrFgxVCJAFQ,5629 +ray/serve/_private/usage.py,sha256=5ZeTG4_xyzdnlYPkMdH52zZcHDs-nh3lTLgfez4jHCw,2580 +ray/serve/_private/utils.py,sha256=o9rmk_9teNnjidjrCdcZ0kL2Gq-Nebr_UPuU2XU_R0A,23017 +ray/serve/_private/version.py,sha256=cxKQJdO6BEveId94JDvuOjZF7n-I5mHQLyCxd-C7Hpg,9283 +ray/serve/api.py,sha256=fAawVbxCzmV6ikBw6TwU1yzCOWsNyw-pSqU3JT6d7-A,39718 +ray/serve/autoscaling_policy.py,sha256=EmfKvLgjpq08caEtdByAeVXmr_iwHIuc3aZLO-x9caE,7498 +ray/serve/batching.py,sha256=BhB1GXoA23JDe23e679w8Jji7Kn8z4lYIC8Qow9bhAw,28662 +ray/serve/config.py,sha256=kyfpy24HjiYMCpt9HwCiNeEbCk35faUQloPrc8ZQ98E,26951 +ray/serve/context.py,sha256=fFjcMAZFI-MFkSOLWXk-64CqaIaJaJbLFHl_cGhZQxQ,11300 +ray/serve/dag.py,sha256=6rXZdW60pdKgF0FltQvqduwR9NCLkwfHJ6rIBbe8aQg,73 +ray/serve/deployment.py,sha256=BtmG0NNKoN9gmw0f2WcPOqDRjQRZWepFQRqYUNdcD4E,19169 +ray/serve/exceptions.py,sha256=XqlDd_KWWfFB7yvkPIqkuRCDWkQEfUQLSK1HD3Hscws,1786 +ray/serve/generated/__pycache__/serve_pb2.cpython-312.pyc,, +ray/serve/generated/__pycache__/serve_pb2_grpc.cpython-312.pyc,, +ray/serve/generated/serve_pb2.py,sha256=dii3eMy-sFohqunWFljgPIqR81DAdrLqwk-sadfM5yE,37806 +ray/serve/generated/serve_pb2_grpc.py,sha256=uwqOT_p3A5WEdTvbGJ1m9WeSLGVRYMn0zLh41ZFG9oA,18527 +ray/serve/gradio_integrations.py,sha256=1IJAUrR_tB7xwwfI5dVNTgrslb3jkfDEb1kVKOqAAu0,968 +ray/serve/grpc_util.py,sha256=FKpVvDuaSEAncAb3tNtKzBN3QcSoAyfzlZJEKqDC5MY,5939 +ray/serve/handle.py,sha256=XceCa3G5MZmp51Nc-06B3V91QELtv3A-aipqXc7zues,29338 +ray/serve/llm/__init__.py,sha256=xWw3F7wrWBhJwUsUj_9vPDv_o_pBTajQ-Wm00TsskKg,12794 +ray/serve/llm/__pycache__/__init__.cpython-312.pyc,, +ray/serve/llm/__pycache__/deployment.cpython-312.pyc,, +ray/serve/llm/__pycache__/gen_config.cpython-312.pyc,, +ray/serve/llm/__pycache__/ingress.cpython-312.pyc,, +ray/serve/llm/__pycache__/openai_api_models.cpython-312.pyc,, +ray/serve/llm/__pycache__/request_router.cpython-312.pyc,, +ray/serve/llm/deployment.py,sha256=VUiPz3Gi_mpjQRLg78SLhEAg8saT1BmIquRmeZ2QAjE,4145 +ray/serve/llm/gen_config.py,sha256=RclEGKfWyQ0ePdL3mIiB2fYG_-P6FrPlKh-CqWE0S-k,200 +ray/serve/llm/ingress.py,sha256=ImoqdrqinJlODLLQ8q5sJwPadkZxKff3ohWUzF8pBfs,2811 +ray/serve/llm/openai_api_models.py,sha256=qu9R5hOYvAmxGyHeL3zIAN4232hoR2Ic2otfE869Z8s,3320 +ray/serve/llm/request_router.py,sha256=arcbHN2RnefuB9CfM47HDLXQ04j1cGPypeuTS--z52s,1797 +ray/serve/metrics.py,sha256=0fd0A1-ODcv8JlcBNNRlceO38wVoMbdo-BcUig0i8AQ,9272 +ray/serve/multiplex.py,sha256=IZMAygFYW0k1DjbV-LKv4K1djFYAE20sL2rihy4yAO4,11015 +ray/serve/request_router.py,sha256=GQ7Crm-ga3gDOubaPFZ_vMYRESzzyhCprDDvsMdrzPo,479 +ray/serve/schema.py,sha256=FAN5lYZ3sw_U4Ac-hqNmacssAn-xs-LqL-Db9Nm0wv0,56564 +ray/serve/scripts.py,sha256=E3tV67VIFFPrBIjMidst8a8Gli4g0mdp8zBDq2TmRLg,29271 +ray/serve/task_consumer.py,sha256=nP2Z19N61tjluq6O7n8VHUbXaPptLK7l-8Ni2mRABSE,7220 +ray/serve/task_processor.py,sha256=qgSpdZ44AxOt2xJ-S6ig0-am27lnsNGHQIramxRz_ZU,12922 +ray/setup-dev.py,sha256=RLAPsyr6WEiGsKDuUn_o2Wi8xN-11mnlClCrmIZXu-o,6844 +ray/thirdparty_files/colorama-0.4.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/thirdparty_files/colorama-0.4.6.dist-info/METADATA,sha256=e67SnrUMOym9sz_4TjF3vxvAV4T3aF7NyqRHHH3YEMw,17158 +ray/thirdparty_files/colorama-0.4.6.dist-info/RECORD,sha256=seVTCEP4TJmavRMV14xcqS0IAZkfHqm2f0-q5ErSwk0,2263 +ray/thirdparty_files/colorama-0.4.6.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/thirdparty_files/colorama-0.4.6.dist-info/WHEEL,sha256=cdcF4Fbd0FPtw2EMIOwH-3rSOTUdTCeOSXRMD1iLUb8,105 +ray/thirdparty_files/colorama-0.4.6.dist-info/licenses/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491 +ray/thirdparty_files/colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266 +ray/thirdparty_files/colorama/__pycache__/__init__.cpython-312.pyc,, +ray/thirdparty_files/colorama/__pycache__/ansi.cpython-312.pyc,, +ray/thirdparty_files/colorama/__pycache__/ansitowin32.cpython-312.pyc,, +ray/thirdparty_files/colorama/__pycache__/initialise.cpython-312.pyc,, +ray/thirdparty_files/colorama/__pycache__/win32.cpython-312.pyc,, +ray/thirdparty_files/colorama/__pycache__/winterm.cpython-312.pyc,, +ray/thirdparty_files/colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 +ray/thirdparty_files/colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128 +ray/thirdparty_files/colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325 +ray/thirdparty_files/colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181 +ray/thirdparty_files/colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134 +ray/thirdparty_files/psutil-7.1.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ray/thirdparty_files/psutil-7.1.3.dist-info/LICENSE,sha256=uJwGOzeG4o4MCjjxkx22H-015p3SopZvvs_-4PRsjRA,1548 +ray/thirdparty_files/psutil-7.1.3.dist-info/METADATA,sha256=NXjcTuX0PlQtKYOXQbnj2HYMPA5djDiOwrC5gL2MZqE,23082 +ray/thirdparty_files/psutil-7.1.3.dist-info/RECORD,sha256=vO-p-4W4K8qN5OyFARlUxJQNQ2TyvAdR5_NPJhuxAJI,4587 +ray/thirdparty_files/psutil-7.1.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/thirdparty_files/psutil-7.1.3.dist-info/WHEEL,sha256=k0riQKE5_0GXActL4egeNSDuQ6nsFSKiWV91AD_j7xg,184 +ray/thirdparty_files/psutil-7.1.3.dist-info/top_level.txt,sha256=gCNhn57wzksDjSAISmgMJ0aiXzQulk0GJhb2-BAyYgw,7 +ray/thirdparty_files/psutil/__init__.py,sha256=7BTYRH1NsMvPBQyeRKqecd5d-JcxaeNFwnXtPYckDzo,87789 +ray/thirdparty_files/psutil/__pycache__/__init__.cpython-312.pyc,, +ray/thirdparty_files/psutil/__pycache__/_common.cpython-312.pyc,, +ray/thirdparty_files/psutil/__pycache__/_psaix.cpython-312.pyc,, +ray/thirdparty_files/psutil/__pycache__/_psbsd.cpython-312.pyc,, +ray/thirdparty_files/psutil/__pycache__/_pslinux.cpython-312.pyc,, +ray/thirdparty_files/psutil/__pycache__/_psosx.cpython-312.pyc,, +ray/thirdparty_files/psutil/__pycache__/_psposix.cpython-312.pyc,, +ray/thirdparty_files/psutil/__pycache__/_pssunos.cpython-312.pyc,, +ray/thirdparty_files/psutil/__pycache__/_pswindows.cpython-312.pyc,, +ray/thirdparty_files/psutil/_common.py,sha256=kDPOMktstdNqB0MbHgCvjR2wl8DQmwfIauBZXOsYLsI,28584 +ray/thirdparty_files/psutil/_psaix.py,sha256=Pcos7TWAKSoU-eNFQ-u8wpMKlOS5xnA1f1TMgtmpC2s,18179 +ray/thirdparty_files/psutil/_psbsd.py,sha256=K_Dn2NU6XsgW2ZTrVB5sH3AaQ6TGc3Q09a4rUcX-a6w,30701 +ray/thirdparty_files/psutil/_pslinux.py,sha256=FFjrlyH8_vZndau5DBLpXFxePZ0AozDIKtF0Tn0_BgM,86321 +ray/thirdparty_files/psutil/_psosx.py,sha256=ixb1t2ketUSzt8_AzVkixT8cly8qf7J5qet8KLZsZIo,16541 +ray/thirdparty_files/psutil/_psposix.py,sha256=5pBE2Mk8LCWLthOpgKM5_S6AJOSankF0dHrozwtDi-Y,7141 +ray/thirdparty_files/psutil/_pssunos.py,sha256=K99FU9-0k1W08ynXxX8LFaUaz4e59Iu-4vgp6gYSNCE,24848 +ray/thirdparty_files/psutil/_psutil_linux.abi3.so,sha256=EiNyuoEmue7geZAxz5owrp6iFwHUfWsGEjtrDtgLJ_E,146080 +ray/thirdparty_files/psutil/_pswindows.py,sha256=QInpXnEXWc_FyN0cHkgb53p-USMbWEgRqQG95iGnH64,36532 +ray/train/__init__.py,sha256=p66VE185n5cdZLbw5-sbB3OcO_PKjQ5YNwcj42HI6x0,3852 +ray/train/__pycache__/__init__.cpython-312.pyc,, +ray/train/__pycache__/_checkpoint.cpython-312.pyc,, +ray/train/__pycache__/backend.cpython-312.pyc,, +ray/train/__pycache__/base_trainer.cpython-312.pyc,, +ray/train/__pycache__/constants.cpython-312.pyc,, +ray/train/__pycache__/context.cpython-312.pyc,, +ray/train/__pycache__/data_parallel_trainer.cpython-312.pyc,, +ray/train/__pycache__/error.cpython-312.pyc,, +ray/train/__pycache__/predictor.cpython-312.pyc,, +ray/train/__pycache__/session.cpython-312.pyc,, +ray/train/__pycache__/trainer.cpython-312.pyc,, +ray/train/__pycache__/utils.cpython-312.pyc,, +ray/train/_checkpoint.py,sha256=YWOQiql-um_YqqlfgytVa8ABkxQU8lk7sxZE-QR4IAQ,17068 +ray/train/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/_internal/__pycache__/__init__.cpython-312.pyc,, +ray/train/_internal/__pycache__/accelerator.cpython-312.pyc,, +ray/train/_internal/__pycache__/backend_executor.cpython-312.pyc,, +ray/train/_internal/__pycache__/base_worker_group.cpython-312.pyc,, +ray/train/_internal/__pycache__/checkpoint_manager.cpython-312.pyc,, +ray/train/_internal/__pycache__/data_config.cpython-312.pyc,, +ray/train/_internal/__pycache__/dl_predictor.cpython-312.pyc,, +ray/train/_internal/__pycache__/framework_checkpoint.cpython-312.pyc,, +ray/train/_internal/__pycache__/session.cpython-312.pyc,, +ray/train/_internal/__pycache__/storage.cpython-312.pyc,, +ray/train/_internal/__pycache__/syncer.cpython-312.pyc,, +ray/train/_internal/__pycache__/utils.cpython-312.pyc,, +ray/train/_internal/__pycache__/worker_group.cpython-312.pyc,, +ray/train/_internal/accelerator.py,sha256=Dko-pPYKMhpcmuSH6cKW-hJvqx8brgmuB0_aCLxcXX8,107 +ray/train/_internal/backend_executor.py,sha256=15W6LW4GgXu8leNH_DfBmZgubdoZrvjqgPDZOmduxg4,30697 +ray/train/_internal/base_worker_group.py,sha256=Lanu2N9VHtIVUEYL2R7LtbLalMF3me77T5QgNHmBrds,3406 +ray/train/_internal/checkpoint_manager.py,sha256=-CXCXymuVmMUwne45Kwdghg5If3SHdOfrtreqUmHHug,8074 +ray/train/_internal/data_config.py,sha256=zZT3TJL0IEHYN_NZjLcECNr27jlRdhohuJWCAv4OdVs,6059 +ray/train/_internal/dl_predictor.py,sha256=821wcP_AxBj0fDRCyhgJD0U9nI2qdCLd4eezmiuvRTk,3570 +ray/train/_internal/framework_checkpoint.py,sha256=v1Xb5wniN546Dde5jrKZDQ3lje6dPcQQ4Q-8tVEt1tE,1491 +ray/train/_internal/session.py,sha256=UupZ5LostwbnGNrZtKdLyoUQh3VgKRlXVIjFfUdx7Vs,41239 +ray/train/_internal/state/__init__.py,sha256=MmAOOS_XhXYiecXSy9TQmY_4WHYsXjmH7dBlevWKwpQ,315 +ray/train/_internal/state/__pycache__/__init__.cpython-312.pyc,, +ray/train/_internal/state/__pycache__/export.cpython-312.pyc,, +ray/train/_internal/state/__pycache__/schema.cpython-312.pyc,, +ray/train/_internal/state/__pycache__/state_actor.cpython-312.pyc,, +ray/train/_internal/state/__pycache__/state_manager.cpython-312.pyc,, +ray/train/_internal/state/export.py,sha256=zrrY2Ubr8aZf8d5IyiCDLP7Q7sBL672iFWVFogxlbZU,3729 +ray/train/_internal/state/schema.py,sha256=gQ332m8CC7xVqngyRACrK2B5ycBMM7xQRGXOSxHNLXQ,5093 +ray/train/_internal/state/state_actor.py,sha256=KFv-QNzlG1sj9skx71Kh1JhyTkgAgwMMeYixwpaGy3c,5054 +ray/train/_internal/state/state_manager.py,sha256=7cv3wWQik94EYYLIjliVGhXMSlJ_-G3XAATZiiyYEwM,4323 +ray/train/_internal/storage.py,sha256=tRYorn49qaUmL-eqmCVc2zcISnr3YODZ1Pu6q2r5fm0,27646 +ray/train/_internal/syncer.py,sha256=aoyosq9OHT6U7vqqYYPOdLSgScVuVHhZnU0jMPH8-0c,14003 +ray/train/_internal/utils.py,sha256=TlwdOJtanRGRpzQ25ypvSNYYLoF1_PI8cC-mIn0bt-A,6851 +ray/train/_internal/worker_group.py,sha256=vwS6IbhzbVlXKdtjbuuW2MHPisJEZ9Z-rzG3iPtKe7c,15665 +ray/train/backend.py,sha256=R5jqM_64p631p2kqWrIA-2V65zeYudPMv-Epe7S4M1I,1761 +ray/train/base_trainer.py,sha256=ujIxHQStrWLQOEFVpCL9EGA6C5eeWd_KTpWePeq3phQ,37414 +ray/train/collective/__init__.py,sha256=Gj0iEoGatq-o2t6oIeOPehod1cEPA2zB-lLm_HN7yi4,560 +ray/train/collective/__pycache__/__init__.cpython-312.pyc,, +ray/train/collective/__pycache__/collectives.cpython-312.pyc,, +ray/train/collective/collectives.py,sha256=2aQ5Z1vjoyacMqLaMPJUdDtZYGqyJyPDq77Pir1nMxY,2405 +ray/train/constants.py,sha256=UtAlwkuytnkQeALr7p38Nrd1uxp9QXtS7_J3bxXiIaA,6261 +ray/train/context.py,sha256=Iv_2bQhsyAzUigbrw6maBngLjG9wvzk0rIarkEm4n2c,4745 +ray/train/data_parallel_trainer.py,sha256=Y-4op8jv6fctCQd-NyL-PQQAWGHUJzUljIAsyaXCsEQ,22551 +ray/train/error.py,sha256=euJNlXHzrvcM40f831icdRoX5zV4SjRTJNEC-G2BOo8,183 +ray/train/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/examples/__pycache__/__init__.cpython-312.pyc,, +ray/train/examples/horovod/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/examples/horovod/__pycache__/__init__.cpython-312.pyc,, +ray/train/examples/horovod/__pycache__/horovod_example.cpython-312.pyc,, +ray/train/examples/horovod/__pycache__/horovod_pytorch_example.cpython-312.pyc,, +ray/train/examples/horovod/__pycache__/horovod_tune_example.cpython-312.pyc,, +ray/train/examples/horovod/horovod_example.py,sha256=uhCeg40nFaOiilucOSBedHkjRSqk-3VmTvAo-23Z4Xg,8286 +ray/train/examples/horovod/horovod_pytorch_example.py,sha256=IcdBk7RVE38cHHP048ryxQlzQgqu4mHC1P7frW4jRuA,8268 +ray/train/examples/horovod/horovod_tune_example.py,sha256=L0cCd0DX55eX2PpaGrFaWR3u7CLxEIjdrMmhwHfn0a0,3946 +ray/train/examples/pytorch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/examples/pytorch/__pycache__/__init__.cpython-312.pyc,, +ray/train/examples/pytorch/__pycache__/torch_fashion_mnist_example.cpython-312.pyc,, +ray/train/examples/pytorch/__pycache__/torch_linear_example.cpython-312.pyc,, +ray/train/examples/pytorch/__pycache__/torch_quick_start.cpython-312.pyc,, +ray/train/examples/pytorch/__pycache__/torch_regression_example.cpython-312.pyc,, +ray/train/examples/pytorch/torch_data_prefetch_benchmark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/examples/pytorch/torch_data_prefetch_benchmark/__pycache__/__init__.cpython-312.pyc,, +ray/train/examples/pytorch/torch_data_prefetch_benchmark/__pycache__/auto_pipeline_for_host_to_device_data_transfer.cpython-312.pyc,, +ray/train/examples/pytorch/torch_data_prefetch_benchmark/auto_pipeline_for_host_to_device_data_transfer.py,sha256=WIwsCRd0Ry1RK7W0cCBFJcpye4B-FXcXxd7yL8zEkas,4444 +ray/train/examples/pytorch/torch_fashion_mnist_example.py,sha256=03VBsFQ1_GrD6nDh7mRA5qy5PGzH_JFpzw7iI9K1Q5k,4851 +ray/train/examples/pytorch/torch_linear_example.py,sha256=uwgchCs6CUWVz41ppWocEmJDyXEYMQifMEeo7qSNmfw,4264 +ray/train/examples/pytorch/torch_quick_start.py,sha256=vQ5E5SN5D4BlRnVUe2b28NWYqMHvYWFV_fifg_fCtc0,2882 +ray/train/examples/pytorch/torch_regression_example.py,sha256=ziv6tkmqbz5LTCZuvX3VLsoNFdEZkglmCwq7M3N9kbU,4657 +ray/train/examples/pytorch_geometric/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/examples/pytorch_geometric/__pycache__/__init__.cpython-312.pyc,, +ray/train/examples/pytorch_geometric/__pycache__/distributed_sage_example.cpython-312.pyc,, +ray/train/examples/pytorch_geometric/distributed_sage_example.py,sha256=3Zv-gcOtb4eKmX-k8wJ1OI4R7GnzXku3agX5FkFUsC8,8004 +ray/train/examples/tf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/examples/tf/__pycache__/__init__.cpython-312.pyc,, +ray/train/examples/tf/__pycache__/tensorflow_autoencoder_example.cpython-312.pyc,, +ray/train/examples/tf/__pycache__/tensorflow_mnist_example.cpython-312.pyc,, +ray/train/examples/tf/__pycache__/tensorflow_quick_start.cpython-312.pyc,, +ray/train/examples/tf/__pycache__/tensorflow_regression_example.cpython-312.pyc,, +ray/train/examples/tf/tensorflow_autoencoder_example.py,sha256=7ewtUQmk-43J5vB9Yo2ZBIPnji7q1rD1sqC2qSQrBE8,5553 +ray/train/examples/tf/tensorflow_mnist_example.py,sha256=K594Xnvg8tYe6vjwhjjA-t7Edktt4I00uCPwcuVMTk4,4302 +ray/train/examples/tf/tensorflow_quick_start.py,sha256=OWZ1LcqSuIzpkP_HyY6axDHYTBnvv-b_HBSCfVmUtrI,2789 +ray/train/examples/tf/tensorflow_regression_example.py,sha256=jj8zWD8GEih2OnomgxBTcKPdO7dhnmjgwZW5fUNTWRA,3507 +ray/train/horovod/__init__.py,sha256=tm8swrwj4TENah8D-Eqj4CGGu_-BRjnANP9gmty7XXw,712 +ray/train/horovod/__pycache__/__init__.cpython-312.pyc,, +ray/train/horovod/__pycache__/config.cpython-312.pyc,, +ray/train/horovod/__pycache__/horovod_trainer.cpython-312.pyc,, +ray/train/horovod/config.py,sha256=7n6ze29mofmXM7_yeGyydE-trcqDoz4V-kEE5q9crr4,6086 +ray/train/horovod/horovod_trainer.py,sha256=WdtWokrMCNf2HtfdmwBeglUVTtfFjf7BnRJ9QHxPhRg,7968 +ray/train/huggingface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/huggingface/__pycache__/__init__.cpython-312.pyc,, +ray/train/huggingface/transformers/__init__.py,sha256=Ib3o0MSFGA4LlFDVM6O3tWTSIqz1ylXHNcmIDUGMooQ,229 +ray/train/huggingface/transformers/__pycache__/__init__.cpython-312.pyc,, +ray/train/huggingface/transformers/__pycache__/_transformers_utils.cpython-312.pyc,, +ray/train/huggingface/transformers/_transformers_utils.py,sha256=9Ae7s4UliVuLg2qm_JNzsDuIqnPWHCV5SoiVCt4WsyM,5865 +ray/train/lightgbm/__init__.py,sha256=WGmAfVKrQlpDYdkKyS_a0ophi-lRoUVpIbRpAGGgRbw,718 +ray/train/lightgbm/__pycache__/__init__.cpython-312.pyc,, +ray/train/lightgbm/__pycache__/_lightgbm_utils.cpython-312.pyc,, +ray/train/lightgbm/__pycache__/config.cpython-312.pyc,, +ray/train/lightgbm/__pycache__/lightgbm_checkpoint.cpython-312.pyc,, +ray/train/lightgbm/__pycache__/lightgbm_predictor.cpython-312.pyc,, +ray/train/lightgbm/__pycache__/lightgbm_trainer.cpython-312.pyc,, +ray/train/lightgbm/__pycache__/v2.cpython-312.pyc,, +ray/train/lightgbm/_lightgbm_utils.py,sha256=ekwPlcKWZ283XX8QpcpnkiGtWEIEl3tzMRafO3dHIZA,7543 +ray/train/lightgbm/config.py,sha256=Fbec0RjTzxjZX1vGn7gGl6uiHh6bvLQy-6zBbTvieB8,3091 +ray/train/lightgbm/lightgbm_checkpoint.py,sha256=dGaZGW9M_fL621cGswF80b4ZS0Rrm3wB7nV6SEqXg-8,2542 +ray/train/lightgbm/lightgbm_predictor.py,sha256=XUIJEE3QkxHZ-6bd6-j54jWcoVHBVgSvSa5T5pO5yis,5532 +ray/train/lightgbm/lightgbm_trainer.py,sha256=Y4MRk-KB02mxJjv6Zqcf8SQQ1k1p9dt3Jh38hWy8zlc,13540 +ray/train/lightgbm/v2.py,sha256=kKyLm3O0ZygQDpnoX6SrsAgTdJgs283RW8irv5ISYck,5714 +ray/train/lightning/__init__.py,sha256=Aehbb6TxsXA7uWMuvQJvk-vFyikW13N2FS_6y-L5aFc,767 +ray/train/lightning/__pycache__/__init__.cpython-312.pyc,, +ray/train/lightning/__pycache__/_lightning_utils.cpython-312.pyc,, +ray/train/lightning/_lightning_utils.py,sha256=QWj1z-qlwTAB6ULHNkK0Y2_aFk6xlJpmRu34ePjJoC8,10615 +ray/train/predictor.py,sha256=JCbutZP5SAcOZQ-HAYKfUkefh9MTLufvYaqI8IFHFJ8,9689 +ray/train/session.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/tensorflow/__init__.py,sha256=iTXFUv6FkmBiggPhqFXt-ZrE5P_mtdVmRqT0Ek4mflk,966 +ray/train/tensorflow/__pycache__/__init__.cpython-312.pyc,, +ray/train/tensorflow/__pycache__/config.cpython-312.pyc,, +ray/train/tensorflow/__pycache__/keras.cpython-312.pyc,, +ray/train/tensorflow/__pycache__/tensorflow_checkpoint.cpython-312.pyc,, +ray/train/tensorflow/__pycache__/tensorflow_predictor.cpython-312.pyc,, +ray/train/tensorflow/__pycache__/tensorflow_trainer.cpython-312.pyc,, +ray/train/tensorflow/__pycache__/train_loop_utils.cpython-312.pyc,, +ray/train/tensorflow/config.py,sha256=CcKcTQb8ACnW96uJCBqn8zzq-jnd7Rob5fWOVjqEMTk,1845 +ray/train/tensorflow/keras.py,sha256=WdRY-g0brF_dHgb54P1g09PkJFM-N0YTDXVKgtF4nGo,7342 +ray/train/tensorflow/tensorflow_checkpoint.py,sha256=k_9YVg5Z3KpakG8WwjI8M5d8X3WNHFO4i3ehyiPSHyk,5524 +ray/train/tensorflow/tensorflow_predictor.py,sha256=_P7ncCJJ4YPuu-1es9RWvt04HevQPuQbP24GEpSifn0,9625 +ray/train/tensorflow/tensorflow_trainer.py,sha256=RVGsoSzoFJBF5jG-3kmSRTJf1IbTkkfIww7MP68w89o,7436 +ray/train/tensorflow/train_loop_utils.py,sha256=d6_L-_lvVp1Jgqm5fncHLKNAJvplkITt-7kVtIgVcN0,934 +ray/train/torch/__init__.py,sha256=0fxqMHEUTpXTZb-s5PNpjzijw5Sj-drFeVnoHY5ZCL0,1479 +ray/train/torch/__pycache__/__init__.cpython-312.pyc,, +ray/train/torch/__pycache__/config.cpython-312.pyc,, +ray/train/torch/__pycache__/torch_checkpoint.cpython-312.pyc,, +ray/train/torch/__pycache__/torch_detection_predictor.cpython-312.pyc,, +ray/train/torch/__pycache__/torch_predictor.cpython-312.pyc,, +ray/train/torch/__pycache__/torch_trainer.cpython-312.pyc,, +ray/train/torch/__pycache__/train_loop_utils.cpython-312.pyc,, +ray/train/torch/config.py,sha256=JgZz25FP3tAA56JikZbjQDCAH7lfNR6uo9SMtCQOPz8,8451 +ray/train/torch/torch_checkpoint.py,sha256=t8SS2WcFk7BgfGR7FrG4eMje1DSv7Bs1h-ToPlvbR1A,6254 +ray/train/torch/torch_detection_predictor.py,sha256=KqM3SA2BMoydM3_llTLtOJqzDAFsF-S9mNuv3zMtrHw,2931 +ray/train/torch/torch_predictor.py,sha256=XgS64FwseUrsv9Q4J4zcb9RLEraPArFGec9lCYyrkPE,9649 +ray/train/torch/torch_trainer.py,sha256=7uGgalrz2oypNOIIVNEe1KYnUGCG8i-ZEQBEQbIZFTM,8482 +ray/train/torch/train_loop_utils.py,sha256=n-dfzIwdrSXzUShveKSOmH8GWnmrf90yYKxP2HIIrpk,29137 +ray/train/torch/xla/__init__.py,sha256=HqATrA5R9ncndEv6qkWLkMrCp29Veq9w0cSm6eQpxtw,91 +ray/train/torch/xla/__pycache__/__init__.cpython-312.pyc,, +ray/train/torch/xla/__pycache__/config.cpython-312.pyc,, +ray/train/torch/xla/config.py,sha256=19qkZ5UcnhPl8HdHgreCmOO49YW8WYzV7FGHufFmgL4,6465 +ray/train/trainer.py,sha256=UJZBObNzL7TdMYFu37S4ocq72rc4T01Uhw1gK_FjIfQ,6812 +ray/train/utils.py,sha256=Lsut8Asu-cUtzKpzg9LYY6fDwE5ZsxLYUfLnhkRigmA,807 +ray/train/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/_internal/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/__pycache__/constants.cpython-312.pyc,, +ray/train/v2/_internal/__pycache__/exceptions.cpython-312.pyc,, +ray/train/v2/_internal/__pycache__/migration_utils.cpython-312.pyc,, +ray/train/v2/_internal/__pycache__/util.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/__init__.py,sha256=DmbYUPyMIf51qhqONTeuL_iMVvvYzM5VyzFiaMUnqbY,553 +ray/train/v2/_internal/callbacks/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/__pycache__/accelerators.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/__pycache__/backend_setup.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/__pycache__/datasets.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/__pycache__/env_callback.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/__pycache__/metrics.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/__pycache__/state_manager.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/__pycache__/tpu_reservation_callback.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/__pycache__/user_callback.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/__pycache__/working_dir_setup.cpython-312.pyc,, +ray/train/v2/_internal/callbacks/accelerators.py,sha256=hZUl_-FskRSu8vjR4JQVe7DbvTCcspF9whAk-gxek20,5548 +ray/train/v2/_internal/callbacks/backend_setup.py,sha256=3eUrS42MELgFf_rCscIX4d1elT2jio1ATypVLIwp8iY,1056 +ray/train/v2/_internal/callbacks/datasets.py,sha256=0Av56bOVPmZoiRThvc7MUSfHEGbelQSBZfO-VgqCs6Y,4015 +ray/train/v2/_internal/callbacks/env_callback.py,sha256=8WpuPwZVGnjg-6rSYsj_bZg9Fb5qmZLpZfDw2jJtaZI,1410 +ray/train/v2/_internal/callbacks/metrics.py,sha256=kXYIvCH3fUYu3qhzmqXiyVIw8k1VzMt-VFlVHSgC8BU,3910 +ray/train/v2/_internal/callbacks/state_manager.py,sha256=Q3TSVGvajkMAeWhOZ0aS6ZLiUeWTXhL9AxMFtTNahAs,6382 +ray/train/v2/_internal/callbacks/tpu_reservation_callback.py,sha256=LVBU-znKQ8302LSfm1aaAJ99ALHeOcbPbaiwC2g-uaE,1662 +ray/train/v2/_internal/callbacks/user_callback.py,sha256=76VQtf8dBQhfZG7jHkp2OWhRkYE_EsdFU4ebwmascTk,1743 +ray/train/v2/_internal/callbacks/working_dir_setup.py,sha256=3hkjCiTRG7KlRTxgckwWNMC3Y_5D2d7Vq_XN_bNczl0,933 +ray/train/v2/_internal/constants.py,sha256=0A_NRcdTX6MxB_4zOday474xXl3tT2Svu4LeD_58pJI,5685 +ray/train/v2/_internal/data_integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/_internal/data_integration/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/data_integration/__pycache__/interfaces.cpython-312.pyc,, +ray/train/v2/_internal/data_integration/interfaces.py,sha256=jSz5FnPNc4PD70iau-NZMf0OrgiN4gh2AIoNail9RGo,969 +ray/train/v2/_internal/exceptions.py,sha256=mP2XgFxrBKDrr17Lf8IzL7-a8WvOwY_jAB6aOcy-hn0,5935 +ray/train/v2/_internal/execution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/_internal/execution/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/execution/__pycache__/callback.cpython-312.pyc,, +ray/train/v2/_internal/execution/__pycache__/collective_impl.cpython-312.pyc,, +ray/train/v2/_internal/execution/__pycache__/context.cpython-312.pyc,, +ray/train/v2/_internal/execution/__pycache__/storage.cpython-312.pyc,, +ray/train/v2/_internal/execution/__pycache__/train_fn_utils.cpython-312.pyc,, +ray/train/v2/_internal/execution/__pycache__/training_report.cpython-312.pyc,, +ray/train/v2/_internal/execution/callback.py,sha256=GHNhs38qKPowo5PeXA4Inv_Hu__oK-ltSfdrYOFWBzk,6430 +ray/train/v2/_internal/execution/checkpoint/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/_internal/execution/checkpoint/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/execution/checkpoint/__pycache__/checkpoint_manager.cpython-312.pyc,, +ray/train/v2/_internal/execution/checkpoint/__pycache__/report_handler.cpython-312.pyc,, +ray/train/v2/_internal/execution/checkpoint/__pycache__/sync_actor.cpython-312.pyc,, +ray/train/v2/_internal/execution/checkpoint/__pycache__/validation_manager.cpython-312.pyc,, +ray/train/v2/_internal/execution/checkpoint/checkpoint_manager.py,sha256=110D5WKCD7TZlLEcLXcoHYmQyz_xzvPM4ZaYOv7tdd8,15124 +ray/train/v2/_internal/execution/checkpoint/report_handler.py,sha256=IiY30_gum7Z0pvrEsIkW8PWONTi-mo6QDpMQzPJWP_Y,5329 +ray/train/v2/_internal/execution/checkpoint/sync_actor.py,sha256=5WZ3O0oeyVM74_HnntABMmVQ-3jy_sPH9IGKnvSzbeM,8732 +ray/train/v2/_internal/execution/checkpoint/validation_manager.py,sha256=r-VIkJOWRYgF79fds5Za5UbyHJBiEI5kHY3Tk2Wpqek,5627 +ray/train/v2/_internal/execution/collective_impl.py,sha256=CW83NLviqDK3nxrFm1UDFrF9kXwqLNAXTi1j5a1RY40,1880 +ray/train/v2/_internal/execution/context.py,sha256=4UlWP9TgbwS4yDT5QzaSrFJbjbXJEpfyljm1eT7LRSw,17346 +ray/train/v2/_internal/execution/controller/__init__.py,sha256=DFK8IXpD3lqtNhfaVhgPIy7erg45Gz9uwWdKqh6YC2Y,71 +ray/train/v2/_internal/execution/controller/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/execution/controller/__pycache__/controller.cpython-312.pyc,, +ray/train/v2/_internal/execution/controller/__pycache__/state.cpython-312.pyc,, +ray/train/v2/_internal/execution/controller/controller.py,sha256=_CzFehDjUwzYLi7D2C4pLbEYOVZvKpChFMUjawgd-Pc,22919 +ray/train/v2/_internal/execution/controller/state.py,sha256=Ejsxq4KlswR-GFGPnjTyzkkffe1Jt2k6XxRnVDrfrmE,5682 +ray/train/v2/_internal/execution/failure_handling/__init__.py,sha256=38KZGMtyxQi79wQD6HN8sWbPnXXRFYGNmn90j3a4lgk,327 +ray/train/v2/_internal/execution/failure_handling/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/execution/failure_handling/__pycache__/default.cpython-312.pyc,, +ray/train/v2/_internal/execution/failure_handling/__pycache__/factory.cpython-312.pyc,, +ray/train/v2/_internal/execution/failure_handling/__pycache__/failure_policy.cpython-312.pyc,, +ray/train/v2/_internal/execution/failure_handling/default.py,sha256=ZgYUF9p4E5DxUhEIn-E358UbSyz1K7M043z-_PzbY04,3432 +ray/train/v2/_internal/execution/failure_handling/factory.py,sha256=lH2CoyIxNJVXVv2pCl8csLZccu88noxIQWeUrJx3-tw,417 +ray/train/v2/_internal/execution/failure_handling/failure_policy.py,sha256=tEil4omF6Yzf0u4dmD28AlOMYQsvNJnsturACryoGOE,779 +ray/train/v2/_internal/execution/local_mode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/_internal/execution/local_mode/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/execution/local_mode/__pycache__/torch.cpython-312.pyc,, +ray/train/v2/_internal/execution/local_mode/__pycache__/utils.cpython-312.pyc,, +ray/train/v2/_internal/execution/local_mode/torch.py,sha256=neR-mXDSVmvACjeZM_iMfXoruAWy7e2JJoILSMeM-kw,3190 +ray/train/v2/_internal/execution/local_mode/utils.py,sha256=lCnj-7CDJ3LfJf60vrOMy4dj_IPa2ZAsa0EXBl4OMuk,1200 +ray/train/v2/_internal/execution/scaling_policy/__init__.py,sha256=ErmB335ru4RLODjGWtJz0YxlkUph-_fiePI8Qsly0XI,394 +ray/train/v2/_internal/execution/scaling_policy/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/execution/scaling_policy/__pycache__/factory.cpython-312.pyc,, +ray/train/v2/_internal/execution/scaling_policy/__pycache__/fixed.cpython-312.pyc,, +ray/train/v2/_internal/execution/scaling_policy/__pycache__/scaling_policy.cpython-312.pyc,, +ray/train/v2/_internal/execution/scaling_policy/factory.py,sha256=nxQKnFbjUuHbElXBVWdRYXzgp4KFEYbgPfWsKO5zQ9Q,423 +ray/train/v2/_internal/execution/scaling_policy/fixed.py,sha256=qZkjmPtPweqKfbLT9ry52mpcG8bQ35vkMyGNLanldhw,773 +ray/train/v2/_internal/execution/scaling_policy/scaling_policy.py,sha256=kjcX5EW2bsjqxM1oJgqV5t-zWcTvx3h0aG_qM0nQxsY,1698 +ray/train/v2/_internal/execution/storage.py,sha256=j4IL1nrVLETsiPb421MIkPyPcbKKixc8kpWth7o5rUo,21029 +ray/train/v2/_internal/execution/train_fn_utils.py,sha256=LyWzBgoDbVY0bQDxE8CWLO6Cu2OqqHFw9piW-v85YEs,10101 +ray/train/v2/_internal/execution/training_report.py,sha256=WT2UC7ktZXMdalBgAz0DQpiFCqyywFMRbLN_0dr3V4k,1124 +ray/train/v2/_internal/execution/worker_group/__init__.py,sha256=8qAup6gRMzStV4xTpYL_NjpTbWjVsvKseLuFfdKHcik,465 +ray/train/v2/_internal/execution/worker_group/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/execution/worker_group/__pycache__/poll.cpython-312.pyc,, +ray/train/v2/_internal/execution/worker_group/__pycache__/state.cpython-312.pyc,, +ray/train/v2/_internal/execution/worker_group/__pycache__/thread_runner.cpython-312.pyc,, +ray/train/v2/_internal/execution/worker_group/__pycache__/worker.cpython-312.pyc,, +ray/train/v2/_internal/execution/worker_group/__pycache__/worker_group.cpython-312.pyc,, +ray/train/v2/_internal/execution/worker_group/poll.py,sha256=dsym_0uwLjqP8DUMr3qYnosBVpvW0yMBoQbtzH2Ua_U,4111 +ray/train/v2/_internal/execution/worker_group/state.py,sha256=nioXDTo6Tti1yVfztfHVmZD7GW7qpxg-PnwBmWU8ui4,4361 +ray/train/v2/_internal/execution/worker_group/thread_runner.py,sha256=6EMMcIm3hg7zcyfRDDhE-mbPU8BcBz_tqnD1QpTnG6s,3129 +ray/train/v2/_internal/execution/worker_group/worker.py,sha256=89ShFThLjTFB3-ti_fOImwBA0OK-WmRrJ2XfweUDFnQ,8649 +ray/train/v2/_internal/execution/worker_group/worker_group.py,sha256=pFE7JeYc7PAH82BSe3ZE7ux4_b_djU80Iwdg7q4ZhGI,32379 +ray/train/v2/_internal/logging/__init__.py,sha256=ikEwR6jPbgiSa4zan2lRHrS4wzx6rPy3j_ZilEHTu_o,66 +ray/train/v2/_internal/logging/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/logging/__pycache__/logging.cpython-312.pyc,, +ray/train/v2/_internal/logging/__pycache__/patch_print.cpython-312.pyc,, +ray/train/v2/_internal/logging/logging.py,sha256=XWrEU7Ue_XMlMdp2vyndAcnAmvQO-RMPW3cMxx75UeA,11618 +ray/train/v2/_internal/logging/patch_print.py,sha256=bKL-lBTCX8FSntz17gddJ9MwnRerb3LUuWntD462YwE,2557 +ray/train/v2/_internal/metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/_internal/metrics/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/metrics/__pycache__/base.cpython-312.pyc,, +ray/train/v2/_internal/metrics/__pycache__/controller.cpython-312.pyc,, +ray/train/v2/_internal/metrics/__pycache__/worker.cpython-312.pyc,, +ray/train/v2/_internal/metrics/base.py,sha256=wAvqYQfRtHY36LbGnz-piflX_PZrbF2axuMFfMPBkT8,4386 +ray/train/v2/_internal/metrics/controller.py,sha256=7iPtFJhvV25bgbQasfpaX4iMJsZtQ0Z8IytF3dG2tmE,2360 +ray/train/v2/_internal/metrics/worker.py,sha256=pvA0-jFQBE5ZCufDNImfi-fKtmezdfLBdBznPcsUIXE,1628 +ray/train/v2/_internal/migration_utils.py,sha256=-Slxd7i0_-JMkYSR3V-4I5UUMLF2Q2hUwTIsUusEZUA,2876 +ray/train/v2/_internal/state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/_internal/state/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/_internal/state/__pycache__/export.cpython-312.pyc,, +ray/train/v2/_internal/state/__pycache__/schema.cpython-312.pyc,, +ray/train/v2/_internal/state/__pycache__/state_actor.cpython-312.pyc,, +ray/train/v2/_internal/state/__pycache__/state_manager.cpython-312.pyc,, +ray/train/v2/_internal/state/__pycache__/util.cpython-312.pyc,, +ray/train/v2/_internal/state/export.py,sha256=2SfPb7GhmtAByWISfOSU9YGZlNUuv6pwXeB-rqhTtgQ,4562 +ray/train/v2/_internal/state/schema.py,sha256=t2vH4YNzdHzE0VTX5MuuLlQvMnkMauvhLzhYV3lc0nk,9230 +ray/train/v2/_internal/state/state_actor.py,sha256=bsUSA9S5rMFyI82_q-c653G4vvGIBS-4xH6osxOLxFA,11304 +ray/train/v2/_internal/state/state_manager.py,sha256=XHx8B9InFExKnZ29C-J3_9hWk4P8DvBCcP7NtkbJLUs,8150 +ray/train/v2/_internal/state/util.py,sha256=sxo6HQsg977_npkBqRa20hk6mnfNioxAu-r2QXg_hSY,1486 +ray/train/v2/_internal/util.py,sha256=3ckxZUw2Emz-wOFSIDYraTh-anIKBzeijwQA0C5cpws,9074 +ray/train/v2/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/api/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/api/__pycache__/callback.cpython-312.pyc,, +ray/train/v2/api/__pycache__/config.cpython-312.pyc,, +ray/train/v2/api/__pycache__/context.cpython-312.pyc,, +ray/train/v2/api/__pycache__/data_parallel_trainer.cpython-312.pyc,, +ray/train/v2/api/__pycache__/exceptions.cpython-312.pyc,, +ray/train/v2/api/__pycache__/report_config.cpython-312.pyc,, +ray/train/v2/api/__pycache__/reported_checkpoint.cpython-312.pyc,, +ray/train/v2/api/__pycache__/result.cpython-312.pyc,, +ray/train/v2/api/__pycache__/train_fn_utils.cpython-312.pyc,, +ray/train/v2/api/callback.py,sha256=V3tu9O6RjRiV7pzqBp2pVSG9f-km1IyS4RseXRZDsFk,1716 +ray/train/v2/api/config.py,sha256=5pH_rXjFZ-U9iQ6C_t_y7bEsgRcnn-5UCEUwtwb_Mhk,13840 +ray/train/v2/api/context.py,sha256=GAApSAHl67G0dRvoA9-iXuRjOo6XDsEQw2fX3l-fD5U,7686 +ray/train/v2/api/data_parallel_trainer.py,sha256=HKmBHusvrpE9Z3T34eqCUFCwnPUQwsOnSCmsKpjL7aE,13192 +ray/train/v2/api/exceptions.py,sha256=FyT1uksSimHXTAScandF-DWFWUJhNmhQR2ot7EEbvaE,1739 +ray/train/v2/api/report_config.py,sha256=iTOESF5PsRCPPUROllPjsOAAjhKQpnDpc0zCbvJZRQ4,540 +ray/train/v2/api/reported_checkpoint.py,sha256=17Z1cThQewHA6c2TIQ_XQxNLrZtbGiktkox_WVR5aHk,511 +ray/train/v2/api/result.py,sha256=fRyx87X4oss-rasTqx-JAqyuMCugMhldKKQoFVkJYb0,4859 +ray/train/v2/api/train_fn_utils.py,sha256=5MbkV_VcgUmujDAk5CzUAXIlYtEeYiV1Wjv9HoMBw_8,10350 +ray/train/v2/horovod/__init__.py,sha256=Br95ZDGGdgreAX5Fgkc1Cmeqo-9A9dE6FaeATXL5Su0,39 +ray/train/v2/horovod/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/horovod/__pycache__/horovod_trainer.cpython-312.pyc,, +ray/train/v2/horovod/horovod_trainer.py,sha256=voUsALtb6Rqf2TtpFo65PKwuRtBGFpZK-I7ypaOAyXI,1442 +ray/train/v2/jax/__init__.py,sha256=4bGMTXwBfDcULr4TsTVQ1YKH09_1rFaKlKXPDJOyZZ0,483 +ray/train/v2/jax/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/jax/__pycache__/config.cpython-312.pyc,, +ray/train/v2/jax/__pycache__/jax_trainer.cpython-312.pyc,, +ray/train/v2/jax/config.py,sha256=5n1tscfab6e_-SETtVCFx3661kkG4hwA8BlV-qLcV90,3994 +ray/train/v2/jax/jax_trainer.py,sha256=wJArvky0MYkxhueSpCdwFra_gss8NMy_nT17ILkGjYk,6629 +ray/train/v2/lightgbm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/lightgbm/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/lightgbm/__pycache__/lightgbm_trainer.cpython-312.pyc,, +ray/train/v2/lightgbm/lightgbm_trainer.py,sha256=3_MvnEg56Q0PYNC_2oiG-AhspA2nJOyk8_XdaJ1NIqk,7197 +ray/train/v2/tensorflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/train/v2/tensorflow/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/tensorflow/__pycache__/tensorflow_trainer.cpython-312.pyc,, +ray/train/v2/tensorflow/tensorflow_trainer.py,sha256=beHzhZDOdIxjgZiOnjIKIeUGv3R42qIown-aipZLjxA,7493 +ray/train/v2/torch/__init__.py,sha256=WCOUgvllFY-eSL_2aV37YvnxEipdYOHxKYf4_AcF4ro,92 +ray/train/v2/torch/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/torch/__pycache__/torch_trainer.cpython-312.pyc,, +ray/train/v2/torch/__pycache__/train_loop_utils.cpython-312.pyc,, +ray/train/v2/torch/torch_trainer.py,sha256=qgJd1WtY5AcKxr07oCqtiUof4RFt94DQS1OIUc9sfyg,8637 +ray/train/v2/torch/train_loop_utils.py,sha256=5GMS6DVgYYWrJ2WRX-Bj5hd8UY3UxPDyS7k55Yy62es,15589 +ray/train/v2/xgboost/__init__.py,sha256=eyoQprMTQg0Hl38SxtpZi8Cj40rny4l_qmnKbnPP89U,111 +ray/train/v2/xgboost/__pycache__/__init__.cpython-312.pyc,, +ray/train/v2/xgboost/__pycache__/config.cpython-312.pyc,, +ray/train/v2/xgboost/__pycache__/xgboost_trainer.cpython-312.pyc,, +ray/train/v2/xgboost/config.py,sha256=LZQ2-dD2OShP1wXh-5i2HpnBuKSQC3vvGqWy3uA3ArY,711 +ray/train/v2/xgboost/xgboost_trainer.py,sha256=zhMKKt6D-DAGsP5fepZUDt4zoU5E4pWIqJGq_7xsREY,6853 +ray/train/xgboost/__init__.py,sha256=vcwud9pv4WZH3pZJuczmPLdpypdEcqU3enq7U0ufPsk,724 +ray/train/xgboost/__pycache__/__init__.cpython-312.pyc,, +ray/train/xgboost/__pycache__/_xgboost_utils.cpython-312.pyc,, +ray/train/xgboost/__pycache__/config.cpython-312.pyc,, +ray/train/xgboost/__pycache__/v2.cpython-312.pyc,, +ray/train/xgboost/__pycache__/xgboost_checkpoint.cpython-312.pyc,, +ray/train/xgboost/__pycache__/xgboost_predictor.cpython-312.pyc,, +ray/train/xgboost/__pycache__/xgboost_trainer.cpython-312.pyc,, +ray/train/xgboost/_xgboost_utils.py,sha256=nzU9fK9FT5abWrBVWRaL7gA0-5Zyc1Zw57UqWNhfrhc,8873 +ray/train/xgboost/config.py,sha256=upANRHKvyMwJfADAlXgCVVN6ueCO3H8bYUAc6nAD--8,6980 +ray/train/xgboost/v2.py,sha256=iqUfoyY4q5ohGyUkne-KC3Dwm4aWBMmsmJ2WtvWH46A,5655 +ray/train/xgboost/xgboost_checkpoint.py,sha256=svToFi-g7Uko5_R2C9-0B4ILJn1ep0tnOUi4YJP9yhs,2591 +ray/train/xgboost/xgboost_predictor.py,sha256=wZwT2pS1C1TQo-JLzZ4rNgP89qTlFOK-Sju5wnI2qIA,5718 +ray/train/xgboost/xgboost_trainer.py,sha256=6fXWk39vUlxia53ketrheS9lb_zNVx50e7Y2MK5EfOE,13184 +ray/tune/__init__.py,sha256=unATkNBZ-bueVCtajY4wclNV1CdGDf4_qlsRCg43GN8,2982 +ray/tune/__pycache__/__init__.cpython-312.pyc,, +ray/tune/__pycache__/callback.cpython-312.pyc,, +ray/tune/__pycache__/constants.cpython-312.pyc,, +ray/tune/__pycache__/context.cpython-312.pyc,, +ray/tune/__pycache__/error.cpython-312.pyc,, +ray/tune/__pycache__/progress_reporter.cpython-312.pyc,, +ray/tune/__pycache__/registry.cpython-312.pyc,, +ray/tune/__pycache__/resources.cpython-312.pyc,, +ray/tune/__pycache__/result.cpython-312.pyc,, +ray/tune/__pycache__/result_grid.cpython-312.pyc,, +ray/tune/__pycache__/syncer.cpython-312.pyc,, +ray/tune/__pycache__/tune.cpython-312.pyc,, +ray/tune/__pycache__/tune_config.cpython-312.pyc,, +ray/tune/__pycache__/tuner.cpython-312.pyc,, +ray/tune/analysis/__init__.py,sha256=Sxr-eFig0Rj8jDrCJa32Daqjv_DO128TqnFqEdkG7Kg,103 +ray/tune/analysis/__pycache__/__init__.cpython-312.pyc,, +ray/tune/analysis/__pycache__/experiment_analysis.cpython-312.pyc,, +ray/tune/analysis/experiment_analysis.py,sha256=5LpJ7jH1YYGjqWINP_hfvvV6SvEQZw3Z6RebJCQbjso,27759 +ray/tune/automl/__init__.py,sha256=UqhsT-DhBbmz6AOlDYz3ag6DZYRzRHo8htgqnIxmvlU,72 +ray/tune/automl/__pycache__/__init__.cpython-312.pyc,, +ray/tune/callback.py,sha256=sAXyNn5BIT_ykNIPggjlkDKy1nPgKn5K_1th0itvVYg,17047 +ray/tune/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/tune/cli/__pycache__/__init__.cpython-312.pyc,, +ray/tune/cli/__pycache__/commands.cpython-312.pyc,, +ray/tune/cli/__pycache__/scripts.cpython-312.pyc,, +ray/tune/cli/commands.py,sha256=F8VgBXl7mDHSMkxHE_cYr9yIncpjeYYmq0LRerTQ8YE,9995 +ray/tune/cli/scripts.py,sha256=k3eklucFbN8SOEeLz2EBElrtZPG8G6abXzfjgY3FsIU,2826 +ray/tune/constants.py,sha256=XLkcZ-DJYx6a0fm5QcxdBR0n5VFdEN6Iylt_kkB4m70,1298 +ray/tune/context.py,sha256=YyyNGigLBB0NX1vyJswIyMK2cwI0p1tTMzz1uB5BGN4,3887 +ray/tune/error.py,sha256=16nO4FFJyHaxN-hjwaljJ8qCqjL7NV7oabY-DsTYDcM,1115 +ray/tune/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/tune/examples/__pycache__/__init__.cpython-312.pyc,, +ray/tune/examples/__pycache__/async_hyperband_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/ax_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/bayesopt_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/bohb_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/cifar10_pytorch.cpython-312.pyc,, +ray/tune/examples/__pycache__/custom_checkpointing_with_callback.cpython-312.pyc,, +ray/tune/examples/__pycache__/custom_func_checkpointing.cpython-312.pyc,, +ray/tune/examples/__pycache__/hyperband_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/hyperband_function_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/hyperopt_conditional_search_space_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/lightgbm_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/logging_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/mlflow_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/mlflow_ptl.cpython-312.pyc,, +ray/tune/examples/__pycache__/mnist_ptl_mini.cpython-312.pyc,, +ray/tune/examples/__pycache__/mnist_pytorch.cpython-312.pyc,, +ray/tune/examples/__pycache__/mnist_pytorch_trainable.cpython-312.pyc,, +ray/tune/examples/__pycache__/nevergrad_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/optuna_define_by_run_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/optuna_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/optuna_multiobjective_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/pb2_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/pb2_ppo_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/pbt_convnet_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/pbt_convnet_function_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/pbt_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/pbt_function.cpython-312.pyc,, +ray/tune/examples/__pycache__/pbt_memnn_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/pbt_ppo_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/pbt_tune_cifar10_with_keras.cpython-312.pyc,, +ray/tune/examples/__pycache__/tf_mnist_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/tune_basic_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/tune_mnist_keras.cpython-312.pyc,, +ray/tune/examples/__pycache__/utils.cpython-312.pyc,, +ray/tune/examples/__pycache__/xgboost_dynamic_resources_example.cpython-312.pyc,, +ray/tune/examples/__pycache__/xgboost_example.cpython-312.pyc,, +ray/tune/examples/async_hyperband_example.py,sha256=dQQe006pVOyhQIWtRmkkCFa_miGSzLwvvJK-c9iPBnQ,2067 +ray/tune/examples/ax_example.py,sha256=j5sWPm3zCSh2UyOy2AlwnPSB_bxfhp_nesmb41gXr68,2810 +ray/tune/examples/bayesopt_example.py,sha256=7YF_wuX9wtBQUtZOCUg6JfDlu3WiUHPf1_KQLswrcYw,1945 +ray/tune/examples/bohb_example.py,sha256=V-QCiyF6NfShT0S4lEOVjReyLJJ7P8BQbtnPR81otdU,3203 +ray/tune/examples/cifar10_pytorch.py,sha256=GdJQG7naNjVY3M30_qQidE3kQCTnrQo5psMJYYuV71Y,9332 +ray/tune/examples/custom_checkpointing_with_callback.py,sha256=8DHfbsYxaLOAegZuw4FSs0oNmMgB9vcuIQzl6btzlwM,8147 +ray/tune/examples/custom_func_checkpointing.py,sha256=8VWgM0S9QYER0d0syZUgZ8Z2kOm_0qIKGVSiIi1jzAU,2262 +ray/tune/examples/hyperband_example.py,sha256=t31YtY-Q-mMxZDusNhv1hYePGMWTgDabdFcGkLT1eBA,1437 +ray/tune/examples/hyperband_function_example.py,sha256=mMksl4C1acDCcXHfWcjqGl7xt5Z4nR5HNEj0fAyYII8,2499 +ray/tune/examples/hyperopt_conditional_search_space_example.py,sha256=DCKWem1Pt9Yw0tnzGxCYCWIkjnvKnyArYd6zXfXTxAk,3004 +ray/tune/examples/lightgbm_example.py,sha256=WOrcIWfKCtwKmpYLdquKRHqay_0-HxAiKImmhXvrxxw,3130 +ray/tune/examples/logging_example.py,sha256=acembYNzu5yE7odY5H1Jw3SH0DOJkjGRlPGaULjrfzQ,1877 +ray/tune/examples/mlflow_example.py,sha256=tNkRq5GtLUf6RU525t0zPrWxIxaXYET8UQ9zGuY-Ks4,3833 +ray/tune/examples/mlflow_ptl.py,sha256=bJ-2qrPH7NkBHkaP2cN_rfZl002-X7vUjRDEaxu47dA,3153 +ray/tune/examples/mnist_ptl_mini.py,sha256=IQvvczCYa5udukgsuwI_uO6RIfH2zHRVixXVi8EiMrg,5527 +ray/tune/examples/mnist_pytorch.py,sha256=QMJTB9x_d9bHAmqbaLNPhJT0ITxB3_xi6lGihk775IE,5044 +ray/tune/examples/mnist_pytorch_trainable.py,sha256=wbEurOpTEIXUqHyfFlgPUF8OmtC3CXixak1WqAAbSto,3106 +ray/tune/examples/nevergrad_example.py,sha256=VPIFuWdBhOyfPhO9nELxJWcH6LMQck5-uRx6svDp0Ps,2329 +ray/tune/examples/optuna_define_by_run_example.py,sha256=dskRFVkcnSCo6UiN0HNF-FxQqNFRH-KZiu29wK3VrkE,2969 +ray/tune/examples/optuna_example.py,sha256=6Yc5BXcUjah36ENicmxxh1ZORPvZpure4a_Xh2JDj4A,2165 +ray/tune/examples/optuna_multiobjective_example.py,sha256=1xvTmKklPOFCmeSLuT0qLQkIY3YhoMuX-1CGIgZuQ0I,2252 +ray/tune/examples/pb2_example.py,sha256=XHJeD1it3TwNfExzsvPd4SABF0b95ghxXMsZT1u1GgA,1818 +ray/tune/examples/pb2_ppo_example.py,sha256=_Cwb-o332TRH2kIBtDbH9oWT_GmKR5w9EPjvM2nB7wM,5391 +ray/tune/examples/pbt_convnet_example.py,sha256=7APMP4VygYfeB7SwOuDC3M1aWGwgl9m7m43F-n6zb04,4175 +ray/tune/examples/pbt_convnet_function_example.py,sha256=md6n9_BXoHobkUg2hDNB4Tw0rV_L3I2T4wb7VRH4oR0,4644 +ray/tune/examples/pbt_dcgan_mnist/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/tune/examples/pbt_dcgan_mnist/__pycache__/__init__.cpython-312.pyc,, +ray/tune/examples/pbt_dcgan_mnist/__pycache__/common.cpython-312.pyc,, +ray/tune/examples/pbt_dcgan_mnist/__pycache__/pbt_dcgan_mnist_func.cpython-312.pyc,, +ray/tune/examples/pbt_dcgan_mnist/__pycache__/pbt_dcgan_mnist_trainable.cpython-312.pyc,, +ray/tune/examples/pbt_dcgan_mnist/common.py,sha256=zUOEcuyITpO9ae3lD3clYPO4IqyWIcpHhkDEgbmgGTg,8107 +ray/tune/examples/pbt_dcgan_mnist/pbt_dcgan_mnist_func.py,sha256=tBjz5sxL5LI6sh8sH3qoaYnn9XCKm9er4lChAIgUsqA,5964 +ray/tune/examples/pbt_dcgan_mnist/pbt_dcgan_mnist_trainable.py,sha256=oNr8QC_8ZiqxeC_ctCTrnEVBVftv5281wpsY2z8OuHo,5822 +ray/tune/examples/pbt_example.py,sha256=od1rVLlUqlgh3HkQ6wjbzuF9uxHnmDqLFV0w_1h1NCI,5070 +ray/tune/examples/pbt_function.py,sha256=xWqgcRMh81ykzKooz_QeUHyW1S0nuIuz_EuI9gLl4sU,6689 +ray/tune/examples/pbt_memnn_example.py,sha256=ovRr3xY-EXjHJ6vu4yRw6KKENMZ3omOxrX6D926jOrI,11098 +ray/tune/examples/pbt_ppo_example.py,sha256=BBXo4GsCGSoMP3KrZjDURqgFSAUHy2FHtXc4HlfY37o,2669 +ray/tune/examples/pbt_transformers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/tune/examples/pbt_transformers/__pycache__/__init__.cpython-312.pyc,, +ray/tune/examples/pbt_transformers/__pycache__/pbt_transformers.cpython-312.pyc,, +ray/tune/examples/pbt_transformers/__pycache__/utils.cpython-312.pyc,, +ray/tune/examples/pbt_transformers/pbt_transformers.py,sha256=szWDgtp7maw9FIEwI22tVoVZbAIJbr1JbHW65sUWKhg,4932 +ray/tune/examples/pbt_transformers/utils.py,sha256=yx8y1lSDfDNKSivZGj6FTpeabxJhknsXJTjuLFERd9I,1571 +ray/tune/examples/pbt_tune_cifar10_with_keras.py,sha256=ZjoqcNy7yvD5t-lRTSCcvH6iv3bDIebnfX6zaODtTLE,7575 +ray/tune/examples/tf_mnist_example.py,sha256=JAwCI6tZVrfzMmvXaH8q3IxpwKHoFzKg_oPZ-bsqoXY,5060 +ray/tune/examples/tune_basic_example.py,sha256=X3zOhibZ_mD9OKyEwGXj3esUR9nF_ukp86c4JxqDvpo,1823 +ray/tune/examples/tune_mnist_keras.py,sha256=FffBGqx3fp2tG3RUJy0LKVmlwp-vs5apODgo-Vtw6rQ,2824 +ray/tune/examples/utils.py,sha256=G0HzAiN4HikUGD-zkR2zwdkJbXFjUs4LyeUwvhCT6wA,747 +ray/tune/examples/xgboost_dynamic_resources_example.py,sha256=GaAJCTb3pspAB3mXwuy-U8zZB4jAFR1-MGmpn0NxdEw,6516 +ray/tune/examples/xgboost_example.py,sha256=NiXiRAcZQlhb02x0qjh_b2qCweGIlhNFQoPNp2whYcw,3992 +ray/tune/execution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/tune/execution/__pycache__/__init__.cpython-312.pyc,, +ray/tune/execution/__pycache__/class_cache.cpython-312.pyc,, +ray/tune/execution/__pycache__/cluster_info.cpython-312.pyc,, +ray/tune/execution/__pycache__/experiment_state.cpython-312.pyc,, +ray/tune/execution/__pycache__/insufficient_resources_manager.cpython-312.pyc,, +ray/tune/execution/__pycache__/placement_groups.cpython-312.pyc,, +ray/tune/execution/__pycache__/tune_controller.cpython-312.pyc,, +ray/tune/execution/class_cache.py,sha256=bf3E672_X6ZQXidavDoAFzGe_CKwlXBcK2lAke94GPU,2377 +ray/tune/execution/cluster_info.py,sha256=rwJC-i6hwLzF38tTVzjgYs64Xc6vwPJxGxU4QIVno7o,326 +ray/tune/execution/experiment_state.py,sha256=DBGYLLlFmw68rVLSzfivrOGmcfUMCwMObg0_WsJUO_o,11784 +ray/tune/execution/insufficient_resources_manager.py,sha256=P8L-ogsvuySa7Wti6rUpjM-OSCQWMX5rGzk5HtK3K9k,5982 +ray/tune/execution/placement_groups.py,sha256=FrtdH4Lu5VYMZFdPQFEpuQF0HuESqZ0cQX_1BgAiOTk,4176 +ray/tune/execution/tune_controller.py,sha256=hM13_kdvZdLYY-X-fyPgCpeuwUUSjPcGjgVA-bJmyDw,84556 +ray/tune/experiment/__init__.py,sha256=dSHNyUYJumOR_g6a3q945P960PsUc3LK34wJBkHyZMA,193 +ray/tune/experiment/__pycache__/__init__.cpython-312.pyc,, +ray/tune/experiment/__pycache__/config_parser.cpython-312.pyc,, +ray/tune/experiment/__pycache__/experiment.cpython-312.pyc,, +ray/tune/experiment/__pycache__/trial.cpython-312.pyc,, +ray/tune/experiment/config_parser.py,sha256=bkrN9XDfPknDg51zxoY_t07AUgXIERuhyf38FR2VYEg,6997 +ray/tune/experiment/experiment.py,sha256=aajbMPPFmsmTVJO4-akr4nlVl6MFYRa3bMEOuZOGdBk,16042 +ray/tune/experiment/trial.py,sha256=Qt-47pD2eflVH334NLTmrigKexTmdE8rl8fDAkp7kvo,38610 +ray/tune/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/tune/experimental/__pycache__/__init__.cpython-312.pyc,, +ray/tune/experimental/__pycache__/output.cpython-312.pyc,, +ray/tune/experimental/output.py,sha256=lCfFrxrLVnFRx3G-Ev3M4SwhbZQHNxKdrvNaCVRon8k,32553 +ray/tune/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/tune/impl/__pycache__/__init__.cpython-312.pyc,, +ray/tune/impl/__pycache__/config.cpython-312.pyc,, +ray/tune/impl/__pycache__/out_of_band_serialize_dataset.cpython-312.pyc,, +ray/tune/impl/__pycache__/placeholder.cpython-312.pyc,, +ray/tune/impl/__pycache__/test_utils.cpython-312.pyc,, +ray/tune/impl/__pycache__/tuner_internal.cpython-312.pyc,, +ray/tune/impl/config.py,sha256=bgoW3SWEAQ3u7-J91ZcYNgtHv0Dwt_0IAEuZCB10Rnc,1746 +ray/tune/impl/out_of_band_serialize_dataset.py,sha256=NbgL8paX7IN3XPn9hy63qjvwdundFh-D5SWmHzrZ2VI,1056 +ray/tune/impl/placeholder.py,sha256=IWgvbFGRSty6tQnWL6CJljpGBxXgUFyT_9npR87j28w,8813 +ray/tune/impl/test_utils.py,sha256=pDWVF14rD8D8X6H9vvBf46oUNCCEwYIuBhe7QBvw0zc,2186 +ray/tune/impl/tuner_internal.py,sha256=vxmZB68aFPPfX26WtCVa76Xj7Y52wWcZm2EiPndurzY,27258 +ray/tune/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/tune/integration/__pycache__/__init__.cpython-312.pyc,, +ray/tune/integration/__pycache__/keras.cpython-312.pyc,, +ray/tune/integration/__pycache__/lightgbm.cpython-312.pyc,, +ray/tune/integration/__pycache__/pytorch_lightning.cpython-312.pyc,, +ray/tune/integration/__pycache__/ray_train.cpython-312.pyc,, +ray/tune/integration/__pycache__/xgboost.cpython-312.pyc,, +ray/tune/integration/keras.py,sha256=bVl7D37MlxBqRRW89ESurVmNpvVcmxdqlRyjV0U5I6g,2516 +ray/tune/integration/lightgbm.py,sha256=PHFKgF5dtPohe7purkfljkd5RdvmSk2_lEhAVN7ee5w,3025 +ray/tune/integration/pytorch_lightning.py,sha256=fc8aG3F_C5IJa6xPQBhh6dlx8ILqam1XiWup1v8dlU0,7143 +ray/tune/integration/ray_train.py,sha256=Y0P_W1Y50PG2ftxGWTmIA5u_EOYiytleLspvag7td50,1513 +ray/tune/integration/xgboost.py,sha256=7mCYy2jx14dNW96dhbHIaJr5GizqRnl8EOZOCNmqinQ,3795 +ray/tune/logger/__init__.py,sha256=ec5n9fHOC6AYFe6mVK3_JQVsEm9QEQpiSUSDnChkjIQ,765 +ray/tune/logger/__pycache__/__init__.cpython-312.pyc,, +ray/tune/logger/__pycache__/aim.cpython-312.pyc,, +ray/tune/logger/__pycache__/comet.cpython-312.pyc,, +ray/tune/logger/__pycache__/csv.cpython-312.pyc,, +ray/tune/logger/__pycache__/json.cpython-312.pyc,, +ray/tune/logger/__pycache__/logger.cpython-312.pyc,, +ray/tune/logger/__pycache__/mlflow.cpython-312.pyc,, +ray/tune/logger/__pycache__/noop.cpython-312.pyc,, +ray/tune/logger/__pycache__/tensorboardx.cpython-312.pyc,, +ray/tune/logger/__pycache__/unified.cpython-312.pyc,, +ray/tune/logger/__pycache__/wandb.cpython-312.pyc,, +ray/tune/logger/aim.py,sha256=Dr0sTPvOYY4IbMo0sVHve5qQ20bAskfu8FvR176s0s8,6820 +ray/tune/logger/comet.py,sha256=09ZFx30_O-Ixa438gkpdb2VXyQaL6R9i6z6gJwqaiPw,117 +ray/tune/logger/csv.py,sha256=y412dEU18vaRZCCMDQ_pQ1lBLuD7l5ToY_f2ZTaGgtI,4149 +ray/tune/logger/json.py,sha256=bWXvg9cP-q4BNLxO53XGHZmeVHkYrKM7V7Q-upKPHCc,4179 +ray/tune/logger/logger.py,sha256=27FXI7Z9pBazie_NMhU0ERXk28jVJZh_SJhLBIs0oRc,8122 +ray/tune/logger/mlflow.py,sha256=iyOkDQZjyMwfBVvjBjWnwRyumU_Z63qTzUqjy3EMWCc,121 +ray/tune/logger/noop.py,sha256=j_LlYmHYMOELf6Umqyu5H4z3MoVd7WF33ozGYcgIM_I,246 +ray/tune/logger/tensorboardx.py,sha256=bE3Py7OaYAI8tUZgIKww6qka3AvYKJaA8nnwdf_sltY,12347 +ray/tune/logger/unified.py,sha256=GdRmhAZI9PgEb_U_X0gWqSz78xHi0EcBnCKzDvJhEmk,2318 +ray/tune/logger/wandb.py,sha256=huF0YInAclUPEwJ2nFnvv54eZ-Lz_dkRM-5PbCth5p0,117 +ray/tune/progress_reporter.py,sha256=pzNY4dsrNgAu_RyPtv6B9XsrU_Z5UVioZw_z9eEXL1c,58718 +ray/tune/registry.py,sha256=gxZ-onnXvApjJofIfzfmj1zwIXXmjPmdGjjL7kil9yA,9426 +ray/tune/resources.py,sha256=W54BRyE7POQi4xErotDBEDHPy_Cqb3EdkcLVFwk2N9o,2535 +ray/tune/result.py,sha256=BCyS4NQkXLnABeAMJsjw22Cluf1k_gMuftgYMQZy5GY,3484 +ray/tune/result_grid.py,sha256=_U17twulIJfXgERyZ5gFfyp5ZOWZya2Fynbyq_QNzaA,10432 +ray/tune/schedulers/__init__.py,sha256=MFKKUApee95fgsLJyxo1pABzyxw94XEU-0erobMIlfY,3021 +ray/tune/schedulers/__pycache__/__init__.cpython-312.pyc,, +ray/tune/schedulers/__pycache__/async_hyperband.cpython-312.pyc,, +ray/tune/schedulers/__pycache__/hb_bohb.cpython-312.pyc,, +ray/tune/schedulers/__pycache__/hyperband.cpython-312.pyc,, +ray/tune/schedulers/__pycache__/median_stopping_rule.cpython-312.pyc,, +ray/tune/schedulers/__pycache__/pb2.cpython-312.pyc,, +ray/tune/schedulers/__pycache__/pb2_utils.cpython-312.pyc,, +ray/tune/schedulers/__pycache__/pbt.cpython-312.pyc,, +ray/tune/schedulers/__pycache__/resource_changing_scheduler.cpython-312.pyc,, +ray/tune/schedulers/__pycache__/trial_scheduler.cpython-312.pyc,, +ray/tune/schedulers/__pycache__/util.cpython-312.pyc,, +ray/tune/schedulers/async_hyperband.py,sha256=aKMF2Gptft3k4uB92HlP6iel8gGtZdj6leqovh_5I9Q,10201 +ray/tune/schedulers/hb_bohb.py,sha256=NkeBKbw8YlG8TV8QnlHeDtpiTh7neX5emedfIng6YZY,7700 +ray/tune/schedulers/hyperband.py,sha256=lIZxmYc4rMoNRaMy3Ybj6jTHP0KnbQK7AEXs_yB_nOw,24762 +ray/tune/schedulers/median_stopping_rule.py,sha256=Hsdw1N04HSfxoek_lx-RSmDQCfbFiO7ZJy-uYg3c9WY,8420 +ray/tune/schedulers/pb2.py,sha256=B9pdab2Yg6vnYGIyE9gJA3pst9HINESNZEx_if69S3M,19672 +ray/tune/schedulers/pb2_utils.py,sha256=DJh_vANYbvsjpCTTi7Gpqw-1Gz1Kun9vQruF7FIHJfA,5744 +ray/tune/schedulers/pbt.py,sha256=yOed19OPI9aVjuFHOqpJEk5YQexistX7cZ4kjCHVBUk,49324 +ray/tune/schedulers/resource_changing_scheduler.py,sha256=872qzWmz2-7XktCkNHcYgHR2w5RgT4-mntEErecEy38,34329 +ray/tune/schedulers/trial_scheduler.py,sha256=xnUinLLItShYuayPJtr-sEnJPgT0rXajApJsOyEw1XY,5484 +ray/tune/schedulers/util.py,sha256=37XMePBGzv0-pOfOMQZ2GVOtY_XeLNJ7QpsdUrwz6Yg,918 +ray/tune/search/__init__.py,sha256=LZKDcDeQNR2ZQ3YhM-5iBzxFn_QhHwF0LkzotW6bOsk,4485 +ray/tune/search/__pycache__/__init__.cpython-312.pyc,, +ray/tune/search/__pycache__/_mock.cpython-312.pyc,, +ray/tune/search/__pycache__/basic_variant.cpython-312.pyc,, +ray/tune/search/__pycache__/concurrency_limiter.cpython-312.pyc,, +ray/tune/search/__pycache__/repeater.cpython-312.pyc,, +ray/tune/search/__pycache__/sample.cpython-312.pyc,, +ray/tune/search/__pycache__/search_algorithm.cpython-312.pyc,, +ray/tune/search/__pycache__/search_generator.cpython-312.pyc,, +ray/tune/search/__pycache__/searcher.cpython-312.pyc,, +ray/tune/search/__pycache__/util.cpython-312.pyc,, +ray/tune/search/__pycache__/variant_generator.cpython-312.pyc,, +ray/tune/search/_mock.py,sha256=sC3y8LmTi2zqoPmGy67nsM1iC5NWiSldVXHwdVwbVR8,1744 +ray/tune/search/ax/__init__.py,sha256=FGvbOwy-mtT3_YS6ZMBHJ7Msbnk9jEr_zbOPyMp4-SY,74 +ray/tune/search/ax/__pycache__/__init__.cpython-312.pyc,, +ray/tune/search/ax/__pycache__/ax_search.cpython-312.pyc,, +ray/tune/search/ax/ax_search.py,sha256=OTWLYN9V_5rYx1QkfLvAeUwOEpL77QWjLpXWsfJ1SGQ,15666 +ray/tune/search/basic_variant.py,sha256=5msq-EgbP94nSP0ntp_MrpkPxDZMsteU3YoL7bH4IJs,15380 +ray/tune/search/bayesopt/__init__.py,sha256=3DznYC9QeZGRzZGjZYr7cKhBm1nVEX2xbnkKmgqoYLU,98 +ray/tune/search/bayesopt/__pycache__/__init__.cpython-312.pyc,, +ray/tune/search/bayesopt/__pycache__/bayesopt_search.cpython-312.pyc,, +ray/tune/search/bayesopt/bayesopt_search.py,sha256=tkpwbTlcc2TCalJb9D2lq8gVANM54MeQ57li2BfJrHI,16238 +ray/tune/search/bohb/__init__.py,sha256=eKU452gquD3_rEMMumU38h4Z1lHuzWW_6NFgz6MArWI,92 +ray/tune/search/bohb/__pycache__/__init__.cpython-312.pyc,, +ray/tune/search/bohb/__pycache__/bohb_search.cpython-312.pyc,, +ray/tune/search/bohb/bohb_search.py,sha256=vUxGJGPYjUfT0ZVV8NNvjCLx3s5Bph3nfhsfNqawSug,13730 +ray/tune/search/concurrency_limiter.py,sha256=f7L2xdhYUe-Knbuu993zwvEWJ7w3Ev4VvOOChAfxoyQ,6325 +ray/tune/search/hebo/__init__.py,sha256=f9h2WIEtOVo_cYCBccYXV_kptAZU78xwUoB90__OyKE,82 +ray/tune/search/hebo/__pycache__/__init__.cpython-312.pyc,, +ray/tune/search/hebo/__pycache__/hebo_search.cpython-312.pyc,, +ray/tune/search/hebo/hebo_search.py,sha256=TgPCMkWpwBQgpHWsC67RD4H1vSd2EFMJNAoOHMV4JxU,16848 +ray/tune/search/hyperopt/__init__.py,sha256=wImM0x3WS0f-1MTuKfzwXQa6drX4gmofGidJJJTdJ8Q,98 +ray/tune/search/hyperopt/__pycache__/__init__.cpython-312.pyc,, +ray/tune/search/hyperopt/__pycache__/hyperopt_search.cpython-312.pyc,, +ray/tune/search/hyperopt/hyperopt_search.py,sha256=Tl4lEGMHvTcyd2qL5oIUlL3N8OtAxg6_7IWr2zwG2_I,21122 +ray/tune/search/nevergrad/__init__.py,sha256=vNDjYIDWJVHqmZqMVbO-Dc0FMD5bLtzDluwrcOtQFYs,102 +ray/tune/search/nevergrad/__pycache__/__init__.cpython-312.pyc,, +ray/tune/search/nevergrad/__pycache__/nevergrad_search.cpython-312.pyc,, +ray/tune/search/nevergrad/nevergrad_search.py,sha256=UET3mGhhKqm1j5EmvWhR1oa2XuNPvH5rZwy4yYq1Sg4,13533 +ray/tune/search/optuna/__init__.py,sha256=3DqongrESbcBwTYXC1QGWvWczh84NJZHx5ddXC45SpM,90 +ray/tune/search/optuna/__pycache__/__init__.cpython-312.pyc,, +ray/tune/search/optuna/__pycache__/optuna_search.cpython-312.pyc,, +ray/tune/search/optuna/optuna_search.py,sha256=bSrCj6AHsHep7vBpWO6ad3OVN6tbiMTtkZRzns7k6Dg,26571 +ray/tune/search/repeater.py,sha256=sYH0yj6SyH8KVxNuxeG-3tc5abdaIEmzr9V2wXzUnaU,7007 +ray/tune/search/sample.py,sha256=MCOprQKuW8aMh3viuQTcZ5wibmDYOO3uCnnyC_O0vQg,24262 +ray/tune/search/search_algorithm.py,sha256=3IkZPXBSthMLAFCMDESjg-8hqMroSypeE1k8zp8aJXA,3941 +ray/tune/search/search_generator.py,sha256=U1lqGxnB009bC-yONENTWeuG_KXSM3xtbOaiokgZuF4,8185 +ray/tune/search/searcher.py,sha256=TLiB-cKkQO3USnEYTZ0NRjefrDAw9lIcA1TDhBZrbiM,21389 +ray/tune/search/util.py,sha256=KGL7-NTlMaSVTsJFId1jYXdr18NEzCTTthDxSyh64NM,976 +ray/tune/search/variant_generator.py,sha256=3huEv3KJbShQwg_vc8P0REJZk_H-j4qko_t-i5nBZtE,17420 +ray/tune/search/zoopt/__init__.py,sha256=DzlNJOuxvCW1ETYkol-xIqW3BVTv5LCR8-ZCmfCoHko,86 +ray/tune/search/zoopt/__pycache__/__init__.cpython-312.pyc,, +ray/tune/search/zoopt/__pycache__/zoopt_search.cpython-312.pyc,, +ray/tune/search/zoopt/zoopt_search.py,sha256=QhjgQbiTzOT3zYeGZJyun6sw-E-EATlvqp5L-iTQqWY,12441 +ray/tune/stopper/__init__.py,sha256=h0MRFnWskA0aPSK4Jz38W6fOOsuFX1PNkuZA2tkGYkk,636 +ray/tune/stopper/__pycache__/__init__.cpython-312.pyc,, +ray/tune/stopper/__pycache__/experiment_plateau.cpython-312.pyc,, +ray/tune/stopper/__pycache__/function_stopper.cpython-312.pyc,, +ray/tune/stopper/__pycache__/maximum_iteration.cpython-312.pyc,, +ray/tune/stopper/__pycache__/noop.cpython-312.pyc,, +ray/tune/stopper/__pycache__/stopper.cpython-312.pyc,, +ray/tune/stopper/__pycache__/timeout.cpython-312.pyc,, +ray/tune/stopper/__pycache__/trial_plateau.cpython-312.pyc,, +ray/tune/stopper/experiment_plateau.py,sha256=FTcNkmHOlX-xs7SupuTx7kIiTZZcSB2TjFuCsEIxATQ,3208 +ray/tune/stopper/function_stopper.py,sha256=YtkP6O6j5cmgwwGEwEmaJUvAJuG_4pN6eSSqdl0O2vQ,1142 +ray/tune/stopper/maximum_iteration.py,sha256=zhZ3_KbpwKYzSJEi_5iD6RX-UGVOzsvJXqNiBy5V5CE,656 +ray/tune/stopper/noop.py,sha256=e4Pj9GzYi6grdb8InDn_GaVbiXKK8QtuPGZO7X4Ey1M,238 +ray/tune/stopper/stopper.py,sha256=FEDD_OY9yQyLQOl4oTkzl8J32FUfa7J-FK93OQOC5hY,3079 +ray/tune/stopper/timeout.py,sha256=dW3sZ8ID5744dVMKMcvgZC0RppyjPFijRerQtrbkWIM,1813 +ray/tune/stopper/trial_plateau.py,sha256=RjnRrBt3xyyy0UZ4N7K--jr0GY2ikQB9v2gVVX5VRuc,3332 +ray/tune/syncer.py,sha256=1FsX0uGqGTPv1Nc_pb-gJy3FYhltjQqa-dQsiZd7T8s,2172 +ray/tune/trainable/__init__.py,sha256=Px0KvVAvgKo53W5CxXO0hl1ss7sbKapwLdB47rz1XOQ,287 +ray/tune/trainable/__pycache__/__init__.cpython-312.pyc,, +ray/tune/trainable/__pycache__/function_trainable.cpython-312.pyc,, +ray/tune/trainable/__pycache__/metadata.cpython-312.pyc,, +ray/tune/trainable/__pycache__/trainable.cpython-312.pyc,, +ray/tune/trainable/__pycache__/trainable_fn_utils.cpython-312.pyc,, +ray/tune/trainable/__pycache__/util.cpython-312.pyc,, +ray/tune/trainable/function_trainable.py,sha256=5KlTqGR1T_zkKm8UKIW5MOh9H3OKxeWVQu6fkdwUdFI,9803 +ray/tune/trainable/metadata.py,sha256=qa-8UdMYEwmFPQyKwwf8MlcEmNCZVxTZP1Yokxvuxws,3522 +ray/tune/trainable/trainable.py,sha256=zVaK1t6rZKQ-2lgrhTlnJjkZMnWdUoHIfaIzq9uNtj8,36971 +ray/tune/trainable/trainable_fn_utils.py,sha256=9LSGcKb5ryVZtbmBSSe_WuylknYaZGarDYN28-d1IEo,2163 +ray/tune/trainable/util.py,sha256=NJwfX7qp93JHO0tElJeArkpvXlRmzz9IvNHxNHGUEcc,7754 +ray/tune/tune.py,sha256=PwcNizZ3v86G2tGTbVVzPv3x9CXJKdj39bDdTzUN1so,47425 +ray/tune/tune_config.py,sha256=e_EtXwnV2mrxUjTPGFV0Q01nQciF5xGEyNDadm9dTsA,4718 +ray/tune/tuner.py,sha256=XnZ26xA6OUoOWeVvhYdVmkaDXRuWr8rv8g2RmUE9WcE,15588 +ray/tune/utils/__init__.py,sha256=lj3-3FcZUD9njRCJNAcysjTyeWE79vQiKN35JQkTO98,523 +ray/tune/utils/__pycache__/__init__.cpython-312.pyc,, +ray/tune/utils/__pycache__/callback.cpython-312.pyc,, +ray/tune/utils/__pycache__/file_transfer.cpython-312.pyc,, +ray/tune/utils/__pycache__/log.cpython-312.pyc,, +ray/tune/utils/__pycache__/mock.cpython-312.pyc,, +ray/tune/utils/__pycache__/mock_trainable.cpython-312.pyc,, +ray/tune/utils/__pycache__/object_cache.cpython-312.pyc,, +ray/tune/utils/__pycache__/release_test_util.cpython-312.pyc,, +ray/tune/utils/__pycache__/resource_updater.cpython-312.pyc,, +ray/tune/utils/__pycache__/serialization.cpython-312.pyc,, +ray/tune/utils/__pycache__/util.cpython-312.pyc,, +ray/tune/utils/callback.py,sha256=OW8dyefQRoIeeJubttbDl_GeuwJoIMLC3zMg4NsUfpI,6414 +ray/tune/utils/file_transfer.py,sha256=5ulHz0vHDltBwk9JVjuIzzCjrtfo4Ba_iDm30Oxcbyc,17431 +ray/tune/utils/log.py,sha256=3Yco2YXLRj-4hi_LX40jCs9s7jMeIrMntNArPr9_REI,1462 +ray/tune/utils/mock.py,sha256=QvIWuqMDJWUvgzB4Pmg6A7yk--afZiX5KLJLFzmXwUE,4004 +ray/tune/utils/mock_trainable.py,sha256=z60orHWJyU9TBPGAsSmpeb40Ib37n3ERMRpbgw6Cs-U,1884 +ray/tune/utils/object_cache.py,sha256=pIwWqbGTjF9t0vXgZMj3BqrqcQN-R-kg3LDzfg7o6SU,5512 +ray/tune/utils/release_test_util.py,sha256=OISH3mXL0tGmocbgnMVTGqvP3BexgI1_9Us3nMI-c6E,5775 +ray/tune/utils/resource_updater.py,sha256=XHJ9W6xyXYhVNvO12yw1Qkht2K-S99_sylbrnwn_O2Q,12890 +ray/tune/utils/serialization.py,sha256=Ozwf3oJw2vFTnvH9w9N8lwO6Lxqf8GDhlaKuY1LiJO4,1343 +ray/tune/utils/util.py,sha256=rrTBH6vgYswnbwNTtP6bdv1bCSweCaIDRNtv5p_o4Co,19672 +ray/types.py,sha256=2cbqnQ29__PbiqmZDvTOtMn_Gkrpaab21Gil84vWtGg,412 +ray/util/__init__.py,sha256=1nnBeLNGflZzp4wJN_LXNF76STbzVASRAgZ8haHhAUk,2432 +ray/util/__pycache__/__init__.cpython-312.pyc,, +ray/util/__pycache__/actor_group.cpython-312.pyc,, +ray/util/__pycache__/actor_pool.cpython-312.pyc,, +ray/util/__pycache__/annotations.cpython-312.pyc,, +ray/util/__pycache__/check_open_ports.cpython-312.pyc,, +ray/util/__pycache__/check_serialize.cpython-312.pyc,, +ray/util/__pycache__/client_connect.cpython-312.pyc,, +ray/util/__pycache__/common.cpython-312.pyc,, +ray/util/__pycache__/debug.cpython-312.pyc,, +ray/util/__pycache__/debugpy.cpython-312.pyc,, +ray/util/__pycache__/helpers.cpython-312.pyc,, +ray/util/__pycache__/iter.cpython-312.pyc,, +ray/util/__pycache__/iter_metrics.cpython-312.pyc,, +ray/util/__pycache__/metrics.cpython-312.pyc,, +ray/util/__pycache__/placement_group.cpython-312.pyc,, +ray/util/__pycache__/queue.cpython-312.pyc,, +ray/util/__pycache__/rpdb.cpython-312.pyc,, +ray/util/__pycache__/scheduling_strategies.cpython-312.pyc,, +ray/util/__pycache__/serialization.cpython-312.pyc,, +ray/util/__pycache__/serialization_addons.cpython-312.pyc,, +ray/util/__pycache__/timer.cpython-312.pyc,, +ray/util/__pycache__/tpu.cpython-312.pyc,, +ray/util/accelerators/__init__.py,sha256=Njp2eQWKT1RA_OQw17N4uLja-6Q9dS1NzmhUc2tCKy8,1778 +ray/util/accelerators/__pycache__/__init__.cpython-312.pyc,, +ray/util/accelerators/__pycache__/accelerators.cpython-312.pyc,, +ray/util/accelerators/accelerators.py,sha256=gYt6xIF3IlOauwkO8dqbE7eK8ttH94NHAYez3Yt1uC0,1543 +ray/util/actor_group.py,sha256=Rp11YN7CUQh2-E7z6IYKmWxsR63T7T-filELvOxmeNU,7980 +ray/util/actor_pool.py,sha256=GuHc26UUgi-HfD2g5H0ld8SALLML4NPzBxJBdRGvIbg,14541 +ray/util/annotations.py,sha256=b2sUaicwDjGrwGo8_st55yYjYpJtMhOXGIqurljoUfI,7988 +ray/util/check_open_ports.py,sha256=PJAi3nqAtbZMX2lFV6RMbhBQSNz0hG9MEZgbhcsCoO4,5953 +ray/util/check_serialize.py,sha256=8tXLE98b-FKx0IPCvkB2UrutfhcNQAVYbaTa9ZAgmuo,8440 +ray/util/client/__init__.py,sha256=cpBeoaTQVDDe-qGVyu1N_cLtm5Nn-fabmLjMG9Ejcb4,10615 +ray/util/client/__pycache__/__init__.cpython-312.pyc,, +ray/util/client/__pycache__/api.cpython-312.pyc,, +ray/util/client/__pycache__/client_app.cpython-312.pyc,, +ray/util/client/__pycache__/client_pickler.cpython-312.pyc,, +ray/util/client/__pycache__/common.cpython-312.pyc,, +ray/util/client/__pycache__/dataclient.cpython-312.pyc,, +ray/util/client/__pycache__/logsclient.cpython-312.pyc,, +ray/util/client/__pycache__/options.cpython-312.pyc,, +ray/util/client/__pycache__/ray_client_helpers.cpython-312.pyc,, +ray/util/client/__pycache__/runtime_context.cpython-312.pyc,, +ray/util/client/__pycache__/worker.cpython-312.pyc,, +ray/util/client/api.py,sha256=8DDkJXHkffR0HmRPIeW92S2qc9wHWhjpfdyBHkaW84U,15343 +ray/util/client/client_app.py,sha256=5L1raW-tDAq3gx51UDJuZ-LQBG5MFxJmEuH5ytVGNTU,1823 +ray/util/client/client_pickler.py,sha256=Cd9YpoU9LWgxb6_en5EpuKxrR6KdNNdumavyfkfC-Tk,6012 +ray/util/client/common.py,sha256=QdJ_cXYPP6fMUKaz2RMzqmWsqHzN_lu5HhbInTitEC4,35219 +ray/util/client/dataclient.py,sha256=cghggVjUFcu0l21eRGx23d46-gO-J2J5Dxt_0yTSKqI,22951 +ray/util/client/logsclient.py,sha256=XctFwOjWkCWZzyLlSrIxY6da5wfkbSWZKAuE_CAq8O4,4945 +ray/util/client/options.py,sha256=8OVVdUfZKP8voCdWdncMDsUHsNaVxJ5lFI1klqwAKEQ,1850 +ray/util/client/ray_client_helpers.py,sha256=xuaV4KLYxIluBqs9UmxZ-A4mBk9mXuQtDzhpOv3jTiQ,2491 +ray/util/client/runtime_context.py,sha256=1thIfUOK9CXpX9JYot85Wzrm9Qb4S47cZ7Ks7Ct5sCg,1886 +ray/util/client/server/__init__.py,sha256=og0t86nF86MB8dm80ZrQ3A9og0aUvf6TXKeH0I5Z6AA,56 +ray/util/client/server/__main__.py,sha256=Oj32656eOno-hNr_Hqq47_oQn0TfFBkXnIGeRrYhk94,90 +ray/util/client/server/__pycache__/__init__.cpython-312.pyc,, +ray/util/client/server/__pycache__/__main__.cpython-312.pyc,, +ray/util/client/server/__pycache__/dataservicer.cpython-312.pyc,, +ray/util/client/server/__pycache__/logservicer.cpython-312.pyc,, +ray/util/client/server/__pycache__/proxier.cpython-312.pyc,, +ray/util/client/server/__pycache__/server.cpython-312.pyc,, +ray/util/client/server/__pycache__/server_pickler.cpython-312.pyc,, +ray/util/client/server/__pycache__/server_stubs.cpython-312.pyc,, +ray/util/client/server/dataservicer.py,sha256=Ii63Tsf0Ddiirw4CHYGCdhB5slFixxB7uvcjbEnP2Go,18813 +ray/util/client/server/logservicer.py,sha256=8vBPneYS4lGKclmmGwz1TdpgNNOULAgajpXhfyP3MHw,4278 +ray/util/client/server/proxier.py,sha256=xS0xyq-jujrgZdbq0kkztG0nr033i0em1Ibww__QdGg,36320 +ray/util/client/server/server.py,sha256=4JOmmqOYv5Fi6ScPV2vQ_7O8JUg3NRY8wKSOjf-dTy4,38964 +ray/util/client/server/server_pickler.py,sha256=fOp3atcvQKD7t9qlrPrdhkDDB9M6AKX-VRhTJtEmAaU,4450 +ray/util/client/server/server_stubs.py,sha256=SQStHr7bDuV4Qac58UYRoiKVZShvl8gWcnGFcTh3Roo,1717 +ray/util/client/worker.py,sha256=2P2RCBGNjoEZTrE9-UjKCvdKOpBgcQBxFMSaFsnPxTY,36789 +ray/util/client_connect.py,sha256=0mLL2Ul-AmPyJ0XVf7hPbfkI9vop-73vMRj_IESuPs4,2604 +ray/util/collective/__init__.py,sha256=AciLTYN7_qmrUN3Ps6GennMiL1UT9yxq5vETRIPT9g4,1084 +ray/util/collective/__pycache__/__init__.cpython-312.pyc,, +ray/util/collective/__pycache__/collective.cpython-312.pyc,, +ray/util/collective/__pycache__/const.cpython-312.pyc,, +ray/util/collective/__pycache__/types.cpython-312.pyc,, +ray/util/collective/__pycache__/util.cpython-312.pyc,, +ray/util/collective/collective.py,sha256=0gd8mEFgUBD1WPhiGRa4bq0KYuhw7xio80DB-4Vz-ck,29043 +ray/util/collective/collective_group/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/util/collective/collective_group/__pycache__/__init__.cpython-312.pyc,, +ray/util/collective/collective_group/__pycache__/base_collective_group.cpython-312.pyc,, +ray/util/collective/collective_group/__pycache__/cuda_stream.cpython-312.pyc,, +ray/util/collective/collective_group/__pycache__/nccl_collective_group.cpython-312.pyc,, +ray/util/collective/collective_group/__pycache__/nccl_util.cpython-312.pyc,, +ray/util/collective/collective_group/__pycache__/nixl_backend.cpython-312.pyc,, +ray/util/collective/collective_group/__pycache__/torch_gloo_collective_group.cpython-312.pyc,, +ray/util/collective/collective_group/base_collective_group.py,sha256=4PBLssG2vscr1OEH3pmh6kISSb4yYCu1FYt5qpOhyEE,2330 +ray/util/collective/collective_group/cuda_stream.py,sha256=migikW0Lc1JzMvZzjmJZQPwEZkjlFuhZ93c2iznUfkY,2970 +ray/util/collective/collective_group/nccl_collective_group.py,sha256=YTtsHZlq_gzuq_ygN948UDZNbStX50VA-MOIKHTqzeo,30843 +ray/util/collective/collective_group/nccl_util.py,sha256=iRVG9w3YyhwG6jIBLk_M1CCPkR4egS8voWruyiw1HQs,9121 +ray/util/collective/collective_group/nixl_backend.py,sha256=7lQucJzdonwabnOW_6u9oXxW97AILUkurpA_3SompZs,5367 +ray/util/collective/collective_group/torch_gloo_collective_group.py,sha256=pfMN5LGxxFM4fzSevZoQHy3Quzmg1L9OucjMCnqRRRk,8873 +ray/util/collective/const.py,sha256=J9r6vjcBtLpvZt6LM-LqWenEX1ThZgF8ClQS6r5lf9A,865 +ray/util/collective/types.py,sha256=h0W92XES6V6n80aeT_NvGvweArddwy74yrNsZxdAa5M,4339 +ray/util/collective/util.py,sha256=pj6-5Xs3Q906csM1IjH-_Dxge6djSh6Bs-0B8-p8BuA,2118 +ray/util/common.py,sha256=POgfMjbcuHZHBGpGzngIjygaG7Jsz0jfigjCJJYfs7Y,24 +ray/util/dask/__init__.py,sha256=EQzConA8dAgrRCwsnDUK-mxPjHGMVPlyL_8zPow4mE8,1907 +ray/util/dask/__pycache__/__init__.cpython-312.pyc,, +ray/util/dask/__pycache__/callbacks.cpython-312.pyc,, +ray/util/dask/__pycache__/common.cpython-312.pyc,, +ray/util/dask/__pycache__/optimizations.cpython-312.pyc,, +ray/util/dask/__pycache__/scheduler.cpython-312.pyc,, +ray/util/dask/__pycache__/scheduler_utils.cpython-312.pyc,, +ray/util/dask/callbacks.py,sha256=dU1eNe9mv60Kx5QsRturvfqy089B8JQa5M6C2BYsops,10511 +ray/util/dask/common.py,sha256=CNU5d8PqYOAdBHnWQ_zTOvuRROF12goEu6yKpMtzWA8,2652 +ray/util/dask/optimizations.py,sha256=JmdKArdxvXvxmCCRMrGpcK-czywmE0mwYZQX1I3_IUE,5214 +ray/util/dask/scheduler.py,sha256=c58_XD2TFYmuKZUa3q4lceqr-MxtUp2kopOXlX7Yor8,24439 +ray/util/dask/scheduler_utils.py,sha256=i3PoenkdXF-sPOfmZ8of59Mr6J_Af54bOqFELkCGDxo,11604 +ray/util/debug.py,sha256=pTAHxWuIW9_MzxrDHrgyXGTgB_JWP9VkJXnoG-78kjc,8644 +ray/util/debugpy.py,sha256=imtLu_l9-9Y0jDKt4rdoIx6ATnEbXghlF4AL9dIa4Ns,4120 +ray/util/helpers.py,sha256=rmzCv75sCtfi5uew_oJGMxN_HuE48yiE1DeAx5FLvHc,8437 +ray/util/horovod/__init__.py,sha256=eHQd2sGhmlA42dukBrIGIOj0k8k5f5BExabycshR5yE,172 +ray/util/horovod/__pycache__/__init__.cpython-312.pyc,, +ray/util/iter.py,sha256=P9dWumFhzkYU71L304tMD5ysB4zqN4vjNRL3y7RywTg,46939 +ray/util/iter_metrics.py,sha256=U_7iDnbTItUA20EYx_8Q2Kg7a9TCM8eVd57qr4pY8so,2227 +ray/util/joblib/__init__.py,sha256=ze4lZZU0ltg367PHT2svbmEcnKfuz1OPdq_E4afv5eg,588 +ray/util/joblib/__pycache__/__init__.cpython-312.pyc,, +ray/util/joblib/__pycache__/ray_backend.cpython-312.pyc,, +ray/util/joblib/ray_backend.py,sha256=BUa14yXsh-jWJo547vim28zh7qy9_9P4v3KQ6oyyeAM,3343 +ray/util/lightgbm/__init__.py,sha256=qSTKqnmHoVx06HHOmxZOoBDFjbP7fStk32Us_YOz6jM,179 +ray/util/lightgbm/__pycache__/__init__.cpython-312.pyc,, +ray/util/metrics.py,sha256=aUDqY0r1kQZnhkt4eqF_V-inv1vV3K9Nha2DqlN4O78,12497 +ray/util/multiprocessing/__init__.py,sha256=zccBaK6B-qJRU-OSD21drLG_2YLd5uwT4M8XM5f8uRM,133 +ray/util/multiprocessing/__pycache__/__init__.cpython-312.pyc,, +ray/util/multiprocessing/__pycache__/pool.cpython-312.pyc,, +ray/util/multiprocessing/pool.py,sha256=05bzkY_mmxSA9Z41p8UhdiWS6Yn7YOyvY7QQnEmG-P0,37603 +ray/util/placement_group.py,sha256=DhngDcBWImnPKxrl_sglSaQ8wtAK-dbNayvvTLV4N68,20584 +ray/util/queue.py,sha256=poQwHozIhvCFMmQFHjalkhxCUJWJDc9_Km4Wlc150JQ,10146 +ray/util/rpdb.py,sha256=007znx7dwiHrY7DoPm0-6TtpJK-UBLTzsKcy60lxEPk,12146 +ray/util/scheduling_strategies.py,sha256=NHkU87Xu7jsJ_d0-P7lTrl_Wu3Jw0ePnNxVg02TiWTo,7622 +ray/util/serialization.py,sha256=l4lV_6Uk1xqWoN6zJZ15GMIeW1BFl-r1DjBYk61IMG8,2009 +ray/util/serialization_addons.py,sha256=Dk7qHli9jWqiptn0vgBlBqz1Az4y3A9YKTG9wun3XWg,1093 +ray/util/sgd/__init__.py,sha256=XhFzqjBfLVHFPk0-z2kBrIOSCxmuiKlsomJdzNp6fM0,152 +ray/util/sgd/__pycache__/__init__.cpython-312.pyc,, +ray/util/spark/__init__.py,sha256=sdHl65enTyL0YHmVtSdlNN8W35IPznfFmtMq5EN7R8c,277 +ray/util/spark/__pycache__/__init__.cpython-312.pyc,, +ray/util/spark/__pycache__/cluster_init.cpython-312.pyc,, +ray/util/spark/__pycache__/databricks_hook.cpython-312.pyc,, +ray/util/spark/__pycache__/start_hook_base.cpython-312.pyc,, +ray/util/spark/__pycache__/start_ray_node.cpython-312.pyc,, +ray/util/spark/__pycache__/utils.cpython-312.pyc,, +ray/util/spark/cluster_init.py,sha256=wIuH0vuQ8yCVXF7APigK53ly80NYW6WblPgjrkv7aVc,76922 +ray/util/spark/databricks_hook.py,sha256=g6J_8Wrpj602SpxolTjTQHl5TlnTGAAEgyHHtvtFDtg,8866 +ray/util/spark/start_hook_base.py,sha256=Pn71SPD5ujBSfeAabw5frTc6Aun923CKjdHRMTKXL3A,417 +ray/util/spark/start_ray_node.py,sha256=slssPvLI6olZxcEi8nkun3cCvKtz7EJNATHZp-d2gIU,8191 +ray/util/spark/utils.py,sha256=gS9JKapJwh9P8IiE6QqyRIpylDdSZVVwGHBJ-GE887Q,17083 +ray/util/state/__init__.py,sha256=tNuhczvLDGWskB6rV9dyAQt8A14wLmhraSXoXNempU0,900 +ray/util/state/__pycache__/__init__.cpython-312.pyc,, +ray/util/state/__pycache__/api.cpython-312.pyc,, +ray/util/state/__pycache__/common.cpython-312.pyc,, +ray/util/state/__pycache__/exception.cpython-312.pyc,, +ray/util/state/__pycache__/state_cli.cpython-312.pyc,, +ray/util/state/__pycache__/state_manager.cpython-312.pyc,, +ray/util/state/__pycache__/util.cpython-312.pyc,, +ray/util/state/api.py,sha256=SbpeSDUYzH7QeqTTr_wIXGHepTQg1z2OXT3dWaC2ALE,54583 +ray/util/state/common.py,sha256=4lMFNwLCVV5gRI7sQWrxVzRFCh9aauFOaQ5J7zMBKVQ,67705 +ray/util/state/exception.py,sha256=SQWb86M80lNy4-TBfy1LoPjWJEBat-knpYCSHORZ8UY,268 +ray/util/state/state_cli.py,sha256=gPpfU-2-BV1CzFuIO12hiHiea57l_kQRfy_DzKD9GsI,35507 +ray/util/state/state_manager.py,sha256=jXKysRINzfwlkAjMAb1ZFY96CU1R3GPDEKy998wDPt8,18981 +ray/util/state/util.py,sha256=D8_E3F0HFTCiUxEZJlVKgm6OAKW3U2GaCJhodbzZvSQ,1932 +ray/util/timer.py,sha256=enqflszQt04fVHtcQLyE4XsfeFlNxpSFXKuHHMvAgs8,1877 +ray/util/tpu.py,sha256=qGoEgQeNppP3R3mkna97pQ5aUtzfjB4F27LIpyAZMmI,9154 +ray/util/tracing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ray/util/tracing/__pycache__/__init__.cpython-312.pyc,, +ray/util/tracing/__pycache__/setup_local_tmp_tracing.cpython-312.pyc,, +ray/util/tracing/__pycache__/setup_tempo_tracing.cpython-312.pyc,, +ray/util/tracing/__pycache__/tracing_helper.cpython-312.pyc,, +ray/util/tracing/setup_local_tmp_tracing.py,sha256=5z80HbDVGONAO7BWKujKZEEn-W16k6nHxVlfelTICsc,828 +ray/util/tracing/setup_tempo_tracing.py,sha256=nu0TnqW2IO85bCf4eB6XJbgLo-lPXYj6p3sLP8Pc5lw,860 +ray/util/tracing/tracing_helper.py,sha256=DGWtpVjv9wkEndKYlL1QefXQfajiGcvEYbYfB_bEsns,19076 +ray/util/xgboost/__init__.py,sha256=fiAT1Q_R0kf1TNR3QBZXzB-Z3xXxff8w9NCOkJhX_0M,176 +ray/util/xgboost/__pycache__/__init__.cpython-312.pyc,, +ray/widgets/__init__.py,sha256=mKmmZs53_ii5nlBmT26kDX1x7a4RyfPvjc6XFvZ_EKI,138 +ray/widgets/__pycache__/__init__.cpython-312.pyc,, +ray/widgets/__pycache__/render.cpython-312.pyc,, +ray/widgets/__pycache__/util.cpython-312.pyc,, +ray/widgets/render.py,sha256=CdxaN0PMIrPF-tp24Zqqv9Z5sB3MdzIxh-2Yh4SaoZA,1226 +ray/widgets/templates/context.html.j2,sha256=GJ5t67VpG9DWYqvMqIlG0kVf5Jl8w5sBZ7eBTlw_mKA,235 +ray/widgets/templates/context_dashrow.html.j2,sha256=0sod0SSVgvvBtGmJkbdjH29aBb8c_0s5ygz-Navqeuc,182 +ray/widgets/templates/context_logo.html.j2,sha256=2eq4DsgvKXyTZx4LQwp2hvA4Ix7v0HDM1yQ45bBCuIo,5571 +ray/widgets/templates/context_table.html.j2,sha256=G2BmWG2Mra_eEC53UOb7UEpA0vr8LX1dHbg-1DK6C3E,475 +ray/widgets/templates/divider.html.j2,sha256=YWaWbc6yc3AhN4kpOlvIFz0-8eIsKpQ1OXDxiw34I3c,210 +ray/widgets/templates/rendered_html_common.html.j2,sha256=6e3NaDpVWHzniqTOI0ywpnoS3YcgraMwUHh3_N6VurM,59 +ray/widgets/templates/run_config.html.j2,sha256=3Txr5nDnn20wx1iwd8tpBJPGzTQXrBB1_yDfaDdVj48,325 +ray/widgets/templates/scrollableTable.html.j2,sha256=TAOV7gvFXvG1WD9gyt2ZBmDvzra1Mbu6Nl5yaNAW3h8,367 +ray/widgets/templates/title_data.html.j2,sha256=j_B2qxqblbBvoiysGrjZZzKTp8Xw5cYSdUn5b-VJiYo,255 +ray/widgets/templates/title_data_mini.html.j2,sha256=w04_i3wa7yKK7QN39TqWyF25mF3Mm0nVvPbGJuqjClo,100 +ray/widgets/templates/trial_progress.html.j2,sha256=7dRrXFrsMyj4PBSLaNgoaUVttEv-pX0-nblnUvR30lI,277 +ray/widgets/templates/tune_status.html.j2,sha256=zzyLLQhWzhW5n1tIuX8Zkq3STVnvQPCH_5w_7bDdPLg,1097 +ray/widgets/templates/tune_status_messages.html.j2,sha256=0jm-6FxHpop1liapLUKvlpxiO-E-hIjWG5x1RJupLKU,507 +ray/widgets/util.py,sha256=swpokHhNLkM1MWFQJfOS05oeH_GgdagnNgxGrgQALRs,6345 +ray/workflow/__init__.py,sha256=GC8BYy1jvKcsffzDj1YTypsDpOtQAzCzgvgRt_rjwcY,185 +ray/workflow/__pycache__/__init__.cpython-312.pyc,, diff --git a/lib/python3.12/site-packages/ray-2.52.1.dist-info/REQUESTED b/lib/python3.12/site-packages/ray-2.52.1.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/ray-2.52.1.dist-info/WHEEL b/lib/python3.12/site-packages/ray-2.52.1.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..8de3dd04b181d011138aec587ebd86c84184fa09 --- /dev/null +++ b/lib/python3.12/site-packages/ray-2.52.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp312-cp312-linux_x86_64 + diff --git a/lib/python3.12/site-packages/ray-2.52.1.dist-info/entry_points.txt b/lib/python3.12/site-packages/ray-2.52.1.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..b48cc567cd0a4f5eda8af8e202bb381f835de5d0 --- /dev/null +++ b/lib/python3.12/site-packages/ray-2.52.1.dist-info/entry_points.txt @@ -0,0 +1,4 @@ +[console_scripts] +ray = ray.scripts.scripts:main +serve = ray.serve.scripts:cli +tune = ray.tune.cli.scripts:cli diff --git a/lib/python3.12/site-packages/ray-2.52.1.dist-info/top_level.txt b/lib/python3.12/site-packages/ray-2.52.1.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..e0275631807fc1c22cd3593cd1c48d29d1bbaff9 --- /dev/null +++ b/lib/python3.12/site-packages/ray-2.52.1.dist-info/top_level.txt @@ -0,0 +1 @@ +ray diff --git a/lib/python3.12/site-packages/setuptools/__init__.py b/lib/python3.12/site-packages/setuptools/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b3e78edab6e1bc7839bf73fcd969e4e76f42f83e --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/__init__.py @@ -0,0 +1,250 @@ +"""Extensions to the 'distutils' for large or complex distributions""" +# mypy: disable_error_code=override +# Command.reinitialize_command has an extra **kw param that distutils doesn't have +# Can't disable on the exact line because distutils doesn't exists on Python 3.12 +# and mypy isn't aware of distutils_hack, causing distutils.core.Command to be Any, +# and a [unused-ignore] to be raised on 3.12+ + +from __future__ import annotations + +import functools +import os +import sys +from abc import abstractmethod +from collections.abc import Mapping +from typing import TYPE_CHECKING, TypeVar, overload + +sys.path.extend(((vendor_path := os.path.join(os.path.dirname(os.path.dirname(__file__)), 'setuptools', '_vendor')) not in sys.path) * [vendor_path]) # fmt: skip +# workaround for #4476 +sys.modules.pop('backports', None) + +import _distutils_hack.override # noqa: F401 + +from . import logging, monkey +from .depends import Require +from .discovery import PackageFinder, PEP420PackageFinder +from .dist import Distribution +from .extension import Extension +from .version import __version__ as __version__ +from .warnings import SetuptoolsDeprecationWarning + +import distutils.core + +__all__ = [ + 'setup', + 'Distribution', + 'Command', + 'Extension', + 'Require', + 'SetuptoolsDeprecationWarning', + 'find_packages', + 'find_namespace_packages', +] + +_CommandT = TypeVar("_CommandT", bound="_Command") + +bootstrap_install_from = None + +find_packages = PackageFinder.find +find_namespace_packages = PEP420PackageFinder.find + + +def _install_setup_requires(attrs): + # Note: do not use `setuptools.Distribution` directly, as + # our PEP 517 backend patch `distutils.core.Distribution`. + class MinimalDistribution(distutils.core.Distribution): + """ + A minimal version of a distribution for supporting the + fetch_build_eggs interface. + """ + + def __init__(self, attrs: Mapping[str, object]) -> None: + _incl = 'dependency_links', 'setup_requires' + filtered = {k: attrs[k] for k in set(_incl) & set(attrs)} + super().__init__(filtered) + # Prevent accidentally triggering discovery with incomplete set of attrs + self.set_defaults._disable() + + def _get_project_config_files(self, filenames=None): + """Ignore ``pyproject.toml``, they are not related to setup_requires""" + try: + cfg, _toml = super()._split_standard_project_metadata(filenames) + except Exception: + return filenames, () + return cfg, () + + def finalize_options(self): + """ + Disable finalize_options to avoid building the working set. + Ref #2158. + """ + + dist = MinimalDistribution(attrs) + + # Honor setup.cfg's options. + dist.parse_config_files(ignore_option_errors=True) + if dist.setup_requires: + _fetch_build_eggs(dist) + + +def _fetch_build_eggs(dist: Distribution): + try: + dist.fetch_build_eggs(dist.setup_requires) + except Exception as ex: + msg = """ + It is possible a package already installed in your system + contains an version that is invalid according to PEP 440. + You can try `pip install --use-pep517` as a workaround for this problem, + or rely on a new virtual environment. + + If the problem refers to a package that is not installed yet, + please contact that package's maintainers or distributors. + """ + if "InvalidVersion" in ex.__class__.__name__: + if hasattr(ex, "add_note"): + ex.add_note(msg) # PEP 678 + else: + dist.announce(f"\n{msg}\n") + raise + + +def setup(**attrs) -> Distribution: + logging.configure() + # Make sure we have any requirements needed to interpret 'attrs'. + _install_setup_requires(attrs) + # Override return type of distutils.core.Distribution with setuptools.dist.Distribution + # (implicitly implemented via `setuptools.monkey.patch_all`). + return distutils.core.setup(**attrs) # type: ignore[return-value] + + +setup.__doc__ = distutils.core.setup.__doc__ + +if TYPE_CHECKING: + # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962 + from distutils.core import Command as _Command +else: + _Command = monkey.get_unpatched(distutils.core.Command) + + +class Command(_Command): + """ + Setuptools internal actions are organized using a *command design pattern*. + This means that each action (or group of closely related actions) executed during + the build should be implemented as a ``Command`` subclass. + + These commands are abstractions and do not necessarily correspond to a command that + can (or should) be executed via a terminal, in a CLI fashion (although historically + they would). + + When creating a new command from scratch, custom defined classes **SHOULD** inherit + from ``setuptools.Command`` and implement a few mandatory methods. + Between these mandatory methods, are listed: + :meth:`initialize_options`, :meth:`finalize_options` and :meth:`run`. + + A useful analogy for command classes is to think of them as subroutines with local + variables called "options". The options are "declared" in :meth:`initialize_options` + and "defined" (given their final values, aka "finalized") in :meth:`finalize_options`, + both of which must be defined by every command class. The "body" of the subroutine, + (where it does all the work) is the :meth:`run` method. + Between :meth:`initialize_options` and :meth:`finalize_options`, ``setuptools`` may set + the values for options/attributes based on user's input (or circumstance), + which means that the implementation should be careful to not overwrite values in + :meth:`finalize_options` unless necessary. + + Please note that other commands (or other parts of setuptools) may also overwrite + the values of the command's options/attributes multiple times during the build + process. + Therefore it is important to consistently implement :meth:`initialize_options` and + :meth:`finalize_options`. For example, all derived attributes (or attributes that + depend on the value of other attributes) **SHOULD** be recomputed in + :meth:`finalize_options`. + + When overwriting existing commands, custom defined classes **MUST** abide by the + same APIs implemented by the original class. They also **SHOULD** inherit from the + original class. + """ + + command_consumes_arguments = False + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + + def __init__(self, dist: Distribution, **kw) -> None: + """ + Construct the command for dist, updating + vars(self) with any keyword parameters. + """ + super().__init__(dist) + vars(self).update(kw) + + @overload + def reinitialize_command( + self, command: str, reinit_subcommands: bool = False, **kw + ) -> Command: ... # override distutils.cmd.Command with setuptools.Command + @overload + def reinitialize_command( + self, command: _CommandT, reinit_subcommands: bool = False, **kw + ) -> _CommandT: ... + def reinitialize_command( + self, command: str | _Command, reinit_subcommands: bool = False, **kw + ) -> Command | _Command: + cmd = _Command.reinitialize_command(self, command, reinit_subcommands) + vars(cmd).update(kw) + return cmd # pyright: ignore[reportReturnType] # pypa/distutils#307 + + @abstractmethod + def initialize_options(self) -> None: + """ + Set or (reset) all options/attributes/caches used by the command + to their default values. Note that these values may be overwritten during + the build. + """ + raise NotImplementedError + + @abstractmethod + def finalize_options(self) -> None: + """ + Set final values for all options/attributes used by the command. + Most of the time, each option/attribute/cache should only be set if it does not + have any value yet (e.g. ``if self.attr is None: self.attr = val``). + """ + raise NotImplementedError + + @abstractmethod + def run(self) -> None: + """ + Execute the actions intended by the command. + (Side effects **SHOULD** only take place when :meth:`run` is executed, + for example, creating new files or writing to the terminal output). + """ + raise NotImplementedError + + +def _find_all_simple(path): + """ + Find all files under 'path' + """ + results = ( + os.path.join(base, file) + for base, dirs, files in os.walk(path, followlinks=True) + for file in files + ) + return filter(os.path.isfile, results) + + +def findall(dir=os.curdir): + """ + Find all files under 'dir' and return the list of full filenames. + Unless dir is '.', return full filenames with dir prepended. + """ + files = _find_all_simple(dir) + if dir == os.curdir: + make_rel = functools.partial(os.path.relpath, start=dir) + files = map(make_rel, files) + return list(files) + + +class sic(str): + """Treat this string as-is (https://en.wikipedia.org/wiki/Sic)""" + + +# Apply monkey patches +monkey.patch_all() diff --git a/lib/python3.12/site-packages/setuptools/_core_metadata.py b/lib/python3.12/site-packages/setuptools/_core_metadata.py new file mode 100644 index 0000000000000000000000000000000000000000..a52d5cf755cdd6a502e752c2f7a3afa3b25897d5 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_core_metadata.py @@ -0,0 +1,337 @@ +""" +Handling of Core Metadata for Python packages (including reading and writing). + +See: https://packaging.python.org/en/latest/specifications/core-metadata/ +""" + +from __future__ import annotations + +import os +import stat +import textwrap +from email import message_from_file +from email.message import Message +from tempfile import NamedTemporaryFile + +from packaging.markers import Marker +from packaging.requirements import Requirement +from packaging.utils import canonicalize_name, canonicalize_version +from packaging.version import Version + +from . import _normalization, _reqs +from ._static import is_static +from .warnings import SetuptoolsDeprecationWarning + +from distutils.util import rfc822_escape + + +def get_metadata_version(self): + mv = getattr(self, 'metadata_version', None) + if mv is None: + mv = Version('2.4') + self.metadata_version = mv + return mv + + +def rfc822_unescape(content: str) -> str: + """Reverse RFC-822 escaping by removing leading whitespaces from content.""" + lines = content.splitlines() + if len(lines) == 1: + return lines[0].lstrip() + return '\n'.join((lines[0].lstrip(), textwrap.dedent('\n'.join(lines[1:])))) + + +def _read_field_from_msg(msg: Message, field: str) -> str | None: + """Read Message header field.""" + value = msg[field] + if value == 'UNKNOWN': + return None + return value + + +def _read_field_unescaped_from_msg(msg: Message, field: str) -> str | None: + """Read Message header field and apply rfc822_unescape.""" + value = _read_field_from_msg(msg, field) + if value is None: + return value + return rfc822_unescape(value) + + +def _read_list_from_msg(msg: Message, field: str) -> list[str] | None: + """Read Message header field and return all results as list.""" + values = msg.get_all(field, None) + if values == []: + return None + return values + + +def _read_payload_from_msg(msg: Message) -> str | None: + value = str(msg.get_payload()).strip() + if value == 'UNKNOWN' or not value: + return None + return value + + +def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = message_from_file(file) + + self.metadata_version = Version(msg['metadata-version']) + self.name = _read_field_from_msg(msg, 'name') + self.version = _read_field_from_msg(msg, 'version') + self.description = _read_field_from_msg(msg, 'summary') + # we are filling author only. + self.author = _read_field_from_msg(msg, 'author') + self.maintainer = None + self.author_email = _read_field_from_msg(msg, 'author-email') + self.maintainer_email = None + self.url = _read_field_from_msg(msg, 'home-page') + self.download_url = _read_field_from_msg(msg, 'download-url') + self.license = _read_field_unescaped_from_msg(msg, 'license') + self.license_expression = _read_field_unescaped_from_msg(msg, 'license-expression') + + self.long_description = _read_field_unescaped_from_msg(msg, 'description') + if self.long_description is None and self.metadata_version >= Version('2.1'): + self.long_description = _read_payload_from_msg(msg) + self.description = _read_field_from_msg(msg, 'summary') + + if 'keywords' in msg: + self.keywords = _read_field_from_msg(msg, 'keywords').split(',') + + self.platforms = _read_list_from_msg(msg, 'platform') + self.classifiers = _read_list_from_msg(msg, 'classifier') + + # PEP 314 - these fields only exist in 1.1 + if self.metadata_version == Version('1.1'): + self.requires = _read_list_from_msg(msg, 'requires') + self.provides = _read_list_from_msg(msg, 'provides') + self.obsoletes = _read_list_from_msg(msg, 'obsoletes') + else: + self.requires = None + self.provides = None + self.obsoletes = None + + self.license_files = _read_list_from_msg(msg, 'license-file') + + +def single_line(val): + """ + Quick and dirty validation for Summary pypa/setuptools#1390. + """ + if '\n' in val: + # TODO: Replace with `raise ValueError("newlines not allowed")` + # after reviewing #2893. + msg = "newlines are not allowed in `summary` and will break in the future" + SetuptoolsDeprecationWarning.emit("Invalid config.", msg) + # due_date is undefined. Controversial change, there was a lot of push back. + val = val.strip().split('\n')[0] + return val + + +def write_pkg_info(self, base_dir): + """Write the PKG-INFO file into the release tree.""" + temp = "" + final = os.path.join(base_dir, 'PKG-INFO') + try: + # Use a temporary file while writing to avoid race conditions + # (e.g. `importlib.metadata` reading `.egg-info/PKG-INFO`): + with NamedTemporaryFile("w", encoding="utf-8", dir=base_dir, delete=False) as f: + temp = f.name + self.write_pkg_file(f) + permissions = stat.S_IMODE(os.lstat(temp).st_mode) + os.chmod(temp, permissions | stat.S_IRGRP | stat.S_IROTH) + os.replace(temp, final) # atomic operation. + finally: + if temp and os.path.exists(temp): + os.remove(temp) + + +# Based on Python 3.5 version +def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME + """Write the PKG-INFO format data to a file object.""" + version = self.get_metadata_version() + + def write_field(key, value): + file.write(f"{key}: {value}\n") + + write_field('Metadata-Version', str(version)) + write_field('Name', self.get_name()) + write_field('Version', self.get_version()) + + summary = self.get_description() + if summary: + write_field('Summary', single_line(summary)) + + optional_fields = ( + ('Home-page', 'url'), + ('Download-URL', 'download_url'), + ('Author', 'author'), + ('Author-email', 'author_email'), + ('Maintainer', 'maintainer'), + ('Maintainer-email', 'maintainer_email'), + ) + + for field, attr in optional_fields: + attr_val = getattr(self, attr, None) + if attr_val is not None: + write_field(field, attr_val) + + if license_expression := self.license_expression: + write_field('License-Expression', license_expression) + elif license := self.get_license(): + write_field('License', rfc822_escape(license)) + + for label, url in self.project_urls.items(): + write_field('Project-URL', f'{label}, {url}') + + keywords = ','.join(self.get_keywords()) + if keywords: + write_field('Keywords', keywords) + + platforms = self.get_platforms() or [] + for platform in platforms: + write_field('Platform', platform) + + self._write_list(file, 'Classifier', self.get_classifiers()) + + # PEP 314 + self._write_list(file, 'Requires', self.get_requires()) + self._write_list(file, 'Provides', self.get_provides()) + self._write_list(file, 'Obsoletes', self.get_obsoletes()) + + # Setuptools specific for PEP 345 + if hasattr(self, 'python_requires'): + write_field('Requires-Python', self.python_requires) + + # PEP 566 + if self.long_description_content_type: + write_field('Description-Content-Type', self.long_description_content_type) + + safe_license_files = map(_safe_license_file, self.license_files or []) + self._write_list(file, 'License-File', safe_license_files) + _write_requirements(self, file) + + for field, attr in _POSSIBLE_DYNAMIC_FIELDS.items(): + if (val := getattr(self, attr, None)) and not is_static(val): + write_field('Dynamic', field) + + long_description = self.get_long_description() + if long_description: + file.write(f"\n{long_description}") + if not long_description.endswith("\n"): + file.write("\n") + + +def _write_requirements(self, file): + for req in _reqs.parse(self.install_requires): + file.write(f"Requires-Dist: {req}\n") + + processed_extras = {} + for augmented_extra, reqs in self.extras_require.items(): + # Historically, setuptools allows "augmented extras": `:` + unsafe_extra, _, condition = augmented_extra.partition(":") + unsafe_extra = unsafe_extra.strip() + extra = _normalization.safe_extra(unsafe_extra) + + if extra: + _write_provides_extra(file, processed_extras, extra, unsafe_extra) + for req in _reqs.parse_strings(reqs): + r = _include_extra(req, extra, condition.strip()) + file.write(f"Requires-Dist: {r}\n") + + return processed_extras + + +def _include_extra(req: str, extra: str, condition: str) -> Requirement: + r = Requirement(req) # create a fresh object that can be modified + parts = ( + f"({r.marker})" if r.marker else None, + f"({condition})" if condition else None, + f"extra == {extra!r}" if extra else None, + ) + r.marker = Marker(" and ".join(x for x in parts if x)) + return r + + +def _write_provides_extra(file, processed_extras, safe, unsafe): + previous = processed_extras.get(safe) + if previous == unsafe: + SetuptoolsDeprecationWarning.emit( + 'Ambiguity during "extra" normalization for dependencies.', + f""" + {previous!r} and {unsafe!r} normalize to the same value:\n + {safe!r}\n + In future versions, setuptools might halt the build process. + """, + see_url="https://peps.python.org/pep-0685/", + ) + else: + processed_extras[safe] = unsafe + file.write(f"Provides-Extra: {safe}\n") + + +# from pypa/distutils#244; needed only until that logic is always available +def get_fullname(self): + return _distribution_fullname(self.get_name(), self.get_version()) + + +def _distribution_fullname(name: str, version: str) -> str: + """ + >>> _distribution_fullname('setup.tools', '1.0-2') + 'setup_tools-1.0.post2' + >>> _distribution_fullname('setup-tools', '1.2post2') + 'setup_tools-1.2.post2' + >>> _distribution_fullname('setup-tools', '1.0-r2') + 'setup_tools-1.0.post2' + >>> _distribution_fullname('setup.tools', '1.0.post') + 'setup_tools-1.0.post0' + >>> _distribution_fullname('setup.tools', '1.0+ubuntu-1') + 'setup_tools-1.0+ubuntu.1' + """ + return "{}-{}".format( + canonicalize_name(name).replace('-', '_'), + canonicalize_version(version, strip_trailing_zero=False), + ) + + +def _safe_license_file(file): + # XXX: Do we need this after the deprecation discussed in #4892, #4896?? + normalized = os.path.normpath(file).replace(os.sep, "/") + if "../" in normalized: + return os.path.basename(normalized) # Temporarily restore pre PEP639 behaviour + return normalized + + +_POSSIBLE_DYNAMIC_FIELDS = { + # Core Metadata Field x related Distribution attribute + "author": "author", + "author-email": "author_email", + "classifier": "classifiers", + "description": "long_description", + "description-content-type": "long_description_content_type", + "download-url": "download_url", + "home-page": "url", + "keywords": "keywords", + "license": "license", + # XXX: License-File is complicated because the user gives globs that are expanded + # during the build. Without special handling it is likely always + # marked as Dynamic, which is an acceptable outcome according to: + # https://github.com/pypa/setuptools/issues/4629#issuecomment-2331233677 + "license-file": "license_files", + "license-expression": "license_expression", # PEP 639 + "maintainer": "maintainer", + "maintainer-email": "maintainer_email", + "obsoletes": "obsoletes", + # "obsoletes-dist": "obsoletes_dist", # NOT USED + "platform": "platforms", + "project-url": "project_urls", + "provides": "provides", + # "provides-dist": "provides_dist", # NOT USED + "provides-extra": "extras_require", + "requires": "requires", + "requires-dist": "install_requires", + # "requires-external": "requires_external", # NOT USED + "requires-python": "python_requires", + "summary": "description", + # "supported-platform": "supported_platforms", # NOT USED +} diff --git a/lib/python3.12/site-packages/setuptools/_discovery.py b/lib/python3.12/site-packages/setuptools/_discovery.py new file mode 100644 index 0000000000000000000000000000000000000000..d1b4a0ee0351d69de5f0ab7d65bbe6958e398916 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_discovery.py @@ -0,0 +1,33 @@ +import functools +import operator + +import packaging.requirements + + +# from coherent.build.discovery +def extras_from_dep(dep): + try: + markers = packaging.requirements.Requirement(dep).marker._markers + except AttributeError: + markers = () + return set( + marker[2].value + for marker in markers + if isinstance(marker, tuple) and marker[0].value == 'extra' + ) + + +def extras_from_deps(deps): + """ + >>> extras_from_deps(['requests']) + set() + >>> extras_from_deps(['pytest; extra == "test"']) + {'test'} + >>> sorted(extras_from_deps([ + ... 'requests', + ... 'pytest; extra == "test"', + ... 'pytest-cov; extra == "test"', + ... 'sphinx; extra=="doc"'])) + ['doc', 'test'] + """ + return functools.reduce(operator.or_, map(extras_from_dep, deps), set()) diff --git a/lib/python3.12/site-packages/setuptools/_entry_points.py b/lib/python3.12/site-packages/setuptools/_entry_points.py new file mode 100644 index 0000000000000000000000000000000000000000..cd5dd2c8ac99783a2edb617278627ba170a9872b --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_entry_points.py @@ -0,0 +1,94 @@ +import functools +import itertools +import operator + +from jaraco.functools import pass_none +from jaraco.text import yield_lines +from more_itertools import consume + +from ._importlib import metadata +from ._itertools import ensure_unique +from .errors import OptionError + + +def ensure_valid(ep): + """ + Exercise one of the dynamic properties to trigger + the pattern match. + + This function is deprecated in favor of importlib_metadata 8.7 and + Python 3.14 importlib.metadata, which validates entry points on + construction. + """ + try: + ep.extras + except (AttributeError, AssertionError) as ex: + # Why both? See https://github.com/python/importlib_metadata/issues/488 + msg = ( + f"Problems to parse {ep}.\nPlease ensure entry-point follows the spec: " + "https://packaging.python.org/en/latest/specifications/entry-points/" + ) + raise OptionError(msg) from ex + + +def load_group(value, group): + """ + Given a value of an entry point or series of entry points, + return each as an EntryPoint. + """ + # normalize to a single sequence of lines + lines = yield_lines(value) + text = f'[{group}]\n' + '\n'.join(lines) + return metadata.EntryPoints._from_text(text) + + +def by_group_and_name(ep): + return ep.group, ep.name + + +def validate(eps: metadata.EntryPoints): + """ + Ensure entry points are unique by group and name and validate each. + """ + consume(map(ensure_valid, ensure_unique(eps, key=by_group_and_name))) + return eps + + +@functools.singledispatch +def load(eps): + """ + Given a Distribution.entry_points, produce EntryPoints. + """ + groups = itertools.chain.from_iterable( + load_group(value, group) for group, value in eps.items() + ) + return validate(metadata.EntryPoints(groups)) + + +@load.register(str) +def _(eps): + r""" + >>> ep, = load('[console_scripts]\nfoo=bar') + >>> ep.group + 'console_scripts' + >>> ep.name + 'foo' + >>> ep.value + 'bar' + """ + return validate(metadata.EntryPoints(metadata.EntryPoints._from_text(eps))) + + +load.register(type(None), lambda x: x) + + +@pass_none +def render(eps: metadata.EntryPoints): + by_group = operator.attrgetter('group') + groups = itertools.groupby(sorted(eps, key=by_group), by_group) + + return '\n'.join(f'[{group}]\n{render_items(items)}\n' for group, items in groups) + + +def render_items(eps): + return '\n'.join(f'{ep.name} = {ep.value}' for ep in sorted(eps)) diff --git a/lib/python3.12/site-packages/setuptools/_imp.py b/lib/python3.12/site-packages/setuptools/_imp.py new file mode 100644 index 0000000000000000000000000000000000000000..f1d9f29218987d4f830f2d57aca9e3f74d00a095 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_imp.py @@ -0,0 +1,87 @@ +""" +Re-implementation of find_module and get_frozen_object +from the deprecated imp module. +""" + +import importlib.machinery +import importlib.util +import os +import tokenize +from importlib.util import module_from_spec + +PY_SOURCE = 1 +PY_COMPILED = 2 +C_EXTENSION = 3 +C_BUILTIN = 6 +PY_FROZEN = 7 + + +def find_spec(module, paths): + finder = ( + importlib.machinery.PathFinder().find_spec + if isinstance(paths, list) + else importlib.util.find_spec + ) + return finder(module, paths) + + +def find_module(module, paths=None): + """Just like 'imp.find_module()', but with package support""" + spec = find_spec(module, paths) + if spec is None: + raise ImportError(f"Can't find {module}") + if not spec.has_location and hasattr(spec, 'submodule_search_locations'): + spec = importlib.util.spec_from_loader('__init__.py', spec.loader) + + kind = -1 + file = None + static = isinstance(spec.loader, type) + if ( + spec.origin == 'frozen' + or static + and issubclass(spec.loader, importlib.machinery.FrozenImporter) + ): + kind = PY_FROZEN + path = None # imp compabilty + suffix = mode = '' # imp compatibility + elif ( + spec.origin == 'built-in' + or static + and issubclass(spec.loader, importlib.machinery.BuiltinImporter) + ): + kind = C_BUILTIN + path = None # imp compabilty + suffix = mode = '' # imp compatibility + elif spec.has_location: + path = spec.origin + suffix = os.path.splitext(path)[1] + mode = 'r' if suffix in importlib.machinery.SOURCE_SUFFIXES else 'rb' + + if suffix in importlib.machinery.SOURCE_SUFFIXES: + kind = PY_SOURCE + file = tokenize.open(path) + elif suffix in importlib.machinery.BYTECODE_SUFFIXES: + kind = PY_COMPILED + file = open(path, 'rb') + elif suffix in importlib.machinery.EXTENSION_SUFFIXES: + kind = C_EXTENSION + + else: + path = None + suffix = mode = '' + + return file, path, (suffix, mode, kind) + + +def get_frozen_object(module, paths=None): + spec = find_spec(module, paths) + if not spec: + raise ImportError(f"Can't find {module}") + return spec.loader.get_code(module) + + +def get_module(module, paths, info): + spec = find_spec(module, paths) + if not spec: + raise ImportError(f"Can't find {module}") + return module_from_spec(spec) diff --git a/lib/python3.12/site-packages/setuptools/_importlib.py b/lib/python3.12/site-packages/setuptools/_importlib.py new file mode 100644 index 0000000000000000000000000000000000000000..ce0fd52653b56c9c2cb2b2c7bfb35e3ec3c61408 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_importlib.py @@ -0,0 +1,9 @@ +import sys + +if sys.version_info < (3, 10): + import importlib_metadata as metadata # pragma: no cover +else: + import importlib.metadata as metadata # noqa: F401 + + +import importlib.resources as resources # noqa: F401 diff --git a/lib/python3.12/site-packages/setuptools/_itertools.py b/lib/python3.12/site-packages/setuptools/_itertools.py new file mode 100644 index 0000000000000000000000000000000000000000..d6ca841353ce39ac4361013f5c8160d69028d0d8 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_itertools.py @@ -0,0 +1,23 @@ +from more_itertools import consume # noqa: F401 + + +# copied from jaraco.itertools 6.1 +def ensure_unique(iterable, key=lambda x: x): + """ + Wrap an iterable to raise a ValueError if non-unique values are encountered. + + >>> list(ensure_unique('abc')) + ['a', 'b', 'c'] + >>> consume(ensure_unique('abca')) + Traceback (most recent call last): + ... + ValueError: Duplicate element 'a' encountered. + """ + seen = set() + seen_add = seen.add + for element in iterable: + k = key(element) + if k in seen: + raise ValueError(f"Duplicate element {element!r} encountered.") + seen_add(k) + yield element diff --git a/lib/python3.12/site-packages/setuptools/_normalization.py b/lib/python3.12/site-packages/setuptools/_normalization.py new file mode 100644 index 0000000000000000000000000000000000000000..6b8d4ddbf900d648e30d61d96d9b9993c7c57663 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_normalization.py @@ -0,0 +1,180 @@ +""" +Helpers for normalization as expected in wheel/sdist/module file names +and core metadata +""" + +import re +from typing import TYPE_CHECKING + +import packaging + +# https://packaging.python.org/en/latest/specifications/core-metadata/#name +_VALID_NAME = re.compile(r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE) +_UNSAFE_NAME_CHARS = re.compile(r"[^A-Z0-9._-]+", re.IGNORECASE) +_NON_ALPHANUMERIC = re.compile(r"[^A-Z0-9]+", re.IGNORECASE) +_PEP440_FALLBACK = re.compile( + r"^v?(?P(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.IGNORECASE +) + + +def safe_identifier(name: str) -> str: + """Make a string safe to be used as Python identifier. + >>> safe_identifier("12abc") + '_12abc' + >>> safe_identifier("__editable__.myns.pkg-78.9.3_local") + '__editable___myns_pkg_78_9_3_local' + """ + safe = re.sub(r'\W|^(?=\d)', '_', name) + assert safe.isidentifier() + return safe + + +def safe_name(component: str) -> str: + """Escape a component used as a project name according to Core Metadata. + >>> safe_name("hello world") + 'hello-world' + >>> safe_name("hello?world") + 'hello-world' + >>> safe_name("hello_world") + 'hello_world' + """ + return _UNSAFE_NAME_CHARS.sub("-", component) + + +def safe_version(version: str) -> str: + """Convert an arbitrary string into a valid version string. + Can still raise an ``InvalidVersion`` exception. + To avoid exceptions use ``best_effort_version``. + >>> safe_version("1988 12 25") + '1988.12.25' + >>> safe_version("v0.2.1") + '0.2.1' + >>> safe_version("v0.2?beta") + '0.2b0' + >>> safe_version("v0.2 beta") + '0.2b0' + >>> safe_version("ubuntu lts") + Traceback (most recent call last): + ... + packaging.version.InvalidVersion: Invalid version: 'ubuntu.lts' + """ + v = version.replace(' ', '.') + try: + return str(packaging.version.Version(v)) + except packaging.version.InvalidVersion: + attempt = _UNSAFE_NAME_CHARS.sub("-", v) + return str(packaging.version.Version(attempt)) + + +def best_effort_version(version: str) -> str: + """Convert an arbitrary string into a version-like string. + Fallback when ``safe_version`` is not safe enough. + >>> best_effort_version("v0.2 beta") + '0.2b0' + >>> best_effort_version("ubuntu lts") + '0.dev0+sanitized.ubuntu.lts' + >>> best_effort_version("0.23ubuntu1") + '0.23.dev0+sanitized.ubuntu1' + >>> best_effort_version("0.23-") + '0.23.dev0+sanitized' + >>> best_effort_version("0.-_") + '0.dev0+sanitized' + >>> best_effort_version("42.+?1") + '42.dev0+sanitized.1' + """ + try: + return safe_version(version) + except packaging.version.InvalidVersion: + v = version.replace(' ', '.') + match = _PEP440_FALLBACK.search(v) + if match: + safe = match["safe"] + rest = v[len(safe) :] + else: + safe = "0" + rest = version + safe_rest = _NON_ALPHANUMERIC.sub(".", rest).strip(".") + local = f"sanitized.{safe_rest}".strip(".") + return safe_version(f"{safe}.dev0+{local}") + + +def safe_extra(extra: str) -> str: + """Normalize extra name according to PEP 685 + >>> safe_extra("_FrIeNdLy-._.-bArD") + 'friendly-bard' + >>> safe_extra("FrIeNdLy-._.-bArD__._-") + 'friendly-bard' + """ + return _NON_ALPHANUMERIC.sub("-", extra).strip("-").lower() + + +def filename_component(value: str) -> str: + """Normalize each component of a filename (e.g. distribution/version part of wheel) + Note: ``value`` needs to be already normalized. + >>> filename_component("my-pkg") + 'my_pkg' + """ + return value.replace("-", "_").strip("_") + + +def filename_component_broken(value: str) -> str: + """ + Produce the incorrect filename component for compatibility. + + See pypa/setuptools#4167 for detailed analysis. + + TODO: replace this with filename_component after pip 24 is + nearly-ubiquitous. + + >>> filename_component_broken('foo_bar-baz') + 'foo-bar-baz' + """ + return value.replace('_', '-') + + +def safer_name(value: str) -> str: + """Like ``safe_name`` but can be used as filename component for wheel""" + # See bdist_wheel.safer_name + return ( + # Per https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization + re + .sub(r"[-_.]+", "-", safe_name(value)) + .lower() + # Per https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode + .replace("-", "_") + ) + + +def safer_best_effort_version(value: str) -> str: + """Like ``best_effort_version`` but can be used as filename component for wheel""" + # See bdist_wheel.safer_verion + # TODO: Replace with only safe_version in the future (no need for best effort) + return filename_component(best_effort_version(value)) + + +def _missing_canonicalize_license_expression(expression: str) -> str: + """ + Defer import error to affect only users that actually use it + https://github.com/pypa/setuptools/issues/4894 + >>> _missing_canonicalize_license_expression("a OR b") + Traceback (most recent call last): + ... + ImportError: ...Cannot import `packaging.licenses`... + """ + raise ImportError( + "Cannot import `packaging.licenses`." + """ + Setuptools>=77.0.0 requires "packaging>=24.2" to work properly. + Please make sure you have a suitable version installed. + """ + ) + + +try: + from packaging.licenses import ( + canonicalize_license_expression as _canonicalize_license_expression, + ) +except ImportError: # pragma: nocover + if not TYPE_CHECKING: + # XXX: pyright is still upset even with # pyright: ignore[reportAssignmentType] + _canonicalize_license_expression = _missing_canonicalize_license_expression diff --git a/lib/python3.12/site-packages/setuptools/_path.py b/lib/python3.12/site-packages/setuptools/_path.py new file mode 100644 index 0000000000000000000000000000000000000000..2b78022934d89599e642a10f861b382947031be9 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_path.py @@ -0,0 +1,93 @@ +from __future__ import annotations + +import contextlib +import os +import sys +from typing import TYPE_CHECKING, TypeVar, Union + +from more_itertools import unique_everseen + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +StrPath: TypeAlias = Union[str, os.PathLike[str]] # Same as _typeshed.StrPath +StrPathT = TypeVar("StrPathT", bound=Union[str, os.PathLike[str]]) + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + os.makedirs(dirname, exist_ok=True) + + +def same_path(p1: StrPath, p2: StrPath) -> bool: + """Differs from os.path.samefile because it does not require paths to exist. + Purely string based (no comparison between i-nodes). + >>> same_path("a/b", "./a/b") + True + >>> same_path("a/b", "a/./b") + True + >>> same_path("a/b", "././a/b") + True + >>> same_path("a/b", "./a/b/c/..") + True + >>> same_path("a/b", "../a/b/c") + False + >>> same_path("a", "a/b") + False + """ + return normpath(p1) == normpath(p2) + + +def _cygwin_patch(filename: StrPath): # pragma: nocover + """ + Contrary to POSIX 2008, on Cygwin, getcwd (3) contains + symlink components. Using + os.path.abspath() works around this limitation. A fix in os.getcwd() + would probably better, in Cygwin even more so, except + that this seems to be by design... + """ + return os.path.abspath(filename) if sys.platform == 'cygwin' else filename + + +def normpath(filename: StrPath) -> str: + """Normalize a file/dir name for comparison purposes.""" + return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename)))) + + +@contextlib.contextmanager +def paths_on_pythonpath(paths): + """ + Add the indicated paths to the head of the PYTHONPATH environment + variable so that subprocesses will also see the packages at + these paths. + + Do this in a context that restores the value on exit. + + >>> getfixture('monkeypatch').setenv('PYTHONPATH', 'anything') + >>> with paths_on_pythonpath(['foo', 'bar']): + ... assert 'foo' in os.environ['PYTHONPATH'] + ... assert 'anything' in os.environ['PYTHONPATH'] + >>> os.environ['PYTHONPATH'] + 'anything' + + >>> getfixture('monkeypatch').delenv('PYTHONPATH') + >>> with paths_on_pythonpath(['foo', 'bar']): + ... assert 'foo' in os.environ['PYTHONPATH'] + >>> os.environ.get('PYTHONPATH') + """ + nothing = object() + orig_pythonpath = os.environ.get('PYTHONPATH', nothing) + current_pythonpath = os.environ.get('PYTHONPATH', '') + try: + prefix = os.pathsep.join(unique_everseen(paths)) + to_join = filter(None, [prefix, current_pythonpath]) + new_path = os.pathsep.join(to_join) + if new_path: + os.environ['PYTHONPATH'] = new_path + yield + finally: + if orig_pythonpath is nothing: + os.environ.pop('PYTHONPATH', None) + else: + os.environ['PYTHONPATH'] = orig_pythonpath diff --git a/lib/python3.12/site-packages/setuptools/_reqs.py b/lib/python3.12/site-packages/setuptools/_reqs.py new file mode 100644 index 0000000000000000000000000000000000000000..7be56cbf35d4a7cbeacdca1c18599433d7fb2f7f --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_reqs.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from collections.abc import Iterable, Iterator +from functools import lru_cache +from typing import TYPE_CHECKING, Callable, TypeVar, Union, overload + +import jaraco.text as text +from packaging.requirements import Requirement + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +_T = TypeVar("_T") +_StrOrIter: TypeAlias = Union[str, Iterable[str]] + + +parse_req: Callable[[str], Requirement] = lru_cache()(Requirement) +# Setuptools parses the same requirement many times +# (e.g. first for validation than for normalisation), +# so it might be worth to cache. + + +def parse_strings(strs: _StrOrIter) -> Iterator[str]: + """ + Yield requirement strings for each specification in `strs`. + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + return text.join_continuation(map(text.drop_comment, text.yield_lines(strs))) + + +# These overloads are only needed because of a mypy false-positive, pyright gets it right +# https://github.com/python/mypy/issues/3737 +@overload +def parse(strs: _StrOrIter) -> Iterator[Requirement]: ... +@overload +def parse(strs: _StrOrIter, parser: Callable[[str], _T]) -> Iterator[_T]: ... +def parse(strs: _StrOrIter, parser: Callable[[str], _T] = parse_req) -> Iterator[_T]: # type: ignore[assignment] + """ + Parse requirements. + """ + return map(parser, parse_strings(strs)) diff --git a/lib/python3.12/site-packages/setuptools/_scripts.py b/lib/python3.12/site-packages/setuptools/_scripts.py new file mode 100644 index 0000000000000000000000000000000000000000..88bf02f927ba7cb47731fc0984f4b4f135fbdd45 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_scripts.py @@ -0,0 +1,361 @@ +from __future__ import annotations + +import os +import re +import shlex +import shutil +import struct +import subprocess +import sys +import textwrap +from collections.abc import Iterable +from typing import TYPE_CHECKING, TypedDict + +from ._importlib import metadata, resources + +if TYPE_CHECKING: + from typing_extensions import Self + +from .warnings import SetuptoolsWarning + +from distutils.command.build_scripts import first_line_re +from distutils.util import get_platform + + +class _SplitArgs(TypedDict, total=False): + comments: bool + posix: bool + + +class CommandSpec(list): + """ + A command spec for a #! header, specified as a list of arguments akin to + those passed to Popen. + """ + + options: list[str] = [] + split_args = _SplitArgs() + + @classmethod + def best(cls): + """ + Choose the best CommandSpec class based on environmental conditions. + """ + return cls + + @classmethod + def _sys_executable(cls): + _default = os.path.normpath(sys.executable) + return os.environ.get('__PYVENV_LAUNCHER__', _default) + + @classmethod + def from_param(cls, param: Self | str | Iterable[str] | None) -> Self: + """ + Construct a CommandSpec from a parameter to build_scripts, which may + be None. + """ + if isinstance(param, cls): + return param + if isinstance(param, str): + return cls.from_string(param) + if isinstance(param, Iterable): + return cls(param) + if param is None: + return cls.from_environment() + raise TypeError(f"Argument has an unsupported type {type(param)}") + + @classmethod + def from_environment(cls): + return cls([cls._sys_executable()]) + + @classmethod + def from_string(cls, string: str) -> Self: + """ + Construct a command spec from a simple string representing a command + line parseable by shlex.split. + """ + items = shlex.split(string, **cls.split_args) + return cls(items) + + def install_options(self, script_text: str): + self.options = shlex.split(self._extract_options(script_text)) + cmdline = subprocess.list2cmdline(self) + if not isascii(cmdline): + self.options[:0] = ['-x'] + + @staticmethod + def _extract_options(orig_script): + """ + Extract any options from the first line of the script. + """ + first = (orig_script + '\n').splitlines()[0] + match = _first_line_re().match(first) + options = match.group(1) or '' if match else '' + return options.strip() + + def as_header(self): + return self._render(self + list(self.options)) + + @staticmethod + def _strip_quotes(item): + _QUOTES = '"\'' + for q in _QUOTES: + if item.startswith(q) and item.endswith(q): + return item[1:-1] + return item + + @staticmethod + def _render(items): + cmdline = subprocess.list2cmdline( + CommandSpec._strip_quotes(item.strip()) for item in items + ) + return '#!' + cmdline + '\n' + + +class WindowsCommandSpec(CommandSpec): + split_args = _SplitArgs(posix=False) + + +class ScriptWriter: + """ + Encapsulates behavior around writing entry point scripts for console and + gui apps. + """ + + template = textwrap.dedent( + r""" + # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r + import re + import sys + + # for compatibility with easy_install; see #2198 + __requires__ = %(spec)r + + try: + from importlib.metadata import distribution + except ImportError: + try: + from importlib_metadata import distribution + except ImportError: + from pkg_resources import load_entry_point + + + def importlib_load_entry_point(spec, group, name): + dist_name, _, _ = spec.partition('==') + matches = ( + entry_point + for entry_point in distribution(dist_name).entry_points + if entry_point.group == group and entry_point.name == name + ) + return next(matches).load() + + + globals().setdefault('load_entry_point', importlib_load_entry_point) + + + if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(load_entry_point(%(spec)r, %(group)r, %(name)r)()) + """ + ).lstrip() + + command_spec_class = CommandSpec + + @classmethod + def get_args(cls, dist, header=None): + """ + Yield write_script() argument tuples for a distribution's + console_scripts and gui_scripts entry points. + """ + + # If distribution is not an importlib.metadata.Distribution, assume + # it's a pkg_resources.Distribution and transform it. + if not hasattr(dist, 'entry_points'): + SetuptoolsWarning.emit("Unsupported distribution encountered.") + dist = metadata.Distribution.at(dist.egg_info) + + if header is None: + header = cls.get_header() + spec = f'{dist.name}=={dist.version}' + for type_ in 'console', 'gui': + group = f'{type_}_scripts' + for ep in dist.entry_points.select(group=group): + name = ep.name + cls._ensure_safe_name(ep.name) + script_text = cls.template % locals() + args = cls._get_script_args(type_, ep.name, header, script_text) + yield from args + + @staticmethod + def _ensure_safe_name(name): + """ + Prevent paths in *_scripts entry point names. + """ + has_path_sep = re.search(r'[\\/]', name) + if has_path_sep: + raise ValueError("Path separators not allowed in script names") + + @classmethod + def best(cls): + """ + Select the best ScriptWriter for this environment. + """ + if sys.platform == 'win32' or (os.name == 'java' and os._name == 'nt'): + return WindowsScriptWriter.best() + else: + return cls + + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + # Simply write the stub with no extension. + yield (name, header + script_text) + + @classmethod + def get_header( + cls, + script_text: str = "", + executable: str | CommandSpec | Iterable[str] | None = None, + ) -> str: + """Create a #! line, getting options (if any) from script_text""" + cmd = cls.command_spec_class.best().from_param(executable) + cmd.install_options(script_text) + return cmd.as_header() + + +class WindowsScriptWriter(ScriptWriter): + command_spec_class = WindowsCommandSpec + + @classmethod + def best(cls): + """ + Select the best ScriptWriter suitable for Windows + """ + writer_lookup = dict( + executable=WindowsExecutableLauncherWriter, + natural=cls, + ) + # for compatibility, use the executable launcher by default + launcher = os.environ.get('SETUPTOOLS_LAUNCHER', 'executable') + return writer_lookup[launcher] + + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + "For Windows, add a .py extension" + ext = dict(console='.pya', gui='.pyw')[type_] + if ext not in os.environ['PATHEXT'].lower().split(';'): + msg = ( + "{ext} not listed in PATHEXT; scripts will not be " + "recognized as executables." + ).format(**locals()) + SetuptoolsWarning.emit(msg) + old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe'] + old.remove(ext) + header = cls._adjust_header(type_, header) + blockers = [name + x for x in old] + yield name + ext, header + script_text, 't', blockers + + @classmethod + def _adjust_header(cls, type_, orig_header): + """ + Make sure 'pythonw' is used for gui and 'python' is used for + console (regardless of what sys.executable is). + """ + pattern = 'pythonw.exe' + repl = 'python.exe' + if type_ == 'gui': + pattern, repl = repl, pattern + pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE) + new_header = pattern_ob.sub(string=orig_header, repl=repl) + return new_header if cls._use_header(new_header) else orig_header + + @staticmethod + def _use_header(new_header): + """ + Should _adjust_header use the replaced header? + + On non-windows systems, always use. On + Windows systems, only use the replaced header if it resolves + to an executable on the system. + """ + clean_header = new_header[2:-1].strip('"') + return sys.platform != 'win32' or shutil.which(clean_header) + + +class WindowsExecutableLauncherWriter(WindowsScriptWriter): + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + """ + For Windows, add a .py extension and an .exe launcher + """ + if type_ == 'gui': + launcher_type = 'gui' + ext = '-script.pyw' + old = ['.pyw'] + else: + launcher_type = 'cli' + ext = '-script.py' + old = ['.py', '.pyc', '.pyo'] + hdr = cls._adjust_header(type_, header) + blockers = [name + x for x in old] + yield (name + ext, hdr + script_text, 't', blockers) + yield ( + name + '.exe', + get_win_launcher(launcher_type), + 'b', # write in binary mode + ) + if not is_64bit(): + # install a manifest for the launcher to prevent Windows + # from detecting it as an installer (which it will for + # launchers like easy_install.exe). Consider only + # adding a manifest for launchers detected as installers. + # See Distribute #143 for details. + m_name = name + '.exe.manifest' + yield (m_name, load_launcher_manifest(name), 't') + + +def get_win_launcher(type): + """ + Load the Windows launcher (executable) suitable for launching a script. + + `type` should be either 'cli' or 'gui' + + Returns the executable as a byte string. + """ + launcher_fn = f'{type}.exe' + if is_64bit(): + if get_platform() == "win-arm64": + launcher_fn = launcher_fn.replace(".", "-arm64.") + else: + launcher_fn = launcher_fn.replace(".", "-64.") + else: + launcher_fn = launcher_fn.replace(".", "-32.") + return resources.files('setuptools').joinpath(launcher_fn).read_bytes() + + +def load_launcher_manifest(name): + res = resources.files(__name__).joinpath('launcher manifest.xml') + return res.read_text(encoding='utf-8') % vars() + + +def _first_line_re(): + """ + Return a regular expression based on first_line_re suitable for matching + strings. + """ + if isinstance(first_line_re.pattern, str): + return first_line_re + + # first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern. + return re.compile(first_line_re.pattern.decode()) + + +def is_64bit(): + return struct.calcsize("P") == 8 + + +def isascii(s): + try: + s.encode('ascii') + except UnicodeError: + return False + return True diff --git a/lib/python3.12/site-packages/setuptools/_shutil.py b/lib/python3.12/site-packages/setuptools/_shutil.py new file mode 100644 index 0000000000000000000000000000000000000000..660459a1102eed0da597ebd7c1a38cb4eee99791 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_shutil.py @@ -0,0 +1,59 @@ +"""Convenience layer on top of stdlib's shutil and os""" + +import os +import stat +from typing import Callable, TypeVar + +from .compat import py311 + +from distutils import log + +try: + from os import chmod # pyright: ignore[reportAssignmentType] + # Losing type-safety w/ pyright, but that's ok +except ImportError: # pragma: no cover + # Jython compatibility + def chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy reuses the imported definition anyway + pass + + +_T = TypeVar("_T") + + +def attempt_chmod_verbose(path, mode): + log.debug("changing mode of %s to %o", path, mode) + try: + chmod(path, mode) + except OSError as e: # pragma: no cover + log.debug("chmod failed: %s", e) + + +# Must match shutil._OnExcCallback +def _auto_chmod( + func: Callable[..., _T], arg: str, exc: BaseException +) -> _T: # pragma: no cover + """shutils onexc callback to automatically call chmod for certain functions.""" + # Only retry for scenarios known to have an issue + if func in [os.unlink, os.remove] and os.name == 'nt': + attempt_chmod_verbose(arg, stat.S_IWRITE) + return func(arg) + raise exc + + +def rmtree(path, ignore_errors=False, onexc=_auto_chmod): + """ + Similar to ``shutil.rmtree`` but automatically executes ``chmod`` + for well know Windows failure scenarios. + """ + return py311.shutil_rmtree(path, ignore_errors, onexc) + + +def rmdir(path, **opts): + if os.path.isdir(path): + rmtree(path, **opts) + + +def current_umask(): + tmp = os.umask(0o022) + os.umask(tmp) + return tmp diff --git a/lib/python3.12/site-packages/setuptools/_static.py b/lib/python3.12/site-packages/setuptools/_static.py new file mode 100644 index 0000000000000000000000000000000000000000..af35862cf8b759a0e60110ce9e92bfdb1b49bc5f --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/_static.py @@ -0,0 +1,188 @@ +from functools import wraps +from typing import TypeVar + +import packaging.specifiers + +from .warnings import SetuptoolsDeprecationWarning + + +class Static: + """ + Wrapper for built-in object types that are allow setuptools to identify + static core metadata (in opposition to ``Dynamic``, as defined :pep:`643`). + + The trick is to mark values with :class:`Static` when they come from + ``pyproject.toml`` or ``setup.cfg``, so if any plugin overwrite the value + with a built-in, setuptools will be able to recognise the change. + + We inherit from built-in classes, so that we don't need to change the existing + code base to deal with the new types. + We also should strive for immutability objects to avoid changes after the + initial parsing. + """ + + _mutated_: bool = False # TODO: Remove after deprecation warning is solved + + +def _prevent_modification(target: type, method: str, copying: str) -> None: + """ + Because setuptools is very flexible we cannot fully prevent + plugins and user customizations from modifying static values that were + parsed from config files. + But we can attempt to block "in-place" mutations and identify when they + were done. + """ + fn = getattr(target, method, None) + if fn is None: + return + + @wraps(fn) + def _replacement(self: Static, *args, **kwargs): + # TODO: After deprecation period raise NotImplementedError instead of warning + # which obviated the existence and checks of the `_mutated_` attribute. + self._mutated_ = True + SetuptoolsDeprecationWarning.emit( + "Direct modification of value will be disallowed", + f""" + In an effort to implement PEP 643, direct/in-place changes of static values + that come from configuration files are deprecated. + If you need to modify this value, please first create a copy with {copying} + and make sure conform to all relevant standards when overriding setuptools + functionality (https://packaging.python.org/en/latest/specifications/). + """, + due_date=(2025, 10, 10), # Initially introduced in 2024-09-06 + ) + return fn(self, *args, **kwargs) + + _replacement.__doc__ = "" # otherwise doctest may fail. + setattr(target, method, _replacement) + + +class Str(str, Static): + pass + + +class Tuple(tuple, Static): + pass + + +class List(list, Static): + """ + :meta private: + >>> x = List([1, 2, 3]) + >>> is_static(x) + True + >>> x += [0] # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + SetuptoolsDeprecationWarning: Direct modification ... + >>> is_static(x) # no longer static after modification + False + >>> y = list(x) + >>> y.clear() + >>> y + [] + >>> y == x + False + >>> is_static(List(y)) + True + """ + + +# Make `List` immutable-ish +# (certain places of setuptools/distutils issue a warn if we use tuple instead of list) +for _method in ( + '__delitem__', + '__iadd__', + '__setitem__', + 'append', + 'clear', + 'extend', + 'insert', + 'remove', + 'reverse', + 'pop', +): + _prevent_modification(List, _method, "`list(value)`") + + +class Dict(dict, Static): + """ + :meta private: + >>> x = Dict({'a': 1, 'b': 2}) + >>> is_static(x) + True + >>> x['c'] = 0 # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + SetuptoolsDeprecationWarning: Direct modification ... + >>> x._mutated_ + True + >>> is_static(x) # no longer static after modification + False + >>> y = dict(x) + >>> y.popitem() + ('b', 2) + >>> y == x + False + >>> is_static(Dict(y)) + True + """ + + +# Make `Dict` immutable-ish (we cannot inherit from types.MappingProxyType): +for _method in ( + '__delitem__', + '__ior__', + '__setitem__', + 'clear', + 'pop', + 'popitem', + 'setdefault', + 'update', +): + _prevent_modification(Dict, _method, "`dict(value)`") + + +class SpecifierSet(packaging.specifiers.SpecifierSet, Static): + """Not exactly a built-in type but useful for ``requires-python``""" + + +T = TypeVar("T") + + +def noop(value: T) -> T: + """ + >>> noop(42) + 42 + """ + return value + + +_CONVERSIONS = {str: Str, tuple: Tuple, list: List, dict: Dict} + + +def attempt_conversion(value: T) -> T: + """ + >>> is_static(attempt_conversion("hello")) + True + >>> is_static(object()) + False + """ + return _CONVERSIONS.get(type(value), noop)(value) # type: ignore[call-overload] + + +def is_static(value: object) -> bool: + """ + >>> is_static(a := Dict({'a': 1})) + True + >>> is_static(dict(a)) + False + >>> is_static(b := List([1, 2, 3])) + True + >>> is_static(list(b)) + False + """ + return isinstance(value, Static) and not value._mutated_ + + +EMPTY_LIST = List() +EMPTY_DICT = Dict() diff --git a/lib/python3.12/site-packages/setuptools/archive_util.py b/lib/python3.12/site-packages/setuptools/archive_util.py new file mode 100644 index 0000000000000000000000000000000000000000..1a02010bb2af2be0487730d6a32080877b9ac220 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/archive_util.py @@ -0,0 +1,219 @@ +"""Utilities for extracting common archive formats""" + +import contextlib +import os +import posixpath +import shutil +import tarfile +import zipfile + +from ._path import ensure_directory + +from distutils.errors import DistutilsError + +__all__ = [ + "unpack_archive", + "unpack_zipfile", + "unpack_tarfile", + "default_filter", + "UnrecognizedFormat", + "extraction_drivers", + "unpack_directory", +] + + +class UnrecognizedFormat(DistutilsError): + """Couldn't recognize the archive type""" + + +def default_filter(src, dst): + """The default progress/filter callback; returns True for all files""" + return dst + + +def unpack_archive( + filename, extract_dir, progress_filter=default_filter, drivers=None +) -> None: + """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat`` + + `progress_filter` is a function taking two arguments: a source path + internal to the archive ('/'-separated), and a filesystem path where it + will be extracted. The callback must return the desired extract path + (which may be the same as the one passed in), or else ``None`` to skip + that file or directory. The callback can thus be used to report on the + progress of the extraction, as well as to filter the items extracted or + alter their extraction paths. + + `drivers`, if supplied, must be a non-empty sequence of functions with the + same signature as this function (minus the `drivers` argument), that raise + ``UnrecognizedFormat`` if they do not support extracting the designated + archive type. The `drivers` are tried in sequence until one is found that + does not raise an error, or until all are exhausted (in which case + ``UnrecognizedFormat`` is raised). If you do not supply a sequence of + drivers, the module's ``extraction_drivers`` constant will be used, which + means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that + order. + """ + for driver in drivers or extraction_drivers: + try: + driver(filename, extract_dir, progress_filter) + except UnrecognizedFormat: + continue + else: + return + else: + raise UnrecognizedFormat(f"Not a recognized archive type: {filename}") + + +def unpack_directory(filename, extract_dir, progress_filter=default_filter) -> None: + """ "Unpack" a directory, using the same interface as for archives + + Raises ``UnrecognizedFormat`` if `filename` is not a directory + """ + if not os.path.isdir(filename): + raise UnrecognizedFormat(f"{filename} is not a directory") + + paths = { + filename: ('', extract_dir), + } + for base, dirs, files in os.walk(filename): + src, dst = paths[base] + for d in dirs: + paths[os.path.join(base, d)] = src + d + '/', os.path.join(dst, d) + for f in files: + target = os.path.join(dst, f) + target = progress_filter(src + f, target) + if not target: + # skip non-files + continue + ensure_directory(target) + f = os.path.join(base, f) + shutil.copyfile(f, target) + shutil.copystat(f, target) + + +def unpack_zipfile(filename, extract_dir, progress_filter=default_filter) -> None: + """Unpack zip `filename` to `extract_dir` + + Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined + by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation + of the `progress_filter` argument. + """ + + if not zipfile.is_zipfile(filename): + raise UnrecognizedFormat(f"{filename} is not a zip file") + + with zipfile.ZipFile(filename) as z: + _unpack_zipfile_obj(z, extract_dir, progress_filter) + + +def _unpack_zipfile_obj(zipfile_obj, extract_dir, progress_filter=default_filter): + """Internal/private API used by other parts of setuptools. + Similar to ``unpack_zipfile``, but receives an already opened :obj:`zipfile.ZipFile` + object instead of a filename. + """ + for info in zipfile_obj.infolist(): + name = info.filename + + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name.split('/'): + continue + + target = os.path.join(extract_dir, *name.split('/')) + target = progress_filter(name, target) + if not target: + continue + if name.endswith('/'): + # directory + ensure_directory(target) + else: + # file + ensure_directory(target) + data = zipfile_obj.read(info.filename) + with open(target, 'wb') as f: + f.write(data) + unix_attributes = info.external_attr >> 16 + if unix_attributes: + os.chmod(target, unix_attributes) + + +def _resolve_tar_file_or_dir(tar_obj, tar_member_obj): + """Resolve any links and extract link targets as normal files.""" + while tar_member_obj is not None and ( + tar_member_obj.islnk() or tar_member_obj.issym() + ): + linkpath = tar_member_obj.linkname + if tar_member_obj.issym(): + base = posixpath.dirname(tar_member_obj.name) + linkpath = posixpath.join(base, linkpath) + linkpath = posixpath.normpath(linkpath) + tar_member_obj = tar_obj._getmember(linkpath) + + is_file_or_dir = tar_member_obj is not None and ( + tar_member_obj.isfile() or tar_member_obj.isdir() + ) + if is_file_or_dir: + return tar_member_obj + + raise LookupError('Got unknown file type') + + +def _iter_open_tar(tar_obj, extract_dir, progress_filter): + """Emit member-destination pairs from a tar archive.""" + # don't do any chowning! + tar_obj.chown = lambda *args: None + + with contextlib.closing(tar_obj): + for member in tar_obj: + name = member.name + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name.split('/'): + continue + + prelim_dst = os.path.join(extract_dir, *name.split('/')) + + try: + member = _resolve_tar_file_or_dir(tar_obj, member) + except LookupError: + continue + + final_dst = progress_filter(name, prelim_dst) + if not final_dst: + continue + + if final_dst.endswith(os.sep): + final_dst = final_dst[:-1] + + yield member, final_dst + + +def unpack_tarfile(filename, extract_dir, progress_filter=default_filter) -> bool: + """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` + + Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined + by ``tarfile.open()``). See ``unpack_archive()`` for an explanation + of the `progress_filter` argument. + """ + try: + tarobj = tarfile.open(filename) + except tarfile.TarError as e: + raise UnrecognizedFormat( + f"{filename} is not a compressed or uncompressed tar file" + ) from e + + for member, final_dst in _iter_open_tar( + tarobj, + extract_dir, + progress_filter, + ): + try: + # XXX Ugh + tarobj._extract_member(member, final_dst) + except tarfile.ExtractError: + # chown/chmod/mkfifo/mknode/makedev failed + pass + + return True + + +extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile diff --git a/lib/python3.12/site-packages/setuptools/build_meta.py b/lib/python3.12/site-packages/setuptools/build_meta.py new file mode 100644 index 0000000000000000000000000000000000000000..0dc04f6cbb7f1f012b26b99cc3b204cabaefdf4a --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/build_meta.py @@ -0,0 +1,554 @@ +"""A PEP 517 interface to setuptools + +Previously, when a user or a command line tool (let's call it a "frontend") +needed to make a request of setuptools to take a certain action, for +example, generating a list of installation requirements, the frontend +would call "setup.py egg_info" or "setup.py bdist_wheel" on the command line. + +PEP 517 defines a different method of interfacing with setuptools. Rather +than calling "setup.py" directly, the frontend should: + + 1. Set the current directory to the directory with a setup.py file + 2. Import this module into a safe python interpreter (one in which + setuptools can potentially set global variables or crash hard). + 3. Call one of the functions defined in PEP 517. + +What each function does is defined in PEP 517. However, here is a "casual" +definition of the functions (this definition should not be relied on for +bug reports or API stability): + + - `build_wheel`: build a wheel in the folder and return the basename + - `get_requires_for_build_wheel`: get the `setup_requires` to build + - `prepare_metadata_for_build_wheel`: get the `install_requires` + - `build_sdist`: build an sdist in the folder and return the basename + - `get_requires_for_build_sdist`: get the `setup_requires` to build + +Again, this is not a formal definition! Just a "taste" of the module. +""" + +from __future__ import annotations + +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import tokenize +import warnings +from collections.abc import Iterable, Iterator, Mapping +from pathlib import Path +from typing import TYPE_CHECKING, NoReturn, Union + +import setuptools + +from . import errors +from ._path import StrPath, same_path +from ._reqs import parse_strings +from .warnings import SetuptoolsDeprecationWarning + +import distutils +from distutils.util import strtobool + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +__all__ = [ + 'get_requires_for_build_sdist', + 'get_requires_for_build_wheel', + 'prepare_metadata_for_build_wheel', + 'build_wheel', + 'build_sdist', + 'get_requires_for_build_editable', + 'prepare_metadata_for_build_editable', + 'build_editable', + '__legacy__', + 'SetupRequirementsError', +] + + +class SetupRequirementsError(BaseException): + def __init__(self, specifiers) -> None: + self.specifiers = specifiers + + +class Distribution(setuptools.dist.Distribution): + def fetch_build_eggs(self, specifiers) -> NoReturn: + specifier_list = list(parse_strings(specifiers)) + + raise SetupRequirementsError(specifier_list) + + @classmethod + @contextlib.contextmanager + def patch(cls) -> Iterator[None]: + """ + Replace + distutils.dist.Distribution with this class + for the duration of this context. + """ + orig = distutils.core.Distribution + distutils.core.Distribution = cls # type: ignore[misc] # monkeypatching + try: + yield + finally: + distutils.core.Distribution = orig # type: ignore[misc] # monkeypatching + + +@contextlib.contextmanager +def no_install_setup_requires(): + """Temporarily disable installing setup_requires + + Under PEP 517, the backend reports build dependencies to the frontend, + and the frontend is responsible for ensuring they're installed. + So setuptools (acting as a backend) should not try to install them. + """ + orig = setuptools._install_setup_requires + setuptools._install_setup_requires = lambda attrs: None + try: + yield + finally: + setuptools._install_setup_requires = orig + + +def _get_immediate_subdirectories(a_dir): + return [ + name for name in os.listdir(a_dir) if os.path.isdir(os.path.join(a_dir, name)) + ] + + +def _file_with_extension(directory: StrPath, extension: str | tuple[str, ...]): + matching = (f for f in os.listdir(directory) if f.endswith(extension)) + try: + (file,) = matching + except ValueError: + raise ValueError( + 'No distribution was found. Ensure that `setup.py` ' + 'is not empty and that it calls `setup()`.' + ) from None + return file + + +def _open_setup_script(setup_script): + if not os.path.exists(setup_script): + # Supply a default setup.py + return io.StringIO("from setuptools import setup; setup()") + + return tokenize.open(setup_script) + + +@contextlib.contextmanager +def suppress_known_deprecation(): + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', 'setup.py install is deprecated') + yield + + +_ConfigSettings: TypeAlias = Union[Mapping[str, Union[str, list[str], None]], None] +""" +Currently the user can run:: + + pip install -e . --config-settings key=value + python -m build -C--key=value -C key=value + +- pip will pass both key and value as strings and overwriting repeated keys + (pypa/pip#11059). +- build will accumulate values associated with repeated keys in a list. + It will also accept keys with no associated value. + This means that an option passed by build can be ``str | list[str] | None``. +- PEP 517 specifies that ``config_settings`` is an optional dict. +""" + + +class _ConfigSettingsTranslator: + """Translate ``config_settings`` into distutils-style command arguments. + Only a limited number of options is currently supported. + """ + + # See pypa/setuptools#1928 pypa/setuptools#2491 + + def _get_config(self, key: str, config_settings: _ConfigSettings) -> list[str]: + """ + Get the value of a specific key in ``config_settings`` as a list of strings. + + >>> fn = _ConfigSettingsTranslator()._get_config + >>> fn("--global-option", None) + [] + >>> fn("--global-option", {}) + [] + >>> fn("--global-option", {'--global-option': 'foo'}) + ['foo'] + >>> fn("--global-option", {'--global-option': ['foo']}) + ['foo'] + >>> fn("--global-option", {'--global-option': 'foo'}) + ['foo'] + >>> fn("--global-option", {'--global-option': 'foo bar'}) + ['foo', 'bar'] + """ + cfg = config_settings or {} + opts = cfg.get(key) or [] + return shlex.split(opts) if isinstance(opts, str) else opts + + def _global_args(self, config_settings: _ConfigSettings) -> Iterator[str]: + """ + Let the user specify ``verbose`` or ``quiet`` + escape hatch via + ``--global-option``. + Note: ``-v``, ``-vv``, ``-vvv`` have similar effects in setuptools, + so we just have to cover the basic scenario ``-v``. + + >>> fn = _ConfigSettingsTranslator()._global_args + >>> list(fn(None)) + [] + >>> list(fn({"verbose": "False"})) + ['-q'] + >>> list(fn({"verbose": "1"})) + ['-v'] + >>> list(fn({"--verbose": None})) + ['-v'] + >>> list(fn({"verbose": "true", "--global-option": "-q --no-user-cfg"})) + ['-v', '-q', '--no-user-cfg'] + >>> list(fn({"--quiet": None})) + ['-q'] + """ + cfg = config_settings or {} + falsey = {"false", "no", "0", "off"} + if "verbose" in cfg or "--verbose" in cfg: + level = str(cfg.get("verbose") or cfg.get("--verbose") or "1") + yield ("-q" if level.lower() in falsey else "-v") + if "quiet" in cfg or "--quiet" in cfg: + level = str(cfg.get("quiet") or cfg.get("--quiet") or "1") + yield ("-v" if level.lower() in falsey else "-q") + + yield from self._get_config("--global-option", config_settings) + + def __dist_info_args(self, config_settings: _ConfigSettings) -> Iterator[str]: + """ + The ``dist_info`` command accepts ``tag-date`` and ``tag-build``. + + .. warning:: + We cannot use this yet as it requires the ``sdist`` and ``bdist_wheel`` + commands run in ``build_sdist`` and ``build_wheel`` to reuse the egg-info + directory created in ``prepare_metadata_for_build_wheel``. + + >>> fn = _ConfigSettingsTranslator()._ConfigSettingsTranslator__dist_info_args + >>> list(fn(None)) + [] + >>> list(fn({"tag-date": "False"})) + ['--no-date'] + >>> list(fn({"tag-date": None})) + ['--no-date'] + >>> list(fn({"tag-date": "true", "tag-build": ".a"})) + ['--tag-date', '--tag-build', '.a'] + """ + cfg = config_settings or {} + if "tag-date" in cfg: + val = strtobool(str(cfg["tag-date"] or "false")) + yield ("--tag-date" if val else "--no-date") + if "tag-build" in cfg: + yield from ["--tag-build", str(cfg["tag-build"])] + + def _editable_args(self, config_settings: _ConfigSettings) -> Iterator[str]: + """ + The ``editable_wheel`` command accepts ``editable-mode=strict``. + + >>> fn = _ConfigSettingsTranslator()._editable_args + >>> list(fn(None)) + [] + >>> list(fn({"editable-mode": "strict"})) + ['--mode', 'strict'] + """ + cfg = config_settings or {} + mode = cfg.get("editable-mode") or cfg.get("editable_mode") + if not mode: + return + yield from ["--mode", str(mode)] + + def _arbitrary_args(self, config_settings: _ConfigSettings) -> Iterator[str]: + """ + Users may expect to pass arbitrary lists of arguments to a command + via "--global-option" (example provided in PEP 517 of a "escape hatch"). + + >>> fn = _ConfigSettingsTranslator()._arbitrary_args + >>> list(fn(None)) + [] + >>> list(fn({})) + [] + >>> list(fn({'--build-option': 'foo'})) + ['foo'] + >>> list(fn({'--build-option': ['foo']})) + ['foo'] + >>> list(fn({'--build-option': 'foo'})) + ['foo'] + >>> list(fn({'--build-option': 'foo bar'})) + ['foo', 'bar'] + >>> list(fn({'--global-option': 'foo'})) + [] + """ + yield from self._get_config("--build-option", config_settings) + + +class _BuildMetaBackend(_ConfigSettingsTranslator): + def _get_build_requires( + self, config_settings: _ConfigSettings, requirements: list[str] + ): + sys.argv = [ + *sys.argv[:1], + *self._global_args(config_settings), + "egg_info", + ] + try: + with Distribution.patch(): + self.run_setup() + except SetupRequirementsError as e: + requirements += e.specifiers + + return requirements + + def run_setup(self, setup_script: str = 'setup.py') -> None: + # Note that we can reuse our build directory between calls + # Correctness comes first, then optimization later + __file__ = os.path.abspath(setup_script) + __name__ = '__main__' + + with _open_setup_script(__file__) as f: + code = f.read().replace(r'\r\n', r'\n') + + try: + exec(code, locals()) + except SystemExit as e: + if e.code: + raise + # We ignore exit code indicating success + SetuptoolsDeprecationWarning.emit( + "Running `setup.py` directly as CLI tool is deprecated.", + "Please avoid using `sys.exit(0)` or similar statements " + "that don't fit in the paradigm of a configuration file.", + see_url="https://blog.ganssle.io/articles/2021/10/" + "setup-py-deprecated.html", + ) + + def get_requires_for_build_wheel( + self, config_settings: _ConfigSettings = None + ) -> list[str]: + return self._get_build_requires(config_settings, requirements=[]) + + def get_requires_for_build_sdist( + self, config_settings: _ConfigSettings = None + ) -> list[str]: + return self._get_build_requires(config_settings, requirements=[]) + + def _bubble_up_info_directory( + self, metadata_directory: StrPath, suffix: str + ) -> str: + """ + PEP 517 requires that the .dist-info directory be placed in the + metadata_directory. To comply, we MUST copy the directory to the root. + + Returns the basename of the info directory, e.g. `proj-0.0.0.dist-info`. + """ + info_dir = self._find_info_directory(metadata_directory, suffix) + if not same_path(info_dir.parent, metadata_directory): + shutil.move(str(info_dir), metadata_directory) + # PEP 517 allow other files and dirs to exist in metadata_directory + return info_dir.name + + def _find_info_directory(self, metadata_directory: StrPath, suffix: str) -> Path: + for parent, dirs, _ in os.walk(metadata_directory): + candidates = [f for f in dirs if f.endswith(suffix)] + + if len(candidates) != 0 or len(dirs) != 1: + assert len(candidates) == 1, f"Multiple {suffix} directories found" + return Path(parent, candidates[0]) + + msg = f"No {suffix} directory found in {metadata_directory}" + raise errors.InternalError(msg) + + def prepare_metadata_for_build_wheel( + self, metadata_directory: StrPath, config_settings: _ConfigSettings = None + ) -> str: + sys.argv = [ + *sys.argv[:1], + *self._global_args(config_settings), + "dist_info", + "--output-dir", + str(metadata_directory), + "--keep-egg-info", + ] + with no_install_setup_requires(): + self.run_setup() + + self._bubble_up_info_directory(metadata_directory, ".egg-info") + return self._bubble_up_info_directory(metadata_directory, ".dist-info") + + def _build_with_temp_dir( + self, + setup_command: Iterable[str], + result_extension: str | tuple[str, ...], + result_directory: StrPath, + config_settings: _ConfigSettings, + arbitrary_args: Iterable[str] = (), + ): + result_directory = os.path.abspath(result_directory) + + # Build in a temporary directory, then copy to the target. + os.makedirs(result_directory, exist_ok=True) + + with tempfile.TemporaryDirectory( + prefix=".tmp-", dir=result_directory + ) as tmp_dist_dir: + sys.argv = [ + *sys.argv[:1], + *self._global_args(config_settings), + *setup_command, + "--dist-dir", + tmp_dist_dir, + *arbitrary_args, + ] + with no_install_setup_requires(): + self.run_setup() + + result_basename = _file_with_extension(tmp_dist_dir, result_extension) + result_path = os.path.join(result_directory, result_basename) + if os.path.exists(result_path): + # os.rename will fail overwriting on non-Unix. + os.remove(result_path) + os.rename(os.path.join(tmp_dist_dir, result_basename), result_path) + + return result_basename + + def build_wheel( + self, + wheel_directory: StrPath, + config_settings: _ConfigSettings = None, + metadata_directory: StrPath | None = None, + ) -> str: + def _build(cmd: list[str]): + with suppress_known_deprecation(): + return self._build_with_temp_dir( + cmd, + '.whl', + wheel_directory, + config_settings, + self._arbitrary_args(config_settings), + ) + + if metadata_directory is None: + return _build(['bdist_wheel']) + + try: + return _build(['bdist_wheel', '--dist-info-dir', str(metadata_directory)]) + except SystemExit as ex: # pragma: nocover + # pypa/setuptools#4683 + if "--dist-info-dir not recognized" not in str(ex): + raise + _IncompatibleBdistWheel.emit() + return _build(['bdist_wheel']) + + def build_sdist( + self, sdist_directory: StrPath, config_settings: _ConfigSettings = None + ) -> str: + return self._build_with_temp_dir( + ['sdist', '--formats', 'gztar'], '.tar.gz', sdist_directory, config_settings + ) + + def _get_dist_info_dir(self, metadata_directory: StrPath | None) -> str | None: + if not metadata_directory: + return None + dist_info_candidates = list(Path(metadata_directory).glob("*.dist-info")) + assert len(dist_info_candidates) <= 1 + return str(dist_info_candidates[0]) if dist_info_candidates else None + + def build_editable( + self, + wheel_directory: StrPath, + config_settings: _ConfigSettings = None, + metadata_directory: StrPath | None = None, + ) -> str: + # XXX can or should we hide our editable_wheel command normally? + info_dir = self._get_dist_info_dir(metadata_directory) + opts = ["--dist-info-dir", info_dir] if info_dir else [] + cmd = ["editable_wheel", *opts, *self._editable_args(config_settings)] + with suppress_known_deprecation(): + return self._build_with_temp_dir( + cmd, ".whl", wheel_directory, config_settings + ) + + def get_requires_for_build_editable( + self, config_settings: _ConfigSettings = None + ) -> list[str]: + return self.get_requires_for_build_wheel(config_settings) + + def prepare_metadata_for_build_editable( + self, metadata_directory: StrPath, config_settings: _ConfigSettings = None + ) -> str: + return self.prepare_metadata_for_build_wheel( + metadata_directory, config_settings + ) + + +class _BuildMetaLegacyBackend(_BuildMetaBackend): + """Compatibility backend for setuptools + + This is a version of setuptools.build_meta that endeavors + to maintain backwards + compatibility with pre-PEP 517 modes of invocation. It + exists as a temporary + bridge between the old packaging mechanism and the new + packaging mechanism, + and will eventually be removed. + """ + + def run_setup(self, setup_script: str = 'setup.py') -> None: + # In order to maintain compatibility with scripts assuming that + # the setup.py script is in a directory on the PYTHONPATH, inject + # '' into sys.path. (pypa/setuptools#1642) + sys_path = list(sys.path) # Save the original path + + script_dir = os.path.dirname(os.path.abspath(setup_script)) + if script_dir not in sys.path: + sys.path.insert(0, script_dir) + + # Some setup.py scripts (e.g. in pygame and numpy) use sys.argv[0] to + # get the directory of the source code. They expect it to refer to the + # setup.py script. + sys_argv_0 = sys.argv[0] + sys.argv[0] = setup_script + + try: + super().run_setup(setup_script=setup_script) + finally: + # While PEP 517 frontends should be calling each hook in a fresh + # subprocess according to the standard (and thus it should not be + # strictly necessary to restore the old sys.path), we'll restore + # the original path so that the path manipulation does not persist + # within the hook after run_setup is called. + sys.path[:] = sys_path + sys.argv[0] = sys_argv_0 + + +class _IncompatibleBdistWheel(SetuptoolsDeprecationWarning): + _SUMMARY = "wheel.bdist_wheel is deprecated, please import it from setuptools" + _DETAILS = """ + Ensure that any custom bdist_wheel implementation is a subclass of + setuptools.command.bdist_wheel.bdist_wheel. + """ + _DUE_DATE = (2025, 10, 15) + # Initially introduced in 2024/10/15, but maybe too disruptive to be enforced? + _SEE_URL = "https://github.com/pypa/wheel/pull/631" + + +# The primary backend +_BACKEND = _BuildMetaBackend() + +get_requires_for_build_wheel = _BACKEND.get_requires_for_build_wheel +get_requires_for_build_sdist = _BACKEND.get_requires_for_build_sdist +prepare_metadata_for_build_wheel = _BACKEND.prepare_metadata_for_build_wheel +build_wheel = _BACKEND.build_wheel +build_sdist = _BACKEND.build_sdist +get_requires_for_build_editable = _BACKEND.get_requires_for_build_editable +prepare_metadata_for_build_editable = _BACKEND.prepare_metadata_for_build_editable +build_editable = _BACKEND.build_editable + + +# The legacy backend +__legacy__ = _BuildMetaLegacyBackend() diff --git a/lib/python3.12/site-packages/setuptools/cli-32.exe b/lib/python3.12/site-packages/setuptools/cli-32.exe new file mode 100644 index 0000000000000000000000000000000000000000..65c3cd99cc7433f271a5b9387abdd1ddb949d1a6 Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/cli-32.exe differ diff --git a/lib/python3.12/site-packages/setuptools/cli-64.exe b/lib/python3.12/site-packages/setuptools/cli-64.exe new file mode 100644 index 0000000000000000000000000000000000000000..3ea50eebfe3f0113b231a318cc1ad6e238afd60d Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/cli-64.exe differ diff --git a/lib/python3.12/site-packages/setuptools/cli-arm64.exe b/lib/python3.12/site-packages/setuptools/cli-arm64.exe new file mode 100644 index 0000000000000000000000000000000000000000..da96455a07a0bad4cde5dc5626544325f82c722b Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/cli-arm64.exe differ diff --git a/lib/python3.12/site-packages/setuptools/cli.exe b/lib/python3.12/site-packages/setuptools/cli.exe new file mode 100644 index 0000000000000000000000000000000000000000..65c3cd99cc7433f271a5b9387abdd1ddb949d1a6 Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/cli.exe differ diff --git a/lib/python3.12/site-packages/setuptools/depends.py b/lib/python3.12/site-packages/setuptools/depends.py new file mode 100644 index 0000000000000000000000000000000000000000..e5223b79561c36d9b6c45ead78288098e1cb0f1d --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/depends.py @@ -0,0 +1,185 @@ +from __future__ import annotations + +import contextlib +import dis +import marshal +import sys +from types import CodeType +from typing import Any, Literal, TypeVar + +from packaging.version import Version + +from . import _imp +from ._imp import PY_COMPILED, PY_FROZEN, PY_SOURCE, find_module + +_T = TypeVar("_T") + +__all__ = ['Require', 'find_module'] + + +class Require: + """A prerequisite to building or installing a distribution""" + + def __init__( + self, + name, + requested_version, + module, + homepage: str = '', + attribute=None, + format=None, + ) -> None: + if format is None and requested_version is not None: + format = Version + + if format is not None: + requested_version = format(requested_version) + if attribute is None: + attribute = '__version__' + + self.__dict__.update(locals()) + del self.self + + def full_name(self): + """Return full package/distribution name, w/version""" + if self.requested_version is not None: + return f'{self.name}-{self.requested_version}' + return self.name + + def version_ok(self, version): + """Is 'version' sufficiently up-to-date?""" + return ( + self.attribute is None + or self.format is None + or str(version) != "unknown" + and self.format(version) >= self.requested_version + ) + + def get_version( + self, paths=None, default: _T | Literal["unknown"] = "unknown" + ) -> _T | Literal["unknown"] | None | Any: + """Get version number of installed module, 'None', or 'default' + + Search 'paths' for module. If not found, return 'None'. If found, + return the extracted version attribute, or 'default' if no version + attribute was specified, or the value cannot be determined without + importing the module. The version is formatted according to the + requirement's version format (if any), unless it is 'None' or the + supplied 'default'. + """ + + if self.attribute is None: + try: + f, _p, _i = find_module(self.module, paths) + except ImportError: + return None + if f: + f.close() + return default + + v = get_module_constant(self.module, self.attribute, default, paths) + + if v is not None and v is not default and self.format is not None: + return self.format(v) + + return v + + def is_present(self, paths=None): + """Return true if dependency is present on 'paths'""" + return self.get_version(paths) is not None + + def is_current(self, paths=None): + """Return true if dependency is present and up-to-date on 'paths'""" + version = self.get_version(paths) + if version is None: + return False + return self.version_ok(str(version)) + + +def maybe_close(f): + @contextlib.contextmanager + def empty(): + yield + return + + if not f: + return empty() + + return contextlib.closing(f) + + +# Some objects are not available on some platforms. +# XXX it'd be better to test assertions about bytecode instead. +if not sys.platform.startswith('java') and sys.platform != 'cli': + + def get_module_constant( + module, symbol, default: _T | int = -1, paths=None + ) -> _T | int | None | Any: + """Find 'module' by searching 'paths', and extract 'symbol' + + Return 'None' if 'module' does not exist on 'paths', or it does not define + 'symbol'. If the module defines 'symbol' as a constant, return the + constant. Otherwise, return 'default'.""" + + try: + f, path, (_suffix, _mode, kind) = info = find_module(module, paths) + except ImportError: + # Module doesn't exist + return None + + with maybe_close(f): + if kind == PY_COMPILED: + f.read(8) # skip magic & date + code = marshal.load(f) + elif kind == PY_FROZEN: + code = _imp.get_frozen_object(module, paths) + elif kind == PY_SOURCE: + code = compile(f.read(), path, 'exec') + else: + # Not something we can parse; we'll have to import it. :( + imported = _imp.get_module(module, paths, info) + return getattr(imported, symbol, None) + + return extract_constant(code, symbol, default) + + def extract_constant( + code: CodeType, symbol: str, default: _T | int = -1 + ) -> _T | int | None | Any: + """Extract the constant value of 'symbol' from 'code' + + If the name 'symbol' is bound to a constant value by the Python code + object 'code', return that value. If 'symbol' is bound to an expression, + return 'default'. Otherwise, return 'None'. + + Return value is based on the first assignment to 'symbol'. 'symbol' must + be a global, or at least a non-"fast" local in the code block. That is, + only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol' + must be present in 'code.co_names'. + """ + if symbol not in code.co_names: + # name's not there, can't possibly be an assignment + return None + + name_idx = list(code.co_names).index(symbol) + + STORE_NAME = dis.opmap['STORE_NAME'] + STORE_GLOBAL = dis.opmap['STORE_GLOBAL'] + LOAD_CONST = dis.opmap['LOAD_CONST'] + + const = default + + for byte_code in dis.Bytecode(code): + op = byte_code.opcode + arg = byte_code.arg + + if op == LOAD_CONST: + assert arg is not None + const = code.co_consts[arg] + elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL): + return const + else: + const = default + + return None + + __all__ += ['get_module_constant', 'extract_constant'] diff --git a/lib/python3.12/site-packages/setuptools/discovery.py b/lib/python3.12/site-packages/setuptools/discovery.py new file mode 100644 index 0000000000000000000000000000000000000000..296d3193ab429f64bc5f3bfdcad7ee6e1050dc6d --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/discovery.py @@ -0,0 +1,614 @@ +"""Automatic discovery of Python modules and packages (for inclusion in the +distribution) and other config values. + +For the purposes of this module, the following nomenclature is used: + +- "src-layout": a directory representing a Python project that contains a "src" + folder. Everything under the "src" folder is meant to be included in the + distribution when packaging the project. Example:: + + . + ├── tox.ini + ├── pyproject.toml + └── src/ + └── mypkg/ + ├── __init__.py + ├── mymodule.py + └── my_data_file.txt + +- "flat-layout": a Python project that does not use "src-layout" but instead + have a directory under the project root for each package:: + + . + ├── tox.ini + ├── pyproject.toml + └── mypkg/ + ├── __init__.py + ├── mymodule.py + └── my_data_file.txt + +- "single-module": a project that contains a single Python script direct under + the project root (no directory used):: + + . + ├── tox.ini + ├── pyproject.toml + └── mymodule.py + +""" + +from __future__ import annotations + +import itertools +import os +from collections.abc import Iterable, Iterator, Mapping +from fnmatch import fnmatchcase +from glob import glob +from pathlib import Path +from typing import TYPE_CHECKING, ClassVar + +import _distutils_hack.override # noqa: F401 + +from ._path import StrPath + +from distutils import log +from distutils.util import convert_path + +if TYPE_CHECKING: + from setuptools import Distribution + +chain_iter = itertools.chain.from_iterable + + +def _valid_name(path: StrPath) -> bool: + # Ignore invalid names that cannot be imported directly + return os.path.basename(path).isidentifier() + + +class _Filter: + """ + Given a list of patterns, create a callable that will be true only if + the input matches at least one of the patterns. + """ + + def __init__(self, *patterns: str) -> None: + self._patterns = dict.fromkeys(patterns) + + def __call__(self, item: str) -> bool: + return any(fnmatchcase(item, pat) for pat in self._patterns) + + def __contains__(self, item: str) -> bool: + return item in self._patterns + + +class _Finder: + """Base class that exposes functionality for module/package finders""" + + ALWAYS_EXCLUDE: ClassVar[tuple[str, ...]] = () + DEFAULT_EXCLUDE: ClassVar[tuple[str, ...]] = () + + @classmethod + def find( + cls, + where: StrPath = '.', + exclude: Iterable[str] = (), + include: Iterable[str] = ('*',), + ) -> list[str]: + """Return a list of all Python items (packages or modules, depending on + the finder implementation) found within directory ``where``. + + ``where`` is the root directory which will be searched. + It should be supplied as a "cross-platform" (i.e. URL-style) path; + it will be converted to the appropriate local path syntax. + + ``exclude`` is a sequence of names to exclude; ``*`` can be used + as a wildcard in the names. + When finding packages, ``foo.*`` will exclude all subpackages of ``foo`` + (but not ``foo`` itself). + + ``include`` is a sequence of names to include. + If it's specified, only the named items will be included. + If it's not specified, all found items will be included. + ``include`` can contain shell style wildcard patterns just like + ``exclude``. + """ + + exclude = exclude or cls.DEFAULT_EXCLUDE + return list( + cls._find_iter( + convert_path(str(where)), + _Filter(*cls.ALWAYS_EXCLUDE, *exclude), + _Filter(*include), + ) + ) + + @classmethod + def _find_iter( + cls, where: StrPath, exclude: _Filter, include: _Filter + ) -> Iterator[str]: + raise NotImplementedError + + +class PackageFinder(_Finder): + """ + Generate a list of all Python packages found within a directory + """ + + ALWAYS_EXCLUDE = ("ez_setup", "*__pycache__") + + @classmethod + def _find_iter( + cls, where: StrPath, exclude: _Filter, include: _Filter + ) -> Iterator[str]: + """ + All the packages found in 'where' that pass the 'include' filter, but + not the 'exclude' filter. + """ + for root, dirs, files in os.walk(str(where), followlinks=True): + # Copy dirs to iterate over it, then empty dirs. + all_dirs = dirs[:] + dirs[:] = [] + + for dir in all_dirs: + full_path = os.path.join(root, dir) + rel_path = os.path.relpath(full_path, where) + package = rel_path.replace(os.path.sep, '.') + + # Skip directory trees that are not valid packages + if '.' in dir or not cls._looks_like_package(full_path, package): + continue + + # Should this package be included? + if include(package) and not exclude(package): + yield package + + # Early pruning if there is nothing else to be scanned + if f"{package}*" in exclude or f"{package}.*" in exclude: + continue + + # Keep searching subdirectories, as there may be more packages + # down there, even if the parent was excluded. + dirs.append(dir) + + @staticmethod + def _looks_like_package(path: StrPath, _package_name: str) -> bool: + """Does a directory look like a package?""" + return os.path.isfile(os.path.join(path, '__init__.py')) + + +class PEP420PackageFinder(PackageFinder): + @staticmethod + def _looks_like_package(_path: StrPath, _package_name: str) -> bool: + return True + + +class ModuleFinder(_Finder): + """Find isolated Python modules. + This function will **not** recurse subdirectories. + """ + + @classmethod + def _find_iter( + cls, where: StrPath, exclude: _Filter, include: _Filter + ) -> Iterator[str]: + for file in glob(os.path.join(where, "*.py")): + module, _ext = os.path.splitext(os.path.basename(file)) + + if not cls._looks_like_module(module): + continue + + if include(module) and not exclude(module): + yield module + + _looks_like_module = staticmethod(_valid_name) + + +# We have to be extra careful in the case of flat layout to not include files +# and directories not meant for distribution (e.g. tool-related) + + +class FlatLayoutPackageFinder(PEP420PackageFinder): + _EXCLUDE = ( + "ci", + "bin", + "debian", + "doc", + "docs", + "documentation", + "manpages", + "news", + "newsfragments", + "changelog", + "test", + "tests", + "unit_test", + "unit_tests", + "example", + "examples", + "scripts", + "tools", + "util", + "utils", + "python", + "build", + "dist", + "venv", + "env", + "requirements", + # ---- Task runners / Build tools ---- + "tasks", # invoke + "fabfile", # fabric + "site_scons", # SCons + # ---- Other tools ---- + "benchmark", + "benchmarks", + "exercise", + "exercises", + "htmlcov", # Coverage.py + # ---- Hidden directories/Private packages ---- + "[._]*", + ) + + DEFAULT_EXCLUDE = tuple(chain_iter((p, f"{p}.*") for p in _EXCLUDE)) + """Reserved package names""" + + @staticmethod + def _looks_like_package(_path: StrPath, package_name: str) -> bool: + names = package_name.split('.') + # Consider PEP 561 + root_pkg_is_valid = names[0].isidentifier() or names[0].endswith("-stubs") + return root_pkg_is_valid and all(name.isidentifier() for name in names[1:]) + + +class FlatLayoutModuleFinder(ModuleFinder): + DEFAULT_EXCLUDE = ( + "setup", + "conftest", + "test", + "tests", + "example", + "examples", + "build", + # ---- Task runners ---- + "toxfile", + "noxfile", + "pavement", + "dodo", + "tasks", + "fabfile", + # ---- Other tools ---- + "[Ss][Cc]onstruct", # SCons + "conanfile", # Connan: C/C++ build tool + "manage", # Django + "benchmark", + "benchmarks", + "exercise", + "exercises", + # ---- Hidden files/Private modules ---- + "[._]*", + ) + """Reserved top-level module names""" + + +def _find_packages_within(root_pkg: str, pkg_dir: StrPath) -> list[str]: + nested = PEP420PackageFinder.find(pkg_dir) + return [root_pkg] + [".".join((root_pkg, n)) for n in nested] + + +class ConfigDiscovery: + """Fill-in metadata and options that can be automatically derived + (from other metadata/options, the file system or conventions) + """ + + def __init__(self, distribution: Distribution) -> None: + self.dist = distribution + self._called = False + self._disabled = False + self._skip_ext_modules = False + + def _disable(self): + """Internal API to disable automatic discovery""" + self._disabled = True + + def _ignore_ext_modules(self): + """Internal API to disregard ext_modules. + + Normally auto-discovery would not be triggered if ``ext_modules`` are set + (this is done for backward compatibility with existing packages relying on + ``setup.py`` or ``setup.cfg``). However, ``setuptools`` can call this function + to ignore given ``ext_modules`` and proceed with the auto-discovery if + ``packages`` and ``py_modules`` are not given (e.g. when using pyproject.toml + metadata). + """ + self._skip_ext_modules = True + + @property + def _root_dir(self) -> StrPath: + # The best is to wait until `src_root` is set in dist, before using _root_dir. + return self.dist.src_root or os.curdir + + @property + def _package_dir(self) -> dict[str, str]: + if self.dist.package_dir is None: + return {} + return self.dist.package_dir + + def __call__( + self, force: bool = False, name: bool = True, ignore_ext_modules: bool = False + ) -> None: + """Automatically discover missing configuration fields + and modifies the given ``distribution`` object in-place. + + Note that by default this will only have an effect the first time the + ``ConfigDiscovery`` object is called. + + To repeatedly invoke automatic discovery (e.g. when the project + directory changes), please use ``force=True`` (or create a new + ``ConfigDiscovery`` instance). + """ + if force is False and (self._called or self._disabled): + # Avoid overhead of multiple calls + return + + self._analyse_package_layout(ignore_ext_modules) + if name: + self.analyse_name() # depends on ``packages`` and ``py_modules`` + + self._called = True + + def _explicitly_specified(self, ignore_ext_modules: bool) -> bool: + """``True`` if the user has specified some form of package/module listing""" + ignore_ext_modules = ignore_ext_modules or self._skip_ext_modules + ext_modules = not (self.dist.ext_modules is None or ignore_ext_modules) + return ( + self.dist.packages is not None + or self.dist.py_modules is not None + or ext_modules + or hasattr(self.dist, "configuration") + and self.dist.configuration + # ^ Some projects use numpy.distutils.misc_util.Configuration + ) + + def _analyse_package_layout(self, ignore_ext_modules: bool) -> bool: + if self._explicitly_specified(ignore_ext_modules): + # For backward compatibility, just try to find modules/packages + # when nothing is given + return True + + log.debug( + "No `packages` or `py_modules` configuration, performing " + "automatic discovery." + ) + + return ( + self._analyse_explicit_layout() + or self._analyse_src_layout() + # flat-layout is the trickiest for discovery so it should be last + or self._analyse_flat_layout() + ) + + def _analyse_explicit_layout(self) -> bool: + """The user can explicitly give a package layout via ``package_dir``""" + package_dir = self._package_dir.copy() # don't modify directly + package_dir.pop("", None) # This falls under the "src-layout" umbrella + root_dir = self._root_dir + + if not package_dir: + return False + + log.debug(f"`explicit-layout` detected -- analysing {package_dir}") + pkgs = chain_iter( + _find_packages_within(pkg, os.path.join(root_dir, parent_dir)) + for pkg, parent_dir in package_dir.items() + ) + self.dist.packages = list(pkgs) + log.debug(f"discovered packages -- {self.dist.packages}") + return True + + def _analyse_src_layout(self) -> bool: + """Try to find all packages or modules under the ``src`` directory + (or anything pointed by ``package_dir[""]``). + + The "src-layout" is relatively safe for automatic discovery. + We assume that everything within is meant to be included in the + distribution. + + If ``package_dir[""]`` is not given, but the ``src`` directory exists, + this function will set ``package_dir[""] = "src"``. + """ + package_dir = self._package_dir + src_dir = os.path.join(self._root_dir, package_dir.get("", "src")) + if not os.path.isdir(src_dir): + return False + + log.debug(f"`src-layout` detected -- analysing {src_dir}") + package_dir.setdefault("", os.path.basename(src_dir)) + self.dist.package_dir = package_dir # persist eventual modifications + self.dist.packages = PEP420PackageFinder.find(src_dir) + self.dist.py_modules = ModuleFinder.find(src_dir) + log.debug(f"discovered packages -- {self.dist.packages}") + log.debug(f"discovered py_modules -- {self.dist.py_modules}") + return True + + def _analyse_flat_layout(self) -> bool: + """Try to find all packages and modules under the project root. + + Since the ``flat-layout`` is more dangerous in terms of accidentally including + extra files/directories, this function is more conservative and will raise an + error if multiple packages or modules are found. + + This assumes that multi-package dists are uncommon and refuse to support that + use case in order to be able to prevent unintended errors. + """ + log.debug(f"`flat-layout` detected -- analysing {self._root_dir}") + return self._analyse_flat_packages() or self._analyse_flat_modules() + + def _analyse_flat_packages(self) -> bool: + self.dist.packages = FlatLayoutPackageFinder.find(self._root_dir) + top_level = remove_nested_packages(remove_stubs(self.dist.packages)) + log.debug(f"discovered packages -- {self.dist.packages}") + self._ensure_no_accidental_inclusion(top_level, "packages") + return bool(top_level) + + def _analyse_flat_modules(self) -> bool: + self.dist.py_modules = FlatLayoutModuleFinder.find(self._root_dir) + log.debug(f"discovered py_modules -- {self.dist.py_modules}") + self._ensure_no_accidental_inclusion(self.dist.py_modules, "modules") + return bool(self.dist.py_modules) + + def _ensure_no_accidental_inclusion(self, detected: list[str], kind: str): + if len(detected) > 1: + from inspect import cleandoc + + from setuptools.errors import PackageDiscoveryError + + msg = f"""Multiple top-level {kind} discovered in a flat-layout: {detected}. + + To avoid accidental inclusion of unwanted files or directories, + setuptools will not proceed with this build. + + If you are trying to create a single distribution with multiple {kind} + on purpose, you should not rely on automatic discovery. + Instead, consider the following options: + + 1. set up custom discovery (`find` directive with `include` or `exclude`) + 2. use a `src-layout` + 3. explicitly set `py_modules` or `packages` with a list of names + + To find more information, look for "package discovery" on setuptools docs. + """ + raise PackageDiscoveryError(cleandoc(msg)) + + def analyse_name(self) -> None: + """The packages/modules are the essential contribution of the author. + Therefore the name of the distribution can be derived from them. + """ + if self.dist.metadata.name or self.dist.name: + # get_name() is not reliable (can return "UNKNOWN") + return + + log.debug("No `name` configuration, performing automatic discovery") + + name = ( + self._find_name_single_package_or_module() + or self._find_name_from_packages() + ) + if name: + self.dist.metadata.name = name + + def _find_name_single_package_or_module(self) -> str | None: + """Exactly one module or package""" + for field in ('packages', 'py_modules'): + items = getattr(self.dist, field, None) or [] + if items and len(items) == 1: + log.debug(f"Single module/package detected, name: {items[0]}") + return items[0] + + return None + + def _find_name_from_packages(self) -> str | None: + """Try to find the root package that is not a PEP 420 namespace""" + if not self.dist.packages: + return None + + packages = remove_stubs(sorted(self.dist.packages, key=len)) + package_dir = self.dist.package_dir or {} + + parent_pkg = find_parent_package(packages, package_dir, self._root_dir) + if parent_pkg: + log.debug(f"Common parent package detected, name: {parent_pkg}") + return parent_pkg + + log.warn("No parent package detected, impossible to derive `name`") + return None + + +def remove_nested_packages(packages: list[str]) -> list[str]: + """Remove nested packages from a list of packages. + + >>> remove_nested_packages(["a", "a.b1", "a.b2", "a.b1.c1"]) + ['a'] + >>> remove_nested_packages(["a", "b", "c.d", "c.d.e.f", "g.h", "a.a1"]) + ['a', 'b', 'c.d', 'g.h'] + """ + pkgs = sorted(packages, key=len) + top_level = pkgs[:] + size = len(pkgs) + for i, name in enumerate(reversed(pkgs)): + if any(name.startswith(f"{other}.") for other in top_level): + top_level.pop(size - i - 1) + + return top_level + + +def remove_stubs(packages: list[str]) -> list[str]: + """Remove type stubs (:pep:`561`) from a list of packages. + + >>> remove_stubs(["a", "a.b", "a-stubs", "a-stubs.b.c", "b", "c-stubs"]) + ['a', 'a.b', 'b'] + """ + return [pkg for pkg in packages if not pkg.split(".")[0].endswith("-stubs")] + + +def find_parent_package( + packages: list[str], package_dir: Mapping[str, str], root_dir: StrPath +) -> str | None: + """Find the parent package that is not a namespace.""" + packages = sorted(packages, key=len) + common_ancestors = [] + for i, name in enumerate(packages): + if not all(n.startswith(f"{name}.") for n in packages[i + 1 :]): + # Since packages are sorted by length, this condition is able + # to find a list of all common ancestors. + # When there is divergence (e.g. multiple root packages) + # the list will be empty + break + common_ancestors.append(name) + + for name in common_ancestors: + pkg_path = find_package_path(name, package_dir, root_dir) + init = os.path.join(pkg_path, "__init__.py") + if os.path.isfile(init): + return name + + return None + + +def find_package_path( + name: str, package_dir: Mapping[str, str], root_dir: StrPath +) -> str: + """Given a package name, return the path where it should be found on + disk, considering the ``package_dir`` option. + + >>> path = find_package_path("my.pkg", {"": "root/is/nested"}, ".") + >>> path.replace(os.sep, "/") + './root/is/nested/my/pkg' + + >>> path = find_package_path("my.pkg", {"my": "root/is/nested"}, ".") + >>> path.replace(os.sep, "/") + './root/is/nested/pkg' + + >>> path = find_package_path("my.pkg", {"my.pkg": "root/is/nested"}, ".") + >>> path.replace(os.sep, "/") + './root/is/nested' + + >>> path = find_package_path("other.pkg", {"my.pkg": "root/is/nested"}, ".") + >>> path.replace(os.sep, "/") + './other/pkg' + """ + parts = name.split(".") + for i in range(len(parts), 0, -1): + # Look backwards, the most specific package_dir first + partial_name = ".".join(parts[:i]) + if partial_name in package_dir: + parent = package_dir[partial_name] + return os.path.join(root_dir, parent, *parts[i:]) + + parent = package_dir.get("") or "" + return os.path.join(root_dir, *parent.split("/"), *parts) + + +def construct_package_dir(packages: list[str], package_path: StrPath) -> dict[str, str]: + parent_pkgs = remove_nested_packages(packages) + prefix = Path(package_path).parts + return {pkg: "/".join([*prefix, *pkg.split(".")]) for pkg in parent_pkgs} diff --git a/lib/python3.12/site-packages/setuptools/dist.py b/lib/python3.12/site-packages/setuptools/dist.py new file mode 100644 index 0000000000000000000000000000000000000000..a3d1e5f91e1086cc8c975600fa0c8cbf1f7187fc --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/dist.py @@ -0,0 +1,1124 @@ +from __future__ import annotations + +import functools +import io +import itertools +import numbers +import os +import re +import sys +from collections.abc import Iterable, Iterator, MutableMapping, Sequence +from glob import glob +from pathlib import Path +from typing import TYPE_CHECKING, Any, Union + +from more_itertools import partition, unique_everseen +from packaging.markers import InvalidMarker, Marker +from packaging.specifiers import InvalidSpecifier, SpecifierSet +from packaging.version import Version + +from . import ( + _entry_points, + _reqs, + _static, + command as _, # noqa: F401 # imported for side-effects +) +from ._importlib import metadata +from ._normalization import _canonicalize_license_expression +from ._path import StrPath +from ._reqs import _StrOrIter +from .config import pyprojecttoml, setupcfg +from .discovery import ConfigDiscovery +from .errors import InvalidConfigError +from .monkey import get_unpatched +from .warnings import InformationOnly, SetuptoolsDeprecationWarning + +import distutils.cmd +import distutils.command +import distutils.core +import distutils.dist +import distutils.log +from distutils.debug import DEBUG +from distutils.errors import DistutilsOptionError, DistutilsSetupError +from distutils.fancy_getopt import translate_longopt +from distutils.util import strtobool + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + + +__all__ = ['Distribution'] + +_sequence = tuple, list +""" +:meta private: + +Supported iterable types that are known to be: +- ordered (which `set` isn't) +- not match a str (which `Sequence[str]` does) +- not imply a nested type (like `dict`) +for use with `isinstance`. +""" +_Sequence: TypeAlias = Union[tuple[str, ...], list[str]] +# This is how stringifying _Sequence would look in Python 3.10 +_sequence_type_repr = "tuple[str, ...] | list[str]" +_OrderedStrSequence: TypeAlias = Union[str, dict[str, Any], Sequence[str]] +""" +:meta private: +Avoid single-use iterable. Disallow sets. +A poor approximation of an OrderedSequence (dict doesn't match a Sequence). +""" + + +def __getattr__(name: str) -> Any: # pragma: no cover + if name == "sequence": + SetuptoolsDeprecationWarning.emit( + "`setuptools.dist.sequence` is an internal implementation detail.", + "Please define your own `sequence = tuple, list` instead.", + due_date=(2025, 8, 28), # Originally added on 2024-08-27 + ) + return _sequence + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def check_importable(dist, attr, value): + try: + ep = metadata.EntryPoint(value=value, name=None, group=None) + assert not ep.extras + except (TypeError, ValueError, AttributeError, AssertionError) as e: + raise DistutilsSetupError( + f"{attr!r} must be importable 'module:attrs' string (got {value!r})" + ) from e + + +def assert_string_list(dist, attr: str, value: _Sequence) -> None: + """Verify that value is a string list""" + try: + # verify that value is a list or tuple to exclude unordered + # or single-use iterables + assert isinstance(value, _sequence) + # verify that elements of value are strings + assert ''.join(value) != value + except (TypeError, ValueError, AttributeError, AssertionError) as e: + raise DistutilsSetupError( + f"{attr!r} must be of type <{_sequence_type_repr}> (got {value!r})" + ) from e + + +def check_nsp(dist, attr, value): + """Verify that namespace packages are valid""" + ns_packages = value + assert_string_list(dist, attr, ns_packages) + for nsp in ns_packages: + if not dist.has_contents_for(nsp): + raise DistutilsSetupError( + f"Distribution contains no modules or packages for namespace package {nsp!r}" + ) + parent, _sep, _child = nsp.rpartition('.') + if parent and parent not in ns_packages: + distutils.log.warn( + "WARNING: %r is declared as a package namespace, but %r" + " is not: please correct this in setup.py", + nsp, + parent, + ) + SetuptoolsDeprecationWarning.emit( + "The namespace_packages parameter is deprecated.", + "Please replace its usage with implicit namespaces (PEP 420).", + see_docs="references/keywords.html#keyword-namespace-packages", + # TODO: define due_date, it may break old packages that are no longer + # maintained (e.g. sphinxcontrib extensions) when installed from source. + # Warning officially introduced in May 2022, however the deprecation + # was mentioned much earlier in the docs (May 2020, see #2149). + ) + + +def check_extras(dist, attr, value): + """Verify that extras_require mapping is valid""" + try: + list(itertools.starmap(_check_extra, value.items())) + except (TypeError, ValueError, AttributeError) as e: + raise DistutilsSetupError( + "'extras_require' must be a dictionary whose values are " + "strings or lists of strings containing valid project/version " + "requirement specifiers." + ) from e + + +def _check_extra(extra, reqs): + _name, _sep, marker = extra.partition(':') + try: + _check_marker(marker) + except InvalidMarker: + msg = f"Invalid environment marker: {marker} ({extra!r})" + raise DistutilsSetupError(msg) from None + list(_reqs.parse(reqs)) + + +def _check_marker(marker): + if not marker: + return + m = Marker(marker) + m.evaluate() + + +def assert_bool(dist, attr, value): + """Verify that value is True, False, 0, or 1""" + if bool(value) != value: + raise DistutilsSetupError(f"{attr!r} must be a boolean value (got {value!r})") + + +def invalid_unless_false(dist, attr, value): + if not value: + DistDeprecationWarning.emit(f"{attr} is ignored.") + # TODO: should there be a `due_date` here? + return + raise DistutilsSetupError(f"{attr} is invalid.") + + +def check_requirements(dist, attr: str, value: _OrderedStrSequence) -> None: + """Verify that install_requires is a valid requirements list""" + try: + list(_reqs.parse(value)) + if isinstance(value, set): + raise TypeError("Unordered types are not allowed") + except (TypeError, ValueError) as error: + msg = ( + f"{attr!r} must be a string or iterable of strings " + f"containing valid project/version requirement specifiers; {error}" + ) + raise DistutilsSetupError(msg) from error + + +def check_specifier(dist, attr, value): + """Verify that value is a valid version specifier""" + try: + SpecifierSet(value) + except (InvalidSpecifier, AttributeError) as error: + msg = f"{attr!r} must be a string containing valid version specifiers; {error}" + raise DistutilsSetupError(msg) from error + + +def check_entry_points(dist, attr, value): + """Verify that entry_points map is parseable""" + try: + _entry_points.load(value) + except Exception as e: + raise DistutilsSetupError(e) from e + + +def check_package_data(dist, attr, value): + """Verify that value is a dictionary of package names to glob lists""" + if not isinstance(value, dict): + raise DistutilsSetupError( + f"{attr!r} must be a dictionary mapping package names to lists of " + "string wildcard patterns" + ) + for k, v in value.items(): + if not isinstance(k, str): + raise DistutilsSetupError( + f"keys of {attr!r} dict must be strings (got {k!r})" + ) + assert_string_list(dist, f'values of {attr!r} dict', v) + + +def check_packages(dist, attr, value): + for pkgname in value: + if not re.match(r'\w+(\.\w+)*', pkgname): + distutils.log.warn( + "WARNING: %r not a valid package name; please use only " + ".-separated package names in setup.py", + pkgname, + ) + + +if TYPE_CHECKING: + # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962 + from distutils.core import Distribution as _Distribution +else: + _Distribution = get_unpatched(distutils.core.Distribution) + + +class Distribution(_Distribution): + """Distribution with support for tests and package data + + This is an enhanced version of 'distutils.dist.Distribution' that + effectively adds the following new optional keyword arguments to 'setup()': + + 'install_requires' -- a string or sequence of strings specifying project + versions that the distribution requires when installed, in the format + used by 'pkg_resources.require()'. They will be installed + automatically when the package is installed. If you wish to use + packages that are not available in PyPI, or want to give your users an + alternate download location, you can add a 'find_links' option to the + '[easy_install]' section of your project's 'setup.cfg' file, and then + setuptools will scan the listed web pages for links that satisfy the + requirements. + + 'extras_require' -- a dictionary mapping names of optional "extras" to the + additional requirement(s) that using those extras incurs. For example, + this:: + + extras_require = dict(reST = ["docutils>=0.3", "reSTedit"]) + + indicates that the distribution can optionally provide an extra + capability called "reST", but it can only be used if docutils and + reSTedit are installed. If the user installs your package using + EasyInstall and requests one of your extras, the corresponding + additional requirements will be installed if needed. + + 'package_data' -- a dictionary mapping package names to lists of filenames + or globs to use to find data files contained in the named packages. + If the dictionary has filenames or globs listed under '""' (the empty + string), those names will be searched for in every package, in addition + to any names for the specific package. Data files found using these + names/globs will be installed along with the package, in the same + location as the package. Note that globs are allowed to reference + the contents of non-package subdirectories, as long as you use '/' as + a path separator. (Globs are automatically converted to + platform-specific paths at runtime.) + + In addition to these new keywords, this class also has several new methods + for manipulating the distribution's contents. For example, the 'include()' + and 'exclude()' methods can be thought of as in-place add and subtract + commands that add or remove packages, modules, extensions, and so on from + the distribution. + """ + + _DISTUTILS_UNSUPPORTED_METADATA = { + 'long_description_content_type': lambda: None, + 'project_urls': dict, + 'provides_extras': dict, # behaves like an ordered set + 'license_expression': lambda: None, + 'license_file': lambda: None, + 'license_files': lambda: None, + 'install_requires': list, + 'extras_require': dict, + } + + # Used by build_py, editable_wheel and install_lib commands for legacy namespaces + namespace_packages: list[str] #: :meta private: DEPRECATED + + # Any: Dynamic assignment results in Incompatible types in assignment + def __init__(self, attrs: MutableMapping[str, Any] | None = None) -> None: + have_package_data = hasattr(self, "package_data") + if not have_package_data: + self.package_data: dict[str, list[str]] = {} + attrs = attrs or {} + self.dist_files: list[tuple[str, str, str]] = [] + self.include_package_data: bool | None = None + self.exclude_package_data: dict[str, list[str]] | None = None + # Filter-out setuptools' specific options. + self.src_root: str | None = attrs.pop("src_root", None) + self.dependency_links: list[str] = attrs.pop('dependency_links', []) + self.setup_requires: list[str] = attrs.pop('setup_requires', []) + for ep in metadata.entry_points(group='distutils.setup_keywords'): + vars(self).setdefault(ep.name, None) + + metadata_only = set(self._DISTUTILS_UNSUPPORTED_METADATA) + metadata_only -= {"install_requires", "extras_require"} + dist_attrs = {k: v for k, v in attrs.items() if k not in metadata_only} + _Distribution.__init__(self, dist_attrs) + + # Private API (setuptools-use only, not restricted to Distribution) + # Stores files that are referenced by the configuration and need to be in the + # sdist (e.g. `version = file: VERSION.txt`) + self._referenced_files = set[str]() + + self.set_defaults = ConfigDiscovery(self) + + self._set_metadata_defaults(attrs) + + self.metadata.version = self._normalize_version(self.metadata.version) + self._finalize_requires() + + def _validate_metadata(self): + required = {"name"} + provided = { + key + for key in vars(self.metadata) + if getattr(self.metadata, key, None) is not None + } + missing = required - provided + + if missing: + msg = f"Required package metadata is missing: {missing}" + raise DistutilsSetupError(msg) + + def _set_metadata_defaults(self, attrs): + """ + Fill-in missing metadata fields not supported by distutils. + Some fields may have been set by other tools (e.g. pbr). + Those fields (vars(self.metadata)) take precedence to + supplied attrs. + """ + for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items(): + vars(self.metadata).setdefault(option, attrs.get(option, default())) + + @staticmethod + def _normalize_version(version): + from . import sic + + if isinstance(version, numbers.Number): + # Some people apparently take "version number" too literally :) + version = str(version) + elif isinstance(version, sic) or version is None: + return version + + normalized = str(Version(version)) + if version != normalized: + InformationOnly.emit(f"Normalizing '{version}' to '{normalized}'") + return normalized + return version + + def _finalize_requires(self): + """ + Set `metadata.python_requires` and fix environment markers + in `install_requires` and `extras_require`. + """ + if getattr(self, 'python_requires', None): + self.metadata.python_requires = self.python_requires + + self._normalize_requires() + self.metadata.install_requires = self.install_requires + self.metadata.extras_require = self.extras_require + + if self.extras_require: + for extra in self.extras_require.keys(): + # Setuptools allows a weird ": syntax for extras + extra = extra.split(':')[0] + if extra: + self.metadata.provides_extras.setdefault(extra) + + def _normalize_requires(self): + """Make sure requirement-related attributes exist and are normalized""" + install_requires = getattr(self, "install_requires", None) or [] + extras_require = getattr(self, "extras_require", None) or {} + + # Preserve the "static"-ness of values parsed from config files + list_ = _static.List if _static.is_static(install_requires) else list + self.install_requires = list_(map(str, _reqs.parse(install_requires))) + + dict_ = _static.Dict if _static.is_static(extras_require) else dict + self.extras_require = dict_( + (k, list(map(str, _reqs.parse(v or [])))) for k, v in extras_require.items() + ) + + def _finalize_license_expression(self) -> None: + """ + Normalize license and license_expression. + >>> dist = Distribution({"license_expression": _static.Str("mit aNd gpl-3.0-OR-later")}) + >>> _static.is_static(dist.metadata.license_expression) + True + >>> dist._finalize_license_expression() + >>> _static.is_static(dist.metadata.license_expression) # preserve "static-ness" + True + >>> print(dist.metadata.license_expression) + MIT AND GPL-3.0-or-later + """ + classifiers = self.metadata.get_classifiers() + license_classifiers = [cl for cl in classifiers if cl.startswith("License :: ")] + + license_expr = self.metadata.license_expression + if license_expr: + str_ = _static.Str if _static.is_static(license_expr) else str + normalized = str_(_canonicalize_license_expression(license_expr)) + if license_expr != normalized: + InformationOnly.emit(f"Normalizing '{license_expr}' to '{normalized}'") + self.metadata.license_expression = normalized + if license_classifiers: + raise InvalidConfigError( + "License classifiers have been superseded by license expressions " + "(see https://peps.python.org/pep-0639/). Please remove:\n\n" + + "\n".join(license_classifiers), + ) + elif license_classifiers: + pypa_guides = "guides/writing-pyproject-toml/#license" + SetuptoolsDeprecationWarning.emit( + "License classifiers are deprecated.", + "Please consider removing the following classifiers in favor of a " + "SPDX license expression:\n\n" + "\n".join(license_classifiers), + see_url=f"https://packaging.python.org/en/latest/{pypa_guides}", + # Warning introduced on 2025-02-17 + # TODO: Should we add a due date? It may affect old/unmaintained + # packages in the ecosystem and cause problems... + ) + + def _finalize_license_files(self) -> None: + """Compute names of all license files which should be included.""" + license_files: list[str] | None = self.metadata.license_files + patterns = license_files or [] + + license_file: str | None = self.metadata.license_file + if license_file and license_file not in patterns: + patterns.append(license_file) + + if license_files is None and license_file is None: + # Default patterns match the ones wheel uses + # See https://wheel.readthedocs.io/en/stable/user_guide.html + # -> 'Including license files in the generated wheel file' + patterns = ['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*'] + files = self._expand_patterns(patterns, enforce_match=False) + else: # Patterns explicitly given by the user + files = self._expand_patterns(patterns, enforce_match=True) + + self.metadata.license_files = list(unique_everseen(files)) + + @classmethod + def _expand_patterns( + cls, patterns: list[str], enforce_match: bool = True + ) -> Iterator[str]: + """ + >>> getfixture('sample_project_cwd') + >>> list(Distribution._expand_patterns(['LICENSE.txt'])) + ['LICENSE.txt'] + >>> list(Distribution._expand_patterns(['pyproject.toml', 'LIC*'])) + ['pyproject.toml', 'LICENSE.txt'] + >>> list(Distribution._expand_patterns(['src/**/*.dat'])) + ['src/sample/package_data.dat'] + """ + return ( + path.replace(os.sep, "/") + for pattern in patterns + for path in sorted(cls._find_pattern(pattern, enforce_match)) + if not path.endswith('~') and os.path.isfile(path) + ) + + @staticmethod + def _find_pattern(pattern: str, enforce_match: bool = True) -> list[str]: + r""" + >>> getfixture('sample_project_cwd') + >>> Distribution._find_pattern("LICENSE.txt") + ['LICENSE.txt'] + >>> Distribution._find_pattern("/LICENSE.MIT") + Traceback (most recent call last): + ... + setuptools.errors.InvalidConfigError: Pattern '/LICENSE.MIT' should be relative... + >>> Distribution._find_pattern("../LICENSE.MIT") + Traceback (most recent call last): + ... + setuptools.warnings.SetuptoolsDeprecationWarning: ...Pattern '../LICENSE.MIT' cannot contain '..'... + >>> Distribution._find_pattern("LICEN{CSE*") + Traceback (most recent call last): + ... + setuptools.warnings.SetuptoolsDeprecationWarning: ...Pattern 'LICEN{CSE*' contains invalid characters... + """ + pypa_guides = "specifications/glob-patterns/" + if ".." in pattern: + SetuptoolsDeprecationWarning.emit( + f"Pattern {pattern!r} cannot contain '..'", + """ + Please ensure the files specified are contained by the root + of the Python package (normally marked by `pyproject.toml`). + """, + see_url=f"https://packaging.python.org/en/latest/{pypa_guides}", + due_date=(2027, 2, 18), # Introduced in 2025-03-20 + # Replace with InvalidConfigError after deprecation + ) + if pattern.startswith((os.sep, "/")) or ":\\" in pattern: + raise InvalidConfigError( + f"Pattern {pattern!r} should be relative and must not start with '/'" + ) + if re.match(r'^[\w\-\.\/\*\?\[\]]+$', pattern) is None: + SetuptoolsDeprecationWarning.emit( + "Please provide a valid glob pattern.", + "Pattern {pattern!r} contains invalid characters.", + pattern=pattern, + see_url=f"https://packaging.python.org/en/latest/{pypa_guides}", + due_date=(2027, 2, 18), # Introduced in 2025-02-20 + ) + + found = glob(pattern, recursive=True) + + if enforce_match and not found: + SetuptoolsDeprecationWarning.emit( + "Cannot find any files for the given pattern.", + "Pattern {pattern!r} did not match any files.", + pattern=pattern, + due_date=(2027, 2, 18), # Introduced in 2025-02-20 + # PEP 639 requires us to error, but as a transition period + # we will only issue a warning to give people time to prepare. + # After the transition, this should raise an InvalidConfigError. + ) + return found + + # FIXME: 'Distribution._parse_config_files' is too complex (14) + def _parse_config_files(self, filenames=None): # noqa: C901 + """ + Adapted from distutils.dist.Distribution.parse_config_files, + this method provides the same functionality in subtly-improved + ways. + """ + from configparser import ConfigParser + + # Ignore install directory options if we have a venv + ignore_options = ( + [] + if sys.prefix == sys.base_prefix + else [ + 'install-base', + 'install-platbase', + 'install-lib', + 'install-platlib', + 'install-purelib', + 'install-headers', + 'install-scripts', + 'install-data', + 'prefix', + 'exec-prefix', + 'home', + 'user', + 'root', + ] + ) + + ignore_options = frozenset(ignore_options) + + if filenames is None: + filenames = self.find_config_files() + + if DEBUG: + self.announce("Distribution.parse_config_files():") + + parser = ConfigParser() + parser.optionxform = str + for filename in filenames: + with open(filename, encoding='utf-8') as reader: + if DEBUG: + self.announce(" reading {filename}".format(**locals())) + parser.read_file(reader) + for section in parser.sections(): + options = parser.options(section) + opt_dict = self.get_option_dict(section) + + for opt in options: + if opt == '__name__' or opt in ignore_options: + continue + + val = parser.get(section, opt) + opt = self._enforce_underscore(opt, section) + opt = self._enforce_option_lowercase(opt, section) + opt_dict[opt] = (filename, val) + + # Make the ConfigParser forget everything (so we retain + # the original filenames that options come from) + parser.__init__() + + if 'global' not in self.command_options: + return + + # If there was a "global" section in the config file, use it + # to set Distribution options. + + for opt, (src, val) in self.command_options['global'].items(): + alias = self.negative_opt.get(opt) + if alias: + val = not strtobool(val) + elif opt in ('verbose', 'dry_run'): # ugh! + val = strtobool(val) + + try: + setattr(self, alias or opt, val) + except ValueError as e: + raise DistutilsOptionError(e) from e + + def _enforce_underscore(self, opt: str, section: str) -> str: + if "-" not in opt or self._skip_setupcfg_normalization(section): + return opt + + underscore_opt = opt.replace('-', '_') + affected = f"(Affected: {self.metadata.name})." if self.metadata.name else "" + SetuptoolsDeprecationWarning.emit( + f"Invalid dash-separated key {opt!r} in {section!r} (setup.cfg), " + f"please use the underscore name {underscore_opt!r} instead.", + f""" + Usage of dash-separated {opt!r} will not be supported in future + versions. Please use the underscore name {underscore_opt!r} instead. + {affected} + + Available configuration options are listed in: + https://setuptools.pypa.io/en/latest/userguide/declarative_config.html + """, + see_url="https://github.com/pypa/setuptools/discussions/5011", + due_date=(2026, 3, 3), + # Warning initially introduced in 3 Mar 2021 + ) + return underscore_opt + + def _enforce_option_lowercase(self, opt: str, section: str) -> str: + if opt.islower() or self._skip_setupcfg_normalization(section): + return opt + + lowercase_opt = opt.lower() + affected = f"(Affected: {self.metadata.name})." if self.metadata.name else "" + SetuptoolsDeprecationWarning.emit( + f"Invalid uppercase key {opt!r} in {section!r} (setup.cfg), " + f"please use lowercase {lowercase_opt!r} instead.", + f""" + Usage of uppercase key {opt!r} in {section!r} will not be supported in + future versions. Please use lowercase {lowercase_opt!r} instead. + {affected} + + Available configuration options are listed in: + https://setuptools.pypa.io/en/latest/userguide/declarative_config.html + """, + see_url="https://github.com/pypa/setuptools/discussions/5011", + due_date=(2026, 3, 3), + # Warning initially introduced in 6 Mar 2021 + ) + return lowercase_opt + + def _skip_setupcfg_normalization(self, section: str) -> bool: + skip = ( + 'options.extras_require', + 'options.data_files', + 'options.entry_points', + 'options.package_data', + 'options.exclude_package_data', + ) + return section in skip or not self._is_setuptools_section(section) + + def _is_setuptools_section(self, section: str) -> bool: + return ( + section == "metadata" + or section.startswith("options") + or section in _setuptools_commands() + ) + + # FIXME: 'Distribution._set_command_options' is too complex (14) + def _set_command_options(self, command_obj, option_dict=None): # noqa: C901 + """ + Set the options for 'command_obj' from 'option_dict'. Basically + this means copying elements of a dictionary ('option_dict') to + attributes of an instance ('command'). + + 'command_obj' must be a Command instance. If 'option_dict' is not + supplied, uses the standard option dictionary for this command + (from 'self.command_options'). + + (Adopted from distutils.dist.Distribution._set_command_options) + """ + command_name = command_obj.get_command_name() + if option_dict is None: + option_dict = self.get_option_dict(command_name) + + if DEBUG: + self.announce(f" setting options for '{command_name}' command:") + for option, (source, value) in option_dict.items(): + if DEBUG: + self.announce(f" {option} = {value} (from {source})") + try: + bool_opts = [translate_longopt(o) for o in command_obj.boolean_options] + except AttributeError: + bool_opts = [] + try: + neg_opt = command_obj.negative_opt + except AttributeError: + neg_opt = {} + + try: + is_string = isinstance(value, str) + if option in neg_opt and is_string: + setattr(command_obj, neg_opt[option], not strtobool(value)) + elif option in bool_opts and is_string: + setattr(command_obj, option, strtobool(value)) + elif hasattr(command_obj, option): + setattr(command_obj, option, value) + else: + raise DistutilsOptionError( + f"error in {source}: command '{command_name}' has no such option '{option}'" + ) + except ValueError as e: + raise DistutilsOptionError(e) from e + + def _get_project_config_files(self, filenames: Iterable[StrPath] | None): + """Add default file and split between INI and TOML""" + tomlfiles = [] + standard_project_metadata = Path(self.src_root or os.curdir, "pyproject.toml") + if filenames is not None: + parts = partition(lambda f: Path(f).suffix == ".toml", filenames) + filenames = list(parts[0]) # 1st element => predicate is False + tomlfiles = list(parts[1]) # 2nd element => predicate is True + elif standard_project_metadata.exists(): + tomlfiles = [standard_project_metadata] + return filenames, tomlfiles + + def parse_config_files( + self, + filenames: Iterable[StrPath] | None = None, + ignore_option_errors: bool = False, + ) -> None: + """Parses configuration files from various levels + and loads configuration. + """ + inifiles, tomlfiles = self._get_project_config_files(filenames) + + self._parse_config_files(filenames=inifiles) + + setupcfg.parse_configuration( + self, self.command_options, ignore_option_errors=ignore_option_errors + ) + for filename in tomlfiles: + pyprojecttoml.apply_configuration(self, filename, ignore_option_errors) + + self._finalize_requires() + self._finalize_license_expression() + self._finalize_license_files() + + def fetch_build_eggs(self, requires: _StrOrIter) -> list[metadata.Distribution]: + """Resolve pre-setup requirements""" + from .installer import _fetch_build_eggs + + return _fetch_build_eggs(self, requires) + + def finalize_options(self) -> None: + """ + Allow plugins to apply arbitrary operations to the + distribution. Each hook may optionally define a 'order' + to influence the order of execution. Smaller numbers + go first and the default is 0. + """ + group = 'setuptools.finalize_distribution_options' + + def by_order(hook): + return getattr(hook, 'order', 0) + + defined = metadata.entry_points(group=group) + filtered = itertools.filterfalse(self._removed, defined) + loaded = map(lambda e: e.load(), filtered) + for ep in sorted(loaded, key=by_order): + ep(self) + + @staticmethod + def _removed(ep): + """ + When removing an entry point, if metadata is loaded + from an older version of Setuptools, that removed + entry point will attempt to be loaded and will fail. + See #2765 for more details. + """ + removed = { + # removed 2021-09-05 + '2to3_doctests', + } + return ep.name in removed + + def _finalize_setup_keywords(self): + for ep in metadata.entry_points(group='distutils.setup_keywords'): + value = getattr(self, ep.name, None) + if value is not None: + ep.load()(self, ep.name, value) + + def get_egg_cache_dir(self) -> str: + from . import windows_support + + egg_cache_dir = os.path.join(os.curdir, '.eggs') + if not os.path.exists(egg_cache_dir): + os.mkdir(egg_cache_dir) + windows_support.hide_file(egg_cache_dir) + readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') + with open(readme_txt_filename, 'w', encoding="utf-8") as f: + f.write( + 'This directory contains eggs that were downloaded ' + 'by setuptools to build, test, and run plug-ins.\n\n' + ) + f.write( + 'This directory caches those eggs to prevent ' + 'repeated downloads.\n\n' + ) + f.write('However, it is safe to delete this directory.\n\n') + + return egg_cache_dir + + def fetch_build_egg(self, req): + """Fetch an egg needed for building""" + from .installer import fetch_build_egg + + return fetch_build_egg(self, req) + + def get_command_class(self, command: str) -> type[distutils.cmd.Command]: # type: ignore[override] # Not doing complex overrides yet + """Pluggable version of get_command_class()""" + if command in self.cmdclass: + return self.cmdclass[command] + + # Special case bdist_wheel so it's never loaded from "wheel" + if command == 'bdist_wheel': + from .command.bdist_wheel import bdist_wheel + + return bdist_wheel + + eps = metadata.entry_points(group='distutils.commands', name=command) + for ep in eps: + self.cmdclass[command] = cmdclass = ep.load() + return cmdclass + else: + return _Distribution.get_command_class(self, command) + + def print_commands(self): + for ep in metadata.entry_points(group='distutils.commands'): + if ep.name not in self.cmdclass: + cmdclass = ep.load() + self.cmdclass[ep.name] = cmdclass + return _Distribution.print_commands(self) + + def get_command_list(self): + for ep in metadata.entry_points(group='distutils.commands'): + if ep.name not in self.cmdclass: + cmdclass = ep.load() + self.cmdclass[ep.name] = cmdclass + return _Distribution.get_command_list(self) + + def include(self, **attrs) -> None: + """Add items to distribution that are named in keyword arguments + + For example, 'dist.include(py_modules=["x"])' would add 'x' to + the distribution's 'py_modules' attribute, if it was not already + there. + + Currently, this method only supports inclusion for attributes that are + lists or tuples. If you need to add support for adding to other + attributes in this or a subclass, you can add an '_include_X' method, + where 'X' is the name of the attribute. The method will be called with + the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})' + will try to call 'dist._include_foo({"bar":"baz"})', which can then + handle whatever special inclusion logic is needed. + """ + for k, v in attrs.items(): + include = getattr(self, '_include_' + k, None) + if include: + include(v) + else: + self._include_misc(k, v) + + def exclude_package(self, package: str) -> None: + """Remove packages, modules, and extensions in named package""" + + pfx = package + '.' + if self.packages: + self.packages = [ + p for p in self.packages if p != package and not p.startswith(pfx) + ] + + if self.py_modules: + self.py_modules = [ + p for p in self.py_modules if p != package and not p.startswith(pfx) + ] + + if self.ext_modules: + self.ext_modules = [ + p + for p in self.ext_modules + if p.name != package and not p.name.startswith(pfx) + ] + + def has_contents_for(self, package: str) -> bool: + """Return true if 'exclude_package(package)' would do something""" + + pfx = package + '.' + + for p in self.iter_distribution_names(): + if p == package or p.startswith(pfx): + return True + + return False + + def _exclude_misc(self, name: str, value: _Sequence) -> None: + """Handle 'exclude()' for list/tuple attrs without a special handler""" + if not isinstance(value, _sequence): + raise DistutilsSetupError( + f"{name}: setting must be of type <{_sequence_type_repr}> (got {value!r})" + ) + try: + old = getattr(self, name) + except AttributeError as e: + raise DistutilsSetupError(f"{name}: No such distribution setting") from e + if old is not None and not isinstance(old, _sequence): + raise DistutilsSetupError( + name + ": this setting cannot be changed via include/exclude" + ) + elif old: + setattr(self, name, [item for item in old if item not in value]) + + def _include_misc(self, name: str, value: _Sequence) -> None: + """Handle 'include()' for list/tuple attrs without a special handler""" + + if not isinstance(value, _sequence): + raise DistutilsSetupError( + f"{name}: setting must be of type <{_sequence_type_repr}> (got {value!r})" + ) + try: + old = getattr(self, name) + except AttributeError as e: + raise DistutilsSetupError(f"{name}: No such distribution setting") from e + if old is None: + setattr(self, name, value) + elif not isinstance(old, _sequence): + raise DistutilsSetupError( + name + ": this setting cannot be changed via include/exclude" + ) + else: + new = [item for item in value if item not in old] + setattr(self, name, list(old) + new) + + def exclude(self, **attrs) -> None: + """Remove items from distribution that are named in keyword arguments + + For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from + the distribution's 'py_modules' attribute. Excluding packages uses + the 'exclude_package()' method, so all of the package's contained + packages, modules, and extensions are also excluded. + + Currently, this method only supports exclusion from attributes that are + lists or tuples. If you need to add support for excluding from other + attributes in this or a subclass, you can add an '_exclude_X' method, + where 'X' is the name of the attribute. The method will be called with + the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})' + will try to call 'dist._exclude_foo({"bar":"baz"})', which can then + handle whatever special exclusion logic is needed. + """ + for k, v in attrs.items(): + exclude = getattr(self, '_exclude_' + k, None) + if exclude: + exclude(v) + else: + self._exclude_misc(k, v) + + def _exclude_packages(self, packages: _Sequence) -> None: + if not isinstance(packages, _sequence): + raise DistutilsSetupError( + f"packages: setting must be of type <{_sequence_type_repr}> (got {packages!r})" + ) + list(map(self.exclude_package, packages)) + + def _parse_command_opts(self, parser, args): + # Remove --with-X/--without-X options when processing command args + self.global_options = self.__class__.global_options + self.negative_opt = self.__class__.negative_opt + + # First, expand any aliases + command = args[0] + aliases = self.get_option_dict('aliases') + while command in aliases: + _src, alias = aliases[command] + del aliases[command] # ensure each alias can expand only once! + import shlex + + args[:1] = shlex.split(alias, True) + command = args[0] + + nargs = _Distribution._parse_command_opts(self, parser, args) + + # Handle commands that want to consume all remaining arguments + cmd_class = self.get_command_class(command) + if getattr(cmd_class, 'command_consumes_arguments', None): + self.get_option_dict(command)['args'] = ("command line", nargs) + if nargs is not None: + return [] + + return nargs + + def get_cmdline_options(self) -> dict[str, dict[str, str | None]]: + """Return a '{cmd: {opt:val}}' map of all command-line options + + Option names are all long, but do not include the leading '--', and + contain dashes rather than underscores. If the option doesn't take + an argument (e.g. '--quiet'), the 'val' is 'None'. + + Note that options provided by config files are intentionally excluded. + """ + + d: dict[str, dict[str, str | None]] = {} + + for cmd, opts in self.command_options.items(): + val: str | None + for opt, (src, val) in opts.items(): + if src != "command line": + continue + + opt = opt.replace('_', '-') + + if val == 0: + cmdobj = self.get_command_obj(cmd) + neg_opt = self.negative_opt.copy() + neg_opt.update(getattr(cmdobj, 'negative_opt', {})) + for neg, pos in neg_opt.items(): + if pos == opt: + opt = neg + val = None + break + else: + raise AssertionError("Shouldn't be able to get here") + + elif val == 1: + val = None + + d.setdefault(cmd, {})[opt] = val + + return d + + def iter_distribution_names(self) -> Iterator[str]: + """Yield all packages, modules, and extension names in distribution""" + + yield from self.packages or () + + yield from self.py_modules or () + + for ext in self.ext_modules or (): + if isinstance(ext, tuple): + name, _buildinfo = ext + else: + name = ext.name + name = name.removesuffix('module') + yield name + + def handle_display_options(self, option_order): + """If there were any non-global "display-only" options + (--help-commands or the metadata display options) on the command + line, display the requested info and return true; else return + false. + """ + import sys + + if self.help_commands: + return _Distribution.handle_display_options(self, option_order) + + # Stdout may be StringIO (e.g. in tests) + if not isinstance(sys.stdout, io.TextIOWrapper): + return _Distribution.handle_display_options(self, option_order) + + # Don't wrap stdout if utf-8 is already the encoding. Provides + # workaround for #334. + if sys.stdout.encoding.lower() in ('utf-8', 'utf8'): + return _Distribution.handle_display_options(self, option_order) + + # Print metadata in UTF-8 no matter the platform + encoding = sys.stdout.encoding + sys.stdout.reconfigure(encoding='utf-8') + try: + return _Distribution.handle_display_options(self, option_order) + finally: + sys.stdout.reconfigure(encoding=encoding) + + def run_command(self, command) -> None: + self.set_defaults() + # Postpone defaults until all explicit configuration is considered + # (setup() args, config files, command line and plugins) + + super().run_command(command) + + +@functools.cache +def _setuptools_commands() -> set[str]: + try: + # Use older API for importlib.metadata compatibility + entry_points = metadata.distribution('setuptools').entry_points + eps: Iterable[str] = (ep.name for ep in entry_points) + except metadata.PackageNotFoundError: + # during bootstrapping, distribution doesn't exist + eps = [] + return {*distutils.command.__all__, *eps} + + +class DistDeprecationWarning(SetuptoolsDeprecationWarning): + """Class for warning about deprecations in dist in + setuptools. Not ignored by default, unlike DeprecationWarning.""" diff --git a/lib/python3.12/site-packages/setuptools/errors.py b/lib/python3.12/site-packages/setuptools/errors.py new file mode 100644 index 0000000000000000000000000000000000000000..990ecbf4e2f18eb188addc9e0466152a20193a90 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/errors.py @@ -0,0 +1,67 @@ +"""setuptools.errors + +Provides exceptions used by setuptools modules. +""" + +from __future__ import annotations + +from distutils import errors as _distutils_errors + +# Re-export errors from distutils to facilitate the migration to PEP632 + +ByteCompileError = _distutils_errors.DistutilsByteCompileError +CCompilerError = _distutils_errors.CCompilerError +ClassError = _distutils_errors.DistutilsClassError +CompileError = _distutils_errors.CompileError +ExecError = _distutils_errors.DistutilsExecError +FileError = _distutils_errors.DistutilsFileError +InternalError = _distutils_errors.DistutilsInternalError +LibError = _distutils_errors.LibError +LinkError = _distutils_errors.LinkError +ModuleError = _distutils_errors.DistutilsModuleError +OptionError = _distutils_errors.DistutilsOptionError +PlatformError = _distutils_errors.DistutilsPlatformError +PreprocessError = _distutils_errors.PreprocessError +SetupError = _distutils_errors.DistutilsSetupError +TemplateError = _distutils_errors.DistutilsTemplateError +UnknownFileError = _distutils_errors.UnknownFileError + +# The root error class in the hierarchy +BaseError = _distutils_errors.DistutilsError + + +class InvalidConfigError(OptionError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+ + """Error used for invalid configurations.""" + + +class RemovedConfigError(OptionError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+ + """Error used for configurations that were deprecated and removed.""" + + +class RemovedCommandError(BaseError, RuntimeError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+ + """Error used for commands that have been removed in setuptools. + + Since ``setuptools`` is built on ``distutils``, simply removing a command + from ``setuptools`` will make the behavior fall back to ``distutils``; this + error is raised if a command exists in ``distutils`` but has been actively + removed in ``setuptools``. + """ + + +class PackageDiscoveryError(BaseError, RuntimeError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+ + """Impossible to perform automatic discovery of packages and/or modules. + + The current project layout or given discovery options can lead to problems when + scanning the project directory. + + Setuptools might also refuse to complete auto-discovery if an error prone condition + is detected (e.g. when a project is organised as a flat-layout but contains + multiple directories that can be taken as top-level packages inside a single + distribution [*]_). In these situations the users are encouraged to be explicit + about which packages to include or to make the discovery parameters more specific. + + .. [*] Since multi-package distributions are uncommon it is very likely that the + developers did not intend for all the directories to be packaged, and are just + leaving auxiliary code in the repository top-level, such as maintenance-related + scripts. + """ diff --git a/lib/python3.12/site-packages/setuptools/extension.py b/lib/python3.12/site-packages/setuptools/extension.py new file mode 100644 index 0000000000000000000000000000000000000000..3e63cbe12aefc3b52a5d1ca2fe3e1ca2e172e29d --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/extension.py @@ -0,0 +1,179 @@ +from __future__ import annotations + +import functools +import re +from collections.abc import Iterable +from typing import TYPE_CHECKING + +from setuptools._path import StrPath + +from .monkey import get_unpatched + +import distutils.core +import distutils.errors +import distutils.extension + + +def _have_cython() -> bool: + """ + Return True if Cython can be imported. + """ + cython_impl = 'Cython.Distutils.build_ext' + try: + # from (cython_impl) import build_ext + __import__(cython_impl, fromlist=['build_ext']).build_ext + except Exception: + return False + return True + + +# for compatibility +have_pyrex = _have_cython +if TYPE_CHECKING: + # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962 + from distutils.core import Extension as _Extension +else: + _Extension = get_unpatched(distutils.core.Extension) + + +class Extension(_Extension): + """ + Describes a single extension module. + + This means that all source files will be compiled into a single binary file + ``.`` (with ```` derived from ``name`` and + ```` defined by one of the values in + ``importlib.machinery.EXTENSION_SUFFIXES``). + + In the case ``.pyx`` files are passed as ``sources and`` ``Cython`` is **not** + installed in the build environment, ``setuptools`` may also try to look for the + equivalent ``.cpp`` or ``.c`` files. + + :arg str name: + the full name of the extension, including any packages -- ie. + *not* a filename or pathname, but Python dotted name + + :arg Iterable[str | os.PathLike[str]] sources: + iterable of source filenames, (except strings, which could be misinterpreted + as a single filename), relative to the distribution root + (where the setup script lives), in Unix form (slash-separated) + for portability. Source files may be C, C++, SWIG (.i), + platform-specific resource files, or whatever else is recognized + by the "build_ext" command as source for a Python extension. + + :keyword list[str] include_dirs: + list of directories to search for C/C++ header files (in Unix + form for portability) + + :keyword list[tuple[str, str|None]] define_macros: + list of macros to define; each macro is defined using a 2-tuple: + the first item corresponding to the name of the macro and the second + item either a string with its value or None to + define it without a particular value (equivalent of "#define + FOO" in source or -DFOO on Unix C compiler command line) + + :keyword list[str] undef_macros: + list of macros to undefine explicitly + + :keyword list[str] library_dirs: + list of directories to search for C/C++ libraries at link time + + :keyword list[str] libraries: + list of library names (not filenames or paths) to link against + + :keyword list[str] runtime_library_dirs: + list of directories to search for C/C++ libraries at run time + (for shared extensions, this is when the extension is loaded). + Setting this will cause an exception during build on Windows + platforms. + + :keyword list[str] extra_objects: + list of extra files to link with (eg. object files not implied + by 'sources', static library that must be explicitly specified, + binary resource files, etc.) + + :keyword list[str] extra_compile_args: + any extra platform- and compiler-specific information to use + when compiling the source files in 'sources'. For platforms and + compilers where "command line" makes sense, this is typically a + list of command-line arguments, but for other platforms it could + be anything. + + :keyword list[str] extra_link_args: + any extra platform- and compiler-specific information to use + when linking object files together to create the extension (or + to create a new static Python interpreter). Similar + interpretation as for 'extra_compile_args'. + + :keyword list[str] export_symbols: + list of symbols to be exported from a shared extension. Not + used on all platforms, and not generally necessary for Python + extensions, which typically export exactly one symbol: "init" + + extension_name. + + :keyword list[str] swig_opts: + any extra options to pass to SWIG if a source file has the .i + extension. + + :keyword list[str] depends: + list of files that the extension depends on + + :keyword str language: + extension language (i.e. "c", "c++", "objc"). Will be detected + from the source extensions if not provided. + + :keyword bool optional: + specifies that a build failure in the extension should not abort the + build process, but simply not install the failing extension. + + :keyword bool py_limited_api: + opt-in flag for the usage of :doc:`Python's limited API `. + + :raises setuptools.errors.PlatformError: if ``runtime_library_dirs`` is + specified on Windows. (since v63) + """ + + # These 4 are set and used in setuptools/command/build_ext.py + # The lack of a default value and risk of `AttributeError` is purposeful + # to avoid people forgetting to call finalize_options if they modify the extension list. + # See example/rationale in https://github.com/pypa/setuptools/issues/4529. + _full_name: str #: Private API, internal use only. + _links_to_dynamic: bool #: Private API, internal use only. + _needs_stub: bool #: Private API, internal use only. + _file_name: str #: Private API, internal use only. + + def __init__( + self, + name: str, + sources: Iterable[StrPath], + *args, + py_limited_api: bool = False, + **kw, + ) -> None: + # The *args is needed for compatibility as calls may use positional + # arguments. py_limited_api may be set only via keyword. + self.py_limited_api = py_limited_api + super().__init__( + name, + sources, # type: ignore[arg-type] # Vendored version of setuptools supports PathLike + *args, + **kw, + ) + + def _convert_pyx_sources_to_lang(self): + """ + Replace sources with .pyx extensions to sources with the target + language extension. This mechanism allows language authors to supply + pre-converted sources but to prefer the .pyx sources. + """ + if _have_cython(): + # the build has Cython, so allow it to compile the .pyx files + return + lang = self.language or '' + target_ext = '.cpp' if lang.lower() == 'c++' else '.c' + sub = functools.partial(re.sub, '.pyx$', target_ext) + self.sources = list(map(sub, self.sources)) + + +class Library(Extension): + """Just like a regular Extension, but built as a library instead""" diff --git a/lib/python3.12/site-packages/setuptools/glob.py b/lib/python3.12/site-packages/setuptools/glob.py new file mode 100644 index 0000000000000000000000000000000000000000..1dfff2cd50ff87b8cef9d936f1fc9d4a2478b136 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/glob.py @@ -0,0 +1,185 @@ +""" +Filename globbing utility. Mostly a copy of `glob` from Python 3.5. + +Changes include: + * `yield from` and PEP3102 `*` removed. + * Hidden files are not ignored. +""" + +from __future__ import annotations + +import fnmatch +import os +import re +from collections.abc import Iterable, Iterator +from typing import TYPE_CHECKING, AnyStr, overload + +if TYPE_CHECKING: + from _typeshed import BytesPath, StrOrBytesPath, StrPath + +__all__ = ["glob", "iglob", "escape"] + + +def glob(pathname: AnyStr, recursive: bool = False) -> list[AnyStr]: + """Return a list of paths matching a pathname pattern. + + The pattern may contain simple shell-style wildcards a la + fnmatch. However, unlike fnmatch, filenames starting with a + dot are special cases that are not matched by '*' and '?' + patterns. + + If recursive is true, the pattern '**' will match any files and + zero or more directories and subdirectories. + """ + return list(iglob(pathname, recursive=recursive)) + + +def iglob(pathname: AnyStr, recursive: bool = False) -> Iterator[AnyStr]: + """Return an iterator which yields the paths matching a pathname pattern. + + The pattern may contain simple shell-style wildcards a la + fnmatch. However, unlike fnmatch, filenames starting with a + dot are special cases that are not matched by '*' and '?' + patterns. + + If recursive is true, the pattern '**' will match any files and + zero or more directories and subdirectories. + """ + it = _iglob(pathname, recursive) + if recursive and _isrecursive(pathname): + s = next(it) # skip empty string + assert not s + return it + + +def _iglob(pathname: AnyStr, recursive: bool) -> Iterator[AnyStr]: + dirname, basename = os.path.split(pathname) + glob_in_dir = glob2 if recursive and _isrecursive(basename) else glob1 + + if not has_magic(pathname): + if basename: + if os.path.lexists(pathname): + yield pathname + else: + # Patterns ending with a slash should match only directories + if os.path.isdir(dirname): + yield pathname + return + + if not dirname: + yield from glob_in_dir(dirname, basename) + return + # `os.path.split()` returns the argument itself as a dirname if it is a + # drive or UNC path. Prevent an infinite recursion if a drive or UNC path + # contains magic characters (i.e. r'\\?\C:'). + if dirname != pathname and has_magic(dirname): + dirs: Iterable[AnyStr] = _iglob(dirname, recursive) + else: + dirs = [dirname] + if not has_magic(basename): + glob_in_dir = glob0 + for dirname in dirs: + for name in glob_in_dir(dirname, basename): + yield os.path.join(dirname, name) + + +# These 2 helper functions non-recursively glob inside a literal directory. +# They return a list of basenames. `glob1` accepts a pattern while `glob0` +# takes a literal basename (so it only has to check for its existence). + + +@overload +def glob1(dirname: StrPath, pattern: str) -> list[str]: ... +@overload +def glob1(dirname: BytesPath, pattern: bytes) -> list[bytes]: ... +def glob1(dirname: StrOrBytesPath, pattern: str | bytes) -> list[str] | list[bytes]: + if not dirname: + if isinstance(pattern, bytes): + dirname = os.curdir.encode('ASCII') + else: + dirname = os.curdir + try: + names = os.listdir(dirname) + except OSError: + return [] + # mypy false-positives: str or bytes type possibility is always kept in sync + return fnmatch.filter(names, pattern) # type: ignore[type-var, return-value] + + +def glob0(dirname, basename): + if not basename: + # `os.path.split()` returns an empty basename for paths ending with a + # directory separator. 'q*x/' should match only directories. + if os.path.isdir(dirname): + return [basename] + else: + if os.path.lexists(os.path.join(dirname, basename)): + return [basename] + return [] + + +# This helper function recursively yields relative pathnames inside a literal +# directory. + + +@overload +def glob2(dirname: StrPath, pattern: str) -> Iterator[str]: ... +@overload +def glob2(dirname: BytesPath, pattern: bytes) -> Iterator[bytes]: ... +def glob2(dirname: StrOrBytesPath, pattern: str | bytes) -> Iterator[str | bytes]: + assert _isrecursive(pattern) + yield pattern[:0] + yield from _rlistdir(dirname) + + +# Recursively yields relative pathnames inside a literal directory. +@overload +def _rlistdir(dirname: StrPath) -> Iterator[str]: ... +@overload +def _rlistdir(dirname: BytesPath) -> Iterator[bytes]: ... +def _rlistdir(dirname: StrOrBytesPath) -> Iterator[str | bytes]: + if not dirname: + if isinstance(dirname, bytes): + dirname = os.curdir.encode('ASCII') + else: + dirname = os.curdir + try: + names = os.listdir(dirname) + except OSError: + return + for x in names: + yield x + # mypy false-positives: str or bytes type possibility is always kept in sync + path = os.path.join(dirname, x) if dirname else x # type: ignore[arg-type] + for y in _rlistdir(path): + yield os.path.join(x, y) # type: ignore[arg-type] + + +magic_check = re.compile('([*?[])') +magic_check_bytes = re.compile(b'([*?[])') + + +def has_magic(s: str | bytes) -> bool: + if isinstance(s, bytes): + return magic_check_bytes.search(s) is not None + else: + return magic_check.search(s) is not None + + +def _isrecursive(pattern: str | bytes) -> bool: + if isinstance(pattern, bytes): + return pattern == b'**' + else: + return pattern == '**' + + +def escape(pathname): + """Escape all special characters.""" + # Escaping is done by wrapping any of "*?[" between square brackets. + # Metacharacters do not work in the drive part and shouldn't be escaped. + drive, pathname = os.path.splitdrive(pathname) + if isinstance(pathname, bytes): + pathname = magic_check_bytes.sub(rb'[\1]', pathname) + else: + pathname = magic_check.sub(r'[\1]', pathname) + return drive + pathname diff --git a/lib/python3.12/site-packages/setuptools/gui-32.exe b/lib/python3.12/site-packages/setuptools/gui-32.exe new file mode 100644 index 0000000000000000000000000000000000000000..1eb430c6d614a5daea4139badc09c222a4b0e72a Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/gui-32.exe differ diff --git a/lib/python3.12/site-packages/setuptools/gui-64.exe b/lib/python3.12/site-packages/setuptools/gui-64.exe new file mode 100644 index 0000000000000000000000000000000000000000..031cb77c17ba8d8a983448268851d612e05e80d1 Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/gui-64.exe differ diff --git a/lib/python3.12/site-packages/setuptools/gui-arm64.exe b/lib/python3.12/site-packages/setuptools/gui-arm64.exe new file mode 100644 index 0000000000000000000000000000000000000000..1e00ffacb182c2af206e5dd9d9fbc41d236da0d1 Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/gui-arm64.exe differ diff --git a/lib/python3.12/site-packages/setuptools/gui.exe b/lib/python3.12/site-packages/setuptools/gui.exe new file mode 100644 index 0000000000000000000000000000000000000000..1eb430c6d614a5daea4139badc09c222a4b0e72a Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/gui.exe differ diff --git a/lib/python3.12/site-packages/setuptools/installer.py b/lib/python3.12/site-packages/setuptools/installer.py new file mode 100644 index 0000000000000000000000000000000000000000..36a8b092279780d730fd0a3cc20c1c76bd72c8a0 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/installer.py @@ -0,0 +1,155 @@ +from __future__ import annotations + +import glob +import itertools +import os +import subprocess +import sys +import tempfile + +import packaging.requirements +import packaging.utils + +from . import _reqs +from ._importlib import metadata +from .warnings import SetuptoolsDeprecationWarning +from .wheel import Wheel + +from distutils import log +from distutils.errors import DistutilsError + + +def _fixup_find_links(find_links): + """Ensure find-links option end-up being a list of strings.""" + if isinstance(find_links, str): + return find_links.split() + assert isinstance(find_links, (tuple, list)) + return find_links + + +def fetch_build_egg(dist, req) -> metadata.Distribution | metadata.PathDistribution: + """Fetch an egg needed for building. + + Use pip/wheel to fetch/build a wheel.""" + _DeprecatedInstaller.emit() + _warn_wheel_not_available(dist) + return _fetch_build_egg_no_warn(dist, req) + + +def _present(req): + return any(_dist_matches_req(dist, req) for dist in metadata.distributions()) + + +def _fetch_build_eggs(dist, requires: _reqs._StrOrIter) -> list[metadata.Distribution]: + _DeprecatedInstaller.emit(stacklevel=3) + _warn_wheel_not_available(dist) + + parsed_reqs = _reqs.parse(requires) + + missing_reqs = itertools.filterfalse(_present, parsed_reqs) + + needed_reqs = ( + req for req in missing_reqs if not req.marker or req.marker.evaluate() + ) + resolved_dists = [_fetch_build_egg_no_warn(dist, req) for req in needed_reqs] + for dist in resolved_dists: + # dist.locate_file('') is the directory containing EGG-INFO, where the importabl + # contents can be found. + sys.path.insert(0, str(dist.locate_file(''))) + return resolved_dists + + +def _dist_matches_req(egg_dist, req): + return ( + packaging.utils.canonicalize_name(egg_dist.name) + == packaging.utils.canonicalize_name(req.name) + and egg_dist.version in req.specifier + ) + + +def _fetch_build_egg_no_warn(dist, req): # noqa: C901 # is too complex (16) # FIXME + # Ignore environment markers; if supplied, it is required. + req = strip_marker(req) + # Take easy_install options into account, but do not override relevant + # pip environment variables (like PIP_INDEX_URL or PIP_QUIET); they'll + # take precedence. + opts = dist.get_option_dict('easy_install') + if 'allow_hosts' in opts: + raise DistutilsError( + 'the `allow-hosts` option is not supported ' + 'when using pip to install requirements.' + ) + quiet = 'PIP_QUIET' not in os.environ and 'PIP_VERBOSE' not in os.environ + if 'PIP_INDEX_URL' in os.environ: + index_url = None + elif 'index_url' in opts: + index_url = opts['index_url'][1] + else: + index_url = None + find_links = ( + _fixup_find_links(opts['find_links'][1])[:] if 'find_links' in opts else [] + ) + if dist.dependency_links: + find_links.extend(dist.dependency_links) + eggs_dir = os.path.realpath(dist.get_egg_cache_dir()) + cached_dists = metadata.Distribution.discover(path=glob.glob(f'{eggs_dir}/*.egg')) + for egg_dist in cached_dists: + if _dist_matches_req(egg_dist, req): + return egg_dist + with tempfile.TemporaryDirectory() as tmpdir: + cmd = [ + sys.executable, + '-m', + 'pip', + '--disable-pip-version-check', + 'wheel', + '--no-deps', + '-w', + tmpdir, + ] + if quiet: + cmd.append('--quiet') + if index_url is not None: + cmd.extend(('--index-url', index_url)) + for link in find_links or []: + cmd.extend(('--find-links', link)) + # If requirement is a PEP 508 direct URL, directly pass + # the URL to pip, as `req @ url` does not work on the + # command line. + cmd.append(req.url or str(req)) + try: + subprocess.check_call(cmd) + except subprocess.CalledProcessError as e: + raise DistutilsError(str(e)) from e + wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0]) + dist_location = os.path.join(eggs_dir, wheel.egg_name()) + wheel.install_as_egg(dist_location) + return metadata.Distribution.at(dist_location + '/EGG-INFO') + + +def strip_marker(req) -> packaging.requirements.Requirement: + """ + Return a new requirement without the environment marker to avoid + calling pip with something like `babel; extra == "i18n"`, which + would always be ignored. + """ + # create a copy to avoid mutating the input + req = packaging.requirements.Requirement(str(req)) + req.marker = None + return req + + +def _warn_wheel_not_available(dist): + try: + metadata.distribution('wheel') + except metadata.PackageNotFoundError: + dist.announce('WARNING: The wheel package is not available.', log.WARN) + + +class _DeprecatedInstaller(SetuptoolsDeprecationWarning): + _SUMMARY = "setuptools.installer and fetch_build_eggs are deprecated." + _DETAILS = """ + Requirements should be satisfied by a PEP 517 installer. + If you are using pip, you can try `pip install --use-pep517`. + """ + _DUE_DATE = 2025, 10, 31 diff --git a/lib/python3.12/site-packages/setuptools/launch.py b/lib/python3.12/site-packages/setuptools/launch.py new file mode 100644 index 0000000000000000000000000000000000000000..0d162647d55777d7afa1bf1e44a6c200a3f82419 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/launch.py @@ -0,0 +1,36 @@ +""" +Launch the Python script on the command line after +setuptools is bootstrapped via import. +""" + +# Note that setuptools gets imported implicitly by the +# invocation of this script using python -m setuptools.launch + +import sys +import tokenize + + +def run() -> None: + """ + Run the script in sys.argv[1] as if it had + been invoked naturally. + """ + __builtins__ + script_name = sys.argv[1] + namespace = dict( + __file__=script_name, + __name__='__main__', + __doc__=None, + ) + sys.argv[:] = sys.argv[1:] + + open_ = getattr(tokenize, 'open', open) + with open_(script_name) as fid: + script = fid.read() + norm_script = script.replace('\\r\\n', '\\n') + code = compile(norm_script, script_name, 'exec') + exec(code, namespace) + + +if __name__ == '__main__': + run() diff --git a/lib/python3.12/site-packages/setuptools/logging.py b/lib/python3.12/site-packages/setuptools/logging.py new file mode 100644 index 0000000000000000000000000000000000000000..532da899f7dc02f9fea9a44c429086b98fe043d8 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/logging.py @@ -0,0 +1,40 @@ +import inspect +import logging +import sys + +from . import monkey + +import distutils.log + + +def _not_warning(record): + return record.levelno < logging.WARNING + + +def configure() -> None: + """ + Configure logging to emit warning and above to stderr + and everything else to stdout. This behavior is provided + for compatibility with distutils.log but may change in + the future. + """ + err_handler = logging.StreamHandler() + err_handler.setLevel(logging.WARNING) + out_handler = logging.StreamHandler(sys.stdout) + out_handler.addFilter(_not_warning) + handlers = err_handler, out_handler + logging.basicConfig( + format="{message}", style='{', handlers=handlers, level=logging.DEBUG + ) + if inspect.ismodule(distutils.dist.log): + monkey.patch_func(set_threshold, distutils.log, 'set_threshold') + # For some reason `distutils.log` module is getting cached in `distutils.dist` + # and then loaded again when patched, + # implying: id(distutils.log) != id(distutils.dist.log). + # Make sure the same module object is used everywhere: + distutils.dist.log = distutils.log + + +def set_threshold(level: int) -> int: + logging.root.setLevel(level * 10) + return set_threshold.unpatched(level) diff --git a/lib/python3.12/site-packages/setuptools/modified.py b/lib/python3.12/site-packages/setuptools/modified.py new file mode 100644 index 0000000000000000000000000000000000000000..6ba02fab68734e1e96fd50d7c4b6ffb1442717fb --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/modified.py @@ -0,0 +1,18 @@ +try: + # Ensure a DistutilsError raised by these methods is the same as distutils.errors.DistutilsError + from distutils._modified import ( + newer, + newer_group, + newer_pairwise, + newer_pairwise_group, + ) +except ImportError: + # fallback for SETUPTOOLS_USE_DISTUTILS=stdlib, because _modified never existed in stdlib + from ._distutils._modified import ( + newer, + newer_group, + newer_pairwise, + newer_pairwise_group, + ) + +__all__ = ['newer', 'newer_pairwise', 'newer_group', 'newer_pairwise_group'] diff --git a/lib/python3.12/site-packages/setuptools/monkey.py b/lib/python3.12/site-packages/setuptools/monkey.py new file mode 100644 index 0000000000000000000000000000000000000000..24bb8180f960a2cd62f352a41241e107e9521750 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/monkey.py @@ -0,0 +1,126 @@ +""" +Monkey patching of distutils. +""" + +from __future__ import annotations + +import inspect +import platform +import sys +import types +from typing import TypeVar, cast, overload + +import distutils.filelist + +_T = TypeVar("_T") +_UnpatchT = TypeVar("_UnpatchT", type, types.FunctionType) + + +__all__: list[str] = [] +""" +Everything is private. Contact the project team +if you think you need this functionality. +""" + + +def _get_mro(cls): + """ + Returns the bases classes for cls sorted by the MRO. + + Works around an issue on Jython where inspect.getmro will not return all + base classes if multiple classes share the same name. Instead, this + function will return a tuple containing the class itself, and the contents + of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024. + """ + if platform.python_implementation() == "Jython": + return (cls,) + cls.__bases__ + return inspect.getmro(cls) + + +@overload +def get_unpatched(item: _UnpatchT) -> _UnpatchT: ... +@overload +def get_unpatched(item: object) -> None: ... +def get_unpatched( + item: type | types.FunctionType | object, +) -> type | types.FunctionType | None: + if isinstance(item, type): + return get_unpatched_class(item) + if isinstance(item, types.FunctionType): + return get_unpatched_function(item) + return None + + +def get_unpatched_class(cls: type[_T]) -> type[_T]: + """Protect against re-patching the distutils if reloaded + + Also ensures that no other distutils extension monkeypatched the distutils + first. + """ + external_bases = ( + cast(type[_T], cls) + for cls in _get_mro(cls) + if not cls.__module__.startswith('setuptools') + ) + base = next(external_bases) + if not base.__module__.startswith('distutils'): + msg = f"distutils has already been patched by {cls!r}" + raise AssertionError(msg) + return base + + +def patch_all() -> None: + import setuptools + + # we can't patch distutils.cmd, alas + distutils.core.Command = setuptools.Command # type: ignore[misc,assignment] # monkeypatching + + _patch_distribution_metadata() + + # Install Distribution throughout the distutils + for module in distutils.dist, distutils.core, distutils.cmd: + module.Distribution = setuptools.dist.Distribution + + # Install the patched Extension + distutils.core.Extension = setuptools.extension.Extension # type: ignore[misc,assignment] # monkeypatching + distutils.extension.Extension = setuptools.extension.Extension # type: ignore[misc,assignment] # monkeypatching + if 'distutils.command.build_ext' in sys.modules: + sys.modules[ + 'distutils.command.build_ext' + ].Extension = setuptools.extension.Extension + + +def _patch_distribution_metadata(): + from . import _core_metadata + + """Patch write_pkg_file and read_pkg_file for higher metadata standards""" + for attr in ( + 'write_pkg_info', + 'write_pkg_file', + 'read_pkg_file', + 'get_metadata_version', + 'get_fullname', + ): + new_val = getattr(_core_metadata, attr) + setattr(distutils.dist.DistributionMetadata, attr, new_val) + + +def patch_func(replacement, target_mod, func_name) -> None: + """ + Patch func_name in target_mod with replacement + + Important - original must be resolved by name to avoid + patching an already patched function. + """ + original = getattr(target_mod, func_name) + + # set the 'unpatched' attribute on the replacement to + # point to the original. + vars(replacement).setdefault('unpatched', original) + + # replace the function in the original module + setattr(target_mod, func_name, replacement) + + +def get_unpatched_function(candidate): + return candidate.unpatched diff --git a/lib/python3.12/site-packages/setuptools/msvc.py b/lib/python3.12/site-packages/setuptools/msvc.py new file mode 100644 index 0000000000000000000000000000000000000000..f506c8222dc1994dfd155f3b2c2404c62fc50659 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/msvc.py @@ -0,0 +1,1557 @@ +""" +Environment info about Microsoft Compilers. + +>>> getfixture('windows_only') +>>> ei = EnvironmentInfo('amd64') +""" + +from __future__ import annotations + +import contextlib +import itertools +import json +import os +import os.path +import platform +from typing import TYPE_CHECKING, TypedDict, overload + +from more_itertools import unique_everseen + +from ._path import StrPath +from .compat import py310 + +import distutils.errors + +if TYPE_CHECKING: + from typing_extensions import LiteralString, NotRequired + +# https://github.com/python/mypy/issues/8166 +if not TYPE_CHECKING and platform.system() == 'Windows': + import winreg + from os import environ +else: + # Mock winreg and environ so the module can be imported on this platform. + + class winreg: + HKEY_USERS = None + HKEY_CURRENT_USER = None + HKEY_LOCAL_MACHINE = None + HKEY_CLASSES_ROOT = None + + environ: dict[str, str] = dict() + + +class PlatformInfo: + """ + Current and Target Architectures information. + + Parameters + ---------- + arch: str + Target architecture. + """ + + current_cpu = environ.get('processor_architecture', '').lower() + + def __init__(self, arch: str) -> None: + self.arch = arch.lower().replace('x64', 'amd64') + + @property + def target_cpu(self) -> str: + """ + Return Target CPU architecture. + + Return + ------ + str + Target CPU + """ + return self.arch[self.arch.find('_') + 1 :] + + def target_is_x86(self) -> bool: + """ + Return True if target CPU is x86 32 bits.. + + Return + ------ + bool + CPU is x86 32 bits + """ + return self.target_cpu == 'x86' + + def current_is_x86(self) -> bool: + """ + Return True if current CPU is x86 32 bits.. + + Return + ------ + bool + CPU is x86 32 bits + """ + return self.current_cpu == 'x86' + + def current_dir(self, hidex86=False, x64=False) -> str: + """ + Current platform specific subfolder. + + Parameters + ---------- + hidex86: bool + return '' and not '\x86' if architecture is x86. + x64: bool + return '\x64' and not '\amd64' if architecture is amd64. + + Return + ------ + str + subfolder: '\target', or '' (see hidex86 parameter) + """ + return ( + '' + if (self.current_cpu == 'x86' and hidex86) + else r'\x64' + if (self.current_cpu == 'amd64' and x64) + else rf'\{self.current_cpu}' + ) + + def target_dir(self, hidex86=False, x64=False) -> str: + r""" + Target platform specific subfolder. + + Parameters + ---------- + hidex86: bool + return '' and not '\x86' if architecture is x86. + x64: bool + return '\x64' and not '\amd64' if architecture is amd64. + + Return + ------ + str + subfolder: '\current', or '' (see hidex86 parameter) + """ + return ( + '' + if (self.target_cpu == 'x86' and hidex86) + else r'\x64' + if (self.target_cpu == 'amd64' and x64) + else rf'\{self.target_cpu}' + ) + + def cross_dir(self, forcex86=False) -> str: + r""" + Cross platform specific subfolder. + + Parameters + ---------- + forcex86: bool + Use 'x86' as current architecture even if current architecture is + not x86. + + Return + ------ + str + subfolder: '' if target architecture is current architecture, + '\current_target' if not. + """ + current = 'x86' if forcex86 else self.current_cpu + return ( + '' + if self.target_cpu == current + else self.target_dir().replace('\\', f'\\{current}_') + ) + + +class RegistryInfo: + """ + Microsoft Visual Studio related registry information. + + Parameters + ---------- + platform_info: PlatformInfo + "PlatformInfo" instance. + """ + + HKEYS = ( + winreg.HKEY_USERS, + winreg.HKEY_CURRENT_USER, + winreg.HKEY_LOCAL_MACHINE, + winreg.HKEY_CLASSES_ROOT, + ) + + def __init__(self, platform_info: PlatformInfo) -> None: + self.pi = platform_info + + @property + def visualstudio(self) -> LiteralString: + """ + Microsoft Visual Studio root registry key. + + Return + ------ + str + Registry key + """ + return 'VisualStudio' + + @property + def sxs(self) -> LiteralString: + """ + Microsoft Visual Studio SxS registry key. + + Return + ------ + str + Registry key + """ + return os.path.join(self.visualstudio, 'SxS') + + @property + def vc(self) -> LiteralString: + """ + Microsoft Visual C++ VC7 registry key. + + Return + ------ + str + Registry key + """ + return os.path.join(self.sxs, 'VC7') + + @property + def vs(self) -> LiteralString: + """ + Microsoft Visual Studio VS7 registry key. + + Return + ------ + str + Registry key + """ + return os.path.join(self.sxs, 'VS7') + + @property + def vc_for_python(self) -> LiteralString: + """ + Microsoft Visual C++ for Python registry key. + + Return + ------ + str + Registry key + """ + return r'DevDiv\VCForPython' + + @property + def microsoft_sdk(self) -> LiteralString: + """ + Microsoft SDK registry key. + + Return + ------ + str + Registry key + """ + return 'Microsoft SDKs' + + @property + def windows_sdk(self) -> LiteralString: + """ + Microsoft Windows/Platform SDK registry key. + + Return + ------ + str + Registry key + """ + return os.path.join(self.microsoft_sdk, 'Windows') + + @property + def netfx_sdk(self) -> LiteralString: + """ + Microsoft .NET Framework SDK registry key. + + Return + ------ + str + Registry key + """ + return os.path.join(self.microsoft_sdk, 'NETFXSDK') + + @property + def windows_kits_roots(self) -> LiteralString: + """ + Microsoft Windows Kits Roots registry key. + + Return + ------ + str + Registry key + """ + return r'Windows Kits\Installed Roots' + + @overload + def microsoft(self, key: LiteralString, x86: bool = False) -> LiteralString: ... + @overload + def microsoft(self, key: str, x86: bool = False) -> str: ... # type: ignore[misc] + def microsoft(self, key: str, x86: bool = False) -> str: + """ + Return key in Microsoft software registry. + + Parameters + ---------- + key: str + Registry key path where look. + x86: bool + Force x86 software registry. + + Return + ------ + str + Registry key + """ + node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node' + return os.path.join('Software', node64, 'Microsoft', key) + + def lookup(self, key: str, name: str) -> str | None: + """ + Look for values in registry in Microsoft software registry. + + Parameters + ---------- + key: str + Registry key path where look. + name: str + Value name to find. + + Return + ------ + str | None + value + """ + key_read = winreg.KEY_READ + openkey = winreg.OpenKey + closekey = winreg.CloseKey + ms = self.microsoft + for hkey in self.HKEYS: + bkey = None + try: + bkey = openkey(hkey, ms(key), 0, key_read) + except OSError: + if not self.pi.current_is_x86(): + try: + bkey = openkey(hkey, ms(key, True), 0, key_read) + except OSError: + continue + else: + continue + try: + return winreg.QueryValueEx(bkey, name)[0] + except OSError: + pass + finally: + if bkey: + closekey(bkey) + return None + + +class SystemInfo: + """ + Microsoft Windows and Visual Studio related system information. + + Parameters + ---------- + registry_info: RegistryInfo + "RegistryInfo" instance. + vc_ver: float + Required Microsoft Visual C++ version. + """ + + # Variables and properties in this class use originals CamelCase variables + # names from Microsoft source files for more easy comparison. + WinDir = environ.get('WinDir', '') + ProgramFiles = environ.get('ProgramFiles', '') + ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles) + + def __init__( + self, registry_info: RegistryInfo, vc_ver: float | None = None + ) -> None: + self.ri = registry_info + self.pi = self.ri.pi + + self.known_vs_paths = self.find_programdata_vs_vers() + + # Except for VS15+, VC version is aligned with VS version + self.vs_ver = self.vc_ver = vc_ver or self._find_latest_available_vs_ver() + + def _find_latest_available_vs_ver(self): + """ + Find the latest VC version + + Return + ------ + float + version + """ + reg_vc_vers = self.find_reg_vs_vers() + + if not (reg_vc_vers or self.known_vs_paths): + raise distutils.errors.DistutilsPlatformError( + 'No Microsoft Visual C++ version found' + ) + + vc_vers = set(reg_vc_vers) + vc_vers.update(self.known_vs_paths) + return max(vc_vers) + + def find_reg_vs_vers(self) -> list[float]: + """ + Find Microsoft Visual Studio versions available in registry. + + Return + ------ + list of float + Versions + """ + ms = self.ri.microsoft + vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs) + vs_vers = [] + for hkey, key in itertools.product(self.ri.HKEYS, vckeys): + try: + bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) + except OSError: + continue + with bkey: + subkeys, values, _ = winreg.QueryInfoKey(bkey) + for i in range(values): + with contextlib.suppress(ValueError): + ver = float(winreg.EnumValue(bkey, i)[0]) + if ver not in vs_vers: + vs_vers.append(ver) + for i in range(subkeys): + with contextlib.suppress(ValueError): + ver = float(winreg.EnumKey(bkey, i)) + if ver not in vs_vers: + vs_vers.append(ver) + return sorted(vs_vers) + + def find_programdata_vs_vers(self) -> dict[float, str]: + r""" + Find Visual studio 2017+ versions from information in + "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances". + + Return + ------ + dict + float version as key, path as value. + """ + vs_versions: dict[float, str] = {} + instances_dir = r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances' + + try: + hashed_names = os.listdir(instances_dir) + + except OSError: + # Directory not exists with all Visual Studio versions + return vs_versions + + for name in hashed_names: + try: + # Get VS installation path from "state.json" file + state_path = os.path.join(instances_dir, name, 'state.json') + with open(state_path, 'rt', encoding='utf-8') as state_file: + state = json.load(state_file) + vs_path = state['installationPath'] + + # Raises OSError if this VS installation does not contain VC + os.listdir(os.path.join(vs_path, r'VC\Tools\MSVC')) + + # Store version and path + vs_versions[self._as_float_version(state['installationVersion'])] = ( + vs_path + ) + + except (OSError, KeyError): + # Skip if "state.json" file is missing or bad format + continue + + return vs_versions + + @staticmethod + def _as_float_version(version): + """ + Return a string version as a simplified float version (major.minor) + + Parameters + ---------- + version: str + Version. + + Return + ------ + float + version + """ + return float('.'.join(version.split('.')[:2])) + + @property + def VSInstallDir(self) -> str: + """ + Microsoft Visual Studio directory. + + Return + ------ + str + path + """ + # Default path + default = os.path.join( + self.ProgramFilesx86, f'Microsoft Visual Studio {self.vs_ver:0.1f}' + ) + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vs, f'{self.vs_ver:0.1f}') or default + + @property + def VCInstallDir(self) -> str: + """ + Microsoft Visual C++ directory. + + Return + ------ + str + path + """ + path = self._guess_vc() or self._guess_vc_legacy() + + if not os.path.isdir(path): + msg = 'Microsoft Visual C++ directory not found' + raise distutils.errors.DistutilsPlatformError(msg) + + return path + + def _guess_vc(self): + """ + Locate Visual C++ for VS2017+. + + Return + ------ + str + path + """ + if self.vs_ver <= 14.0: + return '' + + try: + # First search in known VS paths + vs_dir = self.known_vs_paths[self.vs_ver] + except KeyError: + # Else, search with path from registry + vs_dir = self.VSInstallDir + + guess_vc = os.path.join(vs_dir, r'VC\Tools\MSVC') + + # Subdir with VC exact version as name + try: + # Update the VC version with real one instead of VS version + vc_ver = os.listdir(guess_vc)[-1] + self.vc_ver = self._as_float_version(vc_ver) + return os.path.join(guess_vc, vc_ver) + except (OSError, IndexError): + return '' + + def _guess_vc_legacy(self): + """ + Locate Visual C++ for versions prior to 2017. + + Return + ------ + str + path + """ + default = os.path.join( + self.ProgramFilesx86, + rf'Microsoft Visual Studio {self.vs_ver:0.1f}\VC', + ) + + # Try to get "VC++ for Python" path from registry as default path + reg_path = os.path.join(self.ri.vc_for_python, f'{self.vs_ver:0.1f}') + python_vc = self.ri.lookup(reg_path, 'installdir') + default_vc = os.path.join(python_vc, 'VC') if python_vc else default + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, f'{self.vs_ver:0.1f}') or default_vc + + @property + def WindowsSdkVersion(self) -> tuple[LiteralString, ...]: + """ + Microsoft Windows SDK versions for specified MSVC++ version. + + Return + ------ + tuple of str + versions + """ + if self.vs_ver <= 9.0: + return '7.0', '6.1', '6.0a' + elif self.vs_ver == 10.0: + return '7.1', '7.0a' + elif self.vs_ver == 11.0: + return '8.0', '8.0a' + elif self.vs_ver == 12.0: + return '8.1', '8.1a' + elif self.vs_ver >= 14.0: + return '10.0', '8.1' + return () + + @property + def WindowsSdkLastVersion(self) -> str: + """ + Microsoft Windows SDK last version. + + Return + ------ + str + version + """ + return self._use_last_dir_name(os.path.join(self.WindowsSdkDir, 'lib')) + + @property + def WindowsSdkDir(self) -> str: # noqa: C901 # is too complex (12) # FIXME + """ + Microsoft Windows SDK directory. + + Return + ------ + str + path + """ + sdkdir: str | None = '' + for ver in self.WindowsSdkVersion: + # Try to get it from registry + loc = os.path.join(self.ri.windows_sdk, f'v{ver}') + sdkdir = self.ri.lookup(loc, 'installationfolder') + if sdkdir: + break + if not sdkdir or not os.path.isdir(sdkdir): + # Try to get "VC++ for Python" version from registry + path = os.path.join(self.ri.vc_for_python, f'{self.vc_ver:0.1f}') + install_base = self.ri.lookup(path, 'installdir') + if install_base: + sdkdir = os.path.join(install_base, 'WinSDK') + if not sdkdir or not os.path.isdir(sdkdir): + # If fail, use default new path + for ver in self.WindowsSdkVersion: + intver = ver[: ver.rfind('.')] + path = rf'Microsoft SDKs\Windows Kits\{intver}' + d = os.path.join(self.ProgramFiles, path) + if os.path.isdir(d): + sdkdir = d + if not sdkdir or not os.path.isdir(sdkdir): + # If fail, use default old path + for ver in self.WindowsSdkVersion: + path = rf'Microsoft SDKs\Windows\v{ver}' + d = os.path.join(self.ProgramFiles, path) + if os.path.isdir(d): + sdkdir = d + if not sdkdir: + # If fail, use Platform SDK + sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') + return sdkdir + + @property + def WindowsSDKExecutablePath(self) -> str | None: + """ + Microsoft Windows SDK executable directory. + + Return + ------ + str | None + path + """ + # Find WinSDK NetFx Tools registry dir name + if self.vs_ver <= 11.0: + netfxver = 35 + arch = '' + else: + netfxver = 40 + hidex86 = True if self.vs_ver <= 12.0 else False + arch = self.pi.current_dir(x64=True, hidex86=hidex86).replace('\\', '-') + fx = f'WinSDK-NetFx{netfxver}Tools{arch}' + + # list all possibles registry paths + regpaths = [] + if self.vs_ver >= 14.0: + for ver in self.NetFxSdkVersion: + regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)] + + for ver in self.WindowsSdkVersion: + regpaths += [os.path.join(self.ri.windows_sdk, f'v{ver}A', fx)] + + # Return installation folder from the more recent path + for path in regpaths: + execpath = self.ri.lookup(path, 'installationfolder') + if execpath: + return execpath + + return None + + @property + def FSharpInstallDir(self) -> str: + """ + Microsoft Visual F# directory. + + Return + ------ + str + path + """ + path = os.path.join(self.ri.visualstudio, rf'{self.vs_ver:0.1f}\Setup\F#') + return self.ri.lookup(path, 'productdir') or '' + + @property + def UniversalCRTSdkDir(self) -> str | None: + """ + Microsoft Universal CRT SDK directory. + + Return + ------ + str | None + path + """ + # Set Kit Roots versions for specified MSVC++ version + vers = ('10', '81') if self.vs_ver >= 14.0 else () + + # Find path of the more recent Kit + for ver in vers: + sdkdir = self.ri.lookup(self.ri.windows_kits_roots, f'kitsroot{ver}') + if sdkdir: + return sdkdir + + return None + + @property + def UniversalCRTSdkLastVersion(self) -> str: + """ + Microsoft Universal C Runtime SDK last version. + + Return + ------ + str + version + """ + try: + return self._use_last_dir_name(os.path.join(self.UniversalCRTSdkDir, 'lib')) # type: ignore[arg-type] # Expected TypeError + except TypeError as ex: + py310.add_note(ex, "Cannot find UniversalCRTSdkDir") + raise + + @property + def NetFxSdkVersion(self) -> tuple[LiteralString, ...]: + """ + Microsoft .NET Framework SDK versions. + + Return + ------ + tuple of str + versions + """ + # Set FxSdk versions for specified VS version + return ( + ('4.7.2', '4.7.1', '4.7', '4.6.2', '4.6.1', '4.6', '4.5.2', '4.5.1', '4.5') + if self.vs_ver >= 14.0 + else () + ) + + @property + def NetFxSdkDir(self) -> str | None: + """ + Microsoft .NET Framework SDK directory. + + Return + ------ + str | None + path + """ + sdkdir: str | None = '' + for ver in self.NetFxSdkVersion: + loc = os.path.join(self.ri.netfx_sdk, ver) + sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') + if sdkdir: + break + return sdkdir + + @property + def FrameworkDir32(self) -> str: + """ + Microsoft .NET Framework 32bit directory. + + Return + ------ + str + path + """ + # Default path + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework') + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw + + @property + def FrameworkDir64(self) -> str: + """ + Microsoft .NET Framework 64bit directory. + + Return + ------ + str + path + """ + # Default path + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64') + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw + + @property + def FrameworkVersion32(self) -> tuple[str, ...]: + """ + Microsoft .NET Framework 32bit versions. + + Return + ------ + tuple of str + versions + """ + return self._find_dot_net_versions(32) + + @property + def FrameworkVersion64(self) -> tuple[str, ...]: + """ + Microsoft .NET Framework 64bit versions. + + Return + ------ + tuple of str + versions + """ + return self._find_dot_net_versions(64) + + def _find_dot_net_versions(self, bits) -> tuple[str, ...]: + """ + Find Microsoft .NET Framework versions. + + Parameters + ---------- + bits: int + Platform number of bits: 32 or 64. + + Return + ------ + tuple of str + versions + """ + # Find actual .NET version in registry + reg_ver = self.ri.lookup(self.ri.vc, f'frameworkver{bits}') + dot_net_dir = getattr(self, f'FrameworkDir{bits}') + ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or '' + + # Set .NET versions for specified MSVC++ version + if self.vs_ver >= 12.0: + return ver, 'v4.0' + elif self.vs_ver >= 10.0: + return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5' + elif self.vs_ver == 9.0: + return 'v3.5', 'v2.0.50727' + elif self.vs_ver == 8.0: + return 'v3.0', 'v2.0.50727' + return () + + @staticmethod + def _use_last_dir_name(path: StrPath, prefix: str = '') -> str: + """ + Return name of the last dir in path or '' if no dir found. + + Parameters + ---------- + path: StrPath + Use dirs in this path + prefix: str + Use only dirs starting by this prefix + + Return + ------ + str + name + """ + matching_dirs = ( + dir_name + for dir_name in reversed(os.listdir(path)) + if os.path.isdir(os.path.join(path, dir_name)) + and dir_name.startswith(prefix) + ) + return next(matching_dirs, '') + + +class _EnvironmentDict(TypedDict): + include: str + lib: str + libpath: str + path: str + py_vcruntime_redist: NotRequired[str | None] + + +class EnvironmentInfo: + """ + Return environment variables for specified Microsoft Visual C++ version + and platform : Lib, Include, Path and libpath. + + This function is compatible with Microsoft Visual C++ 9.0 to 14.X. + + Script created by analysing Microsoft environment configuration files like + "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ... + + Parameters + ---------- + arch: str + Target architecture. + vc_ver: float + Required Microsoft Visual C++ version. If not set, autodetect the last + version. + vc_min_ver: float + Minimum Microsoft Visual C++ version. + """ + + # Variables and properties in this class use originals CamelCase variables + # names from Microsoft source files for more easy comparison. + + def __init__(self, arch, vc_ver=None, vc_min_ver=0) -> None: + self.pi = PlatformInfo(arch) + self.ri = RegistryInfo(self.pi) + self.si = SystemInfo(self.ri, vc_ver) + + if self.vc_ver < vc_min_ver: + err = 'No suitable Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) + + @property + def vs_ver(self): + """ + Microsoft Visual Studio. + + Return + ------ + float + version + """ + return self.si.vs_ver + + @property + def vc_ver(self): + """ + Microsoft Visual C++ version. + + Return + ------ + float + version + """ + return self.si.vc_ver + + @property + def VSTools(self): + """ + Microsoft Visual Studio Tools. + + Return + ------ + list of str + paths + """ + paths = [r'Common7\IDE', r'Common7\Tools'] + + if self.vs_ver >= 14.0: + arch_subdir = self.pi.current_dir(hidex86=True, x64=True) + paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] + paths += [r'Team Tools\Performance Tools'] + paths += [rf'Team Tools\Performance Tools{arch_subdir}'] + + return [os.path.join(self.si.VSInstallDir, path) for path in paths] + + @property + def VCIncludes(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Includes. + + Return + ------ + list of str + paths + """ + return [ + os.path.join(self.si.VCInstallDir, 'Include'), + os.path.join(self.si.VCInstallDir, r'ATLMFC\Include'), + ] + + @property + def VCLibraries(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver >= 15.0: + arch_subdir = self.pi.target_dir(x64=True) + else: + arch_subdir = self.pi.target_dir(hidex86=True) + paths = [f'Lib{arch_subdir}', rf'ATLMFC\Lib{arch_subdir}'] + + if self.vs_ver >= 14.0: + paths += [rf'Lib\store{arch_subdir}'] + + return [os.path.join(self.si.VCInstallDir, path) for path in paths] + + @property + def VCStoreRefs(self): + """ + Microsoft Visual C++ store references Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0: + return [] + return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')] + + @property + def VCTools(self): + """ + Microsoft Visual C++ Tools. + + Return + ------ + list of str + paths + + When host CPU is ARM, the tools should be found for ARM. + + >>> getfixture('windows_only') + >>> mp = getfixture('monkeypatch') + >>> mp.setattr(PlatformInfo, 'current_cpu', 'arm64') + >>> ei = EnvironmentInfo(arch='irrelevant') + >>> paths = ei.VCTools + >>> any('HostARM64' in path for path in paths) + True + """ + si = self.si + tools = [os.path.join(si.VCInstallDir, 'VCPackages')] + + forcex86 = True if self.vs_ver <= 10.0 else False + arch_subdir = self.pi.cross_dir(forcex86) + if arch_subdir: + tools += [os.path.join(si.VCInstallDir, f'Bin{arch_subdir}')] + + if self.vs_ver == 14.0: + path = f'Bin{self.pi.current_dir(hidex86=True)}' + tools += [os.path.join(si.VCInstallDir, path)] + + elif self.vs_ver >= 15.0: + host_id = self.pi.current_cpu.replace('amd64', 'x64').upper() + host_dir = os.path.join('bin', f'Host{host_id}%s') + tools += [ + os.path.join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True)) + ] + + if self.pi.current_cpu != self.pi.target_cpu: + tools += [ + os.path.join( + si.VCInstallDir, host_dir % self.pi.current_dir(x64=True) + ) + ] + + else: + tools += [os.path.join(si.VCInstallDir, 'Bin')] + + return tools + + @property + def OSLibraries(self): + """ + Microsoft Windows SDK Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver <= 10.0: + arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + return [os.path.join(self.si.WindowsSdkDir, f'Lib{arch_subdir}')] + + else: + arch_subdir = self.pi.target_dir(x64=True) + lib = os.path.join(self.si.WindowsSdkDir, 'lib') + libver = self._sdk_subdir + return [os.path.join(lib, f'{libver}um{arch_subdir}')] + + @property + def OSIncludes(self): + """ + Microsoft Windows SDK Include. + + Return + ------ + list of str + paths + """ + include = os.path.join(self.si.WindowsSdkDir, 'include') + + if self.vs_ver <= 10.0: + return [include, os.path.join(include, 'gl')] + + else: + if self.vs_ver >= 14.0: + sdkver = self._sdk_subdir + else: + sdkver = '' + return [ + os.path.join(include, f'{sdkver}shared'), + os.path.join(include, f'{sdkver}um'), + os.path.join(include, f'{sdkver}winrt'), + ] + + @property + def OSLibpath(self): + """ + Microsoft Windows SDK Libraries Paths. + + Return + ------ + list of str + paths + """ + ref = os.path.join(self.si.WindowsSdkDir, 'References') + libpath = [] + + if self.vs_ver <= 9.0: + libpath += self.OSLibraries + + if self.vs_ver >= 11.0: + libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] + + if self.vs_ver >= 14.0: + libpath += [ + ref, + os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), + os.path.join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'), + os.path.join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'), + os.path.join( + ref, 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0' + ), + os.path.join( + self.si.WindowsSdkDir, + 'ExtensionSDKs', + 'Microsoft.VCLibs', + f'{self.vs_ver:0.1f}', + 'References', + 'CommonConfiguration', + 'neutral', + ), + ] + return libpath + + @property + def SdkTools(self): + """ + Microsoft Windows SDK Tools. + + Return + ------ + list of str + paths + """ + return list(self._sdk_tools()) + + def _sdk_tools(self): + """ + Microsoft Windows SDK Tools paths generator. + + Return + ------ + generator of str + paths + """ + if self.vs_ver < 15.0: + bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86' + yield os.path.join(self.si.WindowsSdkDir, bin_dir) + + if not self.pi.current_is_x86(): + arch_subdir = self.pi.current_dir(x64=True) + path = f'Bin{arch_subdir}' + yield os.path.join(self.si.WindowsSdkDir, path) + + if self.vs_ver in (10.0, 11.0): + if self.pi.target_is_x86(): + arch_subdir = '' + else: + arch_subdir = self.pi.current_dir(hidex86=True, x64=True) + path = rf'Bin\NETFX 4.0 Tools{arch_subdir}' + yield os.path.join(self.si.WindowsSdkDir, path) + + elif self.vs_ver >= 15.0: + path = os.path.join(self.si.WindowsSdkDir, 'Bin') + arch_subdir = self.pi.current_dir(x64=True) + sdkver = self.si.WindowsSdkLastVersion + yield os.path.join(path, f'{sdkver}{arch_subdir}') + + if self.si.WindowsSDKExecutablePath: + yield self.si.WindowsSDKExecutablePath + + @property + def _sdk_subdir(self) -> str: + """ + Microsoft Windows SDK version subdir. + + Return + ------ + str + subdir + """ + ucrtver = self.si.WindowsSdkLastVersion + return (f'{ucrtver}\\') if ucrtver else '' + + @property + def SdkSetup(self): + """ + Microsoft Windows SDK Setup. + + Return + ------ + list of str + paths + """ + if self.vs_ver > 9.0: + return [] + + return [os.path.join(self.si.WindowsSdkDir, 'Setup')] + + @property + def FxTools(self): + """ + Microsoft .NET Framework Tools. + + Return + ------ + list of str + paths + """ + pi = self.pi + si = self.si + + if self.vs_ver <= 10.0: + include32 = True + include64 = not pi.target_is_x86() and not pi.current_is_x86() + else: + include32 = pi.target_is_x86() or pi.current_is_x86() + include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' + + tools = [] + if include32: + tools += [ + os.path.join(si.FrameworkDir32, ver) for ver in si.FrameworkVersion32 + ] + if include64: + tools += [ + os.path.join(si.FrameworkDir64, ver) for ver in si.FrameworkVersion64 + ] + return tools + + @property + def NetFxSDKLibraries(self): + """ + Microsoft .Net Framework SDK Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: + return [] + + arch_subdir = self.pi.target_dir(x64=True) + return [os.path.join(self.si.NetFxSdkDir, rf'lib\um{arch_subdir}')] + + @property + def NetFxSDKIncludes(self): + """ + Microsoft .Net Framework SDK Includes. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: + return [] + + return [os.path.join(self.si.NetFxSdkDir, r'include\um')] + + @property + def VsTDb(self): + """ + Microsoft Visual Studio Team System Database. + + Return + ------ + list of str + paths + """ + return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')] + + @property + def MSBuild(self): + """ + Microsoft Build Engine. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 12.0: + return [] + elif self.vs_ver < 15.0: + base_path = self.si.ProgramFilesx86 + arch_subdir = self.pi.current_dir(hidex86=True) + else: + base_path = self.si.VSInstallDir + arch_subdir = '' + + path = rf'MSBuild\{self.vs_ver:0.1f}\bin{arch_subdir}' + build = [os.path.join(base_path, path)] + + if self.vs_ver >= 15.0: + # Add Roslyn C# & Visual Basic Compiler + build += [os.path.join(base_path, path, 'Roslyn')] + + return build + + @property + def HTMLHelpWorkshop(self): + """ + Microsoft HTML Help Workshop. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 11.0: + return [] + + return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')] + + @property + def UCRTLibraries(self) -> list[str]: + """ + Microsoft Universal C Runtime SDK Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0: + return [] + + arch_subdir = self.pi.target_dir(x64=True) + try: + lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib') # type: ignore[arg-type] # Expected TypeError + except TypeError as ex: + py310.add_note(ex, "Cannot find UniversalCRTSdkDir") + raise + ucrtver = self._ucrt_subdir + return [os.path.join(lib, f'{ucrtver}ucrt{arch_subdir}')] + + @property + def UCRTIncludes(self) -> list[str]: + """ + Microsoft Universal C Runtime SDK Include. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0: + return [] + + try: + include = os.path.join(self.si.UniversalCRTSdkDir, 'include') # type: ignore[arg-type] # Expected TypeError + except TypeError as ex: + py310.add_note(ex, "Cannot find UniversalCRTSdkDir") + raise + return [os.path.join(include, f'{self._ucrt_subdir}ucrt')] + + @property + def _ucrt_subdir(self) -> str: + """ + Microsoft Universal C Runtime SDK version subdir. + + Return + ------ + str + subdir + """ + ucrtver = self.si.UniversalCRTSdkLastVersion + return (f'{ucrtver}\\') if ucrtver else '' + + @property + def FSharp(self): + """ + Microsoft Visual F#. + + Return + ------ + list of str + paths + """ + if 11.0 > self.vs_ver > 12.0: + return [] + + return [self.si.FSharpInstallDir] + + @property + def VCRuntimeRedist(self) -> str | None: + """ + Microsoft Visual C++ runtime redistributable dll. + + Returns the first suitable path found or None. + """ + vcruntime = f'vcruntime{self.vc_ver}0.dll' + arch_subdir = self.pi.target_dir(x64=True).strip('\\') + + # Installation prefixes candidates + prefixes = [] + tools_path = self.si.VCInstallDir + redist_path = os.path.dirname(tools_path.replace(r'\Tools', r'\Redist')) + if os.path.isdir(redist_path): + # Redist version may not be exactly the same as tools + redist_path = os.path.join(redist_path, os.listdir(redist_path)[-1]) + prefixes += [redist_path, os.path.join(redist_path, 'onecore')] + + prefixes += [os.path.join(tools_path, 'redist')] # VS14 legacy path + + # CRT directory + crt_dirs = ( + f'Microsoft.VC{self.vc_ver * 10}.CRT', + # Sometime store in directory with VS version instead of VC + f'Microsoft.VC{int(self.vs_ver) * 10}.CRT', + ) + + # vcruntime path + candidate_paths = ( + os.path.join(prefix, arch_subdir, crt_dir, vcruntime) + for (prefix, crt_dir) in itertools.product(prefixes, crt_dirs) + ) + return next(filter(os.path.isfile, candidate_paths), None) # type: ignore[arg-type] #python/mypy#12682 + + def return_env(self, exists: bool = True) -> _EnvironmentDict: + """ + Return environment dict. + + Parameters + ---------- + exists: bool + It True, only return existing paths. + + Return + ------ + dict + environment + """ + env = _EnvironmentDict( + include=self._build_paths( + 'include', + [ + self.VCIncludes, + self.OSIncludes, + self.UCRTIncludes, + self.NetFxSDKIncludes, + ], + exists, + ), + lib=self._build_paths( + 'lib', + [ + self.VCLibraries, + self.OSLibraries, + self.FxTools, + self.UCRTLibraries, + self.NetFxSDKLibraries, + ], + exists, + ), + libpath=self._build_paths( + 'libpath', + [self.VCLibraries, self.FxTools, self.VCStoreRefs, self.OSLibpath], + exists, + ), + path=self._build_paths( + 'path', + [ + self.VCTools, + self.VSTools, + self.VsTDb, + self.SdkTools, + self.SdkSetup, + self.FxTools, + self.MSBuild, + self.HTMLHelpWorkshop, + self.FSharp, + ], + exists, + ), + ) + if self.vs_ver >= 14 and self.VCRuntimeRedist: + env['py_vcruntime_redist'] = self.VCRuntimeRedist + return env + + def _build_paths(self, name, spec_path_lists, exists): + """ + Given an environment variable name and specified paths, + return a pathsep-separated string of paths containing + unique, extant, directories from those paths and from + the environment variable. Raise an error if no paths + are resolved. + + Parameters + ---------- + name: str + Environment variable name + spec_path_lists: list of str + Paths + exists: bool + It True, only return existing paths. + + Return + ------ + str + Pathsep-separated paths + """ + # flatten spec_path_lists + spec_paths = itertools.chain.from_iterable(spec_path_lists) + env_paths = environ.get(name, '').split(os.pathsep) + paths = itertools.chain(spec_paths, env_paths) + extant_paths = list(filter(os.path.isdir, paths)) if exists else paths + if not extant_paths: + msg = f"{name.upper()} environment variable is empty" + raise distutils.errors.DistutilsPlatformError(msg) + unique_paths = unique_everseen(extant_paths) + return os.pathsep.join(unique_paths) diff --git a/lib/python3.12/site-packages/setuptools/namespaces.py b/lib/python3.12/site-packages/setuptools/namespaces.py new file mode 100644 index 0000000000000000000000000000000000000000..85ea2ebd654c480b8c19d1715b3772c4bcfd812e --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/namespaces.py @@ -0,0 +1,106 @@ +import itertools +import os + +from .compat import py312 + +from distutils import log + +flatten = itertools.chain.from_iterable + + +class Installer: + nspkg_ext = '-nspkg.pth' + + def install_namespaces(self) -> None: + nsp = self._get_all_ns_packages() + if not nsp: + return + filename = self._get_nspkg_file() + self.outputs.append(filename) + log.info("Installing %s", filename) + lines = map(self._gen_nspkg_line, nsp) + + if self.dry_run: + # always generate the lines, even in dry run + list(lines) + return + + with open(filename, 'wt', encoding=py312.PTH_ENCODING) as f: + # Python<3.13 requires encoding="locale" instead of "utf-8" + # See: python/cpython#77102 + f.writelines(lines) + + def uninstall_namespaces(self) -> None: + filename = self._get_nspkg_file() + if not os.path.exists(filename): + return + log.info("Removing %s", filename) + os.remove(filename) + + def _get_nspkg_file(self): + filename, _ = os.path.splitext(self._get_target()) + return filename + self.nspkg_ext + + def _get_target(self): + return self.target + + _nspkg_tmpl = ( + "import sys, types, os", + "p = os.path.join(%(root)s, *%(pth)r)", + "importlib = __import__('importlib.util')", + "__import__('importlib.machinery')", + ( + "m = " + "sys.modules.setdefault(%(pkg)r, " + "importlib.util.module_from_spec(" + "importlib.machinery.PathFinder.find_spec(%(pkg)r, " + "[os.path.dirname(p)])))" + ), + ("m = m or sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))"), + "mp = (m or []) and m.__dict__.setdefault('__path__',[])", + "(p not in mp) and mp.append(p)", + ) + "lines for the namespace installer" + + _nspkg_tmpl_multi = ('m and setattr(sys.modules[%(parent)r], %(child)r, m)',) + "additional line(s) when a parent package is indicated" + + def _get_root(self): + return "sys._getframe(1).f_locals['sitedir']" + + def _gen_nspkg_line(self, pkg): + pth = tuple(pkg.split('.')) + root = self._get_root() + tmpl_lines = self._nspkg_tmpl + parent, sep, child = pkg.rpartition('.') + if parent: + tmpl_lines += self._nspkg_tmpl_multi + return ';'.join(tmpl_lines) % locals() + '\n' + + def _get_all_ns_packages(self): + """Return sorted list of all package namespaces""" + pkgs = self.distribution.namespace_packages or [] + return sorted(set(flatten(map(self._pkg_names, pkgs)))) + + @staticmethod + def _pkg_names(pkg): + """ + Given a namespace package, yield the components of that + package. + + >>> names = Installer._pkg_names('a.b.c') + >>> set(names) == set(['a', 'a.b', 'a.b.c']) + True + """ + parts = pkg.split('.') + while parts: + yield '.'.join(parts) + parts.pop() + + +class DevelopInstaller(Installer): + def _get_root(self): + return repr(str(self.egg_path)) + + def _get_target(self): + return self.egg_link diff --git a/lib/python3.12/site-packages/setuptools/script (dev).tmpl b/lib/python3.12/site-packages/setuptools/script (dev).tmpl new file mode 100644 index 0000000000000000000000000000000000000000..39a24b04888e79df51e2237577b303a2f901be63 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/script (dev).tmpl @@ -0,0 +1,6 @@ +# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r +__requires__ = %(spec)r +__import__('pkg_resources').require(%(spec)r) +__file__ = %(dev_path)r +with open(__file__) as f: + exec(compile(f.read(), __file__, 'exec')) diff --git a/lib/python3.12/site-packages/setuptools/script.tmpl b/lib/python3.12/site-packages/setuptools/script.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..ff5efbcab3b58063dd84787181c26a95fb663d94 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/script.tmpl @@ -0,0 +1,3 @@ +# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r +__requires__ = %(spec)r +__import__('pkg_resources').run_script(%(spec)r, %(script_name)r) diff --git a/lib/python3.12/site-packages/setuptools/tests/__init__.py b/lib/python3.12/site-packages/setuptools/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..eb70bfb7115a2a94a8b942b31cafc3a550f0c005 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/__init__.py @@ -0,0 +1,13 @@ +import locale +import sys + +import pytest + +__all__ = ['fail_on_ascii'] + +if sys.version_info >= (3, 11): + locale_encoding = locale.getencoding() +else: + locale_encoding = locale.getpreferredencoding(False) +is_ascii = locale_encoding == 'ANSI_X3.4-1968' +fail_on_ascii = pytest.mark.xfail(is_ascii, reason="Test fails in this locale") diff --git a/lib/python3.12/site-packages/setuptools/tests/config/__init__.py b/lib/python3.12/site-packages/setuptools/tests/config/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_apply_pyprojecttoml.cpython-312.pyc b/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_apply_pyprojecttoml.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b907946e36ee8a4d92f58c7dec657ed1030b1e58 Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_apply_pyprojecttoml.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_expand.cpython-312.pyc b/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_expand.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..adf1e275d1d1b8d465f4d04523a1202e626dbc52 Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_expand.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_pyprojecttoml_dynamic_deps.cpython-312.pyc b/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_pyprojecttoml_dynamic_deps.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a7206f1faa3cbd2eb0639deb105076313aa440a Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_pyprojecttoml_dynamic_deps.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_setupcfg.cpython-312.pyc b/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_setupcfg.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02efd092af4c84b01a7a7b2bfebf0e1147be5795 Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/tests/config/__pycache__/test_setupcfg.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/setuptools/tests/config/downloads/__init__.py b/lib/python3.12/site-packages/setuptools/tests/config/downloads/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..00a16423f448e7773f5e6dc9b365efe80f40a778 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/config/downloads/__init__.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import re +import time +from pathlib import Path +from urllib.error import HTTPError +from urllib.request import urlopen + +__all__ = ["DOWNLOAD_DIR", "retrieve_file", "output_file", "urls_from_file"] + + +NAME_REMOVE = ("http://", "https://", "github.com/", "/raw/") +DOWNLOAD_DIR = Path(__file__).parent + + +# ---------------------------------------------------------------------- +# Please update ./preload.py accordingly when modifying this file +# ---------------------------------------------------------------------- + + +def output_file(url: str, download_dir: Path = DOWNLOAD_DIR) -> Path: + file_name = url.strip() + for part in NAME_REMOVE: + file_name = file_name.replace(part, '').strip().strip('/:').strip() + return Path(download_dir, re.sub(r"[^\-_\.\w\d]+", "_", file_name)) + + +def retrieve_file(url: str, download_dir: Path = DOWNLOAD_DIR, wait: float = 5) -> Path: + path = output_file(url, download_dir) + if path.exists(): + print(f"Skipping {url} (already exists: {path})") + else: + download_dir.mkdir(exist_ok=True, parents=True) + print(f"Downloading {url} to {path}") + try: + download(url, path) + except HTTPError: + time.sleep(wait) # wait a few seconds and try again. + download(url, path) + return path + + +def urls_from_file(list_file: Path) -> list[str]: + """``list_file`` should be a text file where each line corresponds to a URL to + download. + """ + print(f"file: {list_file}") + content = list_file.read_text(encoding="utf-8") + return [url for url in content.splitlines() if not url.startswith("#")] + + +def download(url: str, dest: Path): + with urlopen(url) as f: + data = f.read() + + with open(dest, "wb") as f: + f.write(data) + + assert Path(dest).exists() diff --git a/lib/python3.12/site-packages/setuptools/tests/config/downloads/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/setuptools/tests/config/downloads/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab1712336be1199853da7ee5ad84192a22c1939e Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/tests/config/downloads/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/setuptools/tests/config/downloads/__pycache__/preload.cpython-312.pyc b/lib/python3.12/site-packages/setuptools/tests/config/downloads/__pycache__/preload.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b9f655b615eb8f34238a3f3c552d4158cf16b70 Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/tests/config/downloads/__pycache__/preload.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/setuptools/tests/config/downloads/preload.py b/lib/python3.12/site-packages/setuptools/tests/config/downloads/preload.py new file mode 100644 index 0000000000000000000000000000000000000000..8eeb5dd75d3dcb375cee5acaf11ad385084bff5a --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/config/downloads/preload.py @@ -0,0 +1,18 @@ +"""This file can be used to preload files needed for testing. + +For example you can use:: + + cd setuptools/tests/config + python -m downloads.preload setupcfg_examples.txt + +to make sure the `setup.cfg` examples are downloaded before starting the tests. +""" + +import sys +from pathlib import Path + +from . import retrieve_file, urls_from_file + +if __name__ == "__main__": + urls = urls_from_file(Path(sys.argv[1])) + list(map(retrieve_file, urls)) diff --git a/lib/python3.12/site-packages/setuptools/tests/config/setupcfg_examples.txt b/lib/python3.12/site-packages/setuptools/tests/config/setupcfg_examples.txt new file mode 100644 index 0000000000000000000000000000000000000000..6aab887ff1fe631d97f1abea90a8448040746a12 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/config/setupcfg_examples.txt @@ -0,0 +1,22 @@ +# ==================================================================== +# Some popular packages that use setup.cfg (and others not so popular) +# Reference: https://hugovk.github.io/top-pypi-packages/ +# ==================================================================== +https://github.com/pypa/setuptools/raw/52c990172fec37766b3566679724aa8bf70ae06d/setup.cfg +https://github.com/pypa/wheel/raw/0acd203cd896afec7f715aa2ff5980a403459a3b/setup.cfg +https://github.com/python/importlib_metadata/raw/2f05392ca980952a6960d82b2f2d2ea10aa53239/setup.cfg +https://github.com/jaraco/skeleton/raw/d9008b5c510cd6969127a6a2ab6f832edddef296/setup.cfg +https://github.com/jaraco/zipp/raw/700d3a96390e970b6b962823bfea78b4f7e1c537/setup.cfg +https://github.com/pallets/jinja/raw/7d72eb7fefb7dce065193967f31f805180508448/setup.cfg +https://github.com/tkem/cachetools/raw/2fd87a94b8d3861d80e9e4236cd480bfdd21c90d/setup.cfg +https://github.com/aio-libs/aiohttp/raw/5e0e6b7080f2408d5f1dd544c0e1cf88378b7b10/setup.cfg +https://github.com/pallets/flask/raw/9486b6cf57bd6a8a261f67091aca8ca78eeec1e3/setup.cfg +https://github.com/pallets/click/raw/6411f425fae545f42795665af4162006b36c5e4a/setup.cfg +https://github.com/sqlalchemy/sqlalchemy/raw/533f5718904b620be8d63f2474229945d6f8ba5d/setup.cfg +https://github.com/pytest-dev/pluggy/raw/461ef63291d13589c4e21aa182cd1529257e9a0a/setup.cfg +https://github.com/pytest-dev/pytest/raw/c7be96dae487edbd2f55b561b31b68afac1dabe6/setup.cfg +https://github.com/platformdirs/platformdirs/raw/7b7852128dd6f07511b618d6edea35046bd0c6ff/setup.cfg +https://github.com/pandas-dev/pandas/raw/bc17343f934a33dc231c8c74be95d8365537c376/setup.cfg +https://github.com/django/django/raw/4e249d11a6e56ca8feb4b055b681cec457ef3a3d/setup.cfg +https://github.com/pyscaffold/pyscaffold/raw/de7aa5dc059fbd04307419c667cc4961bc9df4b8/setup.cfg +https://github.com/pypa/virtualenv/raw/f92eda6e3da26a4d28c2663ffb85c4960bdb990c/setup.cfg diff --git a/lib/python3.12/site-packages/setuptools/tests/config/test_apply_pyprojecttoml.py b/lib/python3.12/site-packages/setuptools/tests/config/test_apply_pyprojecttoml.py new file mode 100644 index 0000000000000000000000000000000000000000..8f48c4316d9e9c3d09fe2abda9bd4aab63d90355 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/config/test_apply_pyprojecttoml.py @@ -0,0 +1,774 @@ +"""Make sure that applying the configuration from pyproject.toml is equivalent to +applying a similar configuration from setup.cfg + +To run these tests offline, please have a look on ``./downloads/preload.py`` +""" + +from __future__ import annotations + +import io +import re +import tarfile +from inspect import cleandoc +from pathlib import Path +from unittest.mock import Mock + +import pytest +from ini2toml.api import LiteTranslator +from packaging.metadata import Metadata + +import setuptools # noqa: F401 # ensure monkey patch to metadata +from setuptools._static import is_static +from setuptools.command.egg_info import write_requirements +from setuptools.config import expand, pyprojecttoml, setupcfg +from setuptools.config._apply_pyprojecttoml import _MissingDynamic, _some_attrgetter +from setuptools.dist import Distribution +from setuptools.errors import InvalidConfigError, RemovedConfigError +from setuptools.warnings import InformationOnly, SetuptoolsDeprecationWarning + +from .downloads import retrieve_file, urls_from_file + +HERE = Path(__file__).parent +EXAMPLES_FILE = "setupcfg_examples.txt" + + +def makedist(path, **attrs): + return Distribution({"src_root": path, **attrs}) + + +def _mock_expand_patterns(patterns, *_, **__): + """ + Allow comparing the given patterns for 2 dist objects. + We need to strip special chars to avoid errors when validating. + """ + return [ + re.sub("[^a-z0-9]+", "", p, flags=re.IGNORECASE) or "empty" for p in patterns + ] + + +@pytest.mark.parametrize("url", urls_from_file(HERE / EXAMPLES_FILE)) +@pytest.mark.filterwarnings("ignore") +@pytest.mark.uses_network +def test_apply_pyproject_equivalent_to_setupcfg(url, monkeypatch, tmp_path): + monkeypatch.setattr(expand, "read_attr", Mock(return_value="0.0.1")) + monkeypatch.setattr( + Distribution, "_expand_patterns", Mock(side_effect=_mock_expand_patterns) + ) + setupcfg_example = retrieve_file(url) + pyproject_example = Path(tmp_path, "pyproject.toml") + setupcfg_text = setupcfg_example.read_text(encoding="utf-8") + toml_config = LiteTranslator().translate(setupcfg_text, "setup.cfg") + pyproject_example.write_text(toml_config, encoding="utf-8") + + dist_toml = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject_example) + dist_cfg = setupcfg.apply_configuration(makedist(tmp_path), setupcfg_example) + + pkg_info_toml = core_metadata(dist_toml) + pkg_info_cfg = core_metadata(dist_cfg) + assert pkg_info_toml == pkg_info_cfg + + if any(getattr(d, "license_files", None) for d in (dist_toml, dist_cfg)): + assert set(dist_toml.license_files) == set(dist_cfg.license_files) + + if any(getattr(d, "entry_points", None) for d in (dist_toml, dist_cfg)): + print(dist_cfg.entry_points) + ep_toml = { + (k, *sorted(i.replace(" ", "") for i in v)) + for k, v in dist_toml.entry_points.items() + } + ep_cfg = { + (k, *sorted(i.replace(" ", "") for i in v)) + for k, v in dist_cfg.entry_points.items() + } + assert ep_toml == ep_cfg + + if any(getattr(d, "package_data", None) for d in (dist_toml, dist_cfg)): + pkg_data_toml = {(k, *sorted(v)) for k, v in dist_toml.package_data.items()} + pkg_data_cfg = {(k, *sorted(v)) for k, v in dist_cfg.package_data.items()} + assert pkg_data_toml == pkg_data_cfg + + if any(getattr(d, "data_files", None) for d in (dist_toml, dist_cfg)): + data_files_toml = {(k, *sorted(v)) for k, v in dist_toml.data_files} + data_files_cfg = {(k, *sorted(v)) for k, v in dist_cfg.data_files} + assert data_files_toml == data_files_cfg + + assert set(dist_toml.install_requires) == set(dist_cfg.install_requires) + if any(getattr(d, "extras_require", None) for d in (dist_toml, dist_cfg)): + extra_req_toml = {(k, *sorted(v)) for k, v in dist_toml.extras_require.items()} + extra_req_cfg = {(k, *sorted(v)) for k, v in dist_cfg.extras_require.items()} + assert extra_req_toml == extra_req_cfg + + +PEP621_EXAMPLE = """\ +[project] +name = "spam" +version = "2020.0.0" +description = "Lovely Spam! Wonderful Spam!" +readme = "README.rst" +requires-python = ">=3.8" +license-files = ["LICENSE.txt"] # Updated to be PEP 639 compliant +keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"] +authors = [ + {email = "hi@pradyunsg.me"}, + {name = "Tzu-Ping Chung"} +] +maintainers = [ + {name = "Brett Cannon", email = "brett@python.org"}, + {name = "John X. Ãørçeč", email = "john@utf8.org"}, + {name = "Γαμα קּ 東", email = "gama@utf8.org"}, +] +classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python" +] + +dependencies = [ + "httpx", + "gidgethub[httpx]>4.0.0", + "django>2.1; os_name != 'nt'", + "django>2.0; os_name == 'nt'" +] + +[project.optional-dependencies] +test = [ + "pytest < 5.0.0", + "pytest-cov[all]" +] + +[project.urls] +homepage = "http://example.com" +documentation = "http://readthedocs.org" +repository = "http://github.com" +changelog = "http://github.com/me/spam/blob/master/CHANGELOG.md" + +[project.scripts] +spam-cli = "spam:main_cli" + +[project.gui-scripts] +spam-gui = "spam:main_gui" + +[project.entry-points."spam.magical"] +tomatoes = "spam:main_tomatoes" +""" + +PEP621_INTERNATIONAL_EMAIL_EXAMPLE = """\ +[project] +name = "spam" +version = "2020.0.0" +authors = [ + {email = "hi@pradyunsg.me"}, + {name = "Tzu-Ping Chung"} +] +maintainers = [ + {name = "अंकित अहलावत", email = "ankit@example.com"}, +] +""" + +PEP621_EXAMPLE_SCRIPT = """ +def main_cli(): pass +def main_gui(): pass +def main_tomatoes(): pass +""" + +PEP639_LICENSE_TEXT = """\ +[project] +name = "spam" +version = "2020.0.0" +authors = [ + {email = "hi@pradyunsg.me"}, + {name = "Tzu-Ping Chung"} +] +license = {text = "MIT"} +""" + +PEP639_LICENSE_EXPRESSION = """\ +[project] +name = "spam" +version = "2020.0.0" +authors = [ + {email = "hi@pradyunsg.me"}, + {name = "Tzu-Ping Chung"} +] +license = "mit or apache-2.0" # should be normalized in metadata +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Programming Language :: Python", +] +""" + + +def _pep621_example_project( + tmp_path, + readme="README.rst", + pyproject_text=PEP621_EXAMPLE, +): + pyproject = tmp_path / "pyproject.toml" + text = pyproject_text + replacements = {'readme = "README.rst"': f'readme = "{readme}"'} + for orig, subst in replacements.items(): + text = text.replace(orig, subst) + pyproject.write_text(text, encoding="utf-8") + + (tmp_path / readme).write_text("hello world", encoding="utf-8") + (tmp_path / "LICENSE.txt").write_text("--- LICENSE stub ---", encoding="utf-8") + (tmp_path / "spam.py").write_text(PEP621_EXAMPLE_SCRIPT, encoding="utf-8") + return pyproject + + +def test_pep621_example(tmp_path): + """Make sure the example in PEP 621 works""" + pyproject = _pep621_example_project(tmp_path) + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + assert set(dist.metadata.license_files) == {"LICENSE.txt"} + + +@pytest.mark.parametrize( + ("readme", "ctype"), + [ + ("Readme.txt", "text/plain"), + ("readme.md", "text/markdown"), + ("text.rst", "text/x-rst"), + ], +) +def test_readme_content_type(tmp_path, readme, ctype): + pyproject = _pep621_example_project(tmp_path, readme) + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + assert dist.metadata.long_description_content_type == ctype + + +def test_undefined_content_type(tmp_path): + pyproject = _pep621_example_project(tmp_path, "README.tex") + with pytest.raises(ValueError, match="Undefined content type for README.tex"): + pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + + +def test_no_explicit_content_type_for_missing_extension(tmp_path): + pyproject = _pep621_example_project(tmp_path, "README") + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + assert dist.metadata.long_description_content_type is None + + +@pytest.mark.parametrize( + ("pyproject_text", "expected_maintainers_meta_value"), + ( + pytest.param( + PEP621_EXAMPLE, + ( + 'Brett Cannon , "John X. Ãørçeč" , ' + 'Γαμα קּ 東 ' + ), + id='non-international-emails', + ), + pytest.param( + PEP621_INTERNATIONAL_EMAIL_EXAMPLE, + 'Ankit Ahlawat <अंकित@उदाहरण.भारत>', + marks=pytest.mark.xfail( + reason="CPython's `email.headerregistry.Address` only supports " + 'RFC 5322, as of Oct 20, 2025 and latest Python 3.13.0', + strict=True, + ), + id='international-email', + ), + ), +) +def test_utf8_maintainer_in_metadata( # issue-3663 + expected_maintainers_meta_value, + pyproject_text, + tmp_path, +): + pyproject = _pep621_example_project( + tmp_path, + "README", + pyproject_text=pyproject_text, + ) + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + assert dist.metadata.maintainer_email == expected_maintainers_meta_value + pkg_file = tmp_path / "PKG-FILE" + with open(pkg_file, "w", encoding="utf-8") as fh: + dist.metadata.write_pkg_file(fh) + content = pkg_file.read_text(encoding="utf-8") + assert f"Maintainer-email: {expected_maintainers_meta_value}" in content + + +@pytest.mark.parametrize( + ( + 'pyproject_text', + 'license', + 'license_expression', + 'content_str', + 'not_content_str', + ), + ( + pytest.param( + PEP639_LICENSE_TEXT, + 'MIT', + None, + 'License: MIT', + 'License-Expression: ', + id='license-text', + marks=[ + pytest.mark.filterwarnings( + "ignore:.project.license. as a TOML table is deprecated", + ) + ], + ), + pytest.param( + PEP639_LICENSE_EXPRESSION, + None, + 'MIT OR Apache-2.0', + 'License-Expression: MIT OR Apache-2.0', + 'License: ', + id='license-expression', + ), + ), +) +def test_license_in_metadata( + license, + license_expression, + content_str, + not_content_str, + pyproject_text, + tmp_path, +): + pyproject = _pep621_example_project( + tmp_path, + "README", + pyproject_text=pyproject_text, + ) + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + assert dist.metadata.license == license + assert dist.metadata.license_expression == license_expression + pkg_file = tmp_path / "PKG-FILE" + with open(pkg_file, "w", encoding="utf-8") as fh: + dist.metadata.write_pkg_file(fh) + content = pkg_file.read_text(encoding="utf-8") + assert "Metadata-Version: 2.4" in content + assert content_str in content + assert not_content_str not in content + + +def test_license_classifier_with_license_expression(tmp_path): + text = PEP639_LICENSE_EXPRESSION.rsplit("\n", 2)[0] + pyproject = _pep621_example_project( + tmp_path, + "README", + f"{text}\n \"License :: OSI Approved :: MIT License\"\n]", + ) + msg = "License classifiers have been superseded by license expressions" + with pytest.raises(InvalidConfigError, match=msg) as exc: + pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + + assert "License :: OSI Approved :: MIT License" in str(exc.value) + + +def test_license_classifier_without_license_expression(tmp_path): + text = """\ + [project] + name = "spam" + version = "2020.0.0" + license = {text = "mit or apache-2.0"} + classifiers = ["License :: OSI Approved :: MIT License"] + """ + pyproject = _pep621_example_project(tmp_path, "README", text) + + msg1 = "License classifiers are deprecated(?:.|\n)*MIT License" + msg2 = ".project.license. as a TOML table is deprecated" + with ( + pytest.warns(SetuptoolsDeprecationWarning, match=msg1), + pytest.warns(SetuptoolsDeprecationWarning, match=msg2), + ): + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + + # Check license classifier is still included + assert dist.metadata.get_classifiers() == ["License :: OSI Approved :: MIT License"] + + +class TestLicenseFiles: + def base_pyproject( + self, + tmp_path, + additional_text="", + license_toml='license = {file = "LICENSE.txt"}\n', + ): + text = PEP639_LICENSE_EXPRESSION + + # Sanity-check + assert 'license = "mit or apache-2.0"' in text + assert 'license-files' not in text + assert "[tool.setuptools]" not in text + + text = re.sub( + r"(license = .*)\n", + license_toml, + text, + count=1, + ) + assert license_toml in text # sanity check + text = f"{text}\n{additional_text}\n" + pyproject = _pep621_example_project(tmp_path, "README", pyproject_text=text) + return pyproject + + def base_pyproject_license_pep639(self, tmp_path, additional_text=""): + return self.base_pyproject( + tmp_path, + additional_text=additional_text, + license_toml='license = "licenseref-Proprietary"' + '\nlicense-files = ["_FILE*"]\n', + ) + + def test_both_license_and_license_files_defined(self, tmp_path): + setuptools_config = '[tool.setuptools]\nlicense-files = ["_FILE*"]' + pyproject = self.base_pyproject(tmp_path, setuptools_config) + + (tmp_path / "_FILE.txt").touch() + (tmp_path / "_FILE.rst").touch() + + # Would normally match the `license_files` patterns, but we want to exclude it + # by being explicit. On the other hand, contents should be added to `license` + license = tmp_path / "LICENSE.txt" + license.write_text("LicenseRef-Proprietary\n", encoding="utf-8") + + msg1 = "'tool.setuptools.license-files' is deprecated in favor of 'project.license-files'" + msg2 = ".project.license. as a TOML table is deprecated" + with ( + pytest.warns(SetuptoolsDeprecationWarning, match=msg1), + pytest.warns(SetuptoolsDeprecationWarning, match=msg2), + ): + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + assert set(dist.metadata.license_files) == {"_FILE.rst", "_FILE.txt"} + assert dist.metadata.license == "LicenseRef-Proprietary\n" + + def test_both_license_and_license_files_defined_pep639(self, tmp_path): + # Set license and license-files + pyproject = self.base_pyproject_license_pep639(tmp_path) + + (tmp_path / "_FILE.txt").touch() + (tmp_path / "_FILE.rst").touch() + + msg = "Normalizing.*LicenseRef" + with pytest.warns(InformationOnly, match=msg): + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + + assert set(dist.metadata.license_files) == {"_FILE.rst", "_FILE.txt"} + assert dist.metadata.license is None + assert dist.metadata.license_expression == "LicenseRef-Proprietary" + + def test_license_files_defined_twice(self, tmp_path): + # Set project.license-files and tools.setuptools.license-files + setuptools_config = '[tool.setuptools]\nlicense-files = ["_FILE*"]' + pyproject = self.base_pyproject_license_pep639(tmp_path, setuptools_config) + + msg = "'project.license-files' is defined already. Remove 'tool.setuptools.license-files'" + with pytest.raises(InvalidConfigError, match=msg): + pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + + def test_default_patterns(self, tmp_path): + setuptools_config = '[tool.setuptools]\nzip-safe = false' + # ^ used just to trigger section validation + pyproject = self.base_pyproject(tmp_path, setuptools_config, license_toml="") + + license_files = "LICENCE-a.html COPYING-abc.txt AUTHORS-xyz NOTICE,def".split() + + for fname in license_files: + (tmp_path / fname).write_text(f"{fname}\n", encoding="utf-8") + + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + + assert (tmp_path / "LICENSE.txt").exists() # from base example + assert set(dist.metadata.license_files) == {*license_files, "LICENSE.txt"} + + def test_missing_patterns(self, tmp_path): + pyproject = self.base_pyproject_license_pep639(tmp_path) + assert list(tmp_path.glob("_FILE*")) == [] # sanity check + + msg1 = "Cannot find any files for the given pattern.*" + msg2 = "Normalizing 'licenseref-Proprietary' to 'LicenseRef-Proprietary'" + with ( + pytest.warns(SetuptoolsDeprecationWarning, match=msg1), + pytest.warns(InformationOnly, match=msg2), + ): + pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + + def test_deprecated_file_expands_to_text(self, tmp_path): + """Make sure the old example with ``license = {text = ...}`` works""" + + assert 'license-files = ["LICENSE.txt"]' in PEP621_EXAMPLE # sanity check + text = PEP621_EXAMPLE.replace( + 'license-files = ["LICENSE.txt"]', + 'license = {file = "LICENSE.txt"}', + ) + pyproject = _pep621_example_project(tmp_path, pyproject_text=text) + + msg = ".project.license. as a TOML table is deprecated" + with pytest.warns(SetuptoolsDeprecationWarning, match=msg): + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + + assert dist.metadata.license == "--- LICENSE stub ---" + assert set(dist.metadata.license_files) == {"LICENSE.txt"} # auto-filled + + +class TestPyModules: + # https://github.com/pypa/setuptools/issues/4316 + + def dist(self, name): + toml_config = f""" + [project] + name = "test" + version = "42.0" + [tool.setuptools] + py-modules = [{name!r}] + """ + pyproject = Path("pyproject.toml") + pyproject.write_text(cleandoc(toml_config), encoding="utf-8") + return pyprojecttoml.apply_configuration(Distribution({}), pyproject) + + @pytest.mark.parametrize("module", ["pip-run", "abc-d.λ-xyz-e"]) + def test_valid_module_name(self, tmp_path, monkeypatch, module): + monkeypatch.chdir(tmp_path) + assert module in self.dist(module).py_modules + + @pytest.mark.parametrize("module", ["pip run", "-pip-run", "pip-run-stubs"]) + def test_invalid_module_name(self, tmp_path, monkeypatch, module): + monkeypatch.chdir(tmp_path) + with pytest.raises(ValueError, match="py-modules"): + self.dist(module).py_modules + + +class TestExtModules: + def test_pyproject_sets_attribute(self, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + pyproject = Path("pyproject.toml") + toml_config = """ + [project] + name = "test" + version = "42.0" + [tool.setuptools] + ext-modules = [ + {name = "my.ext", sources = ["hello.c", "world.c"]} + ] + """ + pyproject.write_text(cleandoc(toml_config), encoding="utf-8") + with pytest.warns(pyprojecttoml._ExperimentalConfiguration): + dist = pyprojecttoml.apply_configuration(Distribution({}), pyproject) + assert len(dist.ext_modules) == 1 + assert dist.ext_modules[0].name == "my.ext" + assert set(dist.ext_modules[0].sources) == {"hello.c", "world.c"} + + +class TestDeprecatedFields: + def test_namespace_packages(self, tmp_path): + pyproject = tmp_path / "pyproject.toml" + config = """ + [project] + name = "myproj" + version = "42" + [tool.setuptools] + namespace-packages = ["myproj.pkg"] + """ + pyproject.write_text(cleandoc(config), encoding="utf-8") + with pytest.raises(RemovedConfigError, match="namespace-packages"): + pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + + +class TestPresetField: + def pyproject(self, tmp_path, dynamic, extra_content=""): + content = f"[project]\nname = 'proj'\ndynamic = {dynamic!r}\n" + if "version" not in dynamic: + content += "version = '42'\n" + file = tmp_path / "pyproject.toml" + file.write_text(content + extra_content, encoding="utf-8") + return file + + @pytest.mark.parametrize( + ("attr", "field", "value"), + [ + ("license_expression", "license", "MIT"), + pytest.param( + *("license", "license", "Not SPDX"), + marks=[pytest.mark.filterwarnings("ignore:.*license. overwritten")], + ), + ("classifiers", "classifiers", ["Private :: Classifier"]), + ("entry_points", "scripts", {"console_scripts": ["foobar=foobar:main"]}), + ("entry_points", "gui-scripts", {"gui_scripts": ["bazquux=bazquux:main"]}), + pytest.param( + *("install_requires", "dependencies", ["six"]), + marks=[ + pytest.mark.filterwarnings("ignore:.*install_requires. overwritten") + ], + ), + ], + ) + def test_not_listed_in_dynamic(self, tmp_path, attr, field, value): + """Setuptools cannot set a field if not listed in ``dynamic``""" + pyproject = self.pyproject(tmp_path, []) + dist = makedist(tmp_path, **{attr: value}) + msg = re.compile(f"defined outside of `pyproject.toml`:.*{field}", re.DOTALL) + with pytest.warns(_MissingDynamic, match=msg): + dist = pyprojecttoml.apply_configuration(dist, pyproject) + + dist_value = _some_attrgetter(f"metadata.{attr}", attr)(dist) + assert not dist_value + + @pytest.mark.parametrize( + ("attr", "field", "value"), + [ + ("license_expression", "license", "MIT"), + ("install_requires", "dependencies", []), + ("extras_require", "optional-dependencies", {}), + ("install_requires", "dependencies", ["six"]), + ("classifiers", "classifiers", ["Private :: Classifier"]), + ], + ) + def test_listed_in_dynamic(self, tmp_path, attr, field, value): + pyproject = self.pyproject(tmp_path, [field]) + dist = makedist(tmp_path, **{attr: value}) + dist = pyprojecttoml.apply_configuration(dist, pyproject) + dist_value = _some_attrgetter(f"metadata.{attr}", attr)(dist) + assert dist_value == value + + def test_license_files_exempt_from_dynamic(self, monkeypatch, tmp_path): + """ + license-file is currently not considered in the context of dynamic. + As per 2025-02-19, https://packaging.python.org/en/latest/specifications/pyproject-toml/#license-files + allows setuptools to fill-in `license-files` the way it sees fit: + + > If the license-files key is not defined, tools can decide how to handle license files. + > For example they can choose not to include any files or use their own + > logic to discover the appropriate files in the distribution. + + Using license_files from setup.py to fill-in the value is in accordance + with this rule. + """ + monkeypatch.chdir(tmp_path) + pyproject = self.pyproject(tmp_path, []) + dist = makedist(tmp_path, license_files=["LIC*"]) + (tmp_path / "LIC1").write_text("42", encoding="utf-8") + dist = pyprojecttoml.apply_configuration(dist, pyproject) + assert dist.metadata.license_files == ["LIC1"] + + def test_warning_overwritten_dependencies(self, tmp_path): + src = "[project]\nname='pkg'\nversion='0.1'\ndependencies=['click']\n" + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text(src, encoding="utf-8") + dist = makedist(tmp_path, install_requires=["wheel"]) + with pytest.warns(match="`install_requires` overwritten"): + dist = pyprojecttoml.apply_configuration(dist, pyproject) + assert "wheel" not in dist.install_requires + + def test_optional_dependencies_dont_remove_env_markers(self, tmp_path): + """ + Internally setuptools converts dependencies with markers to "extras". + If ``install_requires`` is given by ``setup.py``, we have to ensure that + applying ``optional-dependencies`` does not overwrite the mandatory + dependencies with markers (see #3204). + """ + # If setuptools replace its internal mechanism that uses `requires.txt` + # this test has to be rewritten to adapt accordingly + extra = "\n[project.optional-dependencies]\nfoo = ['bar>1']\n" + pyproject = self.pyproject(tmp_path, ["dependencies"], extra) + install_req = ['importlib-resources (>=3.0.0) ; python_version < "3.7"'] + dist = makedist(tmp_path, install_requires=install_req) + dist = pyprojecttoml.apply_configuration(dist, pyproject) + assert "foo" in dist.extras_require + egg_info = dist.get_command_obj("egg_info") + write_requirements(egg_info, tmp_path, tmp_path / "requires.txt") + reqs = (tmp_path / "requires.txt").read_text(encoding="utf-8") + assert "importlib-resources" in reqs + assert "bar" in reqs + assert ':python_version < "3.7"' in reqs + + @pytest.mark.parametrize( + ("field", "group"), + [("scripts", "console_scripts"), ("gui-scripts", "gui_scripts")], + ) + @pytest.mark.filterwarnings("error") + def test_scripts_dont_require_dynamic_entry_points(self, tmp_path, field, group): + # Issue 3862 + pyproject = self.pyproject(tmp_path, [field]) + dist = makedist(tmp_path, entry_points={group: ["foobar=foobar:main"]}) + dist = pyprojecttoml.apply_configuration(dist, pyproject) + assert group in dist.entry_points + + +class TestMeta: + def test_example_file_in_sdist(self, setuptools_sdist): + """Meta test to ensure tests can run from sdist""" + with tarfile.open(setuptools_sdist) as tar: + assert any(name.endswith(EXAMPLES_FILE) for name in tar.getnames()) + + +class TestInteropCommandLineParsing: + def test_version(self, tmp_path, monkeypatch, capsys): + # See pypa/setuptools#4047 + # This test can be removed once the CLI interface of setup.py is removed + monkeypatch.chdir(tmp_path) + toml_config = """ + [project] + name = "test" + version = "42.0" + """ + pyproject = Path(tmp_path, "pyproject.toml") + pyproject.write_text(cleandoc(toml_config), encoding="utf-8") + opts = {"script_args": ["--version"]} + dist = pyprojecttoml.apply_configuration(Distribution(opts), pyproject) + dist.parse_command_line() # <-- there should be no exception here. + captured = capsys.readouterr() + assert "42.0" in captured.out + + +class TestStaticConfig: + def test_mark_static_fields(self, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + toml_config = """ + [project] + name = "test" + version = "42.0" + dependencies = ["hello"] + keywords = ["world"] + classifiers = ["private :: hello world"] + [tool.setuptools] + obsoletes = ["abcd"] + provides = ["abcd"] + platforms = ["abcd"] + """ + pyproject = Path(tmp_path, "pyproject.toml") + pyproject.write_text(cleandoc(toml_config), encoding="utf-8") + dist = pyprojecttoml.apply_configuration(Distribution({}), pyproject) + assert is_static(dist.install_requires) + assert is_static(dist.metadata.keywords) + assert is_static(dist.metadata.classifiers) + assert is_static(dist.metadata.obsoletes) + assert is_static(dist.metadata.provides) + assert is_static(dist.metadata.platforms) + + +# --- Auxiliary Functions --- + + +def core_metadata(dist) -> str: + with io.StringIO() as buffer: + dist.metadata.write_pkg_file(buffer) + pkg_file_txt = buffer.getvalue() + + # Make sure core metadata is valid + Metadata.from_email(pkg_file_txt, validate=True) # can raise exceptions + + skip_prefixes: tuple[str, ...] = () + skip_lines = set() + # ---- DIFF NORMALISATION ---- + # PEP 621 is very particular about author/maintainer metadata conversion, so skip + skip_prefixes += ("Author:", "Author-email:", "Maintainer:", "Maintainer-email:") + # May be redundant with Home-page + skip_prefixes += ("Project-URL: Homepage,", "Home-page:") + # May be missing in original (relying on default) but backfilled in the TOML + skip_prefixes += ("Description-Content-Type:",) + # Remove empty lines + skip_lines.add("") + + result = [] + for line in pkg_file_txt.splitlines(): + if line.startswith(skip_prefixes) or line in skip_lines: + continue + result.append(line + "\n") + + return "".join(result) diff --git a/lib/python3.12/site-packages/setuptools/tests/config/test_expand.py b/lib/python3.12/site-packages/setuptools/tests/config/test_expand.py new file mode 100644 index 0000000000000000000000000000000000000000..c5710ec63d7d9d4ed7b709203bb2fc4b512f2093 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/config/test_expand.py @@ -0,0 +1,247 @@ +import os +import sys +from pathlib import Path + +import pytest + +from setuptools._static import is_static +from setuptools.config import expand +from setuptools.discovery import find_package_path + +from distutils.errors import DistutilsOptionError + + +def write_files(files, root_dir): + for file, content in files.items(): + path = root_dir / file + path.parent.mkdir(exist_ok=True, parents=True) + path.write_text(content, encoding="utf-8") + + +def test_glob_relative(tmp_path, monkeypatch): + files = { + "dir1/dir2/dir3/file1.txt", + "dir1/dir2/file2.txt", + "dir1/file3.txt", + "a.ini", + "b.ini", + "dir1/c.ini", + "dir1/dir2/a.ini", + } + + write_files({k: "" for k in files}, tmp_path) + patterns = ["**/*.txt", "[ab].*", "**/[ac].ini"] + monkeypatch.chdir(tmp_path) + assert set(expand.glob_relative(patterns)) == files + # Make sure the same APIs work outside cwd + assert set(expand.glob_relative(patterns, tmp_path)) == files + + +def test_read_files(tmp_path, monkeypatch): + dir_ = tmp_path / "dir_" + (tmp_path / "_dir").mkdir(exist_ok=True) + (tmp_path / "a.txt").touch() + files = {"a.txt": "a", "dir1/b.txt": "b", "dir1/dir2/c.txt": "c"} + write_files(files, dir_) + + secrets = Path(str(dir_) + "secrets") + secrets.mkdir(exist_ok=True) + write_files({"secrets.txt": "secret keys"}, secrets) + + with monkeypatch.context() as m: + m.chdir(dir_) + assert expand.read_files(list(files)) == "a\nb\nc" + + cannot_access_msg = r"Cannot access '.*\.\..a\.txt'" + with pytest.raises(DistutilsOptionError, match=cannot_access_msg): + expand.read_files(["../a.txt"]) + + cannot_access_secrets_msg = r"Cannot access '.*secrets\.txt'" + with pytest.raises(DistutilsOptionError, match=cannot_access_secrets_msg): + expand.read_files(["../dir_secrets/secrets.txt"]) + + # Make sure the same APIs work outside cwd + assert expand.read_files(list(files), dir_) == "a\nb\nc" + with pytest.raises(DistutilsOptionError, match=cannot_access_msg): + expand.read_files(["../a.txt"], dir_) + + +class TestReadAttr: + @pytest.mark.parametrize( + "example", + [ + # No cookie means UTF-8: + b"__version__ = '\xc3\xa9'\nraise SystemExit(1)\n", + # If a cookie is present, honor it: + b"# -*- coding: utf-8 -*-\n__version__ = '\xc3\xa9'\nraise SystemExit(1)\n", + b"# -*- coding: latin1 -*-\n__version__ = '\xe9'\nraise SystemExit(1)\n", + ], + ) + def test_read_attr_encoding_cookie(self, example, tmp_path): + (tmp_path / "mod.py").write_bytes(example) + assert expand.read_attr('mod.__version__', root_dir=tmp_path) == 'é' + + def test_read_attr(self, tmp_path, monkeypatch): + files = { + "pkg/__init__.py": "", + "pkg/sub/__init__.py": "VERSION = '0.1.1'", + "pkg/sub/mod.py": ( + "VALUES = {'a': 0, 'b': {42}, 'c': (0, 1, 1)}\nraise SystemExit(1)" + ), + } + write_files(files, tmp_path) + + with monkeypatch.context() as m: + m.chdir(tmp_path) + # Make sure it can read the attr statically without evaluating the module + version = expand.read_attr('pkg.sub.VERSION') + values = expand.read_attr('lib.mod.VALUES', {'lib': 'pkg/sub'}) + + assert version == '0.1.1' + assert is_static(values) + + assert values['a'] == 0 + assert values['b'] == {42} + assert is_static(values) + + # Make sure the same APIs work outside cwd + assert expand.read_attr('pkg.sub.VERSION', root_dir=tmp_path) == '0.1.1' + values = expand.read_attr('lib.mod.VALUES', {'lib': 'pkg/sub'}, tmp_path) + assert values['c'] == (0, 1, 1) + + @pytest.mark.parametrize( + "example", + [ + "VERSION: str\nVERSION = '0.1.1'\nraise SystemExit(1)\n", + "VERSION: str = '0.1.1'\nraise SystemExit(1)\n", + ], + ) + def test_read_annotated_attr(self, tmp_path, example): + files = { + "pkg/__init__.py": "", + "pkg/sub/__init__.py": example, + } + write_files(files, tmp_path) + # Make sure this attribute can be read statically + version = expand.read_attr('pkg.sub.VERSION', root_dir=tmp_path) + assert version == '0.1.1' + assert is_static(version) + + @pytest.mark.parametrize( + "example", + [ + "VERSION = (lambda: '0.1.1')()\n", + "def fn(): return '0.1.1'\nVERSION = fn()\n", + "VERSION: str = (lambda: '0.1.1')()\n", + ], + ) + def test_read_dynamic_attr(self, tmp_path, monkeypatch, example): + files = { + "pkg/__init__.py": "", + "pkg/sub/__init__.py": example, + } + write_files(files, tmp_path) + monkeypatch.chdir(tmp_path) + version = expand.read_attr('pkg.sub.VERSION') + assert version == '0.1.1' + assert not is_static(version) + + def test_import_order(self, tmp_path): + """ + Sometimes the import machinery will import the parent package of a nested + module, which triggers side-effects and might create problems (see issue #3176) + + ``read_attr`` should bypass these limitations by resolving modules statically + (via ast.literal_eval). + """ + files = { + "src/pkg/__init__.py": "from .main import func\nfrom .about import version", + "src/pkg/main.py": "import super_complicated_dep\ndef func(): return 42", + "src/pkg/about.py": "version = '42'", + } + write_files(files, tmp_path) + attr_desc = "pkg.about.version" + package_dir = {"": "src"} + # `import super_complicated_dep` should not run, otherwise the build fails + assert expand.read_attr(attr_desc, package_dir, tmp_path) == "42" + + +@pytest.mark.parametrize( + ("package_dir", "file", "module", "return_value"), + [ + ({"": "src"}, "src/pkg/main.py", "pkg.main", 42), + ({"pkg": "lib"}, "lib/main.py", "pkg.main", 13), + ({}, "single_module.py", "single_module", 70), + ({}, "flat_layout/pkg.py", "flat_layout.pkg", 836), + ], +) +def test_resolve_class(monkeypatch, tmp_path, package_dir, file, module, return_value): + monkeypatch.setattr(sys, "modules", {}) # reproducibility + files = {file: f"class Custom:\n def testing(self): return {return_value}"} + write_files(files, tmp_path) + cls = expand.resolve_class(f"{module}.Custom", package_dir, tmp_path) + assert cls().testing() == return_value + + +@pytest.mark.parametrize( + ("args", "pkgs"), + [ + ({"where": ["."], "namespaces": False}, {"pkg", "other"}), + ({"where": [".", "dir1"], "namespaces": False}, {"pkg", "other", "dir2"}), + ({"namespaces": True}, {"pkg", "other", "dir1", "dir1.dir2"}), + ({}, {"pkg", "other", "dir1", "dir1.dir2"}), # default value for `namespaces` + ], +) +def test_find_packages(tmp_path, args, pkgs): + files = { + "pkg/__init__.py", + "other/__init__.py", + "dir1/dir2/__init__.py", + } + write_files({k: "" for k in files}, tmp_path) + + package_dir = {} + kwargs = {"root_dir": tmp_path, "fill_package_dir": package_dir, **args} + where = kwargs.get("where", ["."]) + assert set(expand.find_packages(**kwargs)) == pkgs + for pkg in pkgs: + pkg_path = find_package_path(pkg, package_dir, tmp_path) + assert os.path.exists(pkg_path) + + # Make sure the same APIs work outside cwd + where = [ + str((tmp_path / p).resolve()).replace(os.sep, "/") # ensure posix-style paths + for p in args.pop("where", ["."]) + ] + + assert set(expand.find_packages(where=where, **args)) == pkgs + + +@pytest.mark.parametrize( + ("files", "where", "expected_package_dir"), + [ + (["pkg1/__init__.py", "pkg1/other.py"], ["."], {}), + (["pkg1/__init__.py", "pkg2/__init__.py"], ["."], {}), + (["src/pkg1/__init__.py", "src/pkg1/other.py"], ["src"], {"": "src"}), + (["src/pkg1/__init__.py", "src/pkg2/__init__.py"], ["src"], {"": "src"}), + ( + ["src1/pkg1/__init__.py", "src2/pkg2/__init__.py"], + ["src1", "src2"], + {"pkg1": "src1/pkg1", "pkg2": "src2/pkg2"}, + ), + ( + ["src/pkg1/__init__.py", "pkg2/__init__.py"], + ["src", "."], + {"pkg1": "src/pkg1"}, + ), + ], +) +def test_fill_package_dir(tmp_path, files, where, expected_package_dir): + write_files({k: "" for k in files}, tmp_path) + pkg_dir = {} + kwargs = {"root_dir": tmp_path, "fill_package_dir": pkg_dir, "namespaces": False} + pkgs = expand.find_packages(where=where, **kwargs) + assert set(pkg_dir.items()) == set(expected_package_dir.items()) + for pkg in pkgs: + pkg_path = find_package_path(pkg, pkg_dir, tmp_path) + assert os.path.exists(pkg_path) diff --git a/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml.py b/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml.py new file mode 100644 index 0000000000000000000000000000000000000000..6d995d23af12b4f103dbb310635d14687ab1a0be --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml.py @@ -0,0 +1,398 @@ +import re +from configparser import ConfigParser +from inspect import cleandoc + +import jaraco.path +import pytest +import tomli_w +from path import Path + +import setuptools # noqa: F401 # force distutils.core to be patched +from setuptools.config.pyprojecttoml import ( + _ToolsTypoInMetadata, + apply_configuration, + expand_configuration, + read_configuration, + validate, +) +from setuptools.dist import Distribution +from setuptools.errors import OptionError + +import distutils.core + +EXAMPLE = """ +[project] +name = "myproj" +keywords = ["some", "key", "words"] +dynamic = ["version", "readme"] +requires-python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +dependencies = [ + 'importlib-metadata>=0.12;python_version<"3.8"', + 'importlib-resources>=1.0;python_version<"3.7"', + 'pathlib2>=2.3.3,<3;python_version < "3.4" and sys.platform != "win32"', +] + +[project.optional-dependencies] +docs = [ + "sphinx>=3", + "sphinx-argparse>=0.2.5", + "sphinx-rtd-theme>=0.4.3", +] +testing = [ + "pytest>=1", + "coverage>=3,<5", +] + +[project.scripts] +exec = "pkg.__main__:exec" + +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +package-dir = {"" = "src"} +zip-safe = true +platforms = ["any"] + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.setuptools.cmdclass] +sdist = "pkg.mod.CustomSdist" + +[tool.setuptools.dynamic.version] +attr = "pkg.__version__.VERSION" + +[tool.setuptools.dynamic.readme] +file = ["README.md"] +content-type = "text/markdown" + +[tool.setuptools.package-data] +"*" = ["*.txt"] + +[tool.setuptools.data-files] +"data" = ["_files/*.txt"] + +[tool.distutils.sdist] +formats = "gztar" + +[tool.distutils.bdist_wheel] +universal = true +""" + + +def create_example(path, pkg_root): + files = { + "pyproject.toml": EXAMPLE, + "README.md": "hello world", + "_files": { + "file.txt": "", + }, + } + packages = { + "pkg": { + "__init__.py": "", + "mod.py": "class CustomSdist: pass", + "__version__.py": "VERSION = (3, 10)", + "__main__.py": "def exec(): print('hello')", + }, + } + + assert pkg_root # Meta-test: cannot be empty string. + + if pkg_root == ".": + files = {**files, **packages} + # skip other files: flat-layout will raise error for multi-package dist + else: + # Use this opportunity to ensure namespaces are discovered + files[pkg_root] = {**packages, "other": {"nested": {"__init__.py": ""}}} + + jaraco.path.build(files, prefix=path) + + +def verify_example(config, path, pkg_root): + pyproject = path / "pyproject.toml" + pyproject.write_text(tomli_w.dumps(config), encoding="utf-8") + expanded = expand_configuration(config, path) + expanded_project = expanded["project"] + assert read_configuration(pyproject, expand=True) == expanded + assert expanded_project["version"] == "3.10" + assert expanded_project["readme"]["text"] == "hello world" + assert "packages" in expanded["tool"]["setuptools"] + if pkg_root == ".": + # Auto-discovery will raise error for multi-package dist + assert set(expanded["tool"]["setuptools"]["packages"]) == {"pkg"} + else: + assert set(expanded["tool"]["setuptools"]["packages"]) == { + "pkg", + "other", + "other.nested", + } + assert expanded["tool"]["setuptools"]["include-package-data"] is True + assert "" in expanded["tool"]["setuptools"]["package-data"] + assert "*" not in expanded["tool"]["setuptools"]["package-data"] + assert expanded["tool"]["setuptools"]["data-files"] == [ + ("data", ["_files/file.txt"]) + ] + + +def test_read_configuration(tmp_path): + create_example(tmp_path, "src") + pyproject = tmp_path / "pyproject.toml" + + config = read_configuration(pyproject, expand=False) + assert config["project"].get("version") is None + assert config["project"].get("readme") is None + + verify_example(config, tmp_path, "src") + + +@pytest.mark.parametrize( + ("pkg_root", "opts"), + [ + (".", {}), + ("src", {}), + ("lib", {"packages": {"find": {"where": ["lib"]}}}), + ], +) +def test_discovered_package_dir_with_attr_directive_in_config(tmp_path, pkg_root, opts): + create_example(tmp_path, pkg_root) + + pyproject = tmp_path / "pyproject.toml" + + config = read_configuration(pyproject, expand=False) + assert config["project"].get("version") is None + assert config["project"].get("readme") is None + config["tool"]["setuptools"].pop("packages", None) + config["tool"]["setuptools"].pop("package-dir", None) + + config["tool"]["setuptools"].update(opts) + verify_example(config, tmp_path, pkg_root) + + +ENTRY_POINTS = { + "console_scripts": {"a": "mod.a:func"}, + "gui_scripts": {"b": "mod.b:func"}, + "other": {"c": "mod.c:func [extra]"}, +} + + +class TestEntryPoints: + def write_entry_points(self, tmp_path): + entry_points = ConfigParser() + entry_points.read_dict(ENTRY_POINTS) + with open(tmp_path / "entry-points.txt", "w", encoding="utf-8") as f: + entry_points.write(f) + + def pyproject(self, dynamic=None): + project = {"dynamic": dynamic or ["scripts", "gui-scripts", "entry-points"]} + tool = {"dynamic": {"entry-points": {"file": "entry-points.txt"}}} + return {"project": project, "tool": {"setuptools": tool}} + + def test_all_listed_in_dynamic(self, tmp_path): + self.write_entry_points(tmp_path) + expanded = expand_configuration(self.pyproject(), tmp_path) + expanded_project = expanded["project"] + assert len(expanded_project["scripts"]) == 1 + assert expanded_project["scripts"]["a"] == "mod.a:func" + assert len(expanded_project["gui-scripts"]) == 1 + assert expanded_project["gui-scripts"]["b"] == "mod.b:func" + assert len(expanded_project["entry-points"]) == 1 + assert expanded_project["entry-points"]["other"]["c"] == "mod.c:func [extra]" + + @pytest.mark.parametrize("missing_dynamic", ("scripts", "gui-scripts")) + def test_scripts_not_listed_in_dynamic(self, tmp_path, missing_dynamic): + self.write_entry_points(tmp_path) + dynamic = {"scripts", "gui-scripts", "entry-points"} - {missing_dynamic} + + msg = f"defined outside of `pyproject.toml`:.*{missing_dynamic}" + with pytest.raises(OptionError, match=re.compile(msg, re.DOTALL)): + expand_configuration(self.pyproject(dynamic), tmp_path) + + +class TestClassifiers: + def test_dynamic(self, tmp_path): + # Let's create a project example that has dynamic classifiers + # coming from a txt file. + create_example(tmp_path, "src") + classifiers = cleandoc( + """ + Framework :: Flask + Programming Language :: Haskell + """ + ) + (tmp_path / "classifiers.txt").write_text(classifiers, encoding="utf-8") + + pyproject = tmp_path / "pyproject.toml" + config = read_configuration(pyproject, expand=False) + dynamic = config["project"]["dynamic"] + config["project"]["dynamic"] = list({*dynamic, "classifiers"}) + dynamic_config = config["tool"]["setuptools"]["dynamic"] + dynamic_config["classifiers"] = {"file": "classifiers.txt"} + + # When the configuration is expanded, + # each line of the file should be an different classifier. + validate(config, pyproject) + expanded = expand_configuration(config, tmp_path) + + assert set(expanded["project"]["classifiers"]) == { + "Framework :: Flask", + "Programming Language :: Haskell", + } + + def test_dynamic_without_config(self, tmp_path): + config = """ + [project] + name = "myproj" + version = '42' + dynamic = ["classifiers"] + """ + + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text(cleandoc(config), encoding="utf-8") + with pytest.raises(OptionError, match="No configuration .* .classifiers."): + read_configuration(pyproject) + + def test_dynamic_readme_from_setup_script_args(self, tmp_path): + config = """ + [project] + name = "myproj" + version = '42' + dynamic = ["readme"] + """ + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text(cleandoc(config), encoding="utf-8") + dist = Distribution(attrs={"long_description": "42"}) + # No error should occur because of missing `readme` + dist = apply_configuration(dist, pyproject) + assert dist.metadata.long_description == "42" + + def test_dynamic_without_file(self, tmp_path): + config = """ + [project] + name = "myproj" + version = '42' + dynamic = ["classifiers"] + + [tool.setuptools.dynamic] + classifiers = {file = ["classifiers.txt"]} + """ + + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text(cleandoc(config), encoding="utf-8") + with pytest.warns(UserWarning, match="File .*classifiers.txt. cannot be found"): + expanded = read_configuration(pyproject) + assert "classifiers" not in expanded["project"] + + +@pytest.mark.parametrize( + "example", + ( + """ + [project] + name = "myproj" + version = "1.2" + + [my-tool.that-disrespect.pep518] + value = 42 + """, + ), +) +def test_ignore_unrelated_config(tmp_path, example): + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text(cleandoc(example), encoding="utf-8") + + # Make sure no error is raised due to 3rd party configs in pyproject.toml + assert read_configuration(pyproject) is not None + + +@pytest.mark.parametrize( + ("example", "error_msg"), + [ + ( + """ + [project] + name = "myproj" + version = "1.2" + requires = ['pywin32; platform_system=="Windows"' ] + """, + "configuration error: .project. must not contain ..requires.. properties", + ), + ], +) +def test_invalid_example(tmp_path, example, error_msg): + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text(cleandoc(example), encoding="utf-8") + + pattern = re.compile( + f"invalid pyproject.toml.*{error_msg}.*", re.MULTILINE | re.DOTALL + ) + with pytest.raises(ValueError, match=pattern): + read_configuration(pyproject) + + +@pytest.mark.parametrize("config", ("", "[tool.something]\nvalue = 42")) +def test_empty(tmp_path, config): + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text(config, encoding="utf-8") + + # Make sure no error is raised + assert read_configuration(pyproject) == {} + + +@pytest.mark.parametrize("config", ("[project]\nname = 'myproj'\nversion='42'\n",)) +def test_include_package_data_by_default(tmp_path, config): + """Builds with ``pyproject.toml`` should consider ``include-package-data=True`` as + default. + """ + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text(config, encoding="utf-8") + + config = read_configuration(pyproject) + assert config["tool"]["setuptools"]["include-package-data"] is True + + +def test_include_package_data_in_setuppy(tmp_path): + """Builds with ``pyproject.toml`` should consider ``include_package_data`` set in + ``setup.py``. + + See https://github.com/pypa/setuptools/issues/3197#issuecomment-1079023889 + """ + files = { + "pyproject.toml": "[project]\nname = 'myproj'\nversion='42'\n", + "setup.py": "__import__('setuptools').setup(include_package_data=False)", + } + jaraco.path.build(files, prefix=tmp_path) + + with Path(tmp_path): + dist = distutils.core.run_setup("setup.py", {}, stop_after="config") + + assert dist.get_name() == "myproj" + assert dist.get_version() == "42" + assert dist.include_package_data is False + + +def test_warn_tools_typo(tmp_path): + """Test that the common ``tools.setuptools`` typo in ``pyproject.toml`` issues a warning + + See https://github.com/pypa/setuptools/issues/4150 + """ + config = """ + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + + [project] + name = "myproj" + version = '42' + + [tools.setuptools] + packages = ["package"] + """ + + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text(cleandoc(config), encoding="utf-8") + + with pytest.warns(_ToolsTypoInMetadata): + read_configuration(pyproject) diff --git a/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py b/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py new file mode 100644 index 0000000000000000000000000000000000000000..9fc8050743c4b1a8497d5ea20c571b565d074e59 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py @@ -0,0 +1,111 @@ +from inspect import cleandoc + +import pytest +from jaraco import path + +from setuptools.config.pyprojecttoml import apply_configuration +from setuptools.dist import Distribution +from setuptools.warnings import SetuptoolsWarning + + +def test_dynamic_dependencies(tmp_path): + files = { + "requirements.txt": "six\n # comment\n", + "pyproject.toml": cleandoc( + """ + [project] + name = "myproj" + version = "1.0" + dynamic = ["dependencies"] + + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [tool.setuptools.dynamic.dependencies] + file = ["requirements.txt"] + """ + ), + } + path.build(files, prefix=tmp_path) + dist = Distribution() + dist = apply_configuration(dist, tmp_path / "pyproject.toml") + assert dist.install_requires == ["six"] + + +def test_dynamic_optional_dependencies(tmp_path): + files = { + "requirements-docs.txt": "sphinx\n # comment\n", + "pyproject.toml": cleandoc( + """ + [project] + name = "myproj" + version = "1.0" + dynamic = ["optional-dependencies"] + + [tool.setuptools.dynamic.optional-dependencies.docs] + file = ["requirements-docs.txt"] + + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + """ + ), + } + path.build(files, prefix=tmp_path) + dist = Distribution() + dist = apply_configuration(dist, tmp_path / "pyproject.toml") + assert dist.extras_require == {"docs": ["sphinx"]} + + +def test_mixed_dynamic_optional_dependencies(tmp_path): + """ + Test that if PEP 621 was loosened to allow mixing of dynamic and static + configurations in the case of fields containing sub-fields (groups), + things would work out. + """ + files = { + "requirements-images.txt": "pillow~=42.0\n # comment\n", + "pyproject.toml": cleandoc( + """ + [project] + name = "myproj" + version = "1.0" + dynamic = ["optional-dependencies"] + + [project.optional-dependencies] + docs = ["sphinx"] + + [tool.setuptools.dynamic.optional-dependencies.images] + file = ["requirements-images.txt"] + """ + ), + } + + path.build(files, prefix=tmp_path) + pyproject = tmp_path / "pyproject.toml" + with pytest.raises(ValueError, match="project.optional-dependencies"): + apply_configuration(Distribution(), pyproject) + + +def test_mixed_extras_require_optional_dependencies(tmp_path): + files = { + "pyproject.toml": cleandoc( + """ + [project] + name = "myproj" + version = "1.0" + optional-dependencies.docs = ["sphinx"] + """ + ), + } + + path.build(files, prefix=tmp_path) + pyproject = tmp_path / "pyproject.toml" + + dist = Distribution({"extras_require": {"hello": ["world"]}}) + + with pytest.warns(SetuptoolsWarning, match=".extras_require. overwritten"): + dist = apply_configuration(dist, pyproject) + + assert dist.extras_require == {"docs": ["sphinx"]} diff --git a/lib/python3.12/site-packages/setuptools/tests/config/test_setupcfg.py b/lib/python3.12/site-packages/setuptools/tests/config/test_setupcfg.py new file mode 100644 index 0000000000000000000000000000000000000000..495337a9a5815e3d2a0aefa584b8ee81f5136d95 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/config/test_setupcfg.py @@ -0,0 +1,987 @@ +import configparser +import contextlib +import inspect +import re +import sys +from pathlib import Path +from unittest.mock import Mock, patch + +import pytest +from packaging.requirements import InvalidRequirement + +from setuptools.config.setupcfg import ConfigHandler, Target, read_configuration +from setuptools.dist import Distribution, _Distribution +from setuptools.warnings import SetuptoolsDeprecationWarning + +from ..textwrap import DALS + +from distutils.errors import DistutilsFileError, DistutilsOptionError + +IS_PYPY = '__pypy__' in sys.builtin_module_names + + +class ErrConfigHandler(ConfigHandler[Target]): + """Erroneous handler. Fails to implement required methods.""" + + section_prefix = "**err**" + + +def make_package_dir(name, base_dir, ns=False): + dir_package = base_dir + for dir_name in name.split('/'): + dir_package = dir_package.mkdir(dir_name) + init_file = None + if not ns: + init_file = dir_package.join('__init__.py') + init_file.write('') + return dir_package, init_file + + +def fake_env( + tmpdir, setup_cfg, setup_py=None, encoding='ascii', package_path='fake_package' +): + if setup_py is None: + setup_py = 'from setuptools import setup\nsetup()\n' + + tmpdir.join('setup.py').write(setup_py) + config = tmpdir.join('setup.cfg') + config.write(setup_cfg.encode(encoding), mode='wb') + + package_dir, init_file = make_package_dir(package_path, tmpdir) + + init_file.write( + 'VERSION = (1, 2, 3)\n' + '\n' + 'VERSION_MAJOR = 1' + '\n' + 'def get_version():\n' + ' return [3, 4, 5, "dev"]\n' + '\n' + ) + + return package_dir, config + + +@contextlib.contextmanager +def get_dist(tmpdir, kwargs_initial=None, parse=True): + kwargs_initial = kwargs_initial or {} + + with tmpdir.as_cwd(): + dist = Distribution(kwargs_initial) + dist.script_name = 'setup.py' + parse and dist.parse_config_files() + + yield dist + + +def test_parsers_implemented(): + with pytest.raises(NotImplementedError): + handler = ErrConfigHandler(None, {}, False, Mock()) + handler.parsers + + +class TestConfigurationReader: + def test_basic(self, tmpdir): + _, config = fake_env( + tmpdir, + '[metadata]\n' + 'version = 10.1.1\n' + 'keywords = one, two\n' + '\n' + '[options]\n' + 'scripts = bin/a.py, bin/b.py\n', + ) + config_dict = read_configuration(str(config)) + assert config_dict['metadata']['version'] == '10.1.1' + assert config_dict['metadata']['keywords'] == ['one', 'two'] + assert config_dict['options']['scripts'] == ['bin/a.py', 'bin/b.py'] + + def test_no_config(self, tmpdir): + with pytest.raises(DistutilsFileError): + read_configuration(str(tmpdir.join('setup.cfg'))) + + def test_ignore_errors(self, tmpdir): + _, config = fake_env( + tmpdir, + '[metadata]\nversion = attr: none.VERSION\nkeywords = one, two\n', + ) + with pytest.raises(ImportError): + read_configuration(str(config)) + + config_dict = read_configuration(str(config), ignore_option_errors=True) + + assert config_dict['metadata']['keywords'] == ['one', 'two'] + assert 'version' not in config_dict['metadata'] + + config.remove() + + +class TestMetadata: + def test_basic(self, tmpdir): + fake_env( + tmpdir, + '[metadata]\n' + 'version = 10.1.1\n' + 'description = Some description\n' + 'long_description_content_type = text/something\n' + 'long_description = file: README\n' + 'name = fake_name\n' + 'keywords = one, two\n' + 'provides = package, package.sub\n' + 'license = otherlic\n' + 'download_url = http://test.test.com/test/\n' + 'maintainer_email = test@test.com\n', + ) + + tmpdir.join('README').write('readme contents\nline2') + + meta_initial = { + # This will be used so `otherlic` won't replace it. + 'license': 'BSD 3-Clause License', + } + + with get_dist(tmpdir, meta_initial) as dist: + metadata = dist.metadata + + assert metadata.version == '10.1.1' + assert metadata.description == 'Some description' + assert metadata.long_description_content_type == 'text/something' + assert metadata.long_description == 'readme contents\nline2' + assert metadata.provides == ['package', 'package.sub'] + assert metadata.license == 'BSD 3-Clause License' + assert metadata.name == 'fake_name' + assert metadata.keywords == ['one', 'two'] + assert metadata.download_url == 'http://test.test.com/test/' + assert metadata.maintainer_email == 'test@test.com' + + def test_license_cfg(self, tmpdir): + fake_env( + tmpdir, + DALS( + """ + [metadata] + name=foo + version=0.0.1 + license=Apache 2.0 + """ + ), + ) + + with get_dist(tmpdir) as dist: + metadata = dist.metadata + + assert metadata.name == "foo" + assert metadata.version == "0.0.1" + assert metadata.license == "Apache 2.0" + + def test_file_mixed(self, tmpdir): + fake_env( + tmpdir, + '[metadata]\nlong_description = file: README.rst, CHANGES.rst\n\n', + ) + + tmpdir.join('README.rst').write('readme contents\nline2') + tmpdir.join('CHANGES.rst').write('changelog contents\nand stuff') + + with get_dist(tmpdir) as dist: + assert dist.metadata.long_description == ( + 'readme contents\nline2\nchangelog contents\nand stuff' + ) + + def test_file_sandboxed(self, tmpdir): + tmpdir.ensure("README") + project = tmpdir.join('depth1', 'depth2') + project.ensure(dir=True) + fake_env(project, '[metadata]\nlong_description = file: ../../README\n') + + with get_dist(project, parse=False) as dist: + with pytest.raises(DistutilsOptionError): + dist.parse_config_files() # file: out of sandbox + + def test_aliases(self, tmpdir): + fake_env( + tmpdir, + '[metadata]\n' + 'author_email = test@test.com\n' + 'home_page = http://test.test.com/test/\n' + 'summary = Short summary\n' + 'platform = a, b\n' + 'classifier =\n' + ' Framework :: Django\n' + ' Programming Language :: Python :: 3.5\n', + ) + + with get_dist(tmpdir) as dist: + metadata = dist.metadata + assert metadata.author_email == 'test@test.com' + assert metadata.url == 'http://test.test.com/test/' + assert metadata.description == 'Short summary' + assert metadata.platforms == ['a', 'b'] + assert metadata.classifiers == [ + 'Framework :: Django', + 'Programming Language :: Python :: 3.5', + ] + + def test_multiline(self, tmpdir): + fake_env( + tmpdir, + '[metadata]\n' + 'name = fake_name\n' + 'keywords =\n' + ' one\n' + ' two\n' + 'classifiers =\n' + ' Framework :: Django\n' + ' Programming Language :: Python :: 3.5\n', + ) + with get_dist(tmpdir) as dist: + metadata = dist.metadata + assert metadata.keywords == ['one', 'two'] + assert metadata.classifiers == [ + 'Framework :: Django', + 'Programming Language :: Python :: 3.5', + ] + + def test_dict(self, tmpdir): + fake_env( + tmpdir, + '[metadata]\n' + 'project_urls =\n' + ' Link One = https://example.com/one/\n' + ' Link Two = https://example.com/two/\n', + ) + with get_dist(tmpdir) as dist: + metadata = dist.metadata + assert metadata.project_urls == { + 'Link One': 'https://example.com/one/', + 'Link Two': 'https://example.com/two/', + } + + def test_version(self, tmpdir): + package_dir, config = fake_env( + tmpdir, '[metadata]\nversion = attr: fake_package.VERSION\n' + ) + + sub_a = package_dir.mkdir('subpkg_a') + sub_a.join('__init__.py').write('') + sub_a.join('mod.py').write('VERSION = (2016, 11, 26)') + + sub_b = package_dir.mkdir('subpkg_b') + sub_b.join('__init__.py').write('') + sub_b.join('mod.py').write( + 'import third_party_module\nVERSION = (2016, 11, 26)' + ) + + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1.2.3' + + config.write('[metadata]\nversion = attr: fake_package.get_version\n') + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '3.4.5.dev' + + config.write('[metadata]\nversion = attr: fake_package.VERSION_MAJOR\n') + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1' + + config.write('[metadata]\nversion = attr: fake_package.subpkg_a.mod.VERSION\n') + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '2016.11.26' + + config.write('[metadata]\nversion = attr: fake_package.subpkg_b.mod.VERSION\n') + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '2016.11.26' + + def test_version_file(self, tmpdir): + fake_env(tmpdir, '[metadata]\nversion = file: fake_package/version.txt\n') + tmpdir.join('fake_package', 'version.txt').write('1.2.3\n') + + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1.2.3' + + tmpdir.join('fake_package', 'version.txt').write('1.2.3\n4.5.6\n') + with pytest.raises(DistutilsOptionError): + with get_dist(tmpdir) as dist: + dist.metadata.version + + def test_version_with_package_dir_simple(self, tmpdir): + fake_env( + tmpdir, + '[metadata]\n' + 'version = attr: fake_package_simple.VERSION\n' + '[options]\n' + 'package_dir =\n' + ' = src\n', + package_path='src/fake_package_simple', + ) + + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1.2.3' + + def test_version_with_package_dir_rename(self, tmpdir): + fake_env( + tmpdir, + '[metadata]\n' + 'version = attr: fake_package_rename.VERSION\n' + '[options]\n' + 'package_dir =\n' + ' fake_package_rename = fake_dir\n', + package_path='fake_dir', + ) + + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1.2.3' + + def test_version_with_package_dir_complex(self, tmpdir): + fake_env( + tmpdir, + '[metadata]\n' + 'version = attr: fake_package_complex.VERSION\n' + '[options]\n' + 'package_dir =\n' + ' fake_package_complex = src/fake_dir\n', + package_path='src/fake_dir', + ) + + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1.2.3' + + def test_unknown_meta_item(self, tmpdir): + fake_env(tmpdir, '[metadata]\nname = fake_name\nunknown = some\n') + with get_dist(tmpdir, parse=False) as dist: + dist.parse_config_files() # Skip unknown. + + def test_usupported_section(self, tmpdir): + fake_env(tmpdir, '[metadata.some]\nkey = val\n') + with get_dist(tmpdir, parse=False) as dist: + with pytest.raises(DistutilsOptionError): + dist.parse_config_files() + + def test_classifiers(self, tmpdir): + expected = set([ + 'Framework :: Django', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + ]) + + # From file. + _, config = fake_env(tmpdir, '[metadata]\nclassifiers = file: classifiers\n') + + tmpdir.join('classifiers').write( + 'Framework :: Django\n' + 'Programming Language :: Python :: 3\n' + 'Programming Language :: Python :: 3.5\n' + ) + + with get_dist(tmpdir) as dist: + assert set(dist.metadata.classifiers) == expected + + # From list notation + config.write( + '[metadata]\n' + 'classifiers =\n' + ' Framework :: Django\n' + ' Programming Language :: Python :: 3\n' + ' Programming Language :: Python :: 3.5\n' + ) + with get_dist(tmpdir) as dist: + assert set(dist.metadata.classifiers) == expected + + def test_interpolation(self, tmpdir): + fake_env(tmpdir, '[metadata]\ndescription = %(message)s\n') + with pytest.raises(configparser.InterpolationMissingOptionError): + with get_dist(tmpdir): + pass + + def test_non_ascii_1(self, tmpdir): + fake_env(tmpdir, '[metadata]\ndescription = éàïôñ\n', encoding='utf-8') + with get_dist(tmpdir): + pass + + def test_non_ascii_3(self, tmpdir): + fake_env(tmpdir, '\n# -*- coding: invalid\n') + with get_dist(tmpdir): + pass + + def test_non_ascii_4(self, tmpdir): + fake_env( + tmpdir, + '# -*- coding: utf-8\n[metadata]\ndescription = éàïôñ\n', + encoding='utf-8', + ) + with get_dist(tmpdir) as dist: + assert dist.metadata.description == 'éàïôñ' + + def test_not_utf8(self, tmpdir): + """ + Config files encoded not in UTF-8 will fail + """ + fake_env( + tmpdir, + '# vim: set fileencoding=iso-8859-15 :\n[metadata]\ndescription = éàïôñ\n', + encoding='iso-8859-15', + ) + with pytest.raises(UnicodeDecodeError): + with get_dist(tmpdir): + pass + + @pytest.mark.parametrize( + ("error_msg", "config", "invalid"), + [ + ( + "Invalid dash-separated key 'author-email' in 'metadata' (setup.cfg)", + DALS( + """ + [metadata] + author-email = test@test.com + maintainer_email = foo@foo.com + """ + ), + {"author-email": "test@test.com"}, + ), + ( + "Invalid uppercase key 'Name' in 'metadata' (setup.cfg)", + DALS( + """ + [metadata] + Name = foo + description = Some description + """ + ), + {"Name": "foo"}, + ), + ], + ) + def test_invalid_options_previously_deprecated( + self, tmpdir, error_msg, config, invalid + ): + # This test and related methods can be removed when no longer needed. + # Deprecation postponed due to push-back from the community in + # https://github.com/pypa/setuptools/issues/4910 + fake_env(tmpdir, config) + with pytest.warns(SetuptoolsDeprecationWarning, match=re.escape(error_msg)): + dist = get_dist(tmpdir).__enter__() + + tmpdir.join('setup.cfg').remove() + + for field, value in invalid.items(): + attr = field.replace("-", "_").lower() + assert getattr(dist.metadata, attr) == value + + +class TestOptions: + def test_basic(self, tmpdir): + fake_env( + tmpdir, + '[options]\n' + 'zip_safe = True\n' + 'include_package_data = yes\n' + 'package_dir = b=c, =src\n' + 'packages = pack_a, pack_b.subpack\n' + 'namespace_packages = pack1, pack2\n' + 'scripts = bin/one.py, bin/two.py\n' + 'eager_resources = bin/one.py, bin/two.py\n' + 'install_requires = docutils>=0.3; pack ==1.1, ==1.3; hey\n' + 'setup_requires = docutils>=0.3; spack ==1.1, ==1.3; there\n' + 'dependency_links = http://some.com/here/1, ' + 'http://some.com/there/2\n' + 'python_requires = >=1.0, !=2.8\n' + 'py_modules = module1, module2\n', + ) + deprec = pytest.warns(SetuptoolsDeprecationWarning, match="namespace_packages") + with deprec, get_dist(tmpdir) as dist: + assert dist.zip_safe + assert dist.include_package_data + assert dist.package_dir == {'': 'src', 'b': 'c'} + assert dist.packages == ['pack_a', 'pack_b.subpack'] + assert dist.namespace_packages == ['pack1', 'pack2'] + assert dist.scripts == ['bin/one.py', 'bin/two.py'] + assert dist.dependency_links == ([ + 'http://some.com/here/1', + 'http://some.com/there/2', + ]) + assert dist.install_requires == ([ + 'docutils>=0.3', + 'pack==1.1,==1.3', + 'hey', + ]) + assert dist.setup_requires == ([ + 'docutils>=0.3', + 'spack ==1.1, ==1.3', + 'there', + ]) + assert dist.python_requires == '>=1.0, !=2.8' + assert dist.py_modules == ['module1', 'module2'] + + def test_multiline(self, tmpdir): + fake_env( + tmpdir, + '[options]\n' + 'package_dir = \n' + ' b=c\n' + ' =src\n' + 'packages = \n' + ' pack_a\n' + ' pack_b.subpack\n' + 'namespace_packages = \n' + ' pack1\n' + ' pack2\n' + 'scripts = \n' + ' bin/one.py\n' + ' bin/two.py\n' + 'eager_resources = \n' + ' bin/one.py\n' + ' bin/two.py\n' + 'install_requires = \n' + ' docutils>=0.3\n' + ' pack ==1.1, ==1.3\n' + ' hey\n' + 'setup_requires = \n' + ' docutils>=0.3\n' + ' spack ==1.1, ==1.3\n' + ' there\n' + 'dependency_links = \n' + ' http://some.com/here/1\n' + ' http://some.com/there/2\n', + ) + deprec = pytest.warns(SetuptoolsDeprecationWarning, match="namespace_packages") + with deprec, get_dist(tmpdir) as dist: + assert dist.package_dir == {'': 'src', 'b': 'c'} + assert dist.packages == ['pack_a', 'pack_b.subpack'] + assert dist.namespace_packages == ['pack1', 'pack2'] + assert dist.scripts == ['bin/one.py', 'bin/two.py'] + assert dist.dependency_links == ([ + 'http://some.com/here/1', + 'http://some.com/there/2', + ]) + assert dist.install_requires == ([ + 'docutils>=0.3', + 'pack==1.1,==1.3', + 'hey', + ]) + assert dist.setup_requires == ([ + 'docutils>=0.3', + 'spack ==1.1, ==1.3', + 'there', + ]) + + def test_package_dir_fail(self, tmpdir): + fake_env(tmpdir, '[options]\npackage_dir = a b\n') + with get_dist(tmpdir, parse=False) as dist: + with pytest.raises(DistutilsOptionError): + dist.parse_config_files() + + def test_package_data(self, tmpdir): + fake_env( + tmpdir, + '[options.package_data]\n' + '* = *.txt, *.rst\n' + 'hello = *.msg\n' + '\n' + '[options.exclude_package_data]\n' + '* = fake1.txt, fake2.txt\n' + 'hello = *.dat\n', + ) + + with get_dist(tmpdir) as dist: + assert dist.package_data == { + '': ['*.txt', '*.rst'], + 'hello': ['*.msg'], + } + assert dist.exclude_package_data == { + '': ['fake1.txt', 'fake2.txt'], + 'hello': ['*.dat'], + } + + def test_packages(self, tmpdir): + fake_env(tmpdir, '[options]\npackages = find:\n') + + with get_dist(tmpdir) as dist: + assert dist.packages == ['fake_package'] + + def test_find_directive(self, tmpdir): + dir_package, config = fake_env(tmpdir, '[options]\npackages = find:\n') + + make_package_dir('sub_one', dir_package) + make_package_dir('sub_two', dir_package) + + with get_dist(tmpdir) as dist: + assert set(dist.packages) == set([ + 'fake_package', + 'fake_package.sub_two', + 'fake_package.sub_one', + ]) + + config.write( + '[options]\n' + 'packages = find:\n' + '\n' + '[options.packages.find]\n' + 'where = .\n' + 'include =\n' + ' fake_package.sub_one\n' + ' two\n' + ) + with get_dist(tmpdir) as dist: + assert dist.packages == ['fake_package.sub_one'] + + config.write( + '[options]\n' + 'packages = find:\n' + '\n' + '[options.packages.find]\n' + 'exclude =\n' + ' fake_package.sub_one\n' + ) + with get_dist(tmpdir) as dist: + assert set(dist.packages) == set(['fake_package', 'fake_package.sub_two']) + + def test_find_namespace_directive(self, tmpdir): + dir_package, config = fake_env( + tmpdir, '[options]\npackages = find_namespace:\n' + ) + + make_package_dir('sub_one', dir_package) + make_package_dir('sub_two', dir_package, ns=True) + + with get_dist(tmpdir) as dist: + assert set(dist.packages) == { + 'fake_package', + 'fake_package.sub_two', + 'fake_package.sub_one', + } + + config.write( + '[options]\n' + 'packages = find_namespace:\n' + '\n' + '[options.packages.find]\n' + 'where = .\n' + 'include =\n' + ' fake_package.sub_one\n' + ' two\n' + ) + with get_dist(tmpdir) as dist: + assert dist.packages == ['fake_package.sub_one'] + + config.write( + '[options]\n' + 'packages = find_namespace:\n' + '\n' + '[options.packages.find]\n' + 'exclude =\n' + ' fake_package.sub_one\n' + ) + with get_dist(tmpdir) as dist: + assert set(dist.packages) == {'fake_package', 'fake_package.sub_two'} + + def test_extras_require(self, tmpdir): + fake_env( + tmpdir, + '[options.extras_require]\n' + 'pdf = ReportLab>=1.2; RXP\n' + 'rest = \n' + ' docutils>=0.3\n' + ' pack ==1.1, ==1.3\n', + ) + + with get_dist(tmpdir) as dist: + assert dist.extras_require == { + 'pdf': ['ReportLab>=1.2', 'RXP'], + 'rest': ['docutils>=0.3', 'pack==1.1,==1.3'], + } + assert set(dist.metadata.provides_extras) == {'pdf', 'rest'} + + @pytest.mark.parametrize( + "config", + [ + "[options.extras_require]\nfoo = bar;python_version<'3'", + "[options.extras_require]\nfoo = bar;os_name=='linux'", + "[options.extras_require]\nfoo = bar;python_version<'3'\n", + "[options.extras_require]\nfoo = bar;os_name=='linux'\n", + "[options]\ninstall_requires = bar;python_version<'3'", + "[options]\ninstall_requires = bar;os_name=='linux'", + "[options]\ninstall_requires = bar;python_version<'3'\n", + "[options]\ninstall_requires = bar;os_name=='linux'\n", + ], + ) + @pytest.mark.xfail(IS_PYPY, reason="Exceptions missing on PyPy") + # TODO: investigate PyPy problem + def test_raises_accidental_env_marker_misconfig(self, config, tmpdir): + fake_env(tmpdir, config) + match = ( + r"One of the parsed requirements in `(install_requires|extras_require.+)` " + "looks like a valid environment marker.*" + ) + with pytest.raises(InvalidRequirement, match=match): + with get_dist(tmpdir) as _: + pass + + @pytest.mark.parametrize( + "config", + [ + "[options.extras_require]\nfoo = bar;python_version<3", + "[options.extras_require]\nfoo = bar;python_version<3\n", + "[options]\ninstall_requires = bar;python_version<3", + "[options]\ninstall_requires = bar;python_version<3\n", + ], + ) + @pytest.mark.xfail(IS_PYPY, reason="Warnings missing on PyPy (minor issue)") + # TODO: investigate PyPy problem + def test_warn_accidental_env_marker_misconfig(self, config, tmpdir): + fake_env(tmpdir, config) + match = ( + r"One of the parsed requirements in `(install_requires|extras_require.+)` " + "looks like a valid environment marker.*" + ) + with pytest.warns(SetuptoolsDeprecationWarning, match=match): + with get_dist(tmpdir) as _: + pass + + @pytest.mark.parametrize( + "config", + [ + "[options.extras_require]\nfoo =\n bar;python_version<'3'", + "[options.extras_require]\nfoo = bar;baz\nboo = xxx;yyy", + "[options.extras_require]\nfoo =\n bar;python_version<'3'\n", + "[options.extras_require]\nfoo = bar;baz\nboo = xxx;yyy\n", + "[options.extras_require]\nfoo =\n bar\n python_version<3\n", + "[options]\ninstall_requires =\n bar;python_version<'3'", + "[options]\ninstall_requires = bar;baz\nboo = xxx;yyy", + "[options]\ninstall_requires =\n bar;python_version<'3'\n", + "[options]\ninstall_requires = bar;baz\nboo = xxx;yyy\n", + "[options]\ninstall_requires =\n bar\n python_version<3\n", + ], + ) + @pytest.mark.filterwarnings("error::setuptools.SetuptoolsDeprecationWarning") + def test_nowarn_accidental_env_marker_misconfig(self, config, tmpdir, recwarn): + fake_env(tmpdir, config) + num_warnings = len(recwarn) + with get_dist(tmpdir) as _: + pass + # The examples are valid, no warnings shown + assert len(recwarn) == num_warnings + + def test_dash_preserved_extras_require(self, tmpdir): + fake_env(tmpdir, '[options.extras_require]\nfoo-a = foo\nfoo_b = test\n') + + with get_dist(tmpdir) as dist: + assert dist.extras_require == {'foo-a': ['foo'], 'foo_b': ['test']} + + def test_entry_points(self, tmpdir): + _, config = fake_env( + tmpdir, + '[options.entry_points]\n' + 'group1 = point1 = pack.module:func, ' + '.point2 = pack.module2:func_rest [rest]\n' + 'group2 = point3 = pack.module:func2\n', + ) + + with get_dist(tmpdir) as dist: + assert dist.entry_points == { + 'group1': [ + 'point1 = pack.module:func', + '.point2 = pack.module2:func_rest [rest]', + ], + 'group2': ['point3 = pack.module:func2'], + } + + expected = ( + '[blogtool.parsers]\n' + '.rst = some.nested.module:SomeClass.some_classmethod[reST]\n' + ) + + tmpdir.join('entry_points').write(expected) + + # From file. + config.write('[options]\nentry_points = file: entry_points\n') + + with get_dist(tmpdir) as dist: + assert dist.entry_points == expected + + def test_case_sensitive_entry_points(self, tmpdir): + fake_env( + tmpdir, + '[options.entry_points]\n' + 'GROUP1 = point1 = pack.module:func, ' + '.point2 = pack.module2:func_rest [rest]\n' + 'group2 = point3 = pack.module:func2\n', + ) + + with get_dist(tmpdir) as dist: + assert dist.entry_points == { + 'GROUP1': [ + 'point1 = pack.module:func', + '.point2 = pack.module2:func_rest [rest]', + ], + 'group2': ['point3 = pack.module:func2'], + } + + def test_data_files(self, tmpdir): + fake_env( + tmpdir, + '[options.data_files]\n' + 'cfg =\n' + ' a/b.conf\n' + ' c/d.conf\n' + 'data = e/f.dat, g/h.dat\n', + ) + + with get_dist(tmpdir) as dist: + expected = [ + ('cfg', ['a/b.conf', 'c/d.conf']), + ('data', ['e/f.dat', 'g/h.dat']), + ] + assert sorted(dist.data_files) == sorted(expected) + + def test_data_files_globby(self, tmpdir): + fake_env( + tmpdir, + '[options.data_files]\n' + 'cfg =\n' + ' a/b.conf\n' + ' c/d.conf\n' + 'data = *.dat\n' + 'icons = \n' + ' *.ico\n' + 'audio = \n' + ' *.wav\n' + ' sounds.db\n', + ) + + # Create dummy files for glob()'s sake: + tmpdir.join('a.dat').write('') + tmpdir.join('b.dat').write('') + tmpdir.join('c.dat').write('') + tmpdir.join('a.ico').write('') + tmpdir.join('b.ico').write('') + tmpdir.join('c.ico').write('') + tmpdir.join('beep.wav').write('') + tmpdir.join('boop.wav').write('') + tmpdir.join('sounds.db').write('') + + with get_dist(tmpdir) as dist: + expected = [ + ('cfg', ['a/b.conf', 'c/d.conf']), + ('data', ['a.dat', 'b.dat', 'c.dat']), + ('icons', ['a.ico', 'b.ico', 'c.ico']), + ('audio', ['beep.wav', 'boop.wav', 'sounds.db']), + ] + assert sorted(dist.data_files) == sorted(expected) + + def test_python_requires_simple(self, tmpdir): + fake_env( + tmpdir, + DALS( + """ + [options] + python_requires=>=2.7 + """ + ), + ) + with get_dist(tmpdir) as dist: + dist.parse_config_files() + + def test_python_requires_compound(self, tmpdir): + fake_env( + tmpdir, + DALS( + """ + [options] + python_requires=>=2.7,!=3.0.* + """ + ), + ) + with get_dist(tmpdir) as dist: + dist.parse_config_files() + + def test_python_requires_invalid(self, tmpdir): + fake_env( + tmpdir, + DALS( + """ + [options] + python_requires=invalid + """ + ), + ) + with pytest.raises(Exception): + with get_dist(tmpdir) as dist: + dist.parse_config_files() + + def test_cmdclass(self, tmpdir): + module_path = Path(tmpdir, "src/custom_build.py") # auto discovery for src + module_path.parent.mkdir(parents=True, exist_ok=True) + module_path.write_text( + "from distutils.core import Command\nclass CustomCmd(Command): pass\n", + encoding="utf-8", + ) + + setup_cfg = """ + [options] + cmdclass = + customcmd = custom_build.CustomCmd + """ + fake_env(tmpdir, inspect.cleandoc(setup_cfg)) + + with get_dist(tmpdir) as dist: + cmdclass = dist.cmdclass['customcmd'] + assert cmdclass.__name__ == "CustomCmd" + assert cmdclass.__module__ == "custom_build" + assert module_path.samefile(inspect.getfile(cmdclass)) + + def test_requirements_file(self, tmpdir): + fake_env( + tmpdir, + DALS( + """ + [options] + install_requires = file:requirements.txt + [options.extras_require] + colors = file:requirements-extra.txt + """ + ), + ) + + tmpdir.join('requirements.txt').write('\ndocutils>=0.3\n\n') + tmpdir.join('requirements-extra.txt').write('colorama') + + with get_dist(tmpdir) as dist: + assert dist.install_requires == ['docutils>=0.3'] + assert dist.extras_require == {'colors': ['colorama']} + + +saved_dist_init = _Distribution.__init__ + + +class TestExternalSetters: + # During creation of the setuptools Distribution() object, we call + # the init of the parent distutils Distribution object via + # _Distribution.__init__ (). + # + # It's possible distutils calls out to various keyword + # implementations (i.e. distutils.setup_keywords entry points) + # that may set a range of variables. + # + # This wraps distutil's Distribution.__init__ and simulates + # pbr or something else setting these values. + def _fake_distribution_init(self, dist, attrs): + saved_dist_init(dist, attrs) + # see self._DISTUTILS_UNSUPPORTED_METADATA + dist.metadata.long_description_content_type = 'text/something' + # Test overwrite setup() args + dist.metadata.project_urls = { + 'Link One': 'https://example.com/one/', + 'Link Two': 'https://example.com/two/', + } + + @patch.object(_Distribution, '__init__', autospec=True) + def test_external_setters(self, mock_parent_init, tmpdir): + mock_parent_init.side_effect = self._fake_distribution_init + + dist = Distribution(attrs={'project_urls': {'will_be': 'ignored'}}) + + assert dist.metadata.long_description_content_type == 'text/something' + assert dist.metadata.project_urls == { + 'Link One': 'https://example.com/one/', + 'Link Two': 'https://example.com/two/', + } diff --git a/lib/python3.12/site-packages/setuptools/tests/contexts.py b/lib/python3.12/site-packages/setuptools/tests/contexts.py new file mode 100644 index 0000000000000000000000000000000000000000..3c931bbd4fd9046702d850a18877622651882d7a --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/contexts.py @@ -0,0 +1,131 @@ +import contextlib +import io +import os +import shutil +import site +import sys +import tempfile + +from filelock import FileLock + + +@contextlib.contextmanager +def tempdir(cd=lambda dir: None, **kwargs): + temp_dir = tempfile.mkdtemp(**kwargs) + orig_dir = os.getcwd() + try: + cd(temp_dir) + yield temp_dir + finally: + cd(orig_dir) + shutil.rmtree(temp_dir) + + +@contextlib.contextmanager +def environment(**replacements): + """ + In a context, patch the environment with replacements. Pass None values + to clear the values. + """ + saved = dict((key, os.environ[key]) for key in replacements if key in os.environ) + + # remove values that are null + remove = (key for (key, value) in replacements.items() if value is None) + for key in list(remove): + os.environ.pop(key, None) + replacements.pop(key) + + os.environ.update(replacements) + + try: + yield saved + finally: + for key in replacements: + os.environ.pop(key, None) + os.environ.update(saved) + + +@contextlib.contextmanager +def quiet(): + """ + Redirect stdout/stderr to StringIO objects to prevent console output from + distutils commands. + """ + + old_stdout = sys.stdout + old_stderr = sys.stderr + new_stdout = sys.stdout = io.StringIO() + new_stderr = sys.stderr = io.StringIO() + try: + yield new_stdout, new_stderr + finally: + new_stdout.seek(0) + new_stderr.seek(0) + sys.stdout = old_stdout + sys.stderr = old_stderr + + +@contextlib.contextmanager +def save_user_site_setting(): + saved = site.ENABLE_USER_SITE + try: + yield saved + finally: + site.ENABLE_USER_SITE = saved + + +@contextlib.contextmanager +def suppress_exceptions(*excs): + try: + yield + except excs: + pass + + +def multiproc(request): + """ + Return True if running under xdist and multiple + workers are used. + """ + try: + worker_id = request.getfixturevalue('worker_id') + except Exception: + return False + return worker_id != 'master' + + +@contextlib.contextmanager +def session_locked_tmp_dir(request, tmp_path_factory, name): + """Uses a file lock to guarantee only one worker can access a temp dir""" + # get the temp directory shared by all workers + base = tmp_path_factory.getbasetemp() + shared_dir = base.parent if multiproc(request) else base + + locked_dir = shared_dir / name + with FileLock(locked_dir.with_suffix(".lock")): + # ^-- prevent multiple workers to access the directory at once + locked_dir.mkdir(exist_ok=True, parents=True) + yield locked_dir + + +@contextlib.contextmanager +def save_paths(): + """Make sure ``sys.path``, ``sys.meta_path`` and ``sys.path_hooks`` are preserved""" + prev = sys.path[:], sys.meta_path[:], sys.path_hooks[:] + + try: + yield + finally: + sys.path, sys.meta_path, sys.path_hooks = prev + + +@contextlib.contextmanager +def save_sys_modules(): + """Make sure initial ``sys.modules`` is preserved""" + prev_modules = sys.modules + + try: + sys.modules = sys.modules.copy() + yield + finally: + sys.modules = prev_modules diff --git a/lib/python3.12/site-packages/setuptools/tests/environment.py b/lib/python3.12/site-packages/setuptools/tests/environment.py new file mode 100644 index 0000000000000000000000000000000000000000..ed5499ef7d73762d033a4877bfe586d6c0b82235 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/environment.py @@ -0,0 +1,95 @@ +import os +import subprocess +import sys +import unicodedata +from subprocess import PIPE as _PIPE, Popen as _Popen + +import jaraco.envs + + +class VirtualEnv(jaraco.envs.VirtualEnv): + name = '.env' + # Some version of PyPy will import distutils on startup, implicitly + # importing setuptools, and thus leading to BackendInvalid errors + # when upgrading Setuptools. Bypass this behavior by avoiding the + # early availability and need to upgrade. + create_opts = ['--no-setuptools'] + + def run(self, cmd, *args, **kwargs): + cmd = [self.exe(cmd[0])] + cmd[1:] + kwargs = {"cwd": self.root, "encoding": "utf-8", **kwargs} # Allow overriding + # In some environments (eg. downstream distro packaging), where: + # - tox isn't used to run tests and + # - PYTHONPATH is set to point to a specific setuptools codebase and + # - no custom env is explicitly set by a test + # PYTHONPATH will leak into the spawned processes. + # In that case tests look for module in the wrong place (on PYTHONPATH). + # Unless the test sets its own special env, pass a copy of the existing + # environment with removed PYTHONPATH to the subprocesses. + if "env" not in kwargs: + env = dict(os.environ) + if "PYTHONPATH" in env: + del env["PYTHONPATH"] + kwargs["env"] = env + return subprocess.check_output(cmd, *args, **kwargs) + + +def _which_dirs(cmd): + result = set() + for path in os.environ.get('PATH', '').split(os.pathsep): + filename = os.path.join(path, cmd) + if os.access(filename, os.X_OK): + result.add(path) + return result + + +def run_setup_py(cmd, pypath=None, path=None, data_stream=0, env=None): + """ + Execution command for tests, separate from those used by the + code directly to prevent accidental behavior issues + """ + if env is None: + env = dict() + for envname in os.environ: + env[envname] = os.environ[envname] + + # override the python path if needed + if pypath is not None: + env["PYTHONPATH"] = pypath + + # override the execution path if needed + if path is not None: + env["PATH"] = path + if not env.get("PATH", ""): + env["PATH"] = _which_dirs("tar").union(_which_dirs("gzip")) + env["PATH"] = os.pathsep.join(env["PATH"]) + + cmd = [sys.executable, "setup.py"] + list(cmd) + + # https://bugs.python.org/issue8557 + shell = sys.platform == 'win32' + + try: + proc = _Popen( + cmd, + stdout=_PIPE, + stderr=_PIPE, + shell=shell, + env=env, + encoding="utf-8", + ) + + if isinstance(data_stream, tuple): + data_stream = slice(*data_stream) + data = proc.communicate()[data_stream] + except OSError: + return 1, '' + + # decode the console string if needed + if hasattr(data, "decode"): + # use the default encoding + data = data.decode() + data = unicodedata.normalize('NFC', data) + + # communicate calls wait() + return proc.returncode, data diff --git a/lib/python3.12/site-packages/setuptools/tests/fixtures.py b/lib/python3.12/site-packages/setuptools/tests/fixtures.py new file mode 100644 index 0000000000000000000000000000000000000000..20b31d4681377745bc2ddaeaf5f0074b4050e5ba --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/fixtures.py @@ -0,0 +1,406 @@ +import contextlib +import io +import os +import subprocess +import sys +import tarfile +import time +from pathlib import Path + +import jaraco.path +import path +import pytest + +from setuptools._normalization import safer_name + +from . import contexts, environment +from .textwrap import DALS + + +@pytest.fixture +def user_override(monkeypatch): + """ + Override site.USER_BASE and site.USER_SITE with temporary directories in + a context. + """ + with contexts.tempdir() as user_base: + monkeypatch.setattr('site.USER_BASE', user_base) + with contexts.tempdir() as user_site: + monkeypatch.setattr('site.USER_SITE', user_site) + with contexts.save_user_site_setting(): + yield + + +@pytest.fixture +def tmpdir_cwd(tmpdir): + with tmpdir.as_cwd() as orig: + yield orig + + +@pytest.fixture(autouse=True, scope="session") +def workaround_xdist_376(request): + """ + Workaround pytest-dev/pytest-xdist#376 + + ``pytest-xdist`` tends to inject '' into ``sys.path``, + which may break certain isolation expectations. + Remove the entry so the import + machinery behaves the same irrespective of xdist. + """ + if not request.config.pluginmanager.has_plugin('xdist'): + return + + with contextlib.suppress(ValueError): + sys.path.remove('') + + +@pytest.fixture +def sample_project(tmp_path): + """ + Clone the 'sampleproject' and return a path to it. + """ + cmd = ['git', 'clone', 'https://github.com/pypa/sampleproject'] + try: + subprocess.check_call(cmd, cwd=str(tmp_path)) + except Exception: + pytest.skip("Unable to clone sampleproject") + return tmp_path / 'sampleproject' + + +@pytest.fixture +def sample_project_cwd(sample_project): + with path.Path(sample_project): + yield + + +# sdist and wheel artifacts should be stable across a round of tests +# so we can build them once per session and use the files as "readonly" + +# In the case of setuptools, building the wheel without sdist may cause +# it to contain the `build` directory, and therefore create situations with +# `setuptools/build/lib/build/lib/...`. To avoid that, build both artifacts at once. + + +def _build_distributions(tmp_path_factory, request): + with contexts.session_locked_tmp_dir( + request, tmp_path_factory, "dist_build" + ) as tmp: # pragma: no cover + sdist = next(tmp.glob("*.tar.gz"), None) + wheel = next(tmp.glob("*.whl"), None) + if sdist and wheel: + return (sdist, wheel) + + # Sanity check: should not create recursive setuptools/build/lib/build/lib/... + assert not Path(request.config.rootdir, "build/lib/build").exists() + + subprocess.check_output([ + sys.executable, + "-m", + "build", + "--outdir", + str(tmp), + str(request.config.rootdir), + ]) + + # Sanity check: should not create recursive setuptools/build/lib/build/lib/... + assert not Path(request.config.rootdir, "build/lib/build").exists() + + return next(tmp.glob("*.tar.gz")), next(tmp.glob("*.whl")) + + +@pytest.fixture(scope="session") +def setuptools_sdist(tmp_path_factory, request): + prebuilt = os.getenv("PRE_BUILT_SETUPTOOLS_SDIST") + if prebuilt and os.path.exists(prebuilt): # pragma: no cover + return Path(prebuilt).resolve() + + sdist, _ = _build_distributions(tmp_path_factory, request) + return sdist + + +@pytest.fixture(scope="session") +def setuptools_wheel(tmp_path_factory, request): + prebuilt = os.getenv("PRE_BUILT_SETUPTOOLS_WHEEL") + if prebuilt and os.path.exists(prebuilt): # pragma: no cover + return Path(prebuilt).resolve() + + _, wheel = _build_distributions(tmp_path_factory, request) + return wheel + + +@pytest.fixture +def venv(tmp_path, setuptools_wheel): + """Virtual env with the version of setuptools under test installed""" + env = environment.VirtualEnv() + env.root = path.Path(tmp_path / 'venv') + env.create_opts = ['--no-setuptools', '--wheel=bundle'] + # TODO: Use `--no-wheel` when setuptools implements its own bdist_wheel + env.req = str(setuptools_wheel) + # In some environments (eg. downstream distro packaging), + # where tox isn't used to run tests and PYTHONPATH is set to point to + # a specific setuptools codebase, PYTHONPATH will leak into the spawned + # processes. + # env.create() should install the just created setuptools + # wheel, but it doesn't if it finds another existing matching setuptools + # installation present on PYTHONPATH: + # `setuptools is already installed with the same version as the provided + # wheel. Use --force-reinstall to force an installation of the wheel.` + # This prevents leaking PYTHONPATH to the created environment. + with contexts.environment(PYTHONPATH=None): + return env.create() + + +@pytest.fixture +def venv_without_setuptools(tmp_path): + """Virtual env without any version of setuptools installed""" + env = environment.VirtualEnv() + env.root = path.Path(tmp_path / 'venv_without_setuptools') + env.create_opts = ['--no-setuptools', '--no-wheel'] + env.ensure_env() + return env + + +@pytest.fixture +def bare_venv(tmp_path): + """Virtual env without any common packages installed""" + env = environment.VirtualEnv() + env.root = path.Path(tmp_path / 'bare_venv') + env.create_opts = ['--no-setuptools', '--no-pip', '--no-wheel', '--no-seed'] + env.ensure_env() + return env + + +def make_sdist(dist_path, files): + """ + Create a simple sdist tarball at dist_path, containing the files + listed in ``files`` as ``(filename, content)`` tuples. + """ + + # Distributions with only one file don't play well with pip. + assert len(files) > 1 + with tarfile.open(dist_path, 'w:gz') as dist: + for filename, content in files: + file_bytes = io.BytesIO(content.encode('utf-8')) + file_info = tarfile.TarInfo(name=filename) + file_info.size = len(file_bytes.getvalue()) + file_info.mtime = int(time.time()) + dist.addfile(file_info, fileobj=file_bytes) + + +def make_trivial_sdist(dist_path, distname, version, setuptools_wheel=None): + """ + Create a simple sdist tarball at dist_path, containing just a simple + setup.py. + + If ``setuptools_wheel`` is passed, a ``pyproject.toml`` file will also + be generated and the passed value will be used as location for + setuptools (as build dependency). + """ + files = [ + ( + 'setup.py', + DALS( + f"""\ + import setuptools + setuptools.setup( + name={distname!r}, + version={version!r} + ) + """ + ), + ), + ('setup.cfg', ''), + ] + + if setuptools_wheel: + files.append(( + "pyproject.toml", + DALS( + f"""\ + [build-system] + requires = ["setuptools @ {setuptools_wheel.as_uri()}"] + build-backend = "setuptools.build_meta" + """ + ), + )) + + make_sdist(dist_path, files) + + +def make_nspkg_sdist(dist_path, distname, version): + """ + Make an sdist tarball with distname and version which also contains one + package with the same name as distname. The top-level package is + designated a namespace package). + """ + # Assert that the distname contains at least one period + assert '.' in distname + + parts = distname.split('.') + nspackage = parts[0] + + packages = ['.'.join(parts[:idx]) for idx in range(1, len(parts) + 1)] + + setup_py = DALS( + f"""\ + import setuptools + setuptools.setup( + name={distname!r}, + version={version!r}, + packages={packages!r}, + namespace_packages=[{nspackage!r}] + ) + """ + ) + + init = "__import__('pkg_resources').declare_namespace(__name__)" + + files = [('setup.py', setup_py), (os.path.join(nspackage, '__init__.py'), init)] + for package in packages[1:]: + filename = os.path.join(*(package.split('.') + ['__init__.py'])) + files.append((filename, '')) + + make_sdist(dist_path, files) + + +def make_python_requires_sdist(dist_path, distname, version, python_requires): + make_sdist( + dist_path, + [ + ( + 'setup.py', + DALS( + """\ + import setuptools + setuptools.setup( + name={name!r}, + version={version!r}, + python_requires={python_requires!r}, + ) + """ + ).format( + name=distname, version=version, python_requires=python_requires + ), + ), + ('setup.cfg', ''), + ], + ) + + +def create_setup_requires_package( + path, + distname='foobar', + version='0.1', + make_package=make_trivial_sdist, + setup_py_template=None, + setup_attrs=None, + use_setup_cfg=(), +): + """Creates a source tree under path for a trivial test package that has a + single requirement in setup_requires--a tarball for that requirement is + also created and added to the dependency_links argument. + + ``distname`` and ``version`` refer to the name/version of the package that + the test package requires via ``setup_requires``. The name of the test + package itself is just 'test_pkg'. + """ + + normalized_distname = safer_name(distname) + test_setup_attrs = { + 'name': 'test_pkg', + 'version': '0.0', + 'setup_requires': [f'{normalized_distname}=={version}'], + 'dependency_links': [os.path.abspath(path)], + } + if setup_attrs: + test_setup_attrs.update(setup_attrs) + + test_pkg = os.path.join(path, 'test_pkg') + os.mkdir(test_pkg) + + # setup.cfg + if use_setup_cfg: + options = [] + metadata = [] + for name in use_setup_cfg: + value = test_setup_attrs.pop(name) + if name in 'name version'.split(): + section = metadata + else: + section = options + if isinstance(value, (tuple, list)): + value = ';'.join(value) + section.append(f'{name}: {value}') + test_setup_cfg_contents = DALS( + """ + [metadata] + {metadata} + [options] + {options} + """ + ).format( + options='\n'.join(options), + metadata='\n'.join(metadata), + ) + else: + test_setup_cfg_contents = '' + with open(os.path.join(test_pkg, 'setup.cfg'), 'w', encoding="utf-8") as f: + f.write(test_setup_cfg_contents) + + # setup.py + if setup_py_template is None: + setup_py_template = DALS( + """\ + import setuptools + setuptools.setup(**%r) + """ + ) + with open(os.path.join(test_pkg, 'setup.py'), 'w', encoding="utf-8") as f: + f.write(setup_py_template % test_setup_attrs) + + foobar_path = os.path.join(path, f'{normalized_distname}-{version}.tar.gz') + make_package(foobar_path, distname, version) + + return test_pkg + + +@pytest.fixture +def pbr_package(tmp_path, monkeypatch, venv): + files = { + "pyproject.toml": DALS( + """ + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + """ + ), + "setup.py": DALS( + """ + __import__('setuptools').setup( + pbr=True, + setup_requires=["pbr"], + ) + """ + ), + "setup.cfg": DALS( + """ + [metadata] + name = mypkg + + [files] + packages = + mypkg + """ + ), + "mypkg": { + "__init__.py": "", + "hello.py": "print('Hello world!')", + }, + "other": {"test.txt": "Another file in here."}, + } + venv.run(["python", "-m", "pip", "install", "pbr"]) + prefix = tmp_path / 'mypkg' + prefix.mkdir() + jaraco.path.build(files, prefix=prefix) + monkeypatch.setenv('PBR_VERSION', "0.42") + return prefix diff --git a/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/external.html b/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/external.html new file mode 100644 index 0000000000000000000000000000000000000000..92e4702f634dfb37a404bec3103b76f6afcaa917 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/external.html @@ -0,0 +1,3 @@ + +bad old link + diff --git a/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/simple/foobar/index.html b/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/simple/foobar/index.html new file mode 100644 index 0000000000000000000000000000000000000000..fefb028bd3ee7d45a414d6e96a7b2a21ffd7eda7 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/simple/foobar/index.html @@ -0,0 +1,4 @@ + +foobar-0.1.tar.gz
+external homepage
+ diff --git a/lib/python3.12/site-packages/setuptools/tests/integration/__init__.py b/lib/python3.12/site-packages/setuptools/tests/integration/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/__init__.cpython-312.pyc b/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05416d4339a20aaaf44011b5d1557e8eb695fcaf Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/__init__.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/helpers.cpython-312.pyc b/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/helpers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20fa00f076e5f7ec114eea80e342412500b9642b Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/helpers.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/test_pbr.cpython-312.pyc b/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/test_pbr.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3983ec4b938c6e02032b94303d200bbef1d858b Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/test_pbr.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/test_pip_install_sdist.cpython-312.pyc b/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/test_pip_install_sdist.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..180c7124c052b5653b990c46462d66e6389bf985 Binary files /dev/null and b/lib/python3.12/site-packages/setuptools/tests/integration/__pycache__/test_pip_install_sdist.cpython-312.pyc differ diff --git a/lib/python3.12/site-packages/setuptools/tests/integration/helpers.py b/lib/python3.12/site-packages/setuptools/tests/integration/helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..16b13022913f62ba86df6b2480d22e3119cdb8cc --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/integration/helpers.py @@ -0,0 +1,80 @@ +"""Reusable functions and classes for different types of integration tests. + +For example ``Archive`` can be used to check the contents of distribution built +with setuptools, and ``run`` will always try to be as verbose as possible to +facilitate debugging. +""" + +from __future__ import annotations + +import os +import subprocess +import tarfile +from collections.abc import Iterator +from pathlib import Path +from zipfile import ZipFile, ZipInfo + + +def run(cmd, env=None): + r = subprocess.run( + cmd, + capture_output=True, + text=True, + encoding="utf-8", + env={**os.environ, **(env or {})}, + # ^-- allow overwriting instead of discarding the current env + ) + + out = r.stdout + "\n" + r.stderr + # pytest omits stdout/err by default, if the test fails they help debugging + print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + print(f"Command: {cmd}\nreturn code: {r.returncode}\n\n{out}") + + if r.returncode == 0: + return out + raise subprocess.CalledProcessError(r.returncode, cmd, r.stdout, r.stderr) + + +class Archive: + """Compatibility layer for ZipFile/Info and TarFile/Info""" + + def __init__(self, filename) -> None: + self._filename = filename + if filename.endswith("tar.gz"): + self._obj: tarfile.TarFile | ZipFile = tarfile.open(filename, "r:gz") + elif filename.endswith("zip"): + self._obj = ZipFile(filename) + else: + raise ValueError(f"{filename} doesn't seem to be a zip or tar.gz") + + def __iter__(self) -> Iterator[ZipInfo] | Iterator[tarfile.TarInfo]: + if hasattr(self._obj, "infolist"): + return iter(self._obj.infolist()) + return iter(self._obj) + + def get_name(self, zip_or_tar_info): + if hasattr(zip_or_tar_info, "filename"): + return zip_or_tar_info.filename + return zip_or_tar_info.name + + def get_content(self, zip_or_tar_info): + if hasattr(self._obj, "extractfile"): + content = self._obj.extractfile(zip_or_tar_info) + if content is None: + msg = f"Invalid {zip_or_tar_info.name} in {self._filename}" + raise ValueError(msg) + return str(content.read(), "utf-8") + return str(self._obj.read(zip_or_tar_info), "utf-8") + + +def get_sdist_members(sdist_path): + with tarfile.open(sdist_path, "r:gz") as tar: + files = [Path(f) for f in tar.getnames()] + # remove root folder + relative_files = ("/".join(f.parts[1:]) for f in files) + return {f for f in relative_files if f} + + +def get_wheel_members(wheel_path): + with ZipFile(wheel_path) as zipfile: + return set(zipfile.namelist()) diff --git a/lib/python3.12/site-packages/setuptools/tests/integration/test_pbr.py b/lib/python3.12/site-packages/setuptools/tests/integration/test_pbr.py new file mode 100644 index 0000000000000000000000000000000000000000..f89e5b8b2151430d0994836dd609f1b75490a336 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/integration/test_pbr.py @@ -0,0 +1,20 @@ +import subprocess + +import pytest + + +@pytest.mark.uses_network +def test_pbr_integration(pbr_package, venv): + """Ensure pbr packages install.""" + cmd = [ + 'python', + '-m', + 'pip', + '-v', + 'install', + '--no-build-isolation', + pbr_package, + ] + venv.run(cmd, stderr=subprocess.STDOUT) + out = venv.run(["python", "-c", "import mypkg.hello"]) + assert "Hello world!" in out diff --git a/lib/python3.12/site-packages/setuptools/tests/integration/test_pip_install_sdist.py b/lib/python3.12/site-packages/setuptools/tests/integration/test_pip_install_sdist.py new file mode 100644 index 0000000000000000000000000000000000000000..4e84f218323e6ad67adfbce07cea5a16e91a311c --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/integration/test_pip_install_sdist.py @@ -0,0 +1,223 @@ +# https://github.com/python/mypy/issues/16936 +# mypy: disable-error-code="has-type" +"""Integration tests for setuptools that focus on building packages via pip. + +The idea behind these tests is not to exhaustively check all the possible +combinations of packages, operating systems, supporting libraries, etc, but +rather check a limited number of popular packages and how they interact with +the exposed public API. This way if any change in API is introduced, we hope to +identify backward compatibility problems before publishing a release. + +The number of tested packages is purposefully kept small, to minimise duration +and the associated maintenance cost (changes in the way these packages define +their build process may require changes in the tests). +""" + +import json +import os +import shutil +import sys +from enum import Enum +from glob import glob +from hashlib import md5 +from urllib.request import urlopen + +import pytest +from packaging.requirements import Requirement + +from .helpers import Archive, run + +pytestmark = pytest.mark.integration + + +(LATEST,) = Enum("v", "LATEST") # type: ignore[misc] # https://github.com/python/mypy/issues/16936 +"""Default version to be checked""" +# There are positive and negative aspects of checking the latest version of the +# packages. +# The main positive aspect is that the latest version might have already +# removed the use of APIs deprecated in previous releases of setuptools. + + +# Packages to be tested: +# (Please notice the test environment cannot support EVERY library required for +# compiling binary extensions. In Ubuntu/Debian nomenclature, we only assume +# that `build-essential`, `gfortran` and `libopenblas-dev` are installed, +# due to their relevance to the numerical/scientific programming ecosystem) +EXAMPLES = [ + ("pip", LATEST), # just in case... + ("pytest", LATEST), # uses setuptools_scm + ("mypy", LATEST), # custom build_py + ext_modules + # --- Popular packages: https://hugovk.github.io/top-pypi-packages/ --- + ("botocore", LATEST), + ("kiwisolver", LATEST), # build_ext + ("brotli", LATEST), # not in the list but used by urllib3 + ("pyyaml", LATEST), # cython + custom build_ext + custom distclass + ("charset-normalizer", LATEST), # uses mypyc, used by aiohttp + ("protobuf", LATEST), + # ("requests", LATEST), # XXX: https://github.com/psf/requests/pull/6920 + ("celery", LATEST), + # When adding packages to this list, make sure they expose a `__version__` + # attribute, or modify the tests below +] + + +# Some packages have "optional" dependencies that modify their build behaviour +# and are not listed in pyproject.toml, others still use `setup_requires` +EXTRA_BUILD_DEPS = { + "pyyaml": ("Cython<3.0",), # constraint to avoid errors + "charset-normalizer": ("mypy>=1.4.1",), # no pyproject.toml available +} + +EXTRA_ENV_VARS = { + "pyyaml": {"PYYAML_FORCE_CYTHON": "1"}, + "charset-normalizer": {"CHARSET_NORMALIZER_USE_MYPYC": "1"}, +} + +IMPORT_NAME = { + "pyyaml": "yaml", + "protobuf": "google.protobuf", +} + + +VIRTUALENV = (sys.executable, "-m", "virtualenv") + + +# By default, pip will try to build packages in isolation (PEP 517), which +# means it will download the previous stable version of setuptools. +# `pip` flags can avoid that (the version of setuptools under test +# should be the one to be used) +INSTALL_OPTIONS = ( + "--ignore-installed", + "--no-build-isolation", + # Omit "--no-binary :all:" the sdist is supplied directly. + # Allows dependencies as wheels. +) +# The downside of `--no-build-isolation` is that pip will not download build +# dependencies. The test script will have to also handle that. + + +@pytest.fixture +def venv_python(tmp_path): + run([*VIRTUALENV, str(tmp_path / ".venv")]) + possible_path = (str(p.parent) for p in tmp_path.glob(".venv/*/python*")) + return shutil.which("python", path=os.pathsep.join(possible_path)) + + +@pytest.fixture(autouse=True) +def _prepare(tmp_path, venv_python, monkeypatch): + download_path = os.getenv("DOWNLOAD_PATH", str(tmp_path)) + os.makedirs(download_path, exist_ok=True) + + # Environment vars used for building some of the packages + monkeypatch.setenv("USE_MYPYC", "1") + + yield + + # Let's provide the maximum amount of information possible in the case + # it is necessary to debug the tests directly from the CI logs. + print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + print("Temporary directory:") + map(print, tmp_path.glob("*")) + print("Virtual environment:") + run([venv_python, "-m", "pip", "freeze"]) + + +@pytest.mark.parametrize(("package", "version"), EXAMPLES) +@pytest.mark.uses_network +def test_install_sdist(package, version, tmp_path, venv_python, setuptools_wheel): + venv_pip = (venv_python, "-m", "pip") + sdist = retrieve_sdist(package, version, tmp_path) + deps = build_deps(package, sdist) + if deps: + print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + print("Dependencies:", deps) + run([*venv_pip, "install", *deps]) + + # Use a virtualenv to simulate PEP 517 isolation + # but install fresh setuptools wheel to ensure the version under development + env = EXTRA_ENV_VARS.get(package, {}) + run([*venv_pip, "install", "--force-reinstall", setuptools_wheel]) + run([*venv_pip, "install", *INSTALL_OPTIONS, sdist], env) + + # Execute a simple script to make sure the package was installed correctly + pkg = IMPORT_NAME.get(package, package).replace("-", "_") + script = f"import {pkg}; print(getattr({pkg}, '__version__', 0))" + run([venv_python, "-c", script]) + + +# ---- Helper Functions ---- + + +def retrieve_sdist(package, version, tmp_path): + """Either use cached sdist file or download it from PyPI""" + # `pip download` cannot be used due to + # https://github.com/pypa/pip/issues/1884 + # https://discuss.python.org/t/pep-625-file-name-of-a-source-distribution/4686 + # We have to find the correct distribution file and download it + download_path = os.getenv("DOWNLOAD_PATH", str(tmp_path)) + dist = retrieve_pypi_sdist_metadata(package, version) + + # Remove old files to prevent cache to grow indefinitely + for file in glob(os.path.join(download_path, f"{package}*")): + if dist["filename"] != file: + os.unlink(file) + + dist_file = os.path.join(download_path, dist["filename"]) + if not os.path.exists(dist_file): + download(dist["url"], dist_file, dist["md5_digest"]) + return dist_file + + +def retrieve_pypi_sdist_metadata(package, version): + # https://warehouse.pypa.io/api-reference/json.html + id_ = package if version is LATEST else f"{package}/{version}" + with urlopen(f"https://pypi.org/pypi/{id_}/json") as f: + metadata = json.load(f) + + if metadata["info"]["yanked"]: + raise ValueError(f"Release for {package} {version} was yanked") + + version = metadata["info"]["version"] + release = metadata["releases"][version] if version is LATEST else metadata["urls"] + (sdist,) = filter(lambda d: d["packagetype"] == "sdist", release) + return sdist + + +def download(url, dest, md5_digest): + with urlopen(url) as f: + data = f.read() + + assert md5(data).hexdigest() == md5_digest + + with open(dest, "wb") as f: + f.write(data) + + assert os.path.exists(dest) + + +def build_deps(package, sdist_file): + """Find out what are the build dependencies for a package. + + "Manually" install them, since pip will not install build + deps with `--no-build-isolation`. + """ + # delay importing, since pytest discovery phase may hit this file from a + # testenv without tomli + from setuptools.compat.py310 import tomllib + + archive = Archive(sdist_file) + info = tomllib.loads(_read_pyproject(archive)) + deps = info.get("build-system", {}).get("requires", []) + deps += EXTRA_BUILD_DEPS.get(package, []) + # Remove setuptools from requirements (and deduplicate) + requirements = {Requirement(d).name: d for d in deps} + return [v for k, v in requirements.items() if k != "setuptools"] + + +def _read_pyproject(archive): + contents = ( + archive.get_content(member) + for member in archive + if os.path.basename(archive.get_name(member)) == "pyproject.toml" + ) + return next(contents, "") diff --git a/lib/python3.12/site-packages/setuptools/tests/mod_with_constant.py b/lib/python3.12/site-packages/setuptools/tests/mod_with_constant.py new file mode 100644 index 0000000000000000000000000000000000000000..ef755dd1c7a8d1f116fe51f1b43315057f03379d --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/mod_with_constant.py @@ -0,0 +1 @@ +value = 'three, sir!' diff --git a/lib/python3.12/site-packages/setuptools/tests/namespaces.py b/lib/python3.12/site-packages/setuptools/tests/namespaces.py new file mode 100644 index 0000000000000000000000000000000000000000..248db98f97951aeeee0222131417e73074cc72d2 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/namespaces.py @@ -0,0 +1,90 @@ +import ast +import json +import textwrap +from pathlib import Path + + +def iter_namespace_pkgs(namespace): + parts = namespace.split(".") + for i in range(len(parts)): + yield ".".join(parts[: i + 1]) + + +def build_namespace_package(tmpdir, name, version="1.0", impl="pkg_resources"): + src_dir = tmpdir / name + src_dir.mkdir() + setup_py = src_dir / 'setup.py' + namespace, _, rest = name.rpartition('.') + namespaces = list(iter_namespace_pkgs(namespace)) + setup_args = { + "name": name, + "version": version, + "packages": namespaces, + } + + if impl == "pkg_resources": + tmpl = '__import__("pkg_resources").declare_namespace(__name__)' + setup_args["namespace_packages"] = namespaces + elif impl == "pkgutil": + tmpl = '__path__ = __import__("pkgutil").extend_path(__path__, __name__)' + else: + raise ValueError(f"Cannot recognise {impl=} when creating namespaces") + + args = json.dumps(setup_args, indent=4) + assert ast.literal_eval(args) # ensure it is valid Python + + script = textwrap.dedent( + """\ + import setuptools + args = {args} + setuptools.setup(**args) + """ + ).format(args=args) + setup_py.write_text(script, encoding='utf-8') + + ns_pkg_dir = Path(src_dir, namespace.replace(".", "/")) + ns_pkg_dir.mkdir(parents=True) + + for ns in namespaces: + pkg_init = src_dir / ns.replace(".", "/") / '__init__.py' + pkg_init.write_text(tmpl, encoding='utf-8') + + pkg_mod = ns_pkg_dir / (rest + '.py') + some_functionality = 'name = {rest!r}'.format(**locals()) + pkg_mod.write_text(some_functionality, encoding='utf-8') + return src_dir + + +def build_pep420_namespace_package(tmpdir, name): + src_dir = tmpdir / name + src_dir.mkdir() + pyproject = src_dir / "pyproject.toml" + namespace, _, rest = name.rpartition(".") + script = f"""\ + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + + [project] + name = "{name}" + version = "3.14159" + """ + pyproject.write_text(textwrap.dedent(script), encoding='utf-8') + ns_pkg_dir = Path(src_dir, namespace.replace(".", "/")) + ns_pkg_dir.mkdir(parents=True) + pkg_mod = ns_pkg_dir / (rest + ".py") + some_functionality = f"name = {rest!r}" + pkg_mod.write_text(some_functionality, encoding='utf-8') + return src_dir + + +def make_site_dir(target): + """ + Add a sitecustomize.py module in target to cause + target to be added to site dirs such that .pth files + are processed there. + """ + sc = target / 'sitecustomize.py' + target_str = str(target) + tmpl = '__import__("site").addsitedir({target_str!r})' + sc.write_text(tmpl.format(**locals()), encoding='utf-8') diff --git a/lib/python3.12/site-packages/setuptools/tests/script-with-bom.py b/lib/python3.12/site-packages/setuptools/tests/script-with-bom.py new file mode 100644 index 0000000000000000000000000000000000000000..c074d263c45bcaebe32fdba328d975c73d1ad5ca --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/script-with-bom.py @@ -0,0 +1 @@ +result = 'passed' diff --git a/lib/python3.12/site-packages/setuptools/tests/test_archive_util.py b/lib/python3.12/site-packages/setuptools/tests/test_archive_util.py new file mode 100644 index 0000000000000000000000000000000000000000..e3efc62889994fa68bc9170e8a0e403f48a204e1 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_archive_util.py @@ -0,0 +1,36 @@ +import io +import tarfile + +import pytest + +from setuptools import archive_util + + +@pytest.fixture +def tarfile_with_unicode(tmpdir): + """ + Create a tarfile containing only a file whose name is + a zero byte file called testimäge.png. + """ + tarobj = io.BytesIO() + + with tarfile.open(fileobj=tarobj, mode="w:gz") as tgz: + data = b"" + + filename = "testimäge.png" + + t = tarfile.TarInfo(filename) + t.size = len(data) + + tgz.addfile(t, io.BytesIO(data)) + + target = tmpdir / 'unicode-pkg-1.0.tar.gz' + with open(str(target), mode='wb') as tf: + tf.write(tarobj.getvalue()) + return str(target) + + +@pytest.mark.xfail(reason="#710 and #712") +def test_unicode_files(tarfile_with_unicode, tmpdir): + target = tmpdir / 'out' + archive_util.unpack_archive(tarfile_with_unicode, str(target)) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_bdist_deprecations.py b/lib/python3.12/site-packages/setuptools/tests/test_bdist_deprecations.py new file mode 100644 index 0000000000000000000000000000000000000000..d9d67b06161a2b36e7b15fab09f797c5c15575c2 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_bdist_deprecations.py @@ -0,0 +1,28 @@ +"""develop tests""" + +import sys +from unittest import mock + +import pytest + +from setuptools import SetuptoolsDeprecationWarning +from setuptools.dist import Distribution + + +@pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only') +@pytest.mark.xfail(reason="bdist_rpm is long deprecated, should we remove it? #1988") +@mock.patch('distutils.command.bdist_rpm.bdist_rpm') +def test_bdist_rpm_warning(distutils_cmd, tmpdir_cwd): + dist = Distribution( + dict( + script_name='setup.py', + script_args=['bdist_rpm'], + name='foo', + py_modules=['hi'], + ) + ) + dist.parse_command_line() + with pytest.warns(SetuptoolsDeprecationWarning): + dist.run_commands() + + distutils_cmd.run.assert_called_once() diff --git a/lib/python3.12/site-packages/setuptools/tests/test_bdist_egg.py b/lib/python3.12/site-packages/setuptools/tests/test_bdist_egg.py new file mode 100644 index 0000000000000000000000000000000000000000..036167dd951e70ad543775529d5ce3f6d6544c71 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_bdist_egg.py @@ -0,0 +1,73 @@ +"""develop tests""" + +import os +import re +import zipfile + +import pytest + +from setuptools.dist import Distribution + +from . import contexts + +SETUP_PY = """\ +from setuptools import setup + +setup(py_modules=['hi']) +""" + + +@pytest.fixture +def setup_context(tmpdir): + with (tmpdir / 'setup.py').open('w') as f: + f.write(SETUP_PY) + with (tmpdir / 'hi.py').open('w') as f: + f.write('1\n') + with tmpdir.as_cwd(): + yield tmpdir + + +class Test: + @pytest.mark.usefixtures("user_override") + @pytest.mark.usefixtures("setup_context") + def test_bdist_egg(self): + dist = Distribution( + dict( + script_name='setup.py', + script_args=['bdist_egg'], + name='foo', + py_modules=['hi'], + ) + ) + os.makedirs(os.path.join('build', 'src')) + with contexts.quiet(): + dist.parse_command_line() + dist.run_commands() + + # let's see if we got our egg link at the right place + [content] = os.listdir('dist') + assert re.match(r'foo-0.0.0-py[23].\d+.egg$', content) + + @pytest.mark.xfail( + os.environ.get('PYTHONDONTWRITEBYTECODE', False), + reason="Byte code disabled", + ) + @pytest.mark.usefixtures("user_override") + @pytest.mark.usefixtures("setup_context") + def test_exclude_source_files(self): + dist = Distribution( + dict( + script_name='setup.py', + script_args=['bdist_egg', '--exclude-source-files'], + py_modules=['hi'], + ) + ) + with contexts.quiet(): + dist.parse_command_line() + dist.run_commands() + [dist_name] = os.listdir('dist') + dist_filename = os.path.join('dist', dist_name) + zip = zipfile.ZipFile(dist_filename) + names = list(zi.filename for zi in zip.filelist) + assert 'hi.pyc' in names + assert 'hi.py' not in names diff --git a/lib/python3.12/site-packages/setuptools/tests/test_bdist_wheel.py b/lib/python3.12/site-packages/setuptools/tests/test_bdist_wheel.py new file mode 100644 index 0000000000000000000000000000000000000000..68cc0c4d3662bcf6f0881df54105ebaf38403564 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_bdist_wheel.py @@ -0,0 +1,708 @@ +from __future__ import annotations + +import builtins +import importlib +import os.path +import platform +import shutil +import stat +import struct +import sys +import sysconfig +from contextlib import suppress +from inspect import cleandoc +from zipfile import ZipFile + +import jaraco.path +import pytest +from packaging import tags + +import setuptools +from setuptools.command.bdist_wheel import bdist_wheel, get_abi_tag +from setuptools.dist import Distribution +from setuptools.warnings import SetuptoolsDeprecationWarning + +from distutils.core import run_setup + +DEFAULT_FILES = { + "dummy_dist-1.0.dist-info/top_level.txt", + "dummy_dist-1.0.dist-info/METADATA", + "dummy_dist-1.0.dist-info/WHEEL", + "dummy_dist-1.0.dist-info/RECORD", +} +DEFAULT_LICENSE_FILES = { + "LICENSE", + "LICENSE.txt", + "LICENCE", + "LICENCE.txt", + "COPYING", + "COPYING.md", + "NOTICE", + "NOTICE.rst", + "AUTHORS", + "AUTHORS.txt", +} +OTHER_IGNORED_FILES = { + "LICENSE~", + "AUTHORS~", +} +SETUPPY_EXAMPLE = """\ +from setuptools import setup + +setup( + name='dummy_dist', + version='1.0', +) +""" + + +EXAMPLES = { + "dummy-dist": { + "setup.py": SETUPPY_EXAMPLE, + "licenses_dir": {"DUMMYFILE": ""}, + **dict.fromkeys(DEFAULT_LICENSE_FILES | OTHER_IGNORED_FILES, ""), + }, + "simple-dist": { + "setup.py": cleandoc( + """ + from setuptools import setup + + setup( + name="simple.dist", + version="0.1", + description="A testing distribution \N{SNOWMAN}", + extras_require={"voting": ["beaglevote"]}, + ) + """ + ), + "simpledist": "", + }, + "complex-dist": { + "setup.py": cleandoc( + """ + from setuptools import setup + + setup( + name="complex-dist", + version="0.1", + description="Another testing distribution \N{SNOWMAN}", + long_description="Another testing distribution \N{SNOWMAN}", + author="Illustrious Author", + author_email="illustrious@example.org", + url="http://example.org/exemplary", + packages=["complexdist"], + setup_requires=["setuptools"], + install_requires=["quux", "splort"], + extras_require={"simple": ["simple.dist"]}, + entry_points={ + "console_scripts": [ + "complex-dist=complexdist:main", + "complex-dist2=complexdist:main", + ], + }, + ) + """ + ), + "complexdist": {"__init__.py": "def main(): return"}, + }, + "headers-dist": { + "setup.py": cleandoc( + """ + from setuptools import setup + + setup( + name="headers.dist", + version="0.1", + description="A distribution with headers", + headers=["header.h"], + ) + """ + ), + "headersdist.py": "", + "header.h": "", + }, + "commasinfilenames-dist": { + "setup.py": cleandoc( + """ + from setuptools import setup + + setup( + name="testrepo", + version="0.1", + packages=["mypackage"], + description="A test package with commas in file names", + include_package_data=True, + package_data={"mypackage.data": ["*"]}, + ) + """ + ), + "mypackage": { + "__init__.py": "", + "data": {"__init__.py": "", "1,2,3.txt": ""}, + }, + "testrepo-0.1.0": { + "mypackage": {"__init__.py": ""}, + }, + }, + "unicode-dist": { + "setup.py": cleandoc( + """ + from setuptools import setup + + setup( + name="unicode.dist", + version="0.1", + description="A testing distribution \N{SNOWMAN}", + packages=["unicodedist"], + zip_safe=True, + ) + """ + ), + "unicodedist": {"__init__.py": "", "åäö_日本語.py": ""}, + }, + "utf8-metadata-dist": { + "setup.cfg": cleandoc( + """ + [metadata] + name = utf8-metadata-dist + version = 42 + author_email = "John X. Ãørçeč" , Γαμα קּ 東 + long_description = file: README.rst + """ + ), + "README.rst": "UTF-8 描述 説明", + }, + "licenses-dist": { + "setup.cfg": cleandoc( + """ + [metadata] + name = licenses-dist + version = 1.0 + license_files = **/LICENSE + """ + ), + "LICENSE": "", + "src": { + "vendor": {"LICENSE": ""}, + }, + }, +} + + +if sys.platform != "win32": + # ABI3 extensions don't really work on Windows + EXAMPLES["abi3extension-dist"] = { + "setup.py": cleandoc( + """ + from setuptools import Extension, setup + + setup( + name="extension.dist", + version="0.1", + description="A testing distribution \N{SNOWMAN}", + ext_modules=[ + Extension( + name="extension", sources=["extension.c"], py_limited_api=True + ) + ], + ) + """ + ), + "setup.cfg": "[bdist_wheel]\npy_limited_api=cp32", + "extension.c": "#define Py_LIMITED_API 0x03020000\n#include ", + } + + +def bdist_wheel_cmd(**kwargs): + """Run command in the same process so that it is easier to collect coverage""" + dist_obj = ( + run_setup("setup.py", stop_after="init") + if os.path.exists("setup.py") + else Distribution({"script_name": "%%build_meta%%"}) + ) + dist_obj.parse_config_files() + cmd = bdist_wheel(dist_obj) + for attr, value in kwargs.items(): + setattr(cmd, attr, value) + cmd.finalize_options() + return cmd + + +def mkexample(tmp_path_factory, name): + basedir = tmp_path_factory.mktemp(name) + jaraco.path.build(EXAMPLES[name], prefix=str(basedir)) + return basedir + + +@pytest.fixture(scope="session") +def wheel_paths(tmp_path_factory): + build_base = tmp_path_factory.mktemp("build") + dist_dir = tmp_path_factory.mktemp("dist") + for name in EXAMPLES: + example_dir = mkexample(tmp_path_factory, name) + build_dir = build_base / name + with jaraco.path.DirectoryStack().context(example_dir): + bdist_wheel_cmd(bdist_dir=str(build_dir), dist_dir=str(dist_dir)).run() + + return sorted(str(fname) for fname in dist_dir.glob("*.whl")) + + +@pytest.fixture +def dummy_dist(tmp_path_factory): + return mkexample(tmp_path_factory, "dummy-dist") + + +@pytest.fixture +def licenses_dist(tmp_path_factory): + return mkexample(tmp_path_factory, "licenses-dist") + + +def test_no_scripts(wheel_paths): + """Make sure entry point scripts are not generated.""" + path = next(path for path in wheel_paths if "complex_dist" in path) + for entry in ZipFile(path).infolist(): + assert ".data/scripts/" not in entry.filename + + +def test_unicode_record(wheel_paths): + path = next(path for path in wheel_paths if "unicode_dist" in path) + with ZipFile(path) as zf: + record = zf.read("unicode_dist-0.1.dist-info/RECORD") + + assert "åäö_日本語.py".encode() in record + + +UTF8_PKG_INFO = """\ +Metadata-Version: 2.1 +Name: helloworld +Version: 42 +Author-email: "John X. Ãørçeč" , Γαμα קּ 東 + + +UTF-8 描述 説明 +""" + + +def test_preserve_unicode_metadata(monkeypatch, tmp_path): + monkeypatch.chdir(tmp_path) + egginfo = tmp_path / "dummy_dist.egg-info" + distinfo = tmp_path / "dummy_dist.dist-info" + + egginfo.mkdir() + (egginfo / "PKG-INFO").write_text(UTF8_PKG_INFO, encoding="utf-8") + (egginfo / "dependency_links.txt").touch() + + class simpler_bdist_wheel(bdist_wheel): + """Avoid messing with setuptools/distutils internals""" + + def __init__(self) -> None: + pass + + @property + def license_paths(self): + return [] + + cmd_obj = simpler_bdist_wheel() + cmd_obj.egg2dist(egginfo, distinfo) + + metadata = (distinfo / "METADATA").read_text(encoding="utf-8") + assert 'Author-email: "John X. Ãørçeč"' in metadata + assert "Γαμα קּ 東 " in metadata + assert "UTF-8 描述 説明" in metadata + + +def test_licenses_default(dummy_dist, monkeypatch, tmp_path): + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path)).run() + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + license_files = { + "dummy_dist-1.0.dist-info/licenses/" + fname + for fname in DEFAULT_LICENSE_FILES + } + assert set(wf.namelist()) == DEFAULT_FILES | license_files + + +def test_licenses_deprecated(dummy_dist, monkeypatch, tmp_path): + dummy_dist.joinpath("setup.cfg").write_text( + "[metadata]\nlicense_file=licenses_dir/DUMMYFILE", encoding="utf-8" + ) + monkeypatch.chdir(dummy_dist) + + bdist_wheel_cmd(bdist_dir=str(tmp_path)).run() + + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + license_files = {"dummy_dist-1.0.dist-info/licenses/licenses_dir/DUMMYFILE"} + assert set(wf.namelist()) == DEFAULT_FILES | license_files + + +@pytest.mark.parametrize( + ("config_file", "config"), + [ + ("setup.cfg", "[metadata]\nlicense_files=licenses_dir/*\n LICENSE"), + ("setup.cfg", "[metadata]\nlicense_files=licenses_dir/*, LICENSE"), + ( + "setup.py", + SETUPPY_EXAMPLE.replace( + ")", " license_files=['licenses_dir/DUMMYFILE', 'LICENSE'])" + ), + ), + ], +) +def test_licenses_override(dummy_dist, monkeypatch, tmp_path, config_file, config): + dummy_dist.joinpath(config_file).write_text(config, encoding="utf-8") + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path)).run() + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + license_files = { + "dummy_dist-1.0.dist-info/licenses/" + fname + for fname in {"licenses_dir/DUMMYFILE", "LICENSE"} + } + assert set(wf.namelist()) == DEFAULT_FILES | license_files + metadata = wf.read("dummy_dist-1.0.dist-info/METADATA").decode("utf8") + assert "License-File: licenses_dir/DUMMYFILE" in metadata + assert "License-File: LICENSE" in metadata + + +def test_licenses_preserve_folder_structure(licenses_dist, monkeypatch, tmp_path): + monkeypatch.chdir(licenses_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path)).run() + print(os.listdir("dist")) + with ZipFile("dist/licenses_dist-1.0-py3-none-any.whl") as wf: + default_files = {name.replace("dummy_", "licenses_") for name in DEFAULT_FILES} + license_files = { + "licenses_dist-1.0.dist-info/licenses/LICENSE", + "licenses_dist-1.0.dist-info/licenses/src/vendor/LICENSE", + } + assert set(wf.namelist()) == default_files | license_files + metadata = wf.read("licenses_dist-1.0.dist-info/METADATA").decode("utf8") + assert "License-File: src/vendor/LICENSE" in metadata + assert "License-File: LICENSE" in metadata + + +def test_licenses_disabled(dummy_dist, monkeypatch, tmp_path): + dummy_dist.joinpath("setup.cfg").write_text( + "[metadata]\nlicense_files=\n", encoding="utf-8" + ) + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path)).run() + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + assert set(wf.namelist()) == DEFAULT_FILES + + +def test_build_number(dummy_dist, monkeypatch, tmp_path): + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path), build_number="2").run() + with ZipFile("dist/dummy_dist-1.0-2-py3-none-any.whl") as wf: + filenames = set(wf.namelist()) + assert "dummy_dist-1.0.dist-info/RECORD" in filenames + assert "dummy_dist-1.0.dist-info/METADATA" in filenames + + +def test_universal_deprecated(dummy_dist, monkeypatch, tmp_path): + monkeypatch.chdir(dummy_dist) + with pytest.warns(SetuptoolsDeprecationWarning, match=".*universal is deprecated"): + bdist_wheel_cmd(bdist_dir=str(tmp_path), universal=True).run() + + # For now we still respect the option + assert os.path.exists("dist/dummy_dist-1.0-py2.py3-none-any.whl") + + +EXTENSION_EXAMPLE = """\ +#include + +static PyMethodDef methods[] = { + { NULL, NULL, 0, NULL } +}; + +static struct PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "extension", + "Dummy extension module", + -1, + methods +}; + +PyMODINIT_FUNC PyInit_extension(void) { + return PyModule_Create(&module_def); +} +""" +EXTENSION_SETUPPY = """\ +from __future__ import annotations + +from setuptools import Extension, setup + +setup( + name="extension.dist", + version="0.1", + description="A testing distribution \N{SNOWMAN}", + ext_modules=[Extension(name="extension", sources=["extension.c"])], +) +""" + + +@pytest.mark.filterwarnings( + "once:Config variable '.*' is unset.*, Python ABI tag may be incorrect" +) +def test_limited_abi(monkeypatch, tmp_path, tmp_path_factory): + """Test that building a binary wheel with the limited ABI works.""" + source_dir = tmp_path_factory.mktemp("extension_dist") + (source_dir / "setup.py").write_text(EXTENSION_SETUPPY, encoding="utf-8") + (source_dir / "extension.c").write_text(EXTENSION_EXAMPLE, encoding="utf-8") + build_dir = tmp_path.joinpath("build") + dist_dir = tmp_path.joinpath("dist") + monkeypatch.chdir(source_dir) + bdist_wheel_cmd(bdist_dir=str(build_dir), dist_dir=str(dist_dir)).run() + + +def test_build_from_readonly_tree(dummy_dist, monkeypatch, tmp_path): + basedir = str(tmp_path.joinpath("dummy")) + shutil.copytree(str(dummy_dist), basedir) + monkeypatch.chdir(basedir) + + # Make the tree read-only + for root, _dirs, files in os.walk(basedir): + for fname in files: + os.chmod(os.path.join(root, fname), stat.S_IREAD) + + bdist_wheel_cmd().run() + + +@pytest.mark.parametrize( + ("option", "compress_type"), + list(bdist_wheel.supported_compressions.items()), + ids=list(bdist_wheel.supported_compressions), +) +def test_compression(dummy_dist, monkeypatch, tmp_path, option, compress_type): + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path), compression=option).run() + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + filenames = set(wf.namelist()) + assert "dummy_dist-1.0.dist-info/RECORD" in filenames + assert "dummy_dist-1.0.dist-info/METADATA" in filenames + for zinfo in wf.filelist: + assert zinfo.compress_type == compress_type + + +def test_wheelfile_line_endings(wheel_paths): + for path in wheel_paths: + with ZipFile(path) as wf: + wheelfile = next(fn for fn in wf.filelist if fn.filename.endswith("WHEEL")) + wheelfile_contents = wf.read(wheelfile) + assert b"\r" not in wheelfile_contents + + +def test_unix_epoch_timestamps(dummy_dist, monkeypatch, tmp_path): + monkeypatch.setenv("SOURCE_DATE_EPOCH", "0") + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path), build_number="2a").run() + with ZipFile("dist/dummy_dist-1.0-2a-py3-none-any.whl") as wf: + for zinfo in wf.filelist: + assert zinfo.date_time >= (1980, 1, 1, 0, 0, 0) # min epoch is used + + +def test_get_abi_tag_windows(monkeypatch): + monkeypatch.setattr(tags, "interpreter_name", lambda: "cp") + monkeypatch.setattr(sysconfig, "get_config_var", lambda x: "cp313-win_amd64") + assert get_abi_tag() == "cp313" + monkeypatch.setattr(sys, "gettotalrefcount", lambda: 1, False) + assert get_abi_tag() == "cp313d" + monkeypatch.setattr(sysconfig, "get_config_var", lambda x: "cp313t-win_amd64") + assert get_abi_tag() == "cp313td" + monkeypatch.delattr(sys, "gettotalrefcount") + assert get_abi_tag() == "cp313t" + + +def test_get_abi_tag_pypy_old(monkeypatch): + monkeypatch.setattr(tags, "interpreter_name", lambda: "pp") + monkeypatch.setattr(sysconfig, "get_config_var", lambda x: "pypy36-pp73") + assert get_abi_tag() == "pypy36_pp73" + + +def test_get_abi_tag_pypy_new(monkeypatch): + monkeypatch.setattr(sysconfig, "get_config_var", lambda x: "pypy37-pp73-darwin") + monkeypatch.setattr(tags, "interpreter_name", lambda: "pp") + assert get_abi_tag() == "pypy37_pp73" + + +def test_get_abi_tag_graalpy(monkeypatch): + monkeypatch.setattr( + sysconfig, "get_config_var", lambda x: "graalpy231-310-native-x86_64-linux" + ) + monkeypatch.setattr(tags, "interpreter_name", lambda: "graalpy") + assert get_abi_tag() == "graalpy231_310_native" + + +def test_get_abi_tag_fallback(monkeypatch): + monkeypatch.setattr(sysconfig, "get_config_var", lambda x: "unknown-python-310") + monkeypatch.setattr(tags, "interpreter_name", lambda: "unknown-python") + assert get_abi_tag() == "unknown_python_310" + + +def test_platform_with_space(dummy_dist, monkeypatch): + """Ensure building on platforms with a space in the name succeed.""" + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(plat_name="isilon onefs").run() + + +def test_data_dir_with_tag_build(monkeypatch, tmp_path): + """ + Setuptools allow authors to set PEP 440's local version segments + using ``egg_info.tag_build``. This should be reflected not only in the + ``.whl`` file name, but also in the ``.dist-info`` and ``.data`` dirs. + See pypa/setuptools#3997. + """ + monkeypatch.chdir(tmp_path) + files = { + "setup.py": """ + from setuptools import setup + setup(headers=["hello.h"]) + """, + "setup.cfg": """ + [metadata] + name = test + version = 1.0 + + [options.data_files] + hello/world = file.txt + + [egg_info] + tag_build = +what + tag_date = 0 + """, + "file.txt": "", + "hello.h": "", + } + for file, content in files.items(): + with open(file, "w", encoding="utf-8") as fh: + fh.write(cleandoc(content)) + + bdist_wheel_cmd().run() + + # Ensure .whl, .dist-info and .data contain the local segment + wheel_path = "dist/test-1.0+what-py3-none-any.whl" + assert os.path.exists(wheel_path) + entries = set(ZipFile(wheel_path).namelist()) + for expected in ( + "test-1.0+what.data/headers/hello.h", + "test-1.0+what.data/data/hello/world/file.txt", + "test-1.0+what.dist-info/METADATA", + "test-1.0+what.dist-info/WHEEL", + ): + assert expected in entries + + for not_expected in ( + "test.data/headers/hello.h", + "test-1.0.data/data/hello/world/file.txt", + "test.dist-info/METADATA", + "test-1.0.dist-info/WHEEL", + ): + assert not_expected not in entries + + +@pytest.mark.parametrize( + ("reported", "expected"), + [("linux-x86_64", "linux_i686"), ("linux-aarch64", "linux_armv7l")], +) +@pytest.mark.skipif( + platform.system() != "Linux", reason="Only makes sense to test on Linux" +) +def test_platform_linux32(reported, expected, monkeypatch): + monkeypatch.setattr(struct, "calcsize", lambda x: 4) + dist = setuptools.Distribution() + cmd = bdist_wheel(dist) + cmd.plat_name = reported + cmd.root_is_pure = False + _, _, actual = cmd.get_tag() + assert actual == expected + + +def test_no_ctypes(monkeypatch) -> None: + def _fake_import(name: str, *args, **kwargs): + if name == "ctypes": + raise ModuleNotFoundError(f"No module named {name}") + + return importlib.__import__(name, *args, **kwargs) + + with suppress(KeyError): + monkeypatch.delitem(sys.modules, "wheel.macosx_libfile") + + # Install an importer shim that refuses to load ctypes + monkeypatch.setattr(builtins, "__import__", _fake_import) + with pytest.raises(ModuleNotFoundError, match="No module named ctypes"): + import wheel.macosx_libfile # noqa: F401 + + # Unload and reimport the bdist_wheel command module to make sure it won't try to + # import ctypes + monkeypatch.delitem(sys.modules, "setuptools.command.bdist_wheel") + + import setuptools.command.bdist_wheel # noqa: F401 + + +def test_dist_info_provided(dummy_dist, monkeypatch, tmp_path): + monkeypatch.chdir(dummy_dist) + distinfo = tmp_path / "dummy_dist.dist-info" + + distinfo.mkdir() + (distinfo / "METADATA").write_text("name: helloworld", encoding="utf-8") + + # We don't control the metadata. According to PEP-517, "The hook MAY also + # create other files inside this directory, and a build frontend MUST + # preserve". + (distinfo / "FOO").write_text("bar", encoding="utf-8") + + bdist_wheel_cmd(bdist_dir=str(tmp_path), dist_info_dir=str(distinfo)).run() + expected = { + "dummy_dist-1.0.dist-info/FOO", + "dummy_dist-1.0.dist-info/RECORD", + } + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + files_found = set(wf.namelist()) + # Check that all expected files are there. + assert expected - files_found == set() + # Make sure there is no accidental egg-info bleeding into the wheel. + assert not [path for path in files_found if 'egg-info' in str(path)] + + +def test_allow_grace_period_parent_directory_license(monkeypatch, tmp_path): + # Motivation: https://github.com/pypa/setuptools/issues/4892 + # TODO: Remove this test after deprecation period is over + files = { + "LICENSE.txt": "parent license", # <---- the license files are outside + "NOTICE.txt": "parent notice", + "python": { + "pyproject.toml": cleandoc( + """ + [project] + name = "test-proj" + dynamic = ["version"] # <---- testing dynamic will not break + [tool.setuptools.dynamic] + version.file = "VERSION" + """ + ), + "setup.cfg": cleandoc( + """ + [metadata] + license_files = + ../LICENSE.txt + ../NOTICE.txt + """ + ), + "VERSION": "42", + }, + } + jaraco.path.build(files, prefix=str(tmp_path)) + monkeypatch.chdir(tmp_path / "python") + msg = "Pattern '../.*.txt' cannot contain '..'" + with pytest.warns(SetuptoolsDeprecationWarning, match=msg): + bdist_wheel_cmd().run() + with ZipFile("dist/test_proj-42-py3-none-any.whl") as wf: + files_found = set(wf.namelist()) + expected_files = { + "test_proj-42.dist-info/licenses/LICENSE.txt", + "test_proj-42.dist-info/licenses/NOTICE.txt", + } + assert expected_files <= files_found + + metadata = wf.read("test_proj-42.dist-info/METADATA").decode("utf8") + assert "License-File: LICENSE.txt" in metadata + assert "License-File: NOTICE.txt" in metadata diff --git a/lib/python3.12/site-packages/setuptools/tests/test_build.py b/lib/python3.12/site-packages/setuptools/tests/test_build.py new file mode 100644 index 0000000000000000000000000000000000000000..f0f1d9dcf21bafe9dc82a76d373b366ddeecfcec --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_build.py @@ -0,0 +1,33 @@ +from setuptools import Command +from setuptools.command.build import build +from setuptools.dist import Distribution + + +def test_distribution_gives_setuptools_build_obj(tmpdir_cwd): + """ + Check that the setuptools Distribution uses the + setuptools specific build object. + """ + + dist = Distribution( + dict( + script_name='setup.py', + script_args=['build'], + packages=[], + package_data={'': ['path/*']}, + ) + ) + assert isinstance(dist.get_command_obj("build"), build) + + +class Subcommand(Command): + """Dummy command to be used in tests""" + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + raise NotImplementedError("just to check if the command runs") diff --git a/lib/python3.12/site-packages/setuptools/tests/test_build_clib.py b/lib/python3.12/site-packages/setuptools/tests/test_build_clib.py new file mode 100644 index 0000000000000000000000000000000000000000..b5315df4f6599cd376e628c0d74cb14129cd89b8 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_build_clib.py @@ -0,0 +1,84 @@ +import random +from unittest import mock + +import pytest + +from setuptools.command.build_clib import build_clib +from setuptools.dist import Distribution + +from distutils.errors import DistutilsSetupError + + +class TestBuildCLib: + @mock.patch('setuptools.command.build_clib.newer_pairwise_group') + def test_build_libraries(self, mock_newer): + dist = Distribution() + cmd = build_clib(dist) + + # this will be a long section, just making sure all + # exceptions are properly raised + libs = [('example', {'sources': 'broken.c'})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + obj_deps = 'some_string' + libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + obj_deps = {'': ''} + libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + obj_deps = {'source.c': ''} + libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + # with that out of the way, let's see if the crude dependency + # system works + cmd.compiler = mock.MagicMock(spec=cmd.compiler) + mock_newer.return_value = ([], []) + + obj_deps = {'': ('global.h',), 'example.c': ('example.h',)} + libs = [('example', {'sources': ['example.c'], 'obj_deps': obj_deps})] + + cmd.build_libraries(libs) + assert [['example.c', 'global.h', 'example.h']] in mock_newer.call_args[0] + assert not cmd.compiler.compile.called + assert cmd.compiler.create_static_lib.call_count == 1 + + # reset the call numbers so we can test again + cmd.compiler.reset_mock() + + mock_newer.return_value = '' # anything as long as it's not ([],[]) + cmd.build_libraries(libs) + assert cmd.compiler.compile.call_count == 1 + assert cmd.compiler.create_static_lib.call_count == 1 + + @mock.patch('setuptools.command.build_clib.newer_pairwise_group') + def test_build_libraries_reproducible(self, mock_newer): + dist = Distribution() + cmd = build_clib(dist) + + # with that out of the way, let's see if the crude dependency + # system works + cmd.compiler = mock.MagicMock(spec=cmd.compiler) + mock_newer.return_value = ([], []) + + original_sources = ['a-example.c', 'example.c'] + sources = original_sources + + obj_deps = {'': ('global.h',), 'example.c': ('example.h',)} + libs = [('example', {'sources': sources, 'obj_deps': obj_deps})] + + cmd.build_libraries(libs) + computed_call_args = mock_newer.call_args[0] + + while sources == original_sources: + sources = random.sample(original_sources, len(original_sources)) + libs = [('example', {'sources': sources, 'obj_deps': obj_deps})] + + cmd.build_libraries(libs) + assert computed_call_args == mock_newer.call_args[0] diff --git a/lib/python3.12/site-packages/setuptools/tests/test_build_ext.py b/lib/python3.12/site-packages/setuptools/tests/test_build_ext.py new file mode 100644 index 0000000000000000000000000000000000000000..c7b60ac32fcd6628cf96396a7e5d8fdb50f67c19 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_build_ext.py @@ -0,0 +1,293 @@ +from __future__ import annotations + +import os +import sys +from importlib.util import cache_from_source as _compiled_file_name + +import pytest +from jaraco import path + +from setuptools.command.build_ext import build_ext, get_abi3_suffix +from setuptools.dist import Distribution +from setuptools.errors import CompileError +from setuptools.extension import Extension + +from . import environment +from .textwrap import DALS + +import distutils.command.build_ext as orig +from distutils.sysconfig import get_config_var + +IS_PYPY = '__pypy__' in sys.builtin_module_names + + +class TestBuildExt: + def test_get_ext_filename(self): + """ + Setuptools needs to give back the same + result as distutils, even if the fullname + is not in ext_map. + """ + dist = Distribution() + cmd = build_ext(dist) + cmd.ext_map['foo/bar'] = '' + res = cmd.get_ext_filename('foo') + wanted = orig.build_ext.get_ext_filename(cmd, 'foo') + assert res == wanted + + def test_abi3_filename(self): + """ + Filename needs to be loadable by several versions + of Python 3 if 'is_abi3' is truthy on Extension() + """ + print(get_abi3_suffix()) + + extension = Extension('spam.eggs', ['eggs.c'], py_limited_api=True) + dist = Distribution(dict(ext_modules=[extension])) + cmd = build_ext(dist) + cmd.finalize_options() + assert 'spam.eggs' in cmd.ext_map + res = cmd.get_ext_filename('spam.eggs') + + if not get_abi3_suffix(): + assert res.endswith(get_config_var('EXT_SUFFIX')) + elif sys.platform == 'win32': + assert res.endswith('eggs.pyd') + else: + assert 'abi3' in res + + def test_ext_suffix_override(self): + """ + SETUPTOOLS_EXT_SUFFIX variable always overrides + default extension options. + """ + dist = Distribution() + cmd = build_ext(dist) + cmd.ext_map['for_abi3'] = ext = Extension( + 'for_abi3', + ['s.c'], + # Override shouldn't affect abi3 modules + py_limited_api=True, + ) + # Mock value needed to pass tests + ext._links_to_dynamic = False + + if not IS_PYPY: + expect = cmd.get_ext_filename('for_abi3') + else: + # PyPy builds do not use ABI3 tag, so they will + # also get the overridden suffix. + expect = 'for_abi3.test-suffix' + + try: + os.environ['SETUPTOOLS_EXT_SUFFIX'] = '.test-suffix' + res = cmd.get_ext_filename('normal') + assert 'normal.test-suffix' == res + res = cmd.get_ext_filename('for_abi3') + assert expect == res + finally: + del os.environ['SETUPTOOLS_EXT_SUFFIX'] + + def dist_with_example(self): + files = { + "src": {"mypkg": {"subpkg": {"ext2.c": ""}}}, + "c-extensions": {"ext1": {"main.c": ""}}, + } + + ext1 = Extension("mypkg.ext1", ["c-extensions/ext1/main.c"]) + ext2 = Extension("mypkg.subpkg.ext2", ["src/mypkg/subpkg/ext2.c"]) + ext3 = Extension("ext3", ["c-extension/ext3.c"]) + + path.build(files) + return Distribution({ + "script_name": "%test%", + "ext_modules": [ext1, ext2, ext3], + "package_dir": {"": "src"}, + }) + + def test_get_outputs(self, tmpdir_cwd, monkeypatch): + monkeypatch.setenv('SETUPTOOLS_EXT_SUFFIX', '.mp3') # make test OS-independent + monkeypatch.setattr('setuptools.command.build_ext.use_stubs', False) + dist = self.dist_with_example() + + # Regular build: get_outputs not empty, but get_output_mappings is empty + build_ext = dist.get_command_obj("build_ext") + build_ext.editable_mode = False + build_ext.ensure_finalized() + build_lib = build_ext.build_lib.replace(os.sep, "/") + outputs = [x.replace(os.sep, "/") for x in build_ext.get_outputs()] + assert outputs == [ + f"{build_lib}/ext3.mp3", + f"{build_lib}/mypkg/ext1.mp3", + f"{build_lib}/mypkg/subpkg/ext2.mp3", + ] + assert build_ext.get_output_mapping() == {} + + # Editable build: get_output_mappings should contain everything in get_outputs + dist.reinitialize_command("build_ext") + build_ext.editable_mode = True + build_ext.ensure_finalized() + mapping = { + k.replace(os.sep, "/"): v.replace(os.sep, "/") + for k, v in build_ext.get_output_mapping().items() + } + assert mapping == { + f"{build_lib}/ext3.mp3": "src/ext3.mp3", + f"{build_lib}/mypkg/ext1.mp3": "src/mypkg/ext1.mp3", + f"{build_lib}/mypkg/subpkg/ext2.mp3": "src/mypkg/subpkg/ext2.mp3", + } + + def test_get_output_mapping_with_stub(self, tmpdir_cwd, monkeypatch): + monkeypatch.setenv('SETUPTOOLS_EXT_SUFFIX', '.mp3') # make test OS-independent + monkeypatch.setattr('setuptools.command.build_ext.use_stubs', True) + dist = self.dist_with_example() + + # Editable build should create compiled stubs (.pyc files only, no .py) + build_ext = dist.get_command_obj("build_ext") + build_ext.editable_mode = True + build_ext.ensure_finalized() + for ext in build_ext.extensions: + monkeypatch.setattr(ext, "_needs_stub", True) + + build_lib = build_ext.build_lib.replace(os.sep, "/") + mapping = { + k.replace(os.sep, "/"): v.replace(os.sep, "/") + for k, v in build_ext.get_output_mapping().items() + } + + def C(file): + """Make it possible to do comparisons and tests in a OS-independent way""" + return _compiled_file_name(file).replace(os.sep, "/") + + assert mapping == { + C(f"{build_lib}/ext3.py"): C("src/ext3.py"), + f"{build_lib}/ext3.mp3": "src/ext3.mp3", + C(f"{build_lib}/mypkg/ext1.py"): C("src/mypkg/ext1.py"), + f"{build_lib}/mypkg/ext1.mp3": "src/mypkg/ext1.mp3", + C(f"{build_lib}/mypkg/subpkg/ext2.py"): C("src/mypkg/subpkg/ext2.py"), + f"{build_lib}/mypkg/subpkg/ext2.mp3": "src/mypkg/subpkg/ext2.mp3", + } + + # Ensure only the compiled stubs are present not the raw .py stub + assert f"{build_lib}/mypkg/ext1.py" not in mapping + assert f"{build_lib}/mypkg/subpkg/ext2.py" not in mapping + + # Visualize what the cached stub files look like + example_stub = C(f"{build_lib}/mypkg/ext1.py") + assert example_stub in mapping + assert example_stub.startswith(f"{build_lib}/mypkg/__pycache__/ext1") + assert example_stub.endswith(".pyc") + + +class TestBuildExtInplace: + def get_build_ext_cmd(self, optional: bool, **opts) -> build_ext: + files: dict[str, str | dict[str, dict[str, str]]] = { + "eggs.c": "#include missingheader.h\n", + ".build": {"lib": {}, "tmp": {}}, + } + path.build(files) + extension = Extension('spam.eggs', ['eggs.c'], optional=optional) + dist = Distribution(dict(ext_modules=[extension])) + dist.script_name = 'setup.py' + cmd = build_ext(dist) + vars(cmd).update(build_lib=".build/lib", build_temp=".build/tmp", **opts) + cmd.ensure_finalized() + return cmd + + def get_log_messages(self, caplog, capsys): + """ + Historically, distutils "logged" by printing to sys.std*. + Later versions adopted the logging framework. Grab + messages regardless of how they were captured. + """ + std = capsys.readouterr() + return std.out.splitlines() + std.err.splitlines() + caplog.messages + + def test_optional(self, tmpdir_cwd, caplog, capsys): + """ + If optional extensions fail to build, setuptools should show the error + in the logs but not fail to build + """ + cmd = self.get_build_ext_cmd(optional=True, inplace=True) + cmd.run() + assert any( + 'build_ext: building extension "spam.eggs" failed' + for msg in self.get_log_messages(caplog, capsys) + ) + # No compile error exception should be raised + + def test_non_optional(self, tmpdir_cwd): + # Non-optional extensions should raise an exception + cmd = self.get_build_ext_cmd(optional=False, inplace=True) + with pytest.raises(CompileError): + cmd.run() + + +def test_build_ext_config_handling(tmpdir_cwd): + files = { + 'setup.py': DALS( + """ + from setuptools import Extension, setup + setup( + name='foo', + version='0.0.0', + ext_modules=[Extension('foo', ['foo.c'])], + ) + """ + ), + 'foo.c': DALS( + """ + #include "Python.h" + + #if PY_MAJOR_VERSION >= 3 + + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "foo", + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL + }; + + #define INITERROR return NULL + + PyMODINIT_FUNC PyInit_foo(void) + + #else + + #define INITERROR return + + void initfoo(void) + + #endif + { + #if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); + #else + PyObject *module = Py_InitModule("extension", NULL); + #endif + if (module == NULL) + INITERROR; + #if PY_MAJOR_VERSION >= 3 + return module; + #endif + } + """ + ), + 'setup.cfg': DALS( + """ + [build] + build_base = foo_build + """ + ), + } + path.build(files) + code, (stdout, stderr) = environment.run_setup_py( + cmd=['build'], + data_stream=(0, 2), + ) + assert code == 0, f'\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}' diff --git a/lib/python3.12/site-packages/setuptools/tests/test_build_meta.py b/lib/python3.12/site-packages/setuptools/tests/test_build_meta.py new file mode 100644 index 0000000000000000000000000000000000000000..2cd0a0a8ede8ecb8d0c5ae55c8c3b09559239ed5 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_build_meta.py @@ -0,0 +1,959 @@ +import contextlib +import importlib +import os +import re +import shutil +import signal +import sys +import tarfile +import warnings +from concurrent import futures +from pathlib import Path +from typing import Any, Callable +from zipfile import ZipFile + +import pytest +from jaraco import path +from packaging.requirements import Requirement + +from setuptools.warnings import SetuptoolsDeprecationWarning + +from .textwrap import DALS + +SETUP_SCRIPT_STUB = "__import__('setuptools').setup()" + + +TIMEOUT = int(os.getenv("TIMEOUT_BACKEND_TEST", "180")) # in seconds +IS_PYPY = '__pypy__' in sys.builtin_module_names + + +pytestmark = pytest.mark.skipif( + sys.platform == "win32" and IS_PYPY, + reason="The combination of PyPy + Windows + pytest-xdist + ProcessPoolExecutor " + "is flaky and problematic", +) + + +class BuildBackendBase: + def __init__(self, cwd='.', env=None, backend_name='setuptools.build_meta') -> None: + self.cwd = cwd + self.env = env or {} + self.backend_name = backend_name + + +class BuildBackend(BuildBackendBase): + """PEP 517 Build Backend""" + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.pool = futures.ProcessPoolExecutor(max_workers=1) + + def __getattr__(self, name: str) -> Callable[..., Any]: + """Handles arbitrary function invocations on the build backend.""" + + def method(*args, **kw): + root = os.path.abspath(self.cwd) + caller = BuildBackendCaller(root, self.env, self.backend_name) + pid = None + try: + pid = self.pool.submit(os.getpid).result(TIMEOUT) + return self.pool.submit(caller, name, *args, **kw).result(TIMEOUT) + except futures.TimeoutError: + self.pool.shutdown(wait=False) # doesn't stop already running processes + self._kill(pid) + pytest.xfail(f"Backend did not respond before timeout ({TIMEOUT} s)") + except (futures.process.BrokenProcessPool, MemoryError, OSError): + if IS_PYPY: + pytest.xfail("PyPy frequently fails tests with ProcessPoolExector") + raise + + return method + + def _kill(self, pid): + if pid is None: + return + with contextlib.suppress(ProcessLookupError, OSError): + os.kill(pid, signal.SIGTERM if os.name == "nt" else signal.SIGKILL) + + +class BuildBackendCaller(BuildBackendBase): + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + (self.backend_name, _, self.backend_obj) = self.backend_name.partition(':') + + def __call__(self, name, *args, **kw) -> Any: + """Handles arbitrary function invocations on the build backend.""" + os.chdir(self.cwd) + os.environ.update(self.env) + mod = importlib.import_module(self.backend_name) + + if self.backend_obj: + backend = getattr(mod, self.backend_obj) + else: + backend = mod + + return getattr(backend, name)(*args, **kw) + + +defns = [ + { # simple setup.py script + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'], + setup_requires=['six'], + ) + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }, + { # setup.py that relies on __name__ + 'setup.py': DALS( + """ + assert __name__ == '__main__' + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'], + setup_requires=['six'], + ) + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }, + { # setup.py script that runs arbitrary code + 'setup.py': DALS( + """ + variable = True + def function(): + return variable + assert variable + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'], + setup_requires=['six'], + ) + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }, + { # setup.py script that constructs temp files to be included in the distribution + 'setup.py': DALS( + """ + # Some packages construct files on the fly, include them in the package, + # and immediately remove them after `setup()` (e.g. pybind11==2.9.1). + # Therefore, we cannot use `distutils.core.run_setup(..., stop_after=...)` + # to obtain a distribution object first, and then run the distutils + # commands later, because these files will be removed in the meantime. + + with open('world.py', 'w', encoding="utf-8") as f: + f.write('x = 42') + + try: + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['world'], + setup_requires=['six'], + ) + finally: + # Some packages will clean temporary files + __import__('os').unlink('world.py') + """ + ), + }, + { # setup.cfg only + 'setup.cfg': DALS( + """ + [metadata] + name = foo + version = 0.0.0 + + [options] + py_modules=hello + setup_requires=six + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }, + { # setup.cfg and setup.py + 'setup.cfg': DALS( + """ + [metadata] + name = foo + version = 0.0.0 + + [options] + py_modules=hello + setup_requires=six + """ + ), + 'setup.py': "__import__('setuptools').setup()", + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }, +] + + +class TestBuildMetaBackend: + backend_name = 'setuptools.build_meta' + + def get_build_backend(self): + return BuildBackend(backend_name=self.backend_name) + + @pytest.fixture(params=defns) + def build_backend(self, tmpdir, request): + path.build(request.param, prefix=str(tmpdir)) + with tmpdir.as_cwd(): + yield self.get_build_backend() + + def test_get_requires_for_build_wheel(self, build_backend): + actual = build_backend.get_requires_for_build_wheel() + expected = ['six'] + assert sorted(actual) == sorted(expected) + + def test_get_requires_for_build_sdist(self, build_backend): + actual = build_backend.get_requires_for_build_sdist() + expected = ['six'] + assert sorted(actual) == sorted(expected) + + def test_build_wheel(self, build_backend): + dist_dir = os.path.abspath('pip-wheel') + os.makedirs(dist_dir) + wheel_name = build_backend.build_wheel(dist_dir) + + wheel_file = os.path.join(dist_dir, wheel_name) + assert os.path.isfile(wheel_file) + + # Temporary files should be removed + assert not os.path.isfile('world.py') + + with ZipFile(wheel_file) as zipfile: + wheel_contents = set(zipfile.namelist()) + + # Each one of the examples have a single module + # that should be included in the distribution + python_scripts = (f for f in wheel_contents if f.endswith('.py')) + modules = [f for f in python_scripts if not f.endswith('setup.py')] + assert len(modules) == 1 + + @pytest.mark.parametrize('build_type', ('wheel', 'sdist')) + def test_build_with_existing_file_present(self, build_type, tmpdir_cwd): + # Building a sdist/wheel should still succeed if there's + # already a sdist/wheel in the destination directory. + files = { + 'setup.py': "from setuptools import setup\nsetup()", + 'VERSION': "0.0.1", + 'setup.cfg': DALS( + """ + [metadata] + name = foo + version = file: VERSION + """ + ), + 'pyproject.toml': DALS( + """ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + """ + ), + } + + path.build(files) + + dist_dir = os.path.abspath('preexisting-' + build_type) + + build_backend = self.get_build_backend() + build_method = getattr(build_backend, 'build_' + build_type) + + # Build a first sdist/wheel. + # Note: this also check the destination directory is + # successfully created if it does not exist already. + first_result = build_method(dist_dir) + + # Change version. + with open("VERSION", "wt", encoding="utf-8") as version_file: + version_file.write("0.0.2") + + # Build a *second* sdist/wheel. + second_result = build_method(dist_dir) + + assert os.path.isfile(os.path.join(dist_dir, first_result)) + assert first_result != second_result + + # And if rebuilding the exact same sdist/wheel? + open(os.path.join(dist_dir, second_result), 'wb').close() + third_result = build_method(dist_dir) + assert third_result == second_result + assert os.path.getsize(os.path.join(dist_dir, third_result)) > 0 + + @pytest.mark.parametrize("setup_script", [None, SETUP_SCRIPT_STUB]) + def test_build_with_pyproject_config(self, tmpdir, setup_script): + files = { + 'pyproject.toml': DALS( + """ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "foo" + license = {text = "MIT"} + description = "This is a Python package" + dynamic = ["version", "readme"] + classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers" + ] + urls = {Homepage = "http://github.com"} + dependencies = [ + "appdirs", + ] + + [project.optional-dependencies] + all = [ + "tomli>=1", + "pyscaffold>=4,<5", + 'importlib; python_version == "2.6"', + ] + + [project.scripts] + foo = "foo.cli:main" + + [tool.setuptools] + zip-safe = false + package-dir = {"" = "src"} + packages = {find = {where = ["src"]}} + license-files = ["LICENSE*"] + + [tool.setuptools.dynamic] + version = {attr = "foo.__version__"} + readme = {file = "README.rst"} + + [tool.distutils.sdist] + formats = "gztar" + """ + ), + "MANIFEST.in": DALS( + """ + global-include *.py *.txt + global-exclude *.py[cod] + """ + ), + "README.rst": "This is a ``README``", + "LICENSE.txt": "---- placeholder MIT license ----", + "src": { + "foo": { + "__init__.py": "__version__ = '0.1'", + "__init__.pyi": "__version__: str", + "cli.py": "def main(): print('hello world')", + "data.txt": "def main(): print('hello world')", + "py.typed": "", + } + }, + } + if setup_script: + files["setup.py"] = setup_script + + build_backend = self.get_build_backend() + with tmpdir.as_cwd(): + path.build(files) + msgs = [ + "'tool.setuptools.license-files' is deprecated in favor of 'project.license-files'", + "`project.license` as a TOML table is deprecated", + ] + with warnings.catch_warnings(): + for msg in msgs: + warnings.filterwarnings("ignore", msg, SetuptoolsDeprecationWarning) + sdist_path = build_backend.build_sdist("temp") + wheel_file = build_backend.build_wheel("temp") + + with tarfile.open(os.path.join(tmpdir, "temp", sdist_path)) as tar: + sdist_contents = set(tar.getnames()) + + with ZipFile(os.path.join(tmpdir, "temp", wheel_file)) as zipfile: + wheel_contents = set(zipfile.namelist()) + metadata = str(zipfile.read("foo-0.1.dist-info/METADATA"), "utf-8") + license = str( + zipfile.read("foo-0.1.dist-info/licenses/LICENSE.txt"), "utf-8" + ) + epoints = str(zipfile.read("foo-0.1.dist-info/entry_points.txt"), "utf-8") + + assert sdist_contents - {"foo-0.1/setup.py"} == { + 'foo-0.1', + 'foo-0.1/LICENSE.txt', + 'foo-0.1/MANIFEST.in', + 'foo-0.1/PKG-INFO', + 'foo-0.1/README.rst', + 'foo-0.1/pyproject.toml', + 'foo-0.1/setup.cfg', + 'foo-0.1/src', + 'foo-0.1/src/foo', + 'foo-0.1/src/foo/__init__.py', + 'foo-0.1/src/foo/__init__.pyi', + 'foo-0.1/src/foo/cli.py', + 'foo-0.1/src/foo/data.txt', + 'foo-0.1/src/foo/py.typed', + 'foo-0.1/src/foo.egg-info', + 'foo-0.1/src/foo.egg-info/PKG-INFO', + 'foo-0.1/src/foo.egg-info/SOURCES.txt', + 'foo-0.1/src/foo.egg-info/dependency_links.txt', + 'foo-0.1/src/foo.egg-info/entry_points.txt', + 'foo-0.1/src/foo.egg-info/requires.txt', + 'foo-0.1/src/foo.egg-info/top_level.txt', + 'foo-0.1/src/foo.egg-info/not-zip-safe', + } + assert wheel_contents == { + "foo/__init__.py", + "foo/__init__.pyi", # include type information by default + "foo/cli.py", + "foo/data.txt", # include_package_data defaults to True + "foo/py.typed", # include type information by default + "foo-0.1.dist-info/licenses/LICENSE.txt", + "foo-0.1.dist-info/METADATA", + "foo-0.1.dist-info/WHEEL", + "foo-0.1.dist-info/entry_points.txt", + "foo-0.1.dist-info/top_level.txt", + "foo-0.1.dist-info/RECORD", + } + assert license == "---- placeholder MIT license ----" + + for line in ( + "Summary: This is a Python package", + "License: MIT", + "License-File: LICENSE.txt", + "Classifier: Intended Audience :: Developers", + "Requires-Dist: appdirs", + "Requires-Dist: " + str(Requirement('tomli>=1 ; extra == "all"')), + "Requires-Dist: " + + str(Requirement('importlib; python_version=="2.6" and extra =="all"')), + ): + assert line in metadata, (line, metadata) + + assert metadata.strip().endswith("This is a ``README``") + assert epoints.strip() == "[console_scripts]\nfoo = foo.cli:main" + + def test_static_metadata_in_pyproject_config(self, tmpdir): + # Make sure static metadata in pyproject.toml is not overwritten by setup.py + # as required by PEP 621 + files = { + 'pyproject.toml': DALS( + """ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "foo" + description = "This is a Python package" + version = "42" + dependencies = ["six"] + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='bar', + version='13', + ) + """ + ), + } + build_backend = self.get_build_backend() + with tmpdir.as_cwd(): + path.build(files) + sdist_path = build_backend.build_sdist("temp") + wheel_file = build_backend.build_wheel("temp") + + assert (tmpdir / "temp/foo-42.tar.gz").exists() + assert (tmpdir / "temp/foo-42-py3-none-any.whl").exists() + assert not (tmpdir / "temp/bar-13.tar.gz").exists() + assert not (tmpdir / "temp/bar-42.tar.gz").exists() + assert not (tmpdir / "temp/foo-13.tar.gz").exists() + assert not (tmpdir / "temp/bar-13-py3-none-any.whl").exists() + assert not (tmpdir / "temp/bar-42-py3-none-any.whl").exists() + assert not (tmpdir / "temp/foo-13-py3-none-any.whl").exists() + + with tarfile.open(os.path.join(tmpdir, "temp", sdist_path)) as tar: + pkg_info = str(tar.extractfile('foo-42/PKG-INFO').read(), "utf-8") + members = tar.getnames() + assert "bar-13/PKG-INFO" not in members + + with ZipFile(os.path.join(tmpdir, "temp", wheel_file)) as zipfile: + metadata = str(zipfile.read("foo-42.dist-info/METADATA"), "utf-8") + members = zipfile.namelist() + assert "bar-13.dist-info/METADATA" not in members + + for file in pkg_info, metadata: + for line in ("Name: foo", "Version: 42"): + assert line in file + for line in ("Name: bar", "Version: 13"): + assert line not in file + + def test_build_sdist(self, build_backend): + dist_dir = os.path.abspath('pip-sdist') + os.makedirs(dist_dir) + sdist_name = build_backend.build_sdist(dist_dir) + + assert os.path.isfile(os.path.join(dist_dir, sdist_name)) + + def test_prepare_metadata_for_build_wheel(self, build_backend): + dist_dir = os.path.abspath('pip-dist-info') + os.makedirs(dist_dir) + + dist_info = build_backend.prepare_metadata_for_build_wheel(dist_dir) + + assert os.path.isfile(os.path.join(dist_dir, dist_info, 'METADATA')) + + def test_prepare_metadata_inplace(self, build_backend): + """ + Some users might pass metadata_directory pre-populated with `.tox` or `.venv`. + See issue #3523. + """ + for pre_existing in [ + ".tox/python/lib/python3.10/site-packages/attrs-22.1.0.dist-info", + ".tox/python/lib/python3.10/site-packages/autocommand-2.2.1.dist-info", + ".nox/python/lib/python3.10/site-packages/build-0.8.0.dist-info", + ".venv/python3.10/site-packages/click-8.1.3.dist-info", + "venv/python3.10/site-packages/distlib-0.3.5.dist-info", + "env/python3.10/site-packages/docutils-0.19.dist-info", + ]: + os.makedirs(pre_existing, exist_ok=True) + dist_info = build_backend.prepare_metadata_for_build_wheel(".") + assert os.path.isfile(os.path.join(dist_info, 'METADATA')) + + def test_build_sdist_explicit_dist(self, build_backend): + # explicitly specifying the dist folder should work + # the folder sdist_directory and the ``--dist-dir`` can be the same + dist_dir = os.path.abspath('dist') + sdist_name = build_backend.build_sdist(dist_dir) + assert os.path.isfile(os.path.join(dist_dir, sdist_name)) + + def test_build_sdist_version_change(self, build_backend): + sdist_into_directory = os.path.abspath("out_sdist") + os.makedirs(sdist_into_directory) + + sdist_name = build_backend.build_sdist(sdist_into_directory) + assert os.path.isfile(os.path.join(sdist_into_directory, sdist_name)) + + # if the setup.py changes subsequent call of the build meta + # should still succeed, given the + # sdist_directory the frontend specifies is empty + setup_loc = os.path.abspath("setup.py") + if not os.path.exists(setup_loc): + setup_loc = os.path.abspath("setup.cfg") + + with open(setup_loc, 'rt', encoding="utf-8") as file_handler: + content = file_handler.read() + with open(setup_loc, 'wt', encoding="utf-8") as file_handler: + file_handler.write(content.replace("version='0.0.0'", "version='0.0.1'")) + + shutil.rmtree(sdist_into_directory) + os.makedirs(sdist_into_directory) + + sdist_name = build_backend.build_sdist("out_sdist") + assert os.path.isfile(os.path.join(os.path.abspath("out_sdist"), sdist_name)) + + def test_build_sdist_pyproject_toml_exists(self, tmpdir_cwd): + files = { + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'] + )""" + ), + 'hello.py': '', + 'pyproject.toml': DALS( + """ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + """ + ), + } + path.build(files) + build_backend = self.get_build_backend() + targz_path = build_backend.build_sdist("temp") + with tarfile.open(os.path.join("temp", targz_path)) as tar: + assert any('pyproject.toml' in name for name in tar.getnames()) + + def test_build_sdist_setup_py_exists(self, tmpdir_cwd): + # If build_sdist is called from a script other than setup.py, + # ensure setup.py is included + path.build(defns[0]) + + build_backend = self.get_build_backend() + targz_path = build_backend.build_sdist("temp") + with tarfile.open(os.path.join("temp", targz_path)) as tar: + assert any('setup.py' in name for name in tar.getnames()) + + def test_build_sdist_setup_py_manifest_excluded(self, tmpdir_cwd): + # Ensure that MANIFEST.in can exclude setup.py + files = { + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'] + )""" + ), + 'hello.py': '', + 'MANIFEST.in': DALS( + """ + exclude setup.py + """ + ), + } + + path.build(files) + + build_backend = self.get_build_backend() + targz_path = build_backend.build_sdist("temp") + with tarfile.open(os.path.join("temp", targz_path)) as tar: + assert not any('setup.py' in name for name in tar.getnames()) + + def test_build_sdist_builds_targz_even_if_zip_indicated(self, tmpdir_cwd): + files = { + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'] + )""" + ), + 'hello.py': '', + 'setup.cfg': DALS( + """ + [sdist] + formats=zip + """ + ), + } + + path.build(files) + + build_backend = self.get_build_backend() + build_backend.build_sdist("temp") + + _relative_path_import_files = { + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='foo', + version=__import__('hello').__version__, + py_modules=['hello'] + )""" + ), + 'hello.py': '__version__ = "0.0.0"', + 'setup.cfg': DALS( + """ + [sdist] + formats=zip + """ + ), + } + + def test_build_sdist_relative_path_import(self, tmpdir_cwd): + path.build(self._relative_path_import_files) + build_backend = self.get_build_backend() + with pytest.raises(ImportError, match="^No module named 'hello'$"): + build_backend.build_sdist("temp") + + _simple_pyproject_example = { + "pyproject.toml": DALS( + """ + [project] + name = "proj" + version = "42" + """ + ), + "src": {"proj": {"__init__.py": ""}}, + } + + def _assert_link_tree(self, parent_dir): + """All files in the directory should be either links or hard links""" + files = list(Path(parent_dir).glob("**/*")) + assert files # Should not be empty + for file in files: + assert file.is_symlink() or os.stat(file).st_nlink > 0 + + def test_editable_without_config_settings(self, tmpdir_cwd): + """ + Sanity check to ensure tests with --mode=strict are different from the ones + without --mode. + + --mode=strict should create a local directory with a package tree. + The directory should not get created otherwise. + """ + path.build(self._simple_pyproject_example) + build_backend = self.get_build_backend() + assert not Path("build").exists() + build_backend.build_editable("temp") + assert not Path("build").exists() + + def test_build_wheel_inplace(self, tmpdir_cwd): + config_settings = {"--build-option": ["build_ext", "--inplace"]} + path.build(self._simple_pyproject_example) + build_backend = self.get_build_backend() + assert not Path("build").exists() + Path("build").mkdir() + build_backend.prepare_metadata_for_build_wheel("build", config_settings) + build_backend.build_wheel("build", config_settings) + assert Path("build/proj-42-py3-none-any.whl").exists() + + @pytest.mark.parametrize("config_settings", [{"editable-mode": "strict"}]) + def test_editable_with_config_settings(self, tmpdir_cwd, config_settings): + path.build({**self._simple_pyproject_example, '_meta': {}}) + assert not Path("build").exists() + build_backend = self.get_build_backend() + build_backend.prepare_metadata_for_build_editable("_meta", config_settings) + build_backend.build_editable("temp", config_settings, "_meta") + self._assert_link_tree(next(Path("build").glob("__editable__.*"))) + + @pytest.mark.parametrize( + ("setup_literal", "requirements"), + [ + ("'foo'", ['foo']), + ("['foo']", ['foo']), + (r"'foo\n'", ['foo']), + (r"'foo\n\n'", ['foo']), + ("['foo', 'bar']", ['foo', 'bar']), + (r"'# Has a comment line\nfoo'", ['foo']), + (r"'foo # Has an inline comment'", ['foo']), + (r"'foo \\\n >=3.0'", ['foo>=3.0']), + (r"'foo\nbar'", ['foo', 'bar']), + (r"'foo\nbar\n'", ['foo', 'bar']), + (r"['foo\n', 'bar\n']", ['foo', 'bar']), + ], + ) + @pytest.mark.parametrize('use_wheel', [True, False]) + def test_setup_requires(self, setup_literal, requirements, use_wheel, tmpdir_cwd): + files = { + 'setup.py': DALS( + """ + from setuptools import setup + + setup( + name="qux", + version="0.0.0", + py_modules=["hello"], + setup_requires={setup_literal}, + ) + """ + ).format(setup_literal=setup_literal), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + } + + path.build(files) + + build_backend = self.get_build_backend() + + if use_wheel: + get_requires = build_backend.get_requires_for_build_wheel + else: + get_requires = build_backend.get_requires_for_build_sdist + + # Ensure that the build requirements are properly parsed + expected = sorted(requirements) + actual = get_requires() + + assert expected == sorted(actual) + + def test_setup_requires_with_auto_discovery(self, tmpdir_cwd): + # Make sure patches introduced to retrieve setup_requires don't accidentally + # activate auto-discovery and cause problems due to the incomplete set of + # attributes passed to MinimalDistribution + files = { + 'pyproject.toml': DALS( + """ + [project] + name = "proj" + version = "42" + """ + ), + "setup.py": DALS( + """ + __import__('setuptools').setup( + setup_requires=["foo"], + py_modules = ["hello", "world"] + ) + """ + ), + 'hello.py': "'hello'", + 'world.py': "'world'", + } + path.build(files) + build_backend = self.get_build_backend() + setup_requires = build_backend.get_requires_for_build_wheel() + assert setup_requires == ["foo"] + + def test_dont_install_setup_requires(self, tmpdir_cwd): + files = { + 'setup.py': DALS( + """ + from setuptools import setup + + setup( + name="qux", + version="0.0.0", + py_modules=["hello"], + setup_requires=["does-not-exist >99"], + ) + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + } + + path.build(files) + + build_backend = self.get_build_backend() + + dist_dir = os.path.abspath('pip-dist-info') + os.makedirs(dist_dir) + + # does-not-exist can't be satisfied, so if it attempts to install + # setup_requires, it will fail. + build_backend.prepare_metadata_for_build_wheel(dist_dir) + + _sys_argv_0_passthrough = { + 'setup.py': DALS( + """ + import os + import sys + + __import__('setuptools').setup( + name='foo', + version='0.0.0', + ) + + sys_argv = os.path.abspath(sys.argv[0]) + file_path = os.path.abspath('setup.py') + assert sys_argv == file_path + """ + ) + } + + def test_sys_argv_passthrough(self, tmpdir_cwd): + path.build(self._sys_argv_0_passthrough) + build_backend = self.get_build_backend() + with pytest.raises(AssertionError): + build_backend.build_sdist("temp") + + _setup_py_file_abspath = { + 'setup.py': DALS( + """ + import os + assert os.path.isabs(__file__) + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'], + setup_requires=['six'], + ) + """ + ) + } + + def test_setup_py_file_abspath(self, tmpdir_cwd): + path.build(self._setup_py_file_abspath) + build_backend = self.get_build_backend() + build_backend.build_sdist("temp") + + @pytest.mark.parametrize('build_hook', ('build_sdist', 'build_wheel')) + def test_build_with_empty_setuppy(self, build_backend, build_hook): + files = {'setup.py': ''} + path.build(files) + + msg = re.escape('No distribution was found.') + with pytest.raises(ValueError, match=msg): + getattr(build_backend, build_hook)("temp") + + +class TestBuildMetaLegacyBackend(TestBuildMetaBackend): + backend_name = 'setuptools.build_meta:__legacy__' + + # build_meta_legacy-specific tests + def test_build_sdist_relative_path_import(self, tmpdir_cwd): + # This must fail in build_meta, but must pass in build_meta_legacy + path.build(self._relative_path_import_files) + + build_backend = self.get_build_backend() + build_backend.build_sdist("temp") + + def test_sys_argv_passthrough(self, tmpdir_cwd): + path.build(self._sys_argv_0_passthrough) + + build_backend = self.get_build_backend() + build_backend.build_sdist("temp") + + +@pytest.mark.filterwarnings("ignore::setuptools.SetuptoolsDeprecationWarning") +def test_sys_exit_0_in_setuppy(monkeypatch, tmp_path): + """Setuptools should be resilient to setup.py with ``sys.exit(0)`` (#3973).""" + monkeypatch.chdir(tmp_path) + setuppy = """ + import sys, setuptools + setuptools.setup(name='foo', version='0.0.0') + sys.exit(0) + """ + (tmp_path / "setup.py").write_text(DALS(setuppy), encoding="utf-8") + backend = BuildBackend(backend_name="setuptools.build_meta") + assert backend.get_requires_for_build_wheel() == [] + + +def test_system_exit_in_setuppy(monkeypatch, tmp_path): + monkeypatch.chdir(tmp_path) + setuppy = "import sys; sys.exit('some error')" + (tmp_path / "setup.py").write_text(setuppy, encoding="utf-8") + with pytest.raises(SystemExit, match="some error"): + backend = BuildBackend(backend_name="setuptools.build_meta") + backend.get_requires_for_build_wheel() diff --git a/lib/python3.12/site-packages/setuptools/tests/test_build_py.py b/lib/python3.12/site-packages/setuptools/tests/test_build_py.py new file mode 100644 index 0000000000000000000000000000000000000000..78848f718281cce38df5deb82b7afa855c6e0d07 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_build_py.py @@ -0,0 +1,480 @@ +import os +import shutil +import stat +import warnings +from pathlib import Path +from unittest.mock import Mock + +import jaraco.path +import pytest + +from setuptools import SetuptoolsDeprecationWarning +from setuptools.dist import Distribution + +from .textwrap import DALS + + +def test_directories_in_package_data_glob(tmpdir_cwd): + """ + Directories matching the glob in package_data should + not be included in the package data. + + Regression test for #261. + """ + dist = Distribution( + dict( + script_name='setup.py', + script_args=['build_py'], + packages=[''], + package_data={'': ['path/*']}, + ) + ) + os.makedirs('path/subpath') + dist.parse_command_line() + dist.run_commands() + + +def test_recursive_in_package_data_glob(tmpdir_cwd): + """ + Files matching recursive globs (**) in package_data should + be included in the package data. + + #1806 + """ + dist = Distribution( + dict( + script_name='setup.py', + script_args=['build_py'], + packages=[''], + package_data={'': ['path/**/data']}, + ) + ) + os.makedirs('path/subpath/subsubpath') + open('path/subpath/subsubpath/data', 'wb').close() + + dist.parse_command_line() + dist.run_commands() + + assert stat.S_ISREG(os.stat('build/lib/path/subpath/subsubpath/data').st_mode), ( + "File is not included" + ) + + +def test_read_only(tmpdir_cwd): + """ + Ensure read-only flag is not preserved in copy + for package modules and package data, as that + causes problems with deleting read-only files on + Windows. + + #1451 + """ + dist = Distribution( + dict( + script_name='setup.py', + script_args=['build_py'], + packages=['pkg'], + package_data={'pkg': ['data.dat']}, + ) + ) + os.makedirs('pkg') + open('pkg/__init__.py', 'wb').close() + open('pkg/data.dat', 'wb').close() + os.chmod('pkg/__init__.py', stat.S_IREAD) + os.chmod('pkg/data.dat', stat.S_IREAD) + dist.parse_command_line() + dist.run_commands() + shutil.rmtree('build') + + +@pytest.mark.xfail( + 'platform.system() == "Windows"', + reason="On Windows, files do not have executable bits", + raises=AssertionError, + strict=True, +) +def test_executable_data(tmpdir_cwd): + """ + Ensure executable bit is preserved in copy for + package data, as users rely on it for scripts. + + #2041 + """ + dist = Distribution( + dict( + script_name='setup.py', + script_args=['build_py'], + packages=['pkg'], + package_data={'pkg': ['run-me']}, + ) + ) + os.makedirs('pkg') + open('pkg/__init__.py', 'wb').close() + open('pkg/run-me', 'wb').close() + os.chmod('pkg/run-me', 0o700) + + dist.parse_command_line() + dist.run_commands() + + assert os.stat('build/lib/pkg/run-me').st_mode & stat.S_IEXEC, ( + "Script is not executable" + ) + + +EXAMPLE_WITH_MANIFEST = { + "setup.cfg": DALS( + """ + [metadata] + name = mypkg + version = 42 + + [options] + include_package_data = True + packages = find: + + [options.packages.find] + exclude = *.tests* + """ + ), + "mypkg": { + "__init__.py": "", + "resource_file.txt": "", + "tests": { + "__init__.py": "", + "test_mypkg.py": "", + "test_file.txt": "", + }, + }, + "MANIFEST.in": DALS( + """ + global-include *.py *.txt + global-exclude *.py[cod] + prune dist + prune build + prune *.egg-info + """ + ), +} + + +def test_excluded_subpackages(tmpdir_cwd): + jaraco.path.build(EXAMPLE_WITH_MANIFEST) + dist = Distribution({"script_name": "%PEP 517%"}) + dist.parse_config_files() + + build_py = dist.get_command_obj("build_py") + + msg = r"Python recognizes 'mypkg\.tests' as an importable package" + with pytest.warns(SetuptoolsDeprecationWarning, match=msg): # noqa: PT031 + # TODO: To fix #3260 we need some transition period to deprecate the + # existing behavior of `include_package_data`. After the transition, we + # should remove the warning and fix the behavior. + + if os.getenv("SETUPTOOLS_USE_DISTUTILS") == "stdlib": + # pytest.warns reset the warning filter temporarily + # https://github.com/pytest-dev/pytest/issues/4011#issuecomment-423494810 + warnings.filterwarnings( + "ignore", + "'encoding' argument not specified", + module="distutils.text_file", + # This warning is already fixed in pypa/distutils but not in stdlib + ) + + build_py.finalize_options() + build_py.run() + + build_dir = Path(dist.get_command_obj("build_py").build_lib) + assert (build_dir / "mypkg/__init__.py").exists() + assert (build_dir / "mypkg/resource_file.txt").exists() + + # Setuptools is configured to ignore `mypkg.tests`, therefore the following + # files/dirs should not be included in the distribution. + for f in [ + "mypkg/tests/__init__.py", + "mypkg/tests/test_mypkg.py", + "mypkg/tests/test_file.txt", + "mypkg/tests", + ]: + with pytest.raises(AssertionError): + # TODO: Enforce the following assertion once #3260 is fixed + # (remove context manager and the following xfail). + assert not (build_dir / f).exists() + + pytest.xfail("#3260") + + +@pytest.mark.filterwarnings("ignore::setuptools.SetuptoolsDeprecationWarning") +def test_existing_egg_info(tmpdir_cwd, monkeypatch): + """When provided with the ``existing_egg_info_dir`` attribute, build_py should not + attempt to run egg_info again. + """ + # == Pre-condition == + # Generate an egg-info dir + jaraco.path.build(EXAMPLE_WITH_MANIFEST) + dist = Distribution({"script_name": "%PEP 517%"}) + dist.parse_config_files() + assert dist.include_package_data + + egg_info = dist.get_command_obj("egg_info") + dist.run_command("egg_info") + egg_info_dir = next(Path(egg_info.egg_base).glob("*.egg-info")) + assert egg_info_dir.is_dir() + + # == Setup == + build_py = dist.get_command_obj("build_py") + build_py.finalize_options() + egg_info = dist.get_command_obj("egg_info") + egg_info_run = Mock(side_effect=egg_info.run) + monkeypatch.setattr(egg_info, "run", egg_info_run) + + # == Remove caches == + # egg_info is called when build_py looks for data_files, which gets cached. + # We need to ensure it is not cached yet, otherwise it may impact on the tests + build_py.__dict__.pop('data_files', None) + dist.reinitialize_command(egg_info) + + # == Sanity check == + # Ensure that if existing_egg_info is not given, build_py attempts to run egg_info + build_py.existing_egg_info_dir = None + build_py.run() + egg_info_run.assert_called() + + # == Remove caches == + egg_info_run.reset_mock() + build_py.__dict__.pop('data_files', None) + dist.reinitialize_command(egg_info) + + # == Actual test == + # Ensure that if existing_egg_info_dir is given, egg_info doesn't run + build_py.existing_egg_info_dir = egg_info_dir + build_py.run() + egg_info_run.assert_not_called() + assert build_py.data_files + + # Make sure the list of outputs is actually OK + outputs = map(lambda x: x.replace(os.sep, "/"), build_py.get_outputs()) + assert outputs + example = str(Path(build_py.build_lib, "mypkg/__init__.py")).replace(os.sep, "/") + assert example in outputs + + +EXAMPLE_ARBITRARY_MAPPING = { + "pyproject.toml": DALS( + """ + [project] + name = "mypkg" + version = "42" + + [tool.setuptools] + packages = ["mypkg", "mypkg.sub1", "mypkg.sub2", "mypkg.sub2.nested"] + + [tool.setuptools.package-dir] + "" = "src" + "mypkg.sub2" = "src/mypkg/_sub2" + "mypkg.sub2.nested" = "other" + """ + ), + "src": { + "mypkg": { + "__init__.py": "", + "resource_file.txt": "", + "sub1": { + "__init__.py": "", + "mod1.py": "", + }, + "_sub2": { + "mod2.py": "", + }, + }, + }, + "other": { + "__init__.py": "", + "mod3.py": "", + }, + "MANIFEST.in": DALS( + """ + global-include *.py *.txt + global-exclude *.py[cod] + """ + ), +} + + +def test_get_outputs(tmpdir_cwd): + jaraco.path.build(EXAMPLE_ARBITRARY_MAPPING) + dist = Distribution({"script_name": "%test%"}) + dist.parse_config_files() + + build_py = dist.get_command_obj("build_py") + build_py.editable_mode = True + build_py.ensure_finalized() + build_lib = build_py.build_lib.replace(os.sep, "/") + outputs = {x.replace(os.sep, "/") for x in build_py.get_outputs()} + assert outputs == { + f"{build_lib}/mypkg/__init__.py", + f"{build_lib}/mypkg/resource_file.txt", + f"{build_lib}/mypkg/sub1/__init__.py", + f"{build_lib}/mypkg/sub1/mod1.py", + f"{build_lib}/mypkg/sub2/mod2.py", + f"{build_lib}/mypkg/sub2/nested/__init__.py", + f"{build_lib}/mypkg/sub2/nested/mod3.py", + } + mapping = { + k.replace(os.sep, "/"): v.replace(os.sep, "/") + for k, v in build_py.get_output_mapping().items() + } + assert mapping == { + f"{build_lib}/mypkg/__init__.py": "src/mypkg/__init__.py", + f"{build_lib}/mypkg/resource_file.txt": "src/mypkg/resource_file.txt", + f"{build_lib}/mypkg/sub1/__init__.py": "src/mypkg/sub1/__init__.py", + f"{build_lib}/mypkg/sub1/mod1.py": "src/mypkg/sub1/mod1.py", + f"{build_lib}/mypkg/sub2/mod2.py": "src/mypkg/_sub2/mod2.py", + f"{build_lib}/mypkg/sub2/nested/__init__.py": "other/__init__.py", + f"{build_lib}/mypkg/sub2/nested/mod3.py": "other/mod3.py", + } + + +class TestTypeInfoFiles: + PYPROJECTS = { + "default_pyproject": DALS( + """ + [project] + name = "foo" + version = "1" + """ + ), + "dont_include_package_data": DALS( + """ + [project] + name = "foo" + version = "1" + + [tool.setuptools] + include-package-data = false + """ + ), + "exclude_type_info": DALS( + """ + [project] + name = "foo" + version = "1" + + [tool.setuptools] + include-package-data = false + + [tool.setuptools.exclude-package-data] + "*" = ["py.typed", "*.pyi"] + """ + ), + } + + EXAMPLES = { + "simple_namespace": { + "directory_structure": { + "foo": { + "bar.pyi": "", + "py.typed": "", + "__init__.py": "", + } + }, + "expected_type_files": {"foo/bar.pyi", "foo/py.typed"}, + }, + "nested_inside_namespace": { + "directory_structure": { + "foo": { + "bar": { + "py.typed": "", + "mod.pyi": "", + } + } + }, + "expected_type_files": {"foo/bar/mod.pyi", "foo/bar/py.typed"}, + }, + "namespace_nested_inside_regular": { + "directory_structure": { + "foo": { + "namespace": { + "foo.pyi": "", + }, + "__init__.pyi": "", + "py.typed": "", + } + }, + "expected_type_files": { + "foo/namespace/foo.pyi", + "foo/__init__.pyi", + "foo/py.typed", + }, + }, + } + + @pytest.mark.parametrize( + "pyproject", + [ + "default_pyproject", + pytest.param( + "dont_include_package_data", + marks=pytest.mark.xfail(reason="pypa/setuptools#4350"), + ), + ], + ) + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_type_files_included_by_default(self, tmpdir_cwd, pyproject, example): + structure = { + **self.EXAMPLES[example]["directory_structure"], + "pyproject.toml": self.PYPROJECTS[pyproject], + } + expected_type_files = self.EXAMPLES[example]["expected_type_files"] + jaraco.path.build(structure) + + build_py = get_finalized_build_py() + outputs = get_outputs(build_py) + assert expected_type_files <= outputs + + @pytest.mark.parametrize("pyproject", ["exclude_type_info"]) + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_type_files_can_be_excluded(self, tmpdir_cwd, pyproject, example): + structure = { + **self.EXAMPLES[example]["directory_structure"], + "pyproject.toml": self.PYPROJECTS[pyproject], + } + expected_type_files = self.EXAMPLES[example]["expected_type_files"] + jaraco.path.build(structure) + + build_py = get_finalized_build_py() + outputs = get_outputs(build_py) + assert expected_type_files.isdisjoint(outputs) + + def test_stub_only_package(self, tmpdir_cwd): + structure = { + "pyproject.toml": DALS( + """ + [project] + name = "foo-stubs" + version = "1" + """ + ), + "foo-stubs": {"__init__.pyi": "", "bar.pyi": ""}, + } + expected_type_files = {"foo-stubs/__init__.pyi", "foo-stubs/bar.pyi"} + jaraco.path.build(structure) + + build_py = get_finalized_build_py() + outputs = get_outputs(build_py) + assert expected_type_files <= outputs + + +def get_finalized_build_py(script_name="%build_py-test%"): + dist = Distribution({"script_name": script_name}) + dist.parse_config_files() + build_py = dist.get_command_obj("build_py") + build_py.finalize_options() + return build_py + + +def get_outputs(build_py): + build_dir = Path(build_py.build_lib) + return { + os.path.relpath(x, build_dir).replace(os.sep, "/") + for x in build_py.get_outputs() + } diff --git a/lib/python3.12/site-packages/setuptools/tests/test_config_discovery.py b/lib/python3.12/site-packages/setuptools/tests/test_config_discovery.py new file mode 100644 index 0000000000000000000000000000000000000000..b5df8203cdb6f9129a65d0c503c4f51e21315b1f --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_config_discovery.py @@ -0,0 +1,647 @@ +import os +import sys +from configparser import ConfigParser +from itertools import product +from typing import cast + +import jaraco.path +import pytest +from path import Path + +import setuptools # noqa: F401 # force distutils.core to be patched +from setuptools.command.sdist import sdist +from setuptools.discovery import find_package_path, find_parent_package +from setuptools.dist import Distribution +from setuptools.errors import PackageDiscoveryError + +from .contexts import quiet +from .integration.helpers import get_sdist_members, get_wheel_members, run +from .textwrap import DALS + +import distutils.core + + +class TestFindParentPackage: + def test_single_package(self, tmp_path): + # find_parent_package should find a non-namespace parent package + (tmp_path / "src/namespace/pkg/nested").mkdir(exist_ok=True, parents=True) + (tmp_path / "src/namespace/pkg/nested/__init__.py").touch() + (tmp_path / "src/namespace/pkg/__init__.py").touch() + packages = ["namespace", "namespace.pkg", "namespace.pkg.nested"] + assert find_parent_package(packages, {"": "src"}, tmp_path) == "namespace.pkg" + + def test_multiple_toplevel(self, tmp_path): + # find_parent_package should return null if the given list of packages does not + # have a single parent package + multiple = ["pkg", "pkg1", "pkg2"] + for name in multiple: + (tmp_path / f"src/{name}").mkdir(exist_ok=True, parents=True) + (tmp_path / f"src/{name}/__init__.py").touch() + assert find_parent_package(multiple, {"": "src"}, tmp_path) is None + + +class TestDiscoverPackagesAndPyModules: + """Make sure discovered values for ``packages`` and ``py_modules`` work + similarly to explicit configuration for the simple scenarios. + """ + + OPTIONS = { + # Different options according to the circumstance being tested + "explicit-src": {"package_dir": {"": "src"}, "packages": ["pkg"]}, + "variation-lib": { + "package_dir": {"": "lib"}, # variation of the source-layout + }, + "explicit-flat": {"packages": ["pkg"]}, + "explicit-single_module": {"py_modules": ["pkg"]}, + "explicit-namespace": {"packages": ["ns", "ns.pkg"]}, + "automatic-src": {}, + "automatic-flat": {}, + "automatic-single_module": {}, + "automatic-namespace": {}, + } + FILES = { + "src": ["src/pkg/__init__.py", "src/pkg/main.py"], + "lib": ["lib/pkg/__init__.py", "lib/pkg/main.py"], + "flat": ["pkg/__init__.py", "pkg/main.py"], + "single_module": ["pkg.py"], + "namespace": ["ns/pkg/__init__.py"], + } + + def _get_info(self, circumstance): + _, _, layout = circumstance.partition("-") + files = self.FILES[layout] + options = self.OPTIONS[circumstance] + return files, options + + @pytest.mark.parametrize("circumstance", OPTIONS.keys()) + def test_sdist_filelist(self, tmp_path, circumstance): + files, options = self._get_info(circumstance) + _populate_project_dir(tmp_path, files, options) + + _, cmd = _run_sdist_programatically(tmp_path, options) + + manifest = [f.replace(os.sep, "/") for f in cmd.filelist.files] + for file in files: + assert any(f.endswith(file) for f in manifest) + + @pytest.mark.parametrize("circumstance", OPTIONS.keys()) + def test_project(self, tmp_path, circumstance): + files, options = self._get_info(circumstance) + _populate_project_dir(tmp_path, files, options) + + # Simulate a pre-existing `build` directory + (tmp_path / "build").mkdir() + (tmp_path / "build/lib").mkdir() + (tmp_path / "build/bdist.linux-x86_64").mkdir() + (tmp_path / "build/bdist.linux-x86_64/file.py").touch() + (tmp_path / "build/lib/__init__.py").touch() + (tmp_path / "build/lib/file.py").touch() + (tmp_path / "dist").mkdir() + (tmp_path / "dist/file.py").touch() + + _run_build(tmp_path) + + sdist_files = get_sdist_members(next(tmp_path.glob("dist/*.tar.gz"))) + print("~~~~~ sdist_members ~~~~~") + print('\n'.join(sdist_files)) + assert sdist_files >= set(files) + + wheel_files = get_wheel_members(next(tmp_path.glob("dist/*.whl"))) + print("~~~~~ wheel_members ~~~~~") + print('\n'.join(wheel_files)) + orig_files = {f.replace("src/", "").replace("lib/", "") for f in files} + assert wheel_files >= orig_files + + # Make sure build files are not included by mistake + for file in wheel_files: + assert "build" not in files + assert "dist" not in files + + PURPOSEFULLY_EMPY = { + "setup.cfg": DALS( + """ + [metadata] + name = myproj + version = 0.0.0 + + [options] + {param} = + """ + ), + "setup.py": DALS( + """ + __import__('setuptools').setup( + name="myproj", + version="0.0.0", + {param}=[] + ) + """ + ), + "pyproject.toml": DALS( + """ + [build-system] + requires = [] + build-backend = 'setuptools.build_meta' + + [project] + name = "myproj" + version = "0.0.0" + + [tool.setuptools] + {param} = [] + """ + ), + "template-pyproject.toml": DALS( + """ + [build-system] + requires = [] + build-backend = 'setuptools.build_meta' + """ + ), + } + + @pytest.mark.parametrize( + ("config_file", "param", "circumstance"), + product( + ["setup.cfg", "setup.py", "pyproject.toml"], + ["packages", "py_modules"], + FILES.keys(), + ), + ) + def test_purposefully_empty(self, tmp_path, config_file, param, circumstance): + files = self.FILES[circumstance] + ["mod.py", "other.py", "src/pkg/__init__.py"] + _populate_project_dir(tmp_path, files, {}) + + if config_file == "pyproject.toml": + template_param = param.replace("_", "-") + else: + # Make sure build works with or without setup.cfg + pyproject = self.PURPOSEFULLY_EMPY["template-pyproject.toml"] + (tmp_path / "pyproject.toml").write_text(pyproject, encoding="utf-8") + template_param = param + + config = self.PURPOSEFULLY_EMPY[config_file].format(param=template_param) + (tmp_path / config_file).write_text(config, encoding="utf-8") + + dist = _get_dist(tmp_path, {}) + # When either parameter package or py_modules is an empty list, + # then there should be no discovery + assert getattr(dist, param) == [] + other = {"py_modules": "packages", "packages": "py_modules"}[param] + assert getattr(dist, other) is None + + @pytest.mark.parametrize( + ("extra_files", "pkgs"), + [ + (["venv/bin/simulate_venv"], {"pkg"}), + (["pkg-stubs/__init__.pyi"], {"pkg", "pkg-stubs"}), + (["other-stubs/__init__.pyi"], {"pkg", "other-stubs"}), + ( + # Type stubs can also be namespaced + ["namespace-stubs/pkg/__init__.pyi"], + {"pkg", "namespace-stubs", "namespace-stubs.pkg"}, + ), + ( + # Just the top-level package can have `-stubs`, ignore nested ones + ["namespace-stubs/pkg-stubs/__init__.pyi"], + {"pkg", "namespace-stubs"}, + ), + (["_hidden/file.py"], {"pkg"}), + (["news/finalize.py"], {"pkg"}), + ], + ) + def test_flat_layout_with_extra_files(self, tmp_path, extra_files, pkgs): + files = self.FILES["flat"] + extra_files + _populate_project_dir(tmp_path, files, {}) + dist = _get_dist(tmp_path, {}) + assert set(dist.packages) == pkgs + + @pytest.mark.parametrize( + "extra_files", + [ + ["other/__init__.py"], + ["other/finalize.py"], + ], + ) + def test_flat_layout_with_dangerous_extra_files(self, tmp_path, extra_files): + files = self.FILES["flat"] + extra_files + _populate_project_dir(tmp_path, files, {}) + with pytest.raises(PackageDiscoveryError, match="multiple (packages|modules)"): + _get_dist(tmp_path, {}) + + def test_flat_layout_with_single_module(self, tmp_path): + files = self.FILES["single_module"] + ["invalid-module-name.py"] + _populate_project_dir(tmp_path, files, {}) + dist = _get_dist(tmp_path, {}) + assert set(dist.py_modules) == {"pkg"} + + def test_flat_layout_with_multiple_modules(self, tmp_path): + files = self.FILES["single_module"] + ["valid_module_name.py"] + _populate_project_dir(tmp_path, files, {}) + with pytest.raises(PackageDiscoveryError, match="multiple (packages|modules)"): + _get_dist(tmp_path, {}) + + def test_py_modules_when_wheel_dir_is_cwd(self, tmp_path): + """Regression for issue 3692""" + from setuptools import build_meta + + pyproject = '[project]\nname = "test"\nversion = "1"' + (tmp_path / "pyproject.toml").write_text(DALS(pyproject), encoding="utf-8") + (tmp_path / "foo.py").touch() + with jaraco.path.DirectoryStack().context(tmp_path): + build_meta.build_wheel(".") + # Ensure py_modules are found + wheel_files = get_wheel_members(next(tmp_path.glob("*.whl"))) + assert "foo.py" in wheel_files + + +class TestNoConfig: + DEFAULT_VERSION = "0.0.0" # Default version given by setuptools + + EXAMPLES = { + "pkg1": ["src/pkg1.py"], + "pkg2": ["src/pkg2/__init__.py"], + "pkg3": ["src/pkg3/__init__.py", "src/pkg3-stubs/__init__.py"], + "pkg4": ["pkg4/__init__.py", "pkg4-stubs/__init__.py"], + "ns.nested.pkg1": ["src/ns/nested/pkg1/__init__.py"], + "ns.nested.pkg2": ["ns/nested/pkg2/__init__.py"], + } + + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_discover_name(self, tmp_path, example): + _populate_project_dir(tmp_path, self.EXAMPLES[example], {}) + dist = _get_dist(tmp_path, {}) + assert dist.get_name() == example + + def test_build_with_discovered_name(self, tmp_path): + files = ["src/ns/nested/pkg/__init__.py"] + _populate_project_dir(tmp_path, files, {}) + _run_build(tmp_path, "--sdist") + # Expected distribution file + dist_file = tmp_path / f"dist/ns_nested_pkg-{self.DEFAULT_VERSION}.tar.gz" + assert dist_file.is_file() + + +class TestWithAttrDirective: + @pytest.mark.parametrize( + ("folder", "opts"), + [ + ("src", {}), + ("lib", {"packages": "find:", "packages.find": {"where": "lib"}}), + ], + ) + def test_setupcfg_metadata(self, tmp_path, folder, opts): + files = [f"{folder}/pkg/__init__.py", "setup.cfg"] + _populate_project_dir(tmp_path, files, opts) + + config = (tmp_path / "setup.cfg").read_text(encoding="utf-8") + overwrite = { + folder: {"pkg": {"__init__.py": "version = 42"}}, + "setup.cfg": "[metadata]\nversion = attr: pkg.version\n" + config, + } + jaraco.path.build(overwrite, prefix=tmp_path) + + dist = _get_dist(tmp_path, {}) + assert dist.get_name() == "pkg" + assert dist.get_version() == "42" + assert dist.package_dir + package_path = find_package_path("pkg", dist.package_dir, tmp_path) + assert os.path.exists(package_path) + assert folder in Path(package_path).parts() + + _run_build(tmp_path, "--sdist") + dist_file = tmp_path / "dist/pkg-42.tar.gz" + assert dist_file.is_file() + + def test_pyproject_metadata(self, tmp_path): + _populate_project_dir(tmp_path, ["src/pkg/__init__.py"], {}) + + overwrite = { + "src": {"pkg": {"__init__.py": "version = 42"}}, + "pyproject.toml": ( + "[project]\nname = 'pkg'\ndynamic = ['version']\n" + "[tool.setuptools.dynamic]\nversion = {attr = 'pkg.version'}\n" + ), + } + jaraco.path.build(overwrite, prefix=tmp_path) + + dist = _get_dist(tmp_path, {}) + assert dist.get_version() == "42" + assert dist.package_dir == {"": "src"} + + +class TestWithCExtension: + def _simulate_package_with_extension(self, tmp_path): + # This example is based on: https://github.com/nucleic/kiwi/tree/1.4.0 + files = [ + "benchmarks/file.py", + "docs/Makefile", + "docs/requirements.txt", + "docs/source/conf.py", + "proj/header.h", + "proj/file.py", + "py/proj.cpp", + "py/other.cpp", + "py/file.py", + "py/py.typed", + "py/tests/test_proj.py", + "README.rst", + ] + _populate_project_dir(tmp_path, files, {}) + + setup_script = """ + from setuptools import Extension, setup + + ext_modules = [ + Extension( + "proj", + ["py/proj.cpp", "py/other.cpp"], + include_dirs=["."], + language="c++", + ), + ] + setup(ext_modules=ext_modules) + """ + (tmp_path / "setup.py").write_text(DALS(setup_script), encoding="utf-8") + + def test_skip_discovery_with_setupcfg_metadata(self, tmp_path): + """Ensure that auto-discovery is not triggered when the project is based on + C-extensions only, for backward compatibility. + """ + self._simulate_package_with_extension(tmp_path) + + pyproject = """ + [build-system] + requires = [] + build-backend = 'setuptools.build_meta' + """ + (tmp_path / "pyproject.toml").write_text(DALS(pyproject), encoding="utf-8") + + setupcfg = """ + [metadata] + name = proj + version = 42 + """ + (tmp_path / "setup.cfg").write_text(DALS(setupcfg), encoding="utf-8") + + dist = _get_dist(tmp_path, {}) + assert dist.get_name() == "proj" + assert dist.get_version() == "42" + assert dist.py_modules is None + assert dist.packages is None + assert len(dist.ext_modules) == 1 + assert dist.ext_modules[0].name == "proj" + + def test_dont_skip_discovery_with_pyproject_metadata(self, tmp_path): + """When opting-in to pyproject.toml metadata, auto-discovery will be active if + the package lists C-extensions, but does not configure py-modules or packages. + + This way we ensure users with complex package layouts that would lead to the + discovery of multiple top-level modules/packages see errors and are forced to + explicitly set ``packages`` or ``py-modules``. + """ + self._simulate_package_with_extension(tmp_path) + + pyproject = """ + [project] + name = 'proj' + version = '42' + """ + (tmp_path / "pyproject.toml").write_text(DALS(pyproject), encoding="utf-8") + with pytest.raises(PackageDiscoveryError, match="multiple (packages|modules)"): + _get_dist(tmp_path, {}) + + +class TestWithPackageData: + def _simulate_package_with_data_files(self, tmp_path, src_root): + files = [ + f"{src_root}/proj/__init__.py", + f"{src_root}/proj/file1.txt", + f"{src_root}/proj/nested/file2.txt", + ] + _populate_project_dir(tmp_path, files, {}) + + manifest = """ + global-include *.py *.txt + """ + (tmp_path / "MANIFEST.in").write_text(DALS(manifest), encoding="utf-8") + + EXAMPLE_SETUPCFG = """ + [metadata] + name = proj + version = 42 + + [options] + include_package_data = True + """ + EXAMPLE_PYPROJECT = """ + [project] + name = "proj" + version = "42" + """ + + PYPROJECT_PACKAGE_DIR = """ + [tool.setuptools] + package-dir = {"" = "src"} + """ + + @pytest.mark.parametrize( + ("src_root", "files"), + [ + (".", {"setup.cfg": DALS(EXAMPLE_SETUPCFG)}), + (".", {"pyproject.toml": DALS(EXAMPLE_PYPROJECT)}), + ("src", {"setup.cfg": DALS(EXAMPLE_SETUPCFG)}), + ("src", {"pyproject.toml": DALS(EXAMPLE_PYPROJECT)}), + ( + "src", + { + "setup.cfg": DALS(EXAMPLE_SETUPCFG) + + DALS( + """ + packages = find: + package_dir = + =src + + [options.packages.find] + where = src + """ + ) + }, + ), + ( + "src", + { + "pyproject.toml": DALS(EXAMPLE_PYPROJECT) + + DALS( + """ + [tool.setuptools] + package-dir = {"" = "src"} + """ + ) + }, + ), + ], + ) + def test_include_package_data(self, tmp_path, src_root, files): + """ + Make sure auto-discovery does not affect package include_package_data. + See issue #3196. + """ + jaraco.path.build(files, prefix=str(tmp_path)) + self._simulate_package_with_data_files(tmp_path, src_root) + + expected = { + os.path.normpath(f"{src_root}/proj/file1.txt").replace(os.sep, "/"), + os.path.normpath(f"{src_root}/proj/nested/file2.txt").replace(os.sep, "/"), + } + + _run_build(tmp_path) + + sdist_files = get_sdist_members(next(tmp_path.glob("dist/*.tar.gz"))) + print("~~~~~ sdist_members ~~~~~") + print('\n'.join(sdist_files)) + assert sdist_files >= expected + + wheel_files = get_wheel_members(next(tmp_path.glob("dist/*.whl"))) + print("~~~~~ wheel_members ~~~~~") + print('\n'.join(wheel_files)) + orig_files = {f.replace("src/", "").replace("lib/", "") for f in expected} + assert wheel_files >= orig_files + + +def test_compatible_with_numpy_configuration(tmp_path): + files = [ + "dir1/__init__.py", + "dir2/__init__.py", + "file.py", + ] + _populate_project_dir(tmp_path, files, {}) + dist = Distribution({}) + dist.configuration = object() + dist.set_defaults() + assert dist.py_modules is None + assert dist.packages is None + + +def test_name_discovery_doesnt_break_cli(tmpdir_cwd): + jaraco.path.build({"pkg.py": ""}) + dist = Distribution({}) + dist.script_args = ["--name"] + dist.set_defaults() + dist.parse_command_line() # <-- no exception should be raised here. + assert dist.get_name() == "pkg" + + +def test_preserve_explicit_name_with_dynamic_version(tmpdir_cwd, monkeypatch): + """According to #3545 it seems that ``name`` discovery is running, + even when the project already explicitly sets it. + This seems to be related to parsing of dynamic versions (via ``attr`` directive), + which requires the auto-discovery of ``package_dir``. + """ + files = { + "src": { + "pkg": {"__init__.py": "__version__ = 42\n"}, + }, + "pyproject.toml": DALS( + """ + [project] + name = "myproj" # purposefully different from package name + dynamic = ["version"] + [tool.setuptools.dynamic] + version = {"attr" = "pkg.__version__"} + """ + ), + } + jaraco.path.build(files) + dist = Distribution({}) + orig_analyse_name = dist.set_defaults.analyse_name + + def spy_analyse_name(): + # We can check if name discovery was triggered by ensuring the original + # name remains instead of the package name. + orig_analyse_name() + assert dist.get_name() == "myproj" + + monkeypatch.setattr(dist.set_defaults, "analyse_name", spy_analyse_name) + dist.parse_config_files() + assert dist.get_version() == "42" + assert set(dist.packages) == {"pkg"} + + +def _populate_project_dir(root, files, options): + # NOTE: Currently pypa/build will refuse to build the project if no + # `pyproject.toml` or `setup.py` is found. So it is impossible to do + # completely "config-less" projects. + basic = { + "setup.py": "import setuptools\nsetuptools.setup()", + "README.md": "# Example Package", + "LICENSE": "Copyright (c) 2018", + } + jaraco.path.build(basic, prefix=root) + _write_setupcfg(root, options) + paths = (root / f for f in files) + for path in paths: + path.parent.mkdir(exist_ok=True, parents=True) + path.touch() + + +def _write_setupcfg(root, options): + if not options: + print("~~~~~ **NO** setup.cfg ~~~~~") + return + setupcfg = ConfigParser() + setupcfg.add_section("options") + for key, value in options.items(): + if key == "packages.find": + setupcfg.add_section(f"options.{key}") + setupcfg[f"options.{key}"].update(value) + elif isinstance(value, list): + setupcfg["options"][key] = ", ".join(value) + elif isinstance(value, dict): + str_value = "\n".join(f"\t{k} = {v}" for k, v in value.items()) + setupcfg["options"][key] = "\n" + str_value + else: + setupcfg["options"][key] = str(value) + with open(root / "setup.cfg", "w", encoding="utf-8") as f: + setupcfg.write(f) + print("~~~~~ setup.cfg ~~~~~") + print((root / "setup.cfg").read_text(encoding="utf-8")) + + +def _run_build(path, *flags): + cmd = [sys.executable, "-m", "build", "--no-isolation", *flags, str(path)] + return run(cmd, env={'DISTUTILS_DEBUG': ''}) + + +def _get_dist(dist_path, attrs): + root = "/".join(os.path.split(dist_path)) # POSIX-style + + script = dist_path / 'setup.py' + if script.exists(): + with Path(dist_path): + dist = cast( + Distribution, + distutils.core.run_setup("setup.py", {}, stop_after="init"), + ) + else: + dist = Distribution(attrs) + + dist.src_root = root + dist.script_name = "setup.py" + with Path(dist_path): + dist.parse_config_files() + + dist.set_defaults() + return dist + + +def _run_sdist_programatically(dist_path, attrs): + dist = _get_dist(dist_path, attrs) + cmd = sdist(dist) + cmd.ensure_finalized() + assert cmd.distribution.packages or cmd.distribution.py_modules + + with quiet(), Path(dist_path): + cmd.run() + + return dist, cmd diff --git a/lib/python3.12/site-packages/setuptools/tests/test_core_metadata.py b/lib/python3.12/site-packages/setuptools/tests/test_core_metadata.py new file mode 100644 index 0000000000000000000000000000000000000000..0d925111fa6e611ae919df8bb30bcda0f248c5b6 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_core_metadata.py @@ -0,0 +1,622 @@ +from __future__ import annotations + +import functools +import importlib +import io +from email import message_from_string +from email.generator import Generator +from email.message import EmailMessage, Message +from email.parser import Parser +from email.policy import EmailPolicy +from inspect import cleandoc +from pathlib import Path +from unittest.mock import Mock + +import jaraco.path +import pytest +from packaging.metadata import Metadata +from packaging.requirements import Requirement + +from setuptools import _reqs, sic +from setuptools._core_metadata import rfc822_escape, rfc822_unescape +from setuptools.command.egg_info import egg_info, write_requirements +from setuptools.config import expand, setupcfg +from setuptools.dist import Distribution + +from .config.downloads import retrieve_file, urls_from_file + +EXAMPLE_BASE_INFO = dict( + name="package", + version="0.0.1", + author="Foo Bar", + author_email="foo@bar.net", + long_description="Long\ndescription", + description="Short description", + keywords=["one", "two"], +) + + +@pytest.mark.parametrize( + ("content", "result"), + ( + pytest.param( + "Just a single line", + None, + id="single_line", + ), + pytest.param( + "Multiline\nText\nwithout\nextra indents\n", + None, + id="multiline", + ), + pytest.param( + "Multiline\n With\n\nadditional\n indentation", + None, + id="multiline_with_indentation", + ), + pytest.param( + " Leading whitespace", + "Leading whitespace", + id="remove_leading_whitespace", + ), + pytest.param( + " Leading whitespace\nIn\n Multiline comment", + "Leading whitespace\nIn\n Multiline comment", + id="remove_leading_whitespace_multiline", + ), + ), +) +def test_rfc822_unescape(content, result): + assert (result or content) == rfc822_unescape(rfc822_escape(content)) + + +def __read_test_cases(): + base = EXAMPLE_BASE_INFO + + params = functools.partial(dict, base) + + return [ + ('Metadata version 1.0', params()), + ( + 'Metadata Version 1.0: Short long description', + params( + long_description='Short long description', + ), + ), + ( + 'Metadata version 1.1: Classifiers', + params( + classifiers=[ + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.7', + 'License :: OSI Approved :: MIT License', + ], + ), + ), + ( + 'Metadata version 1.1: Download URL', + params( + download_url='https://example.com', + ), + ), + ( + 'Metadata Version 1.2: Requires-Python', + params( + python_requires='>=3.7', + ), + ), + pytest.param( + 'Metadata Version 1.2: Project-Url', + params(project_urls=dict(Foo='https://example.bar')), + marks=pytest.mark.xfail( + reason="Issue #1578: project_urls not read", + ), + ), + ( + 'Metadata Version 2.1: Long Description Content Type', + params( + long_description_content_type='text/x-rst; charset=UTF-8', + ), + ), + ( + 'License', + params( + license='MIT', + ), + ), + ( + 'License multiline', + params( + license='This is a long license \nover multiple lines', + ), + ), + pytest.param( + 'Metadata Version 2.1: Provides Extra', + params(provides_extras=['foo', 'bar']), + marks=pytest.mark.xfail(reason="provides_extras not read"), + ), + ( + 'Missing author', + dict( + name='foo', + version='1.0.0', + author_email='snorri@sturluson.name', + ), + ), + ( + 'Missing author e-mail', + dict( + name='foo', + version='1.0.0', + author='Snorri Sturluson', + ), + ), + ( + 'Missing author and e-mail', + dict( + name='foo', + version='1.0.0', + ), + ), + ( + 'Bypass normalized version', + dict( + name='foo', + version=sic('1.0.0a'), + ), + ), + ] + + +@pytest.mark.parametrize(("name", "attrs"), __read_test_cases()) +def test_read_metadata(name, attrs): + dist = Distribution(attrs) + metadata_out = dist.metadata + dist_class = metadata_out.__class__ + + # Write to PKG_INFO and then load into a new metadata object + PKG_INFO = io.StringIO() + + metadata_out.write_pkg_file(PKG_INFO) + PKG_INFO.seek(0) + pkg_info = PKG_INFO.read() + assert _valid_metadata(pkg_info) + + PKG_INFO.seek(0) + metadata_in = dist_class() + metadata_in.read_pkg_file(PKG_INFO) + + tested_attrs = [ + ('name', dist_class.get_name), + ('version', dist_class.get_version), + ('author', dist_class.get_contact), + ('author_email', dist_class.get_contact_email), + ('metadata_version', dist_class.get_metadata_version), + ('provides', dist_class.get_provides), + ('description', dist_class.get_description), + ('long_description', dist_class.get_long_description), + ('download_url', dist_class.get_download_url), + ('keywords', dist_class.get_keywords), + ('platforms', dist_class.get_platforms), + ('obsoletes', dist_class.get_obsoletes), + ('requires', dist_class.get_requires), + ('classifiers', dist_class.get_classifiers), + ('project_urls', lambda s: getattr(s, 'project_urls', {})), + ('provides_extras', lambda s: getattr(s, 'provides_extras', {})), + ] + + for attr, getter in tested_attrs: + assert getter(metadata_in) == getter(metadata_out) + + +def __maintainer_test_cases(): + attrs = {"name": "package", "version": "1.0", "description": "xxx"} + + def merge_dicts(d1, d2): + d1 = d1.copy() + d1.update(d2) + + return d1 + + return [ + ('No author, no maintainer', attrs.copy()), + ( + 'Author (no e-mail), no maintainer', + merge_dicts(attrs, {'author': 'Author Name'}), + ), + ( + 'Author (e-mail), no maintainer', + merge_dicts( + attrs, {'author': 'Author Name', 'author_email': 'author@name.com'} + ), + ), + ( + 'No author, maintainer (no e-mail)', + merge_dicts(attrs, {'maintainer': 'Maintainer Name'}), + ), + ( + 'No author, maintainer (e-mail)', + merge_dicts( + attrs, + { + 'maintainer': 'Maintainer Name', + 'maintainer_email': 'maintainer@name.com', + }, + ), + ), + ( + 'Author (no e-mail), Maintainer (no-email)', + merge_dicts( + attrs, {'author': 'Author Name', 'maintainer': 'Maintainer Name'} + ), + ), + ( + 'Author (e-mail), Maintainer (e-mail)', + merge_dicts( + attrs, + { + 'author': 'Author Name', + 'author_email': 'author@name.com', + 'maintainer': 'Maintainer Name', + 'maintainer_email': 'maintainer@name.com', + }, + ), + ), + ( + 'No author (e-mail), no maintainer (e-mail)', + merge_dicts( + attrs, + { + 'author_email': 'author@name.com', + 'maintainer_email': 'maintainer@name.com', + }, + ), + ), + ('Author unicode', merge_dicts(attrs, {'author': '鉄沢寛'})), + ('Maintainer unicode', merge_dicts(attrs, {'maintainer': 'Jan Łukasiewicz'})), + ] + + +@pytest.mark.parametrize(("name", "attrs"), __maintainer_test_cases()) +def test_maintainer_author(name, attrs, tmpdir): + tested_keys = { + 'author': 'Author', + 'author_email': 'Author-email', + 'maintainer': 'Maintainer', + 'maintainer_email': 'Maintainer-email', + } + + # Generate a PKG-INFO file + dist = Distribution(attrs) + fn = tmpdir.mkdir('pkg_info') + fn_s = str(fn) + + dist.metadata.write_pkg_info(fn_s) + + with open(str(fn.join('PKG-INFO')), 'r', encoding='utf-8') as f: + pkg_info = f.read() + + assert _valid_metadata(pkg_info) + + # Drop blank lines and strip lines from default description + raw_pkg_lines = pkg_info.splitlines() + pkg_lines = list(filter(None, raw_pkg_lines[:-2])) + + pkg_lines_set = set(pkg_lines) + + # Duplicate lines should not be generated + assert len(pkg_lines) == len(pkg_lines_set) + + for fkey, dkey in tested_keys.items(): + val = attrs.get(dkey, None) + if val is None: + for line in pkg_lines: + assert not line.startswith(fkey + ':') + else: + line = f'{fkey}: {val}' + assert line in pkg_lines_set + + +class TestParityWithMetadataFromPyPaWheel: + def base_example(self): + attrs = dict( + **EXAMPLE_BASE_INFO, + # Example with complex requirement definition + python_requires=">=3.8", + install_requires=""" + packaging==23.2 + more-itertools==8.8.0; extra == "other" + jaraco.text==3.7.0 + importlib-resources==5.10.2; python_version<"3.8" + importlib-metadata==6.0.0 ; python_version<"3.8" + colorama>=0.4.4; sys_platform == "win32" + """, + extras_require={ + "testing": """ + pytest >= 6 + pytest-checkdocs >= 2.4 + tomli ; \\ + # Using stdlib when possible + python_version < "3.11" + ini2toml[lite]>=0.9 + """, + "other": [], + }, + ) + # Generate a PKG-INFO file using setuptools + return Distribution(attrs) + + def test_requires_dist(self, tmp_path): + dist = self.base_example() + pkg_info = _get_pkginfo(dist) + assert _valid_metadata(pkg_info) + + # Ensure Requires-Dist is present + expected = [ + 'Metadata-Version:', + 'Requires-Python: >=3.8', + 'Provides-Extra: other', + 'Provides-Extra: testing', + 'Requires-Dist: tomli; python_version < "3.11" and extra == "testing"', + 'Requires-Dist: more-itertools==8.8.0; extra == "other"', + 'Requires-Dist: ini2toml[lite]>=0.9; extra == "testing"', + ] + for line in expected: + assert line in pkg_info + + HERE = Path(__file__).parent + EXAMPLES_FILE = HERE / "config/setupcfg_examples.txt" + + @pytest.fixture(params=[None, *urls_from_file(EXAMPLES_FILE)]) + def dist(self, request, monkeypatch, tmp_path): + """Example of distribution with arbitrary configuration""" + monkeypatch.chdir(tmp_path) + monkeypatch.setattr(expand, "read_attr", Mock(return_value="0.42")) + monkeypatch.setattr(expand, "read_files", Mock(return_value="hello world")) + monkeypatch.setattr( + Distribution, "_finalize_license_files", Mock(return_value=None) + ) + if request.param is None: + yield self.base_example() + else: + # Real-world usage + config = retrieve_file(request.param) + yield setupcfg.apply_configuration(Distribution({}), config) + + @pytest.mark.uses_network + def test_equivalent_output(self, tmp_path, dist): + """Ensure output from setuptools is equivalent to the one from `pypa/wheel`""" + # Generate a METADATA file using pypa/wheel for comparison + wheel_metadata = importlib.import_module("wheel.metadata") + pkginfo_to_metadata = getattr(wheel_metadata, "pkginfo_to_metadata", None) + + if pkginfo_to_metadata is None: # pragma: nocover + pytest.xfail( + "wheel.metadata.pkginfo_to_metadata is undefined, " + "(this is likely to be caused by API changes in pypa/wheel" + ) + + # Generate an simplified "egg-info" dir for pypa/wheel to convert + pkg_info = _get_pkginfo(dist) + egg_info_dir = tmp_path / "pkg.egg-info" + egg_info_dir.mkdir(parents=True) + (egg_info_dir / "PKG-INFO").write_text(pkg_info, encoding="utf-8") + write_requirements(egg_info(dist), egg_info_dir, egg_info_dir / "requires.txt") + + # Get pypa/wheel generated METADATA but normalize requirements formatting + metadata_msg = pkginfo_to_metadata(egg_info_dir, egg_info_dir / "PKG-INFO") + metadata_str = _normalize_metadata(metadata_msg) + pkg_info_msg = message_from_string(pkg_info) + pkg_info_str = _normalize_metadata(pkg_info_msg) + + # Compare setuptools PKG-INFO x pypa/wheel METADATA + assert metadata_str == pkg_info_str + + # Make sure it parses/serializes well in pypa/wheel + _assert_roundtrip_message(pkg_info) + + +class TestPEP643: + STATIC_CONFIG = { + "setup.cfg": cleandoc( + """ + [metadata] + name = package + version = 0.0.1 + author = Foo Bar + author_email = foo@bar.net + long_description = Long + description + description = Short description + keywords = one, two + platforms = abcd + [options] + install_requires = requests + """ + ), + "pyproject.toml": cleandoc( + """ + [project] + name = "package" + version = "0.0.1" + authors = [ + {name = "Foo Bar", email = "foo@bar.net"} + ] + description = "Short description" + readme = {text = "Long\\ndescription", content-type = "text/plain"} + keywords = ["one", "two"] + dependencies = ["requests"] + license = "AGPL-3.0-or-later" + [tool.setuptools] + provides = ["abcd"] + obsoletes = ["abcd"] + """ + ), + } + + @pytest.mark.parametrize("file", STATIC_CONFIG.keys()) + def test_static_config_has_no_dynamic(self, file, tmpdir_cwd): + Path(file).write_text(self.STATIC_CONFIG[file], encoding="utf-8") + metadata = _get_metadata() + assert metadata.get_all("Dynamic") is None + assert metadata.get_all("dynamic") is None + + @pytest.mark.parametrize("file", STATIC_CONFIG.keys()) + @pytest.mark.parametrize( + "fields", + [ + # Single dynamic field + {"requires-python": ("python_requires", ">=3.12")}, + {"author-email": ("author_email", "snoopy@peanuts.com")}, + {"keywords": ("keywords", ["hello", "world"])}, + {"platform": ("platforms", ["abcd"])}, + # Multiple dynamic fields + { + "summary": ("description", "hello world"), + "description": ("long_description", "bla bla bla bla"), + "requires-dist": ("install_requires", ["hello-world"]), + }, + ], + ) + def test_modified_fields_marked_as_dynamic(self, file, fields, tmpdir_cwd): + # We start with a static config + Path(file).write_text(self.STATIC_CONFIG[file], encoding="utf-8") + dist = _makedist() + + # ... but then we simulate the effects of a plugin modifying the distribution + for attr, value in fields.values(): + # `dist` and `dist.metadata` are complicated... + # Some attributes work when set on `dist`, others on `dist.metadata`... + # Here we set in both just in case (this also avoids calling `_finalize_*`) + setattr(dist, attr, value) + setattr(dist.metadata, attr, value) + + # Then we should be able to list the modified fields as Dynamic + metadata = _get_metadata(dist) + assert set(metadata.get_all("Dynamic")) == set(fields) + + @pytest.mark.parametrize( + "extra_toml", + [ + "# Let setuptools autofill license-files", + "license-files = ['LICENSE*', 'AUTHORS*', 'NOTICE']", + ], + ) + def test_license_files_dynamic(self, extra_toml, tmpdir_cwd): + # For simplicity (and for the time being) setuptools is not making + # any special handling to guarantee `License-File` is considered static. + # Instead we rely in the fact that, although suboptimal, it is OK to have + # it as dynamics, as per: + # https://github.com/pypa/setuptools/issues/4629#issuecomment-2331233677 + files = { + "pyproject.toml": self.STATIC_CONFIG["pyproject.toml"].replace( + 'license = "AGPL-3.0-or-later"', + f"dynamic = ['license']\n{extra_toml}", + ), + "LICENSE.md": "--- mock license ---", + "NOTICE": "--- mock notice ---", + "AUTHORS.txt": "--- me ---", + } + # Sanity checks: + assert extra_toml in files["pyproject.toml"] + assert 'license = "AGPL-3.0-or-later"' not in extra_toml + + jaraco.path.build(files) + dist = _makedist(license_expression="AGPL-3.0-or-later") + metadata = _get_metadata(dist) + assert set(metadata.get_all("Dynamic")) == { + 'license-file', + 'license-expression', + } + assert metadata.get("License-Expression") == "AGPL-3.0-or-later" + assert set(metadata.get_all("License-File")) == { + "NOTICE", + "AUTHORS.txt", + "LICENSE.md", + } + + +def _makedist(**attrs): + dist = Distribution(attrs) + dist.parse_config_files() + return dist + + +def _assert_roundtrip_message(metadata: str) -> None: + """Emulate the way wheel.bdist_wheel parses and regenerates the message, + then ensures the metadata generated by setuptools is compatible. + """ + with io.StringIO(metadata) as buffer: + msg = Parser(EmailMessage).parse(buffer) + + serialization_policy = EmailPolicy( + utf8=True, + mangle_from_=False, + max_line_length=0, + ) + with io.BytesIO() as buffer: + out = io.TextIOWrapper(buffer, encoding="utf-8") + Generator(out, policy=serialization_policy).flatten(msg) + out.flush() + regenerated = buffer.getvalue() + + raw_metadata = bytes(metadata, "utf-8") + # Normalise newlines to avoid test errors on Windows: + raw_metadata = b"\n".join(raw_metadata.splitlines()) + regenerated = b"\n".join(regenerated.splitlines()) + assert regenerated == raw_metadata + + +def _normalize_metadata(msg: Message) -> str: + """Allow equivalent metadata to be compared directly""" + # The main challenge regards the requirements and extras. + # Both setuptools and wheel already apply some level of normalization + # but they differ regarding which character is chosen, according to the + # following spec it should be "-": + # https://packaging.python.org/en/latest/specifications/name-normalization/ + + # Related issues: + # https://github.com/pypa/packaging/issues/845 + # https://github.com/pypa/packaging/issues/644#issuecomment-2429813968 + + extras = {x.replace("_", "-"): x for x in msg.get_all("Provides-Extra", [])} + reqs = [ + _normalize_req(req, extras) + for req in _reqs.parse(msg.get_all("Requires-Dist", [])) + ] + del msg["Requires-Dist"] + del msg["Provides-Extra"] + + # Ensure consistent ord + for req in sorted(reqs): + msg["Requires-Dist"] = req + for extra in sorted(extras): + msg["Provides-Extra"] = extra + + # TODO: Handle lack of PEP 643 implementation in pypa/wheel? + del msg["Metadata-Version"] + + return msg.as_string() + + +def _normalize_req(req: Requirement, extras: dict[str, str]) -> str: + """Allow equivalent requirement objects to be compared directly""" + as_str = str(req).replace(req.name, req.name.replace("_", "-")) + for norm, orig in extras.items(): + as_str = as_str.replace(orig, norm) + return as_str + + +def _get_pkginfo(dist: Distribution): + with io.StringIO() as fp: + dist.metadata.write_pkg_file(fp) + return fp.getvalue() + + +def _get_metadata(dist: Distribution | None = None): + return message_from_string(_get_pkginfo(dist or _makedist())) + + +def _valid_metadata(text: str) -> bool: + metadata = Metadata.from_email(text, validate=True) # can raise exceptions + return metadata is not None diff --git a/lib/python3.12/site-packages/setuptools/tests/test_depends.py b/lib/python3.12/site-packages/setuptools/tests/test_depends.py new file mode 100644 index 0000000000000000000000000000000000000000..1714c041f7a23e1ecbfc3245bf964f75c13734ca --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_depends.py @@ -0,0 +1,15 @@ +import sys + +from setuptools import depends + + +class TestGetModuleConstant: + def test_basic(self): + """ + Invoke get_module_constant on a module in + the test package. + """ + mod_name = 'setuptools.tests.mod_with_constant' + val = depends.get_module_constant(mod_name, 'value') + assert val == 'three, sir!' + assert 'setuptools.tests.mod_with_constant' not in sys.modules diff --git a/lib/python3.12/site-packages/setuptools/tests/test_develop.py b/lib/python3.12/site-packages/setuptools/tests/test_develop.py new file mode 100644 index 0000000000000000000000000000000000000000..354c51fc3c7888de2161122f6f3cafa55b181594 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_develop.py @@ -0,0 +1,112 @@ +"""develop tests""" + +import os +import platform +import subprocess +import sys + +import pytest + +from setuptools._path import paths_on_pythonpath + +from . import contexts, namespaces + +SETUP_PY = """\ +from setuptools import setup + +setup(name='foo', + packages=['foo'], +) +""" + +INIT_PY = """print "foo" +""" + + +@pytest.fixture +def temp_user(monkeypatch): + with contexts.tempdir() as user_base: + with contexts.tempdir() as user_site: + monkeypatch.setattr('site.USER_BASE', user_base) + monkeypatch.setattr('site.USER_SITE', user_site) + yield + + +@pytest.fixture +def test_env(tmpdir, temp_user): + target = tmpdir + foo = target.mkdir('foo') + setup = target / 'setup.py' + if setup.isfile(): + raise ValueError(dir(target)) + with setup.open('w') as f: + f.write(SETUP_PY) + init = foo / '__init__.py' + with init.open('w') as f: + f.write(INIT_PY) + with target.as_cwd(): + yield target + + +class TestNamespaces: + @staticmethod + def install_develop(src_dir, target): + develop_cmd = [ + sys.executable, + 'setup.py', + 'develop', + '--install-dir', + str(target), + ] + with src_dir.as_cwd(): + with paths_on_pythonpath([str(target)]): + subprocess.check_call(develop_cmd) + + @pytest.mark.skipif( + bool(os.environ.get("APPVEYOR")), + reason="https://github.com/pypa/setuptools/issues/851", + ) + @pytest.mark.skipif( + platform.python_implementation() == 'PyPy', + reason="https://github.com/pypa/setuptools/issues/1202", + ) + @pytest.mark.uses_network + def test_namespace_package_importable(self, tmpdir): + """ + Installing two packages sharing the same namespace, one installed + naturally using pip or `--single-version-externally-managed` + and the other installed using `develop` should leave the namespace + in tact and both packages reachable by import. + """ + pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') + pkg_B = namespaces.build_namespace_package(tmpdir, 'myns.pkgB') + target = tmpdir / 'packages' + # use pip to install to the target directory + install_cmd = [ + sys.executable, + '-m', + 'pip', + 'install', + str(pkg_A), + '-t', + str(target), + ] + subprocess.check_call(install_cmd) + self.install_develop(pkg_B, target) + namespaces.make_site_dir(target) + try_import = [ + sys.executable, + '-c', + 'import myns.pkgA; import myns.pkgB', + ] + with paths_on_pythonpath([str(target)]): + subprocess.check_call(try_import) + + # additionally ensure that pkg_resources import works + pkg_resources_imp = [ + sys.executable, + '-c', + 'import pkg_resources', + ] + with paths_on_pythonpath([str(target)]): + subprocess.check_call(pkg_resources_imp) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_dist.py b/lib/python3.12/site-packages/setuptools/tests/test_dist.py new file mode 100644 index 0000000000000000000000000000000000000000..9685dcd7cbbeceb1e35d69de30b9e2fdc3f06227 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_dist.py @@ -0,0 +1,280 @@ +import os +import re +import urllib.parse +import urllib.request + +import pytest + +from setuptools import Distribution +from setuptools.dist import check_package_data, check_specifier + +from .fixtures import make_trivial_sdist +from .test_find_packages import ensure_files +from .textwrap import DALS + +from distutils.errors import DistutilsSetupError + + +def test_dist_fetch_build_egg(tmpdir, setuptools_wheel): + """ + Check multiple calls to `Distribution.fetch_build_egg` work as expected. + """ + index = tmpdir.mkdir('index') + index_url = urllib.parse.urljoin('file://', urllib.request.pathname2url(str(index))) + + def sdist_with_index(distname, version): + dist_dir = index.mkdir(distname) + dist_sdist = f'{distname}-{version}.tar.gz' + make_trivial_sdist( + str(dist_dir.join(dist_sdist)), distname, version, setuptools_wheel + ) + with dist_dir.join('index.html').open('w') as fp: + fp.write( + DALS( + """ + + {dist_sdist}
+ + """ + ).format(dist_sdist=dist_sdist) + ) + + sdist_with_index('barbazquux', '3.2.0') + sdist_with_index('barbazquux-runner', '2.11.1') + with tmpdir.join('setup.cfg').open('w') as fp: + fp.write( + DALS( + """ + [easy_install] + index_url = {index_url} + """ + ).format(index_url=index_url) + ) + reqs = """ + barbazquux-runner + barbazquux + """.split() + with tmpdir.as_cwd(): + dist = Distribution() + dist.parse_config_files() + resolved_dists = [dist.fetch_build_egg(r) for r in reqs] + assert [dist.name for dist in resolved_dists if dist] == reqs + + +EXAMPLE_BASE_INFO = dict( + name="package", + version="0.0.1", + author="Foo Bar", + author_email="foo@bar.net", + long_description="Long\ndescription", + description="Short description", + keywords=["one", "two"], +) + + +def test_provides_extras_deterministic_order(): + attrs = dict(extras_require=dict(a=['foo'], b=['bar'])) + dist = Distribution(attrs) + assert list(dist.metadata.provides_extras) == ['a', 'b'] + attrs['extras_require'] = dict(reversed(attrs['extras_require'].items())) + dist = Distribution(attrs) + assert list(dist.metadata.provides_extras) == ['b', 'a'] + + +CHECK_PACKAGE_DATA_TESTS = ( + # Valid. + ( + { + '': ['*.txt', '*.rst'], + 'hello': ['*.msg'], + }, + None, + ), + # Not a dictionary. + ( + ( + ('', ['*.txt', '*.rst']), + ('hello', ['*.msg']), + ), + ( + "'package_data' must be a dictionary mapping package" + " names to lists of string wildcard patterns" + ), + ), + # Invalid key type. + ( + { + 400: ['*.txt', '*.rst'], + }, + ("keys of 'package_data' dict must be strings (got 400)"), + ), + # Invalid value type. + ( + { + 'hello': '*.msg', + }, + ( + "\"values of 'package_data' dict\" must be of type " + " (got '*.msg')" + ), + ), + # Invalid value type (generators are single use) + ( + { + 'hello': (x for x in "generator"), + }, + ( + "\"values of 'package_data' dict\" must be of type " + " (got =3.0, !=3.1'} + dist = Distribution(attrs) + check_specifier(dist, attrs, attrs['python_requires']) + + attrs = {'name': 'foo', 'python_requires': ['>=3.0', '!=3.1']} + dist = Distribution(attrs) + check_specifier(dist, attrs, attrs['python_requires']) + + # invalid specifier value + attrs = {'name': 'foo', 'python_requires': '>=invalid-version'} + with pytest.raises(DistutilsSetupError): + dist = Distribution(attrs) + + +def test_metadata_name(): + with pytest.raises(DistutilsSetupError, match='missing.*name'): + Distribution()._validate_metadata() + + +@pytest.mark.parametrize( + ('dist_name', 'py_module'), + [ + ("my.pkg", "my_pkg"), + ("my-pkg", "my_pkg"), + ("my_pkg", "my_pkg"), + ("pkg", "pkg"), + ], +) +def test_dist_default_py_modules(tmp_path, dist_name, py_module): + (tmp_path / f"{py_module}.py").touch() + + (tmp_path / "setup.py").touch() + (tmp_path / "noxfile.py").touch() + # ^-- make sure common tool files are ignored + + attrs = {**EXAMPLE_BASE_INFO, "name": dist_name, "src_root": str(tmp_path)} + # Find `py_modules` corresponding to dist_name if not given + dist = Distribution(attrs) + dist.set_defaults() + assert dist.py_modules == [py_module] + # When `py_modules` is given, don't do anything + dist = Distribution({**attrs, "py_modules": ["explicity_py_module"]}) + dist.set_defaults() + assert dist.py_modules == ["explicity_py_module"] + # When `packages` is given, don't do anything + dist = Distribution({**attrs, "packages": ["explicity_package"]}) + dist.set_defaults() + assert not dist.py_modules + + +@pytest.mark.parametrize( + ('dist_name', 'package_dir', 'package_files', 'packages'), + [ + ("my.pkg", None, ["my_pkg/__init__.py", "my_pkg/mod.py"], ["my_pkg"]), + ("my-pkg", None, ["my_pkg/__init__.py", "my_pkg/mod.py"], ["my_pkg"]), + ("my_pkg", None, ["my_pkg/__init__.py", "my_pkg/mod.py"], ["my_pkg"]), + ("my.pkg", None, ["my/pkg/__init__.py"], ["my", "my.pkg"]), + ( + "my_pkg", + None, + ["src/my_pkg/__init__.py", "src/my_pkg2/__init__.py"], + ["my_pkg", "my_pkg2"], + ), + ( + "my_pkg", + {"pkg": "lib", "pkg2": "lib2"}, + ["lib/__init__.py", "lib/nested/__init__.pyt", "lib2/__init__.py"], + ["pkg", "pkg.nested", "pkg2"], + ), + ], +) +def test_dist_default_packages( + tmp_path, dist_name, package_dir, package_files, packages +): + ensure_files(tmp_path, package_files) + + (tmp_path / "setup.py").touch() + (tmp_path / "noxfile.py").touch() + # ^-- should not be included by default + + attrs = { + **EXAMPLE_BASE_INFO, + "name": dist_name, + "src_root": str(tmp_path), + "package_dir": package_dir, + } + # Find `packages` either corresponding to dist_name or inside src + dist = Distribution(attrs) + dist.set_defaults() + assert not dist.py_modules + assert not dist.py_modules + assert set(dist.packages) == set(packages) + # When `py_modules` is given, don't do anything + dist = Distribution({**attrs, "py_modules": ["explicit_py_module"]}) + dist.set_defaults() + assert not dist.packages + assert set(dist.py_modules) == {"explicit_py_module"} + # When `packages` is given, don't do anything + dist = Distribution({**attrs, "packages": ["explicit_package"]}) + dist.set_defaults() + assert not dist.py_modules + assert set(dist.packages) == {"explicit_package"} + + +@pytest.mark.parametrize( + ('dist_name', 'package_dir', 'package_files'), + [ + ("my.pkg.nested", None, ["my/pkg/nested/__init__.py"]), + ("my.pkg", None, ["my/pkg/__init__.py", "my/pkg/file.py"]), + ("my_pkg", None, ["my_pkg.py"]), + ("my_pkg", None, ["my_pkg/__init__.py", "my_pkg/nested/__init__.py"]), + ("my_pkg", None, ["src/my_pkg/__init__.py", "src/my_pkg/nested/__init__.py"]), + ( + "my_pkg", + {"my_pkg": "lib", "my_pkg.lib2": "lib2"}, + ["lib/__init__.py", "lib/nested/__init__.pyt", "lib2/__init__.py"], + ), + # Should not try to guess a name from multiple py_modules/packages + ("UNKNOWN", None, ["src/mod1.py", "src/mod2.py"]), + ("UNKNOWN", None, ["src/pkg1/__ini__.py", "src/pkg2/__init__.py"]), + ], +) +def test_dist_default_name(tmp_path, dist_name, package_dir, package_files): + """Make sure dist.name is discovered from packages/py_modules""" + ensure_files(tmp_path, package_files) + attrs = { + **EXAMPLE_BASE_INFO, + "src_root": "/".join(os.path.split(tmp_path)), # POSIX-style + "package_dir": package_dir, + } + del attrs["name"] + + dist = Distribution(attrs) + dist.set_defaults() + assert dist.py_modules or dist.packages + assert dist.get_name() == dist_name diff --git a/lib/python3.12/site-packages/setuptools/tests/test_dist_info.py b/lib/python3.12/site-packages/setuptools/tests/test_dist_info.py new file mode 100644 index 0000000000000000000000000000000000000000..f65d0afbe46299fa816b2c64dc538dbd66880dab --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_dist_info.py @@ -0,0 +1,147 @@ +"""Test .dist-info style distributions.""" + +import pathlib +import re +import shutil +import subprocess +import sys +from functools import partial + +import pytest + +from setuptools.archive_util import unpack_archive + +from .textwrap import DALS + +read = partial(pathlib.Path.read_text, encoding="utf-8") + + +class TestDistInfo: + def test_invalid_version(self, tmp_path): + """ + Supplying an invalid version crashes dist_info. + """ + config = "[metadata]\nname=proj\nversion=42\n[egg_info]\ntag_build=invalid!!!\n" + (tmp_path / "setup.cfg").write_text(config, encoding="utf-8") + msg = re.compile("invalid version", re.MULTILINE | re.IGNORECASE) + proc = run_command_inner("dist_info", cwd=tmp_path, check=False) + assert proc.returncode + assert msg.search(proc.stdout) + assert not list(tmp_path.glob("*.dist-info")) + + def test_tag_arguments(self, tmp_path): + config = """ + [metadata] + name=proj + version=42 + [egg_info] + tag_date=1 + tag_build=.post + """ + (tmp_path / "setup.cfg").write_text(config, encoding="utf-8") + + print(run_command("dist_info", "--no-date", cwd=tmp_path)) + dist_info = next(tmp_path.glob("*.dist-info")) + assert dist_info.name.startswith("proj-42") + shutil.rmtree(dist_info) + + print(run_command("dist_info", "--tag-build", ".a", cwd=tmp_path)) + dist_info = next(tmp_path.glob("*.dist-info")) + assert dist_info.name.startswith("proj-42a") + + @pytest.mark.parametrize("keep_egg_info", (False, True)) + def test_output_dir(self, tmp_path, keep_egg_info): + config = "[metadata]\nname=proj\nversion=42\n" + (tmp_path / "setup.cfg").write_text(config, encoding="utf-8") + out = tmp_path / "__out" + out.mkdir() + opts = ["--keep-egg-info"] if keep_egg_info else [] + run_command("dist_info", "--output-dir", out, *opts, cwd=tmp_path) + assert len(list(out.glob("*.dist-info"))) == 1 + assert len(list(tmp_path.glob("*.dist-info"))) == 0 + expected_egg_info = int(keep_egg_info) + assert len(list(out.glob("*.egg-info"))) == expected_egg_info + assert len(list(tmp_path.glob("*.egg-info"))) == 0 + assert len(list(out.glob("*.__bkp__"))) == 0 + assert len(list(tmp_path.glob("*.__bkp__"))) == 0 + + +class TestWheelCompatibility: + """Make sure the .dist-info directory produced with the ``dist_info`` command + is the same as the one produced by ``bdist_wheel``. + """ + + SETUPCFG = DALS( + """ + [metadata] + name = {name} + version = {version} + + [options] + install_requires = + foo>=12; sys_platform != "linux" + + [options.extras_require] + test = pytest + + [options.entry_points] + console_scripts = + executable-name = my_package.module:function + discover = + myproj = my_package.other_module:function + """ + ) + + EGG_INFO_OPTS = [ + # Related: #3088 #2872 + ("", ""), + (".post", "[egg_info]\ntag_build = post\n"), + (".post", "[egg_info]\ntag_build = .post\n"), + (".post", "[egg_info]\ntag_build = post\ntag_date = 1\n"), + (".dev", "[egg_info]\ntag_build = .dev\n"), + (".dev", "[egg_info]\ntag_build = .dev\ntag_date = 1\n"), + ("a1", "[egg_info]\ntag_build = .a1\n"), + ("+local", "[egg_info]\ntag_build = +local\n"), + ] + + @pytest.mark.parametrize("name", "my-proj my_proj my.proj My.Proj".split()) + @pytest.mark.parametrize("version", ["0.42.13"]) + @pytest.mark.parametrize(("suffix", "cfg"), EGG_INFO_OPTS) + def test_dist_info_is_the_same_as_in_wheel( + self, name, version, tmp_path, suffix, cfg + ): + config = self.SETUPCFG.format(name=name, version=version) + cfg + + for i in "dir_wheel", "dir_dist": + (tmp_path / i).mkdir() + (tmp_path / i / "setup.cfg").write_text(config, encoding="utf-8") + + run_command("bdist_wheel", cwd=tmp_path / "dir_wheel") + wheel = next(tmp_path.glob("dir_wheel/dist/*.whl")) + unpack_archive(wheel, tmp_path / "unpack") + wheel_dist_info = next(tmp_path.glob("unpack/*.dist-info")) + + run_command("dist_info", cwd=tmp_path / "dir_dist") + dist_info = next(tmp_path.glob("dir_dist/*.dist-info")) + + assert dist_info.name == wheel_dist_info.name + assert dist_info.name.startswith(f"my_proj-{version}{suffix}") + for file in "METADATA", "entry_points.txt": + assert read(dist_info / file) == read(wheel_dist_info / file) + + +def run_command_inner(*cmd, **kwargs): + opts = { + "stderr": subprocess.STDOUT, + "stdout": subprocess.PIPE, + "text": True, + "encoding": "utf-8", + "check": True, + **kwargs, + } + cmd = [sys.executable, "-c", "__import__('setuptools').setup()", *map(str, cmd)] + return subprocess.run(cmd, **opts) + + +def run_command(*args, **kwargs): + return run_command_inner(*args, **kwargs).stdout diff --git a/lib/python3.12/site-packages/setuptools/tests/test_distutils_adoption.py b/lib/python3.12/site-packages/setuptools/tests/test_distutils_adoption.py new file mode 100644 index 0000000000000000000000000000000000000000..f99a58849950029f53322c19cde1c171fe26622c --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_distutils_adoption.py @@ -0,0 +1,198 @@ +import os +import platform +import sys +import textwrap + +import pytest + +IS_PYPY = '__pypy__' in sys.builtin_module_names + +_TEXT_KWARGS = {"text": True, "encoding": "utf-8"} # For subprocess.run + + +def win_sr(env): + """ + On Windows, SYSTEMROOT must be present to avoid + + > Fatal Python error: _Py_HashRandomization_Init: failed to + > get random numbers to initialize Python + """ + if env and platform.system() == 'Windows': + env['SYSTEMROOT'] = os.environ['SYSTEMROOT'] + return env + + +def find_distutils(venv, imports='distutils', env=None, **kwargs): + py_cmd = 'import {imports}; print(distutils.__file__)'.format(**locals()) + cmd = ['python', '-c', py_cmd] + return venv.run(cmd, env=win_sr(env), **_TEXT_KWARGS, **kwargs) + + +def count_meta_path(venv, env=None): + py_cmd = textwrap.dedent( + """ + import sys + is_distutils = lambda finder: finder.__class__.__name__ == "DistutilsMetaFinder" + print(len(list(filter(is_distutils, sys.meta_path)))) + """ + ) + cmd = ['python', '-c', py_cmd] + return int(venv.run(cmd, env=win_sr(env), **_TEXT_KWARGS)) + + +skip_without_stdlib_distutils = pytest.mark.skipif( + sys.version_info >= (3, 12), + reason='stdlib distutils is removed from Python 3.12+', +) + + +@skip_without_stdlib_distutils +def test_distutils_stdlib(venv): + """ + Ensure stdlib distutils is used when appropriate. + """ + env = dict(SETUPTOOLS_USE_DISTUTILS='stdlib') + assert venv.name not in find_distutils(venv, env=env).split(os.sep) + assert count_meta_path(venv, env=env) == 0 + + +def test_distutils_local_with_setuptools(venv): + """ + Ensure local distutils is used when appropriate. + """ + env = dict(SETUPTOOLS_USE_DISTUTILS='local') + loc = find_distutils(venv, imports='setuptools, distutils', env=env) + assert venv.name in loc.split(os.sep) + assert count_meta_path(venv, env=env) <= 1 + + +@pytest.mark.xfail('IS_PYPY', reason='pypy imports distutils on startup') +def test_distutils_local(venv): + """ + Even without importing, the setuptools-local copy of distutils is + preferred. + """ + env = dict(SETUPTOOLS_USE_DISTUTILS='local') + assert venv.name in find_distutils(venv, env=env).split(os.sep) + assert count_meta_path(venv, env=env) <= 1 + + +def test_pip_import(venv): + """ + Ensure pip can be imported. + Regression test for #3002. + """ + cmd = ['python', '-c', 'import pip'] + venv.run(cmd, **_TEXT_KWARGS) + + +def test_distutils_has_origin(): + """ + Distutils module spec should have an origin. #2990. + """ + assert __import__('distutils').__spec__.origin + + +ENSURE_IMPORTS_ARE_NOT_DUPLICATED = r""" +# Depending on the importlib machinery and _distutils_hack, some imports are +# duplicated resulting in different module objects being loaded, which prevents +# patches as shown in #3042. +# This script provides a way of verifying if this duplication is happening. + +from distutils import cmd +import distutils.command.sdist as sdist + +# import last to prevent caching +from distutils import {imported_module} + +for mod in (cmd, sdist): + assert mod.{imported_module} == {imported_module}, ( + f"\n{{mod.dir_util}}\n!=\n{{{imported_module}}}" + ) + +print("success") +""" + + +@pytest.mark.usefixtures("tmpdir_cwd") +@pytest.mark.parametrize( + ('distutils_version', 'imported_module'), + [ + pytest.param("stdlib", "dir_util", marks=skip_without_stdlib_distutils), + pytest.param("stdlib", "file_util", marks=skip_without_stdlib_distutils), + pytest.param("stdlib", "archive_util", marks=skip_without_stdlib_distutils), + ("local", "dir_util"), + ("local", "file_util"), + ("local", "archive_util"), + ], +) +def test_modules_are_not_duplicated_on_import(distutils_version, imported_module, venv): + env = dict(SETUPTOOLS_USE_DISTUTILS=distutils_version) + script = ENSURE_IMPORTS_ARE_NOT_DUPLICATED.format(imported_module=imported_module) + cmd = ['python', '-c', script] + output = venv.run(cmd, env=win_sr(env), **_TEXT_KWARGS).strip() + assert output == "success" + + +ENSURE_LOG_IMPORT_IS_NOT_DUPLICATED = r""" +import types +import distutils.dist as dist +from distutils import log +if isinstance(dist.log, types.ModuleType): + assert dist.log == log, f"\n{dist.log}\n!=\n{log}" +print("success") +""" + + +@pytest.mark.usefixtures("tmpdir_cwd") +@pytest.mark.parametrize( + "distutils_version", + [ + "local", + pytest.param("stdlib", marks=skip_without_stdlib_distutils), + ], +) +def test_log_module_is_not_duplicated_on_import(distutils_version, venv): + env = dict(SETUPTOOLS_USE_DISTUTILS=distutils_version) + cmd = ['python', '-c', ENSURE_LOG_IMPORT_IS_NOT_DUPLICATED] + output = venv.run(cmd, env=win_sr(env), **_TEXT_KWARGS).strip() + assert output == "success" + + +ENSURE_CONSISTENT_ERROR_FROM_MODIFIED_PY = r""" +from setuptools.modified import newer +from {imported_module}.errors import DistutilsError + +# Can't use pytest.raises in this context +try: + newer("", "") +except DistutilsError: + print("success") +else: + raise AssertionError("Expected to raise") +""" + + +@pytest.mark.usefixtures("tmpdir_cwd") +@pytest.mark.parametrize( + ('distutils_version', 'imported_module'), + [ + ("local", "distutils"), + # Unfortunately we still get ._distutils.errors.DistutilsError with SETUPTOOLS_USE_DISTUTILS=stdlib + # But that's a deprecated use-case we don't mind not fully supporting in newer code + pytest.param( + "stdlib", "setuptools._distutils", marks=skip_without_stdlib_distutils + ), + ], +) +def test_consistent_error_from_modified_py(distutils_version, imported_module, venv): + env = dict(SETUPTOOLS_USE_DISTUTILS=distutils_version) + cmd = [ + 'python', + '-c', + ENSURE_CONSISTENT_ERROR_FROM_MODIFIED_PY.format( + imported_module=imported_module + ), + ] + output = venv.run(cmd, env=win_sr(env), **_TEXT_KWARGS).strip() + assert output == "success" diff --git a/lib/python3.12/site-packages/setuptools/tests/test_editable_install.py b/lib/python3.12/site-packages/setuptools/tests/test_editable_install.py new file mode 100644 index 0000000000000000000000000000000000000000..225fc6a2f501e46e6b8f0c0010bf332dd1e59c54 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_editable_install.py @@ -0,0 +1,1263 @@ +from __future__ import annotations + +import os +import platform +import stat +import subprocess +import sys +from copy import deepcopy +from importlib import import_module +from importlib.machinery import EXTENSION_SUFFIXES +from pathlib import Path +from textwrap import dedent +from typing import Any +from unittest.mock import Mock +from uuid import uuid4 + +import jaraco.envs +import jaraco.path +import pytest +from path import Path as _Path + +from setuptools._importlib import resources as importlib_resources +from setuptools.command.editable_wheel import ( + _encode_pth, + _find_namespaces, + _find_package_roots, + _find_virtual_namespaces, + _finder_template, + _LinkTree, + _TopLevelFinder, + editable_wheel, +) +from setuptools.dist import Distribution +from setuptools.extension import Extension +from setuptools.warnings import SetuptoolsDeprecationWarning + +from . import contexts, namespaces + +from distutils.core import run_setup + + +@pytest.fixture(params=["strict", "lenient"]) +def editable_opts(request): + if request.param == "strict": + return ["--config-settings", "editable-mode=strict"] + return [] + + +EXAMPLE = { + 'pyproject.toml': dedent( + """\ + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + + [project] + name = "mypkg" + version = "3.14159" + license = {text = "MIT"} + description = "This is a Python package" + dynamic = ["readme"] + classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers" + ] + urls = {Homepage = "https://github.com"} + + [tool.setuptools] + package-dir = {"" = "src"} + packages = {find = {where = ["src"]}} + license-files = ["LICENSE*"] + + [tool.setuptools.dynamic] + readme = {file = "README.rst"} + + [tool.distutils.egg_info] + tag-build = ".post0" + """ + ), + "MANIFEST.in": dedent( + """\ + global-include *.py *.txt + global-exclude *.py[cod] + prune dist + prune build + """ + ).strip(), + "README.rst": "This is a ``README``", + "LICENSE.txt": "---- placeholder MIT license ----", + "src": { + "mypkg": { + "__init__.py": dedent( + """\ + import sys + from importlib.metadata import PackageNotFoundError, version + + try: + __version__ = version(__name__) + except PackageNotFoundError: + __version__ = "unknown" + """ + ), + "__main__.py": dedent( + """\ + from importlib.resources import read_text + from . import __version__, __name__ as parent + from .mod import x + + data = read_text(parent, "data.txt") + print(__version__, data, x) + """ + ), + "mod.py": "x = ''", + "data.txt": "Hello World", + } + }, +} + + +SETUP_SCRIPT_STUB = "__import__('setuptools').setup()" + + +@pytest.mark.xfail(sys.platform == "darwin", reason="pypa/setuptools#4328") +@pytest.mark.parametrize( + "files", + [ + {**EXAMPLE, "setup.py": SETUP_SCRIPT_STUB}, + EXAMPLE, # No setup.py script + ], +) +def test_editable_with_pyproject(tmp_path, venv, files, editable_opts): + project = tmp_path / "mypkg" + project.mkdir() + jaraco.path.build(files, prefix=project) + + cmd = [ + "python", + "-m", + "pip", + "install", + "--no-build-isolation", # required to force current version of setuptools + "-e", + str(project), + *editable_opts, + ] + print(venv.run(cmd)) + + cmd = ["python", "-m", "mypkg"] + assert venv.run(cmd).strip() == "3.14159.post0 Hello World" + + (project / "src/mypkg/data.txt").write_text("foobar", encoding="utf-8") + (project / "src/mypkg/mod.py").write_text("x = 42", encoding="utf-8") + assert venv.run(cmd).strip() == "3.14159.post0 foobar 42" + + +def test_editable_with_flat_layout(tmp_path, venv, editable_opts): + files = { + "mypkg": { + "pyproject.toml": dedent( + """\ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "mypkg" + version = "3.14159" + + [tool.setuptools] + packages = ["pkg"] + py-modules = ["mod"] + """ + ), + "pkg": {"__init__.py": "a = 4"}, + "mod.py": "b = 2", + }, + } + jaraco.path.build(files, prefix=tmp_path) + project = tmp_path / "mypkg" + + cmd = [ + "python", + "-m", + "pip", + "install", + "--no-build-isolation", # required to force current version of setuptools + "-e", + str(project), + *editable_opts, + ] + print(venv.run(cmd)) + cmd = ["python", "-c", "import pkg, mod; print(pkg.a, mod.b)"] + assert venv.run(cmd).strip() == "4 2" + + +def test_editable_with_single_module(tmp_path, venv, editable_opts): + files = { + "mypkg": { + "pyproject.toml": dedent( + """\ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "mod" + version = "3.14159" + + [tool.setuptools] + py-modules = ["mod"] + """ + ), + "mod.py": "b = 2", + }, + } + jaraco.path.build(files, prefix=tmp_path) + project = tmp_path / "mypkg" + + cmd = [ + "python", + "-m", + "pip", + "install", + "--no-build-isolation", # required to force current version of setuptools + "-e", + str(project), + *editable_opts, + ] + print(venv.run(cmd)) + cmd = ["python", "-c", "import mod; print(mod.b)"] + assert venv.run(cmd).strip() == "2" + + +class TestLegacyNamespaces: + # legacy => pkg_resources.declare_namespace(...) + setup(namespace_packages=...) + + def test_nspkg_file_is_unique(self, tmp_path, monkeypatch): + deprecation = pytest.warns( + SetuptoolsDeprecationWarning, match=".*namespace_packages parameter.*" + ) + installation_dir = tmp_path / ".installation_dir" + installation_dir.mkdir() + examples = ( + "myns.pkgA", + "myns.pkgB", + "myns.n.pkgA", + "myns.n.pkgB", + ) + + for name in examples: + pkg = namespaces.build_namespace_package(tmp_path, name, version="42") + with deprecation, monkeypatch.context() as ctx: + ctx.chdir(pkg) + dist = run_setup("setup.py", stop_after="config") + cmd = editable_wheel(dist) + cmd.finalize_options() + editable_name = cmd.get_finalized_command("dist_info").name + cmd._install_namespaces(installation_dir, editable_name) + + files = list(installation_dir.glob("*-nspkg.pth")) + assert len(files) == len(examples) + + @pytest.mark.parametrize( + "impl", + ( + "pkg_resources", + # "pkgutil", => does not work + ), + ) + @pytest.mark.parametrize("ns", ("myns.n",)) + def test_namespace_package_importable( + self, venv, tmp_path, ns, impl, editable_opts + ): + """ + Installing two packages sharing the same namespace, one installed + naturally using pip or `--single-version-externally-managed` + and the other installed in editable mode should leave the namespace + intact and both packages reachable by import. + (Ported from test_develop). + """ + build_system = """\ + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + """ + pkg_A = namespaces.build_namespace_package(tmp_path, f"{ns}.pkgA", impl=impl) + pkg_B = namespaces.build_namespace_package(tmp_path, f"{ns}.pkgB", impl=impl) + (pkg_A / "pyproject.toml").write_text(build_system, encoding="utf-8") + (pkg_B / "pyproject.toml").write_text(build_system, encoding="utf-8") + # use pip to install to the target directory + opts = editable_opts[:] + opts.append("--no-build-isolation") # force current version of setuptools + venv.run(["python", "-m", "pip", "install", str(pkg_A), *opts]) + venv.run(["python", "-m", "pip", "install", "-e", str(pkg_B), *opts]) + venv.run(["python", "-c", f"import {ns}.pkgA; import {ns}.pkgB"]) + # additionally ensure that pkg_resources import works + venv.run(["python", "-c", "import pkg_resources"]) + + +class TestPep420Namespaces: + def test_namespace_package_importable(self, venv, tmp_path, editable_opts): + """ + Installing two packages sharing the same namespace, one installed + normally using pip and the other installed in editable mode + should allow importing both packages. + """ + pkg_A = namespaces.build_pep420_namespace_package(tmp_path, 'myns.n.pkgA') + pkg_B = namespaces.build_pep420_namespace_package(tmp_path, 'myns.n.pkgB') + # use pip to install to the target directory + opts = editable_opts[:] + opts.append("--no-build-isolation") # force current version of setuptools + venv.run(["python", "-m", "pip", "install", str(pkg_A), *opts]) + venv.run(["python", "-m", "pip", "install", "-e", str(pkg_B), *opts]) + venv.run(["python", "-c", "import myns.n.pkgA; import myns.n.pkgB"]) + + def test_namespace_created_via_package_dir(self, venv, tmp_path, editable_opts): + """Currently users can create a namespace by tweaking `package_dir`""" + files = { + "pkgA": { + "pyproject.toml": dedent( + """\ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "pkgA" + version = "3.14159" + + [tool.setuptools] + package-dir = {"myns.n.pkgA" = "src"} + """ + ), + "src": {"__init__.py": "a = 1"}, + }, + } + jaraco.path.build(files, prefix=tmp_path) + pkg_A = tmp_path / "pkgA" + pkg_B = namespaces.build_pep420_namespace_package(tmp_path, 'myns.n.pkgB') + pkg_C = namespaces.build_pep420_namespace_package(tmp_path, 'myns.n.pkgC') + + # use pip to install to the target directory + opts = editable_opts[:] + opts.append("--no-build-isolation") # force current version of setuptools + venv.run(["python", "-m", "pip", "install", str(pkg_A), *opts]) + venv.run(["python", "-m", "pip", "install", "-e", str(pkg_B), *opts]) + venv.run(["python", "-m", "pip", "install", "-e", str(pkg_C), *opts]) + venv.run(["python", "-c", "from myns.n import pkgA, pkgB, pkgC"]) + + def test_namespace_accidental_config_in_lenient_mode(self, venv, tmp_path): + """Sometimes users might specify an ``include`` pattern that ignores parent + packages. In a normal installation this would ignore all modules inside the + parent packages, and make them namespaces (reported in issue #3504), + so the editable mode should preserve this behaviour. + """ + files = { + "pkgA": { + "pyproject.toml": dedent( + """\ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "pkgA" + version = "3.14159" + + [tool.setuptools] + packages.find.include = ["mypkg.*"] + """ + ), + "mypkg": { + "__init__.py": "", + "other.py": "b = 1", + "n": { + "__init__.py": "", + "pkgA.py": "a = 1", + }, + }, + "MANIFEST.in": EXAMPLE["MANIFEST.in"], + }, + } + jaraco.path.build(files, prefix=tmp_path) + pkg_A = tmp_path / "pkgA" + + # use pip to install to the target directory + opts = ["--no-build-isolation"] # force current version of setuptools + venv.run(["python", "-m", "pip", "-v", "install", "-e", str(pkg_A), *opts]) + out = venv.run(["python", "-c", "from mypkg.n import pkgA; print(pkgA.a)"]) + assert out.strip() == "1" + cmd = """\ + try: + import mypkg.other + except ImportError: + print("mypkg.other not defined") + """ + out = venv.run(["python", "-c", dedent(cmd)]) + assert "mypkg.other not defined" in out + + +def test_editable_with_prefix(tmp_path, sample_project, editable_opts): + """ + Editable install to a prefix should be discoverable. + """ + prefix = tmp_path / 'prefix' + + # figure out where pip will likely install the package + site_packages_all = [ + prefix / Path(path).relative_to(sys.prefix) + for path in sys.path + if 'site-packages' in path and path.startswith(sys.prefix) + ] + + for sp in site_packages_all: + sp.mkdir(parents=True) + + # install workaround + _addsitedirs(site_packages_all) + + env = dict(os.environ, PYTHONPATH=os.pathsep.join(map(str, site_packages_all))) + cmd = [ + sys.executable, + '-m', + 'pip', + 'install', + '--editable', + str(sample_project), + '--prefix', + str(prefix), + '--no-build-isolation', + *editable_opts, + ] + subprocess.check_call(cmd, env=env) + + # now run 'sample' with the prefix on the PYTHONPATH + bin = 'Scripts' if platform.system() == 'Windows' else 'bin' + exe = prefix / bin / 'sample' + subprocess.check_call([exe], env=env) + + +class TestFinderTemplate: + """This test focus in getting a particular implementation detail right. + If at some point in time the implementation is changed for something different, + this test can be modified or even excluded. + """ + + def install_finder(self, finder): + loc = {} + exec(finder, loc, loc) + loc["install"]() + + def test_packages(self, tmp_path): + files = { + "src1": { + "pkg1": { + "__init__.py": "", + "subpkg": {"mod1.py": "a = 42"}, + }, + }, + "src2": {"mod2.py": "a = 43"}, + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = { + "pkg1": str(tmp_path / "src1/pkg1"), + "mod2": str(tmp_path / "src2/mod2"), + } + template = _finder_template(str(uuid4()), mapping, {}) + + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ("pkg1", "pkg1.subpkg", "pkg1.subpkg.mod1", "mod2"): + sys.modules.pop(mod, None) + + self.install_finder(template) + mod1 = import_module("pkg1.subpkg.mod1") + mod2 = import_module("mod2") + subpkg = import_module("pkg1.subpkg") + + assert mod1.a == 42 + assert mod2.a == 43 + expected = str((tmp_path / "src1/pkg1/subpkg").resolve()) + assert_path(subpkg, expected) + + def test_namespace(self, tmp_path): + files = {"pkg": {"__init__.py": "a = 13", "text.txt": "abc"}} + jaraco.path.build(files, prefix=tmp_path) + + mapping = {"ns.othername": str(tmp_path / "pkg")} + namespaces = {"ns": []} + + template = _finder_template(str(uuid4()), mapping, namespaces) + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ("ns", "ns.othername"): + sys.modules.pop(mod, None) + + self.install_finder(template) + pkg = import_module("ns.othername") + text = importlib_resources.files(pkg) / "text.txt" + + expected = str((tmp_path / "pkg").resolve()) + assert_path(pkg, expected) + assert pkg.a == 13 + + # Make sure resources can also be found + assert text.read_text(encoding="utf-8") == "abc" + + def test_combine_namespaces(self, tmp_path): + files = { + "src1": {"ns": {"pkg1": {"__init__.py": "a = 13"}}}, + "src2": {"ns": {"mod2.py": "b = 37"}}, + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = { + "ns.pkgA": str(tmp_path / "src1/ns/pkg1"), + "ns": str(tmp_path / "src2/ns"), + } + namespaces_ = {"ns": [str(tmp_path / "src1"), str(tmp_path / "src2")]} + template = _finder_template(str(uuid4()), mapping, namespaces_) + + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ("ns", "ns.pkgA", "ns.mod2"): + sys.modules.pop(mod, None) + + self.install_finder(template) + pkgA = import_module("ns.pkgA") + mod2 = import_module("ns.mod2") + + expected = str((tmp_path / "src1/ns/pkg1").resolve()) + assert_path(pkgA, expected) + assert pkgA.a == 13 + assert mod2.b == 37 + + def test_combine_namespaces_nested(self, tmp_path): + """ + Users may attempt to combine namespace packages in a nested way via + ``package_dir`` as shown in pypa/setuptools#4248. + """ + + files = { + "src": {"my_package": {"my_module.py": "a = 13"}}, + "src2": {"my_package2": {"my_module2.py": "b = 37"}}, + } + + stack = jaraco.path.DirectoryStack() + with stack.context(tmp_path): + jaraco.path.build(files) + attrs = { + "script_name": "%PEP 517%", + "package_dir": { + "different_name": "src/my_package", + "different_name.subpkg": "src2/my_package2", + }, + "packages": ["different_name", "different_name.subpkg"], + } + dist = Distribution(attrs) + finder = _TopLevelFinder(dist, str(uuid4())) + code = next(v for k, v in finder.get_implementation() if k.endswith(".py")) + + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in attrs["packages"]: + sys.modules.pop(mod, None) + + self.install_finder(code) + mod1 = import_module("different_name.my_module") + mod2 = import_module("different_name.subpkg.my_module2") + + expected = str((tmp_path / "src/my_package/my_module.py").resolve()) + assert str(Path(mod1.__file__).resolve()) == expected + + expected = str((tmp_path / "src2/my_package2/my_module2.py").resolve()) + assert str(Path(mod2.__file__).resolve()) == expected + + assert mod1.a == 13 + assert mod2.b == 37 + + def test_dynamic_path_computation(self, tmp_path): + # Follows the example in PEP 420 + files = { + "project1": {"parent": {"child": {"one.py": "x = 1"}}}, + "project2": {"parent": {"child": {"two.py": "x = 2"}}}, + "project3": {"parent": {"child": {"three.py": "x = 3"}}}, + } + jaraco.path.build(files, prefix=tmp_path) + mapping = {} + namespaces_ = {"parent": [str(tmp_path / "project1/parent")]} + template = _finder_template(str(uuid4()), mapping, namespaces_) + + mods = (f"parent.child.{name}" for name in ("one", "two", "three")) + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ("parent", "parent.child", "parent.child", *mods): + sys.modules.pop(mod, None) + + self.install_finder(template) + + one = import_module("parent.child.one") + assert one.x == 1 + + with pytest.raises(ImportError): + import_module("parent.child.two") + + sys.path.append(str(tmp_path / "project2")) + two = import_module("parent.child.two") + assert two.x == 2 + + with pytest.raises(ImportError): + import_module("parent.child.three") + + sys.path.append(str(tmp_path / "project3")) + three = import_module("parent.child.three") + assert three.x == 3 + + def test_no_recursion(self, tmp_path): + # See issue #3550 + files = { + "pkg": { + "__init__.py": "from . import pkg", + }, + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = { + "pkg": str(tmp_path / "pkg"), + } + template = _finder_template(str(uuid4()), mapping, {}) + + with contexts.save_paths(), contexts.save_sys_modules(): + sys.modules.pop("pkg", None) + + self.install_finder(template) + with pytest.raises(ImportError, match="pkg"): + import_module("pkg") + + def test_similar_name(self, tmp_path): + files = { + "foo": { + "__init__.py": "", + "bar": { + "__init__.py": "", + }, + }, + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = { + "foo": str(tmp_path / "foo"), + } + template = _finder_template(str(uuid4()), mapping, {}) + + with contexts.save_paths(), contexts.save_sys_modules(): + sys.modules.pop("foo", None) + sys.modules.pop("foo.bar", None) + + self.install_finder(template) + with pytest.raises(ImportError, match="foobar"): + import_module("foobar") + + def test_case_sensitivity(self, tmp_path): + files = { + "foo": { + "__init__.py": "", + "lowercase.py": "x = 1", + "bar": { + "__init__.py": "", + "lowercase.py": "x = 2", + }, + }, + } + jaraco.path.build(files, prefix=tmp_path) + mapping = { + "foo": str(tmp_path / "foo"), + } + template = _finder_template(str(uuid4()), mapping, {}) + with contexts.save_paths(), contexts.save_sys_modules(): + sys.modules.pop("foo", None) + + self.install_finder(template) + with pytest.raises(ImportError, match="'FOO'"): + import_module("FOO") + + with pytest.raises(ImportError, match="'foo\\.LOWERCASE'"): + import_module("foo.LOWERCASE") + + with pytest.raises(ImportError, match="'foo\\.bar\\.Lowercase'"): + import_module("foo.bar.Lowercase") + + with pytest.raises(ImportError, match="'foo\\.BAR'"): + import_module("foo.BAR.lowercase") + + with pytest.raises(ImportError, match="'FOO'"): + import_module("FOO.bar.lowercase") + + mod = import_module("foo.lowercase") + assert mod.x == 1 + + mod = import_module("foo.bar.lowercase") + assert mod.x == 2 + + def test_namespace_case_sensitivity(self, tmp_path): + files = { + "pkg": { + "__init__.py": "a = 13", + "foo": { + "__init__.py": "b = 37", + "bar.py": "c = 42", + }, + }, + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = {"ns.othername": str(tmp_path / "pkg")} + namespaces = {"ns": []} + + template = _finder_template(str(uuid4()), mapping, namespaces) + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ("ns", "ns.othername"): + sys.modules.pop(mod, None) + + self.install_finder(template) + pkg = import_module("ns.othername") + expected = str((tmp_path / "pkg").resolve()) + assert_path(pkg, expected) + assert pkg.a == 13 + + foo = import_module("ns.othername.foo") + assert foo.b == 37 + + bar = import_module("ns.othername.foo.bar") + assert bar.c == 42 + + with pytest.raises(ImportError, match="'NS'"): + import_module("NS.othername.foo") + + with pytest.raises(ImportError, match="'ns\\.othername\\.FOO\\'"): + import_module("ns.othername.FOO") + + with pytest.raises(ImportError, match="'ns\\.othername\\.foo\\.BAR\\'"): + import_module("ns.othername.foo.BAR") + + def test_intermediate_packages(self, tmp_path): + """ + The finder should not import ``fullname`` if the intermediate segments + don't exist (see pypa/setuptools#4019). + """ + files = { + "src": { + "mypkg": { + "__init__.py": "", + "config.py": "a = 13", + "helloworld.py": "b = 13", + "components": { + "config.py": "a = 37", + }, + }, + } + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = {"mypkg": str(tmp_path / "src/mypkg")} + template = _finder_template(str(uuid4()), mapping, {}) + + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ( + "mypkg", + "mypkg.config", + "mypkg.helloworld", + "mypkg.components", + "mypkg.components.config", + "mypkg.components.helloworld", + ): + sys.modules.pop(mod, None) + + self.install_finder(template) + + config = import_module("mypkg.components.config") + assert config.a == 37 + + helloworld = import_module("mypkg.helloworld") + assert helloworld.b == 13 + + with pytest.raises(ImportError): + import_module("mypkg.components.helloworld") + + +def test_pkg_roots(tmp_path): + """This test focus in getting a particular implementation detail right. + If at some point in time the implementation is changed for something different, + this test can be modified or even excluded. + """ + files = { + "a": {"b": {"__init__.py": "ab = 1"}, "__init__.py": "a = 1"}, + "d": {"__init__.py": "d = 1", "e": {"__init__.py": "de = 1"}}, + "f": {"g": {"h": {"__init__.py": "fgh = 1"}}}, + "other": {"__init__.py": "abc = 1"}, + "another": {"__init__.py": "abcxyz = 1"}, + "yet_another": {"__init__.py": "mnopq = 1"}, + } + jaraco.path.build(files, prefix=tmp_path) + package_dir = { + "a.b.c": "other", + "a.b.c.x.y.z": "another", + "m.n.o.p.q": "yet_another", + } + packages = [ + "a", + "a.b", + "a.b.c", + "a.b.c.x.y", + "a.b.c.x.y.z", + "d", + "d.e", + "f", + "f.g", + "f.g.h", + "m.n.o.p.q", + ] + roots = _find_package_roots(packages, package_dir, tmp_path) + assert roots == { + "a": str(tmp_path / "a"), + "a.b.c": str(tmp_path / "other"), + "a.b.c.x.y.z": str(tmp_path / "another"), + "d": str(tmp_path / "d"), + "f": str(tmp_path / "f"), + "m.n.o.p.q": str(tmp_path / "yet_another"), + } + + ns = set(dict(_find_namespaces(packages, roots))) + assert ns == {"f", "f.g"} + + ns = set(_find_virtual_namespaces(roots)) + assert ns == {"a.b", "a.b.c.x", "a.b.c.x.y", "m", "m.n", "m.n.o", "m.n.o.p"} + + +class TestOverallBehaviour: + PYPROJECT = """\ + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + + [project] + name = "mypkg" + version = "3.14159" + """ + + # Any: Would need a TypedDict. Keep it simple for tests + FLAT_LAYOUT: dict[str, Any] = { + "pyproject.toml": dedent(PYPROJECT), + "MANIFEST.in": EXAMPLE["MANIFEST.in"], + "otherfile.py": "", + "mypkg": { + "__init__.py": "", + "mod1.py": "var = 42", + "subpackage": { + "__init__.py": "", + "mod2.py": "var = 13", + "resource_file.txt": "resource 39", + }, + }, + } + + EXAMPLES = { + "flat-layout": FLAT_LAYOUT, + "src-layout": { + "pyproject.toml": dedent(PYPROJECT), + "MANIFEST.in": EXAMPLE["MANIFEST.in"], + "otherfile.py": "", + "src": {"mypkg": FLAT_LAYOUT["mypkg"]}, + }, + "custom-layout": { + "pyproject.toml": dedent(PYPROJECT) + + dedent( + """\ + [tool.setuptools] + packages = ["mypkg", "mypkg.subpackage"] + + [tool.setuptools.package-dir] + "mypkg.subpackage" = "other" + """ + ), + "MANIFEST.in": EXAMPLE["MANIFEST.in"], + "otherfile.py": "", + "mypkg": { + "__init__.py": "", + "mod1.py": FLAT_LAYOUT["mypkg"]["mod1.py"], + }, + "other": FLAT_LAYOUT["mypkg"]["subpackage"], + }, + "namespace": { + "pyproject.toml": dedent(PYPROJECT), + "MANIFEST.in": EXAMPLE["MANIFEST.in"], + "otherfile.py": "", + "src": { + "mypkg": { + "mod1.py": FLAT_LAYOUT["mypkg"]["mod1.py"], + "subpackage": FLAT_LAYOUT["mypkg"]["subpackage"], + }, + }, + }, + } + + @pytest.mark.xfail(sys.platform == "darwin", reason="pypa/setuptools#4328") + @pytest.mark.parametrize("layout", EXAMPLES.keys()) + def test_editable_install(self, tmp_path, venv, layout, editable_opts): + project, _ = install_project( + "mypkg", venv, tmp_path, self.EXAMPLES[layout], *editable_opts + ) + + # Ensure stray files are not importable + cmd_import_error = """\ + try: + import otherfile + except ImportError as ex: + print(ex) + """ + out = venv.run(["python", "-c", dedent(cmd_import_error)]) + assert "No module named 'otherfile'" in out + + # Ensure the modules are importable + cmd_get_vars = """\ + import mypkg, mypkg.mod1, mypkg.subpackage.mod2 + print(mypkg.mod1.var, mypkg.subpackage.mod2.var) + """ + out = venv.run(["python", "-c", dedent(cmd_get_vars)]) + assert "42 13" in out + + # Ensure resources are reachable + cmd_get_resource = """\ + import mypkg.subpackage + from setuptools._importlib import resources as importlib_resources + text = importlib_resources.files(mypkg.subpackage) / "resource_file.txt" + print(text.read_text(encoding="utf-8")) + """ + out = venv.run(["python", "-c", dedent(cmd_get_resource)]) + assert "resource 39" in out + + # Ensure files are editable + mod1 = next(project.glob("**/mod1.py")) + mod2 = next(project.glob("**/mod2.py")) + resource_file = next(project.glob("**/resource_file.txt")) + + mod1.write_text("var = 17", encoding="utf-8") + mod2.write_text("var = 781", encoding="utf-8") + resource_file.write_text("resource 374", encoding="utf-8") + + out = venv.run(["python", "-c", dedent(cmd_get_vars)]) + assert "42 13" not in out + assert "17 781" in out + + out = venv.run(["python", "-c", dedent(cmd_get_resource)]) + assert "resource 39" not in out + assert "resource 374" in out + + +class TestLinkTree: + FILES = deepcopy(TestOverallBehaviour.EXAMPLES["src-layout"]) + FILES["pyproject.toml"] += dedent( + """\ + [tool.setuptools] + # Temporary workaround: both `include-package-data` and `package-data` configs + # can be removed after #3260 is fixed. + include-package-data = false + package-data = {"*" = ["*.txt"]} + + [tool.setuptools.packages.find] + where = ["src"] + exclude = ["*.subpackage*"] + """ + ) + FILES["src"]["mypkg"]["resource.not_in_manifest"] = "abc" + + def test_generated_tree(self, tmp_path): + jaraco.path.build(self.FILES, prefix=tmp_path) + + with _Path(tmp_path): + name = "mypkg-3.14159" + dist = Distribution({"script_name": "%PEP 517%"}) + dist.parse_config_files() + + wheel = Mock() + aux = tmp_path / ".aux" + build = tmp_path / ".build" + aux.mkdir() + build.mkdir() + + build_py = dist.get_command_obj("build_py") + build_py.editable_mode = True + build_py.build_lib = str(build) + build_py.ensure_finalized() + outputs = build_py.get_outputs() + output_mapping = build_py.get_output_mapping() + + make_tree = _LinkTree(dist, name, aux, build) + make_tree(wheel, outputs, output_mapping) + + mod1 = next(aux.glob("**/mod1.py")) + expected = tmp_path / "src/mypkg/mod1.py" + assert_link_to(mod1, expected) + + assert next(aux.glob("**/subpackage"), None) is None + assert next(aux.glob("**/mod2.py"), None) is None + assert next(aux.glob("**/resource_file.txt"), None) is None + + assert next(aux.glob("**/resource.not_in_manifest"), None) is None + + def test_strict_install(self, tmp_path, venv): + opts = ["--config-settings", "editable-mode=strict"] + install_project("mypkg", venv, tmp_path, self.FILES, *opts) + + out = venv.run(["python", "-c", "import mypkg.mod1; print(mypkg.mod1.var)"]) + assert "42" in out + + # Ensure packages excluded from distribution are not importable + cmd_import_error = """\ + try: + from mypkg import subpackage + except ImportError as ex: + print(ex) + """ + out = venv.run(["python", "-c", dedent(cmd_import_error)]) + assert "cannot import name 'subpackage'" in out + + # Ensure resource files excluded from distribution are not reachable + cmd_get_resource = """\ + import mypkg + from setuptools._importlib import resources as importlib_resources + try: + text = importlib_resources.files(mypkg) / "resource.not_in_manifest" + print(text.read_text(encoding="utf-8")) + except FileNotFoundError as ex: + print(ex) + """ + out = venv.run(["python", "-c", dedent(cmd_get_resource)]) + assert "No such file or directory" in out + assert "resource.not_in_manifest" in out + + +@pytest.mark.filterwarnings("ignore:.*compat.*:setuptools.SetuptoolsDeprecationWarning") +def test_compat_install(tmp_path, venv): + # TODO: Remove `compat` after Dec/2022. + opts = ["--config-settings", "editable-mode=compat"] + files = TestOverallBehaviour.EXAMPLES["custom-layout"] + install_project("mypkg", venv, tmp_path, files, *opts) + + out = venv.run(["python", "-c", "import mypkg.mod1; print(mypkg.mod1.var)"]) + assert "42" in out + + expected_path = comparable_path(str(tmp_path)) + + # Compatible behaviour will make spurious modules and excluded + # files importable directly from the original path + for cmd in ( + "import otherfile; print(otherfile)", + "import other; print(other)", + "import mypkg; print(mypkg)", + ): + out = comparable_path(venv.run(["python", "-c", cmd])) + assert expected_path in out + + # Compatible behaviour will not consider custom mappings + cmd = """\ + try: + from mypkg import subpackage; + except ImportError as ex: + print(ex) + """ + out = venv.run(["python", "-c", dedent(cmd)]) + assert "cannot import name 'subpackage'" in out + + +@pytest.mark.uses_network +def test_pbr_integration(pbr_package, venv, editable_opts): + """Ensure editable installs work with pbr, issue #3500""" + cmd = [ + 'python', + '-m', + 'pip', + '-v', + 'install', + '--editable', + pbr_package, + *editable_opts, + ] + venv.run(cmd, stderr=subprocess.STDOUT) + out = venv.run(["python", "-c", "import mypkg.hello"]) + assert "Hello world!" in out + + +class TestCustomBuildPy: + """ + Issue #3501 indicates that some plugins/customizations might rely on: + + 1. ``build_py`` not running + 2. ``build_py`` always copying files to ``build_lib`` + + During the transition period setuptools should prevent potential errors from + happening due to those assumptions. + """ + + # TODO: Remove tests after _run_build_steps is removed. + + FILES = { + **TestOverallBehaviour.EXAMPLES["flat-layout"], + "setup.py": dedent( + """\ + import pathlib + from setuptools import setup + from setuptools.command.build_py import build_py as orig + + class my_build_py(orig): + def run(self): + super().run() + raise ValueError("TEST_RAISE") + + setup(cmdclass={"build_py": my_build_py}) + """ + ), + } + + def test_safeguarded_from_errors(self, tmp_path, venv): + """Ensure that errors in custom build_py are reported as warnings""" + # Warnings should show up + _, out = install_project("mypkg", venv, tmp_path, self.FILES) + assert "SetuptoolsDeprecationWarning" in out + assert "ValueError: TEST_RAISE" in out + # but installation should be successful + out = venv.run(["python", "-c", "import mypkg.mod1; print(mypkg.mod1.var)"]) + assert "42" in out + + +class TestCustomBuildWheel: + def install_custom_build_wheel(self, dist): + bdist_wheel_cls = dist.get_command_class("bdist_wheel") + + class MyBdistWheel(bdist_wheel_cls): + def get_tag(self): + # In issue #3513, we can see that some extensions may try to access + # the `plat_name` property in bdist_wheel + if self.plat_name.startswith("macosx-"): + _ = "macOS platform" + return super().get_tag() + + dist.cmdclass["bdist_wheel"] = MyBdistWheel + + def test_access_plat_name(self, tmpdir_cwd): + # Even when a custom bdist_wheel tries to access plat_name the build should + # be successful + jaraco.path.build({"module.py": "x = 42"}) + dist = Distribution() + dist.script_name = "setup.py" + dist.set_defaults() + self.install_custom_build_wheel(dist) + cmd = editable_wheel(dist) + cmd.ensure_finalized() + cmd.run() + wheel_file = str(next(Path().glob('dist/*.whl'))) + assert "editable" in wheel_file + + +class TestCustomBuildExt: + def install_custom_build_ext_distutils(self, dist): + from distutils.command.build_ext import build_ext as build_ext_cls + + class MyBuildExt(build_ext_cls): + pass + + dist.cmdclass["build_ext"] = MyBuildExt + + @pytest.mark.skipif( + sys.platform != "linux", reason="compilers may fail without correct setup" + ) + def test_distutils_leave_inplace_files(self, tmpdir_cwd): + jaraco.path.build({"module.c": ""}) + attrs = { + "ext_modules": [Extension("module", ["module.c"])], + } + dist = Distribution(attrs) + dist.script_name = "setup.py" + dist.set_defaults() + self.install_custom_build_ext_distutils(dist) + cmd = editable_wheel(dist) + cmd.ensure_finalized() + cmd.run() + wheel_file = str(next(Path().glob('dist/*.whl'))) + assert "editable" in wheel_file + files = [p for p in Path().glob("module.*") if p.suffix != ".c"] + assert len(files) == 1 + name = files[0].name + assert any(name.endswith(ext) for ext in EXTENSION_SUFFIXES) + + +def test_debugging_tips(tmpdir_cwd, monkeypatch): + """Make sure to display useful debugging tips to the user.""" + jaraco.path.build({"module.py": "x = 42"}) + dist = Distribution() + dist.script_name = "setup.py" + dist.set_defaults() + cmd = editable_wheel(dist) + cmd.ensure_finalized() + + SimulatedErr = type("SimulatedErr", (Exception,), {}) + simulated_failure = Mock(side_effect=SimulatedErr()) + monkeypatch.setattr(cmd, "get_finalized_command", simulated_failure) + + with pytest.raises(SimulatedErr) as ctx: + cmd.run() + assert any('debugging-tips' in note for note in ctx.value.__notes__) + + +@pytest.mark.filterwarnings("error") +def test_encode_pth(): + """Ensure _encode_pth function does not produce encoding warnings""" + content = _encode_pth("tkmilan_ç_utf8") # no warnings (would be turned into errors) + assert isinstance(content, bytes) + + +def install_project(name, venv, tmp_path, files, *opts): + project = tmp_path / name + project.mkdir() + jaraco.path.build(files, prefix=project) + opts = [*opts, "--no-build-isolation"] # force current version of setuptools + out = venv.run( + ["python", "-m", "pip", "-v", "install", "-e", str(project), *opts], + stderr=subprocess.STDOUT, + ) + return project, out + + +def _addsitedirs(new_dirs): + """To use this function, it is necessary to insert new_dir in front of sys.path. + The Python process will try to import a ``sitecustomize`` module on startup. + If we manipulate sys.path/PYTHONPATH, we can force it to run our code, + which invokes ``addsitedir`` and ensure ``.pth`` files are loaded. + """ + content = '\n'.join( + ("import site",) + + tuple(f"site.addsitedir({os.fspath(new_dir)!r})" for new_dir in new_dirs) + ) + (new_dirs[0] / "sitecustomize.py").write_text(content, encoding="utf-8") + + +# ---- Assertion Helpers ---- + + +def assert_path(pkg, expected): + # __path__ is not guaranteed to exist, so we have to account for that + if pkg.__path__: + path = next(iter(pkg.__path__), None) + if path: + assert str(Path(path).resolve()) == expected + + +def assert_link_to(file: Path, other: Path) -> None: + if file.is_symlink(): + assert str(file.resolve()) == str(other.resolve()) + else: + file_stat = file.stat() + other_stat = other.stat() + assert file_stat[stat.ST_INO] == other_stat[stat.ST_INO] + assert file_stat[stat.ST_DEV] == other_stat[stat.ST_DEV] + + +def comparable_path(str_with_path: str) -> str: + return str_with_path.lower().replace(os.sep, "/").replace("//", "/") diff --git a/lib/python3.12/site-packages/setuptools/tests/test_egg_info.py b/lib/python3.12/site-packages/setuptools/tests/test_egg_info.py new file mode 100644 index 0000000000000000000000000000000000000000..3653be096f11b77c71f58679d6e4a108903668a5 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_egg_info.py @@ -0,0 +1,1306 @@ +from __future__ import annotations + +import ast +import glob +import os +import re +import stat +import sys +import time +from pathlib import Path +from unittest import mock + +import pytest +from jaraco import path + +from setuptools import errors +from setuptools.command.egg_info import egg_info, manifest_maker, write_entries +from setuptools.dist import Distribution + +from . import contexts, environment +from .textwrap import DALS + + +class Environment(str): + pass + + +@pytest.fixture +def env(): + with contexts.tempdir(prefix='setuptools-test.') as env_dir: + env = Environment(env_dir) + os.chmod(env_dir, stat.S_IRWXU) + subs = 'home', 'lib', 'scripts', 'data', 'egg-base' + env.paths = dict((dirname, os.path.join(env_dir, dirname)) for dirname in subs) + list(map(os.mkdir, env.paths.values())) + path.build({ + env.paths['home']: { + '.pydistutils.cfg': DALS( + """ + [egg_info] + egg-base = {egg-base} + """.format(**env.paths) + ) + } + }) + yield env + + +class TestEggInfo: + setup_script = DALS( + """ + from setuptools import setup + + setup( + name='foo', + py_modules=['hello'], + entry_points={'console_scripts': ['hi = hello.run']}, + zip_safe=False, + ) + """ + ) + + def _create_project(self): + path.build({ + 'setup.py': self.setup_script, + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }) + + @staticmethod + def _extract_mv_version(pkg_info_lines: list[str]) -> tuple[int, int]: + version_str = pkg_info_lines[0].split(' ')[1] + major, minor = map(int, version_str.split('.')[:2]) + return major, minor + + def test_egg_info_save_version_info_setup_empty(self, tmpdir_cwd, env): + """ + When the egg_info section is empty or not present, running + save_version_info should add the settings to the setup.cfg + in a deterministic order. + """ + setup_cfg = os.path.join(env.paths['home'], 'setup.cfg') + dist = Distribution() + ei = egg_info(dist) + ei.initialize_options() + ei.save_version_info(setup_cfg) + + with open(setup_cfg, 'r', encoding="utf-8") as f: + content = f.read() + + assert '[egg_info]' in content + assert 'tag_build =' in content + assert 'tag_date = 0' in content + + expected_order = ( + 'tag_build', + 'tag_date', + ) + + self._validate_content_order(content, expected_order) + + @staticmethod + def _validate_content_order(content, expected): + """ + Assert that the strings in expected appear in content + in order. + """ + pattern = '.*'.join(expected) + flags = re.MULTILINE | re.DOTALL + assert re.search(pattern, content, flags) + + def test_egg_info_save_version_info_setup_defaults(self, tmpdir_cwd, env): + """ + When running save_version_info on an existing setup.cfg + with the 'default' values present from a previous run, + the file should remain unchanged. + """ + setup_cfg = os.path.join(env.paths['home'], 'setup.cfg') + path.build({ + setup_cfg: DALS( + """ + [egg_info] + tag_build = + tag_date = 0 + """ + ), + }) + dist = Distribution() + ei = egg_info(dist) + ei.initialize_options() + ei.save_version_info(setup_cfg) + + with open(setup_cfg, 'r', encoding="utf-8") as f: + content = f.read() + + assert '[egg_info]' in content + assert 'tag_build =' in content + assert 'tag_date = 0' in content + + expected_order = ( + 'tag_build', + 'tag_date', + ) + + self._validate_content_order(content, expected_order) + + def test_expected_files_produced(self, tmpdir_cwd, env): + self._create_project() + + self._run_egg_info_command(tmpdir_cwd, env) + actual = os.listdir('foo.egg-info') + + expected = [ + 'PKG-INFO', + 'SOURCES.txt', + 'dependency_links.txt', + 'entry_points.txt', + 'not-zip-safe', + 'top_level.txt', + ] + assert sorted(actual) == expected + + def test_handling_utime_error(self, tmpdir_cwd, env): + dist = Distribution() + ei = egg_info(dist) + utime_patch = mock.patch('os.utime', side_effect=OSError("TEST")) + mkpath_patch = mock.patch( + 'setuptools.command.egg_info.egg_info.mkpath', return_val=None + ) + + with utime_patch, mkpath_patch: + import distutils.errors + + msg = r"Cannot update time stamp of directory 'None'" + with pytest.raises(distutils.errors.DistutilsFileError, match=msg): + ei.run() + + def test_license_is_a_string(self, tmpdir_cwd, env): + setup_config = DALS( + """ + [metadata] + name=foo + version=0.0.1 + license=file:MIT + """ + ) + + setup_script = DALS( + """ + from setuptools import setup + + setup() + """ + ) + + path.build({ + 'setup.py': setup_script, + 'setup.cfg': setup_config, + }) + + # This command should fail with a ValueError, but because it's + # currently configured to use a subprocess, the actual traceback + # object is lost and we need to parse it from stderr + with pytest.raises(AssertionError) as exc: + self._run_egg_info_command(tmpdir_cwd, env) + + # The only argument to the assertion error should be a traceback + # containing a ValueError + assert 'ValueError' in exc.value.args[0] + + def test_rebuilt(self, tmpdir_cwd, env): + """Ensure timestamps are updated when the command is re-run.""" + self._create_project() + + self._run_egg_info_command(tmpdir_cwd, env) + timestamp_a = os.path.getmtime('foo.egg-info') + + # arbitrary sleep just to handle *really* fast systems + time.sleep(0.001) + + self._run_egg_info_command(tmpdir_cwd, env) + timestamp_b = os.path.getmtime('foo.egg-info') + + assert timestamp_a != timestamp_b + + def test_manifest_template_is_read(self, tmpdir_cwd, env): + self._create_project() + path.build({ + 'MANIFEST.in': DALS( + """ + recursive-include docs *.rst + """ + ), + 'docs': { + 'usage.rst': "Run 'hi'", + }, + }) + self._run_egg_info_command(tmpdir_cwd, env) + egg_info_dir = os.path.join('.', 'foo.egg-info') + sources_txt = os.path.join(egg_info_dir, 'SOURCES.txt') + with open(sources_txt, encoding="utf-8") as f: + assert 'docs/usage.rst' in f.read().split('\n') + + def _setup_script_with_requires(self, requires, use_setup_cfg=False): + setup_script = DALS( + """ + from setuptools import setup + + setup(name='foo', zip_safe=False, %s) + """ + ) % ('' if use_setup_cfg else requires) + setup_config = requires if use_setup_cfg else '' + path.build({ + 'setup.py': setup_script, + 'setup.cfg': setup_config, + }) + + mismatch_marker = f"python_version<'{sys.version_info[0]}'" + # Alternate equivalent syntax. + mismatch_marker_alternate = f'python_version < "{sys.version_info[0]}"' + invalid_marker = "<=>++" + + class RequiresTestHelper: + @staticmethod + def parametrize(*test_list, **format_dict): + idlist = [] + argvalues = [] + for test in test_list: + test_params = test.lstrip().split('\n\n', 3) + name_kwargs = test_params.pop(0).split('\n') + if len(name_kwargs) > 1: + val = name_kwargs[1].strip() + install_cmd_kwargs = ast.literal_eval(val) + else: + install_cmd_kwargs = {} + name = name_kwargs[0].strip() + setup_py_requires, setup_cfg_requires, expected_requires = [ + DALS(a).format(**format_dict) for a in test_params + ] + for id_, requires, use_cfg in ( + (name, setup_py_requires, False), + (name + '_in_setup_cfg', setup_cfg_requires, True), + ): + idlist.append(id_) + marks = () + if requires.startswith('@xfail\n'): + requires = requires[7:] + marks = pytest.mark.xfail + argvalues.append( + pytest.param( + requires, + use_cfg, + expected_requires, + install_cmd_kwargs, + marks=marks, + ) + ) + return pytest.mark.parametrize( + ( + "requires", + "use_setup_cfg", + "expected_requires", + "install_cmd_kwargs", + ), + argvalues, + ids=idlist, + ) + + @RequiresTestHelper.parametrize( + # Format of a test: + # + # id + # install_cmd_kwargs [optional] + # + # requires block (when used in setup.py) + # + # requires block (when used in setup.cfg) + # + # expected contents of requires.txt + """ + install_requires_deterministic + + install_requires=["wheel>=0.5", "pytest"] + + [options] + install_requires = + wheel>=0.5 + pytest + + wheel>=0.5 + pytest + """, + """ + install_requires_ordered + + install_requires=["pytest>=3.0.2,!=10.9999"] + + [options] + install_requires = + pytest>=3.0.2,!=10.9999 + + pytest!=10.9999,>=3.0.2 + """, + """ + install_requires_with_marker + + install_requires=["barbazquux;{mismatch_marker}"], + + [options] + install_requires = + barbazquux; {mismatch_marker} + + [:{mismatch_marker_alternate}] + barbazquux + """, + """ + install_requires_with_extra + {'cmd': ['egg_info']} + + install_requires=["barbazquux [test]"], + + [options] + install_requires = + barbazquux [test] + + barbazquux[test] + """, + """ + install_requires_with_extra_and_marker + + install_requires=["barbazquux [test]; {mismatch_marker}"], + + [options] + install_requires = + barbazquux [test]; {mismatch_marker} + + [:{mismatch_marker_alternate}] + barbazquux[test] + """, + """ + setup_requires_with_markers + + setup_requires=["barbazquux;{mismatch_marker}"], + + [options] + setup_requires = + barbazquux; {mismatch_marker} + + """, + """ + extras_require_with_extra + {'cmd': ['egg_info']} + + extras_require={{"extra": ["barbazquux [test]"]}}, + + [options.extras_require] + extra = barbazquux [test] + + [extra] + barbazquux[test] + """, + """ + extras_require_with_extra_and_marker_in_req + + extras_require={{"extra": ["barbazquux [test]; {mismatch_marker}"]}}, + + [options.extras_require] + extra = + barbazquux [test]; {mismatch_marker} + + [extra] + + [extra:{mismatch_marker_alternate}] + barbazquux[test] + """, + # FIXME: ConfigParser does not allow : in key names! + """ + extras_require_with_marker + + extras_require={{":{mismatch_marker}": ["barbazquux"]}}, + + @xfail + [options.extras_require] + :{mismatch_marker} = barbazquux + + [:{mismatch_marker}] + barbazquux + """, + """ + extras_require_with_marker_in_req + + extras_require={{"extra": ["barbazquux; {mismatch_marker}"]}}, + + [options.extras_require] + extra = + barbazquux; {mismatch_marker} + + [extra] + + [extra:{mismatch_marker_alternate}] + barbazquux + """, + """ + extras_require_with_empty_section + + extras_require={{"empty": []}}, + + [options.extras_require] + empty = + + [empty] + """, + # Format arguments. + invalid_marker=invalid_marker, + mismatch_marker=mismatch_marker, + mismatch_marker_alternate=mismatch_marker_alternate, + ) + def test_requires( + self, + tmpdir_cwd, + env, + requires, + use_setup_cfg, + expected_requires, + install_cmd_kwargs, + ): + self._setup_script_with_requires(requires, use_setup_cfg) + self._run_egg_info_command(tmpdir_cwd, env, **install_cmd_kwargs) + egg_info_dir = os.path.join('.', 'foo.egg-info') + requires_txt = os.path.join(egg_info_dir, 'requires.txt') + if os.path.exists(requires_txt): + with open(requires_txt, encoding="utf-8") as fp: + install_requires = fp.read() + else: + install_requires = '' + assert install_requires.lstrip() == expected_requires + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_install_requires_unordered_disallowed(self, tmpdir_cwd, env): + """ + Packages that pass unordered install_requires sequences + should be rejected as they produce non-deterministic + builds. See #458. + """ + req = 'install_requires={"fake-factory==0.5.2", "pytz"}' + self._setup_script_with_requires(req) + with pytest.raises(AssertionError): + self._run_egg_info_command(tmpdir_cwd, env) + + def test_extras_require_with_invalid_marker(self, tmpdir_cwd, env): + tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},' + req = tmpl.format(marker=self.invalid_marker) + self._setup_script_with_requires(req) + with pytest.raises(AssertionError): + self._run_egg_info_command(tmpdir_cwd, env) + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_extras_require_with_invalid_marker_in_req(self, tmpdir_cwd, env): + tmpl = 'extras_require={{"extra": ["barbazquux; {marker}"]}},' + req = tmpl.format(marker=self.invalid_marker) + self._setup_script_with_requires(req) + with pytest.raises(AssertionError): + self._run_egg_info_command(tmpdir_cwd, env) + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_provides_extra(self, tmpdir_cwd, env): + self._setup_script_with_requires('extras_require={"foobar": ["barbazquux"]},') + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + assert 'Provides-Extra: foobar' in pkg_info_lines + assert 'Metadata-Version: 2.4' in pkg_info_lines + + def test_doesnt_provides_extra(self, tmpdir_cwd, env): + self._setup_script_with_requires( + """install_requires=["spam ; python_version<'3.6'"]""" + ) + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_text = fp.read() + assert 'Provides-Extra:' not in pkg_info_text + + @pytest.mark.parametrize( + ('files', 'license_in_sources'), + [ + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE + """ + ), + 'LICENSE': "Test license", + }, + True, + ), # with license + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = INVALID_LICENSE + """ + ), + 'LICENSE': "Test license", + }, + False, + ), # with an invalid license + ( + { + 'setup.cfg': DALS( + """ + """ + ), + 'LICENSE': "Test license", + }, + True, + ), # no license_file attribute, LICENSE auto-included + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE + """ + ), + 'MANIFEST.in': "exclude LICENSE", + 'LICENSE': "Test license", + }, + True, + ), # manifest is overwritten by license_file + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICEN[CS]E* + """ + ), + 'LICENSE': "Test license", + }, + True, + id="glob_pattern", + ), + ], + ) + def test_setup_cfg_license_file(self, tmpdir_cwd, env, files, license_in_sources): + self._create_project() + path.build(files) + + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + + sources_text = Path(egg_info_dir, "SOURCES.txt").read_text(encoding="utf-8") + + if license_in_sources: + assert 'LICENSE' in sources_text + else: + assert 'LICENSE' not in sources_text + # for invalid license test + assert 'INVALID_LICENSE' not in sources_text + + @pytest.mark.parametrize( + ('files', 'incl_licenses', 'excl_licenses'), + [ + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE-ABC + LICENSE-XYZ + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + ['LICENSE-ABC', 'LICENSE-XYZ'], + [], + ), # with licenses + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = LICENSE-ABC, LICENSE-XYZ + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + ['LICENSE-ABC', 'LICENSE-XYZ'], + [], + ), # with commas + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE-ABC + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + ['LICENSE-ABC'], + ['LICENSE-XYZ'], + ), # with one license + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + [], + ['LICENSE-ABC', 'LICENSE-XYZ'], + ), # empty + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = LICENSE-XYZ + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + ['LICENSE-XYZ'], + ['LICENSE-ABC'], + ), # on same line + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE-ABC + INVALID_LICENSE + """ + ), + 'LICENSE-ABC': "Test license", + }, + ['LICENSE-ABC'], + ['INVALID_LICENSE'], + ), # with an invalid license + ( + { + 'setup.cfg': DALS( + """ + """ + ), + 'LICENSE': "Test license", + }, + ['LICENSE'], + [], + ), # no license_files attribute, LICENSE auto-included + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = LICENSE + """ + ), + 'MANIFEST.in': "exclude LICENSE", + 'LICENSE': "Test license", + }, + ['LICENSE'], + [], + ), # manifest is overwritten by license_files + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE-ABC + LICENSE-XYZ + """ + ), + 'MANIFEST.in': "exclude LICENSE-XYZ", + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + # manifest is overwritten by license_files + }, + ['LICENSE-ABC', 'LICENSE-XYZ'], + [], + ), + pytest.param( + { + 'setup.cfg': "", + 'LICENSE-ABC': "ABC license", + 'COPYING-ABC': "ABC copying", + 'NOTICE-ABC': "ABC notice", + 'AUTHORS-ABC': "ABC authors", + 'LICENCE-XYZ': "XYZ license", + 'LICENSE': "License", + 'INVALID-LICENSE': "Invalid license", + }, + [ + 'LICENSE-ABC', + 'COPYING-ABC', + 'NOTICE-ABC', + 'AUTHORS-ABC', + 'LICENCE-XYZ', + 'LICENSE', + ], + ['INVALID-LICENSE'], + # ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') + id="default_glob_patterns", + ), + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE* + """ + ), + 'LICENSE-ABC': "ABC license", + 'NOTICE-XYZ': "XYZ notice", + }, + ['LICENSE-ABC'], + ['NOTICE-XYZ'], + id="no_default_glob_patterns", + ), + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE-ABC + LICENSE* + """ + ), + 'LICENSE-ABC': "ABC license", + }, + ['LICENSE-ABC'], + [], + id="files_only_added_once", + ), + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = **/LICENSE + """ + ), + 'LICENSE': "ABC license", + 'LICENSE-OTHER': "Don't include", + 'vendor': {'LICENSE': "Vendor license"}, + }, + ['LICENSE', 'vendor/LICENSE'], + ['LICENSE-OTHER'], + id="recursive_glob", + ), + ], + ) + def test_setup_cfg_license_files( + self, tmpdir_cwd, env, files, incl_licenses, excl_licenses + ): + self._create_project() + path.build(files) + + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + + sources_text = Path(egg_info_dir, "SOURCES.txt").read_text(encoding="utf-8") + sources_lines = [line.strip() for line in sources_text.splitlines()] + + for lf in incl_licenses: + assert sources_lines.count(lf) == 1 + + for lf in excl_licenses: + assert sources_lines.count(lf) == 0 + + @pytest.mark.parametrize( + ('files', 'incl_licenses', 'excl_licenses'), + [ + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = + license_files = + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + [], + ['LICENSE-ABC', 'LICENSE-XYZ'], + ), # both empty + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = + LICENSE-ABC + LICENSE-XYZ + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + # license_file is still singular + }, + [], + ['LICENSE-ABC', 'LICENSE-XYZ'], + ), + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE-ABC + license_files = + LICENSE-XYZ + LICENSE-PQR + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-PQR': "PQR license", + 'LICENSE-XYZ': "XYZ license", + }, + ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], + [], + ), # combined + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE-ABC + license_files = + LICENSE-ABC + LICENSE-XYZ + LICENSE-PQR + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-PQR': "PQR license", + 'LICENSE-XYZ': "XYZ license", + # duplicate license + }, + ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], + [], + ), + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE-ABC + license_files = + LICENSE-XYZ + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-PQR': "PQR license", + 'LICENSE-XYZ': "XYZ license", + # combined subset + }, + ['LICENSE-ABC', 'LICENSE-XYZ'], + ['LICENSE-PQR'], + ), + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE-ABC + license_files = + LICENSE-XYZ + LICENSE-PQR + """ + ), + 'LICENSE-PQR': "Test license", + # with invalid licenses + }, + ['LICENSE-PQR'], + ['LICENSE-ABC', 'LICENSE-XYZ'], + ), + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE-ABC + license_files = + LICENSE-PQR + LICENSE-XYZ + """ + ), + 'MANIFEST.in': "exclude LICENSE-ABC\nexclude LICENSE-PQR", + 'LICENSE-ABC': "ABC license", + 'LICENSE-PQR': "PQR license", + 'LICENSE-XYZ': "XYZ license", + # manifest is overwritten + }, + ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], + [], + ), + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE* + """ + ), + 'LICENSE-ABC': "ABC license", + 'NOTICE-XYZ': "XYZ notice", + }, + ['LICENSE-ABC'], + ['NOTICE-XYZ'], + id="no_default_glob_patterns", + ), + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE* + license_files = + NOTICE* + """ + ), + 'LICENSE-ABC': "ABC license", + 'NOTICE-ABC': "ABC notice", + 'AUTHORS-ABC': "ABC authors", + }, + ['LICENSE-ABC', 'NOTICE-ABC'], + ['AUTHORS-ABC'], + id="combined_glob_patterrns", + ), + ], + ) + def test_setup_cfg_license_file_license_files( + self, tmpdir_cwd, env, files, incl_licenses, excl_licenses + ): + self._create_project() + path.build(files) + + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + + sources_text = Path(egg_info_dir, "SOURCES.txt").read_text(encoding="utf-8") + sources_lines = [line.strip() for line in sources_text.splitlines()] + + for lf in incl_licenses: + assert sources_lines.count(lf) == 1 + + for lf in excl_licenses: + assert sources_lines.count(lf) == 0 + + def test_license_file_attr_pkg_info(self, tmpdir_cwd, env): + """All matched license files should have a corresponding License-File.""" + self._create_project() + path.build({ + "setup.cfg": DALS( + """ + [metadata] + license_files = + NOTICE* + LICENSE* + **/LICENSE + """ + ), + "LICENSE-ABC": "ABC license", + "LICENSE-XYZ": "XYZ license", + "NOTICE": "included", + "IGNORE": "not include", + "vendor": {'LICENSE': "Vendor license"}, + }) + + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + license_file_lines = [ + line for line in pkg_info_lines if line.startswith('License-File:') + ] + + # Only 'NOTICE', LICENSE-ABC', and 'LICENSE-XYZ' should have been matched + # Also assert that order from license_files is keeped + assert len(license_file_lines) == 4 + assert "License-File: NOTICE" == license_file_lines[0] + assert "License-File: LICENSE-ABC" in license_file_lines[1:] + assert "License-File: LICENSE-XYZ" in license_file_lines[1:] + assert "License-File: vendor/LICENSE" in license_file_lines[3] + + def test_metadata_version(self, tmpdir_cwd, env): + """Make sure latest metadata version is used by default.""" + self._setup_script_with_requires("") + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + # Update metadata version if changed + assert self._extract_mv_version(pkg_info_lines) == (2, 4) + + def test_long_description_content_type(self, tmpdir_cwd, env): + # Test that specifying a `long_description_content_type` keyword arg to + # the `setup` function results in writing a `Description-Content-Type` + # line to the `PKG-INFO` file in the `.egg-info` + # directory. + # `Description-Content-Type` is described at + # https://github.com/pypa/python-packaging-user-guide/pull/258 + + self._setup_script_with_requires( + """long_description_content_type='text/markdown',""" + ) + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + expected_line = 'Description-Content-Type: text/markdown' + assert expected_line in pkg_info_lines + assert 'Metadata-Version: 2.4' in pkg_info_lines + + def test_long_description(self, tmpdir_cwd, env): + # Test that specifying `long_description` and `long_description_content_type` + # keyword args to the `setup` function results in writing + # the description in the message payload of the `PKG-INFO` file + # in the `.egg-info` directory. + self._setup_script_with_requires( + "long_description='This is a long description\\nover multiple lines'," + "long_description_content_type='text/markdown'," + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + assert 'Metadata-Version: 2.4' in pkg_info_lines + assert '' == pkg_info_lines[-1] # last line should be empty + long_desc_lines = pkg_info_lines[pkg_info_lines.index('') :] + assert 'This is a long description' in long_desc_lines + assert 'over multiple lines' in long_desc_lines + + def test_project_urls(self, tmpdir_cwd, env): + # Test that specifying a `project_urls` dict to the `setup` + # function results in writing multiple `Project-URL` lines to + # the `PKG-INFO` file in the `.egg-info` + # directory. + # `Project-URL` is described at https://packaging.python.org + # /specifications/core-metadata/#project-url-multiple-use + + self._setup_script_with_requires( + """project_urls={ + 'Link One': 'https://example.com/one/', + 'Link Two': 'https://example.com/two/', + },""" + ) + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + expected_line = 'Project-URL: Link One, https://example.com/one/' + assert expected_line in pkg_info_lines + expected_line = 'Project-URL: Link Two, https://example.com/two/' + assert expected_line in pkg_info_lines + assert self._extract_mv_version(pkg_info_lines) >= (1, 2) + + def test_license(self, tmpdir_cwd, env): + """Test single line license.""" + self._setup_script_with_requires("license='MIT',") + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + assert 'License: MIT' in pkg_info_lines + + def test_license_escape(self, tmpdir_cwd, env): + """Test license is escaped correctly if longer than one line.""" + self._setup_script_with_requires( + "license='This is a long license text \\nover multiple lines'," + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + + assert 'License: This is a long license text ' in pkg_info_lines + assert ' over multiple lines' in pkg_info_lines + assert 'text \n over multiple' in '\n'.join(pkg_info_lines) + + def test_python_requires_egg_info(self, tmpdir_cwd, env): + self._setup_script_with_requires("""python_requires='>=2.7.12',""") + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + assert 'Requires-Python: >=2.7.12' in pkg_info_lines + assert self._extract_mv_version(pkg_info_lines) >= (1, 2) + + def test_manifest_maker_warning_suppression(self): + fixtures = [ + "standard file not found: should have one of foo.py, bar.py", + "standard file 'setup.py' not found", + ] + + for msg in fixtures: + assert manifest_maker._should_suppress_warning(msg) + + def test_egg_info_includes_setup_py(self, tmpdir_cwd): + self._create_project() + dist = Distribution({"name": "foo", "version": "0.0.1"}) + dist.script_name = "non_setup.py" + egg_info_instance = egg_info(dist) + egg_info_instance.finalize_options() + egg_info_instance.run() + + assert 'setup.py' in egg_info_instance.filelist.files + + with open(egg_info_instance.egg_info + "/SOURCES.txt", encoding="utf-8") as f: + sources = f.read().split('\n') + assert 'setup.py' in sources + + def _run_egg_info_command(self, tmpdir_cwd, env, cmd=None, output=None): + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + if cmd is None: + cmd = [ + 'egg_info', + ] + code, data = environment.run_setup_py( + cmd=cmd, + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + assert not code, data + + if output: + assert output in data + + def test_egg_info_tag_only_once(self, tmpdir_cwd, env): + self._create_project() + path.build({ + 'setup.cfg': DALS( + """ + [egg_info] + tag_build = dev + tag_date = 0 + tag_svn_revision = 0 + """ + ), + }) + self._run_egg_info_command(tmpdir_cwd, env) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + assert 'Version: 0.0.0.dev0' in pkg_info_lines + + +class TestWriteEntries: + def test_invalid_entry_point(self, tmpdir_cwd, env): + dist = Distribution({"name": "foo", "version": "0.0.1"}) + dist.entry_points = {"foo": "foo = invalid-identifier:foo"} + cmd = dist.get_command_obj("egg_info") + expected_msg = r"(Invalid object reference|Problems to parse)" + with pytest.raises((errors.OptionError, ValueError), match=expected_msg) as ex: + write_entries(cmd, "entry_points", "entry_points.txt") + assert "ensure entry-point follows the spec" in ex.value.args[0] + assert "invalid-identifier" in str(ex.value) + + def test_valid_entry_point(self, tmpdir_cwd, env): + dist = Distribution({"name": "foo", "version": "0.0.1"}) + dist.entry_points = { + "abc": "foo = bar:baz", + "def": ["faa = bor:boz"], + } + cmd = dist.get_command_obj("egg_info") + write_entries(cmd, "entry_points", "entry_points.txt") + content = Path("entry_points.txt").read_text(encoding="utf-8") + assert "[abc]\nfoo = bar:baz\n" in content + assert "[def]\nfaa = bor:boz\n" in content diff --git a/lib/python3.12/site-packages/setuptools/tests/test_extern.py b/lib/python3.12/site-packages/setuptools/tests/test_extern.py new file mode 100644 index 0000000000000000000000000000000000000000..d7eb3c62c190dacdd4a054d2934962be2f4ee860 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_extern.py @@ -0,0 +1,15 @@ +import importlib +import pickle + +import packaging + +from setuptools import Distribution + + +def test_reimport_extern(): + packaging2 = importlib.import_module(packaging.__name__) + assert packaging is packaging2 + + +def test_distribution_picklable(): + pickle.loads(pickle.dumps(Distribution())) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_find_packages.py b/lib/python3.12/site-packages/setuptools/tests/test_find_packages.py new file mode 100644 index 0000000000000000000000000000000000000000..9fd9f8f6637d13cb898fcd446a6b090323d02014 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_find_packages.py @@ -0,0 +1,218 @@ +"""Tests for automatic package discovery""" + +import os +import shutil +import tempfile + +import pytest + +from setuptools import find_namespace_packages, find_packages +from setuptools.discovery import FlatLayoutPackageFinder + +from .compat.py39 import os_helper + + +class TestFindPackages: + def setup_method(self, method): + self.dist_dir = tempfile.mkdtemp() + self._make_pkg_structure() + + def teardown_method(self, method): + shutil.rmtree(self.dist_dir) + + def _make_pkg_structure(self): + """Make basic package structure. + + dist/ + docs/ + conf.py + pkg/ + __pycache__/ + nspkg/ + mod.py + subpkg/ + assets/ + asset + __init__.py + setup.py + + """ + self.docs_dir = self._mkdir('docs', self.dist_dir) + self._touch('conf.py', self.docs_dir) + self.pkg_dir = self._mkdir('pkg', self.dist_dir) + self._mkdir('__pycache__', self.pkg_dir) + self.ns_pkg_dir = self._mkdir('nspkg', self.pkg_dir) + self._touch('mod.py', self.ns_pkg_dir) + self.sub_pkg_dir = self._mkdir('subpkg', self.pkg_dir) + self.asset_dir = self._mkdir('assets', self.sub_pkg_dir) + self._touch('asset', self.asset_dir) + self._touch('__init__.py', self.sub_pkg_dir) + self._touch('setup.py', self.dist_dir) + + def _mkdir(self, path, parent_dir=None): + if parent_dir: + path = os.path.join(parent_dir, path) + os.mkdir(path) + return path + + def _touch(self, path, dir_=None): + if dir_: + path = os.path.join(dir_, path) + open(path, 'wb').close() + return path + + def test_regular_package(self): + self._touch('__init__.py', self.pkg_dir) + packages = find_packages(self.dist_dir) + assert packages == ['pkg', 'pkg.subpkg'] + + def test_exclude(self): + self._touch('__init__.py', self.pkg_dir) + packages = find_packages(self.dist_dir, exclude=('pkg.*',)) + assert packages == ['pkg'] + + def test_exclude_recursive(self): + """ + Excluding a parent package should not exclude child packages as well. + """ + self._touch('__init__.py', self.pkg_dir) + self._touch('__init__.py', self.sub_pkg_dir) + packages = find_packages(self.dist_dir, exclude=('pkg',)) + assert packages == ['pkg.subpkg'] + + def test_include_excludes_other(self): + """ + If include is specified, other packages should be excluded. + """ + self._touch('__init__.py', self.pkg_dir) + alt_dir = self._mkdir('other_pkg', self.dist_dir) + self._touch('__init__.py', alt_dir) + packages = find_packages(self.dist_dir, include=['other_pkg']) + assert packages == ['other_pkg'] + + def test_dir_with_dot_is_skipped(self): + shutil.rmtree(os.path.join(self.dist_dir, 'pkg/subpkg/assets')) + data_dir = self._mkdir('some.data', self.pkg_dir) + self._touch('__init__.py', data_dir) + self._touch('file.dat', data_dir) + packages = find_packages(self.dist_dir) + assert 'pkg.some.data' not in packages + + def test_dir_with_packages_in_subdir_is_excluded(self): + """ + Ensure that a package in a non-package such as build/pkg/__init__.py + is excluded. + """ + build_dir = self._mkdir('build', self.dist_dir) + build_pkg_dir = self._mkdir('pkg', build_dir) + self._touch('__init__.py', build_pkg_dir) + packages = find_packages(self.dist_dir) + assert 'build.pkg' not in packages + + @pytest.mark.skipif(not os_helper.can_symlink(), reason='Symlink support required') + def test_symlinked_packages_are_included(self): + """ + A symbolically-linked directory should be treated like any other + directory when matched as a package. + + Create a link from lpkg -> pkg. + """ + self._touch('__init__.py', self.pkg_dir) + linked_pkg = os.path.join(self.dist_dir, 'lpkg') + os.symlink('pkg', linked_pkg) + assert os.path.isdir(linked_pkg) + packages = find_packages(self.dist_dir) + assert 'lpkg' in packages + + def _assert_packages(self, actual, expected): + assert set(actual) == set(expected) + + def test_pep420_ns_package(self): + packages = find_namespace_packages( + self.dist_dir, include=['pkg*'], exclude=['pkg.subpkg.assets'] + ) + self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) + + def test_pep420_ns_package_no_includes(self): + packages = find_namespace_packages(self.dist_dir, exclude=['pkg.subpkg.assets']) + self._assert_packages(packages, ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg']) + + def test_pep420_ns_package_no_includes_or_excludes(self): + packages = find_namespace_packages(self.dist_dir) + expected = ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg', 'pkg.subpkg.assets'] + self._assert_packages(packages, expected) + + def test_regular_package_with_nested_pep420_ns_packages(self): + self._touch('__init__.py', self.pkg_dir) + packages = find_namespace_packages( + self.dist_dir, exclude=['docs', 'pkg.subpkg.assets'] + ) + self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) + + def test_pep420_ns_package_no_non_package_dirs(self): + shutil.rmtree(self.docs_dir) + shutil.rmtree(os.path.join(self.dist_dir, 'pkg/subpkg/assets')) + packages = find_namespace_packages(self.dist_dir) + self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) + + +class TestFlatLayoutPackageFinder: + EXAMPLES = { + "hidden-folders": ( + [".pkg/__init__.py", "pkg/__init__.py", "pkg/nested/file.txt"], + ["pkg", "pkg.nested"], + ), + "private-packages": ( + ["_pkg/__init__.py", "pkg/_private/__init__.py"], + ["pkg", "pkg._private"], + ), + "invalid-name": ( + ["invalid-pkg/__init__.py", "other.pkg/__init__.py", "yet,another/file.py"], + [], + ), + "docs": (["pkg/__init__.py", "docs/conf.py", "docs/readme.rst"], ["pkg"]), + "tests": ( + ["pkg/__init__.py", "tests/test_pkg.py", "tests/__init__.py"], + ["pkg"], + ), + "examples": ( + [ + "pkg/__init__.py", + "examples/__init__.py", + "examples/file.py", + "example/other_file.py", + # Sub-packages should always be fine + "pkg/example/__init__.py", + "pkg/examples/__init__.py", + ], + ["pkg", "pkg.examples", "pkg.example"], + ), + "tool-specific": ( + [ + "htmlcov/index.html", + "pkg/__init__.py", + "tasks/__init__.py", + "tasks/subpackage/__init__.py", + "fabfile/__init__.py", + "fabfile/subpackage/__init__.py", + # Sub-packages should always be fine + "pkg/tasks/__init__.py", + "pkg/fabfile/__init__.py", + ], + ["pkg", "pkg.tasks", "pkg.fabfile"], + ), + } + + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_unwanted_directories_not_included(self, tmp_path, example): + files, expected_packages = self.EXAMPLES[example] + ensure_files(tmp_path, files) + found_packages = FlatLayoutPackageFinder.find(str(tmp_path)) + assert set(found_packages) == set(expected_packages) + + +def ensure_files(root_path, files): + for file in files: + path = root_path / file + path.parent.mkdir(parents=True, exist_ok=True) + path.touch() diff --git a/lib/python3.12/site-packages/setuptools/tests/test_find_py_modules.py b/lib/python3.12/site-packages/setuptools/tests/test_find_py_modules.py new file mode 100644 index 0000000000000000000000000000000000000000..8034b544294e5d30274bac82f24f93613120a0d4 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_find_py_modules.py @@ -0,0 +1,73 @@ +"""Tests for automatic discovery of modules""" + +import os + +import pytest + +from setuptools.discovery import FlatLayoutModuleFinder, ModuleFinder + +from .compat.py39 import os_helper +from .test_find_packages import ensure_files + + +class TestModuleFinder: + def find(self, path, *args, **kwargs): + return set(ModuleFinder.find(str(path), *args, **kwargs)) + + EXAMPLES = { + # circumstance: (files, kwargs, expected_modules) + "simple_folder": ( + ["file.py", "other.py"], + {}, # kwargs + ["file", "other"], + ), + "exclude": ( + ["file.py", "other.py"], + {"exclude": ["f*"]}, + ["other"], + ), + "include": ( + ["file.py", "fole.py", "other.py"], + {"include": ["f*"], "exclude": ["fo*"]}, + ["file"], + ), + "invalid-name": (["my-file.py", "other.file.py"], {}, []), + } + + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_finder(self, tmp_path, example): + files, kwargs, expected_modules = self.EXAMPLES[example] + ensure_files(tmp_path, files) + assert self.find(tmp_path, **kwargs) == set(expected_modules) + + @pytest.mark.skipif(not os_helper.can_symlink(), reason='Symlink support required') + def test_symlinked_packages_are_included(self, tmp_path): + src = "_myfiles/file.py" + ensure_files(tmp_path, [src]) + os.symlink(tmp_path / src, tmp_path / "link.py") + assert self.find(tmp_path) == {"link"} + + +class TestFlatLayoutModuleFinder: + def find(self, path, *args, **kwargs): + return set(FlatLayoutModuleFinder.find(str(path))) + + EXAMPLES = { + # circumstance: (files, expected_modules) + "hidden-files": ([".module.py"], []), + "private-modules": (["_module.py"], []), + "common-names": ( + ["setup.py", "conftest.py", "test.py", "tests.py", "example.py", "mod.py"], + ["mod"], + ), + "tool-specific": ( + ["tasks.py", "fabfile.py", "noxfile.py", "dodo.py", "manage.py", "mod.py"], + ["mod"], + ), + } + + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_unwanted_files_not_included(self, tmp_path, example): + files, expected_modules = self.EXAMPLES[example] + ensure_files(tmp_path, files) + assert self.find(tmp_path) == set(expected_modules) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_glob.py b/lib/python3.12/site-packages/setuptools/tests/test_glob.py new file mode 100644 index 0000000000000000000000000000000000000000..8d225a44610163c7d56d65b07c06f0f598ccfe84 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_glob.py @@ -0,0 +1,45 @@ +import pytest +from jaraco import path + +from setuptools.glob import glob + + +@pytest.mark.parametrize( + ('tree', 'pattern', 'matches'), + ( + ('', b'', []), + ('', '', []), + ( + """ + appveyor.yml + CHANGES.rst + LICENSE + MANIFEST.in + pyproject.toml + README.rst + setup.cfg + setup.py + """, + '*.rst', + ('CHANGES.rst', 'README.rst'), + ), + ( + """ + appveyor.yml + CHANGES.rst + LICENSE + MANIFEST.in + pyproject.toml + README.rst + setup.cfg + setup.py + """, + b'*.rst', + (b'CHANGES.rst', b'README.rst'), + ), + ), +) +def test_glob(monkeypatch, tmpdir, tree, pattern, matches): + monkeypatch.chdir(tmpdir) + path.build({name: '' for name in tree.split()}) + assert list(sorted(glob(pattern))) == list(sorted(matches)) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_install_scripts.py b/lib/python3.12/site-packages/setuptools/tests/test_install_scripts.py new file mode 100644 index 0000000000000000000000000000000000000000..e62a6b7f318df2da0cf29e53c41f74e5525e78ac --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_install_scripts.py @@ -0,0 +1,89 @@ +"""install_scripts tests""" + +import sys + +import pytest + +from setuptools.command.install_scripts import install_scripts +from setuptools.dist import Distribution + +from . import contexts + + +class TestInstallScripts: + settings = dict( + name='foo', + entry_points={'console_scripts': ['foo=foo:foo']}, + version='0.0', + ) + unix_exe = '/usr/dummy-test-path/local/bin/python' + unix_spaces_exe = '/usr/bin/env dummy-test-python' + win32_exe = 'C:\\Dummy Test Path\\Program Files\\Python 3.6\\python.exe' + + def _run_install_scripts(self, install_dir, executable=None): + dist = Distribution(self.settings) + dist.script_name = 'setup.py' + cmd = install_scripts(dist) + cmd.install_dir = install_dir + if executable is not None: + bs = cmd.get_finalized_command('build_scripts') + bs.executable = executable + cmd.ensure_finalized() + with contexts.quiet(): + cmd.run() + + @pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only') + def test_sys_executable_escaping_unix(self, tmpdir, monkeypatch): + """ + Ensure that shebang is not quoted on Unix when getting the Python exe + from sys.executable. + """ + expected = f'#!{self.unix_exe}\n' + monkeypatch.setattr('sys.executable', self.unix_exe) + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir)) + with open(str(tmpdir.join('foo')), 'r', encoding="utf-8") as f: + actual = f.readline() + assert actual == expected + + @pytest.mark.skipif(sys.platform != 'win32', reason='Windows only') + def test_sys_executable_escaping_win32(self, tmpdir, monkeypatch): + """ + Ensure that shebang is quoted on Windows when getting the Python exe + from sys.executable and it contains a space. + """ + expected = f'#!"{self.win32_exe}"\n' + monkeypatch.setattr('sys.executable', self.win32_exe) + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir)) + with open(str(tmpdir.join('foo-script.py')), 'r', encoding="utf-8") as f: + actual = f.readline() + assert actual == expected + + @pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only') + def test_executable_with_spaces_escaping_unix(self, tmpdir): + """ + Ensure that shebang on Unix is not quoted, even when + a value with spaces + is specified using --executable. + """ + expected = f'#!{self.unix_spaces_exe}\n' + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir), self.unix_spaces_exe) + with open(str(tmpdir.join('foo')), 'r', encoding="utf-8") as f: + actual = f.readline() + assert actual == expected + + @pytest.mark.skipif(sys.platform != 'win32', reason='Windows only') + def test_executable_arg_escaping_win32(self, tmpdir): + """ + Ensure that shebang on Windows is quoted when + getting a path with spaces + from --executable, that is itself properly quoted. + """ + expected = f'#!"{self.win32_exe}"\n' + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir), '"' + self.win32_exe + '"') + with open(str(tmpdir.join('foo-script.py')), 'r', encoding="utf-8") as f: + actual = f.readline() + assert actual == expected diff --git a/lib/python3.12/site-packages/setuptools/tests/test_logging.py b/lib/python3.12/site-packages/setuptools/tests/test_logging.py new file mode 100644 index 0000000000000000000000000000000000000000..ea58001e93d8e4bcbd50bfd49303324d71858d16 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_logging.py @@ -0,0 +1,76 @@ +import functools +import inspect +import logging +import sys + +import pytest + +IS_PYPY = '__pypy__' in sys.builtin_module_names + + +setup_py = """\ +from setuptools import setup + +setup( + name="test_logging", + version="0.0" +) +""" + + +@pytest.mark.parametrize( + ('flag', 'expected_level'), [("--dry-run", "INFO"), ("--verbose", "DEBUG")] +) +def test_verbosity_level(tmp_path, monkeypatch, flag, expected_level): + """Make sure the correct verbosity level is set (issue #3038)""" + import setuptools # noqa: F401 # import setuptools to monkeypatch distutils + + import distutils # <- load distutils after all the patches take place + + logger = logging.Logger(__name__) + monkeypatch.setattr(logging, "root", logger) + unset_log_level = logger.getEffectiveLevel() + assert logging.getLevelName(unset_log_level) == "NOTSET" + + setup_script = tmp_path / "setup.py" + setup_script.write_text(setup_py, encoding="utf-8") + dist = distutils.core.run_setup(setup_script, stop_after="init") + dist.script_args = [flag, "sdist"] + dist.parse_command_line() # <- where the log level is set + log_level = logger.getEffectiveLevel() + log_level_name = logging.getLevelName(log_level) + assert log_level_name == expected_level + + +def flaky_on_pypy(func): + @functools.wraps(func) + def _func(): + try: + func() + except AssertionError: # pragma: no cover + if IS_PYPY: + msg = "Flaky monkeypatch on PyPy (#4124)" + pytest.xfail(f"{msg}. Original discussion in #3707, #3709.") + raise + + return _func + + +@flaky_on_pypy +def test_patching_does_not_cause_problems(): + # Ensure `dist.log` is only patched if necessary + + import _distutils_hack + + import setuptools.logging + + from distutils import dist + + setuptools.logging.configure() + + if _distutils_hack.enabled(): + # Modern logging infra, no problematic patching. + assert dist.__file__ is None or "setuptools" in dist.__file__ + assert isinstance(dist.log, logging.Logger) + else: + assert inspect.ismodule(dist.log) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_manifest.py b/lib/python3.12/site-packages/setuptools/tests/test_manifest.py new file mode 100644 index 0000000000000000000000000000000000000000..903a528db0cc2bba27bbcef24aa1c59dc4156528 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_manifest.py @@ -0,0 +1,622 @@ +"""sdist tests""" + +from __future__ import annotations + +import contextlib +import io +import itertools +import logging +import os +import shutil +import sys +import tempfile + +import pytest + +from setuptools.command.egg_info import FileList, egg_info, translate_pattern +from setuptools.dist import Distribution +from setuptools.tests.textwrap import DALS + +from distutils import log +from distutils.errors import DistutilsTemplateError + +IS_PYPY = '__pypy__' in sys.builtin_module_names + + +def make_local_path(s): + """Converts '/' in a string to os.sep""" + return s.replace('/', os.sep) + + +SETUP_ATTRS = { + 'name': 'app', + 'version': '0.0', + 'packages': ['app'], +} + +SETUP_PY = f"""\ +from setuptools import setup + +setup(**{SETUP_ATTRS!r}) +""" + + +@contextlib.contextmanager +def quiet(): + old_stdout, old_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = io.StringIO(), io.StringIO() + try: + yield + finally: + sys.stdout, sys.stderr = old_stdout, old_stderr + + +def touch(filename): + open(filename, 'wb').close() + + +# The set of files always in the manifest, including all files in the +# .egg-info directory +default_files = frozenset( + map( + make_local_path, + [ + 'README.rst', + 'MANIFEST.in', + 'setup.py', + 'app.egg-info/PKG-INFO', + 'app.egg-info/SOURCES.txt', + 'app.egg-info/dependency_links.txt', + 'app.egg-info/top_level.txt', + 'app/__init__.py', + ], + ) +) + + +translate_specs: list[tuple[str, list[str], list[str]]] = [ + ('foo', ['foo'], ['bar', 'foobar']), + ('foo/bar', ['foo/bar'], ['foo/bar/baz', './foo/bar', 'foo']), + # Glob matching + ('*.txt', ['foo.txt', 'bar.txt'], ['foo/foo.txt']), + ('dir/*.txt', ['dir/foo.txt', 'dir/bar.txt', 'dir/.txt'], ['notdir/foo.txt']), + ('*/*.py', ['bin/start.py'], []), + ('docs/page-?.txt', ['docs/page-9.txt'], ['docs/page-10.txt']), + # Globstars change what they mean depending upon where they are + ( + 'foo/**/bar', + ['foo/bing/bar', 'foo/bing/bang/bar', 'foo/bar'], + ['foo/abar'], + ), + ( + 'foo/**', + ['foo/bar/bing.py', 'foo/x'], + ['/foo/x'], + ), + ( + '**', + ['x', 'abc/xyz', '@nything'], + [], + ), + # Character classes + ( + 'pre[one]post', + ['preopost', 'prenpost', 'preepost'], + ['prepost', 'preonepost'], + ), + ( + 'hello[!one]world', + ['helloxworld', 'helloyworld'], + ['hellooworld', 'helloworld', 'hellooneworld'], + ), + ( + '[]one].txt', + ['o.txt', '].txt', 'e.txt'], + ['one].txt'], + ), + ( + 'foo[!]one]bar', + ['fooybar'], + ['foo]bar', 'fooobar', 'fooebar'], + ), +] +""" +A spec of inputs for 'translate_pattern' and matches and mismatches +for that input. +""" + +match_params = itertools.chain.from_iterable( + zip(itertools.repeat(pattern), matches) + for pattern, matches, mismatches in translate_specs +) + + +@pytest.fixture(params=match_params) +def pattern_match(request): + return map(make_local_path, request.param) + + +mismatch_params = itertools.chain.from_iterable( + zip(itertools.repeat(pattern), mismatches) + for pattern, matches, mismatches in translate_specs +) + + +@pytest.fixture(params=mismatch_params) +def pattern_mismatch(request): + return map(make_local_path, request.param) + + +def test_translated_pattern_match(pattern_match): + pattern, target = pattern_match + assert translate_pattern(pattern).match(target) + + +def test_translated_pattern_mismatch(pattern_mismatch): + pattern, target = pattern_mismatch + assert not translate_pattern(pattern).match(target) + + +class TempDirTestCase: + def setup_method(self, method): + self.temp_dir = tempfile.mkdtemp() + self.old_cwd = os.getcwd() + os.chdir(self.temp_dir) + + def teardown_method(self, method): + os.chdir(self.old_cwd) + shutil.rmtree(self.temp_dir) + + +class TestManifestTest(TempDirTestCase): + def setup_method(self, method): + super().setup_method(method) + + f = open(os.path.join(self.temp_dir, 'setup.py'), 'w', encoding="utf-8") + f.write(SETUP_PY) + f.close() + """ + Create a file tree like: + - LICENSE + - README.rst + - testing.rst + - .hidden.rst + - app/ + - __init__.py + - a.txt + - b.txt + - c.rst + - static/ + - app.js + - app.js.map + - app.css + - app.css.map + """ + + for fname in ['README.rst', '.hidden.rst', 'testing.rst', 'LICENSE']: + touch(os.path.join(self.temp_dir, fname)) + + # Set up the rest of the test package + test_pkg = os.path.join(self.temp_dir, 'app') + os.mkdir(test_pkg) + for fname in ['__init__.py', 'a.txt', 'b.txt', 'c.rst']: + touch(os.path.join(test_pkg, fname)) + + # Some compiled front-end assets to include + static = os.path.join(test_pkg, 'static') + os.mkdir(static) + for fname in ['app.js', 'app.js.map', 'app.css', 'app.css.map']: + touch(os.path.join(static, fname)) + + def make_manifest(self, contents): + """Write a MANIFEST.in.""" + manifest = os.path.join(self.temp_dir, 'MANIFEST.in') + with open(manifest, 'w', encoding="utf-8") as f: + f.write(DALS(contents)) + + def get_files(self): + """Run egg_info and get all the files to include, as a set""" + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = egg_info(dist) + cmd.ensure_finalized() + + cmd.run() + + return set(cmd.filelist.files) + + def test_no_manifest(self): + """Check a missing MANIFEST.in includes only the standard files.""" + assert (default_files - set(['MANIFEST.in'])) == self.get_files() + + def test_empty_files(self): + """Check an empty MANIFEST.in includes only the standard files.""" + self.make_manifest("") + assert default_files == self.get_files() + + def test_include(self): + """Include extra rst files in the project root.""" + self.make_manifest("include *.rst") + files = default_files | set(['testing.rst', '.hidden.rst']) + assert files == self.get_files() + + def test_exclude(self): + """Include everything in app/ except the text files""" + ml = make_local_path + self.make_manifest( + """ + include app/* + exclude app/*.txt + """ + ) + files = default_files | set([ml('app/c.rst')]) + assert files == self.get_files() + + def test_include_multiple(self): + """Include with multiple patterns.""" + ml = make_local_path + self.make_manifest("include app/*.txt app/static/*") + files = default_files | set([ + ml('app/a.txt'), + ml('app/b.txt'), + ml('app/static/app.js'), + ml('app/static/app.js.map'), + ml('app/static/app.css'), + ml('app/static/app.css.map'), + ]) + assert files == self.get_files() + + def test_graft(self): + """Include the whole app/static/ directory.""" + ml = make_local_path + self.make_manifest("graft app/static") + files = default_files | set([ + ml('app/static/app.js'), + ml('app/static/app.js.map'), + ml('app/static/app.css'), + ml('app/static/app.css.map'), + ]) + assert files == self.get_files() + + def test_graft_glob_syntax(self): + """Include the whole app/static/ directory.""" + ml = make_local_path + self.make_manifest("graft */static") + files = default_files | set([ + ml('app/static/app.js'), + ml('app/static/app.js.map'), + ml('app/static/app.css'), + ml('app/static/app.css.map'), + ]) + assert files == self.get_files() + + def test_graft_global_exclude(self): + """Exclude all *.map files in the project.""" + ml = make_local_path + self.make_manifest( + """ + graft app/static + global-exclude *.map + """ + ) + files = default_files | set([ml('app/static/app.js'), ml('app/static/app.css')]) + assert files == self.get_files() + + def test_global_include(self): + """Include all *.rst, *.js, and *.css files in the whole tree.""" + ml = make_local_path + self.make_manifest( + """ + global-include *.rst *.js *.css + """ + ) + files = default_files | set([ + '.hidden.rst', + 'testing.rst', + ml('app/c.rst'), + ml('app/static/app.js'), + ml('app/static/app.css'), + ]) + assert files == self.get_files() + + def test_graft_prune(self): + """Include all files in app/, except for the whole app/static/ dir.""" + ml = make_local_path + self.make_manifest( + """ + graft app + prune app/static + """ + ) + files = default_files | set([ml('app/a.txt'), ml('app/b.txt'), ml('app/c.rst')]) + assert files == self.get_files() + + +class TestFileListTest(TempDirTestCase): + """ + A copy of the relevant bits of distutils/tests/test_filelist.py, + to ensure setuptools' version of FileList keeps parity with distutils. + """ + + @pytest.fixture(autouse=os.getenv("SETUPTOOLS_USE_DISTUTILS") == "stdlib") + def _compat_record_logs(self, monkeypatch, caplog): + """Account for stdlib compatibility""" + + def _log(_logger, level, msg, args): + exc = sys.exc_info() + rec = logging.LogRecord("distutils", level, "", 0, msg, args, exc) + caplog.records.append(rec) + + monkeypatch.setattr(log.Log, "_log", _log) + + def get_records(self, caplog, *levels): + return [r for r in caplog.records if r.levelno in levels] + + def assertNoWarnings(self, caplog): + assert self.get_records(caplog, log.WARN) == [] + caplog.clear() + + def assertWarnings(self, caplog): + if IS_PYPY and not caplog.records: + pytest.xfail("caplog checks may not work well in PyPy") + else: + assert len(self.get_records(caplog, log.WARN)) > 0 + caplog.clear() + + def make_files(self, files): + for file in files: + file = os.path.join(self.temp_dir, file) + dirname, _basename = os.path.split(file) + os.makedirs(dirname, exist_ok=True) + touch(file) + + def test_process_template_line(self): + # testing all MANIFEST.in template patterns + file_list = FileList() + ml = make_local_path + + # simulated file list + self.make_files([ + 'foo.tmp', + 'ok', + 'xo', + 'four.txt', + 'buildout.cfg', + # filelist does not filter out VCS directories, + # it's sdist that does + ml('.hg/last-message.txt'), + ml('global/one.txt'), + ml('global/two.txt'), + ml('global/files.x'), + ml('global/here.tmp'), + ml('f/o/f.oo'), + ml('dir/graft-one'), + ml('dir/dir2/graft2'), + ml('dir3/ok'), + ml('dir3/sub/ok.txt'), + ]) + + MANIFEST_IN = DALS( + """\ + include ok + include xo + exclude xo + include foo.tmp + include buildout.cfg + global-include *.x + global-include *.txt + global-exclude *.tmp + recursive-include f *.oo + recursive-exclude global *.x + graft dir + prune dir3 + """ + ) + + for line in MANIFEST_IN.split('\n'): + if not line: + continue + file_list.process_template_line(line) + + wanted = [ + 'buildout.cfg', + 'four.txt', + 'ok', + ml('.hg/last-message.txt'), + ml('dir/graft-one'), + ml('dir/dir2/graft2'), + ml('f/o/f.oo'), + ml('global/one.txt'), + ml('global/two.txt'), + ] + + file_list.sort() + assert file_list.files == wanted + + def test_exclude_pattern(self): + # return False if no match + file_list = FileList() + assert not file_list.exclude_pattern('*.py') + + # return True if files match + file_list = FileList() + file_list.files = ['a.py', 'b.py'] + assert file_list.exclude_pattern('*.py') + + # test excludes + file_list = FileList() + file_list.files = ['a.py', 'a.txt'] + file_list.exclude_pattern('*.py') + file_list.sort() + assert file_list.files == ['a.txt'] + + def test_include_pattern(self): + # return False if no match + file_list = FileList() + self.make_files([]) + assert not file_list.include_pattern('*.py') + + # return True if files match + file_list = FileList() + self.make_files(['a.py', 'b.txt']) + assert file_list.include_pattern('*.py') + + # test * matches all files + file_list = FileList() + self.make_files(['a.py', 'b.txt']) + file_list.include_pattern('*') + file_list.sort() + assert file_list.files == ['a.py', 'b.txt'] + + def test_process_template_line_invalid(self): + # invalid lines + file_list = FileList() + for action in ( + 'include', + 'exclude', + 'global-include', + 'global-exclude', + 'recursive-include', + 'recursive-exclude', + 'graft', + 'prune', + 'blarg', + ): + with pytest.raises(DistutilsTemplateError): + file_list.process_template_line(action) + + def test_include(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # include + file_list = FileList() + self.make_files(['a.py', 'b.txt', ml('d/c.py')]) + + file_list.process_template_line('include *.py') + file_list.sort() + assert file_list.files == ['a.py'] + self.assertNoWarnings(caplog) + + file_list.process_template_line('include *.rb') + file_list.sort() + assert file_list.files == ['a.py'] + self.assertWarnings(caplog) + + def test_exclude(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', ml('d/c.py')] + + file_list.process_template_line('exclude *.py') + file_list.sort() + assert file_list.files == ['b.txt', ml('d/c.py')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('exclude *.rb') + file_list.sort() + assert file_list.files == ['b.txt', ml('d/c.py')] + self.assertWarnings(caplog) + + def test_global_include(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # global-include + file_list = FileList() + self.make_files(['a.py', 'b.txt', ml('d/c.py')]) + + file_list.process_template_line('global-include *.py') + file_list.sort() + assert file_list.files == ['a.py', ml('d/c.py')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('global-include *.rb') + file_list.sort() + assert file_list.files == ['a.py', ml('d/c.py')] + self.assertWarnings(caplog) + + def test_global_exclude(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # global-exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', ml('d/c.py')] + + file_list.process_template_line('global-exclude *.py') + file_list.sort() + assert file_list.files == ['b.txt'] + self.assertNoWarnings(caplog) + + file_list.process_template_line('global-exclude *.rb') + file_list.sort() + assert file_list.files == ['b.txt'] + self.assertWarnings(caplog) + + def test_recursive_include(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # recursive-include + file_list = FileList() + self.make_files(['a.py', ml('d/b.py'), ml('d/c.txt'), ml('d/d/e.py')]) + + file_list.process_template_line('recursive-include d *.py') + file_list.sort() + assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('recursive-include e *.py') + file_list.sort() + assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')] + self.assertWarnings(caplog) + + def test_recursive_exclude(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # recursive-exclude + file_list = FileList() + file_list.files = ['a.py', ml('d/b.py'), ml('d/c.txt'), ml('d/d/e.py')] + + file_list.process_template_line('recursive-exclude d *.py') + file_list.sort() + assert file_list.files == ['a.py', ml('d/c.txt')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('recursive-exclude e *.py') + file_list.sort() + assert file_list.files == ['a.py', ml('d/c.txt')] + self.assertWarnings(caplog) + + def test_graft(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # graft + file_list = FileList() + self.make_files(['a.py', ml('d/b.py'), ml('d/d/e.py'), ml('f/f.py')]) + + file_list.process_template_line('graft d') + file_list.sort() + assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('graft e') + file_list.sort() + assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')] + self.assertWarnings(caplog) + + def test_prune(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # prune + file_list = FileList() + file_list.files = ['a.py', ml('d/b.py'), ml('d/d/e.py'), ml('f/f.py')] + + file_list.process_template_line('prune d') + file_list.sort() + assert file_list.files == ['a.py', ml('f/f.py')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('prune e') + file_list.sort() + assert file_list.files == ['a.py', ml('f/f.py')] + self.assertWarnings(caplog) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_namespaces.py b/lib/python3.12/site-packages/setuptools/tests/test_namespaces.py new file mode 100644 index 0000000000000000000000000000000000000000..a0f4120bf7900b2118cc066034e036ab7af1798b --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_namespaces.py @@ -0,0 +1,138 @@ +import subprocess +import sys + +from setuptools._path import paths_on_pythonpath + +from . import namespaces + + +class TestNamespaces: + def test_mixed_site_and_non_site(self, tmpdir): + """ + Installing two packages sharing the same namespace, one installed + to a site dir and the other installed just to a path on PYTHONPATH + should leave the namespace in tact and both packages reachable by + import. + """ + pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') + pkg_B = namespaces.build_namespace_package(tmpdir, 'myns.pkgB') + site_packages = tmpdir / 'site-packages' + path_packages = tmpdir / 'path-packages' + targets = site_packages, path_packages + # use pip to install to the target directory + install_cmd = [ + sys.executable, + '-m', + 'pip.__main__', + 'install', + str(pkg_A), + '-t', + str(site_packages), + ] + subprocess.check_call(install_cmd) + namespaces.make_site_dir(site_packages) + install_cmd = [ + sys.executable, + '-m', + 'pip.__main__', + 'install', + str(pkg_B), + '-t', + str(path_packages), + ] + subprocess.check_call(install_cmd) + try_import = [ + sys.executable, + '-c', + 'import myns.pkgA; import myns.pkgB', + ] + with paths_on_pythonpath(map(str, targets)): + subprocess.check_call(try_import) + + def test_pkg_resources_import(self, tmpdir): + """ + Ensure that a namespace package doesn't break on import + of pkg_resources. + """ + pkg = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') + target = tmpdir / 'packages' + target.mkdir() + install_cmd = [ + sys.executable, + '-m', + 'pip', + 'install', + '-t', + str(target), + str(pkg), + ] + with paths_on_pythonpath([str(target)]): + subprocess.check_call(install_cmd) + namespaces.make_site_dir(target) + try_import = [ + sys.executable, + '-c', + 'import pkg_resources', + ] + with paths_on_pythonpath([str(target)]): + subprocess.check_call(try_import) + + def test_namespace_package_installed_and_cwd(self, tmpdir): + """ + Installing a namespace packages but also having it in the current + working directory, only one version should take precedence. + """ + pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') + target = tmpdir / 'packages' + # use pip to install to the target directory + install_cmd = [ + sys.executable, + '-m', + 'pip.__main__', + 'install', + str(pkg_A), + '-t', + str(target), + ] + subprocess.check_call(install_cmd) + namespaces.make_site_dir(target) + + # ensure that package imports and pkg_resources imports + pkg_resources_imp = [ + sys.executable, + '-c', + 'import pkg_resources; import myns.pkgA', + ] + with paths_on_pythonpath([str(target)]): + subprocess.check_call(pkg_resources_imp, cwd=str(pkg_A)) + + def test_packages_in_the_same_namespace_installed_and_cwd(self, tmpdir): + """ + Installing one namespace package and also have another in the same + namespace in the current working directory, both of them must be + importable. + """ + pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') + pkg_B = namespaces.build_namespace_package(tmpdir, 'myns.pkgB') + target = tmpdir / 'packages' + # use pip to install to the target directory + install_cmd = [ + sys.executable, + '-m', + 'pip.__main__', + 'install', + str(pkg_A), + '-t', + str(target), + ] + subprocess.check_call(install_cmd) + namespaces.make_site_dir(target) + + # ensure that all packages import and pkg_resources imports + pkg_resources_imp = [ + sys.executable, + '-c', + 'import pkg_resources; import myns.pkgA; import myns.pkgB', + ] + with paths_on_pythonpath([str(target)]): + subprocess.check_call(pkg_resources_imp, cwd=str(pkg_B)) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_scripts.py b/lib/python3.12/site-packages/setuptools/tests/test_scripts.py new file mode 100644 index 0000000000000000000000000000000000000000..8641f7b639161525e2fff3100744d7fa55d9d718 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_scripts.py @@ -0,0 +1,12 @@ +from setuptools import _scripts + + +class TestWindowsScriptWriter: + def test_header(self): + hdr = _scripts.WindowsScriptWriter.get_header('') + assert hdr.startswith('#!') + assert hdr.endswith('\n') + hdr = hdr.lstrip('#!') + hdr = hdr.rstrip('\n') + # header should not start with an escaped quote + assert not hdr.startswith('\\"') diff --git a/lib/python3.12/site-packages/setuptools/tests/test_sdist.py b/lib/python3.12/site-packages/setuptools/tests/test_sdist.py new file mode 100644 index 0000000000000000000000000000000000000000..5b435fe111346d026ba20bbbf68994282e8b64c7 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_sdist.py @@ -0,0 +1,980 @@ +"""sdist tests""" + +import contextlib +import io +import logging +import os +import pathlib +import sys +import tarfile +import tempfile +import unicodedata +from inspect import cleandoc +from pathlib import Path +from unittest import mock + +import jaraco.path +import pytest + +from setuptools import Command, SetuptoolsDeprecationWarning +from setuptools._importlib import metadata +from setuptools.command.egg_info import manifest_maker +from setuptools.command.sdist import sdist +from setuptools.dist import Distribution +from setuptools.extension import Extension +from setuptools.tests import fail_on_ascii + +from .text import Filenames + +import distutils +from distutils.core import run_setup + +SETUP_ATTRS = { + 'name': 'sdist_test', + 'version': '0.0', + 'packages': ['sdist_test'], + 'package_data': {'sdist_test': ['*.txt']}, + 'data_files': [("data", [os.path.join("d", "e.dat")])], +} + +SETUP_PY = f"""\ +from setuptools import setup + +setup(**{SETUP_ATTRS!r}) +""" + +EXTENSION = Extension( + name="sdist_test.f", + sources=[os.path.join("sdist_test", "f.c")], + depends=[os.path.join("sdist_test", "f.h")], +) +EXTENSION_SOURCES = EXTENSION.sources + EXTENSION.depends + + +@contextlib.contextmanager +def quiet(): + old_stdout, old_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = io.StringIO(), io.StringIO() + try: + yield + finally: + sys.stdout, sys.stderr = old_stdout, old_stderr + + +# Convert to POSIX path +def posix(path): + if not isinstance(path, str): + return path.replace(os.sep.encode('ascii'), b'/') + else: + return path.replace(os.sep, '/') + + +# HFS Plus uses decomposed UTF-8 +def decompose(path): + if isinstance(path, str): + return unicodedata.normalize('NFD', path) + try: + path = path.decode('utf-8') + path = unicodedata.normalize('NFD', path) + path = path.encode('utf-8') + except UnicodeError: + pass # Not UTF-8 + return path + + +def read_all_bytes(filename): + with open(filename, 'rb') as fp: + return fp.read() + + +def latin1_fail(): + try: + desc, filename = tempfile.mkstemp(suffix=Filenames.latin_1) + os.close(desc) + os.remove(filename) + except Exception: + return True + + +fail_on_latin1_encoded_filenames = pytest.mark.xfail( + latin1_fail(), + reason="System does not support latin-1 filenames", +) + + +skip_under_xdist = pytest.mark.skipif( + "os.environ.get('PYTEST_XDIST_WORKER')", + reason="pytest-dev/pytest-xdist#843", +) +skip_under_stdlib_distutils = pytest.mark.skipif( + not distutils.__package__.startswith('setuptools'), + reason="the test is not supported with stdlib distutils", +) + + +def touch(path): + open(path, 'wb').close() + return path + + +def symlink_or_skip_test(src, dst): + try: + os.symlink(src, dst) + except (OSError, NotImplementedError): + pytest.skip("symlink not supported in OS") + return None + return dst + + +class TestSdistTest: + @pytest.fixture(autouse=True) + def source_dir(self, tmpdir): + tmpdir = tmpdir / "project_root" + tmpdir.mkdir() + + (tmpdir / 'setup.py').write_text(SETUP_PY, encoding='utf-8') + + # Set up the rest of the test package + test_pkg = tmpdir / 'sdist_test' + test_pkg.mkdir() + data_folder = tmpdir / 'd' + data_folder.mkdir() + # *.rst was not included in package_data, so c.rst should not be + # automatically added to the manifest when not under version control + for fname in ['__init__.py', 'a.txt', 'b.txt', 'c.rst']: + touch(test_pkg / fname) + touch(data_folder / 'e.dat') + # C sources are not included by default, but they will be, + # if an extension module uses them as sources or depends + for fname in EXTENSION_SOURCES: + touch(tmpdir / fname) + + with tmpdir.as_cwd(): + yield tmpdir + + def assert_package_data_in_manifest(self, cmd): + manifest = cmd.filelist.files + assert os.path.join('sdist_test', 'a.txt') in manifest + assert os.path.join('sdist_test', 'b.txt') in manifest + assert os.path.join('sdist_test', 'c.rst') not in manifest + assert os.path.join('d', 'e.dat') in manifest + + def setup_with_extension(self): + setup_attrs = {**SETUP_ATTRS, 'ext_modules': [EXTENSION]} + + dist = Distribution(setup_attrs) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + return cmd + + def test_package_data_in_sdist(self): + """Regression test for pull request #4: ensures that files listed in + package_data are included in the manifest even if they're not added to + version control. + """ + + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + self.assert_package_data_in_manifest(cmd) + + def test_package_data_and_include_package_data_in_sdist(self): + """ + Ensure package_data and include_package_data work + together. + """ + setup_attrs = {**SETUP_ATTRS, 'include_package_data': True} + assert setup_attrs['package_data'] + + dist = Distribution(setup_attrs) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + self.assert_package_data_in_manifest(cmd) + + def test_extension_sources_in_sdist(self): + """ + Ensure that the files listed in Extension.sources and Extension.depends + are automatically included in the manifest. + """ + cmd = self.setup_with_extension() + self.assert_package_data_in_manifest(cmd) + manifest = cmd.filelist.files + for path in EXTENSION_SOURCES: + assert path in manifest + + def test_missing_extension_sources(self): + """ + Similar to test_extension_sources_in_sdist but the referenced files don't exist. + Missing files should not be included in distribution (with no error raised). + """ + for path in EXTENSION_SOURCES: + os.remove(path) + + cmd = self.setup_with_extension() + self.assert_package_data_in_manifest(cmd) + manifest = cmd.filelist.files + for path in EXTENSION_SOURCES: + assert path not in manifest + + def test_symlinked_extension_sources(self): + """ + Similar to test_extension_sources_in_sdist but the referenced files are + instead symbolic links to project-local files. Referenced file paths + should be included. Symlink targets themselves should NOT be included. + """ + symlinked = [] + for path in EXTENSION_SOURCES: + base, ext = os.path.splitext(path) + target = base + "_target." + ext + + os.rename(path, target) + symlink_or_skip_test(os.path.basename(target), path) + symlinked.append(target) + + cmd = self.setup_with_extension() + self.assert_package_data_in_manifest(cmd) + manifest = cmd.filelist.files + for path in EXTENSION_SOURCES: + assert path in manifest + for path in symlinked: + assert path not in manifest + + _INVALID_PATHS = { + "must be relative": lambda: os.path.abspath(os.path.join("sdist_test", "f.h")), + "can't have `..` segments": lambda: os.path.join( + "sdist_test", "..", "sdist_test", "f.h" + ), + "doesn't exist": lambda: os.path.join( + "sdist_test", "this_file_does_not_exist.h" + ), + "must be inside the project root": lambda: symlink_or_skip_test( + touch(os.path.join("..", "outside_of_project_root.h")), + "symlink.h", + ), + } + + @skip_under_stdlib_distutils + @pytest.mark.parametrize("reason", _INVALID_PATHS.keys()) + def test_invalid_extension_depends(self, reason, caplog): + """ + Due to backwards compatibility reasons, `Extension.depends` should accept + invalid/weird paths, but then ignore them when building a sdist. + + This test verifies that the source distribution is still built + successfully with such paths, but that instead of adding these paths to + the manifest, we emit an informational message, notifying the user that + the invalid path won't be automatically included. + """ + invalid_path = self._INVALID_PATHS[reason]() + extension = Extension( + name="sdist_test.f", + sources=[], + depends=[invalid_path], + ) + setup_attrs = {**SETUP_ATTRS, 'ext_modules': [extension]} + + dist = Distribution(setup_attrs) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(), caplog.at_level(logging.INFO): + cmd.run() + + self.assert_package_data_in_manifest(cmd) + manifest = cmd.filelist.files + assert invalid_path not in manifest + + expected_message = [ + message + for (logger, level, message) in caplog.record_tuples + if ( + logger == "root" # + and level == logging.INFO # + and invalid_path in message # + ) + ] + assert len(expected_message) == 1 + (expected_message,) = expected_message + assert reason in expected_message + + def test_custom_build_py(self): + """ + Ensure projects defining custom build_py don't break + when creating sdists (issue #2849) + """ + from distutils.command.build_py import build_py as OrigBuildPy + + using_custom_command_guard = mock.Mock() + + class CustomBuildPy(OrigBuildPy): + """ + Some projects have custom commands inheriting from `distutils` + """ + + def get_data_files(self): + using_custom_command_guard() + return super().get_data_files() + + setup_attrs = {**SETUP_ATTRS, 'include_package_data': True} + assert setup_attrs['package_data'] + + dist = Distribution(setup_attrs) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + # Make sure we use the custom command + cmd.cmdclass = {'build_py': CustomBuildPy} + cmd.distribution.cmdclass = {'build_py': CustomBuildPy} + assert cmd.distribution.get_command_class('build_py') == CustomBuildPy + + msg = "setuptools instead of distutils" + with quiet(), pytest.warns(SetuptoolsDeprecationWarning, match=msg): + cmd.run() + + using_custom_command_guard.assert_called() + self.assert_package_data_in_manifest(cmd) + + def test_setup_py_exists(self): + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'foo.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + manifest = cmd.filelist.files + assert 'setup.py' in manifest + + def test_setup_py_missing(self): + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'foo.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + if os.path.exists("setup.py"): + os.remove("setup.py") + with quiet(): + cmd.run() + + manifest = cmd.filelist.files + assert 'setup.py' not in manifest + + def test_setup_py_excluded(self): + with open("MANIFEST.in", "w", encoding="utf-8") as manifest_file: + manifest_file.write("exclude setup.py") + + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'foo.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + manifest = cmd.filelist.files + assert 'setup.py' not in manifest + + def test_defaults_case_sensitivity(self, source_dir): + """ + Make sure default files (README.*, etc.) are added in a case-sensitive + way to avoid problems with packages built on Windows. + """ + + touch(source_dir / 'readme.rst') + touch(source_dir / 'SETUP.cfg') + + dist = Distribution(SETUP_ATTRS) + # the extension deliberately capitalized for this test + # to make sure the actual filename (not capitalized) gets added + # to the manifest + dist.script_name = 'setup.PY' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + # lowercase all names so we can test in a + # case-insensitive way to make sure the files + # are not included. + manifest = map(lambda x: x.lower(), cmd.filelist.files) + assert 'readme.rst' not in manifest, manifest + assert 'setup.py' not in manifest, manifest + assert 'setup.cfg' not in manifest, manifest + + def test_exclude_dev_only_cache_folders(self, source_dir): + included = { + # Emulate problem in https://github.com/pypa/setuptools/issues/4601 + "MANIFEST.in": ( + "global-include LICEN[CS]E* COPYING* NOTICE* AUTHORS*\n" + "global-include *.txt\n" + ), + # For the sake of being conservative and limiting unforeseen side-effects + # we just exclude dev-only cache folders at the root of the repository: + "test/.venv/lib/python3.9/site-packages/bar-2.dist-info/AUTHORS.rst": "", + "src/.nox/py/lib/python3.12/site-packages/bar-2.dist-info/COPYING.txt": "", + "doc/.tox/default/lib/python3.11/site-packages/foo-4.dist-info/LICENSE": "", + # Let's test against false positives with similarly named files: + ".venv-requirements.txt": "", + ".tox-coveragerc.txt": "", + ".noxy/coveragerc.txt": "", + } + + excluded = { + # .tox/.nox/.venv are well-know folders present at the root of Python repos + # and therefore should be excluded + ".tox/release/lib/python3.11/site-packages/foo-4.dist-info/LICENSE": "", + ".nox/py/lib/python3.12/site-packages/bar-2.dist-info/COPYING.txt": "", + ".venv/lib/python3.9/site-packages/bar-2.dist-info/AUTHORS.rst": "", + } + + for file, content in {**excluded, **included}.items(): + Path(source_dir, file).parent.mkdir(parents=True, exist_ok=True) + Path(source_dir, file).write_text(content, encoding="utf-8") + + cmd = self.setup_with_extension() + self.assert_package_data_in_manifest(cmd) + manifest = {f.replace(os.sep, '/') for f in cmd.filelist.files} + for path in excluded: + assert os.path.exists(path) + assert path not in manifest, (path, manifest) + for path in included: + assert os.path.exists(path) + assert path in manifest, (path, manifest) + + @fail_on_ascii + def test_manifest_is_written_with_utf8_encoding(self): + # Test for #303. + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + mm = manifest_maker(dist) + mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') + os.mkdir('sdist_test.egg-info') + + # UTF-8 filename + filename = os.path.join('sdist_test', 'smörbröd.py') + + # Must create the file or it will get stripped. + touch(filename) + + # Add UTF-8 filename and write manifest + with quiet(): + mm.run() + mm.filelist.append(filename) + mm.write_manifest() + + contents = read_all_bytes(mm.manifest) + + # The manifest should be UTF-8 encoded + u_contents = contents.decode('UTF-8') + + # The manifest should contain the UTF-8 filename + assert posix(filename) in u_contents + + @fail_on_ascii + def test_write_manifest_allows_utf8_filenames(self): + # Test for #303. + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + mm = manifest_maker(dist) + mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') + os.mkdir('sdist_test.egg-info') + + filename = os.path.join(b'sdist_test', Filenames.utf_8) + + # Must touch the file or risk removal + touch(filename) + + # Add filename and write manifest + with quiet(): + mm.run() + u_filename = filename.decode('utf-8') + mm.filelist.files.append(u_filename) + # Re-write manifest + mm.write_manifest() + + contents = read_all_bytes(mm.manifest) + + # The manifest should be UTF-8 encoded + contents.decode('UTF-8') + + # The manifest should contain the UTF-8 filename + assert posix(filename) in contents + + # The filelist should have been updated as well + assert u_filename in mm.filelist.files + + @skip_under_xdist + def test_write_manifest_skips_non_utf8_filenames(self): + """ + Files that cannot be encoded to UTF-8 (specifically, those that + weren't originally successfully decoded and have surrogate + escapes) should be omitted from the manifest. + See https://bitbucket.org/tarek/distribute/issue/303 for history. + """ + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + mm = manifest_maker(dist) + mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') + os.mkdir('sdist_test.egg-info') + + # Latin-1 filename + filename = os.path.join(b'sdist_test', Filenames.latin_1) + + # Add filename with surrogates and write manifest + with quiet(): + mm.run() + u_filename = filename.decode('utf-8', 'surrogateescape') + mm.filelist.append(u_filename) + # Re-write manifest + mm.write_manifest() + + contents = read_all_bytes(mm.manifest) + + # The manifest should be UTF-8 encoded + contents.decode('UTF-8') + + # The Latin-1 filename should have been skipped + assert posix(filename) not in contents + + # The filelist should have been updated as well + assert u_filename not in mm.filelist.files + + @fail_on_ascii + def test_manifest_is_read_with_utf8_encoding(self): + # Test for #303. + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + # Create manifest + with quiet(): + cmd.run() + + # Add UTF-8 filename to manifest + filename = os.path.join(b'sdist_test', Filenames.utf_8) + cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') + manifest = open(cmd.manifest, 'ab') + manifest.write(b'\n' + filename) + manifest.close() + + # The file must exist to be included in the filelist + touch(filename) + + # Re-read manifest + cmd.filelist.files = [] + with quiet(): + cmd.read_manifest() + + # The filelist should contain the UTF-8 filename + filename = filename.decode('utf-8') + assert filename in cmd.filelist.files + + @fail_on_latin1_encoded_filenames + def test_read_manifest_skips_non_utf8_filenames(self): + # Test for #303. + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + # Create manifest + with quiet(): + cmd.run() + + # Add Latin-1 filename to manifest + filename = os.path.join(b'sdist_test', Filenames.latin_1) + cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') + manifest = open(cmd.manifest, 'ab') + manifest.write(b'\n' + filename) + manifest.close() + + # The file must exist to be included in the filelist + touch(filename) + + # Re-read manifest + cmd.filelist.files = [] + with quiet(): + cmd.read_manifest() + + # The Latin-1 filename should have been skipped + filename = filename.decode('latin-1') + assert filename not in cmd.filelist.files + + @fail_on_ascii + @fail_on_latin1_encoded_filenames + def test_sdist_with_utf8_encoded_filename(self): + # Test for #303. + dist = Distribution(self.make_strings(SETUP_ATTRS)) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + filename = os.path.join(b'sdist_test', Filenames.utf_8) + touch(filename) + + with quiet(): + cmd.run() + + if sys.platform == 'darwin': + filename = decompose(filename) + + fs_enc = sys.getfilesystemencoding() + + if sys.platform == 'win32': + if fs_enc == 'cp1252': + # Python mangles the UTF-8 filename + filename = filename.decode('cp1252') + assert filename in cmd.filelist.files + else: + filename = filename.decode('mbcs') + assert filename in cmd.filelist.files + else: + filename = filename.decode('utf-8') + assert filename in cmd.filelist.files + + @classmethod + def make_strings(cls, item): + if isinstance(item, dict): + return {key: cls.make_strings(value) for key, value in item.items()} + if isinstance(item, list): + return list(map(cls.make_strings, item)) + return str(item) + + @fail_on_latin1_encoded_filenames + @skip_under_xdist + def test_sdist_with_latin1_encoded_filename(self): + # Test for #303. + dist = Distribution(self.make_strings(SETUP_ATTRS)) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + # Latin-1 filename + filename = os.path.join(b'sdist_test', Filenames.latin_1) + touch(filename) + assert os.path.isfile(filename) + + with quiet(): + cmd.run() + + # not all windows systems have a default FS encoding of cp1252 + if sys.platform == 'win32': + # Latin-1 is similar to Windows-1252 however + # on mbcs filesys it is not in latin-1 encoding + fs_enc = sys.getfilesystemencoding() + if fs_enc != 'mbcs': + fs_enc = 'latin-1' + filename = filename.decode(fs_enc) + + assert filename in cmd.filelist.files + else: + # The Latin-1 filename should have been skipped + filename = filename.decode('latin-1') + assert filename not in cmd.filelist.files + + _EXAMPLE_DIRECTIVES = { + "setup.cfg - long_description and version": """ + [metadata] + name = testing + version = file: src/VERSION.txt + license_files = DOWHATYOUWANT + long_description = file: README.rst, USAGE.rst + """, + "pyproject.toml - static readme/license files and dynamic version": """ + [project] + name = "testing" + readme = "USAGE.rst" + license-files = ["DOWHATYOUWANT"] + dynamic = ["version"] + [tool.setuptools.dynamic] + version = {file = ["src/VERSION.txt"]} + """, + "pyproject.toml - directive with str instead of list": """ + [project] + name = "testing" + readme = "USAGE.rst" + license-files = ["DOWHATYOUWANT"] + dynamic = ["version"] + [tool.setuptools.dynamic] + version = {file = "src/VERSION.txt"} + """, + "pyproject.toml - deprecated license table with file entry": """ + [project] + name = "testing" + readme = "USAGE.rst" + license = {file = "DOWHATYOUWANT"} + dynamic = ["version"] + [tool.setuptools.dynamic] + version = {file = "src/VERSION.txt"} + """, + } + + @pytest.mark.parametrize("config", _EXAMPLE_DIRECTIVES.keys()) + @pytest.mark.filterwarnings( + "ignore:.project.license. as a TOML table is deprecated" + ) + def test_add_files_referenced_by_config_directives(self, source_dir, config): + config_file, _, _ = config.partition(" - ") + config_text = self._EXAMPLE_DIRECTIVES[config] + (source_dir / 'src').mkdir() + (source_dir / 'src/VERSION.txt').write_text("0.42", encoding="utf-8") + (source_dir / 'README.rst').write_text("hello world!", encoding="utf-8") + (source_dir / 'USAGE.rst').write_text("hello world!", encoding="utf-8") + (source_dir / 'DOWHATYOUWANT').write_text("hello world!", encoding="utf-8") + (source_dir / config_file).write_text(config_text, encoding="utf-8") + + dist = Distribution({"packages": []}) + dist.script_name = 'setup.py' + dist.parse_config_files() + + cmd = sdist(dist) + cmd.ensure_finalized() + with quiet(): + cmd.run() + + assert ( + 'src/VERSION.txt' in cmd.filelist.files + or 'src\\VERSION.txt' in cmd.filelist.files + ) + assert 'USAGE.rst' in cmd.filelist.files + assert 'DOWHATYOUWANT' in cmd.filelist.files + assert '/' not in cmd.filelist.files + assert '\\' not in cmd.filelist.files + + def test_pyproject_toml_in_sdist(self, source_dir): + """ + Check if pyproject.toml is included in source distribution if present + """ + touch(source_dir / 'pyproject.toml') + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + with quiet(): + cmd.run() + manifest = cmd.filelist.files + assert 'pyproject.toml' in manifest + + def test_pyproject_toml_excluded(self, source_dir): + """ + Check that pyproject.toml can excluded even if present + """ + touch(source_dir / 'pyproject.toml') + with open('MANIFEST.in', 'w', encoding="utf-8") as mts: + print('exclude pyproject.toml', file=mts) + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + with quiet(): + cmd.run() + manifest = cmd.filelist.files + assert 'pyproject.toml' not in manifest + + def test_build_subcommand_source_files(self, source_dir): + touch(source_dir / '.myfile~') + + # Sanity check: without custom commands file list should not be affected + dist = Distribution({**SETUP_ATTRS, "script_name": "setup.py"}) + cmd = sdist(dist) + cmd.ensure_finalized() + with quiet(): + cmd.run() + manifest = cmd.filelist.files + assert '.myfile~' not in manifest + + # Test: custom command should be able to augment file list + dist = Distribution({**SETUP_ATTRS, "script_name": "setup.py"}) + build = dist.get_command_obj("build") + build.sub_commands = [*build.sub_commands, ("build_custom", None)] + + class build_custom(Command): + def initialize_options(self): ... + + def finalize_options(self): ... + + def run(self): ... + + def get_source_files(self): + return ['.myfile~'] + + dist.cmdclass.update(build_custom=build_custom) + + cmd = sdist(dist) + cmd.use_defaults = True + cmd.ensure_finalized() + with quiet(): + cmd.run() + manifest = cmd.filelist.files + assert '.myfile~' in manifest + + @pytest.mark.skipif("os.environ.get('SETUPTOOLS_USE_DISTUTILS') == 'stdlib'") + def test_build_base_pathlib(self, source_dir): + """ + Ensure if build_base is a pathlib.Path, the build still succeeds. + """ + dist = Distribution({ + **SETUP_ATTRS, + "script_name": "setup.py", + "options": {"build": {"build_base": pathlib.Path('build')}}, + }) + cmd = sdist(dist) + cmd.ensure_finalized() + with quiet(): + cmd.run() + + +def test_default_revctrl(): + """ + When _default_revctrl was removed from the `setuptools.command.sdist` + module in 10.0, it broke some systems which keep an old install of + setuptools (Distribute) around. Those old versions require that the + setuptools package continue to implement that interface, so this + function provides that interface, stubbed. See #320 for details. + + This interface must be maintained until Ubuntu 12.04 is no longer + supported (by Setuptools). + """ + (ep,) = metadata.EntryPoints._from_text( + """ + [setuptools.file_finders] + svn_cvs = setuptools.command.sdist:_default_revctrl + """ + ) + res = ep.load() + assert hasattr(res, '__iter__') + + +class TestRegressions: + """ + Can be removed/changed if the project decides to change how it handles symlinks + or external files. + """ + + @staticmethod + def files_for_symlink_in_extension_depends(tmp_path, dep_path): + return { + "external": { + "dir": {"file.h": ""}, + }, + "project": { + "setup.py": cleandoc( + f""" + from setuptools import Extension, setup + setup( + name="myproj", + version="42", + ext_modules=[ + Extension( + "hello", sources=["hello.pyx"], + depends=[{dep_path!r}] + ) + ], + ) + """ + ), + "hello.pyx": "", + "MANIFEST.in": "global-include *.h", + }, + } + + @pytest.mark.parametrize( + "dep_path", ("myheaders/dir/file.h", "myheaders/dir/../dir/file.h") + ) + def test_symlink_in_extension_depends(self, monkeypatch, tmp_path, dep_path): + # Given a project with a symlinked dir and a "depends" targeting that dir + files = self.files_for_symlink_in_extension_depends(tmp_path, dep_path) + jaraco.path.build(files, prefix=str(tmp_path)) + symlink_or_skip_test(tmp_path / "external", tmp_path / "project/myheaders") + + # When `sdist` runs, there should be no error + members = run_sdist(monkeypatch, tmp_path / "project") + # and the sdist should contain the symlinked files + for expected in ( + "myproj-42/hello.pyx", + "myproj-42/myheaders/dir/file.h", + ): + assert expected in members + + @staticmethod + def files_for_external_path_in_extension_depends(tmp_path, dep_path): + head, _, tail = dep_path.partition("$tmp_path$/") + dep_path = tmp_path / tail if tail else head + + return { + "external": { + "dir": {"file.h": ""}, + }, + "project": { + "setup.py": cleandoc( + f""" + from setuptools import Extension, setup + setup( + name="myproj", + version="42", + ext_modules=[ + Extension( + "hello", sources=["hello.pyx"], + depends=[{str(dep_path)!r}] + ) + ], + ) + """ + ), + "hello.pyx": "", + "MANIFEST.in": "global-include *.h", + }, + } + + @pytest.mark.parametrize( + "dep_path", ("$tmp_path$/external/dir/file.h", "../external/dir/file.h") + ) + def test_external_path_in_extension_depends(self, monkeypatch, tmp_path, dep_path): + # Given a project with a "depends" targeting an external dir + files = self.files_for_external_path_in_extension_depends(tmp_path, dep_path) + jaraco.path.build(files, prefix=str(tmp_path)) + # When `sdist` runs, there should be no error + members = run_sdist(monkeypatch, tmp_path / "project") + # and the sdist should not contain the external file + for name in members: + assert "file.h" not in name + + +def run_sdist(monkeypatch, project): + """Given a project directory, run the sdist and return its contents""" + monkeypatch.chdir(project) + with quiet(): + run_setup("setup.py", ["sdist"]) + + archive = next((project / "dist").glob("*.tar.gz")) + with tarfile.open(str(archive)) as tar: + return set(tar.getnames()) + + +def test_sanity_check_setuptools_own_sdist(setuptools_sdist): + with tarfile.open(setuptools_sdist) as tar: + files = tar.getnames() + + # setuptools sdist should not include the .tox folder + tox_files = [name for name in files if ".tox" in name] + assert len(tox_files) == 0, f"not empty {tox_files}" diff --git a/lib/python3.12/site-packages/setuptools/tests/test_setopt.py b/lib/python3.12/site-packages/setuptools/tests/test_setopt.py new file mode 100644 index 0000000000000000000000000000000000000000..ccf25618a5d6e255ad0be0fbed51fc29c179dcfe --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_setopt.py @@ -0,0 +1,40 @@ +import configparser + +from setuptools.command import setopt + + +class TestEdit: + @staticmethod + def parse_config(filename): + parser = configparser.ConfigParser() + with open(filename, encoding='utf-8') as reader: + parser.read_file(reader) + return parser + + @staticmethod + def write_text(file, content): + with open(file, 'wb') as strm: + strm.write(content.encode('utf-8')) + + def test_utf8_encoding_retained(self, tmpdir): + """ + When editing a file, non-ASCII characters encoded in + UTF-8 should be retained. + """ + config = tmpdir.join('setup.cfg') + self.write_text(str(config), '[names]\njaraco=джарако') + setopt.edit_config(str(config), dict(names=dict(other='yes'))) + parser = self.parse_config(str(config)) + assert parser.get('names', 'jaraco') == 'джарако' + assert parser.get('names', 'other') == 'yes' + + def test_case_retained(self, tmpdir): + """ + When editing a file, case of keys should be retained. + """ + config = tmpdir.join('setup.cfg') + self.write_text(str(config), '[names]\nFoO=bAr') + setopt.edit_config(str(config), dict(names=dict(oTher='yes'))) + actual = config.read_text(encoding='ascii') + assert 'FoO' in actual + assert 'oTher' in actual diff --git a/lib/python3.12/site-packages/setuptools/tests/test_setuptools.py b/lib/python3.12/site-packages/setuptools/tests/test_setuptools.py new file mode 100644 index 0000000000000000000000000000000000000000..1d56e1a8a4ebc5c7aaeb9902ef9972f7de97ecbf --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_setuptools.py @@ -0,0 +1,290 @@ +"""Tests for the 'setuptools' package""" + +import os +import re +import sys +from zipfile import ZipFile + +import pytest +from packaging.version import Version + +import setuptools +import setuptools.depends as dep +import setuptools.dist +from setuptools.depends import Require + +import distutils.cmd +import distutils.core +from distutils.core import Extension +from distutils.errors import DistutilsSetupError + + +@pytest.fixture(autouse=True) +def isolated_dir(tmpdir_cwd): + return + + +def makeSetup(**args): + """Return distribution from 'setup(**args)', without executing commands""" + + distutils.core._setup_stop_after = "commandline" + + # Don't let system command line leak into tests! + args.setdefault('script_args', ['install']) + + try: + return setuptools.setup(**args) + finally: + distutils.core._setup_stop_after = None + + +needs_bytecode = pytest.mark.skipif( + not hasattr(dep, 'get_module_constant'), + reason="bytecode support not available", +) + + +class TestDepends: + def testExtractConst(self): + if not hasattr(dep, 'extract_constant'): + # skip on non-bytecode platforms + return + + def f1(): + global x, y, z + x = "test" + y = z # pyright: ignore[reportUnboundVariable] # Explicitly testing for this runtime issue + + fc = f1.__code__ + + # unrecognized name + assert dep.extract_constant(fc, 'q', -1) is None + + # constant assigned + assert dep.extract_constant(fc, 'x', -1) == "test" + + # expression assigned + assert dep.extract_constant(fc, 'y', -1) == -1 + + # recognized name, not assigned + assert dep.extract_constant(fc, 'z', -1) is None + + def testFindModule(self): + with pytest.raises(ImportError): + dep.find_module('no-such.-thing') + with pytest.raises(ImportError): + dep.find_module('setuptools.non-existent') + f, _p, _i = dep.find_module('setuptools.tests') + f.close() + + @needs_bytecode + def testModuleExtract(self): + from json import __version__ + + assert dep.get_module_constant('json', '__version__') == __version__ + assert dep.get_module_constant('sys', 'version') == sys.version + assert ( + dep.get_module_constant('setuptools.tests.test_setuptools', '__doc__') + == __doc__ + ) + + @needs_bytecode + def testRequire(self): + req = Require('Json', '1.0.3', 'json') + + assert req.name == 'Json' + assert req.module == 'json' + assert req.requested_version == Version('1.0.3') + assert req.attribute == '__version__' + assert req.full_name() == 'Json-1.0.3' + + from json import __version__ + + assert str(req.get_version()) == __version__ + assert req.version_ok('1.0.9') + assert not req.version_ok('0.9.1') + assert not req.version_ok('unknown') + + assert req.is_present() + assert req.is_current() + + req = Require('Do-what-I-mean', '1.0', 'd-w-i-m') + assert not req.is_present() + assert not req.is_current() + + @needs_bytecode + def test_require_present(self): + # In #1896, this test was failing for months with the only + # complaint coming from test runners (not end users). + # TODO: Evaluate if this code is needed at all. + req = Require('Tests', None, 'tests', homepage="http://example.com") + assert req.format is None + assert req.attribute is None + assert req.requested_version is None + assert req.full_name() == 'Tests' + assert req.homepage == 'http://example.com' + + from setuptools.tests import __path__ + + paths = [os.path.dirname(p) for p in __path__] + assert req.is_present(paths) + assert req.is_current(paths) + + +class TestDistro: + def setup_method(self, method): + self.e1 = Extension('bar.ext', ['bar.c']) + self.e2 = Extension('c.y', ['y.c']) + + self.dist = makeSetup( + packages=['a', 'a.b', 'a.b.c', 'b', 'c'], + py_modules=['b.d', 'x'], + ext_modules=(self.e1, self.e2), + package_dir={}, + ) + + def testDistroType(self): + assert isinstance(self.dist, setuptools.dist.Distribution) + + def testExcludePackage(self): + self.dist.exclude_package('a') + assert self.dist.packages == ['b', 'c'] + + self.dist.exclude_package('b') + assert self.dist.packages == ['c'] + assert self.dist.py_modules == ['x'] + assert self.dist.ext_modules == [self.e1, self.e2] + + self.dist.exclude_package('c') + assert self.dist.packages == [] + assert self.dist.py_modules == ['x'] + assert self.dist.ext_modules == [self.e1] + + # test removals from unspecified options + makeSetup().exclude_package('x') + + def testIncludeExclude(self): + # remove an extension + self.dist.exclude(ext_modules=[self.e1]) + assert self.dist.ext_modules == [self.e2] + + # add it back in + self.dist.include(ext_modules=[self.e1]) + assert self.dist.ext_modules == [self.e2, self.e1] + + # should not add duplicate + self.dist.include(ext_modules=[self.e1]) + assert self.dist.ext_modules == [self.e2, self.e1] + + def testExcludePackages(self): + self.dist.exclude(packages=['c', 'b', 'a']) + assert self.dist.packages == [] + assert self.dist.py_modules == ['x'] + assert self.dist.ext_modules == [self.e1] + + def testEmpty(self): + dist = makeSetup() + dist.include(packages=['a'], py_modules=['b'], ext_modules=[self.e2]) + dist = makeSetup() + dist.exclude(packages=['a'], py_modules=['b'], ext_modules=[self.e2]) + + def testContents(self): + assert self.dist.has_contents_for('a') + self.dist.exclude_package('a') + assert not self.dist.has_contents_for('a') + + assert self.dist.has_contents_for('b') + self.dist.exclude_package('b') + assert not self.dist.has_contents_for('b') + + assert self.dist.has_contents_for('c') + self.dist.exclude_package('c') + assert not self.dist.has_contents_for('c') + + def testInvalidIncludeExclude(self): + with pytest.raises(DistutilsSetupError): + self.dist.include(nonexistent_option='x') + with pytest.raises(DistutilsSetupError): + self.dist.exclude(nonexistent_option='x') + with pytest.raises(DistutilsSetupError): + self.dist.include(packages={'x': 'y'}) + with pytest.raises(DistutilsSetupError): + self.dist.exclude(packages={'x': 'y'}) + with pytest.raises(DistutilsSetupError): + self.dist.include(ext_modules={'x': 'y'}) + with pytest.raises(DistutilsSetupError): + self.dist.exclude(ext_modules={'x': 'y'}) + + with pytest.raises(DistutilsSetupError): + self.dist.include(package_dir=['q']) + with pytest.raises(DistutilsSetupError): + self.dist.exclude(package_dir=['q']) + + +@pytest.fixture +def example_source(tmpdir): + tmpdir.mkdir('foo') + (tmpdir / 'foo/bar.py').write('') + (tmpdir / 'readme.txt').write('') + return tmpdir + + +def test_findall(example_source): + found = list(setuptools.findall(str(example_source))) + expected = ['readme.txt', 'foo/bar.py'] + expected = [example_source.join(fn) for fn in expected] + assert found == expected + + +def test_findall_curdir(example_source): + with example_source.as_cwd(): + found = list(setuptools.findall()) + expected = ['readme.txt', os.path.join('foo', 'bar.py')] + assert found == expected + + +@pytest.fixture +def can_symlink(tmpdir): + """ + Skip if cannot create a symbolic link + """ + link_fn = 'link' + target_fn = 'target' + try: + os.symlink(target_fn, link_fn) + except (OSError, NotImplementedError, AttributeError): + pytest.skip("Cannot create symbolic links") + os.remove(link_fn) + + +@pytest.mark.usefixtures("can_symlink") +def test_findall_missing_symlink(tmpdir): + with tmpdir.as_cwd(): + os.symlink('foo', 'bar') + found = list(setuptools.findall()) + assert found == [] + + +@pytest.mark.xfail(reason="unable to exclude tests; #4475 #3260") +def test_its_own_wheel_does_not_contain_tests(setuptools_wheel): + with ZipFile(setuptools_wheel) as zipfile: + contents = [f.replace(os.sep, '/') for f in zipfile.namelist()] + + for member in contents: + assert '/tests/' not in member + + +def test_wheel_includes_cli_scripts(setuptools_wheel): + with ZipFile(setuptools_wheel) as zipfile: + contents = [f.replace(os.sep, '/') for f in zipfile.namelist()] + + assert any('cli-64.exe' in member for member in contents) + + +def test_wheel_includes_vendored_metadata(setuptools_wheel): + with ZipFile(setuptools_wheel) as zipfile: + contents = [f.replace(os.sep, '/') for f in zipfile.namelist()] + + assert any( + re.search(r'_vendor/.*\.dist-info/METADATA', member) for member in contents + ) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_shutil_wrapper.py b/lib/python3.12/site-packages/setuptools/tests/test_shutil_wrapper.py new file mode 100644 index 0000000000000000000000000000000000000000..74ff7e9a896328a3d57ca3639658e3b9d538585f --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_shutil_wrapper.py @@ -0,0 +1,23 @@ +import stat +import sys +from unittest.mock import Mock + +from setuptools import _shutil + + +def test_rmtree_readonly(monkeypatch, tmp_path): + """Verify onerr works as expected""" + + tmp_dir = tmp_path / "with_readonly" + tmp_dir.mkdir() + some_file = tmp_dir.joinpath("file.txt") + some_file.touch() + some_file.chmod(stat.S_IREAD) + + expected_count = 1 if sys.platform.startswith("win") else 0 + chmod_fn = Mock(wraps=_shutil.attempt_chmod_verbose) + monkeypatch.setattr(_shutil, "attempt_chmod_verbose", chmod_fn) + + _shutil.rmtree(tmp_dir) + assert chmod_fn.call_count == expected_count + assert not tmp_dir.is_dir() diff --git a/lib/python3.12/site-packages/setuptools/tests/test_unicode_utils.py b/lib/python3.12/site-packages/setuptools/tests/test_unicode_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..a24a9bd5305d1de7c1c925466bb7d222c85864a7 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_unicode_utils.py @@ -0,0 +1,10 @@ +from setuptools import unicode_utils + + +def test_filesys_decode_fs_encoding_is_None(monkeypatch): + """ + Test filesys_decode does not raise TypeError when + getfilesystemencoding returns None. + """ + monkeypatch.setattr('sys.getfilesystemencoding', lambda: None) + unicode_utils.filesys_decode(b'test') diff --git a/lib/python3.12/site-packages/setuptools/tests/test_virtualenv.py b/lib/python3.12/site-packages/setuptools/tests/test_virtualenv.py new file mode 100644 index 0000000000000000000000000000000000000000..b02949baf9cef8eb4df9c697add28f79a93b64d4 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_virtualenv.py @@ -0,0 +1,113 @@ +import os +import subprocess +import sys +from urllib.error import URLError +from urllib.request import urlopen + +import pytest + + +@pytest.fixture(autouse=True) +def pytest_virtualenv_works(venv): + """ + pytest_virtualenv may not work. if it doesn't, skip these + tests. See #1284. + """ + venv_prefix = venv.run(["python", "-c", "import sys; print(sys.prefix)"]).strip() + if venv_prefix == sys.prefix: + pytest.skip("virtualenv is broken (see pypa/setuptools#1284)") + + +def test_clean_env_install(venv_without_setuptools, setuptools_wheel): + """ + Check setuptools can be installed in a clean environment. + """ + cmd = ["python", "-m", "pip", "install", str(setuptools_wheel)] + venv_without_setuptools.run(cmd) + + +def access_pypi(): + # Detect if tests are being run without connectivity + if not os.environ.get('NETWORK_REQUIRED', False): # pragma: nocover + try: + urlopen('https://pypi.org', timeout=1) + except URLError: + # No network, disable most of these tests + return False + + return True + + +@pytest.mark.skipif( + 'platform.python_implementation() == "PyPy"', + reason="https://github.com/pypa/setuptools/pull/2865#issuecomment-965834995", +) +@pytest.mark.skipif(not access_pypi(), reason="no network") +# ^-- Even when it is not necessary to install a different version of `pip` +# the build process will still try to download `wheel`, see #3147 and #2986. +@pytest.mark.parametrize( + 'pip_version', + [ + None, + pytest.param( + 'pip<20.1', + marks=pytest.mark.xfail( + 'sys.version_info >= (3, 12)', + reason="pip 23.1.2 required for Python 3.12 and later", + ), + ), + pytest.param( + 'pip<21', + marks=pytest.mark.xfail( + 'sys.version_info >= (3, 12)', + reason="pip 23.1.2 required for Python 3.12 and later", + ), + ), + pytest.param( + 'pip<22', + marks=pytest.mark.xfail( + 'sys.version_info >= (3, 12)', + reason="pip 23.1.2 required for Python 3.12 and later", + ), + ), + pytest.param( + 'pip<23', + marks=pytest.mark.xfail( + 'sys.version_info >= (3, 12)', + reason="pip 23.1.2 required for Python 3.12 and later", + ), + ), + pytest.param( + 'https://github.com/pypa/pip/archive/main.zip', + marks=pytest.mark.xfail(reason='#2975'), + ), + ], +) +def test_pip_upgrade_from_source( + pip_version, venv_without_setuptools, setuptools_wheel, setuptools_sdist +): + """ + Check pip can upgrade setuptools from source. + """ + # Install pip/wheel, in a venv without setuptools (as it + # should not be needed for bootstrapping from source) + venv = venv_without_setuptools + venv.run(["pip", "install", "-U", "wheel"]) + if pip_version is not None: + venv.run(["python", "-m", "pip", "install", "-U", pip_version, "--retries=1"]) + with pytest.raises(subprocess.CalledProcessError): + # Meta-test to make sure setuptools is not installed + venv.run(["python", "-c", "import setuptools"]) + + # Then install from wheel. + venv.run(["pip", "install", str(setuptools_wheel)]) + # And finally try to upgrade from source. + venv.run(["pip", "install", "--no-cache-dir", "--upgrade", str(setuptools_sdist)]) + + +def test_no_missing_dependencies(bare_venv, request): + """ + Quick and dirty test to ensure all external dependencies are vendored. + """ + setuptools_dir = request.config.rootdir + bare_venv.run(['python', 'setup.py', '--help'], cwd=setuptools_dir) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_warnings.py b/lib/python3.12/site-packages/setuptools/tests/test_warnings.py new file mode 100644 index 0000000000000000000000000000000000000000..41193d4f717344546f8c70ad18b268a04740129b --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_warnings.py @@ -0,0 +1,106 @@ +from inspect import cleandoc + +import pytest + +from setuptools.warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning + +_EXAMPLES = { + "default": dict( + args=("Hello {x}", "\n\t{target} {v:.1f}"), + kwargs={"x": 5, "v": 3, "target": "World"}, + expected=""" + Hello 5 + !! + + ******************************************************************************** + World 3.0 + ******************************************************************************** + + !! + """, + ), + "futue_due_date": dict( + args=("Summary", "Lorem ipsum"), + kwargs={"due_date": (9999, 11, 22)}, + expected=""" + Summary + !! + + ******************************************************************************** + Lorem ipsum + + By 9999-Nov-22, you need to update your project and remove deprecated calls + or your builds will no longer be supported. + ******************************************************************************** + + !! + """, + ), + "past_due_date_with_docs": dict( + args=("Summary", "Lorem ipsum"), + kwargs={"due_date": (2000, 11, 22), "see_docs": "some_page.html"}, + expected=""" + Summary + !! + + ******************************************************************************** + Lorem ipsum + + This deprecation is overdue, please update your project and remove deprecated + calls to avoid build errors in the future. + + See https://setuptools.pypa.io/en/latest/some_page.html for details. + ******************************************************************************** + + !! + """, + ), +} + + +@pytest.mark.parametrize("example_name", _EXAMPLES.keys()) +def test_formatting(monkeypatch, example_name): + """ + It should automatically handle indentation, interpolation and things like due date. + """ + args = _EXAMPLES[example_name]["args"] + kwargs = _EXAMPLES[example_name]["kwargs"] + expected = _EXAMPLES[example_name]["expected"] + + monkeypatch.setenv("SETUPTOOLS_ENFORCE_DEPRECATION", "false") + with pytest.warns(SetuptoolsWarning) as warn_info: + SetuptoolsWarning.emit(*args, **kwargs) + assert _get_message(warn_info) == cleandoc(expected) + + +def test_due_date_enforcement(monkeypatch): + class _MyDeprecation(SetuptoolsDeprecationWarning): + _SUMMARY = "Summary" + _DETAILS = "Lorem ipsum" + _DUE_DATE = (2000, 11, 22) + _SEE_DOCS = "some_page.html" + + monkeypatch.setenv("SETUPTOOLS_ENFORCE_DEPRECATION", "true") + with pytest.raises(SetuptoolsDeprecationWarning) as exc_info: + _MyDeprecation.emit() + + expected = """ + Summary + !! + + ******************************************************************************** + Lorem ipsum + + This deprecation is overdue, please update your project and remove deprecated + calls to avoid build errors in the future. + + See https://setuptools.pypa.io/en/latest/some_page.html for details. + ******************************************************************************** + + !! + """ + assert str(exc_info.value) == cleandoc(expected) + + +def _get_message(warn_info): + return next(warn.message.args[0] for warn in warn_info) diff --git a/lib/python3.12/site-packages/setuptools/tests/test_wheel.py b/lib/python3.12/site-packages/setuptools/tests/test_wheel.py new file mode 100644 index 0000000000000000000000000000000000000000..c3b215a4744c8118f2667c8a6cc1e1a2ebdce138 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_wheel.py @@ -0,0 +1,690 @@ +"""wheel tests""" + +from __future__ import annotations + +import contextlib +import glob +import inspect +import os +import pathlib +import stat +import subprocess +import sys +import sysconfig +import zipfile +from typing import Any + +import pytest +from jaraco import path +from packaging.tags import parse_tag + +from setuptools._importlib import metadata +from setuptools.wheel import Wheel + +from .contexts import tempdir +from .textwrap import DALS + +from distutils.sysconfig import get_config_var +from distutils.util import get_platform + +WHEEL_INFO_TESTS = ( + ('invalid.whl', ValueError), + ( + 'simplewheel-2.0-1-py2.py3-none-any.whl', + { + 'project_name': 'simplewheel', + 'version': '2.0', + 'build': '1', + 'py_version': 'py2.py3', + 'abi': 'none', + 'platform': 'any', + }, + ), + ( + 'simple.dist-0.1-py2.py3-none-any.whl', + { + 'project_name': 'simple.dist', + 'version': '0.1', + 'build': None, + 'py_version': 'py2.py3', + 'abi': 'none', + 'platform': 'any', + }, + ), + ( + 'example_pkg_a-1-py3-none-any.whl', + { + 'project_name': 'example_pkg_a', + 'version': '1', + 'build': None, + 'py_version': 'py3', + 'abi': 'none', + 'platform': 'any', + }, + ), + ( + 'PyQt5-5.9-5.9.1-cp35.cp36.cp37-abi3-manylinux1_x86_64.whl', + { + 'project_name': 'PyQt5', + 'version': '5.9', + 'build': '5.9.1', + 'py_version': 'cp35.cp36.cp37', + 'abi': 'abi3', + 'platform': 'manylinux1_x86_64', + }, + ), +) + + +@pytest.mark.parametrize( + ('filename', 'info'), WHEEL_INFO_TESTS, ids=[t[0] for t in WHEEL_INFO_TESTS] +) +def test_wheel_info(filename, info): + if inspect.isclass(info): + with pytest.raises(info): + Wheel(filename) + return + w = Wheel(filename) + assert {k: getattr(w, k) for k in info.keys()} == info + + +@contextlib.contextmanager +def build_wheel(extra_file_defs=None, **kwargs): + file_defs = { + 'setup.py': ( + DALS( + """ + # -*- coding: utf-8 -*- + from setuptools import setup + import setuptools + setup(**%r) + """ + ) + % kwargs + ).encode('utf-8'), + } + if extra_file_defs: + file_defs.update(extra_file_defs) + with tempdir() as source_dir: + path.build(file_defs, source_dir) + subprocess.check_call( + (sys.executable, 'setup.py', '-q', 'bdist_wheel'), cwd=source_dir + ) + yield glob.glob(os.path.join(source_dir, 'dist', '*.whl'))[0] + + +def tree_set(root): + return { + os.path.join(os.path.relpath(dirpath, root), filename) + for dirpath, dirnames, filenames in os.walk(root) + for filename in filenames + } + + +def flatten_tree(tree): + """Flatten nested dicts and lists into a full list of paths""" + output = set() + for node, contents in tree.items(): + if isinstance(contents, dict): + contents = flatten_tree(contents) + + for elem in contents: + if isinstance(elem, dict): + output |= {os.path.join(node, val) for val in flatten_tree(elem)} + else: + output.add(os.path.join(node, elem)) + return output + + +def format_install_tree(tree): + return { + x.format( + py_version=sysconfig.get_python_version(), + platform=get_platform(), + shlib_ext=get_config_var('EXT_SUFFIX') or get_config_var('SO'), + ) + for x in tree + } + + +def _check_wheel_install( + filename, install_dir, install_tree_includes, project_name, version, requires_txt +): + w = Wheel(filename) + egg_path = os.path.join(install_dir, w.egg_name()) + w.install_as_egg(egg_path) + if install_tree_includes is not None: + install_tree = format_install_tree(install_tree_includes) + exp = tree_set(install_dir) + assert install_tree.issubset(exp), install_tree - exp + + (dist,) = metadata.Distribution.discover(path=[egg_path]) + + # pyright is nitpicky; fine to assume dist.metadata.__getitem__ will fail or return None + # (https://github.com/pypa/setuptools/pull/5006#issuecomment-2894774288) + assert dist.metadata['Name'] == project_name # pyright: ignore # noqa: PGH003 + assert dist.metadata['Version'] == version # pyright: ignore # noqa: PGH003 + assert dist.read_text('requires.txt') == requires_txt + + +class Record: + def __init__(self, id, **kwargs) -> None: + self._id = id + self._fields = kwargs + + def __repr__(self) -> str: + return f'{self._id}(**{self._fields!r})' + + +# Using Any to avoid possible type union issues later in test +# making a TypedDict is not worth in a test and anonymous/inline TypedDict are experimental +# https://github.com/python/mypy/issues/9884 +WHEEL_INSTALL_TESTS: tuple[dict[str, Any], ...] = ( + dict( + id='basic', + file_defs={'foo': {'__init__.py': ''}}, + setup_kwargs=dict( + packages=['foo'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': ['PKG-INFO', 'RECORD', 'WHEEL', 'top_level.txt'], + 'foo': ['__init__.py'], + } + }), + ), + dict( + id='utf-8', + setup_kwargs=dict( + description='Description accentuée', + ), + ), + dict( + id='data', + file_defs={ + 'data.txt': DALS( + """ + Some data... + """ + ), + }, + setup_kwargs=dict( + data_files=[('data_dir', ['data.txt'])], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': ['PKG-INFO', 'RECORD', 'WHEEL', 'top_level.txt'], + 'data_dir': ['data.txt'], + } + }), + ), + dict( + id='extension', + file_defs={ + 'extension.c': DALS( + """ + #include "Python.h" + + #if PY_MAJOR_VERSION >= 3 + + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "extension", + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL + }; + + #define INITERROR return NULL + + PyMODINIT_FUNC PyInit_extension(void) + + #else + + #define INITERROR return + + void initextension(void) + + #endif + { + #if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); + #else + PyObject *module = Py_InitModule("extension", NULL); + #endif + if (module == NULL) + INITERROR; + #if PY_MAJOR_VERSION >= 3 + return module; + #endif + } + """ + ), + }, + setup_kwargs=dict( + ext_modules=[ + Record( + 'setuptools.Extension', name='extension', sources=['extension.c'] + ) + ], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}-{platform}.egg': [ + 'extension{shlib_ext}', + { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'top_level.txt', + ] + }, + ] + }), + ), + dict( + id='header', + file_defs={ + 'header.h': DALS( + """ + """ + ), + }, + setup_kwargs=dict( + headers=['header.h'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': [ + 'header.h', + { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'top_level.txt', + ] + }, + ] + }), + ), + dict( + id='script', + file_defs={ + 'script.py': DALS( + """ + #/usr/bin/python + print('hello world!') + """ + ), + 'script.sh': DALS( + """ + #/bin/sh + echo 'hello world!' + """ + ), + }, + setup_kwargs=dict( + scripts=['script.py', 'script.sh'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'top_level.txt', + {'scripts': ['script.py', 'script.sh']}, + ] + } + }), + ), + dict( + id='requires1', + install_requires='foobar==2.0', + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'requires.txt', + 'top_level.txt', + ] + } + }), + requires_txt=DALS( + """ + foobar==2.0 + """ + ), + ), + dict( + id='requires2', + install_requires=f""" + bar + foo<=2.0; {sys.platform!r} in sys_platform + """, + requires_txt=DALS( + """ + bar + foo<=2.0 + """ + ), + ), + dict( + id='requires3', + install_requires=f""" + bar; {sys.platform!r} != sys_platform + """, + ), + dict( + id='requires4', + install_requires=""" + foo + """, + extras_require={ + 'extra': 'foobar>3', + }, + requires_txt=DALS( + """ + foo + + [extra] + foobar>3 + """ + ), + ), + dict( + id='requires5', + extras_require={ + 'extra': f'foobar; {sys.platform!r} != sys_platform', + }, + requires_txt='\n' + + DALS( + """ + [extra] + """ + ), + ), + dict( + id='requires_ensure_order', + install_requires=""" + foo + bar + baz + qux + """, + extras_require={ + 'extra': """ + foobar>3 + barbaz>4 + bazqux>5 + quxzap>6 + """, + }, + requires_txt=DALS( + """ + foo + bar + baz + qux + + [extra] + foobar>3 + barbaz>4 + bazqux>5 + quxzap>6 + """ + ), + ), + dict( + id='namespace_package', + file_defs={ + 'foo': { + 'bar': {'__init__.py': ''}, + }, + }, + setup_kwargs=dict( + namespace_packages=['foo'], + packages=['foo.bar'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': [ + 'foo-1.0-py{py_version}-nspkg.pth', + { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'namespace_packages.txt', + 'top_level.txt', + ] + }, + { + 'foo': [ + '__init__.py', + {'bar': ['__init__.py']}, + ] + }, + ] + }), + ), + dict( + id='empty_namespace_package', + file_defs={ + 'foobar': { + '__init__.py': ( + "__import__('pkg_resources').declare_namespace(__name__)" + ) + }, + }, + setup_kwargs=dict( + namespace_packages=['foobar'], + packages=['foobar'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': [ + 'foo-1.0-py{py_version}-nspkg.pth', + { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'namespace_packages.txt', + 'top_level.txt', + ] + }, + { + 'foobar': [ + '__init__.py', + ] + }, + ] + }), + ), + dict( + id='data_in_package', + file_defs={ + 'foo': { + '__init__.py': '', + 'data_dir': { + 'data.txt': DALS( + """ + Some data... + """ + ), + }, + } + }, + setup_kwargs=dict( + packages=['foo'], + data_files=[('foo/data_dir', ['foo/data_dir/data.txt'])], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'top_level.txt', + ], + 'foo': [ + '__init__.py', + { + 'data_dir': [ + 'data.txt', + ] + }, + ], + } + }), + ), +) + + +@pytest.mark.parametrize( + 'params', + WHEEL_INSTALL_TESTS, + ids=[params['id'] for params in WHEEL_INSTALL_TESTS], +) +def test_wheel_install(params): + project_name = params.get('name', 'foo') + version = params.get('version', '1.0') + install_requires = params.get('install_requires', []) + extras_require = params.get('extras_require', {}) + requires_txt = params.get('requires_txt', None) + install_tree = params.get('install_tree') + file_defs = params.get('file_defs', {}) + setup_kwargs = params.get('setup_kwargs', {}) + with ( + build_wheel( + name=project_name, + version=version, + install_requires=install_requires, + extras_require=extras_require, + extra_file_defs=file_defs, + **setup_kwargs, + ) as filename, + tempdir() as install_dir, + ): + _check_wheel_install( + filename, install_dir, install_tree, project_name, version, requires_txt + ) + + +def test_wheel_no_dist_dir(): + project_name = 'nodistinfo' + version = '1.0' + wheel_name = f'{project_name}-{version}-py2.py3-none-any.whl' + with tempdir() as source_dir: + wheel_path = os.path.join(source_dir, wheel_name) + # create an empty zip file + zipfile.ZipFile(wheel_path, 'w').close() + with tempdir() as install_dir: + with pytest.raises(ValueError): + _check_wheel_install( + wheel_path, install_dir, None, project_name, version, None + ) + + +def test_wheel_is_compatible(monkeypatch): + def sys_tags(): + return { + (t.interpreter, t.abi, t.platform) + for t in parse_tag('cp36-cp36m-manylinux1_x86_64') + } + + monkeypatch.setattr('setuptools.wheel._get_supported_tags', sys_tags) + assert Wheel('onnxruntime-0.1.2-cp36-cp36m-manylinux1_x86_64.whl').is_compatible() + + +def test_wheel_mode(): + @contextlib.contextmanager + def build_wheel(extra_file_defs=None, **kwargs): + file_defs = { + 'setup.py': ( + DALS( + """ + # -*- coding: utf-8 -*- + from setuptools import setup + import setuptools + setup(**%r) + """ + ) + % kwargs + ).encode('utf-8'), + } + if extra_file_defs: + file_defs.update(extra_file_defs) + with tempdir() as source_dir: + path.build(file_defs, source_dir) + runsh = pathlib.Path(source_dir) / "script.sh" + os.chmod(runsh, 0o777) + subprocess.check_call( + (sys.executable, 'setup.py', '-q', 'bdist_wheel'), cwd=source_dir + ) + yield glob.glob(os.path.join(source_dir, 'dist', '*.whl'))[0] + + params = dict( + id='script', + file_defs={ + 'script.py': DALS( + """ + #/usr/bin/python + print('hello world!') + """ + ), + 'script.sh': DALS( + """ + #/bin/sh + echo 'hello world!' + """ + ), + }, + setup_kwargs=dict( + scripts=['script.py', 'script.sh'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'top_level.txt', + {'scripts': ['script.py', 'script.sh']}, + ] + } + }), + ) + + project_name = params.get('name', 'foo') + version = params.get('version', '1.0') + install_tree = params.get('install_tree') + file_defs = params.get('file_defs', {}) + setup_kwargs = params.get('setup_kwargs', {}) + + with ( + build_wheel( + name=project_name, + version=version, + install_requires=[], + extras_require={}, + extra_file_defs=file_defs, + **setup_kwargs, + ) as filename, + tempdir() as install_dir, + ): + _check_wheel_install( + filename, install_dir, install_tree, project_name, version, None + ) + w = Wheel(filename) + base = pathlib.Path(install_dir) / w.egg_name() + script_sh = base / "EGG-INFO" / "scripts" / "script.sh" + assert script_sh.exists() + if sys.platform != 'win32': + # Editable file mode has no effect on Windows + assert oct(stat.S_IMODE(script_sh.stat().st_mode)) == "0o777" diff --git a/lib/python3.12/site-packages/setuptools/tests/test_windows_wrappers.py b/lib/python3.12/site-packages/setuptools/tests/test_windows_wrappers.py new file mode 100644 index 0000000000000000000000000000000000000000..4f990eb1c3cc67c61ea17f791430651e91040f18 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/test_windows_wrappers.py @@ -0,0 +1,258 @@ +""" +Python Script Wrapper for Windows +================================= + +setuptools includes wrappers for Python scripts that allows them to be +executed like regular windows programs. There are 2 wrappers, one +for command-line programs, cli.exe, and one for graphical programs, +gui.exe. These programs are almost identical, function pretty much +the same way, and are generated from the same source file. The +wrapper programs are used by copying them to the directory containing +the script they are to wrap and with the same name as the script they +are to wrap. +""" + +import pathlib +import platform +import subprocess +import sys +import textwrap + +import pytest + +from setuptools._importlib import resources + +pytestmark = pytest.mark.skipif(sys.platform != 'win32', reason="Windows only") + + +class WrapperTester: + @classmethod + def prep_script(cls, template): + python_exe = subprocess.list2cmdline([sys.executable]) + return template % locals() + + @classmethod + def create_script(cls, tmpdir): + """ + Create a simple script, foo-script.py + + Note that the script starts with a Unix-style '#!' line saying which + Python executable to run. The wrapper will use this line to find the + correct Python executable. + """ + + script = cls.prep_script(cls.script_tmpl) + + with (tmpdir / cls.script_name).open('w') as f: + f.write(script) + + # also copy cli.exe to the sample directory + with (tmpdir / cls.wrapper_name).open('wb') as f: + w = resources.files('setuptools').joinpath(cls.wrapper_source).read_bytes() + f.write(w) + + +def win_launcher_exe(prefix): + """A simple routine to select launcher script based on platform.""" + assert prefix in ('cli', 'gui') + if platform.machine() == "ARM64": + return f"{prefix}-arm64.exe" + else: + return f"{prefix}-32.exe" + + +class TestCLI(WrapperTester): + script_name = 'foo-script.py' + wrapper_name = 'foo.exe' + wrapper_source = win_launcher_exe('cli') + + script_tmpl = textwrap.dedent( + """ + #!%(python_exe)s + import sys + input = repr(sys.stdin.read()) + print(sys.argv[0][-14:]) + print(sys.argv[1:]) + print(input) + if __debug__: + print('non-optimized') + """ + ).lstrip() + + def test_basic(self, tmpdir): + """ + When the copy of cli.exe, foo.exe in this example, runs, it examines + the path name it was run with and computes a Python script path name + by removing the '.exe' suffix and adding the '-script.py' suffix. (For + GUI programs, the suffix '-script.pyw' is added.) This is why we + named out script the way we did. Now we can run out script by running + the wrapper: + + This example was a little pathological in that it exercised windows + (MS C runtime) quoting rules: + + - Strings containing spaces are surrounded by double quotes. + + - Double quotes in strings need to be escaped by preceding them with + back slashes. + + - One or more backslashes preceding double quotes need to be escaped + by preceding each of them with back slashes. + """ + self.create_script(tmpdir) + cmd = [ + str(tmpdir / 'foo.exe'), + 'arg1', + 'arg 2', + 'arg "2\\"', + 'arg 4\\', + 'arg5 a\\\\b', + ] + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + text=True, + encoding="utf-8", + ) + stdout, _stderr = proc.communicate('hello\nworld\n') + actual = stdout.replace('\r\n', '\n') + expected = textwrap.dedent( + r""" + \foo-script.py + ['arg1', 'arg 2', 'arg "2\\"', 'arg 4\\', 'arg5 a\\\\b'] + 'hello\nworld\n' + non-optimized + """ + ).lstrip() + assert actual == expected + + def test_symlink(self, tmpdir): + """ + Ensure that symlink for the foo.exe is working correctly. + """ + script_dir = tmpdir / "script_dir" + script_dir.mkdir() + self.create_script(script_dir) + symlink = pathlib.Path(tmpdir / "foo.exe") + symlink.symlink_to(script_dir / "foo.exe") + + cmd = [ + str(tmpdir / 'foo.exe'), + 'arg1', + 'arg 2', + 'arg "2\\"', + 'arg 4\\', + 'arg5 a\\\\b', + ] + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + text=True, + encoding="utf-8", + ) + stdout, _stderr = proc.communicate('hello\nworld\n') + actual = stdout.replace('\r\n', '\n') + expected = textwrap.dedent( + r""" + \foo-script.py + ['arg1', 'arg 2', 'arg "2\\"', 'arg 4\\', 'arg5 a\\\\b'] + 'hello\nworld\n' + non-optimized + """ + ).lstrip() + assert actual == expected + + def test_with_options(self, tmpdir): + """ + Specifying Python Command-line Options + -------------------------------------- + + You can specify a single argument on the '#!' line. This can be used + to specify Python options like -O, to run in optimized mode or -i + to start the interactive interpreter. You can combine multiple + options as usual. For example, to run in optimized mode and + enter the interpreter after running the script, you could use -Oi: + """ + self.create_script(tmpdir) + tmpl = textwrap.dedent( + """ + #!%(python_exe)s -Oi + import sys + input = repr(sys.stdin.read()) + print(sys.argv[0][-14:]) + print(sys.argv[1:]) + print(input) + if __debug__: + print('non-optimized') + sys.ps1 = '---' + """ + ).lstrip() + with (tmpdir / 'foo-script.py').open('w') as f: + f.write(self.prep_script(tmpl)) + cmd = [str(tmpdir / 'foo.exe')] + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + encoding="utf-8", + ) + stdout, _stderr = proc.communicate() + actual = stdout.replace('\r\n', '\n') + expected = textwrap.dedent( + r""" + \foo-script.py + [] + '' + --- + """ + ).lstrip() + assert actual == expected + + +class TestGUI(WrapperTester): + """ + Testing the GUI Version + ----------------------- + """ + + script_name = 'bar-script.pyw' + wrapper_source = win_launcher_exe('gui') + wrapper_name = 'bar.exe' + + script_tmpl = textwrap.dedent( + """ + #!%(python_exe)s + import sys + f = open(sys.argv[1], 'wb') + bytes_written = f.write(repr(sys.argv[2]).encode('utf-8')) + f.close() + """ + ).strip() + + def test_basic(self, tmpdir): + """Test the GUI version with the simple script, bar-script.py""" + self.create_script(tmpdir) + + cmd = [ + str(tmpdir / 'bar.exe'), + str(tmpdir / 'test_output.txt'), + 'Test Argument', + ] + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + encoding="utf-8", + ) + stdout, stderr = proc.communicate() + assert not stdout + assert not stderr + with (tmpdir / 'test_output.txt').open('rb') as f_out: + actual = f_out.read().decode('ascii') + assert actual == repr('Test Argument') diff --git a/lib/python3.12/site-packages/setuptools/tests/text.py b/lib/python3.12/site-packages/setuptools/tests/text.py new file mode 100644 index 0000000000000000000000000000000000000000..e05cc633ede9e5ce4f74b66a7bf76327c2000caa --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/text.py @@ -0,0 +1,4 @@ +class Filenames: + unicode = 'smörbröd.py' + latin_1 = unicode.encode('latin-1') + utf_8 = unicode.encode('utf-8') diff --git a/lib/python3.12/site-packages/setuptools/tests/textwrap.py b/lib/python3.12/site-packages/setuptools/tests/textwrap.py new file mode 100644 index 0000000000000000000000000000000000000000..5e39618dca4ad6c3f0d4c8cb20af59ab85fb0eba --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/tests/textwrap.py @@ -0,0 +1,6 @@ +import textwrap + + +def DALS(s): + "dedent and left-strip" + return textwrap.dedent(s).lstrip() diff --git a/lib/python3.12/site-packages/setuptools/unicode_utils.py b/lib/python3.12/site-packages/setuptools/unicode_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..f502f5b089619eafd28e2c7a61967e34e16920e5 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/unicode_utils.py @@ -0,0 +1,102 @@ +import sys +import unicodedata +from configparser import RawConfigParser + +from .compat import py39 +from .warnings import SetuptoolsDeprecationWarning + + +# HFS Plus uses decomposed UTF-8 +def decompose(path): + if isinstance(path, str): + return unicodedata.normalize('NFD', path) + try: + path = path.decode('utf-8') + path = unicodedata.normalize('NFD', path) + path = path.encode('utf-8') + except UnicodeError: + pass # Not UTF-8 + return path + + +def filesys_decode(path): + """ + Ensure that the given path is decoded, + ``None`` when no expected encoding works + """ + + if isinstance(path, str): + return path + + fs_enc = sys.getfilesystemencoding() or 'utf-8' + candidates = fs_enc, 'utf-8' + + for enc in candidates: + try: + return path.decode(enc) + except UnicodeDecodeError: + continue + + return None + + +def try_encode(string, enc): + "turn unicode encoding into a functional routine" + try: + return string.encode(enc) + except UnicodeEncodeError: + return None + + +def _read_utf8_with_fallback(file: str, fallback_encoding=py39.LOCALE_ENCODING) -> str: + """ + First try to read the file with UTF-8, if there is an error fallback to a + different encoding ("locale" by default). Returns the content of the file. + Also useful when reading files that might have been produced by an older version of + setuptools. + """ + try: + with open(file, "r", encoding="utf-8") as f: + return f.read() + except UnicodeDecodeError: # pragma: no cover + _Utf8EncodingNeeded.emit(file=file, fallback_encoding=fallback_encoding) + with open(file, "r", encoding=fallback_encoding) as f: + return f.read() + + +def _cfg_read_utf8_with_fallback( + cfg: RawConfigParser, file: str, fallback_encoding=py39.LOCALE_ENCODING +) -> None: + """Same idea as :func:`_read_utf8_with_fallback`, but for the + :meth:`RawConfigParser.read` method. + + This method may call ``cfg.clear()``. + """ + try: + cfg.read(file, encoding="utf-8") + except UnicodeDecodeError: # pragma: no cover + _Utf8EncodingNeeded.emit(file=file, fallback_encoding=fallback_encoding) + cfg.clear() + cfg.read(file, encoding=fallback_encoding) + + +class _Utf8EncodingNeeded(SetuptoolsDeprecationWarning): + _SUMMARY = """ + `encoding="utf-8"` fails with {file!r}, trying `encoding={fallback_encoding!r}`. + """ + + _DETAILS = """ + Fallback behavior for UTF-8 is considered **deprecated** and future versions of + `setuptools` may not implement it. + + Please encode {file!r} with "utf-8" to ensure future builds will succeed. + + If this file was produced by `setuptools` itself, cleaning up the cached files + and re-building/re-installing the package with a newer version of `setuptools` + (e.g. by updating `build-system.requires` in its `pyproject.toml`) + might solve the problem. + """ + # TODO: Add a deadline? + # Will we be able to remove this? + # The question comes to mind mainly because of sdists that have been produced + # by old versions of setuptools and published to PyPI... diff --git a/lib/python3.12/site-packages/setuptools/version.py b/lib/python3.12/site-packages/setuptools/version.py new file mode 100644 index 0000000000000000000000000000000000000000..ec253c414474677d3a5977511cfe901bfb786740 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/version.py @@ -0,0 +1,6 @@ +from ._importlib import metadata + +try: + __version__ = metadata.version('setuptools') or '0.dev0+unknown' +except Exception: + __version__ = '0.dev0+unknown' diff --git a/lib/python3.12/site-packages/setuptools/warnings.py b/lib/python3.12/site-packages/setuptools/warnings.py new file mode 100644 index 0000000000000000000000000000000000000000..96467787c237846bfbacf2d44eb833be0a88b633 --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/warnings.py @@ -0,0 +1,110 @@ +"""Provide basic warnings used by setuptools modules. + +Using custom classes (other than ``UserWarning``) allow users to set +``PYTHONWARNINGS`` filters to run tests and prepare for upcoming changes in +setuptools. +""" + +from __future__ import annotations + +import os +import warnings +from datetime import date +from inspect import cleandoc +from textwrap import indent +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +_DueDate: TypeAlias = tuple[int, int, int] # time tuple +_INDENT = 8 * " " +_TEMPLATE = f"""{80 * '*'}\n{{details}}\n{80 * '*'}""" + + +class SetuptoolsWarning(UserWarning): + """Base class in ``setuptools`` warning hierarchy.""" + + @classmethod + def emit( + cls, + summary: str | None = None, + details: str | None = None, + due_date: _DueDate | None = None, + see_docs: str | None = None, + see_url: str | None = None, + stacklevel: int = 2, + **kwargs, + ) -> None: + """Private: reserved for ``setuptools`` internal use only""" + # Default values: + summary_ = summary or getattr(cls, "_SUMMARY", None) or "" + details_ = details or getattr(cls, "_DETAILS", None) or "" + due_date = due_date or getattr(cls, "_DUE_DATE", None) + docs_ref = see_docs or getattr(cls, "_SEE_DOCS", None) + docs_url = docs_ref and f"https://setuptools.pypa.io/en/latest/{docs_ref}" + see_url = see_url or getattr(cls, "_SEE_URL", None) + due = date(*due_date) if due_date else None + + text = cls._format(summary_, details_, due, see_url or docs_url, kwargs) + if due and due < date.today() and _should_enforce(): + raise cls(text) + warnings.warn(text, cls, stacklevel=stacklevel + 1) + + @classmethod + def _format( + cls, + summary: str, + details: str, + due_date: date | None = None, + see_url: str | None = None, + format_args: dict | None = None, + ) -> str: + """Private: reserved for ``setuptools`` internal use only""" + today = date.today() + summary = cleandoc(summary).format_map(format_args or {}) + possible_parts = [ + cleandoc(details).format_map(format_args or {}), + ( + f"\nBy {due_date:%Y-%b-%d}, you need to update your project and remove " + "deprecated calls\nor your builds will no longer be supported." + if due_date and due_date > today + else None + ), + ( + "\nThis deprecation is overdue, please update your project and remove " + "deprecated\ncalls to avoid build errors in the future." + if due_date and due_date < today + else None + ), + (f"\nSee {see_url} for details." if see_url else None), + ] + parts = [x for x in possible_parts if x] + if parts: + body = indent(_TEMPLATE.format(details="\n".join(parts)), _INDENT) + return "\n".join([summary, "!!\n", body, "\n!!"]) + return summary + + +class InformationOnly(SetuptoolsWarning): + """Currently there is no clear way of displaying messages to the users + that use the setuptools backend directly via ``pip``. + The only thing that might work is a warning, although it is not the + most appropriate tool for the job... + + See pypa/packaging-problems#558. + """ + + +class SetuptoolsDeprecationWarning(SetuptoolsWarning): + """ + Base class for warning deprecations in ``setuptools`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ + + +def _should_enforce(): + enforce = os.getenv("SETUPTOOLS_ENFORCE_DEPRECATION", "false").lower() + return enforce in ("true", "on", "ok", "1") diff --git a/lib/python3.12/site-packages/setuptools/wheel.py b/lib/python3.12/site-packages/setuptools/wheel.py new file mode 100644 index 0000000000000000000000000000000000000000..93663031541eba1b459f44bd81bc418c9e40decc --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/wheel.py @@ -0,0 +1,262 @@ +"""Wheels support.""" + +import contextlib +import email +import functools +import itertools +import os +import posixpath +import re +import zipfile +from collections.abc import Iterator + +from packaging.requirements import Requirement +from packaging.tags import sys_tags +from packaging.utils import canonicalize_name +from packaging.version import Version as parse_version + +import setuptools +from setuptools.archive_util import _unpack_zipfile_obj +from setuptools.command.egg_info import _egg_basename, write_requirements + +from ._discovery import extras_from_deps +from ._importlib import metadata +from .unicode_utils import _read_utf8_with_fallback + +from distutils.util import get_platform + +WHEEL_NAME = re.compile( + r"""^(?P.+?)-(?P\d.*?) + ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?) + )\.whl$""", + re.VERBOSE, +).match + +NAMESPACE_PACKAGE_INIT = "__import__('pkg_resources').declare_namespace(__name__)\n" + + +@functools.cache +def _get_supported_tags(): + # We calculate the supported tags only once, otherwise calling + # this method on thousands of wheels takes seconds instead of + # milliseconds. + return {(t.interpreter, t.abi, t.platform) for t in sys_tags()} + + +def unpack(src_dir, dst_dir) -> None: + """Move everything under `src_dir` to `dst_dir`, and delete the former.""" + for dirpath, dirnames, filenames in os.walk(src_dir): + subdir = os.path.relpath(dirpath, src_dir) + for f in filenames: + src = os.path.join(dirpath, f) + dst = os.path.join(dst_dir, subdir, f) + os.renames(src, dst) + for n, d in reversed(list(enumerate(dirnames))): + src = os.path.join(dirpath, d) + dst = os.path.join(dst_dir, subdir, d) + if not os.path.exists(dst): + # Directory does not exist in destination, + # rename it and prune it from os.walk list. + os.renames(src, dst) + del dirnames[n] + # Cleanup. + for dirpath, dirnames, filenames in os.walk(src_dir, topdown=True): + assert not filenames + os.rmdir(dirpath) + + +@contextlib.contextmanager +def disable_info_traces() -> Iterator[None]: + """ + Temporarily disable info traces. + """ + from distutils import log + + saved = log.set_threshold(log.WARN) + try: + yield + finally: + log.set_threshold(saved) + + +class Wheel: + def __init__(self, filename) -> None: + match = WHEEL_NAME(os.path.basename(filename)) + if match is None: + raise ValueError(f'invalid wheel name: {filename!r}') + self.filename = filename + for k, v in match.groupdict().items(): + setattr(self, k, v) + + def tags(self): + """List tags (py_version, abi, platform) supported by this wheel.""" + return itertools.product( + self.py_version.split('.'), + self.abi.split('.'), + self.platform.split('.'), + ) + + def is_compatible(self): + """Is the wheel compatible with the current platform?""" + return next((True for t in self.tags() if t in _get_supported_tags()), False) + + def egg_name(self): + return ( + _egg_basename( + self.project_name, + self.version, + platform=(None if self.platform == 'any' else get_platform()), + ) + + ".egg" + ) + + def get_dist_info(self, zf): + # find the correct name of the .dist-info dir in the wheel file + for member in zf.namelist(): + dirname = posixpath.dirname(member) + if dirname.endswith('.dist-info') and canonicalize_name(dirname).startswith( + canonicalize_name(self.project_name) + ): + return dirname + raise ValueError("unsupported wheel format. .dist-info not found") + + def install_as_egg(self, destination_eggdir) -> None: + """Install wheel as an egg directory.""" + with zipfile.ZipFile(self.filename) as zf: + self._install_as_egg(destination_eggdir, zf) + + def _install_as_egg(self, destination_eggdir, zf): + dist_basename = f'{self.project_name}-{self.version}' + dist_info = self.get_dist_info(zf) + dist_data = f'{dist_basename}.data' + egg_info = os.path.join(destination_eggdir, 'EGG-INFO') + + self._convert_metadata(zf, destination_eggdir, dist_info, egg_info) + self._move_data_entries(destination_eggdir, dist_data) + self._fix_namespace_packages(egg_info, destination_eggdir) + + @staticmethod + def _convert_metadata(zf, destination_eggdir, dist_info, egg_info): + def get_metadata(name): + with zf.open(posixpath.join(dist_info, name)) as fp: + value = fp.read().decode('utf-8') + return email.parser.Parser().parsestr(value) + + wheel_metadata = get_metadata('WHEEL') + # Check wheel format version is supported. + wheel_version = parse_version(wheel_metadata.get('Wheel-Version')) + wheel_v1 = parse_version('1.0') <= wheel_version < parse_version('2.0dev0') + if not wheel_v1: + raise ValueError(f'unsupported wheel format version: {wheel_version}') + # Extract to target directory. + _unpack_zipfile_obj(zf, destination_eggdir) + dist_info = os.path.join(destination_eggdir, dist_info) + install_requires, extras_require = Wheel._convert_requires( + destination_eggdir, dist_info + ) + os.rename(dist_info, egg_info) + os.rename( + os.path.join(egg_info, 'METADATA'), + os.path.join(egg_info, 'PKG-INFO'), + ) + setup_dist = setuptools.Distribution( + attrs=dict( + install_requires=install_requires, + extras_require=extras_require, + ), + ) + with disable_info_traces(): + write_requirements( + setup_dist.get_command_obj('egg_info'), + None, + os.path.join(egg_info, 'requires.txt'), + ) + + @staticmethod + def _convert_requires(destination_eggdir, dist_info): + md = metadata.Distribution.at(dist_info).metadata + deps = md.get_all('Requires-Dist') or [] + reqs = list(map(Requirement, deps)) + + extras = extras_from_deps(deps) + + # Note: Evaluate and strip markers now, + # as it's difficult to convert back from the syntax: + # foobar; "linux" in sys_platform and extra == 'test' + def raw_req(req): + req = Requirement(str(req)) + req.marker = None + return str(req) + + def eval(req, **env): + return not req.marker or req.marker.evaluate(env) + + def for_extra(req): + try: + markers = req.marker._markers + except AttributeError: + markers = () + return set( + marker[2].value + for marker in markers + if isinstance(marker, tuple) and marker[0].value == 'extra' + ) + + install_requires = list( + map(raw_req, filter(eval, itertools.filterfalse(for_extra, reqs))) + ) + extras_require = { + extra: list( + map( + raw_req, + (req for req in reqs if for_extra(req) and eval(req, extra=extra)), + ) + ) + for extra in extras + } + return install_requires, extras_require + + @staticmethod + def _move_data_entries(destination_eggdir, dist_data): + """Move data entries to their correct location.""" + dist_data = os.path.join(destination_eggdir, dist_data) + dist_data_scripts = os.path.join(dist_data, 'scripts') + if os.path.exists(dist_data_scripts): + egg_info_scripts = os.path.join(destination_eggdir, 'EGG-INFO', 'scripts') + os.mkdir(egg_info_scripts) + for entry in os.listdir(dist_data_scripts): + # Remove bytecode, as it's not properly handled + # during easy_install scripts install phase. + if entry.endswith('.pyc'): + os.unlink(os.path.join(dist_data_scripts, entry)) + else: + os.rename( + os.path.join(dist_data_scripts, entry), + os.path.join(egg_info_scripts, entry), + ) + os.rmdir(dist_data_scripts) + for subdir in filter( + os.path.exists, + ( + os.path.join(dist_data, d) + for d in ('data', 'headers', 'purelib', 'platlib') + ), + ): + unpack(subdir, destination_eggdir) + if os.path.exists(dist_data): + os.rmdir(dist_data) + + @staticmethod + def _fix_namespace_packages(egg_info, destination_eggdir): + namespace_packages = os.path.join(egg_info, 'namespace_packages.txt') + if os.path.exists(namespace_packages): + namespace_packages = _read_utf8_with_fallback(namespace_packages).split() + + for mod in namespace_packages: + mod_dir = os.path.join(destination_eggdir, *mod.split('.')) + mod_init = os.path.join(mod_dir, '__init__.py') + if not os.path.exists(mod_dir): + os.mkdir(mod_dir) + if not os.path.exists(mod_init): + with open(mod_init, 'w', encoding="utf-8") as fp: + fp.write(NAMESPACE_PACKAGE_INIT) diff --git a/lib/python3.12/site-packages/setuptools/windows_support.py b/lib/python3.12/site-packages/setuptools/windows_support.py new file mode 100644 index 0000000000000000000000000000000000000000..7a2b53a291409c66851961a559eb4d69be0f4acc --- /dev/null +++ b/lib/python3.12/site-packages/setuptools/windows_support.py @@ -0,0 +1,30 @@ +import platform + + +def windows_only(func): + if platform.system() != 'Windows': + return lambda *args, **kwargs: None + return func + + +@windows_only +def hide_file(path: str) -> None: + """ + Set the hidden attribute on a file or directory. + + From https://stackoverflow.com/questions/19622133/ + + `path` must be text. + """ + import ctypes + import ctypes.wintypes + + SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW + SetFileAttributes.argtypes = ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD + SetFileAttributes.restype = ctypes.wintypes.BOOL + + FILE_ATTRIBUTE_HIDDEN = 0x02 + + ret = SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN) + if not ret: + raise ctypes.WinError() diff --git a/lib/python3.12/site-packages/six-1.17.0.dist-info/INSTALLER b/lib/python3.12/site-packages/six-1.17.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/six-1.17.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/six-1.17.0.dist-info/LICENSE b/lib/python3.12/site-packages/six-1.17.0.dist-info/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..1cc22a5aa7679ebaa10934212f356823931bdc3e --- /dev/null +++ b/lib/python3.12/site-packages/six-1.17.0.dist-info/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2010-2024 Benjamin Peterson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/python3.12/site-packages/six-1.17.0.dist-info/METADATA b/lib/python3.12/site-packages/six-1.17.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..cfde03c2631c5c1d5cdc0949d0ee3379e7110f0e --- /dev/null +++ b/lib/python3.12/site-packages/six-1.17.0.dist-info/METADATA @@ -0,0 +1,43 @@ +Metadata-Version: 2.1 +Name: six +Version: 1.17.0 +Summary: Python 2 and 3 compatibility utilities +Home-page: https://github.com/benjaminp/six +Author: Benjamin Peterson +Author-email: benjamin@python.org +License: MIT +Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.* +License-File: LICENSE + +.. image:: https://img.shields.io/pypi/v/six.svg + :target: https://pypi.org/project/six/ + :alt: six on PyPI + +.. image:: https://readthedocs.org/projects/six/badge/?version=latest + :target: https://six.readthedocs.io/ + :alt: six's documentation on Read the Docs + +.. image:: https://img.shields.io/badge/license-MIT-green.svg + :target: https://github.com/benjaminp/six/blob/master/LICENSE + :alt: MIT License badge + +Six is a Python 2 and 3 compatibility library. It provides utility functions +for smoothing over the differences between the Python versions with the goal of +writing Python code that is compatible on both Python versions. See the +documentation for more information on what is provided. + +Six supports Python 2.7 and 3.3+. It is contained in only one Python +file, so it can be easily copied into your project. (The copyright and license +notice must be retained.) + +Online documentation is at https://six.readthedocs.io/. + +Bugs can be reported to https://github.com/benjaminp/six. The code can also +be found there. diff --git a/lib/python3.12/site-packages/six-1.17.0.dist-info/RECORD b/lib/python3.12/site-packages/six-1.17.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..bb90c1a52e0f905a3facec665e8600ef20931282 --- /dev/null +++ b/lib/python3.12/site-packages/six-1.17.0.dist-info/RECORD @@ -0,0 +1,8 @@ +__pycache__/six.cpython-312.pyc,, +six-1.17.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +six-1.17.0.dist-info/LICENSE,sha256=Q3W6IOK5xsTnytKUCmKP2Q6VzD1Q7pKq51VxXYuh-9A,1066 +six-1.17.0.dist-info/METADATA,sha256=ViBCB4wnUlSfbYp8htvF3XCAiKe-bYBnLsewcQC3JGg,1658 +six-1.17.0.dist-info/RECORD,, +six-1.17.0.dist-info/WHEEL,sha256=pxeNX5JdtCe58PUSYP9upmc7jdRPgvT0Gm9kb1SHlVw,109 +six-1.17.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4 +six.py,sha256=xRyR9wPT1LNpbJI8tf7CE-BeddkhU5O--sfy-mo5BN8,34703 diff --git a/lib/python3.12/site-packages/six-1.17.0.dist-info/WHEEL b/lib/python3.12/site-packages/six-1.17.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..104f3874635f24f0d2918dfeaf6a59652274460c --- /dev/null +++ b/lib/python3.12/site-packages/six-1.17.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.6.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/six-1.17.0.dist-info/top_level.txt b/lib/python3.12/site-packages/six-1.17.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..ffe2fce498955b628014618b28c6bcf152466a4a --- /dev/null +++ b/lib/python3.12/site-packages/six-1.17.0.dist-info/top_level.txt @@ -0,0 +1 @@ +six diff --git a/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/INSTALLER b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/LICENCE b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/LICENCE new file mode 100644 index 0000000000000000000000000000000000000000..a8922b182e80d9bcb955e8b8ae2bd9a017d72977 --- /dev/null +++ b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/LICENCE @@ -0,0 +1,49 @@ +`tqdm` is a product of collaborative work. +Unless otherwise stated, all authors (see commit logs) retain copyright +for their respective work, and release the work under the MIT licence +(text below). + +Exceptions or notable authors are listed below +in reverse chronological order: + +* files: * + MPL-2.0 2015-2024 (c) Casper da Costa-Luis + [casperdcl](https://github.com/casperdcl). +* files: tqdm/_tqdm.py + MIT 2016 (c) [PR #96] on behalf of Google Inc. +* files: tqdm/_tqdm.py README.rst .gitignore + MIT 2013 (c) Noam Yorav-Raphael, original author. + +[PR #96]: https://github.com/tqdm/tqdm/pull/96 + + +Mozilla Public Licence (MPL) v. 2.0 - Exhibit A +----------------------------------------------- + +This Source Code Form is subject to the terms of the +Mozilla Public License, v. 2.0. +If a copy of the MPL was not distributed with this project, +You can obtain one at https://mozilla.org/MPL/2.0/. + + +MIT License (MIT) +----------------- + +Copyright (c) 2013 noamraph + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/METADATA b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..181b4dc8b2f8697d1c0374a612ffd8b2f2db346a --- /dev/null +++ b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/METADATA @@ -0,0 +1,1594 @@ +Metadata-Version: 2.1 +Name: tqdm +Version: 4.67.1 +Summary: Fast, Extensible Progress Meter +Maintainer-email: tqdm developers +License: MPL-2.0 AND MIT +Project-URL: homepage, https://tqdm.github.io +Project-URL: repository, https://github.com/tqdm/tqdm +Project-URL: changelog, https://tqdm.github.io/releases +Project-URL: wiki, https://github.com/tqdm/tqdm/wiki +Keywords: progressbar,progressmeter,progress,bar,meter,rate,eta,console,terminal,time +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Environment :: Other Environment +Classifier: Environment :: Win32 (MS Windows) +Classifier: Environment :: X11 Applications +Classifier: Framework :: IPython +Classifier: Framework :: Jupyter +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: End Users/Desktop +Classifier: Intended Audience :: Other Audience +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: MIT License +Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) +Classifier: Operating System :: MacOS +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft +Classifier: Operating System :: Microsoft :: MS-DOS +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Operating System :: POSIX :: BSD +Classifier: Operating System :: POSIX :: BSD :: FreeBSD +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: POSIX :: SunOS/Solaris +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation +Classifier: Programming Language :: Python :: Implementation :: IronPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Unix Shell +Classifier: Topic :: Desktop Environment +Classifier: Topic :: Education :: Computer Aided Instruction (CAI) +Classifier: Topic :: Education :: Testing +Classifier: Topic :: Office/Business +Classifier: Topic :: Other/Nonlisted Topic +Classifier: Topic :: Software Development :: Build Tools +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Pre-processors +Classifier: Topic :: Software Development :: User Interfaces +Classifier: Topic :: System :: Installation/Setup +Classifier: Topic :: System :: Logging +Classifier: Topic :: System :: Monitoring +Classifier: Topic :: System :: Shells +Classifier: Topic :: Terminals +Classifier: Topic :: Utilities +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENCE +Requires-Dist: colorama; platform_system == "Windows" +Provides-Extra: dev +Requires-Dist: pytest>=6; extra == "dev" +Requires-Dist: pytest-cov; extra == "dev" +Requires-Dist: pytest-timeout; extra == "dev" +Requires-Dist: pytest-asyncio>=0.24; extra == "dev" +Requires-Dist: nbval; extra == "dev" +Provides-Extra: discord +Requires-Dist: requests; extra == "discord" +Provides-Extra: slack +Requires-Dist: slack-sdk; extra == "slack" +Provides-Extra: telegram +Requires-Dist: requests; extra == "telegram" +Provides-Extra: notebook +Requires-Dist: ipywidgets>=6; extra == "notebook" + +|Logo| + +tqdm +==== + +|Py-Versions| |Versions| |Conda-Forge-Status| |Docker| |Snapcraft| + +|Build-Status| |Coverage-Status| |Branch-Coverage-Status| |Codacy-Grade| |Libraries-Rank| |PyPI-Downloads| + +|LICENCE| |OpenHub-Status| |binder-demo| |awesome-python| + +``tqdm`` derives from the Arabic word *taqaddum* (تقدّم) which can mean "progress," +and is an abbreviation for "I love you so much" in Spanish (*te quiero demasiado*). + +Instantly make your loops show a smart progress meter - just wrap any +iterable with ``tqdm(iterable)``, and you're done! + +.. code:: python + + from tqdm import tqdm + for i in tqdm(range(10000)): + ... + +``76%|████████████████████████        | 7568/10000 [00:33<00:10, 229.00it/s]`` + +``trange(N)`` can be also used as a convenient shortcut for +``tqdm(range(N))``. + +|Screenshot| + |Video| |Slides| |Merch| + +It can also be executed as a module with pipes: + +.. code:: sh + + $ seq 9999999 | tqdm --bytes | wc -l + 75.2MB [00:00, 217MB/s] + 9999999 + + $ tar -zcf - docs/ | tqdm --bytes --total `du -sb docs/ | cut -f1` \ + > backup.tgz + 32%|██████████▍ | 8.89G/27.9G [00:42<01:31, 223MB/s] + +Overhead is low -- about 60ns per iteration (80ns with ``tqdm.gui``), and is +unit tested against performance regression. +By comparison, the well-established +`ProgressBar `__ has +an 800ns/iter overhead. + +In addition to its low overhead, ``tqdm`` uses smart algorithms to predict +the remaining time and to skip unnecessary iteration displays, which allows +for a negligible overhead in most cases. + +``tqdm`` works on any platform +(Linux, Windows, Mac, FreeBSD, NetBSD, Solaris/SunOS), +in any console or in a GUI, and is also friendly with IPython/Jupyter notebooks. + +``tqdm`` does not require any dependencies (not even ``curses``!), just +Python and an environment supporting ``carriage return \r`` and +``line feed \n`` control characters. + +------------------------------------------ + +.. contents:: Table of contents + :backlinks: top + :local: + + +Installation +------------ + +Latest PyPI stable release +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +|Versions| |PyPI-Downloads| |Libraries-Dependents| + +.. code:: sh + + pip install tqdm + +Latest development release on GitHub +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +|GitHub-Status| |GitHub-Stars| |GitHub-Commits| |GitHub-Forks| |GitHub-Updated| + +Pull and install pre-release ``devel`` branch: + +.. code:: sh + + pip install "git+https://github.com/tqdm/tqdm.git@devel#egg=tqdm" + +Latest Conda release +~~~~~~~~~~~~~~~~~~~~ + +|Conda-Forge-Status| + +.. code:: sh + + conda install -c conda-forge tqdm + +Latest Snapcraft release +~~~~~~~~~~~~~~~~~~~~~~~~ + +|Snapcraft| + +There are 3 channels to choose from: + +.. code:: sh + + snap install tqdm # implies --stable, i.e. latest tagged release + snap install tqdm --candidate # master branch + snap install tqdm --edge # devel branch + +Note that ``snap`` binaries are purely for CLI use (not ``import``-able), and +automatically set up ``bash`` tab-completion. + +Latest Docker release +~~~~~~~~~~~~~~~~~~~~~ + +|Docker| + +.. code:: sh + + docker pull tqdm/tqdm + docker run -i --rm tqdm/tqdm --help + +Other +~~~~~ + +There are other (unofficial) places where ``tqdm`` may be downloaded, particularly for CLI use: + +|Repology| + +.. |Repology| image:: https://repology.org/badge/tiny-repos/python:tqdm.svg + :target: https://repology.org/project/python:tqdm/versions + +Changelog +--------- + +The list of all changes is available either on GitHub's Releases: +|GitHub-Status|, on the +`wiki `__, or on the +`website `__. + + +Usage +----- + +``tqdm`` is very versatile and can be used in a number of ways. +The three main ones are given below. + +Iterable-based +~~~~~~~~~~~~~~ + +Wrap ``tqdm()`` around any iterable: + +.. code:: python + + from tqdm import tqdm + from time import sleep + + text = "" + for char in tqdm(["a", "b", "c", "d"]): + sleep(0.25) + text = text + char + +``trange(i)`` is a special optimised instance of ``tqdm(range(i))``: + +.. code:: python + + from tqdm import trange + + for i in trange(100): + sleep(0.01) + +Instantiation outside of the loop allows for manual control over ``tqdm()``: + +.. code:: python + + pbar = tqdm(["a", "b", "c", "d"]) + for char in pbar: + sleep(0.25) + pbar.set_description("Processing %s" % char) + +Manual +~~~~~~ + +Manual control of ``tqdm()`` updates using a ``with`` statement: + +.. code:: python + + with tqdm(total=100) as pbar: + for i in range(10): + sleep(0.1) + pbar.update(10) + +If the optional variable ``total`` (or an iterable with ``len()``) is +provided, predictive stats are displayed. + +``with`` is also optional (you can just assign ``tqdm()`` to a variable, +but in this case don't forget to ``del`` or ``close()`` at the end: + +.. code:: python + + pbar = tqdm(total=100) + for i in range(10): + sleep(0.1) + pbar.update(10) + pbar.close() + +Module +~~~~~~ + +Perhaps the most wonderful use of ``tqdm`` is in a script or on the command +line. Simply inserting ``tqdm`` (or ``python -m tqdm``) between pipes will pass +through all ``stdin`` to ``stdout`` while printing progress to ``stderr``. + +The example below demonstrate counting the number of lines in all Python files +in the current directory, with timing information included. + +.. code:: sh + + $ time find . -name '*.py' -type f -exec cat \{} \; | wc -l + 857365 + + real 0m3.458s + user 0m0.274s + sys 0m3.325s + + $ time find . -name '*.py' -type f -exec cat \{} \; | tqdm | wc -l + 857366it [00:03, 246471.31it/s] + 857365 + + real 0m3.585s + user 0m0.862s + sys 0m3.358s + +Note that the usual arguments for ``tqdm`` can also be specified. + +.. code:: sh + + $ find . -name '*.py' -type f -exec cat \{} \; | + tqdm --unit loc --unit_scale --total 857366 >> /dev/null + 100%|█████████████████████████████████| 857K/857K [00:04<00:00, 246Kloc/s] + +Backing up a large directory? + +.. code:: sh + + $ tar -zcf - docs/ | tqdm --bytes --total `du -sb docs/ | cut -f1` \ + > backup.tgz + 44%|██████████████▊ | 153M/352M [00:14<00:18, 11.0MB/s] + +This can be beautified further: + +.. code:: sh + + $ BYTES=$(du -sb docs/ | cut -f1) + $ tar -cf - docs/ \ + | tqdm --bytes --total "$BYTES" --desc Processing | gzip \ + | tqdm --bytes --total "$BYTES" --desc Compressed --position 1 \ + > ~/backup.tgz + Processing: 100%|██████████████████████| 352M/352M [00:14<00:00, 30.2MB/s] + Compressed: 42%|█████████▎ | 148M/352M [00:14<00:19, 10.9MB/s] + +Or done on a file level using 7-zip: + +.. code:: sh + + $ 7z a -bd -r backup.7z docs/ | grep Compressing \ + | tqdm --total $(find docs/ -type f | wc -l) --unit files \ + | grep -v Compressing + 100%|██████████████████████████▉| 15327/15327 [01:00<00:00, 712.96files/s] + +Pre-existing CLI programs already outputting basic progress information will +benefit from ``tqdm``'s ``--update`` and ``--update_to`` flags: + +.. code:: sh + + $ seq 3 0.1 5 | tqdm --total 5 --update_to --null + 100%|████████████████████████████████████| 5.0/5 [00:00<00:00, 9673.21it/s] + $ seq 10 | tqdm --update --null # 1 + 2 + ... + 10 = 55 iterations + 55it [00:00, 90006.52it/s] + +FAQ and Known Issues +-------------------- + +|GitHub-Issues| + +The most common issues relate to excessive output on multiple lines, instead +of a neat one-line progress bar. + +- Consoles in general: require support for carriage return (``CR``, ``\r``). + + * Some cloud logging consoles which don't support ``\r`` properly + (`cloudwatch `__, + `K8s `__) may benefit from + ``export TQDM_POSITION=-1``. + +- Nested progress bars: + + * Consoles in general: require support for moving cursors up to the + previous line. For example, + `IDLE `__, + `ConEmu `__ and + `PyCharm `__ (also + `here `__, + `here `__, and + `here `__) + lack full support. + * Windows: additionally may require the Python module ``colorama`` + to ensure nested bars stay within their respective lines. + +- Unicode: + + * Environments which report that they support unicode will have solid smooth + progressbars. The fallback is an ``ascii``-only bar. + * Windows consoles often only partially support unicode and thus + `often require explicit ascii=True `__ + (also `here `__). This is due to + either normal-width unicode characters being incorrectly displayed as + "wide", or some unicode characters not rendering. + +- Wrapping generators: + + * Generator wrapper functions tend to hide the length of iterables. + ``tqdm`` does not. + * Replace ``tqdm(enumerate(...))`` with ``enumerate(tqdm(...))`` or + ``tqdm(enumerate(x), total=len(x), ...)``. + The same applies to ``numpy.ndenumerate``. + * Replace ``tqdm(zip(a, b))`` with ``zip(tqdm(a), b)`` or even + ``zip(tqdm(a), tqdm(b))``. + * The same applies to ``itertools``. + * Some useful convenience functions can be found under ``tqdm.contrib``. + +- `No intermediate output in docker-compose `__: + use ``docker-compose run`` instead of ``docker-compose up`` and ``tty: true``. + +- Overriding defaults via environment variables: + e.g. in CI/cloud jobs, ``export TQDM_MININTERVAL=5`` to avoid log spam. + This override logic is handled by the ``tqdm.utils.envwrap`` decorator + (useful independent of ``tqdm``). + +If you come across any other difficulties, browse and file |GitHub-Issues|. + +Documentation +------------- + +|Py-Versions| |README-Hits| (Since 19 May 2016) + +.. code:: python + + class tqdm(): + """ + Decorate an iterable object, returning an iterator which acts exactly + like the original iterable, but prints a dynamically updating + progressbar every time a value is requested. + """ + + @envwrap("TQDM_") # override defaults via env vars + def __init__(self, iterable=None, desc=None, total=None, leave=True, + file=None, ncols=None, mininterval=0.1, + maxinterval=10.0, miniters=None, ascii=None, disable=False, + unit='it', unit_scale=False, dynamic_ncols=False, + smoothing=0.3, bar_format=None, initial=0, position=None, + postfix=None, unit_divisor=1000, write_bytes=False, + lock_args=None, nrows=None, colour=None, delay=0): + +Parameters +~~~~~~~~~~ + +* iterable : iterable, optional + Iterable to decorate with a progressbar. + Leave blank to manually manage the updates. +* desc : str, optional + Prefix for the progressbar. +* total : int or float, optional + The number of expected iterations. If unspecified, + len(iterable) is used if possible. If float("inf") or as a last + resort, only basic progress statistics are displayed + (no ETA, no progressbar). + If ``gui`` is True and this parameter needs subsequent updating, + specify an initial arbitrary large positive number, + e.g. 9e9. +* leave : bool, optional + If [default: True], keeps all traces of the progressbar + upon termination of iteration. + If ``None``, will leave only if ``position`` is ``0``. +* file : ``io.TextIOWrapper`` or ``io.StringIO``, optional + Specifies where to output the progress messages + (default: sys.stderr). Uses ``file.write(str)`` and ``file.flush()`` + methods. For encoding, see ``write_bytes``. +* ncols : int, optional + The width of the entire output message. If specified, + dynamically resizes the progressbar to stay within this bound. + If unspecified, attempts to use environment width. The + fallback is a meter width of 10 and no limit for the counter and + statistics. If 0, will not print any meter (only stats). +* mininterval : float, optional + Minimum progress display update interval [default: 0.1] seconds. +* maxinterval : float, optional + Maximum progress display update interval [default: 10] seconds. + Automatically adjusts ``miniters`` to correspond to ``mininterval`` + after long display update lag. Only works if ``dynamic_miniters`` + or monitor thread is enabled. +* miniters : int or float, optional + Minimum progress display update interval, in iterations. + If 0 and ``dynamic_miniters``, will automatically adjust to equal + ``mininterval`` (more CPU efficient, good for tight loops). + If > 0, will skip display of specified number of iterations. + Tweak this and ``mininterval`` to get very efficient loops. + If your progress is erratic with both fast and slow iterations + (network, skipping items, etc) you should set miniters=1. +* ascii : bool or str, optional + If unspecified or False, use unicode (smooth blocks) to fill + the meter. The fallback is to use ASCII characters " 123456789#". +* disable : bool, optional + Whether to disable the entire progressbar wrapper + [default: False]. If set to None, disable on non-TTY. +* unit : str, optional + String that will be used to define the unit of each iteration + [default: it]. +* unit_scale : bool or int or float, optional + If 1 or True, the number of iterations will be reduced/scaled + automatically and a metric prefix following the + International System of Units standard will be added + (kilo, mega, etc.) [default: False]. If any other non-zero + number, will scale ``total`` and ``n``. +* dynamic_ncols : bool, optional + If set, constantly alters ``ncols`` and ``nrows`` to the + environment (allowing for window resizes) [default: False]. +* smoothing : float, optional + Exponential moving average smoothing factor for speed estimates + (ignored in GUI mode). Ranges from 0 (average speed) to 1 + (current/instantaneous speed) [default: 0.3]. +* bar_format : str, optional + Specify a custom bar string formatting. May impact performance. + [default: '{l_bar}{bar}{r_bar}'], where + l_bar='{desc}: {percentage:3.0f}%|' and + r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, ' + '{rate_fmt}{postfix}]' + Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt, + percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, + rate, rate_fmt, rate_noinv, rate_noinv_fmt, + rate_inv, rate_inv_fmt, postfix, unit_divisor, + remaining, remaining_s, eta. + Note that a trailing ": " is automatically removed after {desc} + if the latter is empty. +* initial : int or float, optional + The initial counter value. Useful when restarting a progress + bar [default: 0]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. +* position : int, optional + Specify the line offset to print this bar (starting from 0) + Automatic if unspecified. + Useful to manage multiple bars at once (eg, from threads). +* postfix : dict or ``*``, optional + Specify additional stats to display at the end of the bar. + Calls ``set_postfix(**postfix)`` if possible (dict). +* unit_divisor : float, optional + [default: 1000], ignored unless ``unit_scale`` is True. +* write_bytes : bool, optional + Whether to write bytes. If (default: False) will write unicode. +* lock_args : tuple, optional + Passed to ``refresh`` for intermediate output + (initialisation, iterating, and updating). +* nrows : int, optional + The screen height. If specified, hides nested bars outside this + bound. If unspecified, attempts to use environment height. + The fallback is 20. +* colour : str, optional + Bar colour (e.g. 'green', '#00ff00'). +* delay : float, optional + Don't display until [default: 0] seconds have elapsed. + +Extra CLI Options +~~~~~~~~~~~~~~~~~ + +* delim : chr, optional + Delimiting character [default: '\n']. Use '\0' for null. + N.B.: on Windows systems, Python converts '\n' to '\r\n'. +* buf_size : int, optional + String buffer size in bytes [default: 256] + used when ``delim`` is specified. +* bytes : bool, optional + If true, will count bytes, ignore ``delim``, and default + ``unit_scale`` to True, ``unit_divisor`` to 1024, and ``unit`` to 'B'. +* tee : bool, optional + If true, passes ``stdin`` to both ``stderr`` and ``stdout``. +* update : bool, optional + If true, will treat input as newly elapsed iterations, + i.e. numbers to pass to ``update()``. Note that this is slow + (~2e5 it/s) since every input must be decoded as a number. +* update_to : bool, optional + If true, will treat input as total elapsed iterations, + i.e. numbers to assign to ``self.n``. Note that this is slow + (~2e5 it/s) since every input must be decoded as a number. +* null : bool, optional + If true, will discard input (no stdout). +* manpath : str, optional + Directory in which to install tqdm man pages. +* comppath : str, optional + Directory in which to place tqdm completion. +* log : str, optional + CRITICAL|FATAL|ERROR|WARN(ING)|[default: 'INFO']|DEBUG|NOTSET. + +Returns +~~~~~~~ + +* out : decorated iterator. + +.. code:: python + + class tqdm(): + def update(self, n=1): + """ + Manually update the progress bar, useful for streams + such as reading files. + E.g.: + >>> t = tqdm(total=filesize) # Initialise + >>> for current_buffer in stream: + ... ... + ... t.update(len(current_buffer)) + >>> t.close() + The last line is highly recommended, but possibly not necessary if + ``t.update()`` will be called in such a way that ``filesize`` will be + exactly reached and printed. + + Parameters + ---------- + n : int or float, optional + Increment to add to the internal counter of iterations + [default: 1]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. + + Returns + ------- + out : bool or None + True if a ``display()`` was triggered. + """ + + def close(self): + """Cleanup and (if leave=False) close the progressbar.""" + + def clear(self, nomove=False): + """Clear current bar display.""" + + def refresh(self): + """ + Force refresh the display of this bar. + + Parameters + ---------- + nolock : bool, optional + If ``True``, does not lock. + If [default: ``False``]: calls ``acquire()`` on internal lock. + lock_args : tuple, optional + Passed to internal lock's ``acquire()``. + If specified, will only ``display()`` if ``acquire()`` returns ``True``. + """ + + def unpause(self): + """Restart tqdm timer from last print time.""" + + def reset(self, total=None): + """ + Resets to 0 iterations for repeated use. + + Consider combining with ``leave=True``. + + Parameters + ---------- + total : int or float, optional. Total to use for the new bar. + """ + + def set_description(self, desc=None, refresh=True): + """ + Set/modify description of the progress bar. + + Parameters + ---------- + desc : str, optional + refresh : bool, optional + Forces refresh [default: True]. + """ + + def set_postfix(self, ordered_dict=None, refresh=True, **tqdm_kwargs): + """ + Set/modify postfix (additional stats) + with automatic formatting based on datatype. + + Parameters + ---------- + ordered_dict : dict or OrderedDict, optional + refresh : bool, optional + Forces refresh [default: True]. + kwargs : dict, optional + """ + + @classmethod + def write(cls, s, file=sys.stdout, end="\n"): + """Print a message via tqdm (without overlap with bars).""" + + @property + def format_dict(self): + """Public API for read-only member access.""" + + def display(self, msg=None, pos=None): + """ + Use ``self.sp`` to display ``msg`` in the specified ``pos``. + + Consider overloading this function when inheriting to use e.g.: + ``self.some_frontend(**self.format_dict)`` instead of ``self.sp``. + + Parameters + ---------- + msg : str, optional. What to display (default: ``repr(self)``). + pos : int, optional. Position to ``moveto`` + (default: ``abs(self.pos)``). + """ + + @classmethod + @contextmanager + def wrapattr(cls, stream, method, total=None, bytes=True, **tqdm_kwargs): + """ + stream : file-like object. + method : str, "read" or "write". The result of ``read()`` and + the first argument of ``write()`` should have a ``len()``. + + >>> with tqdm.wrapattr(file_obj, "read", total=file_obj.size) as fobj: + ... while True: + ... chunk = fobj.read(chunk_size) + ... if not chunk: + ... break + """ + + @classmethod + def pandas(cls, *targs, **tqdm_kwargs): + """Registers the current `tqdm` class with `pandas`.""" + + def trange(*args, **tqdm_kwargs): + """Shortcut for `tqdm(range(*args), **tqdm_kwargs)`.""" + +Convenience Functions +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + def tqdm.contrib.tenumerate(iterable, start=0, total=None, + tqdm_class=tqdm.auto.tqdm, **tqdm_kwargs): + """Equivalent of `numpy.ndenumerate` or builtin `enumerate`.""" + + def tqdm.contrib.tzip(iter1, *iter2plus, **tqdm_kwargs): + """Equivalent of builtin `zip`.""" + + def tqdm.contrib.tmap(function, *sequences, **tqdm_kwargs): + """Equivalent of builtin `map`.""" + +Submodules +~~~~~~~~~~ + +.. code:: python + + class tqdm.notebook.tqdm(tqdm.tqdm): + """IPython/Jupyter Notebook widget.""" + + class tqdm.auto.tqdm(tqdm.tqdm): + """Automatically chooses beween `tqdm.notebook` and `tqdm.tqdm`.""" + + class tqdm.asyncio.tqdm(tqdm.tqdm): + """Asynchronous version.""" + @classmethod + def as_completed(cls, fs, *, loop=None, timeout=None, total=None, + **tqdm_kwargs): + """Wrapper for `asyncio.as_completed`.""" + + class tqdm.gui.tqdm(tqdm.tqdm): + """Matplotlib GUI version.""" + + class tqdm.tk.tqdm(tqdm.tqdm): + """Tkinter GUI version.""" + + class tqdm.rich.tqdm(tqdm.tqdm): + """`rich.progress` version.""" + + class tqdm.keras.TqdmCallback(keras.callbacks.Callback): + """Keras callback for epoch and batch progress.""" + + class tqdm.dask.TqdmCallback(dask.callbacks.Callback): + """Dask callback for task progress.""" + + +``contrib`` ++++++++++++ + +The ``tqdm.contrib`` package also contains experimental modules: + +- ``tqdm.contrib.itertools``: Thin wrappers around ``itertools`` +- ``tqdm.contrib.concurrent``: Thin wrappers around ``concurrent.futures`` +- ``tqdm.contrib.slack``: Posts to `Slack `__ bots +- ``tqdm.contrib.discord``: Posts to `Discord `__ bots +- ``tqdm.contrib.telegram``: Posts to `Telegram `__ bots +- ``tqdm.contrib.bells``: Automagically enables all optional features + + * ``auto``, ``pandas``, ``slack``, ``discord``, ``telegram`` + +Examples and Advanced Usage +--------------------------- + +- See the `examples `__ + folder; +- import the module and run ``help()``; +- consult the `wiki `__; + + * this has an + `excellent article `__ + on how to make a **great** progressbar; + +- check out the `slides from PyData London `__, or +- run the |binder-demo|. + +Description and additional stats +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Custom information can be displayed and updated dynamically on ``tqdm`` bars +with the ``desc`` and ``postfix`` arguments: + +.. code:: python + + from tqdm import tqdm, trange + from random import random, randint + from time import sleep + + with trange(10) as t: + for i in t: + # Description will be displayed on the left + t.set_description('GEN %i' % i) + # Postfix will be displayed on the right, + # formatted automatically based on argument's datatype + t.set_postfix(loss=random(), gen=randint(1,999), str='h', + lst=[1, 2]) + sleep(0.1) + + with tqdm(total=10, bar_format="{postfix[0]} {postfix[1][value]:>8.2g}", + postfix=["Batch", {"value": 0}]) as t: + for i in range(10): + sleep(0.1) + t.postfix[1]["value"] = i / 2 + t.update() + +Points to remember when using ``{postfix[...]}`` in the ``bar_format`` string: + +- ``postfix`` also needs to be passed as an initial argument in a compatible + format, and +- ``postfix`` will be auto-converted to a string if it is a ``dict``-like + object. To prevent this behaviour, insert an extra item into the dictionary + where the key is not a string. + +Additional ``bar_format`` parameters may also be defined by overriding +``format_dict``, and the bar itself may be modified using ``ascii``: + +.. code:: python + + from tqdm import tqdm + class TqdmExtraFormat(tqdm): + """Provides a `total_time` format parameter""" + @property + def format_dict(self): + d = super().format_dict + total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1) + d.update(total_time=self.format_interval(total_time) + " in total") + return d + + for i in TqdmExtraFormat( + range(9), ascii=" .oO0", + bar_format="{total_time}: {percentage:.0f}%|{bar}{r_bar}"): + if i == 4: + break + +.. code:: + + 00:00 in total: 44%|0000. | 4/9 [00:00<00:00, 962.93it/s] + +Note that ``{bar}`` also supports a format specifier ``[width][type]``. + +- ``width`` + + * unspecified (default): automatic to fill ``ncols`` + * ``int >= 0``: fixed width overriding ``ncols`` logic + * ``int < 0``: subtract from the automatic default + +- ``type`` + + * ``a``: ascii (``ascii=True`` override) + * ``u``: unicode (``ascii=False`` override) + * ``b``: blank (``ascii=" "`` override) + +This means a fixed bar with right-justified text may be created by using: +``bar_format="{l_bar}{bar:10}|{bar:-10b}right-justified"`` + +Nested progress bars +~~~~~~~~~~~~~~~~~~~~ + +``tqdm`` supports nested progress bars. Here's an example: + +.. code:: python + + from tqdm.auto import trange + from time import sleep + + for i in trange(4, desc='1st loop'): + for j in trange(5, desc='2nd loop'): + for k in trange(50, desc='3rd loop', leave=False): + sleep(0.01) + +For manual control over positioning (e.g. for multi-processing use), +you may specify ``position=n`` where ``n=0`` for the outermost bar, +``n=1`` for the next, and so on. +However, it's best to check if ``tqdm`` can work without manual ``position`` +first. + +.. code:: python + + from time import sleep + from tqdm import trange, tqdm + from multiprocessing import Pool, RLock, freeze_support + + L = list(range(9)) + + def progresser(n): + interval = 0.001 / (n + 2) + total = 5000 + text = f"#{n}, est. {interval * total:<04.2}s" + for _ in trange(total, desc=text, position=n): + sleep(interval) + + if __name__ == '__main__': + freeze_support() # for Windows support + tqdm.set_lock(RLock()) # for managing output contention + p = Pool(initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) + p.map(progresser, L) + +Note that in Python 3, ``tqdm.write`` is thread-safe: + +.. code:: python + + from time import sleep + from tqdm import tqdm, trange + from concurrent.futures import ThreadPoolExecutor + + L = list(range(9)) + + def progresser(n): + interval = 0.001 / (n + 2) + total = 5000 + text = f"#{n}, est. {interval * total:<04.2}s" + for _ in trange(total, desc=text): + sleep(interval) + if n == 6: + tqdm.write("n == 6 completed.") + tqdm.write("`tqdm.write()` is thread-safe in py3!") + + if __name__ == '__main__': + with ThreadPoolExecutor() as p: + p.map(progresser, L) + +Hooks and callbacks +~~~~~~~~~~~~~~~~~~~ + +``tqdm`` can easily support callbacks/hooks and manual updates. +Here's an example with ``urllib``: + +**``urllib.urlretrieve`` documentation** + + | [...] + | If present, the hook function will be called once + | on establishment of the network connection and once after each block read + | thereafter. The hook will be passed three arguments; a count of blocks + | transferred so far, a block size in bytes, and the total size of the file. + | [...] + +.. code:: python + + import urllib, os + from tqdm import tqdm + urllib = getattr(urllib, 'request', urllib) + + class TqdmUpTo(tqdm): + """Provides `update_to(n)` which uses `tqdm.update(delta_n)`.""" + def update_to(self, b=1, bsize=1, tsize=None): + """ + b : int, optional + Number of blocks transferred so far [default: 1]. + bsize : int, optional + Size of each block (in tqdm units) [default: 1]. + tsize : int, optional + Total size (in tqdm units). If [default: None] remains unchanged. + """ + if tsize is not None: + self.total = tsize + return self.update(b * bsize - self.n) # also sets self.n = b * bsize + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + with TqdmUpTo(unit='B', unit_scale=True, unit_divisor=1024, miniters=1, + desc=eg_link.split('/')[-1]) as t: # all optional kwargs + urllib.urlretrieve(eg_link, filename=os.devnull, + reporthook=t.update_to, data=None) + t.total = t.n + +Inspired by `twine#242 `__. +Functional alternative in +`examples/tqdm_wget.py `__. + +It is recommend to use ``miniters=1`` whenever there is potentially +large differences in iteration speed (e.g. downloading a file over +a patchy connection). + +**Wrapping read/write methods** + +To measure throughput through a file-like object's ``read`` or ``write`` +methods, use ``CallbackIOWrapper``: + +.. code:: python + + from tqdm.auto import tqdm + from tqdm.utils import CallbackIOWrapper + + with tqdm(total=file_obj.size, + unit='B', unit_scale=True, unit_divisor=1024) as t: + fobj = CallbackIOWrapper(t.update, file_obj, "read") + while True: + chunk = fobj.read(chunk_size) + if not chunk: + break + t.reset() + # ... continue to use `t` for something else + +Alternatively, use the even simpler ``wrapattr`` convenience function, +which would condense both the ``urllib`` and ``CallbackIOWrapper`` examples +down to: + +.. code:: python + + import urllib, os + from tqdm import tqdm + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + response = getattr(urllib, 'request', urllib).urlopen(eg_link) + with tqdm.wrapattr(open(os.devnull, "wb"), "write", + miniters=1, desc=eg_link.split('/')[-1], + total=getattr(response, 'length', None)) as fout: + for chunk in response: + fout.write(chunk) + +The ``requests`` equivalent is nearly identical: + +.. code:: python + + import requests, os + from tqdm import tqdm + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + response = requests.get(eg_link, stream=True) + with tqdm.wrapattr(open(os.devnull, "wb"), "write", + miniters=1, desc=eg_link.split('/')[-1], + total=int(response.headers.get('content-length', 0))) as fout: + for chunk in response.iter_content(chunk_size=4096): + fout.write(chunk) + +**Custom callback** + +``tqdm`` is known for intelligently skipping unnecessary displays. To make a +custom callback take advantage of this, simply use the return value of +``update()``. This is set to ``True`` if a ``display()`` was triggered. + +.. code:: python + + from tqdm.auto import tqdm as std_tqdm + + def external_callback(*args, **kwargs): + ... + + class TqdmExt(std_tqdm): + def update(self, n=1): + displayed = super().update(n) + if displayed: + external_callback(**self.format_dict) + return displayed + +``asyncio`` +~~~~~~~~~~~ + +Note that ``break`` isn't currently caught by asynchronous iterators. +This means that ``tqdm`` cannot clean up after itself in this case: + +.. code:: python + + from tqdm.asyncio import tqdm + + async for i in tqdm(range(9)): + if i == 2: + break + +Instead, either call ``pbar.close()`` manually or use the context manager syntax: + +.. code:: python + + from tqdm.asyncio import tqdm + + with tqdm(range(9)) as pbar: + async for i in pbar: + if i == 2: + break + +Pandas Integration +~~~~~~~~~~~~~~~~~~ + +Due to popular demand we've added support for ``pandas`` -- here's an example +for ``DataFrame.progress_apply`` and ``DataFrameGroupBy.progress_apply``: + +.. code:: python + + import pandas as pd + import numpy as np + from tqdm import tqdm + + df = pd.DataFrame(np.random.randint(0, 100, (100000, 6))) + + # Register `pandas.progress_apply` and `pandas.Series.map_apply` with `tqdm` + # (can use `tqdm.gui.tqdm`, `tqdm.notebook.tqdm`, optional kwargs, etc.) + tqdm.pandas(desc="my bar!") + + # Now you can use `progress_apply` instead of `apply` + # and `progress_map` instead of `map` + df.progress_apply(lambda x: x**2) + # can also groupby: + # df.groupby(0).progress_apply(lambda x: x**2) + +In case you're interested in how this works (and how to modify it for your +own callbacks), see the +`examples `__ +folder or import the module and run ``help()``. + +Keras Integration +~~~~~~~~~~~~~~~~~ + +A ``keras`` callback is also available: + +.. code:: python + + from tqdm.keras import TqdmCallback + + ... + + model.fit(..., verbose=0, callbacks=[TqdmCallback()]) + +Dask Integration +~~~~~~~~~~~~~~~~ + +A ``dask`` callback is also available: + +.. code:: python + + from tqdm.dask import TqdmCallback + + with TqdmCallback(desc="compute"): + ... + arr.compute() + + # or use callback globally + cb = TqdmCallback(desc="global") + cb.register() + arr.compute() + +IPython/Jupyter Integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +IPython/Jupyter is supported via the ``tqdm.notebook`` submodule: + +.. code:: python + + from tqdm.notebook import trange, tqdm + from time import sleep + + for i in trange(3, desc='1st loop'): + for j in tqdm(range(100), desc='2nd loop'): + sleep(0.01) + +In addition to ``tqdm`` features, the submodule provides a native Jupyter +widget (compatible with IPython v1-v4 and Jupyter), fully working nested bars +and colour hints (blue: normal, green: completed, red: error/interrupt, +light blue: no ETA); as demonstrated below. + +|Screenshot-Jupyter1| +|Screenshot-Jupyter2| +|Screenshot-Jupyter3| + +The ``notebook`` version supports percentage or pixels for overall width +(e.g.: ``ncols='100%'`` or ``ncols='480px'``). + +It is also possible to let ``tqdm`` automatically choose between +console or notebook versions by using the ``autonotebook`` submodule: + +.. code:: python + + from tqdm.autonotebook import tqdm + tqdm.pandas() + +Note that this will issue a ``TqdmExperimentalWarning`` if run in a notebook +since it is not meant to be possible to distinguish between ``jupyter notebook`` +and ``jupyter console``. Use ``auto`` instead of ``autonotebook`` to suppress +this warning. + +Note that notebooks will display the bar in the cell where it was created. +This may be a different cell from the one where it is used. +If this is not desired, either + +- delay the creation of the bar to the cell where it must be displayed, or +- create the bar with ``display=False``, and in a later cell call + ``display(bar.container)``: + +.. code:: python + + from tqdm.notebook import tqdm + pbar = tqdm(..., display=False) + +.. code:: python + + # different cell + display(pbar.container) + +The ``keras`` callback has a ``display()`` method which can be used likewise: + +.. code:: python + + from tqdm.keras import TqdmCallback + cbk = TqdmCallback(display=False) + +.. code:: python + + # different cell + cbk.display() + model.fit(..., verbose=0, callbacks=[cbk]) + +Another possibility is to have a single bar (near the top of the notebook) +which is constantly re-used (using ``reset()`` rather than ``close()``). +For this reason, the notebook version (unlike the CLI version) does not +automatically call ``close()`` upon ``Exception``. + +.. code:: python + + from tqdm.notebook import tqdm + pbar = tqdm() + +.. code:: python + + # different cell + iterable = range(100) + pbar.reset(total=len(iterable)) # initialise with new `total` + for i in iterable: + pbar.update() + pbar.refresh() # force print final status but don't `close()` + +Custom Integration +~~~~~~~~~~~~~~~~~~ + +To change the default arguments (such as making ``dynamic_ncols=True``), +simply use built-in Python magic: + +.. code:: python + + from functools import partial + from tqdm import tqdm as std_tqdm + tqdm = partial(std_tqdm, dynamic_ncols=True) + +For further customisation, +``tqdm`` may be inherited from to create custom callbacks (as with the +``TqdmUpTo`` example `above <#hooks-and-callbacks>`__) or for custom frontends +(e.g. GUIs such as notebook or plotting packages). In the latter case: + +1. ``def __init__()`` to call ``super().__init__(..., gui=True)`` to disable + terminal ``status_printer`` creation. +2. Redefine: ``close()``, ``clear()``, ``display()``. + +Consider overloading ``display()`` to use e.g. +``self.frontend(**self.format_dict)`` instead of ``self.sp(repr(self))``. + +Some submodule examples of inheritance: + +- `tqdm/notebook.py `__ +- `tqdm/gui.py `__ +- `tqdm/tk.py `__ +- `tqdm/contrib/slack.py `__ +- `tqdm/contrib/discord.py `__ +- `tqdm/contrib/telegram.py `__ + +Dynamic Monitor/Meter +~~~~~~~~~~~~~~~~~~~~~ + +You can use a ``tqdm`` as a meter which is not monotonically increasing. +This could be because ``n`` decreases (e.g. a CPU usage monitor) or ``total`` +changes. + +One example would be recursively searching for files. The ``total`` is the +number of objects found so far, while ``n`` is the number of those objects which +are files (rather than folders): + +.. code:: python + + from tqdm import tqdm + import os.path + + def find_files_recursively(path, show_progress=True): + files = [] + # total=1 assumes `path` is a file + t = tqdm(total=1, unit="file", disable=not show_progress) + if not os.path.exists(path): + raise IOError("Cannot find:" + path) + + def append_found_file(f): + files.append(f) + t.update() + + def list_found_dir(path): + """returns os.listdir(path) assuming os.path.isdir(path)""" + listing = os.listdir(path) + # subtract 1 since a "file" we found was actually this directory + t.total += len(listing) - 1 + # fancy way to give info without forcing a refresh + t.set_postfix(dir=path[-10:], refresh=False) + t.update(0) # may trigger a refresh + return listing + + def recursively_search(path): + if os.path.isdir(path): + for f in list_found_dir(path): + recursively_search(os.path.join(path, f)) + else: + append_found_file(path) + + recursively_search(path) + t.set_postfix(dir=path) + t.close() + return files + +Using ``update(0)`` is a handy way to let ``tqdm`` decide when to trigger a +display refresh to avoid console spamming. + +Writing messages +~~~~~~~~~~~~~~~~ + +This is a work in progress (see +`#737 `__). + +Since ``tqdm`` uses a simple printing mechanism to display progress bars, +you should not write any message in the terminal using ``print()`` while +a progressbar is open. + +To write messages in the terminal without any collision with ``tqdm`` bar +display, a ``.write()`` method is provided: + +.. code:: python + + from tqdm.auto import tqdm, trange + from time import sleep + + bar = trange(10) + for i in bar: + # Print using tqdm class method .write() + sleep(0.1) + if not (i % 3): + tqdm.write("Done task %i" % i) + # Can also use bar.write() + +By default, this will print to standard output ``sys.stdout``. but you can +specify any file-like object using the ``file`` argument. For example, this +can be used to redirect the messages writing to a log file or class. + +Redirecting writing +~~~~~~~~~~~~~~~~~~~ + +If using a library that can print messages to the console, editing the library +by replacing ``print()`` with ``tqdm.write()`` may not be desirable. +In that case, redirecting ``sys.stdout`` to ``tqdm.write()`` is an option. + +To redirect ``sys.stdout``, create a file-like class that will write +any input string to ``tqdm.write()``, and supply the arguments +``file=sys.stdout, dynamic_ncols=True``. + +A reusable canonical example is given below: + +.. code:: python + + from time import sleep + import contextlib + import sys + from tqdm import tqdm + from tqdm.contrib import DummyTqdmFile + + + @contextlib.contextmanager + def std_out_err_redirect_tqdm(): + orig_out_err = sys.stdout, sys.stderr + try: + sys.stdout, sys.stderr = map(DummyTqdmFile, orig_out_err) + yield orig_out_err[0] + # Relay exceptions + except Exception as exc: + raise exc + # Always restore sys.stdout/err if necessary + finally: + sys.stdout, sys.stderr = orig_out_err + + def some_fun(i): + print("Fee, fi, fo,".split()[i]) + + # Redirect stdout to tqdm.write() (don't forget the `as save_stdout`) + with std_out_err_redirect_tqdm() as orig_stdout: + # tqdm needs the original stdout + # and dynamic_ncols=True to autodetect console width + for i in tqdm(range(3), file=orig_stdout, dynamic_ncols=True): + sleep(.5) + some_fun(i) + + # After the `with`, printing is restored + print("Done!") + +Redirecting ``logging`` +~~~~~~~~~~~~~~~~~~~~~~~ + +Similar to ``sys.stdout``/``sys.stderr`` as detailed above, console ``logging`` +may also be redirected to ``tqdm.write()``. + +Warning: if also redirecting ``sys.stdout``/``sys.stderr``, make sure to +redirect ``logging`` first if needed. + +Helper methods are available in ``tqdm.contrib.logging``. For example: + +.. code:: python + + import logging + from tqdm import trange + from tqdm.contrib.logging import logging_redirect_tqdm + + LOG = logging.getLogger(__name__) + + if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + with logging_redirect_tqdm(): + for i in trange(9): + if i == 4: + LOG.info("console logging redirected to `tqdm.write()`") + # logging restored + +Monitoring thread, intervals and miniters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``tqdm`` implements a few tricks to increase efficiency and reduce overhead. + +- Avoid unnecessary frequent bar refreshing: ``mininterval`` defines how long + to wait between each refresh. ``tqdm`` always gets updated in the background, + but it will display only every ``mininterval``. +- Reduce number of calls to check system clock/time. +- ``mininterval`` is more intuitive to configure than ``miniters``. + A clever adjustment system ``dynamic_miniters`` will automatically adjust + ``miniters`` to the amount of iterations that fit into time ``mininterval``. + Essentially, ``tqdm`` will check if it's time to print without actually + checking time. This behaviour can be still be bypassed by manually setting + ``miniters``. + +However, consider a case with a combination of fast and slow iterations. +After a few fast iterations, ``dynamic_miniters`` will set ``miniters`` to a +large number. When iteration rate subsequently slows, ``miniters`` will +remain large and thus reduce display update frequency. To address this: + +- ``maxinterval`` defines the maximum time between display refreshes. + A concurrent monitoring thread checks for overdue updates and forces one + where necessary. + +The monitoring thread should not have a noticeable overhead, and guarantees +updates at least every 10 seconds by default. +This value can be directly changed by setting the ``monitor_interval`` of +any ``tqdm`` instance (i.e. ``t = tqdm.tqdm(...); t.monitor_interval = 2``). +The monitor thread may be disabled application-wide by setting +``tqdm.tqdm.monitor_interval = 0`` before instantiation of any ``tqdm`` bar. + + +Merch +----- + +You can buy `tqdm branded merch `__ now! + +Contributions +------------- + +|GitHub-Commits| |GitHub-Issues| |GitHub-PRs| |OpenHub-Status| |GitHub-Contributions| |CII Best Practices| + +All source code is hosted on `GitHub `__. +Contributions are welcome. + +See the +`CONTRIBUTING `__ +file for more information. + +Developers who have made significant contributions, ranked by *SLoC* +(surviving lines of code, +`git fame `__ ``-wMC --excl '\.(png|gif|jpg)$'``), +are: + +==================== ======================================================== ==== ================================ +Name ID SLoC Notes +==================== ======================================================== ==== ================================ +Casper da Costa-Luis `casperdcl `__ ~80% primary maintainer |Gift-Casper| +Stephen Larroque `lrq3000 `__ ~9% team member +Martin Zugnoni `martinzugnoni `__ ~3% +Daniel Ecer `de-code `__ ~2% +Richard Sheridan `richardsheridan `__ ~1% +Guangshuo Chen `chengs `__ ~1% +Helio Machado `0x2b3bfa0 `__ ~1% +Kyle Altendorf `altendky `__ <1% +Noam Yorav-Raphael `noamraph `__ <1% original author +Matthew Stevens `mjstevens777 `__ <1% +Hadrien Mary `hadim `__ <1% team member +Mikhail Korobov `kmike `__ <1% team member +==================== ======================================================== ==== ================================ + +Ports to Other Languages +~~~~~~~~~~~~~~~~~~~~~~~~ + +A list is available on +`this wiki page `__. + + +LICENCE +------- + +Open Source (OSI approved): |LICENCE| + +Citation information: |DOI| + +|README-Hits| (Since 19 May 2016) + +.. |Logo| image:: https://tqdm.github.io/img/logo.gif +.. |Screenshot| image:: https://tqdm.github.io/img/tqdm.gif +.. |Video| image:: https://tqdm.github.io/img/video.jpg + :target: https://tqdm.github.io/video +.. |Slides| image:: https://tqdm.github.io/img/slides.jpg + :target: https://tqdm.github.io/PyData2019/slides.html +.. |Merch| image:: https://tqdm.github.io/img/merch.jpg + :target: https://tqdm.github.io/merch +.. |Build-Status| image:: https://img.shields.io/github/actions/workflow/status/tqdm/tqdm/test.yml?branch=master&label=tqdm&logo=GitHub + :target: https://github.com/tqdm/tqdm/actions/workflows/test.yml +.. |Coverage-Status| image:: https://img.shields.io/coveralls/github/tqdm/tqdm/master?logo=coveralls + :target: https://coveralls.io/github/tqdm/tqdm +.. |Branch-Coverage-Status| image:: https://codecov.io/gh/tqdm/tqdm/branch/master/graph/badge.svg + :target: https://codecov.io/gh/tqdm/tqdm +.. |Codacy-Grade| image:: https://app.codacy.com/project/badge/Grade/3f965571598f44549c7818f29cdcf177 + :target: https://www.codacy.com/gh/tqdm/tqdm/dashboard +.. |CII Best Practices| image:: https://bestpractices.coreinfrastructure.org/projects/3264/badge + :target: https://bestpractices.coreinfrastructure.org/projects/3264 +.. |GitHub-Status| image:: https://img.shields.io/github/tag/tqdm/tqdm.svg?maxAge=86400&logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/releases +.. |GitHub-Forks| image:: https://img.shields.io/github/forks/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/network +.. |GitHub-Stars| image:: https://img.shields.io/github/stars/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/stargazers +.. |GitHub-Commits| image:: https://img.shields.io/github/commit-activity/y/tqdm/tqdm.svg?logo=git&logoColor=white + :target: https://github.com/tqdm/tqdm/graphs/commit-activity +.. |GitHub-Issues| image:: https://img.shields.io/github/issues-closed/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/issues?q= +.. |GitHub-PRs| image:: https://img.shields.io/github/issues-pr-closed/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/pulls +.. |GitHub-Contributions| image:: https://img.shields.io/github/contributors/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/graphs/contributors +.. |GitHub-Updated| image:: https://img.shields.io/github/last-commit/tqdm/tqdm/master.svg?logo=github&logoColor=white&label=pushed + :target: https://github.com/tqdm/tqdm/pulse +.. |Gift-Casper| image:: https://img.shields.io/badge/dynamic/json.svg?color=ff69b4&label=gifts%20received&prefix=%C2%A3&query=%24..sum&url=https%3A%2F%2Fcaspersci.uk.to%2Fgifts.json + :target: https://cdcl.ml/sponsor +.. |Versions| image:: https://img.shields.io/pypi/v/tqdm.svg + :target: https://tqdm.github.io/releases +.. |PyPI-Downloads| image:: https://img.shields.io/pypi/dm/tqdm.svg?label=pypi%20downloads&logo=PyPI&logoColor=white + :target: https://pepy.tech/project/tqdm +.. |Py-Versions| image:: https://img.shields.io/pypi/pyversions/tqdm.svg?logo=python&logoColor=white + :target: https://pypi.org/project/tqdm +.. |Conda-Forge-Status| image:: https://img.shields.io/conda/v/conda-forge/tqdm.svg?label=conda-forge&logo=conda-forge + :target: https://anaconda.org/conda-forge/tqdm +.. |Snapcraft| image:: https://img.shields.io/badge/snap-install-82BEA0.svg?logo=snapcraft + :target: https://snapcraft.io/tqdm +.. |Docker| image:: https://img.shields.io/badge/docker-pull-blue.svg?logo=docker&logoColor=white + :target: https://hub.docker.com/r/tqdm/tqdm +.. |Libraries-Rank| image:: https://img.shields.io/librariesio/sourcerank/pypi/tqdm.svg?logo=koding&logoColor=white + :target: https://libraries.io/pypi/tqdm +.. |Libraries-Dependents| image:: https://img.shields.io/librariesio/dependent-repos/pypi/tqdm.svg?logo=koding&logoColor=white + :target: https://github.com/tqdm/tqdm/network/dependents +.. |OpenHub-Status| image:: https://www.openhub.net/p/tqdm/widgets/project_thin_badge?format=gif + :target: https://www.openhub.net/p/tqdm?ref=Thin+badge +.. |awesome-python| image:: https://awesome.re/mentioned-badge.svg + :target: https://github.com/vinta/awesome-python +.. |LICENCE| image:: https://img.shields.io/pypi/l/tqdm.svg + :target: https://raw.githubusercontent.com/tqdm/tqdm/master/LICENCE +.. |DOI| image:: https://img.shields.io/badge/DOI-10.5281/zenodo.595120-blue.svg + :target: https://doi.org/10.5281/zenodo.595120 +.. |binder-demo| image:: https://mybinder.org/badge_logo.svg + :target: https://mybinder.org/v2/gh/tqdm/tqdm/master?filepath=DEMO.ipynb +.. |Screenshot-Jupyter1| image:: https://tqdm.github.io/img/jupyter-1.gif +.. |Screenshot-Jupyter2| image:: https://tqdm.github.io/img/jupyter-2.gif +.. |Screenshot-Jupyter3| image:: https://tqdm.github.io/img/jupyter-3.gif +.. |README-Hits| image:: https://cgi.cdcl.ml/hits?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif + :target: https://cgi.cdcl.ml/hits?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif&style=social diff --git a/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/RECORD b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..d110040e58686096bbb699a8f01e221b88b11a67 --- /dev/null +++ b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/RECORD @@ -0,0 +1,75 @@ +../../../bin/tqdm,sha256=1YTmEpk7ppczVfmPRZrFH-PBaYf1hvSAwdjKzGFbuoc,243 +tqdm-4.67.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +tqdm-4.67.1.dist-info/LICENCE,sha256=3DMlLoKQFeOxUAhvubOkD2rW-zLC9GEM6BL6Z301mGo,1985 +tqdm-4.67.1.dist-info/METADATA,sha256=aIoWMt9SWhmP7FLc_vsSRtMerO6cA1qsrC1-r42P9mk,57675 +tqdm-4.67.1.dist-info/RECORD,, +tqdm-4.67.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +tqdm-4.67.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91 +tqdm-4.67.1.dist-info/entry_points.txt,sha256=ReJCH7Ui3Zyh6M16E4OhsZ1oU7WtMXCfbtoyBhGO29Y,39 +tqdm-4.67.1.dist-info/top_level.txt,sha256=NLiUJNfmc9At15s7JURiwvqMEjUi9G5PMGRrmMYzNSM,5 +tqdm/__init__.py,sha256=9mQNYSSqP99JasubEC1POJLMmhkkBH6cJZxPIR5G2pQ,1572 +tqdm/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30 +tqdm/__pycache__/__init__.cpython-312.pyc,, +tqdm/__pycache__/__main__.cpython-312.pyc,, +tqdm/__pycache__/_dist_ver.cpython-312.pyc,, +tqdm/__pycache__/_main.cpython-312.pyc,, +tqdm/__pycache__/_monitor.cpython-312.pyc,, +tqdm/__pycache__/_tqdm.cpython-312.pyc,, +tqdm/__pycache__/_tqdm_gui.cpython-312.pyc,, +tqdm/__pycache__/_tqdm_notebook.cpython-312.pyc,, +tqdm/__pycache__/_tqdm_pandas.cpython-312.pyc,, +tqdm/__pycache__/_utils.cpython-312.pyc,, +tqdm/__pycache__/asyncio.cpython-312.pyc,, +tqdm/__pycache__/auto.cpython-312.pyc,, +tqdm/__pycache__/autonotebook.cpython-312.pyc,, +tqdm/__pycache__/cli.cpython-312.pyc,, +tqdm/__pycache__/dask.cpython-312.pyc,, +tqdm/__pycache__/gui.cpython-312.pyc,, +tqdm/__pycache__/keras.cpython-312.pyc,, +tqdm/__pycache__/notebook.cpython-312.pyc,, +tqdm/__pycache__/rich.cpython-312.pyc,, +tqdm/__pycache__/std.cpython-312.pyc,, +tqdm/__pycache__/tk.cpython-312.pyc,, +tqdm/__pycache__/utils.cpython-312.pyc,, +tqdm/__pycache__/version.cpython-312.pyc,, +tqdm/_dist_ver.py,sha256=m5AdYI-jB-v6P0VJ_70isH_p24EzSOGSwVvuAZmkmKY,23 +tqdm/_main.py,sha256=9ySvgmi_2Sw4CAo5UDW0Q2dxfTryboEWGHohfCJz0sA,283 +tqdm/_monitor.py,sha256=Uku-DPWgzJ7dO5CK08xKJK-E_F6qQ-JB3ksuXczSYR0,3699 +tqdm/_tqdm.py,sha256=LfLCuJ6bpsVo9xilmtBXyEm1vGnUCFrliW85j3J-nD4,283 +tqdm/_tqdm_gui.py,sha256=03Hc8KayxJveieI5-0-2NGiDpLvw9jZekofJUV7CCwk,287 +tqdm/_tqdm_notebook.py,sha256=BuHiLuxu6uEfZFaPJW3RPpPaxaVctEQA3kdSJSDL1hw,307 +tqdm/_tqdm_pandas.py,sha256=c9jptUgigN6axRDhRd4Rif98Tmxeopc1nFNFhIpbFUE,888 +tqdm/_utils.py,sha256=_4E73bfDj4f1s3sM42NLHNrZDOkijZoWq-n6xWLkdZ8,553 +tqdm/asyncio.py,sha256=Kp2rSkNRf9KRqa3d9YpgeZQ7L7EZf2Ki4bSc7UPIyoo,2757 +tqdm/auto.py,sha256=nDZflj6p2zKkjBCNBourrhS81zYfZy1_dQvbckrdW8o,871 +tqdm/autonotebook.py,sha256=Yb9F5uaiBPhfbDDFpbtoG8I2YUw3uQJ89rUDLbfR6ws,956 +tqdm/cli.py,sha256=SbKlN8QyZ2ogenqt-wT_p6_sx2OOdCjCyhoZBFnlmyI,11010 +tqdm/completion.sh,sha256=j79KbSmpIj_E11jfTfBXrGnUTzKXVpQ1vGVQvsyDRl4,946 +tqdm/contrib/__init__.py,sha256=OgSwVXm-vlDJ-2imtoQ9z8qdom4snMSRztH72KMA82A,2494 +tqdm/contrib/__pycache__/__init__.cpython-312.pyc,, +tqdm/contrib/__pycache__/bells.cpython-312.pyc,, +tqdm/contrib/__pycache__/concurrent.cpython-312.pyc,, +tqdm/contrib/__pycache__/discord.cpython-312.pyc,, +tqdm/contrib/__pycache__/itertools.cpython-312.pyc,, +tqdm/contrib/__pycache__/logging.cpython-312.pyc,, +tqdm/contrib/__pycache__/slack.cpython-312.pyc,, +tqdm/contrib/__pycache__/telegram.cpython-312.pyc,, +tqdm/contrib/__pycache__/utils_worker.cpython-312.pyc,, +tqdm/contrib/bells.py,sha256=Yx1HqGCmHrESCAO700j5wE__JCleNODJxedh1ijPLD0,837 +tqdm/contrib/concurrent.py,sha256=K1yjloKS5WRNFyjLRth0DmU5PAnDbF0A-GD27N-J4a8,3986 +tqdm/contrib/discord.py,sha256=MtVIL1s_dxH21G4sL8FBgQ4Wei23ho9Ek5T-AommvNc,5243 +tqdm/contrib/itertools.py,sha256=WdKKQU5eSzsqHu29SN_oH12huYZo0Jihqoi9-nVhwz4,774 +tqdm/contrib/logging.py,sha256=NsYtnKttj2mMrGm58mEdo5a9DP_2vv8pZyrimSuWulA,3760 +tqdm/contrib/slack.py,sha256=eP_Mr5sQonYniHxxQNGue3jk2JkIPmPWFZqIYxnOui0,4007 +tqdm/contrib/telegram.py,sha256=vn_9SATMbbwn2PAbzSDyOX6av3eBB01QBug11P4H-Og,5008 +tqdm/contrib/utils_worker.py,sha256=HJP5Mz1S1xyzEke2JaqJ2sYLHXADYoo2epT5AzQ38eA,1207 +tqdm/dask.py,sha256=9Ei58eVqTossRLhAfWyUFCduXYKjmLmwkaXIy-CHYfs,1319 +tqdm/gui.py,sha256=STIB3K8iDzDgkNUqWIpvcI_u0OGtbGNy5NwpALXhfWs,5479 +tqdm/keras.py,sha256=op9sBkb6q6c6dw2wJ0SD2ZwpPK7yM1Vbg4l1Qiy3MIo,4373 +tqdm/notebook.py,sha256=GtZ3IapLL1v8WNDaTSvPw0bJGTyfp71Vfz5HDnAzx1M,10895 +tqdm/rich.py,sha256=YyMPkEHVyYUVUR3adJKbVX26iTmNKpNMf3DEqmm-m60,5021 +tqdm/std.py,sha256=tWjz6-QCa92aqYjz7PIdkLUCAfiy-lJZheBtZyIIyO0,57461 +tqdm/tk.py,sha256=Gu0uwXwLCGPRGHORdi3WvBLGiseUp_xxX_h_gp9VpK0,6701 +tqdm/tqdm.1,sha256=aILyUPk2S4OPe_uWy2P4AMjUf0oQ6PUW0nLYXB-BWwI,7889 +tqdm/utils.py,sha256=6E0BQw3Sg7uGWKBM_cDn3P42tXswRhzkggbhBgLDjl8,11821 +tqdm/version.py,sha256=-1yWjfu3P0eghVsysHH07fbzdiADNRdzRtYPqOaqR2A,333 diff --git a/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/REQUESTED b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/WHEEL b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..ae527e7d64811439e61b93aa375defb30e06edfe --- /dev/null +++ b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.6.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/entry_points.txt b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..540e60f4e073bc53a5f0a521a3639e0d80780af4 --- /dev/null +++ b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +tqdm = tqdm.cli:main diff --git a/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/top_level.txt b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..78620c472c9d799a14ccb02a0233f4669b3bcdcb --- /dev/null +++ b/lib/python3.12/site-packages/tqdm-4.67.1.dist-info/top_level.txt @@ -0,0 +1 @@ +tqdm diff --git a/lib/python3.12/site-packages/zipp-3.23.0.dist-info/INSTALLER b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/lib/python3.12/site-packages/zipp-3.23.0.dist-info/METADATA b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..6420117987041c052142deea6b16884cdb435c2f --- /dev/null +++ b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/METADATA @@ -0,0 +1,106 @@ +Metadata-Version: 2.4 +Name: zipp +Version: 3.23.0 +Summary: Backport of pathlib-compatible object wrapper for zip files +Author-email: "Jason R. Coombs" +License-Expression: MIT +Project-URL: Source, https://github.com/jaraco/zipp +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE +Provides-Extra: test +Requires-Dist: pytest!=8.1.*,>=6; extra == "test" +Requires-Dist: jaraco.itertools; extra == "test" +Requires-Dist: jaraco.functools; extra == "test" +Requires-Dist: more_itertools; extra == "test" +Requires-Dist: big-O; extra == "test" +Requires-Dist: pytest-ignore-flaky; extra == "test" +Requires-Dist: jaraco.test; extra == "test" +Provides-Extra: doc +Requires-Dist: sphinx>=3.5; extra == "doc" +Requires-Dist: jaraco.packaging>=9.3; extra == "doc" +Requires-Dist: rst.linker>=1.9; extra == "doc" +Requires-Dist: furo; extra == "doc" +Requires-Dist: sphinx-lint; extra == "doc" +Requires-Dist: jaraco.tidelift>=1.4; extra == "doc" +Provides-Extra: check +Requires-Dist: pytest-checkdocs>=2.4; extra == "check" +Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check" +Provides-Extra: cover +Requires-Dist: pytest-cov; extra == "cover" +Provides-Extra: enabler +Requires-Dist: pytest-enabler>=2.2; extra == "enabler" +Provides-Extra: type +Requires-Dist: pytest-mypy; extra == "type" +Dynamic: license-file + +.. image:: https://img.shields.io/pypi/v/zipp.svg + :target: https://pypi.org/project/zipp + +.. image:: https://img.shields.io/pypi/pyversions/zipp.svg + +.. image:: https://github.com/jaraco/zipp/actions/workflows/main.yml/badge.svg + :target: https://github.com/jaraco/zipp/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Ruff + +.. image:: https://readthedocs.org/projects/zipp/badge/?version=latest +.. :target: https://zipp.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2025-informational + :target: https://blog.jaraco.com/skeleton + +.. image:: https://tidelift.com/badges/package/pypi/zipp + :target: https://tidelift.com/subscription/pkg/pypi-zipp?utm_source=pypi-zipp&utm_medium=readme + + +A pathlib-compatible Zipfile object wrapper. Official backport of the standard library +`Path object `_. + + +Compatibility +============= + +New features are introduced in this third-party library and later merged +into CPython. The following table indicates which versions of this library +were contributed to different versions in the standard library: + +.. list-table:: + :header-rows: 1 + + * - zipp + - stdlib + * - 3.18 + - 3.13 + * - 3.16 + - 3.12 + * - 3.5 + - 3.11 + * - 3.2 + - 3.10 + * - 3.3 ?? + - 3.9 + * - 1.0 + - 3.8 + + +Usage +===== + +Use ``zipp.Path`` in place of ``zipfile.Path`` on any Python. + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. diff --git a/lib/python3.12/site-packages/zipp-3.23.0.dist-info/RECORD b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..93f6395fdf1cd20240b0881c5c0adba547a1ba5b --- /dev/null +++ b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/RECORD @@ -0,0 +1,20 @@ +zipp-3.23.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +zipp-3.23.0.dist-info/METADATA,sha256=vdZ9TRbPC_O4k-fRjNPS13StuC837Zhbx3cMYHIms1s,3563 +zipp-3.23.0.dist-info/RECORD,, +zipp-3.23.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +zipp-3.23.0.dist-info/licenses/LICENSE,sha256=WlfLTbheKi3YjCkGKJCK3VfjRRRJ4KmnH9-zh3b9dZ0,1076 +zipp-3.23.0.dist-info/top_level.txt,sha256=iAbdoSHfaGqBfVb2XuR9JqSQHCoOsOtG6y9C_LSpqFw,5 +zipp/__init__.py,sha256=ieXh9GIMdABjKRX_JUJtP9k5wdBLK4Mt5X4nszSkmYE,11976 +zipp/__pycache__/__init__.cpython-312.pyc,, +zipp/__pycache__/_functools.cpython-312.pyc,, +zipp/__pycache__/glob.cpython-312.pyc,, +zipp/_functools.py,sha256=f6Kt9LxZ4TE-cY1lJVdXSId3memSXmH9IdgMbU-_x2k,575 +zipp/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +zipp/compat/__pycache__/__init__.cpython-312.pyc,, +zipp/compat/__pycache__/overlay.cpython-312.pyc,, +zipp/compat/__pycache__/py310.cpython-312.pyc,, +zipp/compat/__pycache__/py313.cpython-312.pyc,, +zipp/compat/overlay.py,sha256=oEIGAnbr8yGjuKTrVSO2ByewPui71uppbX18BLnYTKE,783 +zipp/compat/py310.py,sha256=S7i6N9mToEn3asNb2ILyjnzvITOXrATD_J4emjyBbDU,256 +zipp/compat/py313.py,sha256=RndvDNtuY7H2D9ecnnzcPBMZ8mZc42gmXD_IwQAXXAE,654 +zipp/glob.py,sha256=DLV9LBsDxA6YVW82e3-tkoNrus1h4R-j3BR6VqS0AzE,3382 diff --git a/lib/python3.12/site-packages/zipp-3.23.0.dist-info/WHEEL b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822 --- /dev/null +++ b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/lib/python3.12/site-packages/zipp-3.23.0.dist-info/licenses/LICENSE b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f60bd572013c6abcb3a82ba9b50d84935de6394f --- /dev/null +++ b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/licenses/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2025 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/python3.12/site-packages/zipp-3.23.0.dist-info/top_level.txt b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..e82f676f82a3381fa909d1e6578c7a22044fafca --- /dev/null +++ b/lib/python3.12/site-packages/zipp-3.23.0.dist-info/top_level.txt @@ -0,0 +1 @@ +zipp