diff --git a/.github/workflows/check_docs.yml b/.github/workflows/check_docs.yml index 99aecda1..1a9a4ab9 100644 --- a/.github/workflows/check_docs.yml +++ b/.github/workflows/check_docs.yml @@ -6,7 +6,7 @@ on: env: POETRY_VERSION: 1.8.2 - PYTHON_VERSION: 3.11.9 + PYTHON_VERSION: 3.13.3 jobs: check_docs: diff --git a/src/nitypes/_typing.py b/src/nitypes/_typing.py index db8a8759..877f1ff5 100644 --- a/src/nitypes/_typing.py +++ b/src/nitypes/_typing.py @@ -1,4 +1,21 @@ -"""Single source for typing backports to avoid depending on typing_extensions at run time.""" +"""Version-specific compatibility shims for the standard `typing` module. + +For `typing` symbols that require Python 3.10 or later, this submodule redirects to the standard +`typing` module (when appropriate), the `typing_extensions` package, or provides a minimial stub +implementation for run time. This allows us to use new typing features without littering the rest of +our code with conditionals or making our Python packages depend on `typing_extenions` at run time. + +For `typing` symbols that are supported in Python 3.9, you do not need this submodule. Import these +symbols directly from the standard `typing` module. + +This submodule is vendored in multiple packages (nitypes, nipanel, etc.) to avoid compatibility +breakage when upgrading these packages. + +Do not add project-specific types to this submodule. + +Many of these symbosl are references to `None` at run time. Clients of this submodule should use +`from __future__ import annotations` to avoid parsing type hints at run time. +""" from __future__ import annotations @@ -6,36 +23,111 @@ from typing import TYPE_CHECKING if sys.version_info >= (3, 10): - from typing import TypeAlias + from typing import ( + Concatenate, + ParamSpec, + ParamSpecArgs, + ParamSpecKwargs, + TypeAlias, + TypeGuard, + ) elif TYPE_CHECKING: - from typing_extensions import TypeAlias + from typing_extensions import ( + Concatenate, + ParamSpec, + ParamSpecArgs, + ParamSpecKwargs, + TypeAlias, + TypeGuard, + ) else: - TypeAlias = None + Concatenate = ParamSpecArgs = ParamSpecKwargs = TypeAlias = TypeGuard = None + + def ParamSpec( # noqa: D103, N802 - Missing docstring, wrong case + name, *, bound=None, covariant=False, contravariant=False + ): + return None + if sys.version_info >= (3, 11): - from typing import Self, assert_type + from typing import ( + LiteralString, + Never, + NotRequired, + Required, + Self, + TypeVarTuple, + Unpack, + assert_never, + assert_type, + reveal_type, + ) elif TYPE_CHECKING: - from typing_extensions import Self, assert_type + from typing_extensions import ( + LiteralString, + Never, + NotRequired, + Required, + Self, + TypeVarTuple, + Unpack, + assert_never, + assert_type, + reveal_type, + ) else: - Self = None + LiteralString = Never = NotRequired = Required = Self = Unpack = None + + def assert_never(arg, /): # noqa: D103 - Missing docstring in public function + pass def assert_type(val, typ, /): # noqa: D103 - Missing docstring in public function pass + def reveal_type(obj, /): # noqa: D103 - Missing docstring in public function + pass + + def TypeVarTuple(name): # noqa: D103, N802 - Missing docstring, wrong case + return None + if sys.version_info >= (3, 12): - from typing import override + from typing import TypeAliasType, override elif TYPE_CHECKING: - from typing_extensions import override + from typing_extensions import TypeAliasType, override else: + TypeAliasType = None def override(arg, /): # noqa: D103 - Missing docstring in public function return arg +if sys.version_info >= (3, 13): + from typing import ReadOnly, TypeIs +elif TYPE_CHECKING: + from typing_extensions import ReadOnly, TypeIs +else: + ReadOnly = TypeIs = None + __all__ = [ + "assert_never", "assert_type", + "Concatenate", + "LiteralString", + "Never", + "NotRequired", "override", + "ParamSpec", + "ParamSpecArgs", + "ParamSpecKwargs", + "ReadOnly", + "Required", + "reveal_type", "Self", "TypeAlias", + "TypeAliasType", + "TypeGuard", + "TypeIs", + "TypeVarTuple", + "Unpack", ] diff --git a/src/nitypes/time/_conversion.py b/src/nitypes/time/_conversion.py index a850c5c9..134b3abf 100644 --- a/src/nitypes/time/_conversion.py +++ b/src/nitypes/time/_conversion.py @@ -8,7 +8,11 @@ import hightime as ht from nitypes._exceptions import invalid_arg_type, invalid_requested_type -from nitypes._typing import TypeAlias + +try: + from typing import TypeAlias +except ImportError: + from nitypes._typing import TypeAlias _AnyDateTime: TypeAlias = Union[dt.datetime, ht.datetime] _TDateTime = TypeVar("_TDateTime", dt.datetime, ht.datetime) diff --git a/src/nitypes/waveform/_analog_waveform.py b/src/nitypes/waveform/_analog_waveform.py index b8f825b4..22146124 100644 --- a/src/nitypes/waveform/_analog_waveform.py +++ b/src/nitypes/waveform/_analog_waveform.py @@ -12,7 +12,6 @@ from nitypes._arguments import arg_to_uint, validate_dtype, validate_unsupported_arg from nitypes._exceptions import invalid_arg_type, invalid_array_ndim -from nitypes._typing import Self, TypeAlias from nitypes.waveform._exceptions import ( input_array_data_type_mismatch, input_waveform_data_type_mismatch, @@ -27,6 +26,11 @@ from nitypes.waveform._timing import BaseTiming, PrecisionTiming, Timing, convert_timing from nitypes.waveform._warnings import scale_mode_mismatch +try: + from typing import Self, TypeAlias +except ImportError: + from nitypes._typing import Self, TypeAlias + if sys.version_info < (3, 10): import array as std_array diff --git a/src/nitypes/waveform/_extended_properties.py b/src/nitypes/waveform/_extended_properties.py index f5247aa6..203127a3 100644 --- a/src/nitypes/waveform/_extended_properties.py +++ b/src/nitypes/waveform/_extended_properties.py @@ -4,7 +4,10 @@ from collections.abc import Mapping from typing import Iterator, MutableMapping, Union -from nitypes._typing import TypeAlias +try: + from typing import TypeAlias +except ImportError: + from nitypes._typing import TypeAlias # Extended property keys CHANNEL_NAME = "NI_ChannelName" diff --git a/src/nitypes/waveform/_timing/_base.py b/src/nitypes/waveform/_timing/_base.py index cea359dc..09192c68 100644 --- a/src/nitypes/waveform/_timing/_base.py +++ b/src/nitypes/waveform/_timing/_base.py @@ -7,13 +7,18 @@ from typing import Any, Generic, SupportsIndex, TypeVar from nitypes._exceptions import add_note -from nitypes._typing import Self from nitypes.waveform._timing._sample_interval import ( SampleIntervalMode, SampleIntervalStrategy, create_sample_interval_strategy, ) +try: + from typing import Self +except ImportError: + from nitypes._typing import Self + + _TDateTime = TypeVar("_TDateTime", bound=dt.datetime) _TTimeDelta = TypeVar("_TTimeDelta", bound=dt.timedelta) diff --git a/src/nitypes/waveform/_timing/_conversion.py b/src/nitypes/waveform/_timing/_conversion.py index 8debd2e7..4242cb19 100644 --- a/src/nitypes/waveform/_timing/_conversion.py +++ b/src/nitypes/waveform/_timing/_conversion.py @@ -8,12 +8,16 @@ import hightime as ht from nitypes._exceptions import invalid_arg_type, invalid_requested_type -from nitypes._typing import TypeAlias from nitypes.time._conversion import convert_datetime, convert_timedelta from nitypes.waveform._timing._base import BaseTiming from nitypes.waveform._timing._precision import PrecisionTiming from nitypes.waveform._timing._standard import Timing +try: + from typing import TypeAlias +except ImportError: + from nitypes._typing import TypeAlias + _AnyTiming: TypeAlias = Union[BaseTiming[Any, Any], Timing, PrecisionTiming] _TTiming = TypeVar("_TTiming", bound=BaseTiming[Any, Any]) diff --git a/src/nitypes/waveform/_timing/_precision.py b/src/nitypes/waveform/_timing/_precision.py index c77effcd..14058f86 100644 --- a/src/nitypes/waveform/_timing/_precision.py +++ b/src/nitypes/waveform/_timing/_precision.py @@ -5,10 +5,14 @@ import hightime as ht -from nitypes._typing import override from nitypes.waveform._timing._base import BaseTiming from nitypes.waveform._timing._sample_interval import SampleIntervalMode +try: + from typing import override +except ImportError: + from nitypes._typing import override + class PrecisionTiming(BaseTiming[ht.datetime, ht.timedelta]): """High-precision waveform timing using the hightime package. diff --git a/src/nitypes/waveform/_timing/_standard.py b/src/nitypes/waveform/_timing/_standard.py index c9d1e88a..faf1fe66 100644 --- a/src/nitypes/waveform/_timing/_standard.py +++ b/src/nitypes/waveform/_timing/_standard.py @@ -4,10 +4,14 @@ from collections.abc import Sequence from typing import ClassVar -from nitypes._typing import override from nitypes.waveform._timing._base import BaseTiming from nitypes.waveform._timing._sample_interval import SampleIntervalMode +try: + from typing import override +except ImportError: + from nitypes._typing import override + class Timing(BaseTiming[dt.datetime, dt.timedelta]): """Waveform timing using the standard datetime module.