diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32953ebe..6e1497e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,23 @@ concurrency: cancel-in-progress: true jobs: + docs: + name: Build docs + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Install the latest version of uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + with: + python-version: "3.14" + + - name: Build docs in nitpicky mode + run: uvx --from sphinx sphinx-build -n -W -b html doc doc/_build/html + tests: name: Run tests diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6e1a338f..ed56aba1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,6 +56,16 @@ Running these commands in the `src/` directory ensures that the local file `typing_extensions.py` is used, instead of any other version of the library you may have installed. +# Building the documentation + +To build the documentation in nitpicky mode and treat warnings as errors, run: + +``` +uvx --from sphinx sphinx-build -n -W -b html doc doc/_build/html +``` + +The generated HTML documentation is written to `doc/_build/html`. + # Linting Linting is done via pre-commit. We recommend running pre-commit via a tool such diff --git a/doc/conf.py b/doc/conf.py index 8f360306..22b3996c 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -27,9 +27,12 @@ templates_path = ['_templates'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] -# This should usually point to /3, unless there is a necessity to link to -# features in future versions of Python. -intersphinx_mapping = {'py': ('https://docs.python.org/3', None)} +# Use Python 3.15 for new features and Python 3.14 as a fallback for APIs that +# were removed in Python 3.15. +intersphinx_mapping = { + 'py': ('https://docs.python.org/3.15', None), + 'py314': ('https://docs.python.org/3.14', None), +} add_module_names = False diff --git a/doc/index.rst b/doc/index.rst index 05da3431..18d7776d 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -587,14 +587,26 @@ Special typing primitives ``typing_extensions`` backports various bug fixes and improvements to ``TypedDict``. - :py:class:`TypedDict` does not store runtime information - about which (if any) keys are non-required in Python 3.8, and does not - honor the ``total`` keyword with old-style ``TypedDict()`` in Python - 3.9.0 and 3.9.1. :py:class:`typing.TypedDict` also does not support multiple inheritance - with :py:class:`typing.Generic` on Python <3.11, and :py:class:`typing.TypedDict` classes do not - consistently have the ``__orig_bases__`` attribute on Python <3.12. The - ``typing_extensions`` backport provides all of these features and bugfixes on - all Python versions. + Features backported by `typing_extensions` include: + + * :py:class:`typing.TypedDict` does not store runtime information about which + (if any) keys are non-required in Python 3.8, and does not honor the + *total* keyword with old-style ``TypedDict()`` on Python 3.9.0 and 3.9.1. + * :py:class:`typing.TypedDict` does not support multiple inheritance with + :py:class:`typing.Generic` on Python <3.11 + * :py:class:`typing.TypedDict` classes do not consistently have the + :attr:`!__orig_bases__` attribute on Python <3.12. + * :py:class:`typing.TypedDict` does not expose the + :py:attr:`~typing.TypedDict.__readonly_keys__` and + :py:attr:`~typing.TypedDict.__mutable_keys__` attributes on Python <3.13. + * :py:class:`typing.TypedDict` does not support the *extra_items* or *closed* + keyword arguments on Python <3.15, and also does not expose the + :py:attr:`~typing.TypedDict.__extra_items__` and + :py:attr:`~typing.TypedDict.__closed__` attributes on Python <3.15. + + ``typing_extensions`` also currently includes experimental support for + :pep:`764` (inline ``TypedDict``\ s), which is not yet supported in the + standard library on any Python version. Historically, ``TypedDict`` has supported an alternative creation syntax where the fields are supplied as keyword arguments (e.g., @@ -603,6 +615,9 @@ Special typing primitives raises a :py:exc:`DeprecationWarning` when this syntax is used in Python 3.12 or lower and fails with a :py:exc:`TypeError` in Python 3.13 and higher. + Introspection attributes + ------------------------ + ``typing_extensions`` supports the :data:`ReadOnly` qualifier introduced by :pep:`705`. It is reflected in the following attributes: @@ -620,7 +635,7 @@ Special typing primitives .. versionadded:: 4.9.0 - The ``closed`` and ``extra_items`` keyword arguments introduced by + The *closed* and *extra_items* keyword arguments introduced by :pep:`728` and supported in Python 3.15 and newer are supported. For runtime introspection, two attributes can be looked at: @@ -640,6 +655,9 @@ Special typing primitives .. versionadded:: 4.10.0 + History + ------- + .. versionchanged:: 4.3.0 Added support for generic ``TypedDict``\ s. @@ -873,9 +891,7 @@ Protocols Decorators ~~~~~~~~~~ -.. decorator:: dataclass_transform(*, eq_default=False, order_default=False, - kw_only_default=False, frozen_default=False, - field_specifiers=(), **kwargs) +.. decorator:: dataclass_transform(*, eq_default=False, order_default=False, kw_only_default=False, frozen_default=False, field_specifiers=(), **kwargs) See :py:func:`typing.dataclass_transform` and :pep:`681`. In ``typing`` since 3.11. diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index fb7d8e29..996d466c 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -7175,7 +7175,10 @@ def test_all_names_in___all__(self): self.assertLessEqual(exclude, actual_names) def test_typing_extensions_defers_when_possible(self): - exclude = set() + # These two are currently always different to `typing.TypedDict` + # as PEP 764 has not yet been accepted/implemented upstream. + exclude = {'TypedDict', 'is_typeddict'} + if sys.version_info < (3, 10): exclude |= {'get_args', 'get_origin'} if sys.version_info < (3, 10, 1): @@ -7202,8 +7205,6 @@ def test_typing_extensions_defers_when_possible(self): exclude |= { 'TypeVarTuple' } - if not typing_extensions._PEP_728_IMPLEMENTED: - exclude |= {'TypedDict', 'is_typeddict'} for item in typing_extensions.__all__: if item not in exclude and hasattr(typing, item): self.assertIs( diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 59f4849c..f95c0bcd 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -1131,10 +1131,10 @@ def __reduce__(self): # Update this to something like >=3.13.0b1 if and when -# PEP 728 is implemented in CPython -_PEP_728_IMPLEMENTED = False +# PEP 764 is implemented in CPython +_PEP_764_IMPLEMENTED = False -if _PEP_728_IMPLEMENTED: +if _PEP_764_IMPLEMENTED: # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 # The standard library TypedDict below Python 3.11 does not store runtime @@ -1144,7 +1144,8 @@ def __reduce__(self): # to enable better runtime introspection. # On 3.13 we deprecate some odd ways of creating TypedDicts. # Also on 3.13, PEP 705 adds the ReadOnly[] qualifier. - # PEP 728 (still pending) makes more changes. + # PEP 728 (Python 3.15+) adds the `extra_items` and `closed` keywords. + # PEP 764 (still pending) allows the `TypedDict` special form to be subscripted. TypedDict = typing.TypedDict _TypedDictMeta = typing._TypedDictMeta is_typeddict = typing.is_typeddict