From 5bd617d79b362cb811ba0a733c21b079b9812063 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 1 Apr 2025 11:52:12 +0100 Subject: [PATCH 1/5] Upgrade platformdirs to 4.3.7 --- news/platformdirs.vendor.rst | 1 + src/pip/_vendor/platformdirs/__init__.py | 40 ++++++++++++------------ src/pip/_vendor/platformdirs/android.py | 6 ++-- src/pip/_vendor/platformdirs/api.py | 5 +-- src/pip/_vendor/platformdirs/unix.py | 5 ++- src/pip/_vendor/platformdirs/version.py | 13 +++++--- src/pip/_vendor/vendor.txt | 2 +- 7 files changed, 41 insertions(+), 31 deletions(-) create mode 100644 news/platformdirs.vendor.rst diff --git a/news/platformdirs.vendor.rst b/news/platformdirs.vendor.rst new file mode 100644 index 00000000000..d1f397d2308 --- /dev/null +++ b/news/platformdirs.vendor.rst @@ -0,0 +1 @@ +Upgrade platformdirs to 4.3.7 diff --git a/src/pip/_vendor/platformdirs/__init__.py b/src/pip/_vendor/platformdirs/__init__.py index edc21fad2e9..2325ec2eb6f 100644 --- a/src/pip/_vendor/platformdirs/__init__.py +++ b/src/pip/_vendor/platformdirs/__init__.py @@ -52,7 +52,7 @@ def _set_platform_dir_class() -> type[PlatformDirsABC]: def user_data_dir( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, roaming: bool = False, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -76,7 +76,7 @@ def user_data_dir( def site_data_dir( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, multipath: bool = False, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -100,7 +100,7 @@ def site_data_dir( def user_config_dir( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, roaming: bool = False, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -124,7 +124,7 @@ def user_config_dir( def site_config_dir( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, multipath: bool = False, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -148,7 +148,7 @@ def site_config_dir( def user_cache_dir( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, opinion: bool = True, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -172,7 +172,7 @@ def user_cache_dir( def site_cache_dir( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, opinion: bool = True, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -196,7 +196,7 @@ def site_cache_dir( def user_state_dir( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, roaming: bool = False, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -220,7 +220,7 @@ def user_state_dir( def user_log_dir( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, opinion: bool = True, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -274,7 +274,7 @@ def user_desktop_dir() -> str: def user_runtime_dir( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, opinion: bool = True, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -298,7 +298,7 @@ def user_runtime_dir( def site_runtime_dir( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, opinion: bool = True, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -322,7 +322,7 @@ def site_runtime_dir( def user_data_path( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, roaming: bool = False, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -346,7 +346,7 @@ def user_data_path( def site_data_path( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, multipath: bool = False, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -370,7 +370,7 @@ def site_data_path( def user_config_path( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, roaming: bool = False, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -394,7 +394,7 @@ def user_config_path( def site_config_path( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, multipath: bool = False, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -418,7 +418,7 @@ def site_config_path( def site_cache_path( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, opinion: bool = True, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -442,7 +442,7 @@ def site_cache_path( def user_cache_path( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, opinion: bool = True, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -466,7 +466,7 @@ def user_cache_path( def user_state_path( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, roaming: bool = False, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -490,7 +490,7 @@ def user_state_path( def user_log_path( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, opinion: bool = True, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -544,7 +544,7 @@ def user_desktop_path() -> Path: def user_runtime_path( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, opinion: bool = True, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 @@ -568,7 +568,7 @@ def user_runtime_path( def site_runtime_path( appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, opinion: bool = True, # noqa: FBT001, FBT002 ensure_exists: bool = False, # noqa: FBT001, FBT002 diff --git a/src/pip/_vendor/platformdirs/android.py b/src/pip/_vendor/platformdirs/android.py index 7004a852422..92efc852d38 100644 --- a/src/pip/_vendor/platformdirs/android.py +++ b/src/pip/_vendor/platformdirs/android.py @@ -23,7 +23,7 @@ class Android(PlatformDirsABC): @property def user_data_dir(self) -> str: """:return: data directory tied to the user, e.g. ``/data/user///files/``""" - return self._append_app_name_and_version(cast(str, _android_folder()), "files") + return self._append_app_name_and_version(cast("str", _android_folder()), "files") @property def site_data_dir(self) -> str: @@ -36,7 +36,7 @@ def user_config_dir(self) -> str: :return: config directory tied to the user, e.g. \ ``/data/user///shared_prefs/`` """ - return self._append_app_name_and_version(cast(str, _android_folder()), "shared_prefs") + return self._append_app_name_and_version(cast("str", _android_folder()), "shared_prefs") @property def site_config_dir(self) -> str: @@ -46,7 +46,7 @@ def site_config_dir(self) -> str: @property def user_cache_dir(self) -> str: """:return: cache directory tied to the user, e.g.,``/data/user///cache/``""" - return self._append_app_name_and_version(cast(str, _android_folder()), "cache") + return self._append_app_name_and_version(cast("str", _android_folder()), "cache") @property def site_cache_dir(self) -> str: diff --git a/src/pip/_vendor/platformdirs/api.py b/src/pip/_vendor/platformdirs/api.py index 18d660e4f8c..a352035ec69 100644 --- a/src/pip/_vendor/platformdirs/api.py +++ b/src/pip/_vendor/platformdirs/api.py @@ -8,7 +8,8 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Iterator, Literal + from collections.abc import Iterator + from typing import Literal class PlatformDirsABC(ABC): # noqa: PLR0904 @@ -17,7 +18,7 @@ class PlatformDirsABC(ABC): # noqa: PLR0904 def __init__( # noqa: PLR0913, PLR0917 self, appname: str | None = None, - appauthor: str | None | Literal[False] = None, + appauthor: str | Literal[False] | None = None, version: str | None = None, roaming: bool = False, # noqa: FBT001, FBT002 multipath: bool = False, # noqa: FBT001, FBT002 diff --git a/src/pip/_vendor/platformdirs/unix.py b/src/pip/_vendor/platformdirs/unix.py index f1942e92ef4..fc75d8d0747 100644 --- a/src/pip/_vendor/platformdirs/unix.py +++ b/src/pip/_vendor/platformdirs/unix.py @@ -6,10 +6,13 @@ import sys from configparser import ConfigParser from pathlib import Path -from typing import Iterator, NoReturn +from typing import TYPE_CHECKING, NoReturn from .api import PlatformDirsABC +if TYPE_CHECKING: + from collections.abc import Iterator + if sys.platform == "win32": def getuid() -> NoReturn: diff --git a/src/pip/_vendor/platformdirs/version.py b/src/pip/_vendor/platformdirs/version.py index afb49243e3d..ed85187adca 100644 --- a/src/pip/_vendor/platformdirs/version.py +++ b/src/pip/_vendor/platformdirs/version.py @@ -1,8 +1,13 @@ -# file generated by setuptools_scm +# file generated by setuptools-scm # don't change, don't track in version control + +__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"] + TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Tuple, Union + from typing import Tuple + from typing import Union + VERSION_TUPLE = Tuple[Union[int, str], ...] else: VERSION_TUPLE = object @@ -12,5 +17,5 @@ __version_tuple__: VERSION_TUPLE version_tuple: VERSION_TUPLE -__version__ = version = '4.3.6' -__version_tuple__ = version_tuple = (4, 3, 6) +__version__ = version = '4.3.7' +__version_tuple__ = version_tuple = (4, 3, 7) diff --git a/src/pip/_vendor/vendor.txt b/src/pip/_vendor/vendor.txt index 0c7733ff8a5..78b7ed2dd8e 100644 --- a/src/pip/_vendor/vendor.txt +++ b/src/pip/_vendor/vendor.txt @@ -3,7 +3,7 @@ distlib==0.3.9 distro==1.9.0 msgpack==1.1.0 packaging==24.2 -platformdirs==4.3.6 +platformdirs==4.3.7 pyproject-hooks==1.2.0 requests==2.32.3 certifi==2025.1.31 From 9490bdf055ec7a086e7ba0ed8d250096d4861a2a Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 1 Apr 2025 11:52:51 +0100 Subject: [PATCH 2/5] Upgrade rich to 14.0.0 --- news/rich.vendor.rst | 1 + src/pip/_vendor/rich/console.py | 28 +++- src/pip/_vendor/rich/default_styles.py | 4 +- src/pip/_vendor/rich/diagnose.py | 13 +- src/pip/_vendor/rich/panel.py | 2 +- src/pip/_vendor/rich/style.py | 2 +- src/pip/_vendor/rich/table.py | 1 - src/pip/_vendor/rich/traceback.py | 171 +++++++++++++++++++------ src/pip/_vendor/vendor.txt | 2 +- 9 files changed, 164 insertions(+), 60 deletions(-) create mode 100644 news/rich.vendor.rst diff --git a/news/rich.vendor.rst b/news/rich.vendor.rst new file mode 100644 index 00000000000..a2f369fa5aa --- /dev/null +++ b/news/rich.vendor.rst @@ -0,0 +1 @@ +Upgrade rich to 14.0.0 diff --git a/src/pip/_vendor/rich/console.py b/src/pip/_vendor/rich/console.py index 572884542a1..57474835f92 100644 --- a/src/pip/_vendor/rich/console.py +++ b/src/pip/_vendor/rich/console.py @@ -500,7 +500,7 @@ def group(fit: bool = True) -> Callable[..., Callable[..., Group]]: """ def decorator( - method: Callable[..., Iterable[RenderableType]] + method: Callable[..., Iterable[RenderableType]], ) -> Callable[..., Group]: """Convert a method that returns an iterable of renderables in to a Group.""" @@ -735,7 +735,9 @@ def __init__( self.get_time = get_time or monotonic self.style = style self.no_color = ( - no_color if no_color is not None else "NO_COLOR" in self._environ + no_color + if no_color is not None + else self._environ.get("NO_COLOR", "") != "" ) self.is_interactive = ( (self.is_terminal and not self.is_dumb_terminal) @@ -933,11 +935,13 @@ def is_terminal(self) -> bool: Returns: bool: True if the console writing to a device capable of - understanding terminal codes, otherwise False. + understanding escape sequences, otherwise False. """ + # If dev has explicitly set this value, return it if self._force_terminal is not None: return self._force_terminal + # Fudge for Idle if hasattr(sys.stdin, "__module__") and sys.stdin.__module__.startswith( "idlelib" ): @@ -948,12 +952,22 @@ def is_terminal(self) -> bool: # return False for Jupyter, which may have FORCE_COLOR set return False - # If FORCE_COLOR env var has any value at all, we assume a terminal. - force_color = self._environ.get("FORCE_COLOR") - if force_color is not None: - self._force_terminal = True + environ = self._environ + + tty_compatible = environ.get("TTY_COMPATIBLE", "") + # 0 indicates device is not tty compatible + if tty_compatible == "0": + return False + # 1 indicates device is tty compatible + if tty_compatible == "1": return True + # https://force-color.org/ + force_color = environ.get("FORCE_COLOR") + if force_color is not None: + return force_color != "" + + # Any other value defaults to auto detect isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None) try: return False if isatty is None else isatty() diff --git a/src/pip/_vendor/rich/default_styles.py b/src/pip/_vendor/rich/default_styles.py index 6c0d73231d8..61797bf312c 100644 --- a/src/pip/_vendor/rich/default_styles.py +++ b/src/pip/_vendor/rich/default_styles.py @@ -120,7 +120,9 @@ "traceback.exc_type": Style(color="bright_red", bold=True), "traceback.exc_value": Style.null(), "traceback.offset": Style(color="bright_red", bold=True), - "traceback.error_range": Style(underline=True, bold=True, dim=False), + "traceback.error_range": Style(underline=True, bold=True), + "traceback.note": Style(color="green", bold=True), + "traceback.group.border": Style(color="magenta"), "bar.back": Style(color="grey23"), "bar.complete": Style(color="rgb(249,38,114)"), "bar.finished": Style(color="rgb(114,156,31)"), diff --git a/src/pip/_vendor/rich/diagnose.py b/src/pip/_vendor/rich/diagnose.py index ad36183898e..b8b8c4347e8 100644 --- a/src/pip/_vendor/rich/diagnose.py +++ b/src/pip/_vendor/rich/diagnose.py @@ -15,16 +15,17 @@ def report() -> None: # pragma: no cover inspect(features) env_names = ( - "TERM", - "COLORTERM", "CLICOLOR", - "NO_COLOR", - "TERM_PROGRAM", + "COLORTERM", "COLUMNS", - "LINES", + "JPY_PARENT_PID", "JUPYTER_COLUMNS", "JUPYTER_LINES", - "JPY_PARENT_PID", + "LINES", + "NO_COLOR", + "TERM_PROGRAM", + "TERM", + "TTY_COMPATIBLE", "VSCODE_VERBOSE_LOGGING", ) env = {name: os.getenv(name) for name in env_names} diff --git a/src/pip/_vendor/rich/panel.py b/src/pip/_vendor/rich/panel.py index 8cfa6f4a243..d411e291533 100644 --- a/src/pip/_vendor/rich/panel.py +++ b/src/pip/_vendor/rich/panel.py @@ -22,7 +22,7 @@ class Panel(JupyterMixin): Args: renderable (RenderableType): A console renderable object. - box (Box, optional): A Box instance that defines the look of the border (see :ref:`appendix_box`. Defaults to box.ROUNDED. + box (Box): A Box instance that defines the look of the border (see :ref:`appendix_box`. Defaults to box.ROUNDED. title (Optional[TextType], optional): Optional title displayed in panel header. Defaults to None. title_align (AlignMethod, optional): Alignment of title. Defaults to "center". subtitle (Optional[TextType], optional): Optional subtitle displayed in panel footer. Defaults to None. diff --git a/src/pip/_vendor/rich/style.py b/src/pip/_vendor/rich/style.py index 262fd6ecad6..835d06f3eb2 100644 --- a/src/pip/_vendor/rich/style.py +++ b/src/pip/_vendor/rich/style.py @@ -524,7 +524,7 @@ def parse(cls, style_definition: str) -> "Style": if not word: raise errors.StyleSyntaxError("color expected after 'on'") try: - Color.parse(word) is None + Color.parse(word) except ColorParseError as error: raise errors.StyleSyntaxError( f"unable to parse {word!r} as background color; {error}" diff --git a/src/pip/_vendor/rich/table.py b/src/pip/_vendor/rich/table.py index 654c8555411..2b9e3b5d7e8 100644 --- a/src/pip/_vendor/rich/table.py +++ b/src/pip/_vendor/rich/table.py @@ -929,7 +929,6 @@ def align_cell( if __name__ == "__main__": # pragma: no cover from pip._vendor.rich.console import Console from pip._vendor.rich.highlighter import ReprHighlighter - from pip._vendor.rich.table import Table as Table from ._timer import timer diff --git a/src/pip/_vendor/rich/traceback.py b/src/pip/_vendor/rich/traceback.py index 28d742b4fd0..f82d06f6d13 100644 --- a/src/pip/_vendor/rich/traceback.py +++ b/src/pip/_vendor/rich/traceback.py @@ -26,15 +26,22 @@ from pip._vendor.pygments.util import ClassNotFound from . import pretty -from ._loop import loop_last +from ._loop import loop_first_last, loop_last from .columns import Columns -from .console import Console, ConsoleOptions, ConsoleRenderable, RenderResult, group +from .console import ( + Console, + ConsoleOptions, + ConsoleRenderable, + Group, + RenderResult, + group, +) from .constrain import Constrain from .highlighter import RegexHighlighter, ReprHighlighter from .panel import Panel from .scope import render_scope from .style import Style -from .syntax import Syntax +from .syntax import Syntax, SyntaxPosition from .text import Text from .theme import Theme @@ -44,6 +51,34 @@ LOCALS_MAX_STRING = 80 +def _iter_syntax_lines( + start: SyntaxPosition, end: SyntaxPosition +) -> Iterable[Tuple[int, int, int]]: + """Yield start and end positions per line. + + Args: + start: Start position. + end: End position. + + Returns: + Iterable of (LINE, COLUMN1, COLUMN2). + """ + + line1, column1 = start + line2, column2 = end + + if line1 == line2: + yield line1, column1, column2 + else: + for first, last, line_no in loop_first_last(range(line1, line2 + 1)): + if first: + yield line_no, column1, -1 + elif last: + yield line_no, 0, column2 + else: + yield line_no, 0, -1 + + def install( *, console: Optional[Console] = None, @@ -100,26 +135,25 @@ def excepthook( value: BaseException, traceback: Optional[TracebackType], ) -> None: - traceback_console.print( - Traceback.from_exception( - type_, - value, - traceback, - width=width, - code_width=code_width, - extra_lines=extra_lines, - theme=theme, - word_wrap=word_wrap, - show_locals=show_locals, - locals_max_length=locals_max_length, - locals_max_string=locals_max_string, - locals_hide_dunder=locals_hide_dunder, - locals_hide_sunder=bool(locals_hide_sunder), - indent_guides=indent_guides, - suppress=suppress, - max_frames=max_frames, - ) + exception_traceback = Traceback.from_exception( + type_, + value, + traceback, + width=width, + code_width=code_width, + extra_lines=extra_lines, + theme=theme, + word_wrap=word_wrap, + show_locals=show_locals, + locals_max_length=locals_max_length, + locals_max_string=locals_max_string, + locals_hide_dunder=locals_hide_dunder, + locals_hide_sunder=bool(locals_hide_sunder), + indent_guides=indent_guides, + suppress=suppress, + max_frames=max_frames, ) + traceback_console.print(exception_traceback) def ipy_excepthook_closure(ip: Any) -> None: # pragma: no cover tb_data = {} # store information about showtraceback call @@ -191,6 +225,7 @@ class _SyntaxError: line: str lineno: int msg: str + notes: List[str] = field(default_factory=list) @dataclass @@ -200,6 +235,9 @@ class Stack: syntax_error: Optional[_SyntaxError] = None is_cause: bool = False frames: List[Frame] = field(default_factory=list) + notes: List[str] = field(default_factory=list) + is_group: bool = False + exceptions: List["Trace"] = field(default_factory=list) @dataclass @@ -403,6 +441,8 @@ def extract( from pip._vendor.rich import _IMPORT_CWD + notes: List[str] = getattr(exc_value, "__notes__", None) or [] + def safe_str(_object: Any) -> str: """Don't allow exceptions from __str__ to propagate.""" try: @@ -415,8 +455,25 @@ def safe_str(_object: Any) -> str: exc_type=safe_str(exc_type.__name__), exc_value=safe_str(exc_value), is_cause=is_cause, + notes=notes, ) + if sys.version_info >= (3, 11): + if isinstance(exc_value, (BaseExceptionGroup, ExceptionGroup)): + stack.is_group = True + for exception in exc_value.exceptions: + stack.exceptions.append( + Traceback.extract( + type(exception), + exception, + exception.__traceback__, + show_locals=show_locals, + locals_max_length=locals_max_length, + locals_hide_dunder=locals_hide_dunder, + locals_hide_sunder=locals_hide_sunder, + ) + ) + if isinstance(exc_value, SyntaxError): stack.syntax_error = _SyntaxError( offset=exc_value.offset or 0, @@ -424,13 +481,14 @@ def safe_str(_object: Any) -> str: lineno=exc_value.lineno or 0, line=exc_value.text or "", msg=exc_value.msg, + notes=notes, ) stacks.append(stack) append = stack.frames.append def get_locals( - iter_locals: Iterable[Tuple[str, object]] + iter_locals: Iterable[Tuple[str, object]], ) -> Iterable[Tuple[str, object]]: """Extract locals from an iterator of key pairs.""" if not (locals_hide_dunder or locals_hide_sunder): @@ -524,6 +582,7 @@ def get_locals( break # pragma: no cover trace = Trace(stacks=stacks) + return trace def __rich_console__( @@ -556,7 +615,9 @@ def __rich_console__( ) highlighter = ReprHighlighter() - for last, stack in loop_last(reversed(self.trace.stacks)): + + @group() + def render_stack(stack: Stack, last: bool) -> RenderResult: if stack.frames: stack_renderable: ConsoleRenderable = Panel( self._render_stack(stack), @@ -569,6 +630,7 @@ def __rich_console__( stack_renderable = Constrain(stack_renderable, self.width) with console.use_theme(traceback_theme): yield stack_renderable + if stack.syntax_error is not None: with console.use_theme(traceback_theme): yield Constrain( @@ -594,6 +656,24 @@ def __rich_console__( else: yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type")) + for note in stack.notes: + yield Text.assemble(("[NOTE] ", "traceback.note"), highlighter(note)) + + if stack.is_group: + for group_no, group_exception in enumerate(stack.exceptions, 1): + grouped_exceptions: List[Group] = [] + for group_last, group_stack in loop_last(group_exception.stacks): + grouped_exceptions.append(render_stack(group_stack, group_last)) + yield "" + yield Constrain( + Panel( + Group(*grouped_exceptions), + title=f"Sub-exception #{group_no}", + border_style="traceback.group.border", + ), + self.width, + ) + if not last: if stack.is_cause: yield Text.from_markup( @@ -604,6 +684,9 @@ def __rich_console__( "\n[i]During handling of the above exception, another exception occurred:\n", ) + for last, stack in loop_last(reversed(self.trace.stacks)): + yield render_stack(stack, last) + @group() def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult: highlighter = ReprHighlighter() @@ -648,17 +731,6 @@ def _render_stack(self, stack: Stack) -> RenderResult: path_highlighter = PathHighlighter() theme = self.theme - def read_code(filename: str) -> str: - """Read files, and cache results on filename. - - Args: - filename (str): Filename to read - - Returns: - str: Contents of file - """ - return "".join(linecache.getlines(filename)) - def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]: if frame.locals: yield render_scope( @@ -720,7 +792,8 @@ def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]: continue if not suppressed: try: - code = read_code(frame.filename) + code_lines = linecache.getlines(frame.filename) + code = "".join(code_lines) if not code: # code may be an empty string if the file doesn't exist, OR # if the traceback filename is generated dynamically @@ -749,12 +822,26 @@ def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]: else: if frame.last_instruction is not None: start, end = frame.last_instruction - syntax.stylize_range( - style="traceback.error_range", - start=start, - end=end, - style_before=True, - ) + + # Stylize a line at a time + # So that indentation isn't underlined (which looks bad) + for line1, column1, column2 in _iter_syntax_lines(start, end): + try: + if column1 == 0: + line = code_lines[line1 - 1] + column1 = len(line) - len(line.lstrip()) + if column2 == -1: + column2 = len(code_lines[line1 - 1]) + except IndexError: + # Being defensive here + # If last_instruction reports a line out-of-bounds, we don't want to crash + continue + + syntax.stylize_range( + style="traceback.error_range", + start=(line1, column1), + end=(line1, column2), + ) yield ( Columns( [ diff --git a/src/pip/_vendor/vendor.txt b/src/pip/_vendor/vendor.txt index 78b7ed2dd8e..8962e6fce7d 100644 --- a/src/pip/_vendor/vendor.txt +++ b/src/pip/_vendor/vendor.txt @@ -9,7 +9,7 @@ requests==2.32.3 certifi==2025.1.31 idna==3.10 urllib3==1.26.20 -rich==13.9.4 +rich==14.0.0 pygments==2.19.1 typing_extensions==4.12.2 resolvelib==1.1.0 From d8b4cd8482b4ecbf39cebd1bb5e90278aae55835 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 1 Apr 2025 11:53:28 +0100 Subject: [PATCH 3/5] Upgrade typing_extensions to 4.13.0 --- news/typing_extensions.vendor.rst | 1 + src/pip/_vendor/typing_extensions.py | 1073 ++++++++++++++++++++++++-- src/pip/_vendor/vendor.txt | 2 +- 3 files changed, 1001 insertions(+), 75 deletions(-) create mode 100644 news/typing_extensions.vendor.rst diff --git a/news/typing_extensions.vendor.rst b/news/typing_extensions.vendor.rst new file mode 100644 index 00000000000..76ba97d4a2a --- /dev/null +++ b/news/typing_extensions.vendor.rst @@ -0,0 +1 @@ +Upgrade typing_extensions to 4.13.0 diff --git a/src/pip/_vendor/typing_extensions.py b/src/pip/_vendor/typing_extensions.py index e429384e76a..e0c2fb62ad5 100644 --- a/src/pip/_vendor/typing_extensions.py +++ b/src/pip/_vendor/typing_extensions.py @@ -1,9 +1,12 @@ import abc +import builtins import collections import collections.abc import contextlib +import enum import functools import inspect +import keyword import operator import sys import types as _types @@ -62,8 +65,11 @@ 'dataclass_transform', 'deprecated', 'Doc', + 'evaluate_forward_ref', 'get_overloads', 'final', + 'Format', + 'get_annotations', 'get_args', 'get_origin', 'get_original_bases', @@ -83,6 +89,7 @@ 'Text', 'TypeAlias', 'TypeAliasType', + 'TypeForm', 'TypeGuard', 'TypeIs', 'TYPE_CHECKING', @@ -91,6 +98,8 @@ 'ReadOnly', 'Required', 'NotRequired', + 'NoDefault', + 'NoExtraItems', # Pure aliases, have always been in typing 'AbstractSet', @@ -117,7 +126,6 @@ 'MutableMapping', 'MutableSequence', 'MutableSet', - 'NoDefault', 'Optional', 'Pattern', 'Reversible', @@ -138,6 +146,9 @@ GenericMeta = type _PEP_696_IMPLEMENTED = sys.version_info >= (3, 13, 0, "beta") +# Added with bpo-45166 to 3.10.1+ and some 3.9 versions +_FORWARD_REF_HAS_CLASS = "__forward_is_class__" in typing.ForwardRef.__slots__ + # The functions below are modified copies of typing internal helpers. # They are needed by _ProtocolMeta and they provide support for PEP 646. @@ -867,6 +878,63 @@ def inner(func): return inner +_NEEDS_SINGLETONMETA = ( + not hasattr(typing, "NoDefault") or not hasattr(typing, "NoExtraItems") +) + +if _NEEDS_SINGLETONMETA: + class SingletonMeta(type): + def __setattr__(cls, attr, value): + # TypeError is consistent with the behavior of NoneType + raise TypeError( + f"cannot set {attr!r} attribute of immutable type {cls.__name__!r}" + ) + + +if hasattr(typing, "NoDefault"): + NoDefault = typing.NoDefault +else: + class NoDefaultType(metaclass=SingletonMeta): + """The type of the NoDefault singleton.""" + + __slots__ = () + + def __new__(cls): + return globals().get("NoDefault") or object.__new__(cls) + + def __repr__(self): + return "typing_extensions.NoDefault" + + def __reduce__(self): + return "NoDefault" + + NoDefault = NoDefaultType() + del NoDefaultType + +if hasattr(typing, "NoExtraItems"): + NoExtraItems = typing.NoExtraItems +else: + class NoExtraItemsType(metaclass=SingletonMeta): + """The type of the NoExtraItems singleton.""" + + __slots__ = () + + def __new__(cls): + return globals().get("NoExtraItems") or object.__new__(cls) + + def __repr__(self): + return "typing_extensions.NoExtraItems" + + def __reduce__(self): + return "NoExtraItems" + + NoExtraItems = NoExtraItemsType() + del NoExtraItemsType + +if _NEEDS_SINGLETONMETA: + del SingletonMeta + + # Update this to something like >=3.13.0b1 if and when # PEP 728 is implemented in CPython _PEP_728_IMPLEMENTED = False @@ -913,7 +981,9 @@ def _get_typeddict_qualifiers(annotation_type): break class _TypedDictMeta(type): - def __new__(cls, name, bases, ns, *, total=True, closed=False): + + def __new__(cls, name, bases, ns, *, total=True, closed=None, + extra_items=NoExtraItems): """Create new typed dict class object. This method is called when TypedDict is subclassed, @@ -925,6 +995,8 @@ def __new__(cls, name, bases, ns, *, total=True, closed=False): if type(base) is not _TypedDictMeta and base is not typing.Generic: raise TypeError('cannot inherit from both a TypedDict type ' 'and a non-TypedDict base class') + if closed is not None and extra_items is not NoExtraItems: + raise TypeError(f"Cannot combine closed={closed!r} and extra_items") if any(issubclass(b, typing.Generic) for b in bases): generic_base = (typing.Generic,) @@ -964,7 +1036,7 @@ def __new__(cls, name, bases, ns, *, total=True, closed=False): optional_keys = set() readonly_keys = set() mutable_keys = set() - extra_items_type = None + extra_items_type = extra_items for base in bases: base_dict = base.__dict__ @@ -974,13 +1046,12 @@ def __new__(cls, name, bases, ns, *, total=True, closed=False): optional_keys.update(base_dict.get('__optional_keys__', ())) readonly_keys.update(base_dict.get('__readonly_keys__', ())) mutable_keys.update(base_dict.get('__mutable_keys__', ())) - base_extra_items_type = base_dict.get('__extra_items__', None) - if base_extra_items_type is not None: - extra_items_type = base_extra_items_type - if closed and extra_items_type is None: - extra_items_type = Never - if closed and "__extra_items__" in own_annotations: + # This was specified in an earlier version of PEP 728. Support + # is retained for backwards compatibility, but only for Python + # 3.13 and lower. + if (closed and sys.version_info < (3, 14) + and "__extra_items__" in own_annotations): annotation_type = own_annotations.pop("__extra_items__") qualifiers = set(_get_typeddict_qualifiers(annotation_type)) if Required in qualifiers: @@ -1019,8 +1090,7 @@ def __new__(cls, name, bases, ns, *, total=True, closed=False): tp_dict.__optional_keys__ = frozenset(optional_keys) tp_dict.__readonly_keys__ = frozenset(readonly_keys) tp_dict.__mutable_keys__ = frozenset(mutable_keys) - if not hasattr(tp_dict, '__total__'): - tp_dict.__total__ = total + tp_dict.__total__ = total tp_dict.__closed__ = closed tp_dict.__extra_items__ = extra_items_type return tp_dict @@ -1036,7 +1106,16 @@ def __subclasscheck__(cls, other): _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {}) @_ensure_subclassable(lambda bases: (_TypedDict,)) - def TypedDict(typename, fields=_marker, /, *, total=True, closed=False, **kwargs): + def TypedDict( + typename, + fields=_marker, + /, + *, + total=True, + closed=None, + extra_items=NoExtraItems, + **kwargs + ): """A simple typed namespace. At runtime it is equivalent to a plain dict. TypedDict creates a dictionary type such that a type checker will expect all @@ -1096,9 +1175,14 @@ class Point2D(TypedDict): "using the functional syntax, pass an empty dictionary, e.g. " ) + example + "." warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2) - if closed is not False and closed is not True: + # Support a field called "closed" + if closed is not False and closed is not True and closed is not None: kwargs["closed"] = closed - closed = False + closed = None + # Or "extra_items" + if extra_items is not NoExtraItems: + kwargs["extra_items"] = extra_items + extra_items = NoExtraItems fields = kwargs elif kwargs: raise TypeError("TypedDict takes either a dict or keyword arguments," @@ -1120,7 +1204,8 @@ class Point2D(TypedDict): # Setting correct module is necessary to make typed dict classes pickleable. ns['__module__'] = module - td = _TypedDictMeta(typename, (), ns, total=total, closed=closed) + td = _TypedDictMeta(typename, (), ns, total=total, closed=closed, + extra_items=extra_items) td.__orig_bases__ = (TypedDict,) return td @@ -1232,10 +1317,90 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False): ) else: # 3.8 hint = typing.get_type_hints(obj, globalns=globalns, localns=localns) + if sys.version_info < (3, 11): + _clean_optional(obj, hint, globalns, localns) + if sys.version_info < (3, 9): + # In 3.8 eval_type does not flatten Optional[ForwardRef] correctly + # This will recreate and and cache Unions. + hint = { + k: (t + if get_origin(t) != Union + else Union[t.__args__]) + for k, t in hint.items() + } if include_extras: return hint return {k: _strip_extras(t) for k, t in hint.items()} + _NoneType = type(None) + + def _could_be_inserted_optional(t): + """detects Union[..., None] pattern""" + # 3.8+ compatible checking before _UnionGenericAlias + if get_origin(t) is not Union: + return False + # Assume if last argument is not None they are user defined + if t.__args__[-1] is not _NoneType: + return False + return True + + # < 3.11 + def _clean_optional(obj, hints, globalns=None, localns=None): + # reverts injected Union[..., None] cases from typing.get_type_hints + # when a None default value is used. + # see https://github.com/python/typing_extensions/issues/310 + if not hints or isinstance(obj, type): + return + defaults = typing._get_defaults(obj) # avoid accessing __annotations___ + if not defaults: + return + original_hints = obj.__annotations__ + for name, value in hints.items(): + # Not a Union[..., None] or replacement conditions not fullfilled + if (not _could_be_inserted_optional(value) + or name not in defaults + or defaults[name] is not None + ): + continue + original_value = original_hints[name] + # value=NoneType should have caused a skip above but check for safety + if original_value is None: + original_value = _NoneType + # Forward reference + if isinstance(original_value, str): + if globalns is None: + if isinstance(obj, _types.ModuleType): + globalns = obj.__dict__ + else: + nsobj = obj + # Find globalns for the unwrapped object. + while hasattr(nsobj, '__wrapped__'): + nsobj = nsobj.__wrapped__ + globalns = getattr(nsobj, '__globals__', {}) + if localns is None: + localns = globalns + elif localns is None: + localns = globalns + if sys.version_info < (3, 9): + original_value = ForwardRef(original_value) + else: + original_value = ForwardRef( + original_value, + is_argument=not isinstance(obj, _types.ModuleType) + ) + original_evaluated = typing._eval_type(original_value, globalns, localns) + if sys.version_info < (3, 9) and get_origin(original_evaluated) is Union: + # Union[str, None, "str"] is not reduced to Union[str, None] + original_evaluated = Union[original_evaluated.__args__] + # Compare if values differ. Note that even if equal + # value might be cached by typing._tp_cache contrary to original_evaluated + if original_evaluated != value or ( + # 3.10: ForwardRefs of UnionType might be turned into _UnionGenericAlias + hasattr(_types, "UnionType") + and isinstance(original_evaluated, _types.UnionType) + and not isinstance(value, _types.UnionType) + ): + hints[name] = original_evaluated # Python 3.9+ has PEP 593 (Annotated) if hasattr(typing, 'Annotated'): @@ -1443,34 +1608,6 @@ def TypeAlias(self, parameters): ) -if hasattr(typing, "NoDefault"): - NoDefault = typing.NoDefault -else: - class NoDefaultTypeMeta(type): - def __setattr__(cls, attr, value): - # TypeError is consistent with the behavior of NoneType - raise TypeError( - f"cannot set {attr!r} attribute of immutable type {cls.__name__!r}" - ) - - class NoDefaultType(metaclass=NoDefaultTypeMeta): - """The type of the NoDefault singleton.""" - - __slots__ = () - - def __new__(cls): - return globals().get("NoDefault") or object.__new__(cls) - - def __repr__(self): - return "typing_extensions.NoDefault" - - def __reduce__(self): - return "NoDefault" - - NoDefault = NoDefaultType() - del NoDefaultType, NoDefaultTypeMeta - - def _set_default(type_param, default): type_param.has_default = lambda: default is not NoDefault type_param.__default__ = default @@ -1761,6 +1898,23 @@ def __call__(self, *args, **kwargs): # 3.8-3.9 if not hasattr(typing, 'Concatenate'): # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + + # 3.9.0-1 + if not hasattr(typing, '_type_convert'): + def _type_convert(arg, module=None, *, allow_special_forms=False): + """For converting None to type(None), and strings to ForwardRef.""" + if arg is None: + return type(None) + if isinstance(arg, str): + if sys.version_info <= (3, 9, 6): + return ForwardRef(arg) + if sys.version_info <= (3, 9, 7): + return ForwardRef(arg, module=module) + return ForwardRef(arg, module=module, is_class=allow_special_forms) + return arg + else: + _type_convert = typing._type_convert + class _ConcatenateGenericAlias(list): # Trick Generic into looking into this for __parameters__. @@ -1792,27 +1946,171 @@ def __parameters__(self): tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) ) + # 3.8; needed for typing._subst_tvars + # 3.9 used by __getitem__ below + def copy_with(self, params): + if isinstance(params[-1], _ConcatenateGenericAlias): + params = (*params[:-1], *params[-1].__args__) + elif isinstance(params[-1], (list, tuple)): + return (*params[:-1], *params[-1]) + elif (not (params[-1] is ... or isinstance(params[-1], ParamSpec))): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable or ellipsis.") + return self.__class__(self.__origin__, params) + + # 3.9; accessed during GenericAlias.__getitem__ when substituting + def __getitem__(self, args): + if self.__origin__ in (Generic, Protocol): + # Can't subscript Generic[...] or Protocol[...]. + raise TypeError(f"Cannot subscript already-subscripted {self}") + if not self.__parameters__: + raise TypeError(f"{self} is not a generic class") + + if not isinstance(args, tuple): + args = (args,) + args = _unpack_args(*(_type_convert(p) for p in args)) + params = self.__parameters__ + for param in params: + prepare = getattr(param, "__typing_prepare_subst__", None) + if prepare is not None: + args = prepare(self, args) + # 3.8 - 3.9 & typing.ParamSpec + elif isinstance(param, ParamSpec): + i = params.index(param) + if ( + i == len(args) + and getattr(param, '__default__', NoDefault) is not NoDefault + ): + args = [*args, param.__default__] + if i >= len(args): + raise TypeError(f"Too few arguments for {self}") + # Special case for Z[[int, str, bool]] == Z[int, str, bool] + if len(params) == 1 and not _is_param_expr(args[0]): + assert i == 0 + args = (args,) + elif ( + isinstance(args[i], list) + # 3.8 - 3.9 + # This class inherits from list do not convert + and not isinstance(args[i], _ConcatenateGenericAlias) + ): + args = (*args[:i], tuple(args[i]), *args[i + 1:]) -# 3.8-3.9 + alen = len(args) + plen = len(params) + if alen != plen: + raise TypeError( + f"Too {'many' if alen > plen else 'few'} arguments for {self};" + f" actual {alen}, expected {plen}" + ) + + subst = dict(zip(self.__parameters__, args)) + # determine new args + new_args = [] + for arg in self.__args__: + if isinstance(arg, type): + new_args.append(arg) + continue + if isinstance(arg, TypeVar): + arg = subst[arg] + if ( + (isinstance(arg, typing._GenericAlias) and _is_unpack(arg)) + or ( + hasattr(_types, "GenericAlias") + and isinstance(arg, _types.GenericAlias) + and getattr(arg, "__unpacked__", False) + ) + ): + raise TypeError(f"{arg} is not valid as type argument") + + elif isinstance(arg, + typing._GenericAlias + if not hasattr(_types, "GenericAlias") else + (typing._GenericAlias, _types.GenericAlias) + ): + subparams = arg.__parameters__ + if subparams: + subargs = tuple(subst[x] for x in subparams) + arg = arg[subargs] + new_args.append(arg) + return self.copy_with(tuple(new_args)) + +# 3.10+ +else: + _ConcatenateGenericAlias = typing._ConcatenateGenericAlias + + # 3.10 + if sys.version_info < (3, 11): + + class _ConcatenateGenericAlias(typing._ConcatenateGenericAlias, _root=True): + # needed for checks in collections.abc.Callable to accept this class + __module__ = "typing" + + def copy_with(self, params): + if isinstance(params[-1], (list, tuple)): + return (*params[:-1], *params[-1]) + if isinstance(params[-1], typing._ConcatenateGenericAlias): + params = (*params[:-1], *params[-1].__args__) + elif not (params[-1] is ... or isinstance(params[-1], ParamSpec)): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable or ellipsis.") + return super(typing._ConcatenateGenericAlias, self).copy_with(params) + + def __getitem__(self, args): + value = super().__getitem__(args) + if isinstance(value, tuple) and any(_is_unpack(t) for t in value): + return tuple(_unpack_args(*(n for n in value))) + return value + + +# 3.8-3.9.2 +class _EllipsisDummy: ... + + +# 3.8-3.10 +def _create_concatenate_alias(origin, parameters): + if parameters[-1] is ... and sys.version_info < (3, 9, 2): + # Hack: Arguments must be types, replace it with one. + parameters = (*parameters[:-1], _EllipsisDummy) + if sys.version_info >= (3, 10, 2): + concatenate = _ConcatenateGenericAlias(origin, parameters, + _typevar_types=(TypeVar, ParamSpec), + _paramspec_tvars=True) + else: + concatenate = _ConcatenateGenericAlias(origin, parameters) + if parameters[-1] is not _EllipsisDummy: + return concatenate + # Remove dummy again + concatenate.__args__ = tuple(p if p is not _EllipsisDummy else ... + for p in concatenate.__args__) + if sys.version_info < (3, 10): + # backport needs __args__ adjustment only + return concatenate + concatenate.__parameters__ = tuple(p for p in concatenate.__parameters__ + if p is not _EllipsisDummy) + return concatenate + + +# 3.8-3.10 @typing._tp_cache def _concatenate_getitem(self, parameters): if parameters == (): raise TypeError("Cannot take a Concatenate of no types.") if not isinstance(parameters, tuple): parameters = (parameters,) - if not isinstance(parameters[-1], ParamSpec): + if not (parameters[-1] is ... or isinstance(parameters[-1], ParamSpec)): raise TypeError("The last parameter to Concatenate should be a " - "ParamSpec variable.") + "ParamSpec variable or ellipsis.") msg = "Concatenate[arg, ...]: each arg must be a type." - parameters = tuple(typing._type_check(p, msg) for p in parameters) - return _ConcatenateGenericAlias(self, parameters) + parameters = (*(typing._type_check(p, msg) for p in parameters[:-1]), + parameters[-1]) + return _create_concatenate_alias(self, parameters) -# 3.10+ -if hasattr(typing, 'Concatenate'): +# 3.11+; Concatenate does not accept ellipsis in 3.10 +if sys.version_info >= (3, 11): Concatenate = typing.Concatenate - _ConcatenateGenericAlias = typing._ConcatenateGenericAlias -# 3.9 +# 3.9-3.10 elif sys.version_info[:2] >= (3, 9): @_ExtensionsSpecialForm def Concatenate(self, parameters): @@ -1976,7 +2274,7 @@ def TypeIs(self, parameters): 1. The return value is a boolean. 2. If the return value is ``True``, the type of its argument - is the intersection of the type inside ``TypeGuard`` and the argument's + is the intersection of the type inside ``TypeIs`` and the argument's previously known type. For example:: @@ -2024,7 +2322,7 @@ def __getitem__(self, parameters): 1. The return value is a boolean. 2. If the return value is ``True``, the type of its argument - is the intersection of the type inside ``TypeGuard`` and the argument's + is the intersection of the type inside ``TypeIs`` and the argument's previously known type. For example:: @@ -2042,6 +2340,69 @@ def f(val: Union[int, Awaitable[int]]) -> int: PEP 742 (Narrowing types with TypeIs). """) +# 3.14+? +if hasattr(typing, 'TypeForm'): + TypeForm = typing.TypeForm +# 3.9 +elif sys.version_info[:2] >= (3, 9): + class _TypeFormForm(_ExtensionsSpecialForm, _root=True): + # TypeForm(X) is equivalent to X but indicates to the type checker + # that the object is a TypeForm. + def __call__(self, obj, /): + return obj + + @_TypeFormForm + def TypeForm(self, parameters): + """A special form representing the value that results from the evaluation + of a type expression. This value encodes the information supplied in the + type expression, and it represents the type described by that type expression. + + When used in a type expression, TypeForm describes a set of type form objects. + It accepts a single type argument, which must be a valid type expression. + ``TypeForm[T]`` describes the set of all type form objects that represent + the type T or types that are assignable to T. + + Usage: + + def cast[T](typ: TypeForm[T], value: Any) -> T: ... + + reveal_type(cast(int, "x")) # int + + See PEP 747 for more information. + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) +# 3.8 +else: + class _TypeFormForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type') + return typing._GenericAlias(self, (item,)) + + def __call__(self, obj, /): + return obj + + TypeForm = _TypeFormForm( + 'TypeForm', + doc="""A special form representing the value that results from the evaluation + of a type expression. This value encodes the information supplied in the + type expression, and it represents the type described by that type expression. + + When used in a type expression, TypeForm describes a set of type form objects. + It accepts a single type argument, which must be a valid type expression. + ``TypeForm[T]`` describes the set of all type form objects that represent + the type T or types that are assignable to T. + + Usage: + + def cast[T](typ: TypeForm[T], value: Any) -> T: ... + + reveal_type(cast(int, "x")) # int + + See PEP 747 for more information. + """) + # Vendored from cpython typing._SpecialFrom class _SpecialForm(typing._Final, _root=True): @@ -2344,7 +2705,9 @@ def __init__(self, getitem): self.__doc__ = _UNPACK_DOC class _UnpackAlias(typing._GenericAlias, _root=True): - __class__ = typing.TypeVar + if sys.version_info < (3, 11): + # needed for compatibility with Generic[Unpack[Ts]] + __class__ = typing.TypeVar @property def __typing_unpacked_tuple_args__(self): @@ -2357,6 +2720,17 @@ def __typing_unpacked_tuple_args__(self): return arg.__args__ return None + @property + def __typing_is_unpacked_typevartuple__(self): + assert self.__origin__ is Unpack + assert len(self.__args__) == 1 + return isinstance(self.__args__[0], TypeVarTuple) + + def __getitem__(self, args): + if self.__typing_is_unpacked_typevartuple__: + return args + return super().__getitem__(args) + @_UnpackSpecialForm def Unpack(self, parameters): item = typing._type_check(parameters, f'{self._name} accepts only a single type.') @@ -2369,6 +2743,28 @@ def _is_unpack(obj): class _UnpackAlias(typing._GenericAlias, _root=True): __class__ = typing.TypeVar + @property + def __typing_unpacked_tuple_args__(self): + assert self.__origin__ is Unpack + assert len(self.__args__) == 1 + arg, = self.__args__ + if isinstance(arg, typing._GenericAlias): + if arg.__origin__ is not tuple: + raise TypeError("Unpack[...] must be used with a tuple type") + return arg.__args__ + return None + + @property + def __typing_is_unpacked_typevartuple__(self): + assert self.__origin__ is Unpack + assert len(self.__args__) == 1 + return isinstance(self.__args__[0], TypeVarTuple) + + def __getitem__(self, args): + if self.__typing_is_unpacked_typevartuple__: + return args + return super().__getitem__(args) + class _UnpackForm(_ExtensionsSpecialForm, _root=True): def __getitem__(self, parameters): item = typing._type_check(parameters, @@ -2381,21 +2777,22 @@ def _is_unpack(obj): return isinstance(obj, _UnpackAlias) +def _unpack_args(*args): + newargs = [] + for arg in args: + subargs = getattr(arg, '__typing_unpacked_tuple_args__', None) + if subargs is not None and (not (subargs and subargs[-1] is ...)): + newargs.extend(subargs) + else: + newargs.append(arg) + return newargs + + if _PEP_696_IMPLEMENTED: from typing import TypeVarTuple elif hasattr(typing, "TypeVarTuple"): # 3.11+ - def _unpack_args(*args): - newargs = [] - for arg in args: - subargs = getattr(arg, '__typing_unpacked_tuple_args__', None) - if subargs is not None and not (subargs and subargs[-1] is ...): - newargs.extend(subargs) - else: - newargs.append(arg) - return newargs - # Add default parameter - PEP 696 class TypeVarTuple(metaclass=_TypeVarLikeMeta): """Type variable tuple.""" @@ -2845,13 +3242,21 @@ def __init_subclass__(*args, **kwargs): __init_subclass__.__deprecated__ = msg return arg elif callable(arg): + import asyncio.coroutines import functools + import inspect @functools.wraps(arg) def wrapper(*args, **kwargs): warnings.warn(msg, category=category, stacklevel=stacklevel + 1) return arg(*args, **kwargs) + if asyncio.coroutines.iscoroutinefunction(arg): + if sys.version_info >= (3, 12): + wrapper = inspect.markcoroutinefunction(wrapper) + else: + wrapper._is_coroutine = asyncio.coroutines._is_coroutine + arg.__deprecated__ = wrapper.__deprecated__ = msg return wrapper else: @@ -2860,6 +3265,24 @@ def wrapper(*args, **kwargs): f"a class or callable, not {arg!r}" ) +if sys.version_info < (3, 10): + def _is_param_expr(arg): + return arg is ... or isinstance( + arg, (tuple, list, ParamSpec, _ConcatenateGenericAlias) + ) +else: + def _is_param_expr(arg): + return arg is ... or isinstance( + arg, + ( + tuple, + list, + ParamSpec, + _ConcatenateGenericAlias, + typing._ConcatenateGenericAlias, + ), + ) + # We have to do some monkey patching to deal with the dual nature of # Unpack/TypeVarTuple: @@ -2874,6 +3297,17 @@ def _check_generic(cls, parameters, elen=_marker): This gives a nice error message in case of count mismatch. """ + # If substituting a single ParamSpec with multiple arguments + # we do not check the count + if (inspect.isclass(cls) and issubclass(cls, typing.Generic) + and len(cls.__parameters__) == 1 + and isinstance(cls.__parameters__[0], ParamSpec) + and parameters + and not _is_param_expr(parameters[0]) + ): + # Generic modifies parameters variable, but here we cannot do this + return + if not elen: raise TypeError(f"{cls} is not a generic class") if elen is _marker: @@ -3007,7 +3441,10 @@ def _collect_type_vars(types, typevar_types=None): for t in types: if _is_unpacked_typevartuple(t): type_var_tuple_encountered = True - elif isinstance(t, typevar_types) and t not in tvars: + elif ( + isinstance(t, typevar_types) and not isinstance(t, _UnpackAlias) + and t not in tvars + ): if enforce_default_ordering: has_default = getattr(t, '__default__', NoDefault) is not NoDefault if has_default: @@ -3022,6 +3459,13 @@ def _collect_type_vars(types, typevar_types=None): tvars.append(t) if _should_collect_from_parameters(t): tvars.extend([t for t in t.__parameters__ if t not in tvars]) + elif isinstance(t, tuple): + # Collect nested type_vars + # tuple wrapped by _prepare_paramspec_params(cls, params) + for x in t: + for collected in _collect_type_vars([x]): + if collected not in tvars: + tvars.append(collected) return tuple(tvars) typing._collect_type_vars = _collect_type_vars @@ -3379,8 +3823,9 @@ def __ror__(self, other): return typing.Union[other, self] -if hasattr(typing, "TypeAliasType"): +if sys.version_info >= (3, 14): TypeAliasType = typing.TypeAliasType +# 3.8-3.13 else: def _is_unionable(obj): """Corresponds to is_unionable() in unionobject.c in CPython.""" @@ -3391,6 +3836,37 @@ def _is_unionable(obj): TypeAliasType, )) + if sys.version_info < (3, 10): + # Copied and pasted from https://github.com/python/cpython/blob/986a4e1b6fcae7fe7a1d0a26aea446107dd58dd2/Objects/genericaliasobject.c#L568-L582, + # so that we emulate the behaviour of `types.GenericAlias` + # on the latest versions of CPython + _ATTRIBUTE_DELEGATION_EXCLUSIONS = frozenset({ + "__class__", + "__bases__", + "__origin__", + "__args__", + "__unpacked__", + "__parameters__", + "__typing_unpacked_tuple_args__", + "__mro_entries__", + "__reduce_ex__", + "__reduce__", + "__copy__", + "__deepcopy__", + }) + + class _TypeAliasGenericAlias(typing._GenericAlias, _root=True): + def __getattr__(self, attr): + if attr in _ATTRIBUTE_DELEGATION_EXCLUSIONS: + return object.__getattr__(self, attr) + return getattr(self.__origin__, attr) + + if sys.version_info < (3, 9): + def __getitem__(self, item): + result = super().__getitem__(item) + result.__class__ = type(self) + return result + class TypeAliasType: """Create named, parameterized type aliases. @@ -3422,11 +3898,29 @@ class TypeAliasType: def __init__(self, name: str, value, *, type_params=()): if not isinstance(name, str): raise TypeError("TypeAliasType name must be a string") + if not isinstance(type_params, tuple): + raise TypeError("type_params must be a tuple") self.__value__ = value self.__type_params__ = type_params + default_value_encountered = False parameters = [] for type_param in type_params: + if ( + not isinstance(type_param, (TypeVar, TypeVarTuple, ParamSpec)) + # 3.8-3.11 + # Unpack Backport passes isinstance(type_param, TypeVar) + or _is_unpack(type_param) + ): + raise TypeError(f"Expected a type param, got {type_param!r}") + has_default = ( + getattr(type_param, '__default__', NoDefault) is not NoDefault + ) + if default_value_encountered and not has_default: + raise TypeError(f"non-default type parameter '{type_param!r}'" + " follows default type parameter") + if has_default: + default_value_encountered = True if isinstance(type_param, TypeVarTuple): parameters.extend(type_param) else: @@ -3463,16 +3957,49 @@ def _raise_attribute_error(self, name: str) -> Never: def __repr__(self) -> str: return self.__name__ + if sys.version_info < (3, 11): + def _check_single_param(self, param, recursion=0): + # Allow [], [int], [int, str], [int, ...], [int, T] + if param is ...: + return ... + if param is None: + return None + # Note in <= 3.9 _ConcatenateGenericAlias inherits from list + if isinstance(param, list) and recursion == 0: + return [self._check_single_param(arg, recursion+1) + for arg in param] + return typing._type_check( + param, f'Subscripting {self.__name__} requires a type.' + ) + + def _check_parameters(self, parameters): + if sys.version_info < (3, 11): + return tuple( + self._check_single_param(item) + for item in parameters + ) + return tuple(typing._type_check( + item, f'Subscripting {self.__name__} requires a type.' + ) + for item in parameters + ) + def __getitem__(self, parameters): + if not self.__type_params__: + raise TypeError("Only generic type aliases are subscriptable") if not isinstance(parameters, tuple): parameters = (parameters,) - parameters = [ - typing._type_check( - item, f'Subscripting {self.__name__} requires a type.' - ) - for item in parameters - ] - return typing._GenericAlias(self, tuple(parameters)) + # Using 3.9 here will create problems with Concatenate + if sys.version_info >= (3, 10): + return _types.GenericAlias(self, parameters) + type_vars = _collect_type_vars(parameters) + parameters = self._check_parameters(parameters) + alias = _TypeAliasGenericAlias(self, parameters) + # alias.__parameters__ is not complete if Concatenate is present + # as it is converted to a list from which no parameters are extracted. + if alias.__parameters__ != type_vars: + alias.__parameters__ = type_vars + return alias def __reduce__(self): return self.__name__ @@ -3599,6 +4126,404 @@ def __eq__(self, other: object) -> bool: __all__.append("CapsuleType") +# Using this convoluted approach so that this keeps working +# whether we end up using PEP 649 as written, PEP 749, or +# some other variation: in any case, inspect.get_annotations +# will continue to exist and will gain a `format` parameter. +_PEP_649_OR_749_IMPLEMENTED = ( + hasattr(inspect, 'get_annotations') + and inspect.get_annotations.__kwdefaults__ is not None + and "format" in inspect.get_annotations.__kwdefaults__ +) + + +class Format(enum.IntEnum): + VALUE = 1 + FORWARDREF = 2 + STRING = 3 + + +if _PEP_649_OR_749_IMPLEMENTED: + get_annotations = inspect.get_annotations +else: + def get_annotations(obj, *, globals=None, locals=None, eval_str=False, + format=Format.VALUE): + """Compute the annotations dict for an object. + + obj may be a callable, class, or module. + Passing in an object of any other type raises TypeError. + + Returns a dict. get_annotations() returns a new dict every time + it's called; calling it twice on the same object will return two + different but equivalent dicts. + + This is a backport of `inspect.get_annotations`, which has been + in the standard library since Python 3.10. See the standard library + documentation for more: + + https://docs.python.org/3/library/inspect.html#inspect.get_annotations + + This backport adds the *format* argument introduced by PEP 649. The + three formats supported are: + * VALUE: the annotations are returned as-is. This is the default and + it is compatible with the behavior on previous Python versions. + * FORWARDREF: return annotations as-is if possible, but replace any + undefined names with ForwardRef objects. The implementation proposed by + PEP 649 relies on language changes that cannot be backported; the + typing-extensions implementation simply returns the same result as VALUE. + * STRING: return annotations as strings, in a format close to the original + source. Again, this behavior cannot be replicated directly in a backport. + As an approximation, typing-extensions retrieves the annotations under + VALUE semantics and then stringifies them. + + The purpose of this backport is to allow users who would like to use + FORWARDREF or STRING semantics once PEP 649 is implemented, but who also + want to support earlier Python versions, to simply write: + + typing_extensions.get_annotations(obj, format=Format.FORWARDREF) + + """ + format = Format(format) + + if eval_str and format is not Format.VALUE: + raise ValueError("eval_str=True is only supported with format=Format.VALUE") + + if isinstance(obj, type): + # class + obj_dict = getattr(obj, '__dict__', None) + if obj_dict and hasattr(obj_dict, 'get'): + ann = obj_dict.get('__annotations__', None) + if isinstance(ann, _types.GetSetDescriptorType): + ann = None + else: + ann = None + + obj_globals = None + module_name = getattr(obj, '__module__', None) + if module_name: + module = sys.modules.get(module_name, None) + if module: + obj_globals = getattr(module, '__dict__', None) + obj_locals = dict(vars(obj)) + unwrap = obj + elif isinstance(obj, _types.ModuleType): + # module + ann = getattr(obj, '__annotations__', None) + obj_globals = obj.__dict__ + obj_locals = None + unwrap = None + elif callable(obj): + # this includes types.Function, types.BuiltinFunctionType, + # types.BuiltinMethodType, functools.partial, functools.singledispatch, + # "class funclike" from Lib/test/test_inspect... on and on it goes. + ann = getattr(obj, '__annotations__', None) + obj_globals = getattr(obj, '__globals__', None) + obj_locals = None + unwrap = obj + elif hasattr(obj, '__annotations__'): + ann = obj.__annotations__ + obj_globals = obj_locals = unwrap = None + else: + raise TypeError(f"{obj!r} is not a module, class, or callable.") + + if ann is None: + return {} + + if not isinstance(ann, dict): + raise ValueError(f"{obj!r}.__annotations__ is neither a dict nor None") + + if not ann: + return {} + + if not eval_str: + if format is Format.STRING: + return { + key: value if isinstance(value, str) else typing._type_repr(value) + for key, value in ann.items() + } + return dict(ann) + + if unwrap is not None: + while True: + if hasattr(unwrap, '__wrapped__'): + unwrap = unwrap.__wrapped__ + continue + if isinstance(unwrap, functools.partial): + unwrap = unwrap.func + continue + break + if hasattr(unwrap, "__globals__"): + obj_globals = unwrap.__globals__ + + if globals is None: + globals = obj_globals + if locals is None: + locals = obj_locals or {} + + # "Inject" type parameters into the local namespace + # (unless they are shadowed by assignments *in* the local namespace), + # as a way of emulating annotation scopes when calling `eval()` + if type_params := getattr(obj, "__type_params__", ()): + locals = {param.__name__: param for param in type_params} | locals + + return_value = {key: + value if not isinstance(value, str) else eval(value, globals, locals) + for key, value in ann.items() } + return return_value + + +if hasattr(typing, "evaluate_forward_ref"): + evaluate_forward_ref = typing.evaluate_forward_ref +else: + # Implements annotationlib.ForwardRef.evaluate + def _eval_with_owner( + forward_ref, *, owner=None, globals=None, locals=None, type_params=None + ): + if forward_ref.__forward_evaluated__: + return forward_ref.__forward_value__ + if getattr(forward_ref, "__cell__", None) is not None: + try: + value = forward_ref.__cell__.cell_contents + except ValueError: + pass + else: + forward_ref.__forward_evaluated__ = True + forward_ref.__forward_value__ = value + return value + if owner is None: + owner = getattr(forward_ref, "__owner__", None) + + if ( + globals is None + and getattr(forward_ref, "__forward_module__", None) is not None + ): + globals = getattr( + sys.modules.get(forward_ref.__forward_module__, None), "__dict__", None + ) + if globals is None: + globals = getattr(forward_ref, "__globals__", None) + if globals is None: + if isinstance(owner, type): + module_name = getattr(owner, "__module__", None) + if module_name: + module = sys.modules.get(module_name, None) + if module: + globals = getattr(module, "__dict__", None) + elif isinstance(owner, _types.ModuleType): + globals = getattr(owner, "__dict__", None) + elif callable(owner): + globals = getattr(owner, "__globals__", None) + + # If we pass None to eval() below, the globals of this module are used. + if globals is None: + globals = {} + + if locals is None: + locals = {} + if isinstance(owner, type): + locals.update(vars(owner)) + + if type_params is None and owner is not None: + # "Inject" type parameters into the local namespace + # (unless they are shadowed by assignments *in* the local namespace), + # as a way of emulating annotation scopes when calling `eval()` + type_params = getattr(owner, "__type_params__", None) + + # type parameters require some special handling, + # as they exist in their own scope + # but `eval()` does not have a dedicated parameter for that scope. + # For classes, names in type parameter scopes should override + # names in the global scope (which here are called `localns`!), + # but should in turn be overridden by names in the class scope + # (which here are called `globalns`!) + if type_params is not None: + globals = dict(globals) + locals = dict(locals) + for param in type_params: + param_name = param.__name__ + if ( + _FORWARD_REF_HAS_CLASS and not forward_ref.__forward_is_class__ + ) or param_name not in globals: + globals[param_name] = param + locals.pop(param_name, None) + + arg = forward_ref.__forward_arg__ + if arg.isidentifier() and not keyword.iskeyword(arg): + if arg in locals: + value = locals[arg] + elif arg in globals: + value = globals[arg] + elif hasattr(builtins, arg): + return getattr(builtins, arg) + else: + raise NameError(arg) + else: + code = forward_ref.__forward_code__ + value = eval(code, globals, locals) + forward_ref.__forward_evaluated__ = True + forward_ref.__forward_value__ = value + return value + + def _lax_type_check( + value, msg, is_argument=True, *, module=None, allow_special_forms=False + ): + """ + A lax Python 3.11+ like version of typing._type_check + """ + if hasattr(typing, "_type_convert"): + if _FORWARD_REF_HAS_CLASS: + type_ = typing._type_convert( + value, + module=module, + allow_special_forms=allow_special_forms, + ) + # module was added with bpo-41249 before is_class (bpo-46539) + elif "__forward_module__" in typing.ForwardRef.__slots__: + type_ = typing._type_convert(value, module=module) + else: + type_ = typing._type_convert(value) + else: + if value is None: + return type(None) + if isinstance(value, str): + return ForwardRef(value) + type_ = value + invalid_generic_forms = (Generic, Protocol) + if not allow_special_forms: + invalid_generic_forms += (ClassVar,) + if is_argument: + invalid_generic_forms += (Final,) + if ( + isinstance(type_, typing._GenericAlias) + and get_origin(type_) in invalid_generic_forms + ): + raise TypeError(f"{type_} is not valid as type argument") from None + if type_ in (Any, LiteralString, NoReturn, Never, Self, TypeAlias): + return type_ + if allow_special_forms and type_ in (ClassVar, Final): + return type_ + if ( + isinstance(type_, (_SpecialForm, typing._SpecialForm)) + or type_ in (Generic, Protocol) + ): + raise TypeError(f"Plain {type_} is not valid as type argument") from None + if type(type_) is tuple: # lax version with tuple instead of callable + raise TypeError(f"{msg} Got {type_!r:.100}.") + return type_ + + def evaluate_forward_ref( + forward_ref, + *, + owner=None, + globals=None, + locals=None, + type_params=None, + format=Format.VALUE, + _recursive_guard=frozenset(), + ): + """Evaluate a forward reference as a type hint. + + This is similar to calling the ForwardRef.evaluate() method, + but unlike that method, evaluate_forward_ref() also: + + * Recursively evaluates forward references nested within the type hint. + * Rejects certain objects that are not valid type hints. + * Replaces type hints that evaluate to None with types.NoneType. + * Supports the *FORWARDREF* and *STRING* formats. + + *forward_ref* must be an instance of ForwardRef. *owner*, if given, + should be the object that holds the annotations that the forward reference + derived from, such as a module, class object, or function. It is used to + infer the namespaces to use for looking up names. *globals* and *locals* + can also be explicitly given to provide the global and local namespaces. + *type_params* is a tuple of type parameters that are in scope when + evaluating the forward reference. This parameter must be provided (though + it may be an empty tuple) if *owner* is not given and the forward reference + does not already have an owner set. *format* specifies the format of the + annotation and is a member of the annotationlib.Format enum. + + """ + if format == Format.STRING: + return forward_ref.__forward_arg__ + if forward_ref.__forward_arg__ in _recursive_guard: + return forward_ref + + # Evaluate the forward reference + try: + value = _eval_with_owner( + forward_ref, + owner=owner, + globals=globals, + locals=locals, + type_params=type_params, + ) + except NameError: + if format == Format.FORWARDREF: + return forward_ref + else: + raise + + msg = "Forward references must evaluate to types." + if not _FORWARD_REF_HAS_CLASS: + allow_special_forms = not forward_ref.__forward_is_argument__ + else: + allow_special_forms = forward_ref.__forward_is_class__ + type_ = _lax_type_check( + value, + msg, + is_argument=forward_ref.__forward_is_argument__, + allow_special_forms=allow_special_forms, + ) + + # Recursively evaluate the type + if isinstance(type_, ForwardRef): + if getattr(type_, "__forward_module__", True) is not None: + globals = None + return evaluate_forward_ref( + type_, + globals=globals, + locals=locals, + type_params=type_params, owner=owner, + _recursive_guard=_recursive_guard, format=format + ) + if sys.version_info < (3, 12, 5) and type_params: + # Make use of type_params + locals = dict(locals) if locals else {} + for tvar in type_params: + if tvar.__name__ not in locals: # lets not overwrite something present + locals[tvar.__name__] = tvar + if sys.version_info < (3, 9): + return typing._eval_type( + type_, + globals, + locals, + ) + if sys.version_info < (3, 12, 5): + return typing._eval_type( + type_, + globals, + locals, + recursive_guard=_recursive_guard | {forward_ref.__forward_arg__}, + ) + if sys.version_info < (3, 14): + return typing._eval_type( + type_, + globals, + locals, + type_params, + recursive_guard=_recursive_guard | {forward_ref.__forward_arg__}, + ) + return typing._eval_type( + type_, + globals, + locals, + type_params, + recursive_guard=_recursive_guard | {forward_ref.__forward_arg__}, + format=format, + owner=owner, + ) + + # Aliases for items that have always been in typing. # Explicitly assign these (rather than using `from typing import *` at the top), # so that we get a CI error if one of these is deleted from typing.py diff --git a/src/pip/_vendor/vendor.txt b/src/pip/_vendor/vendor.txt index 8962e6fce7d..c867341699f 100644 --- a/src/pip/_vendor/vendor.txt +++ b/src/pip/_vendor/vendor.txt @@ -11,7 +11,7 @@ requests==2.32.3 urllib3==1.26.20 rich==14.0.0 pygments==2.19.1 - typing_extensions==4.12.2 + typing_extensions==4.13.0 resolvelib==1.1.0 setuptools==70.3.0 tomli==2.2.1 From b4939beca8276177b1f719efe523e5c9eb4fc3af Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 1 Apr 2025 12:09:30 +0100 Subject: [PATCH 4/5] Document command to upgrade vendored libraries --- src/pip/_vendor/README.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pip/_vendor/README.rst b/src/pip/_vendor/README.rst index e3fe8041ce2..a925e8cc6aa 100644 --- a/src/pip/_vendor/README.rst +++ b/src/pip/_vendor/README.rst @@ -115,6 +115,16 @@ Vendoring is automated via the `vendoring ` Launch it via ``vendoring sync . -v`` (requires ``vendoring>=0.2.2``). Tool configuration is done via ``pyproject.toml``. +To update the vendored library versions, we have a session defined in ``nox``. +The command to upgrade everything is:: + + nox -s vendoring -- --upgrade-all --skip urllib3 --skip setuptools + +At the time of writing (April 2025) we do not upgrade ``urllib3`` because the +next version is a major upgrade and will be handled as an independent PR. We also +do not upgrade ``setuptools``, because we only rely on ``pkg_resources``, and +tracking every ``setuptools`` change is unnecessary for our needs. + Managing Local Patches ====================== From 3427d969981c01a824e27a89151ec40d911818b2 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 11 Apr 2025 15:28:22 +0100 Subject: [PATCH 5/5] Upgrade typing_extensions to 4.13.2 --- news/typing_extensions.vendor.rst | 2 +- src/pip/_vendor/typing_extensions.py | 42 ++++++++++++++++++++-------- src/pip/_vendor/vendor.txt | 2 +- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/news/typing_extensions.vendor.rst b/news/typing_extensions.vendor.rst index 76ba97d4a2a..48de90c4aa4 100644 --- a/news/typing_extensions.vendor.rst +++ b/news/typing_extensions.vendor.rst @@ -1 +1 @@ -Upgrade typing_extensions to 4.13.0 +Upgrade typing_extensions to 4.13.2 diff --git a/src/pip/_vendor/typing_extensions.py b/src/pip/_vendor/typing_extensions.py index e0c2fb62ad5..da8126b5bf6 100644 --- a/src/pip/_vendor/typing_extensions.py +++ b/src/pip/_vendor/typing_extensions.py @@ -2072,7 +2072,7 @@ def _create_concatenate_alias(origin, parameters): if parameters[-1] is ... and sys.version_info < (3, 9, 2): # Hack: Arguments must be types, replace it with one. parameters = (*parameters[:-1], _EllipsisDummy) - if sys.version_info >= (3, 10, 2): + if sys.version_info >= (3, 10, 3): concatenate = _ConcatenateGenericAlias(origin, parameters, _typevar_types=(TypeVar, ParamSpec), _paramspec_tvars=True) @@ -3123,7 +3123,8 @@ def method(self) -> None: return arg -if hasattr(warnings, "deprecated"): +# Python 3.13.3+ contains a fix for the wrapped __new__ +if sys.version_info >= (3, 13, 3): deprecated = warnings.deprecated else: _T = typing.TypeVar("_T") @@ -3203,7 +3204,7 @@ def __call__(self, arg: _T, /) -> _T: original_new = arg.__new__ @functools.wraps(original_new) - def __new__(cls, *args, **kwargs): + def __new__(cls, /, *args, **kwargs): if cls is arg: warnings.warn(msg, category=category, stacklevel=stacklevel + 1) if original_new is not object.__new__: @@ -3827,14 +3828,27 @@ def __ror__(self, other): TypeAliasType = typing.TypeAliasType # 3.8-3.13 else: - def _is_unionable(obj): - """Corresponds to is_unionable() in unionobject.c in CPython.""" - return obj is None or isinstance(obj, ( - type, - _types.GenericAlias, - _types.UnionType, - TypeAliasType, - )) + if sys.version_info >= (3, 12): + # 3.12-3.14 + def _is_unionable(obj): + """Corresponds to is_unionable() in unionobject.c in CPython.""" + return obj is None or isinstance(obj, ( + type, + _types.GenericAlias, + _types.UnionType, + typing.TypeAliasType, + TypeAliasType, + )) + else: + # 3.8-3.11 + def _is_unionable(obj): + """Corresponds to is_unionable() in unionobject.c in CPython.""" + return obj is None or isinstance(obj, ( + type, + _types.GenericAlias, + _types.UnionType, + TypeAliasType, + )) if sys.version_info < (3, 10): # Copied and pasted from https://github.com/python/cpython/blob/986a4e1b6fcae7fe7a1d0a26aea446107dd58dd2/Objects/genericaliasobject.c#L568-L582, @@ -4371,7 +4385,11 @@ def _lax_type_check( A lax Python 3.11+ like version of typing._type_check """ if hasattr(typing, "_type_convert"): - if _FORWARD_REF_HAS_CLASS: + if ( + sys.version_info >= (3, 10, 3) + or (3, 9, 10) < sys.version_info[:3] < (3, 10) + ): + # allow_special_forms introduced later cpython/#30926 (bpo-46539) type_ = typing._type_convert( value, module=module, diff --git a/src/pip/_vendor/vendor.txt b/src/pip/_vendor/vendor.txt index c867341699f..b3e6fd491b8 100644 --- a/src/pip/_vendor/vendor.txt +++ b/src/pip/_vendor/vendor.txt @@ -11,7 +11,7 @@ requests==2.32.3 urllib3==1.26.20 rich==14.0.0 pygments==2.19.1 - typing_extensions==4.13.0 + typing_extensions==4.13.2 resolvelib==1.1.0 setuptools==70.3.0 tomli==2.2.1