Skip to content

Commit 187bf05

Browse files
committed
Implement somewhat pythonic type hints in sctypes
1 parent 3b10709 commit 187bf05

File tree

3 files changed

+42
-10
lines changed

3 files changed

+42
-10
lines changed

CHANGES.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release.
1212

1313
RELEASE VERSION/DATE TO BE FILLED IN LATER
1414

15-
From John Doe:
16-
17-
- Whatever John Doe did.
15+
From Thaddeus Crews:
16+
- Add explicit return types to sctypes `is_*` functions. For Python <=3.9,
17+
the return type is simply `bool`, same as before. Python 3.10 and later
18+
will benefit from `TypeGuard`/`TypeIs`, to produce intellisense similar
19+
to using `isinstance` directly.
1820

1921

2022
RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700

RELEASE.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ DOCUMENTATION
5656
DEVELOPMENT
5757
-----------
5858

59-
- List visible changes in the way SCons is developed
59+
- sctypes `is_*` functions given explicit return types. Python 3.13+ uses
60+
`TypeIs` for a near-equivalent of `isinstance`. Python 3.10 through 3.12
61+
uses `TypeGuard`, a less accurate implementation but still provides
62+
usable type hinting. Python 3.9 and earlier simply returns `bool`, same
63+
as before.
6064

6165
Thanks to the following contributors listed below for their contributions to this release.
6266
==========================================================================================

SCons/Util/sctypes.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import os
1212
import pprint
1313
import re
14-
from typing import Optional
14+
import sys
15+
from typing import Optional, Union
1516

1617
from collections import UserDict, UserList, UserString, deque
1718
from collections.abc import MappingView, Iterable
@@ -50,38 +51,63 @@
5051
# Empirically, it is faster to check explicitly for str than for basestring.
5152
BaseStringTypes = str
5253

54+
# Later Python versions allow us to explicitly apply type hints based off the
55+
# return value similar to isinstance(), albeit not as precise.
56+
if sys.version_info >= (3, 13):
57+
from typing import TypeAlias, TypeIs
58+
59+
DictTypeRet: TypeAlias = TypeIs[Union[dict, UserDict]]
60+
ListTypeRet: TypeAlias = TypeIs[Union[list, UserList, deque]]
61+
SequenceTypeRet: TypeAlias = TypeIs[Union[list, tuple, deque, UserList, MappingView]]
62+
TupleTypeRet: TypeAlias = TypeIs[tuple]
63+
StringTypeRet: TypeAlias = TypeIs[Union[str, UserString]]
64+
elif sys.version_info >= (3, 10):
65+
from typing import TypeAlias, TypeGuard
66+
67+
DictTypeRet: TypeAlias = TypeGuard[Union[dict, UserDict]]
68+
ListTypeRet: TypeAlias = TypeGuard[Union[list, UserList, deque]]
69+
SequenceTypeRet: TypeAlias = TypeGuard[Union[list, tuple, deque, UserList, MappingView]]
70+
TupleTypeRet: TypeAlias = TypeGuard[tuple]
71+
StringTypeRet: TypeAlias = TypeGuard[Union[str, UserString]]
72+
else:
73+
DictTypeRet = Union[bool, bool]
74+
ListTypeRet = Union[bool, bool]
75+
SequenceTypeRet = Union[bool, bool]
76+
TupleTypeRet = Union[bool, bool]
77+
StringTypeRet = Union[bool, bool]
78+
5379

5480
def is_Dict( # pylint: disable=redefined-outer-name,redefined-builtin
5581
obj, isinstance=isinstance, DictTypes=DictTypes
56-
) -> bool:
82+
) -> DictTypeRet:
5783
"""Check if object is a dict."""
5884
return isinstance(obj, DictTypes)
5985

6086

6187
def is_List( # pylint: disable=redefined-outer-name,redefined-builtin
6288
obj, isinstance=isinstance, ListTypes=ListTypes
63-
) -> bool:
89+
) -> ListTypeRet:
6490
"""Check if object is a list."""
6591
return isinstance(obj, ListTypes)
6692

6793

6894
def is_Sequence( # pylint: disable=redefined-outer-name,redefined-builtin
6995
obj, isinstance=isinstance, SequenceTypes=SequenceTypes
70-
) -> bool:
96+
) -> SequenceTypeRet:
7197
"""Check if object is a sequence."""
7298
return isinstance(obj, SequenceTypes)
7399

74100

75101
def is_Tuple( # pylint: disable=redefined-builtin
76102
obj, isinstance=isinstance, tuple=tuple
77-
) -> bool:
103+
) -> TupleTypeRet:
78104
"""Check if object is a tuple."""
79105
return isinstance(obj, tuple)
80106

81107

82108
def is_String( # pylint: disable=redefined-outer-name,redefined-builtin
83109
obj, isinstance=isinstance, StringTypes=StringTypes
84-
) -> bool:
110+
) -> StringTypeRet:
85111
"""Check if object is a string."""
86112
return isinstance(obj, StringTypes)
87113

0 commit comments

Comments
 (0)