Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-eval-type-backport for 
openSUSE:Factory checked in at 2026-06-15 19:43:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-eval-type-backport (Old)
 and      /work/SRC/openSUSE:Factory/.python-eval-type-backport.new.1981 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-eval-type-backport"

Mon Jun 15 19:43:12 2026 rev:2 rq:1359267 version:0.4.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-eval-type-backport/python-eval-type-backport.changes
      2026-01-15 16:48:54.382855385 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-eval-type-backport.new.1981/python-eval-type-backport.changes
    2026-06-15 19:46:16.322525220 +0200
@@ -1,0 +2,6 @@
+Sun Jun 14 15:43:08 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 0.4.0:
+  * Add install_patch() function for easy usage, updated docs
+
+-------------------------------------------------------------------

Old:
----
  eval_type_backport-0.3.1.tar.gz

New:
----
  eval_type_backport-0.4.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-eval-type-backport.spec ++++++
--- /var/tmp/diff_new_pack.wVIyO3/_old  2026-06-15 19:46:17.970594286 +0200
+++ /var/tmp/diff_new_pack.wVIyO3/_new  2026-06-15 19:46:17.978594621 +0200
@@ -1,7 +1,7 @@
 #
-# spec file for package python-eval_type_backport
+# spec file for package python-eval-type-backport
 #
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           python-eval-type-backport
-Version:        0.3.1
+Version:        0.4.0
 Release:        0
 Summary:        `typing._eval_type` for older Python versions
 License:        MIT

++++++ eval_type_backport-0.3.1.tar.gz -> eval_type_backport-0.4.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eval_type_backport-0.3.1/.github/workflows/test.yml 
new/eval_type_backport-0.4.0/.github/workflows/test.yml
--- old/eval_type_backport-0.3.1/.github/workflows/test.yml     2025-12-02 
12:50:19.000000000 +0100
+++ new/eval_type_backport-0.4.0/.github/workflows/test.yml     2026-06-02 
15:20:20.000000000 +0200
@@ -8,6 +8,27 @@
   workflow_dispatch:
 
 jobs:
+  pre-commit:
+    name: pre-commit
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v4
+    - name: Set up Python
+      uses: actions/setup-python@v5
+      with:
+        python-version: '3.13'
+    - name: Install dependencies
+      run: |
+        python -m venv venv
+        . venv/bin/activate
+        python --version
+        pip install --upgrade pip pre-commit uv .[tests]
+    - name: Run pre-commit
+      run: |
+        . venv/bin/activate
+        pre-commit run --all-files
+
   test:
     name: test ${{ matrix.os }} / ${{ matrix.python-version }}
     strategy:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eval_type_backport-0.3.1/.pre-commit-config.yaml 
new/eval_type_backport-0.4.0/.pre-commit-config.yaml
--- old/eval_type_backport-0.3.1/.pre-commit-config.yaml        2024-07-07 
23:49:24.000000000 +0200
+++ new/eval_type_backport-0.4.0/.pre-commit-config.yaml        2026-06-02 
15:20:20.000000000 +0200
@@ -1,6 +1,6 @@
 repos:
 - repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: v4.5.0
+  rev: v6.0.0
   hooks:
   - id: check-yaml
   - id: check-toml
@@ -8,12 +8,21 @@
   - id: trailing-whitespace
   - id: check-merge-conflict
   - id: debug-statements
-- repo: https://github.com/RobertCraigie/pyright-python
-  rev: v1.1.335
+- repo: local
   hooks:
   - id: pyright
-- repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.1.5
-  hooks:
-    - id: ruff
-    - id: ruff-format
+    name: pyright
+    entry: uvx pyright@latest
+    language: system
+    pass_filenames: false
+    types_or: [python, pyi]
+  - id: ruff
+    name: ruff
+    entry: uvx ruff@latest check --force-exclude
+    language: system
+    types_or: [python, pyi]
+  - id: ruff-format
+    name: ruff format
+    entry: uvx ruff@latest format --force-exclude
+    language: system
+    types_or: [python, pyi]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eval_type_backport-0.3.1/PKG-INFO 
new/eval_type_backport-0.4.0/PKG-INFO
--- old/eval_type_backport-0.3.1/PKG-INFO       2025-12-02 12:51:37.228015700 
+0100
+++ new/eval_type_backport-0.4.0/PKG-INFO       2026-06-02 15:22:01.362258200 
+0200
@@ -1,6 +1,6 @@
-Metadata-Version: 2.4
+Metadata-Version: 2.1
 Name: eval_type_backport
-Version: 0.3.1
+Version: 0.4.0
 Summary: Like `typing._eval_type`, but lets older Python versions use newer 
typing features.
 Home-page: https://github.com/alexmojaki/eval_type_backport
 Author: Alex Hall
@@ -22,20 +22,40 @@
 License-File: LICENSE.txt
 Provides-Extra: tests
 Requires-Dist: pytest; extra == "tests"
-Dynamic: license-file
 
 # eval_type_backport
 
 [![Build 
Status](https://github.com/alexmojaki/eval_type_backport/workflows/Tests/badge.svg)](https://github.com/alexmojaki/eval_type_backport/actions)
 [![Coverage 
Status](https://coveralls.io/repos/github/alexmojaki/eval_type_backport/badge.svg)](https://coveralls.io/github/alexmojaki/eval_type_backport)
 [![Supports Python versions 3.7+, including 
PyPy](https://img.shields.io/pypi/pyversions/eval_type_backport.svg)](https://pypi.python.org/pypi/eval_type_backport)
 [![Anaconda's conda-forge 
channel](https://anaconda.org/conda-forge/eval-type-backport/badges/version.svg)](https://anaconda.org/conda-forge/eval-type-backport)
 
-This is a tiny package providing a replacement for `typing._eval_type` to 
support newer typing features in older Python versions.
-
-Yes, that's very specific, and yes, `typing._eval_type` is a protected 
function that you shouldn't normally be using. Really this package is 
specifically made for https://github.com/pydantic/pydantic/issues/7873.
-
+This package makes runtime typing inspection with the `typing` module possible 
with newer syntax in older Python versions.
 Specifically, this transforms `X | Y` into `typing.Union[X, Y]`
 and `list[X]` into `typing.List[X]` etc. (for all the types made generic in 
PEP 585)
 if the original syntax is not supported in the current Python version.
 
+For users of Pydantic, merely having this package installed should make 
Pydantic models with newer syntax work in older Python versions.
+
+Here's an example of how to use it directly:
+
+```python
+import typing
+
+from eval_type_backport import install_patch
+
+install_patch()
+
+
+class Foo:
+    a: 'int | str'
+
+
+print(typing.get_type_hints(Foo))
+```
+
+In Python 3.9, without the `install_patch`, the above code will raise 
`TypeError: unsupported operand type(s) for |: 'type' and 'type'`.
+With it, it prints `{'a': typing.Union[int, str]}` as expected.
+
+`install_patch` monkey-patches the `typing` module so that things should 'just 
work' from wherever. To specifically use the backport where you need it instead 
of patching globally, you can use the `eval_type_backport.eval_type_backport` 
function directly instead of `typing._eval_type`. Yes, `typing._eval_type` is a 
protected function that you shouldn't normally be using. This package was 
written to support Pydantic, which uses it internally.
+
 ## Install
 
 From PyPI:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eval_type_backport-0.3.1/README.md 
new/eval_type_backport-0.4.0/README.md
--- old/eval_type_backport-0.3.1/README.md      2025-12-02 12:50:19.000000000 
+0100
+++ new/eval_type_backport-0.4.0/README.md      2026-05-28 14:05:12.000000000 
+0200
@@ -2,14 +2,35 @@
 
 [![Build 
Status](https://github.com/alexmojaki/eval_type_backport/workflows/Tests/badge.svg)](https://github.com/alexmojaki/eval_type_backport/actions)
 [![Coverage 
Status](https://coveralls.io/repos/github/alexmojaki/eval_type_backport/badge.svg)](https://coveralls.io/github/alexmojaki/eval_type_backport)
 [![Supports Python versions 3.7+, including 
PyPy](https://img.shields.io/pypi/pyversions/eval_type_backport.svg)](https://pypi.python.org/pypi/eval_type_backport)
 [![Anaconda's conda-forge 
channel](https://anaconda.org/conda-forge/eval-type-backport/badges/version.svg)](https://anaconda.org/conda-forge/eval-type-backport)
 
-This is a tiny package providing a replacement for `typing._eval_type` to 
support newer typing features in older Python versions.
-
-Yes, that's very specific, and yes, `typing._eval_type` is a protected 
function that you shouldn't normally be using. Really this package is 
specifically made for https://github.com/pydantic/pydantic/issues/7873.
-
+This package makes runtime typing inspection with the `typing` module possible 
with newer syntax in older Python versions.
 Specifically, this transforms `X | Y` into `typing.Union[X, Y]`
 and `list[X]` into `typing.List[X]` etc. (for all the types made generic in 
PEP 585)
 if the original syntax is not supported in the current Python version.
 
+For users of Pydantic, merely having this package installed should make 
Pydantic models with newer syntax work in older Python versions.
+
+Here's an example of how to use it directly:
+
+```python
+import typing
+
+from eval_type_backport import install_patch
+
+install_patch()
+
+
+class Foo:
+    a: 'int | str'
+
+
+print(typing.get_type_hints(Foo))
+```
+
+In Python 3.9, without the `install_patch`, the above code will raise 
`TypeError: unsupported operand type(s) for |: 'type' and 'type'`.
+With it, it prints `{'a': typing.Union[int, str]}` as expected.
+
+`install_patch` monkey-patches the `typing` module so that things should 'just 
work' from wherever. To specifically use the backport where you need it instead 
of patching globally, you can use the `eval_type_backport.eval_type_backport` 
function directly instead of `typing._eval_type`. Yes, `typing._eval_type` is a 
protected function that you shouldn't normally be using. This package was 
written to support Pydantic, which uses it internally.
+
 ## Install
 
 From PyPI:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/eval_type_backport-0.3.1/eval_type_backport/__init__.py 
new/eval_type_backport-0.4.0/eval_type_backport/__init__.py
--- old/eval_type_backport-0.3.1/eval_type_backport/__init__.py 2024-07-07 
23:49:24.000000000 +0200
+++ new/eval_type_backport-0.4.0/eval_type_backport/__init__.py 2026-05-28 
14:05:12.000000000 +0200
@@ -1,4 +1,4 @@
-from .eval_type_backport import ForwardRef, eval_type_backport
+from .eval_type_backport import ForwardRef, eval_type_backport, install_patch
 
 try:
     from .version import __version__
@@ -6,4 +6,4 @@
     # version.py is auto-generated with the git tag when building
     __version__ = '???'
 
-__all__ = ['ForwardRef', 'eval_type_backport', '__version__']
+__all__ = ['install_patch', 'ForwardRef', 'eval_type_backport', '__version__']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/eval_type_backport-0.3.1/eval_type_backport/eval_type_backport.py 
new/eval_type_backport-0.4.0/eval_type_backport/eval_type_backport.py
--- old/eval_type_backport-0.3.1/eval_type_backport/eval_type_backport.py       
2025-11-13 21:53:00.000000000 +0100
+++ new/eval_type_backport-0.4.0/eval_type_backport/eval_type_backport.py       
2026-06-02 15:20:20.000000000 +0200
@@ -28,52 +28,74 @@
 
 
 # From https://peps.python.org/pep-0585/#implementation
+_generic_type_origins: tuple[Any, ...] = (
+    collections.OrderedDict,
+    collections.Counter,
+    collections.ChainMap,
+    collections.abc.Awaitable,
+    collections.abc.Coroutine,
+    collections.abc.AsyncIterable,
+    collections.abc.AsyncIterator,
+    collections.abc.AsyncGenerator,
+    collections.abc.Iterable,
+    collections.abc.Iterator,
+    collections.abc.Generator,
+    collections.abc.Reversible,
+    collections.abc.Container,
+    collections.abc.Collection,
+    collections.abc.Callable,
+    collections.abc.MutableSet,
+    collections.abc.Mapping,
+    collections.abc.MutableMapping,
+    collections.abc.Sequence,
+    collections.abc.MutableSequence,
+    collections.abc.MappingView,
+    collections.abc.KeysView,
+    collections.abc.ItemsView,
+    collections.abc.ValuesView,
+    re.Pattern,
+    re.Match,
+)
+
+
 new_generic_types = {
-    tuple: 'Tuple',
-    list: 'List',
-    dict: 'Dict',
-    set: 'Set',
-    frozenset: 'FrozenSet',
-    type: 'Type',
-    collections.deque: 'Deque',
-    collections.defaultdict: 'DefaultDict',
-    collections.abc.Set: 'AbstractSet',
-    contextlib.AbstractContextManager: 'ContextManager',
-    contextlib.AbstractAsyncContextManager: 'AsyncContextManager',
-    **{
-        k: k.__name__
-        for k in (
-            collections.OrderedDict,
-            collections.Counter,
-            collections.ChainMap,
-            collections.abc.Awaitable,
-            collections.abc.Coroutine,
-            collections.abc.AsyncIterable,
-            collections.abc.AsyncIterator,
-            collections.abc.AsyncGenerator,
-            collections.abc.Iterable,
-            collections.abc.Iterator,
-            collections.abc.Generator,
-            collections.abc.Reversible,
-            collections.abc.Container,
-            collections.abc.Collection,
-            collections.abc.Callable,
-            collections.abc.MutableSet,
-            collections.abc.Mapping,
-            collections.abc.MutableMapping,
-            collections.abc.Sequence,
-            collections.abc.MutableSequence,
-            collections.abc.MappingView,
-            collections.abc.KeysView,
-            collections.abc.ItemsView,
-            collections.abc.ValuesView,
-            re.Pattern,
-            re.Match,
-        )
-    },
+    tuple: typing.Tuple,
+    list: typing.List,
+    dict: typing.Dict,
+    set: typing.Set,
+    frozenset: typing.FrozenSet,
+    type: typing.Type,
+    collections.deque: typing.Deque,
+    collections.defaultdict: typing.DefaultDict,
+    collections.abc.Set: typing.AbstractSet,
+    contextlib.AbstractContextManager: typing.ContextManager,
+    contextlib.AbstractAsyncContextManager: typing.AsyncContextManager,
+    **{k: getattr(typing, k.__name__) for k in _generic_type_origins},
 }
 
 
+def safe_or(a: Any, b: Any) -> Any:
+    try:
+        return a | b
+    except TypeError as e:
+        if not is_unsupported_types_for_union_error(e):
+            raise
+        union = typing.Union
+        return union[a, b]
+
+
+def safe_subscript(value: Any, index: Any) -> Any:
+    try:
+        return value[index]
+    except TypeError as e:
+        if not is_not_subscriptable_error(e):
+            raise
+        if value not in new_generic_types:
+            raise
+        new_value = new_generic_types[value]
+        return new_value[index]
+
+
 class BackportTransformer(ast.NodeTransformer):
     """
     Transforms `X | Y` into `typing.Union[X, Y]`
@@ -94,81 +116,57 @@
         elif localns is None:
             localns = globalns
 
-        self.typing_name = f'typing_{uuid.uuid4().hex}'
+        self.safe_or_name = f'safe_or_{uuid.uuid4().hex}'
+        self.safe_subscript_name = f'safe_subscript_{uuid.uuid4().hex}'
         self.globalns = globalns
-        self.localns = {**localns, self.typing_name: typing}
+        self.localns = {
+            **localns,
+            self.safe_or_name: safe_or,
+            self.safe_subscript_name: safe_subscript,
+        }
 
     def eval_type(
         self,
-        node: ast.Expression | ast.expr,
-        *,
-        original_ref: typing.ForwardRef | None = None,
+        node: ast.Expression,
+        original_ref: typing.ForwardRef,
     ) -> Any:
-        if not isinstance(node, ast.Expression):
-            node = ast.copy_location(ast.Expression(node), node)
         ref = typing.ForwardRef(ast.dump(node))
-        if original_ref:
-            for attr in 'is_argument is_class module'.split():
-                attr = f'__forward_{attr}__'
-                if hasattr(original_ref, attr):
-                    setattr(ref, attr, getattr(original_ref, attr))
+        for attr in 'is_argument is_class module'.split():
+            attr = f'__forward_{attr}__'
+            if hasattr(original_ref, attr):
+                setattr(ref, attr, getattr(original_ref, attr))
         ref.__forward_code__ = compile(node, '<node>', 'eval')
         return typing._eval_type(  # type: ignore
             ref, self.globalns, self.localns
         )
 
-    def visit_BinOp(self, node) -> ast.BinOp | ast.Subscript:
+    def _call(self, func_name: str, args: list[ast.expr]) -> ast.Call:
+        return ast.fix_missing_locations(
+            ast.Call(
+                func=ast.Name(id=func_name, ctx=ast.Load()),
+                args=args,
+                keywords=[],
+            )
+        )
+
+    def visit_BinOp(self, node) -> ast.BinOp | ast.Call:
         node = self.generic_visit(node)
         assert isinstance(node, ast.BinOp)
-        if isinstance(node.op, ast.BitOr):
-            left_val = self.eval_type(node.left)
-            right_val = self.eval_type(node.right)
-            try:
-                _ = left_val | right_val
-            except TypeError as e:
-                if not is_unsupported_types_for_union_error(e):
-                    raise
-                # Replace `left | right` with `typing.Union[left, right]`
-                replacement = ast.Subscript(
-                    value=ast.Attribute(
-                        value=ast.Name(id=self.typing_name, ctx=ast.Load()),
-                        attr='Union',
-                        ctx=ast.Load(),
-                    ),
-                    slice=ast.Index(
-                        value=ast.Tuple(elts=[node.left, node.right], 
ctx=ast.Load())
-                    ),
-                    ctx=ast.Load(),
-                )
-                return ast.fix_missing_locations(replacement)
+        if not isinstance(node.op, ast.BitOr):
+            return node
 
-        return node
+        return self._call(self.safe_or_name, [node.left, node.right])
 
     if sys.version_info[:2] < (3, 9):
 
-        def visit_Subscript(self, node) -> ast.Subscript:
+        def visit_Subscript(self, node) -> ast.Subscript | ast.Call:
             node = self.generic_visit(node)
             assert isinstance(node, ast.Subscript)
-            try:
-                value_val = self.eval_type(node.value)
-            except TypeError:
-                # Likely typing._type_check complaining that the result isn't 
a type,
-                # e.g. that it's a plain `Literal`.
-                # Either way, this probably isn't one of the new generic types
-                # that needs replacing.
-                return node
-            if value_val not in new_generic_types:
+            if not isinstance(node.slice, ast.Index):
                 return node
-            replacement = ast.Subscript(
-                value=ast.Attribute(
-                    value=ast.Name(id=self.typing_name, ctx=ast.Load()),
-                    attr=new_generic_types[value_val],
-                    ctx=ast.Load(),
-                ),
-                slice=node.slice,
-                ctx=ast.Load(),
-            )
-            return ast.fix_missing_locations(replacement)
+
+            slice_value = node.slice.value  # type: ignore
+            return self._call(self.safe_subscript_name, [node.value, 
slice_value])
 
 
 original_evaluate = typing.ForwardRef._evaluate
@@ -188,7 +186,7 @@
         """
 
         @functools.wraps(original_evaluate)
-        def _evaluate(
+        def _evaluate(  # pyright: ignore[reportIncompatibleMethodOverride]
             self,
             globalns: dict[str, Any] | None,
             localns: dict[str, Any] | None,
@@ -215,7 +213,8 @@
 
 
 if sys.version_info[:2] >= (3, 10):
-    def eval_type_backport(
+
+    def eval_type_backport(  # type: ignore  # allow duplicate declaration
         value: Any,
         globalns: dict[str, Any] | None = None,
         localns: Mapping[str, Any] | None = None,
@@ -252,3 +251,12 @@
             ):
                 raise
             return _eval_direct(value, globalns, localns)
+
+
+def install_patch() -> None:
+    """Monkey-patch `typing.ForwardRef._evaluate` to support newer syntax on 
older Python versions.
+
+    This indirectly makes functions like `typing.get_type_hints` and 
`typing._eval_type` work as well.
+    """
+    if sys.version_info[:2] < (3, 10):
+        typing.ForwardRef._evaluate = ForwardRef._evaluate  # type: ignore
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/eval_type_backport-0.3.1/eval_type_backport/version.py 
new/eval_type_backport-0.4.0/eval_type_backport/version.py
--- old/eval_type_backport-0.3.1/eval_type_backport/version.py  2025-12-02 
12:51:37.000000000 +0100
+++ new/eval_type_backport-0.4.0/eval_type_backport/version.py  2026-06-02 
15:22:01.000000000 +0200
@@ -1 +1 @@
-__version__ = '0.3.1'
\ No newline at end of file
+__version__ = '0.4.0'
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/eval_type_backport-0.3.1/eval_type_backport.egg-info/PKG-INFO 
new/eval_type_backport-0.4.0/eval_type_backport.egg-info/PKG-INFO
--- old/eval_type_backport-0.3.1/eval_type_backport.egg-info/PKG-INFO   
2025-12-02 12:51:37.000000000 +0100
+++ new/eval_type_backport-0.4.0/eval_type_backport.egg-info/PKG-INFO   
2026-06-02 15:22:01.000000000 +0200
@@ -1,6 +1,6 @@
-Metadata-Version: 2.4
+Metadata-Version: 2.1
 Name: eval_type_backport
-Version: 0.3.1
+Version: 0.4.0
 Summary: Like `typing._eval_type`, but lets older Python versions use newer 
typing features.
 Home-page: https://github.com/alexmojaki/eval_type_backport
 Author: Alex Hall
@@ -22,20 +22,40 @@
 License-File: LICENSE.txt
 Provides-Extra: tests
 Requires-Dist: pytest; extra == "tests"
-Dynamic: license-file
 
 # eval_type_backport
 
 [![Build 
Status](https://github.com/alexmojaki/eval_type_backport/workflows/Tests/badge.svg)](https://github.com/alexmojaki/eval_type_backport/actions)
 [![Coverage 
Status](https://coveralls.io/repos/github/alexmojaki/eval_type_backport/badge.svg)](https://coveralls.io/github/alexmojaki/eval_type_backport)
 [![Supports Python versions 3.7+, including 
PyPy](https://img.shields.io/pypi/pyversions/eval_type_backport.svg)](https://pypi.python.org/pypi/eval_type_backport)
 [![Anaconda's conda-forge 
channel](https://anaconda.org/conda-forge/eval-type-backport/badges/version.svg)](https://anaconda.org/conda-forge/eval-type-backport)
 
-This is a tiny package providing a replacement for `typing._eval_type` to 
support newer typing features in older Python versions.
-
-Yes, that's very specific, and yes, `typing._eval_type` is a protected 
function that you shouldn't normally be using. Really this package is 
specifically made for https://github.com/pydantic/pydantic/issues/7873.
-
+This package makes runtime typing inspection with the `typing` module possible 
with newer syntax in older Python versions.
 Specifically, this transforms `X | Y` into `typing.Union[X, Y]`
 and `list[X]` into `typing.List[X]` etc. (for all the types made generic in 
PEP 585)
 if the original syntax is not supported in the current Python version.
 
+For users of Pydantic, merely having this package installed should make 
Pydantic models with newer syntax work in older Python versions.
+
+Here's an example of how to use it directly:
+
+```python
+import typing
+
+from eval_type_backport import install_patch
+
+install_patch()
+
+
+class Foo:
+    a: 'int | str'
+
+
+print(typing.get_type_hints(Foo))
+```
+
+In Python 3.9, without the `install_patch`, the above code will raise 
`TypeError: unsupported operand type(s) for |: 'type' and 'type'`.
+With it, it prints `{'a': typing.Union[int, str]}` as expected.
+
+`install_patch` monkey-patches the `typing` module so that things should 'just 
work' from wherever. To specifically use the backport where you need it instead 
of patching globally, you can use the `eval_type_backport.eval_type_backport` 
function directly instead of `typing._eval_type`. Yes, `typing._eval_type` is a 
protected function that you shouldn't normally be using. This package was 
written to support Pydantic, which uses it internally.
+
 ## Install
 
 From PyPI:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/eval_type_backport-0.3.1/pyproject.toml 
new/eval_type_backport-0.4.0/pyproject.toml
--- old/eval_type_backport-0.3.1/pyproject.toml 2024-07-07 23:49:24.000000000 
+0200
+++ new/eval_type_backport-0.4.0/pyproject.toml 2026-06-02 15:20:20.000000000 
+0200
@@ -7,13 +7,14 @@
 write_to_template = "__version__ = '{version}'"
 
 [tool.pyright]
+include = ["eval_type_backport", "tests"]
 venvPath = "."
 venv = "venv"
 
 [tool.ruff]
 line-length = 89
-flake8-quotes = {inline-quotes = 'single', multiline-quotes = 'double'}
-mccabe = { max-complexity = 14 }
+lint.flake8-quotes = {inline-quotes = 'single', multiline-quotes = 'double'}
+lint.mccabe = { max-complexity = 14 }
 target-version = "py38"
 
 [tool.ruff.format]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/eval_type_backport-0.3.1/tests/test_eval_type_backport.py 
new/eval_type_backport-0.4.0/tests/test_eval_type_backport.py
--- old/eval_type_backport-0.3.1/tests/test_eval_type_backport.py       
2024-12-21 21:10:54.000000000 +0100
+++ new/eval_type_backport-0.4.0/tests/test_eval_type_backport.py       
2026-05-28 20:49:00.000000000 +0200
@@ -8,8 +8,9 @@
 
 import pytest
 
-from eval_type_backport import ForwardRef, eval_type_backport
-from eval_type_backport.eval_type_backport import new_generic_types
+import eval_type_backport as eval_type_backport_module
+from eval_type_backport import ForwardRef, eval_type_backport, install_patch
+from eval_type_backport.eval_type_backport import new_generic_types, 
safe_subscript
 
 str((collections, contextlib, re))  # mark these as used (by eval calls)
 
@@ -136,6 +137,11 @@
     pass
 
 
+class OtherSubscriptError:
+    def __class_getitem__(cls, item):
+        raise TypeError('other')
+
+
 def test_other_or_type_error():
     for code in [
         'Foo | (int | str)',
@@ -182,7 +188,7 @@
                 assert args == t.get_args(expected_new)
                 assert origin == t.get_origin(expected_new)
                 assert origin[args] == expected_new != expected_old
-                assert t.get_origin(getattr(t, new_generic_types[origin])) == 
origin
+                assert t.get_origin(new_generic_types[origin]) == origin
             else:
                 assert eval_type_backport(ref, **kwargs) == expected_old
 
@@ -354,6 +360,42 @@
         t.Match[str],
     )
 
+    assert eval_type_backport(ForwardRef('tuple[(int, str)[:]]')) == 
eval_type_backport(
+        ForwardRef('tuple[int, str]')
+    )
+
+
+def test_unsupported_subscript_type_error():
+    with pytest.raises(TypeError, match='subscriptable'):
+        eval_type_backport(
+            ForwardRef('Foo[int]'),
+            globalns=globals(),
+            localns=locals(),
+        )
+
+
+def test_subscript_other_error():
+    with pytest.raises(TypeError, match='other'):
+        eval_type_backport(
+            ForwardRef('OtherSubscriptError[int]'),
+            globalns=globals(),
+            localns=locals(),
+            try_default=False,
+        )
+
+
+def test_safe_subscript():
+    if sys.version_info[:2] < (3, 9):
+        assert safe_subscript(list, int) == t.List[int]
+    else:
+        assert safe_subscript(list, int) == list[int]
+
+    with pytest.raises(TypeError, match='subscriptable'):
+        safe_subscript(Foo(), int)
+
+    with pytest.raises(TypeError, match='other'):
+        safe_subscript(OtherSubscriptError, int)
+
 
 def test_copy_forward_ref_attrs():
     ref = t.ForwardRef(
@@ -362,3 +404,22 @@
         **({} if sys.version_info < (3, 9, 8) else {'is_class': True}),
     )
     eval_type_backport(ref, globalns=globals(), localns=locals())
+
+
+def test_install_patch_supports_get_type_hints():
+    assert 'install_patch' in eval_type_backport_module.__all__
+    assert eval_type_backport_module.install_patch is install_patch
+
+    class Foo:
+        value: int | str
+
+    original_evaluate = t.ForwardRef._evaluate
+    try:
+        install_patch()
+        assert t.get_type_hints(Foo) == {'value': t.Union[int, str]}
+        if sys.version_info[:2] < (3, 10):
+            assert t.ForwardRef._evaluate is ForwardRef._evaluate
+        else:
+            assert t.ForwardRef._evaluate is original_evaluate
+    finally:
+        t.ForwardRef._evaluate = original_evaluate

Reply via email to