Skip to content

Commit 88ba8f6

Browse files
authored
Merge branch 'main' into add-coverage
2 parents 034ec43 + 5312ff7 commit 88ba8f6

File tree

9 files changed

+267
-148
lines changed

9 files changed

+267
-148
lines changed

.github/workflows/third_party.yml

Lines changed: 119 additions & 132 deletions
Large diffs are not rendered by default.

.pre-commit-config.yaml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.9.6
3+
rev: v0.12.3
44
hooks:
55
- id: ruff
6-
args: [--fix, --exit-non-zero-on-fix]
76
- repo: https://github.com/pre-commit/pre-commit-hooks
8-
rev: v4.5.0
7+
rev: v5.0.0
98
hooks:
109
- id: trailing-whitespace
1110
- id: end-of-file-fixer
@@ -22,7 +21,7 @@ repos:
2221
hooks:
2322
- id: sphinx-lint
2423
- repo: https://github.com/python-jsonschema/check-jsonschema
25-
rev: 0.33.0
24+
rev: 0.33.2
2625
hooks:
2726
- id: check-dependabot
2827
- id: check-github-workflows
@@ -48,3 +47,6 @@ repos:
4847
- repo: meta
4948
hooks:
5049
- id: check-hooks-apply
50+
51+
ci:
52+
autoupdate_schedule: quarterly

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# Unreleased
2+
3+
- Add `typing_extensions.type_repr`, a backport of
4+
[`annotationlib.type_repr`](https://docs.python.org/3.14/library/annotationlib.html#annotationlib.type_repr),
5+
introduced in Python 3.14 (CPython PR [#124551](https://github.com/python/cpython/pull/124551),
6+
originally by Jelle Zijlstra). Patch by Semyon Moroz.
7+
8+
19
# Release 4.14.1 (July 4, 2025)
210

311
- Fix usage of `typing_extensions.TypedDict` nested inside other types

CONTRIBUTING.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ Starting with version 4.0.0, `typing_extensions` uses
2424
[Semantic Versioning](https://semver.org/). See the documentation
2525
for more detail.
2626

27+
## Development version
28+
After a release the version is increased once in [pyproject.toml](/pyproject.toml) and
29+
appended with a `.dev` suffix, e.g. `4.0.1.dev`.
30+
Further subsequent updates are not planned between releases.
31+
2732
# Type stubs
2833

2934
A stub file for `typing_extensions` is maintained
@@ -79,7 +84,7 @@ pipx run pre-commit run -a
7984
# Workflow for PyPI releases
8085

8186
- Make sure you follow the versioning policy in the documentation
82-
(e.g., release candidates before any feature release)
87+
(e.g., release candidates before any feature release, do not release development versions)
8388

8489
- Ensure that GitHub Actions reports no errors.
8590

@@ -93,3 +98,5 @@ pipx run pre-commit run -a
9398

9499
- Release automation will finish the release. You'll have to manually
95100
approve the last step before upload.
101+
102+
- After the release has been published on PyPI upgrade the version in number in [pyproject.toml](/pyproject.toml) to a `dev` version of the next planned release. For example, change 4.1.1 to 4.X.X.dev, see also [Development versions](#development-version). # TODO decide on major vs. minor increase.

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ way as equivalent forms in `typing`.
2323
[Semantic Versioning](https://semver.org/). The
2424
major version will be incremented only for backwards-incompatible changes.
2525
Therefore, it's safe to depend
26-
on `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,
26+
on `typing_extensions` like this: `typing_extensions ~=x.y`,
2727
where `x.y` is the first version that includes all features you need.
28+
[This](https://packaging.python.org/en/latest/specifications/version-specifiers/#compatible-release)
29+
is equivalent to `typing_extensions >=x.y, <(x+1)`. Do not depend on `~= x.y.z`
30+
unless you really know what you're doing; that defeats the purpose of
31+
semantic versioning.
2832

2933
## Included items
3034

doc/index.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,15 @@ Functions
933933

934934
.. versionadded:: 4.1.0
935935

936+
.. function:: type_repr(value)
937+
938+
See :py:func:`annotationlib.type_repr`. In ``annotationlib`` since 3.14.
939+
940+
Convert an arbitrary Python value to a format suitable for use by
941+
the :attr:`Format.STRING`.
942+
943+
.. versionadded:: 4.15.0
944+
936945
Enums
937946
~~~~~
938947

pyproject.toml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,13 @@ ignore = [
9191
"UP019",
9292
"UP035",
9393
"UP038",
94+
"UP045", # X | None instead of Optional[X]
9495
# Not relevant here
95-
"RUF012",
96-
"RUF022",
97-
"RUF023",
96+
"RUF012", # Use ClassVar for mutables
97+
"RUF022", # Unsorted __all__
98+
"RUF023", # Unsorted __slots__
99+
"B903", # Use dataclass / namedtuple
100+
"RUF031", # parentheses for tuples in subscripts
98101
# Ruff doesn't understand the globals() assignment; we test __all__
99102
# directly in test_all_names_in___all__.
100103
"F822",
@@ -109,6 +112,9 @@ ignore = [
109112
"E306",
110113
"E501",
111114
"E701",
115+
# Harmful for tests if applied.
116+
"RUF036", # None not at end of Union
117+
"RUF041", # nested Literal
112118
]
113119

114120
[tool.ruff.lint.isort]

src/test_typing_extensions.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
reveal_type,
102102
runtime,
103103
runtime_checkable,
104+
type_repr,
104105
)
105106

106107
NoneType = type(None)
@@ -1207,13 +1208,15 @@ class My(enum.Enum):
12071208

12081209
self.assertEqual(Literal[My.A].__args__, (My.A,))
12091210

1210-
def test_illegal_parameters_do_not_raise_runtime_errors(self):
1211+
def test_strange_parameters_are_allowed(self):
1212+
# These are explicitly allowed by the typing spec
1213+
Literal[Literal[1, 2], Literal[4, 5]]
1214+
Literal[b"foo", "bar"]
1215+
12111216
# Type checkers should reject these types, but we do not
12121217
# raise errors at runtime to maintain maximum flexibility
12131218
Literal[int]
1214-
Literal[Literal[1, 2], Literal[4, 5]]
12151219
Literal[3j + 2, ..., ()]
1216-
Literal[b"foo", "bar"]
12171220
Literal[{"foo": 3, "bar": 4}]
12181221
Literal[T]
12191222

@@ -8366,6 +8369,44 @@ def test_capsule_type(self):
83668369
self.assertIsInstance(_datetime.datetime_CAPI, typing_extensions.CapsuleType)
83678370

83688371

8372+
class MyClass:
8373+
def __repr__(self):
8374+
return "my repr"
8375+
8376+
8377+
class TestTypeRepr(BaseTestCase):
8378+
def test_custom_types(self):
8379+
8380+
class Nested:
8381+
pass
8382+
8383+
def nested():
8384+
pass
8385+
8386+
self.assertEqual(type_repr(MyClass), f"{__name__}.MyClass")
8387+
self.assertEqual(
8388+
type_repr(Nested),
8389+
f"{__name__}.TestTypeRepr.test_custom_types.<locals>.Nested",
8390+
)
8391+
self.assertEqual(
8392+
type_repr(nested),
8393+
f"{__name__}.TestTypeRepr.test_custom_types.<locals>.nested",
8394+
)
8395+
self.assertEqual(type_repr(times_three), f"{__name__}.times_three")
8396+
self.assertEqual(type_repr(Format.VALUE), repr(Format.VALUE))
8397+
self.assertEqual(type_repr(MyClass()), "my repr")
8398+
8399+
def test_builtin_types(self):
8400+
self.assertEqual(type_repr(int), "int")
8401+
self.assertEqual(type_repr(object), "object")
8402+
self.assertEqual(type_repr(None), "None")
8403+
self.assertEqual(type_repr(len), "len")
8404+
self.assertEqual(type_repr(1), "1")
8405+
self.assertEqual(type_repr("1"), "'1'")
8406+
self.assertEqual(type_repr(''), "''")
8407+
self.assertEqual(type_repr(...), "...")
8408+
8409+
83698410
def times_three(fn):
83708411
@functools.wraps(fn)
83718412
def wrapper(a, b):

0 commit comments

Comments
 (0)