summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThaddeus Crews <repiteo@outlook.com>2024-04-14 18:11:47 (GMT)
committerThaddeus Crews <repiteo@outlook.com>2024-07-09 16:23:26 (GMT)
commit187bf05b1631ff2e2359ce2bc5875854f9082258 (patch)
tree2beaac20a27430382b1f331a424e5859f01d35d0
parent3b10709c1a5486d3e018effd27d85e55807afc56 (diff)
downloadSCons-187bf05b1631ff2e2359ce2bc5875854f9082258.zip
SCons-187bf05b1631ff2e2359ce2bc5875854f9082258.tar.gz
SCons-187bf05b1631ff2e2359ce2bc5875854f9082258.tar.bz2
Implement somewhat pythonic type hints in sctypes
-rw-r--r--CHANGES.txt8
-rw-r--r--RELEASE.txt6
-rw-r--r--SCons/Util/sctypes.py38
3 files changed, 42 insertions, 10 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 390a0d5..dea43b8 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -12,9 +12,11 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release.
RELEASE VERSION/DATE TO BE FILLED IN LATER
- From John Doe:
-
- - Whatever John Doe did.
+ From Thaddeus Crews:
+ - Add explicit return types to sctypes `is_*` functions. For Python <=3.9,
+ the return type is simply `bool`, same as before. Python 3.10 and later
+ will benefit from `TypeGuard`/`TypeIs`, to produce intellisense similar
+ to using `isinstance` directly.
RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700
diff --git a/RELEASE.txt b/RELEASE.txt
index 1797698..5845e56 100644
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -56,7 +56,11 @@ DOCUMENTATION
DEVELOPMENT
-----------
-- List visible changes in the way SCons is developed
+- sctypes `is_*` functions given explicit return types. Python 3.13+ uses
+ `TypeIs` for a near-equivalent of `isinstance`. Python 3.10 through 3.12
+ uses `TypeGuard`, a less accurate implementation but still provides
+ usable type hinting. Python 3.9 and earlier simply returns `bool`, same
+ as before.
Thanks to the following contributors listed below for their contributions to this release.
==========================================================================================
diff --git a/SCons/Util/sctypes.py b/SCons/Util/sctypes.py
index bcbefb6..765458e 100644
--- a/SCons/Util/sctypes.py
+++ b/SCons/Util/sctypes.py
@@ -11,7 +11,8 @@ import codecs
import os
import pprint
import re
-from typing import Optional
+import sys
+from typing import Optional, Union
from collections import UserDict, UserList, UserString, deque
from collections.abc import MappingView, Iterable
@@ -50,38 +51,63 @@ StringTypes = (str, UserString)
# Empirically, it is faster to check explicitly for str than for basestring.
BaseStringTypes = str
+# Later Python versions allow us to explicitly apply type hints based off the
+# return value similar to isinstance(), albeit not as precise.
+if sys.version_info >= (3, 13):
+ from typing import TypeAlias, TypeIs
+
+ DictTypeRet: TypeAlias = TypeIs[Union[dict, UserDict]]
+ ListTypeRet: TypeAlias = TypeIs[Union[list, UserList, deque]]
+ SequenceTypeRet: TypeAlias = TypeIs[Union[list, tuple, deque, UserList, MappingView]]
+ TupleTypeRet: TypeAlias = TypeIs[tuple]
+ StringTypeRet: TypeAlias = TypeIs[Union[str, UserString]]
+elif sys.version_info >= (3, 10):
+ from typing import TypeAlias, TypeGuard
+
+ DictTypeRet: TypeAlias = TypeGuard[Union[dict, UserDict]]
+ ListTypeRet: TypeAlias = TypeGuard[Union[list, UserList, deque]]
+ SequenceTypeRet: TypeAlias = TypeGuard[Union[list, tuple, deque, UserList, MappingView]]
+ TupleTypeRet: TypeAlias = TypeGuard[tuple]
+ StringTypeRet: TypeAlias = TypeGuard[Union[str, UserString]]
+else:
+ DictTypeRet = Union[bool, bool]
+ ListTypeRet = Union[bool, bool]
+ SequenceTypeRet = Union[bool, bool]
+ TupleTypeRet = Union[bool, bool]
+ StringTypeRet = Union[bool, bool]
+
def is_Dict( # pylint: disable=redefined-outer-name,redefined-builtin
obj, isinstance=isinstance, DictTypes=DictTypes
-) -> bool:
+) -> DictTypeRet:
"""Check if object is a dict."""
return isinstance(obj, DictTypes)
def is_List( # pylint: disable=redefined-outer-name,redefined-builtin
obj, isinstance=isinstance, ListTypes=ListTypes
-) -> bool:
+) -> ListTypeRet:
"""Check if object is a list."""
return isinstance(obj, ListTypes)
def is_Sequence( # pylint: disable=redefined-outer-name,redefined-builtin
obj, isinstance=isinstance, SequenceTypes=SequenceTypes
-) -> bool:
+) -> SequenceTypeRet:
"""Check if object is a sequence."""
return isinstance(obj, SequenceTypes)
def is_Tuple( # pylint: disable=redefined-builtin
obj, isinstance=isinstance, tuple=tuple
-) -> bool:
+) -> TupleTypeRet:
"""Check if object is a tuple."""
return isinstance(obj, tuple)
def is_String( # pylint: disable=redefined-outer-name,redefined-builtin
obj, isinstance=isinstance, StringTypes=StringTypes
-) -> bool:
+) -> StringTypeRet:
"""Check if object is a string."""
return isinstance(obj, StringTypes)